mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(scheduler): add cron-style scheduled jobs (backend)
Adds a saved/named scheduling layer on top of Codeman's existing session primitives. Distinct from the legacy run-now ScheduledRun concept. - types/scheduler.ts: ScheduledJob + ScheduledJobRun - state-store: persist scheduledJobs/scheduledJobRuns in ~/.codeman/state.json - scheduler/scheduler-time.ts: pure once/interval/daily/weekly next-run math - scheduler/scheduler-service.ts: CRUD, Run Now, due-checker tick, run history; reuses SessionPort (create -> start -> writeViaMux) for launches - web/routes/scheduler-routes.ts: /api/scheduler/jobs CRUD + run + history - web/schemas.ts: zod validation with schedule-type-aware refinements - web/sse-events.ts: scheduler:* events - server.ts: wire service into route context + 30s background tick loop - test/scheduler-time.test.ts: 14 unit tests for next-run calculations Phase 1 discovery recorded in SCHEDULER_DISCOVERY.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rp7JhmQXcYJhmxFMdZuuah
This commit is contained in:
@@ -272,6 +272,12 @@ export class StateStore {
|
||||
if (this.state.tokenStats) {
|
||||
parts.push(`"tokenStats":${JSON.stringify(this.state.tokenStats)}`);
|
||||
}
|
||||
if (this.state.scheduledJobs) {
|
||||
parts.push(`"scheduledJobs":${JSON.stringify(this.state.scheduledJobs)}`);
|
||||
}
|
||||
if (this.state.scheduledJobRuns) {
|
||||
parts.push(`"scheduledJobRuns":${JSON.stringify(this.state.scheduledJobRuns)}`);
|
||||
}
|
||||
|
||||
return `{${parts.join(',')}}`;
|
||||
}
|
||||
@@ -568,6 +574,51 @@ export class StateStore {
|
||||
this.save();
|
||||
}
|
||||
|
||||
// ========== Scheduled Job Methods (cron-style scheduler) ==========
|
||||
|
||||
/** Returns all scheduled jobs keyed by job ID. */
|
||||
getScheduledJobs(): Record<string, import('./types/scheduler.js').ScheduledJob> {
|
||||
if (!this.state.scheduledJobs) this.state.scheduledJobs = {};
|
||||
return this.state.scheduledJobs;
|
||||
}
|
||||
|
||||
/** Returns a scheduled job by ID, or null if not found. */
|
||||
getScheduledJob(id: string): import('./types/scheduler.js').ScheduledJob | null {
|
||||
return this.state.scheduledJobs?.[id] ?? null;
|
||||
}
|
||||
|
||||
/** Sets a scheduled job and triggers a debounced save. */
|
||||
setScheduledJob(id: string, job: import('./types/scheduler.js').ScheduledJob): void {
|
||||
if (!this.state.scheduledJobs) this.state.scheduledJobs = {};
|
||||
this.state.scheduledJobs[id] = job;
|
||||
this.save();
|
||||
}
|
||||
|
||||
/** Removes a scheduled job and triggers a debounced save. */
|
||||
removeScheduledJob(id: string): void {
|
||||
if (this.state.scheduledJobs) delete this.state.scheduledJobs[id];
|
||||
this.save();
|
||||
}
|
||||
|
||||
/** Returns all scheduled job runs keyed by run ID. */
|
||||
getScheduledJobRuns(): Record<string, import('./types/scheduler.js').ScheduledJobRun> {
|
||||
if (!this.state.scheduledJobRuns) this.state.scheduledJobRuns = {};
|
||||
return this.state.scheduledJobRuns;
|
||||
}
|
||||
|
||||
/** Sets a scheduled job run (history record) and triggers a debounced save. */
|
||||
setScheduledJobRun(id: string, run: import('./types/scheduler.js').ScheduledJobRun): void {
|
||||
if (!this.state.scheduledJobRuns) this.state.scheduledJobRuns = {};
|
||||
this.state.scheduledJobRuns[id] = run;
|
||||
this.save();
|
||||
}
|
||||
|
||||
/** Removes a scheduled job run and triggers a debounced save. */
|
||||
removeScheduledJobRun(id: string): void {
|
||||
if (this.state.scheduledJobRuns) delete this.state.scheduledJobRuns[id];
|
||||
this.save();
|
||||
}
|
||||
|
||||
/** Returns the application configuration. */
|
||||
getConfig() {
|
||||
return this.state.config;
|
||||
|
||||
Reference in New Issue
Block a user