UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

278 lines (277 loc) • 12 kB
import { c as readErrorName, i as extractErrorCode, n as collectErrorGraphCandidates } from "./error-coercion-1P2One0X.mjs"; import { r as resolveStateDir } from "./state-dir-CfAW6S2K.mjs"; import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs"; import { t as createSubsystemLogger } from "./subsystem-BlrgtxyM.mjs"; import { n as runtimeProcessEntrypoints } from "./runtime-process-entrypoints-Bh4reDp0.mjs"; import { r as resolveRuntimeWorkerUrl } from "./runtime-worker-url-S0NIVxUR.mjs"; import { _ as readOpenClawAgentDatabaseIdentity } from "./openclaw-agent-db-validation-cache-D5sa3PLF.mjs"; import { v as isSqliteWorkerError } from "./sqlite-worker-operation-admission-DqFz0WIG.mjs"; import { r as cloneEnvWithPlatformSemantics } from "./config-env-vars-oFV7TqIg.mjs"; import { c as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-DiWMV5LG.mjs"; import { l as resolveSessionStorePathCore } from "./paths-CAzSovN1.mjs"; import { n as sessionChanges } from "./session-row-changes-ZcHZZvDt.mjs"; import { r as getRuntimeConfig } from "./io.runtime-FunTy0y1.mjs"; import "./io-DQAKeHvj.mjs"; import { d as runOpenClawAgentWriteTransaction, o as getOpenClawAgentDatabaseIfOpen, p as withOpenClawAgentDatabaseAsync } from "./openclaw-agent-db-CFSa_Qik.mjs"; import { n as runOpenClawAgentWorkerWrite, r as runOpenClawAgentWriteAdmission } from "./openclaw-agent-write-admission-xWSWPO-q.mjs"; import { r as BoardValidationError } from "./board-layout-6DyK3jbx.mjs"; import { t as openOpenClawAgentSqliteWorkerStore } from "./openclaw-agent-worker-store-BaoL_lT2.mjs"; import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-C4g5X1l9.mjs"; import { o as resolveSqliteTargetFromSessionStorePath } from "./session-sqlite-target-BGognaPy.mjs"; import { a as putBoardWidgetInDatabase, c as readBoardWidgetRow, d as normalizeBoardWidgetPutParams, i as hasBoardSession, l as rowToBoardWidgetDocument, n as ensureBoardSchema, r as grantBoardWidgetInDatabase, s as readBoardSnapshotWithHtmlViewMetadata, t as applyBoardOpsToDatabase, u as cloneBoardSnapshot } from "./sqlite-board-store.kernel-DomVqYGJ.mjs"; import { r as resolveSessionStoreIdentity } from "./session-store-key-11PJMhso.mjs"; import { AsyncLocalStorage } from "node:async_hooks"; import { randomBytes } from "node:crypto"; //#region src/boards/sqlite-board-store.ts const log = createSubsystemLogger("boards/store"); function restoreBoardError(error) { if (error instanceof Error && error.name === "BoardValidationError" && "code" in error && (error.code === "conflict" || error.code === "invalid_operation" || error.code === "not_found")) return new BoardValidationError(error.code, error.message); return error; } /** Invalidation never grants retries; transported post-execution failures are plain Errors. */ function hasUnknownBoardWriteOutcome(error) { return collectErrorGraphCandidates(error, (current) => current instanceof AggregateError ? [current.cause] : []).some((current) => isSqliteWorkerError(current, "outcome-unknown") || current instanceof Error && readErrorName(current) === "SqliteWorkerError" && extractErrorCode(current) === "outcome-unknown"); } function emptyBoardSnapshot(sessionKey) { return { sessionKey, revision: 0, tabs: [], widgets: [] }; } var SqliteBoardStore = class { constructor(options) { this.options = options; } resolve(target) { return this.options.resolveSession(target); } assertTargetCurrent(target, resolved) { const current = this.resolve(target); if (current.agentId !== resolved.agentId || current.path !== resolved.path || current.sessionKey !== resolved.sessionKey) throw new BoardValidationError("invalid_operation", "board session changed; retry"); } requireExistingSession(resolved, env) { const result = withOpenClawAgentDatabaseReadOnly((database) => hasBoardSession(database, resolved.sessionKey), { agentId: resolved.agentId, ...resolved.path ? { path: resolved.path } : {}, env }); if (!result.found || !result.value) throw new BoardValidationError("not_found", `board session not found: ${resolved.sessionKey}`); } write(target, options, operationLabel, native, worker, prepare) { const resolved = this.resolve(target); const env = cloneEnvWithPlatformSemantics(this.options.env ?? process.env); env.OPENCLAW_STATE_DIR = resolveStateDir(env); const databaseOptions = { ...resolved, env, path: resolveOpenClawAgentSqlitePath({ ...resolved, env }) }; const assertCurrent = () => { options?.assertCurrent?.(); this.assertTargetCurrent(target, resolved); }; const assertOpenCurrent = () => { assertCurrent(); this.requireExistingSession({ ...resolved, path: databaseOptions.path }, env); }; assertOpenCurrent(); return runOpenClawAgentWriteAdmission(databaseOptions, () => withOpenClawAgentDatabaseAsync(databaseOptions, async (database) => { if (prepare) await prepare(); assertCurrent(); if (prepare && getOpenClawAgentDatabaseIfOpen(databaseOptions) !== database) throw new BoardValidationError("invalid_operation", "board database closed or changed; retry"); ensureBoardSchema(database); if (typeof readOpenClawAgentDatabaseIdentity(database).identity === "symbol") return runOpenClawAgentWriteTransaction((current) => { assertCurrent(); return native(current, resolved.sessionKey); }, databaseOptions, { operationLabel }); const publication = await openOpenClawAgentSqliteWorkerStore(databaseOptions, database.db, { moduleUrl: resolveRuntimeWorkerUrl(runtimeProcessEntrypoints.boardStore), input: void 0 }); let outcome; try { outcome = { ok: true, value: await publication.run(async (scope) => { let committed; try { committed = await worker(scope, resolved.sessionKey); } catch (error) { if (hasUnknownBoardWriteOutcome(error)) sessionChanges.emit({ sessionKey: resolved.sessionKey, storePath: database.path }); throw error; } sessionChanges.emitBatch(committed.changes); return committed.value; }, assertCurrent) }; } catch (error) { outcome = { ok: false, error: restoreBoardError(error) }; } let cleanup; try { await publication.close(); cleanup = { ok: true, value: void 0 }; } catch (error) { cleanup = { ok: false, error }; } if (!outcome.ok) { if (!cleanup.ok) throw new AggregateError([outcome.error, cleanup.error], "Board publication and cleanup failed", { cause: outcome.error }); throw outcome.error; } if (!cleanup.ok) try { log.warn(`Board publication completed before cleanup failed: ${formatErrorMessage(cleanup.error)}`); } catch {} return outcome.value; }, assertOpenCurrent), true); } async getSnapshot(target) { return this.readSnapshotWithHtmlViewMetadata(this.resolve(target)).snapshot; } async getSnapshotWithHtmlViewMetadata(target) { return this.readSnapshotWithHtmlViewMetadata(this.resolve(target)); } async consumeRead(target, consume) { const resolved = this.resolve(target); const env = cloneEnvWithPlatformSemantics(this.options.env ?? process.env); env.OPENCLAW_STATE_DIR = resolveStateDir(env); const captured = { ...resolved, env, path: resolveOpenClawAgentSqlitePath({ ...resolved, env }) }; const runInCallerContext = AsyncLocalStorage.snapshot(); return await (await runOpenClawAgentWorkerWrite(captured, async () => { this.assertTargetCurrent(target, resolved); return { value: runInCallerContext(consume, captured, env) }; })).value; } async useSnapshot(target, consume) { return this.consumeRead(target, (resolved, env) => consume(this.readSnapshotWithHtmlViewMetadata(resolved, env).snapshot)); } async useWidgetDocument(target, name, consume) { return this.consumeRead(target, (resolved, env) => consume(this.readWidgetDocument(resolved, name, void 0, env))); } readSnapshotWithHtmlViewMetadata(resolved, env = this.options.env) { const result = withOpenClawAgentDatabaseReadOnly((database) => readBoardSnapshotWithHtmlViewMetadata(database, resolved.sessionKey), { agentId: resolved.agentId, ...resolved.path ? { path: resolved.path } : {}, env }); const stored = result.found ? result.value : void 0; return { snapshot: cloneBoardSnapshot(stored?.snapshot ?? emptyBoardSnapshot(resolved.sessionKey)), htmlViewMetadata: stored?.htmlViewMetadata ?? /* @__PURE__ */ new Map() }; } async applyOps(target, ops, options) { if (ops.length === 0) return this.getSnapshot(target); const capturedOps = structuredClone(ops); return this.write(target, options, "board.apply-ops", (database, sessionKey) => applyBoardOpsToDatabase(database, sessionKey, capturedOps), (scope, sessionKey) => scope.execute({ type: "boards.applyOps", input: { sessionKey, ops: capturedOps } })); } async putWidget(params, options) { const capturedParams = structuredClone(params); const viewGeneration = randomBytes(16).toString("hex"); let preparedParams = capturedParams; const content = capturedParams.content; const resolveInteraction = options?.resolveMcpAppInteraction; const prepare = content.kind === "mcp-app" && content.interactive && resolveInteraction ? async () => { if (!await resolveInteraction()) preparedParams = { ...capturedParams, content: { ...content, interactive: false }, declared: void 0 }; } : void 0; return this.write(params, options, "board.put-widget", (database, sessionKey) => putBoardWidgetInDatabase(database, sessionKey, normalizeBoardWidgetPutParams(preparedParams, sessionKey), viewGeneration), (scope, sessionKey) => scope.execute({ type: "boards.putWidget", input: { sessionKey, params: preparedParams, viewGeneration } }), prepare); } async grant(target, name, decision, revision, instanceId, options) { return this.write(target, options, "board.grant-widget", (database, sessionKey) => grantBoardWidgetInDatabase(database, sessionKey, name, decision, revision, instanceId), (scope, sessionKey) => scope.execute({ type: "boards.grant", input: { sessionKey, name, decision, revision, instanceId } })); } readWidgetDocument(resolved, name, contentKind, env = this.options.env) { const result = withOpenClawAgentDatabaseReadOnly((database) => readBoardWidgetRow(database, resolved.sessionKey, name), { agentId: resolved.agentId, ...resolved.path ? { path: resolved.path } : {}, env }); const row = result.found ? result.value : void 0; return row && (!contentKind || row.content_kind === contentKind) ? rowToBoardWidgetDocument(row) : void 0; } async readWidgetMcpApp(target, name) { const document = this.readWidgetDocument(this.resolve(target), name, "mcp-app"); return document && "descriptor" in document ? document : void 0; } }; //#endregion //#region src/gateway/board-store.ts function captureGatewaySessionStoreScope(sessionKey, explicitAgentId) { const cfg = getRuntimeConfig(); const { agentId, canonicalKey } = resolveSessionStoreIdentity({ cfg, sessionKey, agentId: explicitAgentId }); return { agentId, storePath: resolveSessionStorePathCore(cfg.session?.store, { agentId }), sessionKey: canonicalKey }; } function resolveGatewaySessionDatabase(sessionKey, explicitAgentId) { const { agentId, storePath, sessionKey: canonicalKey } = captureGatewaySessionStoreScope(sessionKey, explicitAgentId); const databaseTarget = resolveSqliteTargetFromSessionStorePath(storePath, { agentId }); return { agentId: databaseTarget.agentId ?? agentId, path: databaseTarget.path, sessionKey: canonicalKey }; } const boardStore = new SqliteBoardStore({ resolveSession: ({ sessionKey, agentId }) => resolveGatewaySessionDatabase(sessionKey, agentId) }); //#endregion export { captureGatewaySessionStoreScope as n, resolveGatewaySessionDatabase as r, boardStore as t };