UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

451 lines (450 loc) • 23.3 kB
import { a as resolveGlobalSingleton } from "../global-singleton-0m-JziJW.mjs"; import { c as isRecord } from "../record-coerce-TIsquoVR.mjs"; import { t as drainProcessOutput } from "../output-drain-DMosb1D5.mjs"; import { c as withSqliteReaderOwner } from "../sqlite-reader-lifecycle-CF8Y2ajA.mjs"; import { n as assertDatabasePathIdentity, r as assertExistingDatabaseIdentity } from "../sqlite-worker-identity-BRNRpRyc.mjs"; import { t as captureSqliteNativeRuntimeAdmission } from "../node-sqlite-DyeJqxPf.mjs"; import { t as isPromiseLike } from "../promise-like-D7-l5Fsp.mjs"; import { r as runWithSqliteBusyTimeout } from "../sqlite-busy-timeout-D3v1uB5d.mjs"; import { a as isSqliteLockError } from "../sqlite-error-diagnostics-CWRyv4lz.mjs"; import { n as flushLogger } from "../logger-BhL9CiBr.mjs"; import { i as routeLogsToStderr } from "../console-CI_QNGt_.mjs"; import { c as withSqlitePostCommitPublications } from "../sqlite-post-commit-DENvzR6w.mjs"; import { o as runSqliteReservedTransactionSync, t as assertTransactionUsable } from "../sqlite-transaction-DBRLvCDM.mjs"; import { n as createSqliteWorkerTransferOwner, r as createSqliteWorkerTransferReceiver } from "../sqlite-worker-transfer-gbln6DQ8.mjs"; import { t as encodeOpenClawStateWorkerError } from "../openclaw-state-worker-error-Bi-BnNrV.mjs"; import { a as SQLITE_WORKER_PREPARE_ADMITTED, c as SqliteWorkerError, i as SQLITE_WORKER_OPERATION_CLEANUP, o as SQLITE_WORKER_PREPARE_COMMAND, t as SQLITE_WORKER_CLOSE_RECEIPT } from "../sqlite-worker-contract-C51cJHzg.mjs"; import { d as withSqliteWorkerOperationAdmission, f as withSqliteWorkerSourceReservations, l as settleSqliteWorkerOperationContext, n as assertSqliteWorkerCommitReceiptPending, s as requestSqliteWorkerOperationAdmission, t as SqliteWorkerOpenRefusedError, u as takeSqliteWorkerOperationAdmissionAttachment } from "../sqlite-worker-operation-admission-C5lBzzTk.mjs"; import { o as runWithSqliteWorkerStateContext } from "../sqlite-worker-state-context-G3Xbcj1W.mjs"; import { n as scheduleWorkerIdleGc, t as cancelWorkerIdleGc } from "../worker-idle-gc-N1Ao9aRg.mjs"; import { t as ownedWorkerBytes } from "../worker-transfer-bytes-D_DP0IHa.mjs"; import { deserialize, serialize } from "node:v8"; import { isMainThread, parentPort, workerData } from "node:worker_threads"; import { setTimeout } from "node:timers/promises"; import { isPromise } from "node:util/types"; //#region src/infra/sqlite-source-fence-contract.ts const SQLITE_WORKER_SOURCE_FENCE = Symbol.for("openclaw.sqliteWorkerSourceFence"); //#endregion //#region src/infra/sqlite-source-fence.ts const bindings = resolveGlobalSingleton(Symbol.for("openclaw.sqliteSourceFenceBindings"), () => /* @__PURE__ */ new WeakMap()); /** Bind once after native admission; later uses carry the captured physical facts. */ function bindSqliteSourceFenceDatabase({ database, identity }) { const previous = bindings.get(database); if (previous) { if (!sameIdentity(identity, previous)) throw new SqliteWorkerError("SQLite source fence handle changed incarnation", "closed"); return; } const location = database.location(); if (isMainThread || !location || !identity.physical.key.startsWith("file:") || !identity.incarnation) throw new Error("SQLite source fences require admitted durable worker handles"); assertDatabasePathIdentity(location, identity.physical); bindings.set(database, structuredClone(identity)); } function sameIdentity(left, right) { return right !== void 0 && left.incarnation === right.incarnation && left.physical.key === right.physical.key && left.physical.birthtime === right.physical.birthtime && left.physical.canonicalPath === right.physical.canonicalPath; } function readGrant() { const value = takeSqliteWorkerOperationAdmissionAttachment(); if (!isRecord(value) || value.kind !== "sqlite-source-fence" || value.version !== 1 || !(value.decision instanceof SharedArrayBuffer) || value.decision.byteLength !== 4 || !isRecord(value.destination) || !Array.isArray(value.sources) || typeof value.deadlineNs !== "bigint") throw new SqliteWorkerError("SQLite source fence grant is unavailable", "closed"); return value; } function rollbackReservations(reservations) { const failures = []; for (const { database } of reservations.toReversed()) try { if (database.isOpen && database.isTransaction) database.exec("ROLLBACK"); } catch (error) { failures.push(error); try { database.close(); } catch (closeError) { failures.push(closeError); } } if (failures.length) throw new AggregateError(failures, "SQLite source reservation cleanup failed"); } /** * Only empty acquisition attempts retry. All reservations live in this worker; * source rollback follows the destination's definitive COMMIT or rollback. */ async function runSqliteSourceFence(fence, inOperation, execute) { const grant = inOperation(readGrant); if (isMainThread || fence.sources.length === 0 || !sameIdentity(fence.destination.identity, grant.destination) || fence.sources.length !== grant.sources.length || fence.sources.some((source, index) => !sameIdentity(source.identity, grant.sources[index]))) throw new SqliteWorkerError("SQLite source fence differs from its retained owners", "closed"); const physical = /* @__PURE__ */ new Map(); for (const source of [fence.destination, ...fence.sources]) { bindSqliteSourceFenceDatabase(source); const previous = physical.get(source.identity.physical.key); if (previous && previous.identity.physical.birthtime !== source.identity.physical.birthtime) throw new SqliteWorkerError("SQLite source fence aliases changed incarnation", "closed"); physical.set(source.identity.physical.key, previous ?? source); } const ordered = [...physical.values()].toSorted((a, b) => a.identity.physical.key < b.identity.physical.key ? -1 : 1); const captured = /* @__PURE__ */ new Set([fence.destination, ...fence.sources]); const resolve = (source) => { if (!captured.has(source)) throw new SqliteWorkerError("SQLite source is outside this fence", "closed"); return physical.get(source.identity.physical.key).database; }; const decision = new Int32Array(grant.decision); const assertReady = () => { if (Atomics.load(decision, 0) !== 1 || process.hrtime.bigint() >= grant.deadlineNs) throw new SqliteWorkerError("SQLite source fence authority expired", "closed"); for (const { database } of captured) { assertTransactionUsable(database); if (!database.isOpen) throw new SqliteWorkerError("SQLite source fence owner is closed", "closed"); } }; inOperation(() => requestSqliteWorkerOperationAdmission({ stage: "prepare", facts: "sqlite-source-fence" })); for (;;) { let entered = false; try { return inOperation(() => { let cleanupFailure; let committedResult; try { committedResult = withSqlitePostCommitPublications(fence.destination.database, () => withSqliteWorkerSourceReservations(() => { assertReady(); if ([...captured].some(({ database }) => database.isTransaction)) throw new SqliteWorkerError("SQLite source fence cannot join an outer transaction", "closed"); const reservations = []; try { for (const source of ordered) runWithSqliteBusyTimeout(source.database, 0, () => { source.database.exec("BEGIN IMMEDIATE"); reservations.push(source); }); entered = true; return runSqliteReservedTransactionSync(fence.destination.database, () => { assertReady(); const validation = fence.validate(resolve); if (isPromiseLike(validation)) throw new Error("SQLite source predicates must remain synchronous"); assertReady(); const result = execute(); assertSqliteWorkerCommitReceiptPending(fence.destination.database); return result; }, { withCommit(commit) { assertReady(); if (reservations.some(({ database }) => !database.isTransaction)) throw new SqliteWorkerError("SQLite source reservation was lost", "closed"); if (Atomics.compareExchange(decision, 0, 1, 2) !== 1) throw new SqliteWorkerError("SQLite source fence commit was revoked", "closed"); commit(); } }); } finally { try { rollbackReservations(reservations); } catch (error) { cleanupFailure = { error }; } } })); } catch (error) { if (cleanupFailure) throw new AggregateError([error, cleanupFailure.error], "SQLite fence execution and cleanup failed", { cause: error }); throw error; } if (cleanupFailure) throw cleanupFailure.error; return committedResult; }); } catch (error) { if (entered || !isSqliteLockError(error)) throw error; assertReady(); const remainingMs = Number(grant.deadlineNs - process.hrtime.bigint()) / 1e6; await setTimeout(Math.min(25, Math.max(0, remainingMs))); } } } //#endregion //#region src/infra/sqlite-store.worker.ts if (!parentPort) throw new Error("SQLite store worker requires its host port"); const port = parentPort; routeLogsToStderr(); const actors = /* @__PURE__ */ new Map(); const transfers = createSqliteWorkerTransferOwner(); let pendingResult; let pendingInput; const stateContexts = /* @__PURE__ */ new Map(); let sourceLoaderRegistered = false; let nativeCleanupFailure; let operationAdmission; let nativeRuntimeAdmissionSent = false; async function loadBackendModule(moduleUrl, sourceLoaderUrl) { if (!sourceLoaderRegistered && sourceLoaderUrl) { const loader = await import(sourceLoaderUrl); if (!isRecord(loader) || typeof loader.register !== "function") throw new Error("SQLite source worker loader is unavailable"); loader.register(); sourceLoaderRegistered = true; } return import(moduleUrl); } function runWithActorFacts(actor, operation) { const context = stateContexts.get(actor); return context ? runWithSqliteWorkerStateContext(context, operation) : operation(); } function runInActorContext(actor, operation) { return runWithActorFacts(actor, () => operationAdmission?.actor === actor ? withSqliteWorkerOperationAdmission(operationAdmission.context, operation) : operation()); } function assertSynchronousResult(value, phase, beforeReject) { if (isPromise(value) || isRecord(value) && typeof value.then === "function") { beforeReject?.(); if (isPromise(value)) value.catch(() => {}); throw new Error(`SQLite worker ${phase} must remain synchronous`); } } async function receive(request) { let reply; let executed = pendingResult !== void 0; let retire = false; let completeResult = false; let inputNext = false; let openNotEntered = false; let commandAdmissionRefused = false; try { let value; let closeReceipt; if (request.type !== "result-next" && request.type !== "execute-frame") { if (request.operationAdmission) { if (operationAdmission) throw new Error("SQLite operation admission still belongs to the preceding operation"); operationAdmission = { actor: request.actor, context: { port: request.operationAdmission } }; } if (request.stateContext) stateContexts.set(request.actor, request.stateContext); } const executeCommand = async (command) => { const backend = actors.get(request.actor); if (!backend) throw new Error("SQLite worker actor is closed"); const typedCommand = command; return withSqliteReaderOwner({ operation: typedCommand.type, ownerKind: "worker", actorId: request.actor }, async () => { const assertSettled = () => { const settlement = runInActorContext(request.actor, () => ({ settlement: backend.assertSettled?.() })).settlement; assertSynchronousResult(settlement, "settlement checks"); return backend.assertSettled !== void 0; }; const settleCommand = (failure) => { let verified; try { verified = assertSettled(); } catch (error) { if (operationAdmission) settleSqliteWorkerOperationContext(operationAdmission.context, "unknown"); retire = true; if (failure && failure.error !== error) throw new AggregateError([failure.error, error], `${String(failure.error)}; SQLite worker settlement failed: ${String(error)}`, { cause: error }); throw error; } try { if (verified && backend[SQLITE_WORKER_OPERATION_CLEANUP]) { const cleanup = runInActorContext(request.actor, () => ({ cleanup: backend[SQLITE_WORKER_OPERATION_CLEANUP]?.(typedCommand) })).cleanup; assertSynchronousResult(cleanup, "operation cleanup"); assertSettled(); } } catch (error) { const cleanupError = error instanceof Error ? error : new Error(String(error)); nativeCleanupFailure = encodeOpenClawStateWorkerError(cleanupError, { includeOrdinary: true }) ?? encodeOpenClawStateWorkerError(/* @__PURE__ */ new Error("SQLite worker operation cleanup failed"), { includeOrdinary: true }); } finally { if (operationAdmission) settleSqliteWorkerOperationContext(operationAdmission.context, verified ? "completed" : "unknown"); } }; const loading = backend[SQLITE_WORKER_PREPARE_COMMAND]?.(typedCommand.type); if (loading) await loading; try { const preparation = runWithActorFacts(request.actor, () => backend.prepare?.(typedCommand)); if (preparation !== void 0) await preparation; if (backend[SQLITE_WORKER_PREPARE_ADMITTED]) { const admitted = runInActorContext(request.actor, () => ({ preparation: backend[SQLITE_WORKER_PREPARE_ADMITTED]?.(typedCommand) })).preparation; if (admitted !== void 0) await admitted; } const execute = () => runInActorContext(request.actor, () => ({ result: backend.execute(typedCommand) })).result; const fence = runInActorContext(request.actor, () => backend[SQLITE_WORKER_SOURCE_FENCE]?.(typedCommand)); value = fence ? await runSqliteSourceFence(fence, (operation) => runInActorContext(request.actor, operation), execute) : execute(); } catch (error) { const admissionRefused = operationAdmission?.actor === request.actor && operationAdmission.context.refusal !== void 0 && operationAdmission.context.refusal === error; settleCommand({ error }); commandAdmissionRefused = admissionRefused; throw error; } executed = true; completeResult = true; assertSynchronousResult(value, "operations", () => { retire = true; if (operationAdmission) settleSqliteWorkerOperationContext(operationAdmission.context, "unknown"); }); settleCommand(); }); }; if (request.type === "result-next") { if (pendingResult?.requestId !== request.id || pendingResult.actor !== request.actor || pendingResult.transferId !== request.transferId) throw new Error("SQLite worker result transfer is no longer current"); executed = true; const frame = transfers.next(request.transferId); if (frame.done) { transfers.end(request.transferId); pendingResult = void 0; } value = frame; } else if (pendingResult) throw new Error("SQLite worker result transfer has not finished"); else if (request.type === "execute-start") { retire = true; if (pendingInput || !actors.has(request.actor) || request.transfer.kinds.length !== 1 || request.transfer.kinds[0] !== "command") throw new Error("SQLite worker received unexpected command staging"); const input = { requestId: request.id, actor: request.actor, command: void 0, receiver: createSqliteWorkerTransferReceiver(request.transfer, (record) => { input.command = record.value; }) }; pendingInput = input; inputNext = true; retire = false; } else if (request.type === "execute-frame") { retire = true; const input = pendingInput; if (!input || input.requestId !== request.id || input.actor !== request.actor) throw new Error("SQLite worker command staging is no longer current"); const frame = deserialize(request.input); const counts = input.receiver.accept(frame); if (counts) { if (counts.length !== 1 || counts[0]?.[1] !== 1) throw new Error("SQLite worker received an incomplete command"); pendingInput = void 0; retire = false; await executeCommand(input.command); } else { inputNext = true; retire = false; } } else if (pendingInput) { retire = true; throw new Error("SQLite worker command staging has not finished"); } else if (request.type === "open") { if (actors.has(request.actor)) throw new Error("SQLite worker actor is already open"); if (request.existingIdentity) assertExistingDatabaseIdentity(request.databasePath, request.existingIdentity); const module = await loadBackendModule(request.moduleUrl, request.sourceLoaderUrl); const factoryName = request.existingIdentity ? "openExistingSqliteWorkerBackend" : "createSqliteWorkerBackend"; if (!isRecord(module) || typeof module[factoryName] !== "function") throw new Error(`SQLite worker module must export ${factoryName}`); const factory = module[factoryName]; if (request.existingIdentity) assertExistingDatabaseIdentity(request.databasePath, request.existingIdentity); const backend = await runInActorContext(request.actor, () => { const input = deserialize(request.input); if (request.openAdmission) try { requestSqliteWorkerOperationAdmission({ stage: "open", facts: request.openAdmission === "input" ? input : void 0 }); } catch (error) { openNotEntered = true; throw error; } return factory(input, { databasePath: request.databasePath, ...request.target ? { target: request.target } : {}, ...request.preparation ? { preparation: deserialize(request.preparation) } : {}, ...request.existingIdentity ? { existingIdentity: request.existingIdentity } : {} }); }); if (!isRecord(backend) || typeof backend.execute !== "function" || typeof backend.close !== "function" || SQLITE_WORKER_PREPARE_COMMAND in backend && backend[SQLITE_WORKER_PREPARE_COMMAND] !== void 0 && typeof backend[SQLITE_WORKER_PREPARE_COMMAND] !== "function" || SQLITE_WORKER_PREPARE_ADMITTED in backend && backend[SQLITE_WORKER_PREPARE_ADMITTED] !== void 0 && (typeof backend[SQLITE_WORKER_PREPARE_ADMITTED] !== "function" || typeof backend.assertSettled !== "function") || SQLITE_WORKER_OPERATION_CLEANUP in backend && backend[SQLITE_WORKER_OPERATION_CLEANUP] !== void 0 && (typeof backend[SQLITE_WORKER_OPERATION_CLEANUP] !== "function" || typeof backend.assertSettled !== "function") || SQLITE_WORKER_SOURCE_FENCE in backend && backend[SQLITE_WORKER_SOURCE_FENCE] !== void 0 && (typeof backend[SQLITE_WORKER_SOURCE_FENCE] !== "function" || typeof backend.assertSettled !== "function") || SQLITE_WORKER_CLOSE_RECEIPT in backend && backend[SQLITE_WORKER_CLOSE_RECEIPT] !== void 0 && typeof backend[SQLITE_WORKER_CLOSE_RECEIPT] !== "function" || backend.assertSettled !== void 0 && typeof backend.assertSettled !== "function" || backend.prepare !== void 0 && typeof backend.prepare !== "function") throw new Error("SQLite worker module returned an invalid backend"); actors.set(request.actor, backend); } else if (request.type === "close") { const backend = actors.get(request.actor); if (!backend) throw new Error("SQLite worker actor is closed"); try { await runInActorContext(request.actor, () => backend.close()); closeReceipt = runInActorContext(request.actor, () => backend[SQLITE_WORKER_CLOSE_RECEIPT]?.()); } catch (error) { retire = true; throw error; } actors.delete(request.actor); stateContexts.delete(request.actor); } else await executeCommand(deserialize(request.input)); const serialized = serialize(value); if (serialized.byteLength > 67108864) { if (!completeResult) throw new Error("SQLite worker frame exceeds the transport byte limit"); const handle = transfers.start([{ kind: "result", serialized }].values(), { kinds: ["result"] }); pendingResult = { requestId: request.id, actor: request.actor, transferId: handle.id }; reply = { id: request.id, ok: true, value: serialize(handle), transfer: "start" }; } else reply = { id: request.id, ok: true, value: serialized, ...closeReceipt ? { closeReceipt } : {}, ...request.type === "result-next" ? { transfer: "frame" } : {}, ...inputNext ? { input: "next" } : {} }; } catch (error) { const refusedBeforeAgentOpen = error instanceof SqliteWorkerOpenRefusedError; if (openNotEntered || request.type === "open" && refusedBeforeAgentOpen) stateContexts.delete(request.actor); transfers.cancel(); pendingResult = void 0; pendingInput = void 0; const originalError = refusedBeforeAgentOpen ? error.originalError : error; const admissionRefused = commandAdmissionRefused || request.type === "open" && operationAdmission?.actor === request.actor && operationAdmission.context.refusal !== void 0 && operationAdmission.context.refusal === originalError; const failure = originalError instanceof Error ? originalError : new Error(String(originalError)); const code = executed ? "outcome-unknown" : "code" in failure ? failure.code : void 0; const errorContext = request.stateContext ?? (request.type === "execute-frame" ? stateContexts.get(request.actor) : void 0); const sharedState = !executed ? encodeOpenClawStateWorkerError(failure, { includeOrdinary: Boolean(errorContext) }) : void 0; reply = { id: request.id, ok: false, ...retire || nativeCleanupFailure && executed ? { retire: true } : {}, ...refusedBeforeAgentOpen ? { openOutcome: "refused-before-agent-open" } : {}, ...openNotEntered ? { openNotEntered: true } : {}, ...admissionRefused ? { admissionRefused: true } : {}, error: { name: executed ? "SqliteWorkerError" : failure.name, message: failure.message, ...typeof code === "string" || typeof code === "number" ? { code } : {}, ...sharedState ? { sharedState } : {} } }; } if (!reply.ok || !pendingInput && !pendingResult) { operationAdmission?.context.port.close(); operationAdmission = void 0; } if (request.type === "close" && reply.ok && actors.size === 0) { await flushLogger(); await new Promise((resolve) => { drainProcessOutput(resolve); }); } const complete = !reply.ok || !pendingInput && !pendingResult; if (complete && nativeCleanupFailure) { reply.cleanupFailure = nativeCleanupFailure; nativeCleanupFailure = void 0; } if (reply.ok) { if (complete && !reply.cleanupFailure && !nativeRuntimeAdmissionSent) { const admission = captureSqliteNativeRuntimeAdmission(); if (admission) { reply.nativeRuntimeAdmission = admission; nativeRuntimeAdmissionSent = true; } } const bytes = ownedWorkerBytes(reply.value); port.postMessage({ ...reply, value: bytes }, [bytes.buffer]); } else port.postMessage(reply, []); if (complete) scheduleWorkerIdleGc(); } const runtimeData = workerData; if (isRecord(runtimeData) && runtimeData.sqliteRuntimePreparation !== void 0) { const source = runtimeData.sqliteRuntimePreparation; if (!isRecord(source) || typeof source.moduleUrl !== "string" || source.sourceLoaderUrl !== void 0 && typeof source.sourceLoaderUrl !== "string") throw new Error("SQLite runtime preparation is invalid"); await loadBackendModule(source.moduleUrl, source.sourceLoaderUrl); } port.on("message", (request) => { cancelWorkerIdleGc(); receive(request); }); //#endregion export {};