openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
140 lines (139 loc) • 4.53 kB
JavaScript
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import "./session-key-BnWWjqNc.js";
import { h as clearNodeSqliteKyselyCacheForDatabase, t as openNodeSqliteDatabase } from "./node-sqlite-BpQX3W0e.js";
import "./kysely-sync-COmh4HWh.js";
import { o as OPENCLAW_SQLITE_BUSY_TIMEOUT_MS } from "./openclaw-state-db-contract-DYCYxE4w.js";
import { D as isIncognitoOpenClawAgentSqlitePath, k as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db-lease-Djvd6LWN.js";
import { C as assertExistingAgentSchemaOwner, E as readExistingAgentSchemaMeta, S as assertCanonicalAgentPersistenceVersion, T as assertSupportedAgentSchemaVersion } from "./openclaw-agent-db-maintenance-wTIy-jt-.js";
import { c as getOpenClawAgentDatabaseIfOpen } from "./openclaw-agent-db-CWtDoRbC.js";
import fs from "node:fs";
//#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;
}
}
/** Open one existing agent database without creating, registering, migrating, or adopting it. */
function openOpenClawAgentDatabaseReadOnly(options, behavior = {}) {
const agentId = normalizeAgentId(options.agentId);
const pathname = resolveOpenClawAgentSqlitePath({
...options,
agentId
});
if (isIncognitoOpenClawAgentSqlitePath(pathname, {
agentId,
env: options.env
})) return {
found: false,
reason: "database-missing"
};
if (!fs.existsSync(pathname)) return {
found: false,
reason: "database-missing"
};
const db = openNodeSqliteDatabase(pathname, {
readOnly: true,
...behavior.allowExtension ? { allowExtension: true } : {}
});
let closed = false;
const close = () => {
if (closed) return;
closed = true;
clearNodeSqliteKyselyCacheForDatabase(db);
db.close();
};
try {
db.exec(`PRAGMA busy_timeout = ${OPENCLAW_SQLITE_BUSY_TIMEOUT_MS};`);
const userVersion = assertSupportedAgentSchemaVersion(db, pathname);
assertCanonicalAgentPersistenceVersion(db, pathname, userVersion);
const schemaMeta = readExistingAgentSchemaMeta(db);
if (!schemaMeta) {
close();
return {
found: false,
reason: "schema-missing"
};
}
assertExistingAgentSchemaOwner(schemaMeta, agentId, pathname);
return {
found: true,
database: {
agentId,
db,
path: pathname,
close
}
};
} catch (error) {
close();
throw error;
}
}
/** 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 ? {
found: true,
value: operation(database)
} : {
found: false,
reason: "database-missing"
};
}
const processOpened = behavior.allowExtension ? void 0 : findOpenAgentDatabase({
...options,
agentId
});
const reusable = processOpened && !processOpened.db.isTransaction ? processOpened : void 0;
const fresh = reusable ? void 0 : openOpenClawAgentDatabaseReadOnly({
...options,
agentId
}, behavior);
if (fresh && !fresh.found) return fresh;
const database = reusable ?? fresh.database;
const { db } = database;
try {
if (reusable) {
const userVersion = assertSupportedAgentSchemaVersion(db, pathname);
assertCanonicalAgentPersistenceVersion(db, pathname, userVersion);
}
try {
return {
found: true,
value: operation(database)
};
} catch (error) {
if (error instanceof Error && error.code === "ERR_SQLITE_ERROR" && /\bno such table:/iu.test(error.message) && !behavior.throwOnMissingTable) return {
found: false,
reason: "table-missing"
};
throw error;
}
} finally {
if (fresh?.found) fresh.database.close();
}
}
//#endregion
export { withOpenClawAgentDatabaseReadOnly as n, openOpenClawAgentDatabaseReadOnly as t };