mirror of
https://github.com/hansjone/dsh-ops-cron.git
synced 2026-10-11 00:50:45 +08:00
Add cron_retrigger for consumed one-shot jobs.
Resume only unpauses; listeners need a native way to re-fire the same job id with a new run without creating duplicate executors. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6724332114
commit
c11221606a
9 changed files with 324 additions and 7 deletions
60
lib/tools.js
60
lib/tools.js
|
|
@ -53,6 +53,7 @@ const JOB_SCHEMA = {
|
|||
lastRunAt: { oneOf: [{ type: 'number' }, { type: 'null' }] },
|
||||
lastStatus: { oneOf: [{ type: 'string' }, { type: 'null' }] },
|
||||
nextRunAt: { oneOf: [{ type: 'number' }, { type: 'null' }] },
|
||||
retriggerable: { type: 'boolean' },
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -685,7 +686,7 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
},
|
||||
{
|
||||
name: 'cron_resume',
|
||||
description: 'Resume a paused scheduled task by id.',
|
||||
description: 'Resume a paused scheduled task by id. Only flips enabled=true; does NOT re-fire a consumed one-shot (next=n/a). Use cron_retrigger for that.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
|
|
@ -707,6 +708,56 @@ export function cronToolDefinitions(service, deps = {}) {
|
|||
return { job }
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cron_retrigger',
|
||||
description: 'Re-fire a consumed one-shot job (succeeded/failed/skipped with next=n/a) as a new run. Auto-enables if paused. Use after_minutes>0 to delay. For recurring jobs use pause/resume. Listeners: when retriggerable=true and progress done_flag is false, call this instead of creating a new job.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
task_id: { type: 'string', description: 'One-shot job id (alias: id).' },
|
||||
id: { type: 'string', description: 'Alias of task_id.' },
|
||||
after_minutes: { type: 'number', description: 'Delay before fire. Omit or 0 = immediate run-now.' },
|
||||
},
|
||||
},
|
||||
output: {
|
||||
schema: {
|
||||
type: 'object',
|
||||
additionalProperties: true,
|
||||
properties: {
|
||||
ok: { type: 'boolean' },
|
||||
mode: { type: 'string' },
|
||||
job: JOB_SCHEMA,
|
||||
run: { oneOf: [RUN_SCHEMA, { type: 'null' }] },
|
||||
nextRunAt: { oneOf: [{ type: 'number' }, { type: 'null' }] },
|
||||
},
|
||||
},
|
||||
render: (_args, value) => {
|
||||
const name = value.job?.name || value.job?.id || ''
|
||||
if (value.mode === 'scheduled') {
|
||||
const when = value.nextRunAt ? new Date(value.nextRunAt).toISOString() : 'pending'
|
||||
return text(`Retrigger scheduled "${name}" → next ${when}.`)
|
||||
}
|
||||
return text(`Retriggered "${name}"${value.run?.id ? ` (run ${value.run.id})` : ''}.`)
|
||||
},
|
||||
},
|
||||
presentCall: (args) => ({
|
||||
card: 'generic',
|
||||
title: t('tool.retrigger'),
|
||||
content: String(args?.task_id || args?.id || ''),
|
||||
}),
|
||||
async execute(args, exec) {
|
||||
aborted(exec)
|
||||
const peer = await resolveCallerPeer(exec, getDshIm())
|
||||
const identity = resolveToolIdentity(exec, service)
|
||||
const id = String(args?.task_id || args?.id || '').trim()
|
||||
if (!id) throw new Error('task_id is required')
|
||||
await requireOwnedJob(service, id, peer, identity)
|
||||
return service.retriggerJob(id, {
|
||||
after_minutes: args?.after_minutes,
|
||||
}, identity)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'cron_delete',
|
||||
description: 'Permanently delete a scheduled task by id. History rows for that job remain until pruned.',
|
||||
|
|
@ -761,8 +812,9 @@ export function cronGuidanceText(nowMs = Date.now(), timeZone = 'Asia/Shanghai')
|
|||
'Session mirror: by default the run summary is NOT injected into the origin WhatsApp/Web chat. Pass mirror_to_session=true only when the user wants follow-up context in that chat. Full tool traces always stay in run history; IM delivery (when configured) is independent.',
|
||||
'Agent preset: omit agent_preset to inherit (WhatsApp chat/group preset → creating session → Host default). Pass agent_preset to pin a preset for every scheduled run.',
|
||||
'Labels/monitor: pass labels={"role":"worker","task":"theory"} on workers; listeners pass watch={"taskId":"..."} or watch={"labels":{...},"match":"all"}. Use cron_query / cron_progress / cron_runs to poll state and progress.',
|
||||
'When the user asks to look at, create, pause, resume, or delete 定时任务 / scheduled tasks / cron jobs:',
|
||||
'1. If cron_list / cron_create / cron_pause / cron_resume / cron_delete / cron_query / cron_runs / cron_progress are in your tool list, call them.',
|
||||
'One-shot wake: cron_resume only unpauses. For a finished one-shot (retriggerable=true, next=n/a), call cron_retrigger to start a new run on the same job id — do not create a duplicate executor.',
|
||||
'When the user asks to look at, create, pause, resume, retrigger, or delete 定时任务 / scheduled tasks / cron jobs:',
|
||||
'1. If cron_list / cron_create / cron_pause / cron_resume / cron_retrigger / cron_delete / cron_query / cron_runs / cron_progress are in your tool list, call them.',
|
||||
'2. If they are not listed, load skill "scheduled-tasks" for usage guidance, then look again. skill_load does NOT inject tools — cron_* are registered by the dsh-ops-cron Host plugin at boot.',
|
||||
'3. If cron_* are still missing after a Host restart with the latest dsh-ops-cron, the plugin failed to register (check Host logs for unsupported JSON schema / tools.register). Tell the user; do not invent crontab workarounds.',
|
||||
'4. Never run crontab, never read /etc/cron*, and never say there are no tasks until cron_list has returned.',
|
||||
|
|
@ -786,7 +838,7 @@ Tools:
|
|||
- cron_runs — run history for a task_id
|
||||
- cron_progress — read progress.channel.file snapshots
|
||||
- cron_create — "一分钟后" → after_minutes=1. Clock time → hour+minute only. Do not send at/expr at the same time. Pass cwd as the current workspace path when creating from a workspace chat. Pass provider+model or inherit the current session model. Delivery and agent preset auto from session, or pass delivery=im / agent_preset explicitly. Session mirror is off by default; pass mirror_to_session=true only if the user wants the summary injected into the origin chat. Optional labels/watch/progress/report/persist_history for monitor workers and listeners.
|
||||
- cron_pause / cron_resume / cron_delete — by id from cron_list
|
||||
- cron_pause / cron_resume / cron_retrigger / cron_delete — by id from cron_list. Use cron_retrigger (not resume) to re-fire a consumed one-shot worker.
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue