UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

168 lines (167 loc) • 6.22 kB
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs"; import { c as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-CmKkMYdo.mjs"; import { f as hasRevokedOpenClawAgentDatabaseValidation } from "./openclaw-agent-db-validation-cache-CHrtutWN.mjs"; import path from "node:path"; import { AsyncLocalStorage } from "node:async_hooks"; //#region src/state/openclaw-database-verify.ts const log = createSubsystemLogger("state/database-verify"); const integrityCheckQueues = resolveGlobalSingleton(Symbol.for("openclaw.databaseIntegrityChecks"), () => /* @__PURE__ */ new Map()); function integrityCheckQueue(env) { const key = path.resolve(resolveOpenClawStateSqlitePath(env)); let queue = integrityCheckQueues.get(key); if (!queue) { queue = { paths: /* @__PURE__ */ new Map(), subscribers: /* @__PURE__ */ new Set(), releases: /* @__PURE__ */ new Set() }; integrityCheckQueues.set(key, queue); } return queue; } /** Deferral belongs to a running verifier, never a queue without a consumer. */ function captureOpenClawDatabaseIntegrityVerifier(database) { if (hasRevokedOpenClawAgentDatabaseValidation(database.databasePath)) return; const queue = integrityCheckQueues.get(path.resolve(resolveOpenClawStateSqlitePath(database.environment))); const owner = queue?.subscribers.values().next().value; return owner && queue ? () => { if (!queue.subscribers.has(owner)) throw new Error("Agent admission lost its background integrity verifier"); } : void 0; } function wakeSubscribers(queue) { for (const wake of queue.subscribers) wake(); } function enqueueCheck(queue, pathname, request) { if (request.check === "full" || queue.paths.get(pathname)?.check !== "full") { const previous = queue.paths.get(pathname); queue.paths.set(pathname, request); if (previous) releaseCheck(queue, previous); } else releaseCheck(queue, request); } function releaseCheck(queue, request) { const release = request.release; request.release = void 0; if (!release) return; const pending = release().catch((error) => { log.error("database integrity verification cleanup failed", { error: String(error) }); }); queue.releases.add(pending); pending.finally(() => queue.releases.delete(pending)); } /** Admitted opens queue work; only the listening Gateway starts the verifier. */ function requestOpenClawAgentDatabaseIntegrityCheck(options) { const queue = integrityCheckQueue(options.env); enqueueCheck(queue, path.resolve(options.path), { check: options.check, proof: options.proof, release: options.release }); wakeSubscribers(queue); } /** Consume requested agent checks for the listening Gateway. */ function startOpenClawDatabaseIntegrityVerifier(options) { const env = { ...options.env }; const queue = integrityCheckQueue(env); const owner = {}; const inOwnerContext = AsyncLocalStorage.snapshot(); let activeWorker; let activeRun; let claimedChecks = /* @__PURE__ */ new Map(); let stopPromise; let stopped = false; const controller = new AbortController(); let timer; const workerLifetime = { onWorker: (worker) => { activeWorker = worker; }, assertCurrent: () => { if (stopped) throw new Error("database integrity verifier stopped"); } }; const schedule = () => { if (stopped || queue.active || queue.paths.size === 0) return; if (timer) clearTimeout(timer); timer = setTimeout(() => { timer = void 0; if (stopped || queue.active || queue.paths.size === 0) return; queue.active = owner; activeRun = inOwnerContext(run).finally(() => { activeRun = void 0; queue.active = void 0; wakeSubscribers(queue); }); }, 0); timer.unref?.(); }; const run = async () => { const checks = new Map(queue.paths); claimedChecks = checks; queue.paths.clear(); try { const { applyOpenClawDatabaseVerificationResults, runDatabaseVerifyWorker } = await import("./openclaw-database-verify.impl-CWLbdhUw.mjs"); if (stopped) return; const targets = Array.from(checks, ([pathname, request]) => ({ kind: "agent", label: "OpenClaw agent database", path: pathname, check: request.check, ...request.proof ? { identity: request.proof.identity } : {} })); const results = await runDatabaseVerifyWorker(targets, workerLifetime); if (!stopped) await applyOpenClawDatabaseVerificationResults({ env, results, targets, workerLifetime, onVerified: async (pathname) => { const request = checks.get(pathname); if (request?.check === "full") return request.proof?.complete(workerLifetime.assertCurrent, controller.signal); } }); } catch (error) { if (!stopped) log.error("database integrity verifier failed", { error: String(error) }); } finally { activeWorker = void 0; for (const [pathname, check] of checks) if (queue.paths.get(pathname) !== check) releaseCheck(queue, check); claimedChecks.clear(); await Promise.all(queue.releases); } }; const wake = () => inOwnerContext(schedule); queue.subscribers.add(wake); wake(); return { stop: () => { if (stopPromise) return stopPromise; stopped = true; controller.abort(/* @__PURE__ */ new Error("database integrity verifier stopped")); queue.subscribers.delete(wake); if (queue.subscribers.size === 0) { for (const check of queue.paths.values()) releaseCheck(queue, check); queue.paths.clear(); } else { for (const [pathname, check] of claimedChecks) if (queue.paths.get(pathname)?.check !== "full") enqueueCheck(queue, pathname, check); wakeSubscribers(queue); } if (timer) { clearTimeout(timer); timer = void 0; } stopPromise = (async () => { try { const worker = activeWorker; if (worker) { const { terminateDatabaseVerifyWorker } = await import("./openclaw-database-verify.impl-CWLbdhUw.mjs"); await terminateDatabaseVerifyWorker(worker); } } finally { await activeRun; await Promise.all(queue.releases); } })(); return stopPromise; } }; } //#endregion export { requestOpenClawAgentDatabaseIntegrityCheck as n, startOpenClawDatabaseIntegrityVerifier as r, captureOpenClawDatabaseIntegrityVerifier as t };