import { normalizeCronJobCreate, normalizeCronJobPatch, } from "../../cron/normalize.js"; import { readCronRunLogEntries, resolveCronRunLogPath, } from "../../cron/run-log.js"; import type { CronJobCreate, CronJobPatch } from "../../cron/types.js"; import { ErrorCodes, errorShape, formatValidationErrors, validateCronAddParams, validateCronListParams, validateCronRemoveParams, validateCronRunParams, validateCronRunsParams, validateCronStatusParams, validateCronUpdateParams, validateWakeParams, } from "../protocol/index.js"; import type { GatewayRequestHandlers } from "./types.js"; export const cronHandlers: GatewayRequestHandlers = { wake: ({ params, respond, context }) => { if (!validateWakeParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid wake params: ${formatValidationErrors(validateWakeParams.errors)}`, ), ); return; } const p = params as { mode: "now" | "next-heartbeat"; text: string; }; const result = context.cron.wake({ mode: p.mode, text: p.text }); respond(true, result, undefined); }, "cron.list": async ({ params, respond, context }) => { if (!validateCronListParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.list params: ${formatValidationErrors(validateCronListParams.errors)}`, ), ); return; } const p = params as { includeDisabled?: boolean }; const jobs = await context.cron.list({ includeDisabled: p.includeDisabled, }); respond(true, { jobs }, undefined); }, "cron.status": async ({ params, respond, context }) => { if (!validateCronStatusParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.status params: ${formatValidationErrors(validateCronStatusParams.errors)}`, ), ); return; } const status = await context.cron.status(); respond(true, status, undefined); }, "cron.add": async ({ params, respond, context }) => { const normalized = normalizeCronJobCreate(params) ?? params; if (!validateCronAddParams(normalized)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.add params: ${formatValidationErrors(validateCronAddParams.errors)}`, ), ); return; } const job = await context.cron.add(normalized as unknown as CronJobCreate); respond(true, job, undefined); }, "cron.update": async ({ params, respond, context }) => { const normalizedPatch = normalizeCronJobPatch( (params as { patch?: unknown } | null)?.patch, ); const candidate = normalizedPatch && typeof params === "object" && params !== null ? { ...(params as Record), patch: normalizedPatch } : params; if (!validateCronUpdateParams(candidate)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.update params: ${formatValidationErrors(validateCronUpdateParams.errors)}`, ), ); return; } const p = candidate as { id: string; patch: Record; }; const job = await context.cron.update( p.id, p.patch as unknown as CronJobPatch, ); respond(true, job, undefined); }, "cron.remove": async ({ params, respond, context }) => { if (!validateCronRemoveParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.remove params: ${formatValidationErrors(validateCronRemoveParams.errors)}`, ), ); return; } const p = params as { id: string }; const result = await context.cron.remove(p.id); respond(true, result, undefined); }, "cron.run": async ({ params, respond, context }) => { if (!validateCronRunParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.run params: ${formatValidationErrors(validateCronRunParams.errors)}`, ), ); return; } const p = params as { id: string; mode?: "due" | "force" }; const result = await context.cron.run(p.id, p.mode); respond(true, result, undefined); }, "cron.runs": async ({ params, respond, context }) => { if (!validateCronRunsParams(params)) { respond( false, undefined, errorShape( ErrorCodes.INVALID_REQUEST, `invalid cron.runs params: ${formatValidationErrors(validateCronRunsParams.errors)}`, ), ); return; } const p = params as { id: string; limit?: number }; const logPath = resolveCronRunLogPath({ storePath: context.cronStorePath, jobId: p.id, }); const entries = await readCronRunLogEntries(logPath, { limit: p.limit, jobId: p.id, }); respond(true, { entries }, undefined); }, };