import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { test } from 'node:test' const root = join(dirname(fileURLToPath(import.meta.url)), '..') test('installable bundle declares host apply, client half, unique id, and no @deepseek-ai runtime deps', async () => { const pkg = JSON.parse(await readFile(join(root, 'package.json'), 'utf8')) assert.equal(pkg.name, '@dsh-external/dsh-cron-tasks') assert.equal(pkg.dsh.bundle.patch, './cordis.patch.yml') assert.equal(pkg.dsh.client.platform, 'web') assert.ok(pkg.exports['./client']) assert.equal(pkg.main, 'lib/index.js') assert.ok(!pkg.dependencies || !Object.keys(pkg.dependencies).some((id) => id.startsWith('@deepseek-ai/'))) const patch = await readFile(join(root, 'cordis.patch.yml'), 'utf8') assert.match(patch, /id:\s*dsh-cron-tasks/) const index = await readFile(join(root, 'lib/index.js'), 'utf8') assert.match(index, /export function apply\(/) assert.match(index, /registerCronTools/) assert.match(index, /inject\(\['tools'\]/) const tools = await readFile(join(root, 'lib/tools.js'), 'utf8') assert.match(tools, /name: 'cron_create'/) assert.match(tools, /name: 'cron_list'/) assert.match(tools, /crontab/) assert.match(tools, /scheduled-tasks/) assert.match(tools, /hour/) assert.match(tools, /Current local time/) assert.match(tools, /source: 'runtime'/) assert.match(tools, /current session cwd/) assert.match(tools, /resolveCreateCwd/) assert.match(tools, /Working directory: if the user is chatting in a workspace/) assert.match(tools, /after_minutes/) assert.match(tools, /Extra schedule fields are ignored/) assert.doesNotMatch(tools, /pass only one of expr, at, or hour/) assert.match(index, /makeCronSkill/) assert.doesNotMatch(index, /agent\/session-start/) assert.doesNotMatch(index, /system-reminder/) assert.doesNotMatch(tools, /from ['"]@deepseek-ai\//) const host = await readFile(join(root, 'lib/host.js'), 'utf8') assert.match(host, /agentDefaultModel/) assert.match(host, /system-prompt\/assemble/) assert.match(host, /agentOptions/) assert.match(host, /nextRunAt === null/) assert.match(host, /resolveSessionPlacement/) assert.match(host, /adoptSessionIntoWorkspace/) assert.match(host, /attachLiveSessionToWorkspace/) assert.match(host, /listWorkspaceChoices/) assert.match(host, /\/workspaces/) assert.match(host, /listModelChoices/) assert.match(host, /resolveJobModel/) assert.match(host, /\/models/) assert.doesNotMatch(host, /from ['"]@deepseek-ai\//) const client = await readFile(join(root, 'lib/client.js'), 'utf8') assert.match(client, /定时任务/) assert.match(client, /settings\.plugin\.item/) assert.match(client, /新建会话/) assert.match(client, /logoRow/) assert.match(client, /dsh-ct-entry/) assert.match(client, /sidebarCollapsed/) assert.match(client, /_collapsed/) assert.match(client, /width:36px/) assert.match(client, /返回工作区/) assert.match(client, /runs\/\$\{run\.id\}\/open/) assert.match(client, /regionArea/) assert.match(client, /sessions\.open\(/) assert.match(client, /getService\(ctx, faces, 'sessions'\)/) assert.match(client, /workspaceSkin/) assert.match(client, /IconFolderOpen16/) assert.match(client, /IconProjectAddOutline16/) assert.match(client, /IconPlayOutline16/) assert.match(client, /IconPauseOutline16/) assert.match(client, /IconSearchOutline16/) assert.match(client, /searchPlaceholder/) assert.match(client, /\/conceal/) assert.match(client, /shouldPromoteCronFork/) assert.match(client, /workspaceTitleFromCronFork/) assert.match(client, /leaveCronMode/) assert.match(client, /fork rename failed/) assert.match(client, /sessions\/\$\{encodeURIComponent\(row\.id\)\}\/adopt/) assert.match(client, /sessions\.length > 0 && visible\.length === 0/) assert.match(client, /未分组|未分类/) assert.match(client, /expanded\[job\.id\] === true/) assert.match(client, /inject = \['slots', 'locale', 'settingsScope'\]/) assert.doesNotMatch(client, /inject = \[[^\]]*sessions[^\]]*\]/) assert.match(client, /工作目录/) assert.match(client, /cwdRecent/) assert.match(client, /cwdCustom/) assert.match(client, /CwdField/) assert.match(client, /ModelField/) assert.match(client, /api\('\/models'\)/) assert.match(tools, /resolveCreateModel/) assert.match(tools, /snapshots the current session model/) assert.match(client, /api\('\/workspaces'\)/) assert.match(client, /atToLocalInput/) assert.match(client, /localInputToAt/) const prd = await readFile(join(root, 'docs/PRD.md'), 'utf8') assert.match(prd, /左侧侧边栏「新会话」按钮正下方的「定时任务」入口/) assert.match(prd, /设置页「插件配置」里的定时任务设置/) assert.match(prd, /定时任务历史/) assert.match(prd, /chicheng-cron/) assert.match(prd, /dsh-automation/) assert.match(prd, /cronjob-dsh-plugin/) assert.match(prd, /dsh-cron/) assert.match(prd, /dsh-schedule-tasks/) assert.match(prd, /dsh-schedule/) assert.match(prd, /非目标/) })