UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

223 lines (222 loc) • 9.11 kB
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { r as resolveStateDir } from "./state-dir-CfAW6S2K.mjs"; import { c as isStateDatabaseReadAdmissionInvalidatedError } from "./openclaw-state-db-async-lifecycle-8N3pIeYH.mjs"; import { s as resolveOpenClawStateSqlitePath } from "./openclaw-state-db.paths-BvBEgHyD.mjs"; import "./openclaw-agent-db-contract-ClUH-Vmp.mjs"; import { r as cloneEnvWithPlatformSemantics } from "./config-env-vars-oFV7TqIg.mjs"; import { f as withExistingOpenClawStateDatabaseReadOnly, t as executeExistingOpenClawStateRead, u as withExistingOpenClawStateDatabaseArtifactPreservingReadOnlyAsync } from "./openclaw-state-db-readonly-BLZyK3So.mjs"; import { a as resolveSqliteDatabaseFilePaths } from "./sqlite-files-CYwO2uWF.mjs"; import { r as captureOpenClawStateWorkerContext } from "./openclaw-state-worker-context-epCM4Gdh.mjs"; import { n as sessionChanges } from "./session-row-changes-ZcHZZvDt.mjs"; import { r as readRegisteredAgentDatabaseRows } from "./openclaw-agent-db-registry.read-DIhm9ssK.mjs"; import { lstatSync, statSync } from "node:fs"; import path from "node:path"; import { AsyncLocalStorage } from "node:async_hooks"; //#region src/state/openclaw-agent-db-registry-listing.ts const registry = resolveGlobalSingleton(Symbol.for("openclaw.agentDatabaseRegistryMemo"), () => ({})); function resolveAgentDatabaseRegistryPath(options) { return path.resolve(options.path ?? resolveOpenClawStateSqlitePath(options.env ?? process.env)); } function activateRegisteredAgentDatabasesMemo(options) { const pathname = resolveAgentDatabaseRegistryPath(options); if (registry.memo?.pathname !== pathname) registry.memo = { pathname, token: Symbol(pathname) }; return registry.memo; } /** Return the process-stable generation for the active agent database registry. */ function readOpenClawAgentDatabaseRegistryToken(options = {}) { return activateRegisteredAgentDatabasesMemo(options).token; } function invalidateRegisteredAgentDatabasesMemo(options) { const pathname = resolveAgentDatabaseRegistryPath(options); if (registry.memo?.pathname === pathname) { const previous = registry.memo.token; registry.memo = { pathname, token: Symbol(pathname) }; return { previous, current: registry.memo.token }; } } /** Publish only registration witnessed at COMMIT, under its original shared generation. */ function captureOpenClawAgentDatabaseRegistration(params) { const options = { path: params.admission.databasePath }; let active = false; let committed = false; let finished = false; return { begin() { if (finished) throw new Error("Agent database registration admission is closed"); if (!active) { active = true; const change = invalidateRegisteredAgentDatabasesMemo(options); if (change) params.onRegistryChange?.(change); } }, recordCommitted(receipt) { if (finished || !active || receipt.agentId !== params.agentId || receipt.agentPath !== params.agentPath || receipt.stateDatabasePath !== params.admission.databasePath || receipt.stateDatabaseIdentity !== params.admission.identity.key) throw new Error("Agent registration commit differs from its captured owner"); committed = true; }, finish() { if (finished) return; finished = true; try { params.admission.assertCurrent(); } catch (error) { if (isStateDatabaseReadAdmissionInvalidatedError(error)) return; throw error; } try { const change = active ? invalidateRegisteredAgentDatabasesMemo(options) : void 0; if (change) params.onRegistryChange?.(change); } finally { if (committed) sessionChanges.emit({ all: true, scope: { agentId: params.agentId, topology: true } }); } } }; } function cloneRegisteredAgentDatabases(entries) { return entries.map((entry) => ({ ...entry })); } function hasUnavailableMissingSqlitePath(pathname) { for (const candidate of resolveSqliteDatabaseFilePaths(pathname)) try { lstatSync(candidate); return true; } catch (error) { if (error.code !== "ENOENT") return true; } let ancestor = path.dirname(pathname); while (true) { try { const stat = lstatSync(ancestor); if (!stat.isSymbolicLink()) return !stat.isDirectory(); try { return !statSync(ancestor).isDirectory(); } catch { return true; } } catch (error) { if (error.code !== "ENOENT") return true; } const parent = path.dirname(ancestor); if (parent === ancestor) return false; ancestor = parent; } } var AgentDatabaseRegistryChangedError = class extends Error { constructor() { super("Agent database registry changed during discovery; retry the read."); this.name = "AgentDatabaseRegistryChangedError"; } }; function readRegisteredAgentDatabases(options, artifactPreserving) { const pathname = resolveAgentDatabaseRegistryPath(options); const read = ({ db }) => readRegisteredAgentDatabaseRows(db, pathname, artifactPreserving); const finish = (entries) => { if (entries === void 0) { if (hasUnavailableMissingSqlitePath(pathname)) throw new Error(`OpenClaw state database ${pathname} is unavailable.`); return []; } return options.includeIncompatibleSchemaVersions ? entries : entries.filter((entry) => entry.schemaVersion === 24); }; return artifactPreserving ? withExistingOpenClawStateDatabaseArtifactPreservingReadOnlyAsync(read, options).then(finish) : finish(withExistingOpenClawStateDatabaseReadOnly(read, options)); } /** Inspect a copied registry without creating SQLite artifacts or runtime memo state. */ async function inspectOpenClawRegisteredAgentDatabases(options = {}) { return readRegisteredAgentDatabases(options, true); } /** List agent databases recorded in the shared OpenClaw state registry. */ function listOpenClawRegisteredAgentDatabases(options = {}) { const memo = activateRegisteredAgentDatabasesMemo(options); const cloned = cloneRegisteredAgentDatabases(memo.entries ??= readRegisteredAgentDatabases({ ...options, includeIncompatibleSchemaVersions: true }, false)); return options.includeIncompatibleSchemaVersions ? cloned : cloned.filter((entry) => entry.schemaVersion === 24); } /** Capture authority now, but activate the canonical memo only if discovery needs it. */ function prepareOpenClawAgentDatabaseRegistrySnapshotRead(inputOptions = {}) { try { const env = cloneEnvWithPlatformSemantics(inputOptions.env ?? process.env); env.OPENCLAW_STATE_DIR = resolveStateDir(env); const options = { ...inputOptions, env, path: resolveAgentDatabaseRegistryPath({ ...inputOptions, env }) }; const context = captureOpenClawStateWorkerContext(options); const inCapturedScope = AsyncLocalStorage.snapshot(); let assertPreparedCurrent = () => context.admission.assertCurrent(); return { assertCurrent: () => assertPreparedCurrent(), async read() { context.admission.assertCurrent(); let memo = activateRegisteredAgentDatabasesMemo(options); let invalidated = false; const assertCurrent = () => { context.admission.assertCurrent(); if (invalidated || registry.memo !== memo) { invalidated = true; throw new AgentDatabaseRegistryChangedError(); } }; const followRegistration = (change) => { context.admission.assertCurrent(); if (invalidated || memo.token !== change.previous || registry.memo?.pathname !== memo.pathname || registry.memo.token !== change.current) { invalidated = true; throw new Error("Agent registration cannot replace an invalidated registry read"); } memo = registry.memo; }; assertPreparedCurrent = assertCurrent; if (!memo.entries) { const reply = await inCapturedScope(() => executeExistingOpenClawStateRead(options, { type: "agentDatabaseRegistry.read" })); if (reply && (!reply.ok || reply.type !== "agentDatabaseRegistry.read")) throw new Error("Unexpected agent database registry read result"); const result = reply?.result; assertCurrent(); if (result?.status === "unavailable" || result === void 0 && hasUnavailableMissingSqlitePath(options.path)) return { result: { status: "unavailable" }, assertCurrent, followRegistration }; memo.entries ??= result?.entries ?? []; } const entries = cloneRegisteredAgentDatabases(memo.entries); assertCurrent(); return { result: { status: "available", entries: options.includeIncompatibleSchemaVersions ? entries : entries.filter((entry) => entry.schemaVersion === 24) }, assertCurrent, followRegistration }; } }; } catch (error) { return { assertCurrent() { throw error; }, async read() { throw error; } }; } } //#endregion export { listOpenClawRegisteredAgentDatabases as a, readRegisteredAgentDatabases as c, invalidateRegisteredAgentDatabasesMemo as i, captureOpenClawAgentDatabaseRegistration as n, prepareOpenClawAgentDatabaseRegistrySnapshotRead as o, inspectOpenClawRegisteredAgentDatabases as r, readOpenClawAgentDatabaseRegistryToken as s, AgentDatabaseRegistryChangedError as t };