UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

272 lines (271 loc) • 12.3 kB
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { r as getAsyncWorkSignal } from "./async-work-scope-DDiH0P7W.mjs"; import { _ as getSqlitePinnedReadSnapshot, h as runSqliteReadOperationSync, i as getAdmittedSqliteSchemaFacts, t as admitSqliteSchema, v as runSqlitePinnedReadSnapshotSync } from "./sqlite-schema-facts-BVf0E_0z.mjs"; import { r as assertExistingDatabaseIdentity } from "./sqlite-worker-identity-BRNRpRyc.mjs"; import { t as isPromiseLike } from "./promise-like-D7-l5Fsp.mjs"; import { i as SqliteCoordinatorError, s as throwSqliteLifecycleErrors } from "./sqlite-lifecycle-errors-DuOygjcn.mjs"; import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs"; import { t as assertTransactionUsable } from "./sqlite-transaction-DBRLvCDM.mjs"; import { n as existingPathOrUndefined } from "./openclaw-state-db.paths-CmKkMYdo.mjs"; import { l as getOpenClawDatabaseMaintenanceResourceScope, u as getOpenClawDatabaseMaintenanceScope } from "./openclaw-state-db-async-lifecycle-DwFy2ZTk.mjs"; import { C as retainOpenClawStateDatabaseForIndependentRead, b as requireOpenClawStateDatabaseIdentity, p as openClawStateDatabaseCache, r as captureOpenClawStateDatabaseReadAdmission, v as registerOpenClawStateDatabaseAsyncResource } from "./openclaw-state-db-cache-CmYLFRSt.mjs"; import { a as isExistingOpenClawStateSchema } from "./openclaw-state-db-schema-policy-B65l9xTe.mjs"; import { o as runWithSqliteWorkerStateContext } from "./sqlite-worker-state-context-G3Xbcj1W.mjs"; import { n as openOpenClawStateReadConnection, t as assertStateReadSchema } from "./openclaw-state-db-read-connection-B7Zi_byb.mjs"; import { a as withCurrentOpenClawStateReadScope, m as canReadWarmNativeSourceIndependently, t as executeExistingOpenClawStateRead } from "./openclaw-state-db-readonly-ywwvuxdQ.mjs"; import { t as createSqliteForeignObservation } from "./sqlite-foreign-observation-PXuD_HBR.mjs"; //#region src/state/openclaw-state-db-current-reader.ts const log = createSubsystemLogger("state/db"); const currentReaders = resolveGlobalSingleton(Symbol.for("openclaw.stateCurrentReaders"), () => /* @__PURE__ */ new Map()); /** One private live connection per physical store, independent of caller cursors. */ async function prepareOpenClawStateCurrentReader(context) { const pathname = context.admission.databasePath; const { key, canonicalPath, birthtime } = context.admission.identity; const signal = getAsyncWorkSignal(); const assertSource = () => { signal?.throwIfAborted(); getAsyncWorkSignal()?.throwIfAborted(); context.maintenanceScope?.assertReadAdmission(); context.admission.assertCurrent(); openClawStateDatabaseCache.assertOpenClawStateDatabaseOpenAllowed(pathname, "cached-read"); }; assertSource(); if (!key.startsWith("file:")) { if (existingPathOrUndefined(pathname)) throw new Error("Current shared-state reader source was created after capture"); return; } assertExistingDatabaseIdentity(pathname, key, birthtime); const physicalKey = `${key}:${birthtime ?? ""}`; const previous = currentReaders.get(physicalKey); if (previous?.retiring) previous.close(); if (!currentReaders.has(physicalKey)) { const admitted = await executeExistingOpenClawStateRead({ path: pathname, env: context.environment }, { type: "admit" }, { context, current: true, signal }); assertSource(); assertExistingDatabaseIdentity(pathname, key, birthtime); if (!admitted?.ok || admitted.type !== "admit") throw new Error("Current shared-state reader admission did not settle"); } let reader = currentReaders.get(physicalKey); if (!reader) { const connection = openOpenClawStateReadConnection(pathname, pathname, key); let unregister = () => {}; const opened = { connection, observation: createSqliteForeignObservation(connection.database.db, () => { if (opened.retiring) throw new Error("Current shared-state reader is closed"); assertExistingDatabaseIdentity(canonicalPath, key, birthtime); }), users: 0, retiring: false, closed: false, close() { if (opened.closed) return; opened.retiring = true; opened.observation.invalidate(); if (!connection.close()) throw new Error("Current shared-state reader cleanup is incomplete"); opened.closed = true; if (currentReaders.get(physicalKey) === opened) currentReaders.delete(physicalKey); unregister(); } }; try { unregister = registerOpenClawStateDatabaseAsyncResource({ async close(identity) { if (!identity || identity.key === key || identity.canonicalPath === canonicalPath) opened.close(); } }); } catch (error) { try { opened.close(); } catch (cleanupError) { throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader registration and cleanup failed"); } throw error; } currentReaders.set(physicalKey, opened); reader = opened; } const retained = reader; retained.users += 1; let active = true; const assertCurrent = () => { if (!active || retained.retiring || !retained.connection.database.db.isOpen) throw new Error("Current shared-state reader is closed"); assertSource(); const integrity = context.stateIntegrity; if (integrity && Atomics.load(new BigInt64Array(integrity.revision), 0) !== integrity.epoch || context.existingSchemaPath !== void 0 && (!integrity || Atomics.load(new BigInt64Array(integrity.proof), 0) === -1n)) throw new Error("Shared-state reader requires current worker integrity proof"); try { assertExistingDatabaseIdentity(pathname, key, birthtime); } catch (error) { retained.observation.invalidate(); throw error; } if (retained.connection.database.db.isTransaction || getSqlitePinnedReadSnapshot(retained.connection.database.db)) throw new Error("Current shared-state reader cannot retain a transaction or snapshot"); }; const release = () => { if (active) { active = false; retained.users -= 1; } if (retained.users === 0 && !retained.closed) retained.close(); }; const resource = { async close() { release(); } }; try { context.maintenanceScope?.own(resource, "shared-resources", () => resource.close()); assertCurrent(); return { createCertification() { assertCurrent(); return retained.observation.createCertification(assertCurrent); }, read(operation) { assertCurrent(); const read = () => runWithSqliteWorkerStateContext(context, () => runOpenClawStateCurrentReadConnection(retained.connection, operation, void 0, "require-proof")); const result = context.runInCapturedSchemaScope ? context.runInCapturedSchemaScope(read) : read(); assertCurrent(); return result; }, dispose() { try { release(); } catch (error) { log.warn("Current shared-state reader cleanup failed; retaining cleanup custody", { error }); } } }; } catch (error) { try { release(); } catch (cleanupError) { throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader admission and cleanup failed"); } throw error; } } /** Mutating maintenance can retain an independent current reader under its exact source lifetime. */ function createOpenClawStateCurrentWarmReader(operation, options, openStateSchemaReadAdmission) { let bound; return () => { return withCurrentOpenClawStateReadScope(options, (pathname) => { const scope = getOpenClawDatabaseMaintenanceScope(); if (!scope?.ownsSchemaMaintenance || bound && bound.scope !== scope) return { available: false }; if (bound && bound.path !== pathname) throw new Error("Current shared-state reader cannot change its database before cleanup"); if (!bound) { const native = openClawStateDatabaseCache.getCachedOpenClawStateDatabase(pathname, { readOnly: true }); if (!native?.db.isOpen) return { available: false }; const sourceScope = getOpenClawDatabaseMaintenanceResourceScope(native.db); if (sourceScope && sourceScope !== scope) return { available: false }; const identity = requireOpenClawStateDatabaseIdentity(native); if (!canReadWarmNativeSourceIndependently(native, pathname, identity.key)) return { available: false }; const admission = captureOpenClawStateDatabaseReadAdmission(pathname); const retained = retainOpenClawStateDatabaseForIndependentRead(pathname, "cached-read"); if (!retained) return { available: false }; let connection; try { connection = openOpenClawStateReadConnection(pathname, pathname, identity.key); } catch (error) { try { retained.release(); } catch (cleanupError) { throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader open and cleanup failed."); } throw error; } let active = true; let unregister = () => {}; const assertCurrent = () => { if (!active) throw new Error("Current shared-state reader is closed"); scope?.assertReadAdmission(); admission.assertCurrent(); retained.assertCurrent(); assertExistingDatabaseIdentity(pathname, identity.key); openClawStateDatabaseCache.assertOpenClawStateDatabaseFreshOpenAllowedAtPath(pathname, options.env ?? process.env); }; const reader = { path: pathname, scope, read() { assertCurrent(); const result = runOpenClawStateCurrentReadConnection(connection, operation, openStateSchemaReadAdmission); assertCurrent(); retained.observe(); return result; } }; const resource = { async close(closingIdentity) { if (!active || closingIdentity && closingIdentity.key !== identity.key && closingIdentity.canonicalPath !== identity.canonicalPath) return; connection.close(); retained.release(); active = false; if (bound === reader) bound = void 0; unregister(); } }; unregister = registerOpenClawStateDatabaseAsyncResource(resource); scope?.own(resource, "shared-resources", () => resource.close()); bound = reader; } return { available: true, value: bound.read() }; }); }; } const currentReaderSchemaAdmissions = /* @__PURE__ */ new WeakMap(); /** An independent current reader keeps composite policy rows in one bounded snapshot. */ function runOpenClawStateCurrentReadConnection(connection, operation, openStateSchemaReadAdmission, integrityPolicy) { const { db, path: pathname } = connection.database; let closeAdmission; const errors = []; let result; try { const previous = currentReaderSchemaAdmissions.get(db); const facts = previous && !previous.legacyAdmission ? runSqliteReadOperationSync(db, () => getAdmittedSqliteSchemaFacts(db)) : void 0; if (!previous || previous.admission !== openStateSchemaReadAdmission || previous.legacyAdmission || previous.facts !== facts) closeAdmission = openStateSchemaReadAdmission?.(db); const existingSchema = isExistingOpenClawStateSchema(pathname, db); const admit = () => { const current = getAdmittedSqliteSchemaFacts(db); const accepted = currentReaderSchemaAdmissions.get(db); if (!current || accepted?.facts !== current || accepted.existingSchema !== existingSchema || accepted.admission !== openStateSchemaReadAdmission) { assertStateReadSchema(db, pathname, integrityPolicy); admitSqliteSchema(db); const admitted = getAdmittedSqliteSchemaFacts(db); if (!admitted) throw new Error("Current shared-state reader could not retain schema admission"); currentReaderSchemaAdmissions.set(db, { facts: admitted, existingSchema, admission: openStateSchemaReadAdmission, legacyAdmission: closeAdmission !== void 0 }); } }; runSqliteReadOperationSync(db, admit); result = runSqlitePinnedReadSnapshotSync(db, () => { const value = operation(connection.database); if (isPromiseLike(value)) throw new SqliteCoordinatorError("SQLite current-authority read must remain synchronous"); return value; }); runSqliteReadOperationSync(db, admit); } catch (error) { errors.push(error); } try { closeAdmission?.(); } catch (error) { errors.push(error); } try { assertTransactionUsable(db); } catch (error) { errors.push(error); } throwSqliteLifecycleErrors(errors, "Current shared-state read and schema cleanup failed."); return result; } //#endregion export { prepareOpenClawStateCurrentReader as n, createOpenClawStateCurrentWarmReader as t };