import type { CronService } from '../../cron/service.ts'; import type { CronJob, CronPayload, CronSchedule } from '../../cron/types.ts'; import { strArg } from './base.ts'; import type { Tool } from './base.ts'; export class CronTool implements Tool { readonly name = 'cron'; readonly description = `Manage scheduled tasks (cron jobs). Actions: - list: list all jobs - add: create a new job (requires name, schedule, message) - remove: delete a job by id - enable/disable: toggle a job - run: execute a job immediately - status: summary of all jobs`; readonly parameters = { action: { type: 'string', enum: ['list', 'add', 'remove', 'enable', 'disable', 'run', 'status'], description: 'Action to perform.', }, id: { type: 'string', description: 'Job ID (for remove/enable/disable/run).' }, name: { type: 'string', description: 'Human-readable job name (for add).' }, message: { type: 'string', description: 'Message to inject when the job runs (for add).' }, schedule: { type: 'object', description: 'Schedule definition. One of: {kind:"at",atMs:number}, {kind:"every",everyMs:number}, {kind:"cron",expr:string,tz?:string}', }, deleteAfterRun: { type: 'boolean', description: 'Delete job after first execution (for add).' }, }; readonly required = ['action']; private _service: CronService; constructor(service: CronService) { this._service = service; } async execute(args: Record): Promise { const action = strArg(args, 'action'); switch (action) { case 'list': return this._list(); case 'status': return this._service.status(); case 'add': return this._add(args); case 'remove': { const id = strArg(args, 'id'); if (!id) return 'Error: id is required for remove.'; return this._service.removeJob(id) ? `Job ${id} removed.` : `Error: job ${id} not found.`; } case 'enable': { const id = strArg(args, 'id'); if (!id) return 'Error: id is required for enable.'; return this._service.enableJob(id, true) ? `Job ${id} enabled.` : `Error: job ${id} not found.`; } case 'disable': { const id = strArg(args, 'id'); if (!id) return 'Error: id is required for disable.'; return this._service.enableJob(id, false) ? `Job ${id} disabled.` : `Error: job ${id} not found.`; } case 'run': { const id = strArg(args, 'id'); if (!id) return 'Error: id is required for run.'; return this._service.runJob(id); } default: return `Error: unknown action "${action}". Valid: list, add, remove, enable, disable, run, status.`; } } private _list(): string { const jobs = this._service.listJobs(); if (jobs.length === 0) return 'No cron jobs.'; return jobs .map((j: CronJob) => { const schedule = this._fmtSchedule(j.schedule); const next = j.state.nextRunAtMs ? new Date(j.state.nextRunAtMs).toISOString() : 'N/A'; return `${j.id} [${j.enabled ? 'ON' : 'OFF'}] "${j.name}" ${schedule} next=${next}`; }) .join('\n'); } private _fmtSchedule(s: CronSchedule): string { if (s.kind === 'at') return `at ${new Date(s.atMs).toISOString()}`; if (s.kind === 'every') return `every ${s.everyMs}ms`; return `cron(${s.expr}${s.tz ? ` ${s.tz}` : ''})`; } private _add(args: Record): string { const name = strArg(args, 'name').trim(); const message = strArg(args, 'message').trim(); if (!name) return 'Error: name is required for add.'; if (!message) return 'Error: message is required for add.'; const rawSchedule = args['schedule']; if (!rawSchedule || typeof rawSchedule !== 'object') { return 'Error: schedule is required for add. Format: {kind:"cron",expr:"0 9 * * *"} or {kind:"every",everyMs:60000} or {kind:"at",atMs:1234567890}'; } const id = `job_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`; const payload: CronPayload = { kind: 'agent_turn', message, deliver: false }; const schedule = rawSchedule as CronSchedule; try { const job = this._service.addJob({ id, name, enabled: true, schedule, payload, deleteAfterRun: Boolean(args['deleteAfterRun']), }); return `Job created: ${job.id} "${job.name}"`; } catch (err) { return `Error creating job: ${String(err)}`; } } }