openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
167 lines (166 loc) • 6.84 kB
JavaScript
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { c as registerNodeSqliteDisposeCallback } from "./kysely-sync-cache-state-eUau8Jaa.mjs";
import { t as createSubsystemLogger } from "./subsystem-I-uqdx4f.mjs";
import { o as runInSqliteMaintenanceContext } from "./sqlite-wal-DUSYRhyv.mjs";
import { _ as isOpenClawAgentDatabasePathCurrent, h as createOpenClawAgentDatabaseClaim, v as readOpenClawAgentDatabaseIdentity } from "./openclaw-agent-db-validation-cache-_HDeZNQr.mjs";
import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs";
import { t as SQLITE_IDLE_HANDLE_TTL_MS } from "./sqlite-handle-lifecycle-dWd9h3ii.mjs";
import "./session-key-C09ow4h0.mjs";
import { c as resolveOpenClawAgentSqlitePath, i as isIncognitoOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-DVbbFaej.mjs";
import { r as assertSupportedAgentSchemaVersion, t as assertCanonicalAgentPersistenceVersion } from "./openclaw-agent-db-schema-read-8tLHX1g8.mjs";
import { a as getOpenClawAgentDatabaseIfOpen, t as borrowOpenClawAgentDatabase } from "./openclaw-agent-db-DBug3LUA.mjs";
import { i as withFreshOpenClawAgentDatabaseReadOnly, n as openOpenClawAgentDatabaseReadOnly, r as readOpenClawAgentDatabase, t as hasOpenClawAgentReadOnlySchema } from "./openclaw-agent-db-readonly-open-hQ4ohjUT.mjs";
import { a as withScopedOpenClawAgentDatabaseReadOnly, i as retainCachedOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-scope-CLlkTUQB.mjs";
//#region src/state/openclaw-agent-db-readonly-companion.ts
const log = createSubsystemLogger("state/agent-db");
const companions = resolveGlobalSingleton(Symbol.for("openclaw.agentDatabaseReadOnlyCompanions"), () => /* @__PURE__ */ new WeakMap());
function matchesWriter(reader, writer) {
return isOpenClawAgentDatabasePathCurrent(writer) && isOpenClawAgentDatabasePathCurrent(reader) && readOpenClawAgentDatabaseIdentity(reader).identity === readOpenClawAgentDatabaseIdentity(writer).identity;
}
/** Keep committed reads separate from the active writer without reopening per assertion. */
function withCommittedOpenClawAgentDatabaseReadOnly(writer, operation, options) {
let companion = companions.get(writer.db);
if (companion?.active) return withFreshOpenClawAgentDatabaseReadOnly(operation, options);
if (companion && (!matchesWriter(companion.reader, writer) || companion.reader.db.isTransaction)) {
companion.close();
companion = void 0;
}
if (!companion && !isOpenClawAgentDatabasePathCurrent(writer)) return withFreshOpenClawAgentDatabaseReadOnly(operation, options);
if (!companion) {
const opened = openOpenClawAgentDatabaseReadOnly(options);
if (!opened.found) return opened;
const reader = opened.database;
let unregisterDispose = () => {};
let idleTimer;
const close = () => {
if (reader.db.isOpen) reader.close();
clearTimeout(idleTimer);
if (companions.get(writer.db)?.reader === reader) companions.delete(writer.db);
unregisterDispose();
};
try {
if (!matchesWriter(reader, writer)) return readOpenClawAgentDatabase(reader, operation);
unregisterDispose = registerNodeSqliteDisposeCallback(writer.db, close);
idleTimer = runInSqliteMaintenanceContext(() => setTimeout(() => {
if (companions.get(writer.db)?.reader !== reader) return;
try {
close();
} catch (error) {
log.warn("Idle committed agent reader cleanup failed", {
path: reader.path,
error
});
idleTimer?.refresh();
}
}, SQLITE_IDLE_HANDLE_TTL_MS));
idleTimer.unref();
const next = {
reader,
active: false,
close,
idleTimer
};
companions.set(writer.db, next);
companion = next;
} finally {
if (!companion) close();
}
}
const owned = companion;
try {
if (!hasOpenClawAgentReadOnlySchema(owned.reader)) {
owned.close();
return {
found: false,
reason: "schema-missing"
};
}
owned.idleTimer.refresh();
owned.active = true;
return readOpenClawAgentDatabase(owned.reader, operation);
} catch (error) {
owned.close();
throw error;
} finally {
owned.active = false;
if (!owned.reader.db.isOpen || owned.reader.db.isTransaction || !matchesWriter(owned.reader, writer)) owned.close();
}
}
//#endregion
//#region src/state/openclaw-agent-db-readonly.ts
/**
* Look up a process-held handle without adopting writer-side failures.
*
* Read-only reads are meant to survive a latched open failure or an ownership
* mismatch that only the writable lifecycle cares about; those callers fall
* back to a fresh connection, which reports the precise reason.
*/
function findOpenAgentDatabase(options) {
try {
return getOpenClawAgentDatabaseIfOpen(options);
} catch {
return;
}
}
/** Retain an existing store across awaits without materializing a writable database. */
function retainOpenClawAgentDatabaseReadOnly(options) {
const opened = findOpenAgentDatabase(options);
if (opened && !opened.db.isTransaction) {
const borrowed = borrowOpenClawAgentDatabase(options);
return {
found: true,
database: opened,
claim: createOpenClawAgentDatabaseClaim(opened, borrowed.release)
};
}
const agentId = normalizeAgentId(options.agentId);
const pathname = resolveOpenClawAgentSqlitePath({
...options,
agentId
});
return retainCachedOpenClawAgentDatabaseReadOnly({
...options,
agentId,
path: pathname
});
}
/** Read agent state without creating, registering, migrating, or joining its writable lifecycle. */
function withOpenClawAgentDatabaseReadOnly(operation, options, behavior = {}) {
const agentId = normalizeAgentId(options.agentId);
const pathname = resolveOpenClawAgentSqlitePath({
...options,
agentId
});
if (isIncognitoOpenClawAgentSqlitePath(pathname, {
agentId,
env: options.env
})) {
const database = getOpenClawAgentDatabaseIfOpen({
...options,
agentId
});
if (database && behavior.allowExtension) throw new Error("Extension-capable read-only access is unavailable for incognito databases.");
return database ? readOpenClawAgentDatabase(database, operation) : {
found: false,
reason: "database-missing"
};
}
const processOpened = behavior.allowExtension ? void 0 : findOpenAgentDatabase({
...options,
agentId
});
if (processOpened?.db.isTransaction) return withCommittedOpenClawAgentDatabaseReadOnly(processOpened, operation, {
...options,
agentId
});
if (!processOpened) return withScopedOpenClawAgentDatabaseReadOnly(operation, {
...options,
agentId,
path: pathname
}, behavior);
const userVersion = assertSupportedAgentSchemaVersion(processOpened.db, pathname);
assertCanonicalAgentPersistenceVersion(processOpened.db, pathname, userVersion);
return readOpenClawAgentDatabase(processOpened, operation);
}
//#endregion
export { withOpenClawAgentDatabaseReadOnly as n, retainOpenClawAgentDatabaseReadOnly as t };