UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

138 lines (137 loc) 6.35 kB
import { r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js"; import { r as withExistingOpenClawStateDatabaseReadOnly } from "./openclaw-state-db-readonly-BRgmrGHt.js"; import { i as openOpenClawStateDatabase, s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js"; import { p as getCronStoreKysely } from "./row-codec-Bv7_IRNO.js"; import { t as cronStoreKey } from "./key-BBZ40bDq.js"; import { n as assertCronJobScratchContent } from "./scratch-contract-B-Laspel.js"; import { createHash } from "node:crypto"; //#region src/cron/scratch-store.ts /** Database-backed per-job scratch storage, kept outside public cron job state. */ function rowToState(row) { if (row.content === null) return { currentRevision: row.revision }; return { currentRevision: row.revision, scratch: { content: row.content, revision: row.revision, ...row.source_sha256 ? { sourceSha256: row.source_sha256 } : {}, updatedAtMs: row.updated_at_ms } }; } function readScratchStateFromDatabase(db, storeKey, jobId) { const cronDb = getCronStoreKysely(db); const row = executeSqliteQuerySync(db, cronDb.selectFrom("cron_job_scratch").select([ "content", "revision", "source_sha256", "updated_at_ms" ]).where("store_key", "=", storeKey).where("job_id", "=", jobId)).rows[0]; return row ? rowToState(row) : { currentRevision: 0 }; } /** Reads one job's scratch state without exposing it through cron list/history surfaces. */ function readCronJobScratchState(storePath, jobId, options = {}) { const { db } = openOpenClawStateDatabase(options); return readScratchStateFromDatabase(db, cronStoreKey(storePath), jobId); } function readHeartbeatMonitorScratchFromDatabase(db, storePath, agentId) { const storeKey = cronStoreKey(storePath); const cronDb = getCronStoreKysely(db); const row = executeSqliteQuerySync(db, cronDb.selectFrom("cron_jobs").leftJoin("cron_job_scratch", (join) => join.onRef("cron_job_scratch.store_key", "=", "cron_jobs.store_key").onRef("cron_job_scratch.job_id", "=", "cron_jobs.job_id")).select([ "cron_jobs.job_id as job_id", "cron_job_scratch.content as content", "cron_job_scratch.revision as revision", "cron_job_scratch.source_sha256 as source_sha256", "cron_job_scratch.updated_at_ms as updated_at_ms" ]).where("cron_jobs.store_key", "=", storeKey).where("cron_jobs.declaration_key", "=", `heartbeat:${agentId}`).where("cron_jobs.payload_kind", "=", "heartbeat")).rows[0]; if (!row) return; if (row.revision === null || row.updated_at_ms === null) return { jobId: row.job_id, state: { currentRevision: 0 } }; return { jobId: row.job_id, state: rowToState({ content: row.content, revision: row.revision, source_sha256: row.source_sha256, updated_at_ms: row.updated_at_ms }) }; } /** Resolves the current heartbeat monitor and its scratch with one narrow SQLite query. */ function readHeartbeatMonitorScratch(storePath, agentId, options = {}) { const { db } = openOpenClawStateDatabase(options); return readHeartbeatMonitorScratchFromDatabase(db, storePath, agentId); } /** Reads heartbeat scratch from existing shared state without creating or migrating it. */ function readHeartbeatMonitorScratchReadOnly(storePath, agentId, options = {}) { return withExistingOpenClawStateDatabaseReadOnly(({ db }) => readHeartbeatMonitorScratchFromDatabase(db, storePath, agentId), options); } /** Writes, clears, or compare-and-swaps one scratch row. */ function writeCronJobScratch(params) { if (params.content !== null) assertCronJobScratchContent(params.content); const storeKey = cronStoreKey(params.storePath); const nowMs = params.nowMs ?? Date.now(); return runOpenClawStateWriteTransaction(({ db }) => { const cronDb = getCronStoreKysely(db); const { currentRevision } = readScratchStateFromDatabase(db, storeKey, params.jobId); if (!executeSqliteQuerySync(db, cronDb.selectFrom("cron_jobs").select("job_id").where("store_key", "=", storeKey).where("job_id", "=", params.jobId)).rows[0] || params.expectedRevision !== void 0 && params.expectedRevision !== currentRevision) return { ok: false, reason: "revision-conflict", currentRevision }; if (params.content === null && currentRevision === 0) return { ok: true, currentRevision }; const revision = currentRevision + 1; const sourceSha256 = params.content !== null ? params.sourceSha256?.trim() : void 0; if (currentRevision > 0) executeSqliteQuerySync(db, cronDb.deleteFrom("cron_job_scratch").where("store_key", "=", storeKey).where("job_id", "=", params.jobId)); executeSqliteQuerySync(db, cronDb.insertInto("cron_job_scratch").values({ store_key: storeKey, job_id: params.jobId, content: params.content, revision, ...sourceSha256 ? { source_sha256: sourceSha256 } : {}, updated_at_ms: nowMs })); if (params.content === null) return { ok: true, currentRevision: revision }; return { ok: true, currentRevision: revision, scratch: { content: params.content, revision, ...sourceSha256 ? { sourceSha256 } : {}, updatedAtMs: nowMs } }; }, params.options, { operationLabel: "cron.scratch.write" }); } /** * Deletes scratch when its owning job is removed, or — with expectedRevision — * atomically reverts a migration write back to the no-row state. Returns false * when the guarded revision moved. */ function deleteCronJobScratch(storePath, jobId, options = {}, guard) { return runOpenClawStateWriteTransaction(({ db }) => { const storeKey = cronStoreKey(storePath); if (guard) { const { currentRevision } = readScratchStateFromDatabase(db, storeKey, jobId); if (currentRevision !== guard.expectedRevision) return false; } const cronDb = getCronStoreKysely(db); executeSqliteQuerySync(db, cronDb.deleteFrom("cron_job_scratch").where("store_key", "=", storeKey).where("job_id", "=", jobId)); return true; }, options, { operationLabel: "cron.scratch.delete" }); } /** Hash used by doctor to prove the file it removes is the file it migrated. */ function hashCronScratchSource(content) { return createHash("sha256").update(content, "utf8").digest("hex"); } //#endregion export { readHeartbeatMonitorScratchReadOnly as a, readHeartbeatMonitorScratch as i, hashCronScratchSource as n, writeCronJobScratch as o, readCronJobScratchState as r, deleteCronJobScratch as t };