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