feat: claude one-shot port from nanobot python codebase (v0.1.4.post4)

This commit is contained in:
Joe Fleming
2026-03-13 08:58:43 -06:00
parent 37c66a1bbf
commit f0252e663e
52 changed files with 5002 additions and 7 deletions

122
src/agent/tools/cron.ts Normal file
View File

@@ -0,0 +1,122 @@
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)}`;
}
}
}