UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

111 lines (110 loc) 5.75 kB
import { f as asSafeIntegerInRange, t as MAX_DATE_TIMESTAMP_MS } from "./number-coercion-CLj0HTDM.js"; import { o as asRecord } from "./record-coerce-DItp3I4t.js"; import { t as compileSafeRegex } from "./safe-regex-BQnlv3bA.js"; import { t as parseAbsoluteTimeMs } from "./parse-tlCSjrTr.js"; import { t as isSystemOwnedCronPayloadKind } from "./types-DQrueG6d.js"; //#region src/cron/persisted-shape.ts /** Validates persisted cron job records before loading them from disk/state. */ const CRON_STATE_TIMESTAMP_FIELDS = [ "nextRunAtMs", "scheduleActivatedAtMs", "startupCatchupAtMs", "pacedNextRunAtMs", "forcePreservedNextRunAtMs", "queuedAtMs", "runningAtMs", "lastRunAtMs", "lastFailureAlertAtMs", "lastTriggerEvalAtMs", "lastTriggerFireAtMs", "streamLastStartedAtMs", "streamLastExitAtMs" ]; function isValidStateTimestamp(value) { return asSafeIntegerInRange(value, { min: 0, max: MAX_DATE_TIMESTAMP_MS }) !== void 0; } function getInvalidCronJobStateTimestampField(state) { const record = asRecord(state); const field = CRON_STATE_TIMESTAMP_FIELDS.find((key) => record[key] !== void 0 && !isValidStateTimestamp(record[key])); if (field) return field; const atMs = asRecord(record.autoDisabled).atMs; return atMs !== void 0 && !isValidStateTimestamp(atMs) ? "autoDisabled.atMs" : void 0; } /** Rejects caller-authored state timestamps that cannot round-trip through Date and SQLite. */ function assertCronJobStateTimestamps(state) { const invalidField = getInvalidCronJobStateTimestampField(state); if (invalidField) throw new Error(`cron state.${invalidField} must be a non-negative Date-valid integer timestamp`); } /** Returns the first structural reason a persisted cron job cannot be loaded safely. */ function getInvalidPersistedCronJobReason(candidate) { const id = candidate.id; if (typeof id !== "string" || !id.trim()) return "missing-id"; if (getInvalidCronJobStateTimestampField(candidate.state)) return "invalid-state"; const schedule = candidate.schedule; if (!schedule || Array.isArray(schedule)) return "missing-schedule"; const legacySchedule = typeof schedule === "string"; if (!legacySchedule && typeof schedule !== "object") return "missing-schedule"; const scheduleRecord = asRecord(schedule); const scheduleKind = scheduleRecord.kind; if (!legacySchedule && scheduleKind !== "at" && scheduleKind !== "every" && scheduleKind !== "cron" && scheduleKind !== "on-exit" && scheduleKind !== "stream") return "invalid-schedule"; if (scheduleKind === "at") { const at = scheduleRecord.at; if (typeof at !== "string" || parseAbsoluteTimeMs(at) === null) return "invalid-schedule"; } if (scheduleKind === "every") { const everyMs = scheduleRecord.everyMs; const anchorMs = scheduleRecord.anchorMs; if (asSafeIntegerInRange(everyMs, { min: 1, max: 864e13 }) === void 0 || anchorMs !== void 0 && asSafeIntegerInRange(anchorMs, { min: 0, max: 864e13 }) === void 0) return "invalid-schedule"; } if (scheduleKind === "cron") { const expr = scheduleRecord.expr; const staggerMs = scheduleRecord.staggerMs; if (typeof expr !== "string" || expr.trim().length === 0 || staggerMs !== void 0 && asSafeIntegerInRange(staggerMs, { min: 0, max: 864e13 }) === void 0) return "invalid-schedule"; } if (scheduleKind === "on-exit") { const command = scheduleRecord.command; if (typeof command !== "string" || command.trim().length === 0) return "invalid-schedule"; } if (scheduleKind === "stream") { const command = scheduleRecord.command; const mode = scheduleRecord.mode ?? "line"; const batchFieldValid = (value) => value === void 0 || asSafeIntegerInRange(value, {}) !== void 0; if (!Array.isArray(command) || command.length === 0 || command.some((value) => typeof value !== "string" || value.length === 0) || mode !== "line" && mode !== "match" || mode === "match" && typeof scheduleRecord.match !== "string" || mode === "line" && scheduleRecord.match !== void 0 || !batchFieldValid(scheduleRecord.batchMs) || !batchFieldValid(scheduleRecord.maxBatchBytes)) return "invalid-schedule"; if (mode === "match") { if (!compileSafeRegex(scheduleRecord.match)) return "invalid-schedule"; } } if ("trigger" in candidate) { const trigger = candidate.trigger; if (!trigger || typeof trigger !== "object" || Array.isArray(trigger)) return "invalid-trigger"; const script = trigger.script; if (typeof script !== "string" || script.trim().length === 0 || scheduleKind === "at" || scheduleKind === "on-exit") return "invalid-trigger"; } const payload = candidate.payload; if (!payload || typeof payload !== "object" || Array.isArray(payload)) return "missing-payload"; const payloadRecord = payload; const payloadKind = payloadRecord.kind; if (payloadKind !== "systemEvent" && payloadKind !== "agentTurn" && payloadKind !== "command" && payloadKind !== "script" && !isSystemOwnedCronPayloadKind(payloadKind)) return "invalid-payload"; const requiredText = payloadKind === "systemEvent" ? payloadRecord.text : payloadKind === "agentTurn" ? payloadRecord.message : payloadKind === "script" ? payloadRecord.script : void 0; if ((payloadKind === "systemEvent" || payloadKind === "agentTurn" || payloadKind === "script") && (typeof requiredText !== "string" || payloadKind !== "systemEvent" && !requiredText.trim())) return "invalid-payload"; if (payloadKind === "command") { const argv = payloadRecord.argv; if (!Array.isArray(argv) || argv.length === 0 || argv.some((value) => typeof value !== "string" || value.length === 0)) return "invalid-payload"; if (scheduleKind === "stream") return "invalid-payload"; } return null; } //#endregion export { getInvalidPersistedCronJobReason as n, assertCronJobStateTimestamps as t };