openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
168 lines (167 loc) • 6.22 kB
JavaScript
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 };