123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
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<string, unknown>): Promise<string> {
|
|
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, unknown>): 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)}`;
|
|
}
|
|
}
|
|
}
|