mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-09 00:43:22 +08:00
Fork dsh-cron-tasks as dsh-ops-cron with DSH/IM delivery.
Agent cron_* jobs default to IM when created from a WhatsApp session with a matching delivery target; otherwise DSH sidebar sessions. Fire path soft-calls ctx.dshIm.send for summaries. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4447404361
commit
2f08136adf
17 changed files with 472 additions and 285 deletions
68
test/delivery.test.js
Normal file
68
test/delivery.test.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import {
|
||||
matchTargetForPeer,
|
||||
normalizeDelivery,
|
||||
resolveCreateDelivery,
|
||||
deliverRunToIm,
|
||||
} from '../lib/delivery.js'
|
||||
|
||||
test('normalizeDelivery defaults to dsh', () => {
|
||||
assert.deepEqual(normalizeDelivery({}), { kind: 'dsh' })
|
||||
})
|
||||
|
||||
test('normalizeDelivery requires botId+targetId for im', () => {
|
||||
assert.throws(() => normalizeDelivery({ kind: 'im', botId: 'b' }), /im delivery/)
|
||||
assert.deepEqual(
|
||||
normalizeDelivery({ kind: 'im', botId: 'bot_1', targetId: 'alerts' }),
|
||||
{ kind: 'im', botId: 'bot_1', targetId: 'alerts' },
|
||||
)
|
||||
})
|
||||
|
||||
test('matchTargetForPeer matches whatsapp jid from conversationKey', () => {
|
||||
const peer = { botId: 'bot_1', conversationKey: 'direct:8613800000000@s.whatsapp.net' }
|
||||
const targets = [
|
||||
{ targetId: 'other', route: { jid: '1@s.whatsapp.net' } },
|
||||
{ targetId: 'alerts', route: { jid: '8613800000000@s.whatsapp.net' } },
|
||||
]
|
||||
assert.equal(matchTargetForPeer(targets, peer).targetId, 'alerts')
|
||||
})
|
||||
|
||||
test('resolveCreateDelivery uses explicit im args', async () => {
|
||||
const delivery = await resolveCreateDelivery({
|
||||
delivery: 'im',
|
||||
im_bot_id: 'bot_x',
|
||||
im_target_id: 'tgt_y',
|
||||
}, {})
|
||||
assert.deepEqual(delivery, { kind: 'im', botId: 'bot_x', targetId: 'tgt_y' })
|
||||
})
|
||||
|
||||
test('resolveCreateDelivery auto-matches peer target', async () => {
|
||||
const dshIm = {
|
||||
resolveSessionPeer: async () => ({
|
||||
botId: 'bot_1',
|
||||
conversationKey: 'direct:86138@s.whatsapp.net',
|
||||
}),
|
||||
listTargets: async () => ([
|
||||
{ targetId: 'wa-dm', route: { jid: '86138@s.whatsapp.net' } },
|
||||
]),
|
||||
}
|
||||
const delivery = await resolveCreateDelivery({}, {
|
||||
agent: { session: { id: 'sess-1' } },
|
||||
}, { dshIm })
|
||||
assert.deepEqual(delivery, { kind: 'im', botId: 'bot_1', targetId: 'wa-dm' })
|
||||
})
|
||||
|
||||
test('deliverRunToIm sends via dshIm', async () => {
|
||||
const sent = []
|
||||
await deliverRunToIm(
|
||||
{ name: 'ping', delivery: { kind: 'im', botId: 'b', targetId: 't' } },
|
||||
'hello world',
|
||||
{ dshIm: { send: async (...args) => { sent.push(args) } } },
|
||||
)
|
||||
assert.equal(sent.length, 1)
|
||||
assert.equal(sent[0][0], 'b')
|
||||
assert.equal(sent[0][1], 't')
|
||||
assert.match(sent[0][2], /运维定时/)
|
||||
assert.match(sent[0][2], /hello world/)
|
||||
})
|
||||
|
|
@ -89,7 +89,7 @@ async function jsonRequest(base, path, options = {}) {
|
|||
}
|
||||
|
||||
test('host service: create, list, run-now, history, and workspace isolation', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
|
||||
const archived = []
|
||||
|
|
@ -163,7 +163,7 @@ test('host service: create, list, run-now, history, and workspace isolation', as
|
|||
})
|
||||
|
||||
test('overlap skip writes a skipped history row instead of a second session', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
let clock = Date.parse('2026-08-24T01:00:00.000Z')
|
||||
let inflight = 0
|
||||
|
|
@ -192,7 +192,7 @@ test('overlap skip writes a skipped history row instead of a second session', as
|
|||
})
|
||||
|
||||
test('shipped HTTP handler: create, list, run-now, history', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const archived = []
|
||||
const service = createHostService({
|
||||
|
|
@ -210,7 +210,7 @@ test('shipped HTTP handler: create, list, run-now, history', async (t) => {
|
|||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
|
||||
const created = await jsonRequest(http.url, '/dsh-cron-tasks/jobs', {
|
||||
const created = await jsonRequest(http.url, '/dsh-ops-cron/jobs', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: 'http job',
|
||||
|
|
@ -222,23 +222,23 @@ test('shipped HTTP handler: create, list, run-now, history', async (t) => {
|
|||
assert.equal(created.body.job.name, 'http job')
|
||||
const jobId = created.body.job.id
|
||||
|
||||
const listed = await jsonRequest(http.url, '/dsh-cron-tasks/jobs')
|
||||
const listed = await jsonRequest(http.url, '/dsh-ops-cron/jobs')
|
||||
assert.equal(listed.body.jobs.length, 1)
|
||||
|
||||
const ran = await jsonRequest(http.url, `/dsh-cron-tasks/jobs/${jobId}/run`, { method: 'POST' })
|
||||
const ran = await jsonRequest(http.url, `/dsh-ops-cron/jobs/${jobId}/run`, { method: 'POST' })
|
||||
assert.equal(ran.status, 200)
|
||||
assert.equal(ran.body.run.sessionId, 'http-sess-1')
|
||||
assert.ok(['succeeded', 'running'].includes(ran.body.run.status))
|
||||
assert.deepEqual(archived, ['http-sess-1'])
|
||||
|
||||
const history = await jsonRequest(http.url, '/dsh-cron-tasks/history')
|
||||
const history = await jsonRequest(http.url, '/dsh-ops-cron/history')
|
||||
assert.ok(history.body.runs.some((row) => row.sessionId === 'http-sess-1'))
|
||||
const visible = await jsonRequest(http.url, '/dsh-cron-tasks/workspace-visible?id=http-sess-1&id=other')
|
||||
const visible = await jsonRequest(http.url, '/dsh-ops-cron/workspace-visible?id=http-sess-1&id=other')
|
||||
assert.deepEqual(visible.body.visible, ['other'])
|
||||
})
|
||||
|
||||
test('POST /runs/:id/open reveals the run session id', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const revealed = []
|
||||
const service = createHostService({
|
||||
|
|
@ -253,7 +253,7 @@ test('POST /runs/:id/open reveals the run session id', async (t) => {
|
|||
})
|
||||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
const created = await jsonRequest(http.url, '/dsh-cron-tasks/jobs', {
|
||||
const created = await jsonRequest(http.url, '/dsh-ops-cron/jobs', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: 'open-job',
|
||||
|
|
@ -261,8 +261,8 @@ test('POST /runs/:id/open reveals the run session id', async (t) => {
|
|||
schedule: { kind: 'cron', expr: '0 9 * * *', timezone: 'Asia/Shanghai' },
|
||||
}),
|
||||
})
|
||||
const ran = await jsonRequest(http.url, `/dsh-cron-tasks/jobs/${created.body.job.id}/run`, { method: 'POST' })
|
||||
const opened = await jsonRequest(http.url, `/dsh-cron-tasks/runs/${ran.body.run.id}/open`, { method: 'POST' })
|
||||
const ran = await jsonRequest(http.url, `/dsh-ops-cron/jobs/${created.body.job.id}/run`, { method: 'POST' })
|
||||
const opened = await jsonRequest(http.url, `/dsh-ops-cron/runs/${ran.body.run.id}/open`, { method: 'POST' })
|
||||
assert.equal(opened.status, 200)
|
||||
assert.equal(opened.body.sessionId, 'open-sess')
|
||||
assert.deepEqual(revealed, ['open-sess'])
|
||||
|
|
@ -330,7 +330,7 @@ test('resolveDefaultModel fails loud when Models has no selection', async () =>
|
|||
})
|
||||
|
||||
test('POST /conceal archives every run session id', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const archived = []
|
||||
const service = createHostService({
|
||||
|
|
@ -345,7 +345,7 @@ test('POST /conceal archives every run session id', async (t) => {
|
|||
})
|
||||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
const created = await jsonRequest(http.url, '/dsh-cron-tasks/jobs', {
|
||||
const created = await jsonRequest(http.url, '/dsh-ops-cron/jobs', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: 'hide-job',
|
||||
|
|
@ -353,15 +353,15 @@ test('POST /conceal archives every run session id', async (t) => {
|
|||
schedule: { kind: 'cron', expr: '0 9 * * *', timezone: 'Asia/Shanghai' },
|
||||
}),
|
||||
})
|
||||
await jsonRequest(http.url, `/dsh-cron-tasks/jobs/${created.body.job.id}/run`, { method: 'POST' })
|
||||
await jsonRequest(http.url, `/dsh-ops-cron/jobs/${created.body.job.id}/run`, { method: 'POST' })
|
||||
archived.length = 0
|
||||
const hidden = await jsonRequest(http.url, '/dsh-cron-tasks/conceal', { method: 'POST' })
|
||||
const hidden = await jsonRequest(http.url, '/dsh-ops-cron/conceal', { method: 'POST' })
|
||||
assert.equal(hidden.status, 200)
|
||||
assert.ok(archived.includes('hide-sess'))
|
||||
})
|
||||
|
||||
test('one-shot at job fires once then later ticks wait instead of retriggering', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const at = Date.parse('2026-08-23T14:57:00.000Z')
|
||||
let clock = at - 60_000
|
||||
|
|
@ -508,7 +508,7 @@ test('listModelChoices maps llm providers and the current default', async () =>
|
|||
})
|
||||
|
||||
test('GET /models lists session-port catalog', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const service = createHostService({
|
||||
filePath: join(dir, 'store.json'),
|
||||
|
|
@ -520,7 +520,7 @@ test('GET /models lists session-port catalog', async (t) => {
|
|||
})
|
||||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
const res = await jsonRequest(http.url, '/dsh-cron-tasks/models')
|
||||
const res = await jsonRequest(http.url, '/dsh-ops-cron/models')
|
||||
assert.equal(res.status, 200)
|
||||
assert.equal(res.body.current.model, 'deepseek-chat')
|
||||
assert.equal(res.body.groups[0].models[0].id, 'deepseek-chat')
|
||||
|
|
@ -547,7 +547,7 @@ test('listWorkspaceChoices maps registry entries to title and path', () => {
|
|||
})
|
||||
|
||||
test('GET /workspaces lists session-port choices', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const service = createHostService({
|
||||
filePath: join(dir, 'store.json'),
|
||||
|
|
@ -559,13 +559,13 @@ test('GET /workspaces lists session-port choices', async (t) => {
|
|||
})
|
||||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
const res = await jsonRequest(http.url, '/dsh-cron-tasks/workspaces')
|
||||
const res = await jsonRequest(http.url, '/dsh-ops-cron/workspaces')
|
||||
assert.equal(res.status, 200)
|
||||
assert.deepEqual(res.body.workspaces, [{ id: 'ws-1', title: 'app', path: '/tmp/app' }])
|
||||
})
|
||||
|
||||
test('POST /sessions/:id/adopt uses the session port', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const adopted = []
|
||||
const service = createHostService({
|
||||
|
|
@ -579,7 +579,7 @@ test('POST /sessions/:id/adopt uses the session port', async (t) => {
|
|||
})
|
||||
const http = await listen(service)
|
||||
t.after(() => http.close())
|
||||
const res = await jsonRequest(http.url, '/dsh-cron-tasks/sessions/child-9/adopt', { method: 'POST' })
|
||||
const res = await jsonRequest(http.url, '/dsh-ops-cron/sessions/child-9/adopt', { method: 'POST' })
|
||||
assert.equal(res.status, 200)
|
||||
assert.equal(res.body.attached, true)
|
||||
assert.deepEqual(adopted, ['child-9'])
|
||||
|
|
@ -598,7 +598,7 @@ test('waitForAgentTurn waits for running then idle, and fails if the agent never
|
|||
})
|
||||
|
||||
test('after one live-shaped dispatch, the next run-now still fires', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const archived = []
|
||||
const sessionPort = makeLiveSessionPort(fakeLiveCtx(archived))
|
||||
|
|
@ -632,7 +632,7 @@ test('after one live-shaped dispatch, the next run-now still fires', async (t) =
|
|||
})
|
||||
|
||||
test('after a live-shaped due tick settles, the next due occurrence still fires', async (t) => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-cron-tasks-'))
|
||||
const dir = await mkdtemp(join(tmpdir(), 'dsh-ops-cron-'))
|
||||
t.after(() => rm(dir, { recursive: true, force: true }))
|
||||
const archived = []
|
||||
let clock = Date.parse('2026-08-24T01:00:00.000Z')
|
||||
|
|
|
|||
|
|
@ -8,18 +8,19 @@ 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.name, 'dsh-ops-cron')
|
||||
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/)
|
||||
assert.match(patch, /id:\s*dsh-ops-cron/)
|
||||
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'\]/)
|
||||
assert.match(index, /getDshIm/)
|
||||
const tools = await readFile(join(root, 'lib/tools.js'), 'utf8')
|
||||
assert.match(tools, /name: 'cron_create'/)
|
||||
assert.match(tools, /name: 'cron_list'/)
|
||||
|
|
@ -28,9 +29,9 @@ test('installable bundle declares host apply, client half, unique id, and no @de
|
|||
assert.match(tools, /hour/)
|
||||
assert.match(tools, /Current local time/)
|
||||
assert.match(tools, /source: 'runtime'/)
|
||||
assert.match(tools, /current session cwd/)
|
||||
assert.match(tools, /resolveCreateDelivery/)
|
||||
assert.match(tools, /im_bot_id/)
|
||||
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/)
|
||||
|
|
@ -40,80 +41,26 @@ test('installable bundle declares host apply, client half, unique id, and no @de
|
|||
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, /deliverRunToIm/)
|
||||
assert.match(host, /PLUGIN_NAME = 'dsh-ops-cron'/)
|
||||
assert.match(host, /API_PREFIX = '\/dsh-ops-cron'/)
|
||||
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, /运维定时/)
|
||||
assert.match(client, /dsh-ops-cron/)
|
||||
assert.match(client, /settings\.plugin\.item/)
|
||||
assert.match(client, /新建会话/)
|
||||
assert.match(client, /logoRow/)
|
||||
assert.match(client, /dsh-ct-entry/)
|
||||
assert.match(client, /centerCol/)
|
||||
assert.match(client, /data-dsh-ct-host/)
|
||||
assert.match(client, /_collapsed"\]\[data-dsh-ct-mode=on\] \.dsh-ct-region/)
|
||||
assert.match(client, /listRoot\.parentElement !== region\) region\.appendChild\(listRoot\)/)
|
||||
assert.match(client, /jobStamp/)
|
||||
assert.doesNotMatch(client, /\[data-dsh-ct-mode=on\] \.dsh-ct-region\{display:flex !important;animation:/)
|
||||
assert.match(client, /visibility:hidden/)
|
||||
assert.match(client, /createPortal\(editor, paneHost\)/)
|
||||
assert.match(client, /sidebarCollapsed/)
|
||||
assert.match(client, /_collapsed/)
|
||||
assert.match(client, /width:36px/)
|
||||
assert.match(client, /data-collapsed/)
|
||||
assert.match(client, /translate\(-50%, -50%\)/)
|
||||
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, /deliveryKind/)
|
||||
assert.match(client, /imBotId/)
|
||||
assert.match(client, /imTargetId/)
|
||||
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/)
|
||||
assert.match(client, /inject = \['slots', 'locale', 'settingsScope'\]/)
|
||||
const delivery = await readFile(join(root, 'lib/delivery.js'), 'utf8')
|
||||
assert.match(delivery, /resolveCreateDelivery/)
|
||||
assert.match(delivery, /deliverRunToIm/)
|
||||
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, /定时任务/)
|
||||
assert.match(prd, /dsh-schedule/)
|
||||
assert.match(prd, /非目标/)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue