openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,421 lines • 58.4 kB
JavaScript
import { d as toErrorObject, t as hasErrnoCode } from "./errno-jiy5vtf-.mjs";
import { k as isRecord, w as resolveGlobalSingleton } from "./number-coercion-PJEgwXYS.mjs";
import { S as registerNodeSqliteDisposeCallback, b as kyselyByDatabase, c as readDatabasePathIdentitySync, m as StartupMaintenanceRequiredError, v as executeWithCachedStatement, x as queryErrorHandlerByDatabase, y as installStatementInvalidation } from "./sqlite-worker-identity-CfwbX7eo.mjs";
import { t as formatErrorMessage } from "./errors-Qhjprk1B.mjs";
import { a as ensureSqliteLibrarySelected, c as isNodeVersionAtLeast, l as parseNodeReleaseVersion, n as SQLITE_NATIVE_RUNTIME_ADMISSION_KEY, o as getSqliteRuntimeCapabilities, s as isSqliteWalResetSafeVersion } from "./bun-sqlite-library-2yW9Fayj.mjs";
import { n as compareValidSemver } from "./semver-aYpwYdrQ.mjs";
import { createRequire } from "node:module";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { AsyncLocalStorage } from "node:async_hooks";
import { normalizeWindowsPathForComparison } from "@openclaw/fs-safe/path";
import { getEnvironmentData, isMainThread, setEnvironmentData, threadId } from "node:worker_threads";
import { isDeepStrictEqual } from "node:util";
import { InsertQueryNode, Kysely, SelectQueryNode, SqliteDialect, sql } from "kysely";
//#region src/infra/sqlite-reader-lifecycle.ts
const readerOwners = resolveGlobalSingleton(Symbol.for("openclaw.sqliteReaderOwners"), () => new AsyncLocalStorage());
const activeReaders = resolveGlobalSingleton(Symbol.for("openclaw.sqliteActiveReaders"), () => ({
byDatabase: /* @__PURE__ */ new WeakMap(),
byPath: /* @__PURE__ */ new Map()
}));
const connections = resolveGlobalSingleton(Symbol.for("openclaw.sqliteReaderConnections"), () => ({
nextId: 0,
byDatabase: /* @__PURE__ */ new WeakMap(),
byPath: /* @__PURE__ */ new Map(),
finalizer: new FinalizationRegistry((connection) => forgetConnection(connection))
}));
/** The same Windows file can arrive with a namespaced or differently cased path. */
function sqliteReaderDatabasePathKey(databasePath) {
const resolved = path.resolve(databasePath);
return process.platform === "win32" ? normalizeWindowsPathForComparison(resolved) : resolved;
}
function boundedOperation(operation) {
return (operation.trim() || "sqlite reader").slice(0, 120);
}
function captureSqliteReaderOwner() {
const owner = readerOwners.getStore();
if (!owner) return;
const { timing: _, ...identity } = owner;
return identity;
}
/** Mutable diagnostic counters only; never source or write authority. */
function currentSqliteOperationTiming() {
return readerOwners.getStore()?.timing;
}
function currentOwner(fallbackOperation, capturedOwner) {
const inherited = capturedOwner ?? readerOwners.getStore();
return {
operation: boundedOperation(inherited?.operation ?? fallbackOperation),
ownerKind: inherited?.ownerKind ?? (isMainThread ? "main" : "worker"),
...inherited?.actorId !== void 0 ? { actorId: inherited.actorId } : {}
};
}
function connectionFor(database) {
let connection = connections.byDatabase.get(database);
if (!connection) {
const now = Date.now();
const location = database.location();
connection = {
id: ++connections.nextId,
path: location ? sqliteReaderDatabasePathKey(location) : void 0,
database: new WeakRef(database),
owner: currentOwner("sqlite connection"),
openedAtMs: now
};
connections.byDatabase.set(database, connection);
if (connection.path) {
const entries = connections.byPath.get(connection.path) ?? /* @__PURE__ */ new Map();
entries.set(connection.id, connection);
connections.byPath.set(connection.path, entries);
}
connections.finalizer.register(database, connection, connection);
}
return connection;
}
/** Forget observations only; explicit reader custody belongs to its caller. */
function forgetConnection(connection) {
if (connection.path) {
const entries = connections.byPath.get(connection.path);
entries?.delete(connection.id);
if (entries?.size === 0) connections.byPath.delete(connection.path);
}
const database = connection.database.deref();
if (database && connections.byDatabase.get(database) === connection) connections.byDatabase.delete(database);
connections.finalizer.unregister(connection);
}
/** Register weak metadata without replacing native methods or acquiring lifecycle custody. */
function registerSqliteReaderConnection(database) {
if (database.isOpen) connectionFor(database);
}
function retainSqliteReader(database, fallbackOperation, capturedOwner) {
const connection = connectionFor(database);
const now = Date.now();
const reader = {
...currentOwner(fallbackOperation, capturedOwner),
kind: "iterator",
connectionId: connection.id,
threadId,
startedAtMs: now,
lastProgressAtMs: now
};
const token = Symbol(reader.operation);
const databaseReaders = activeReaders.byDatabase.get(database) ?? /* @__PURE__ */ new Map();
databaseReaders.set(token, reader);
activeReaders.byDatabase.set(database, databaseReaders);
const databasePath = connection.path;
const pathReaders = databasePath ? activeReaders.byPath.get(databasePath) ?? /* @__PURE__ */ new Map() : void 0;
pathReaders?.set(token, reader);
if (databasePath && pathReaders) activeReaders.byPath.set(databasePath, pathReaders);
return {
progress() {
if (databaseReaders.has(token)) reader.lastProgressAtMs = Date.now();
},
release() {
if (!databaseReaders.delete(token)) return;
if (databaseReaders.size === 0 && activeReaders.byDatabase.get(database) === databaseReaders) activeReaders.byDatabase.delete(database);
pathReaders?.delete(token);
if (databasePath && pathReaders?.size === 0 && activeReaders.byPath.get(databasePath) === pathReaders) activeReaders.byPath.delete(databasePath);
}
};
}
function diagnostics(readers) {
const now = Date.now();
return [...readers].map(({ startedAtMs, lastProgressAtMs, ...reader }) => Object.assign(reader, {
ageMs: Math.max(0, now - startedAtMs),
idleMs: Math.max(0, now - lastProgressAtMs)
})).toSorted((left, right) => right.ageMs - left.ageMs).slice(0, 8);
}
/** Observed local activity is diagnostic evidence, not proof of which connection owns a WAL lock. */
function readSqliteReaderDiagnosticsForPath(databasePath) {
const key = sqliteReaderDatabasePathKey(databasePath);
const entries = connections.byPath.get(key);
const localConnections = [];
const now = Date.now();
for (const connection of entries?.values() ?? []) {
const database = connection.database.deref();
if (!database?.isOpen) {
forgetConnection(connection);
continue;
}
const location = database.location();
if (!location || sqliteReaderDatabasePathKey(location) !== key) {
forgetConnection(connection);
continue;
}
const readers = activeReaders.byDatabase.get(database);
localConnections.push({
...connection.owner,
connectionId: connection.id,
threadId,
ageMs: Math.max(0, now - connection.openedAtMs),
transactionOpen: database.isTransaction,
trackedReaders: readers?.size ?? 0
});
}
const readers = activeReaders.byPath.get(key);
return {
scope: "current-thread",
blockingOwner: "unknown",
nativeStatements: "unobserved",
threadId,
observedAtMs: now,
connectionCount: localConnections.length,
readerCount: readers?.size ?? 0,
connections: localConnections.toSorted((left, right) => right.trackedReaders - left.trackedReaders || Number(right.transactionOpen) - Number(left.transactionOpen)).slice(0, 8),
activeReaders: diagnostics(readers?.values() ?? [])
};
}
//#endregion
//#region src/infra/kysely-sync.ts
const nodeVersion = parseNodeReleaseVersion(process.versions.node);
const supportsRepreparedAll = !process.versions.bun && (nodeVersion?.major === 24 && isNodeVersionAtLeast(nodeVersion, {
major: 24,
minor: 20,
patch: 0
}) || isNodeVersionAtLeast(nodeVersion, {
major: 26,
minor: 6,
patch: 0
}));
const compileOnlySqliteDialect = new SqliteDialect({ database: async () => {
throw new Error("getNodeSqliteKysely() returns a compile-only Kysely facade; use executeSqliteQuerySync() to execute node:sqlite queries.");
} });
function getNodeSqliteKysely(db) {
const existing = kyselyByDatabase.get(db);
if (existing) return existing;
const kysely = new Kysely({ dialect: compileOnlySqliteDialect });
kyselyByDatabase.set(db, kysely);
return kysely;
}
function reportNodeSqliteKyselyQueryError(db, error) {
try {
queryErrorHandlerByDatabase.get(db)?.(error);
} catch {}
}
function releaseSqliteIterator(iterator, reader, failed, cleanupErrorMessage) {
let cleanupError;
try {
iterator.return?.();
} catch (error) {
cleanupError = error;
}
reader.release();
if (!failed && cleanupError !== void 0) throw toErrorObject(cleanupError, cleanupErrorMessage);
}
/** Execute a compiled Kysely query synchronously against node:sqlite. */
function executeCompiledSqliteQuerySync(db, compiledQuery, firstRowOnly = false, parameters = compiledQuery.parameters) {
try {
const sql = compiledQuery.sql;
installStatementInvalidation(db);
return executeWithCachedStatement(db, sql, parameters, (statement) => {
if (firstRowOnly && SelectQueryNode.is(compiledQuery.query)) {
const row = statement.get(...parameters);
return { rows: row === void 0 ? [] : [row] };
}
if (SelectQueryNode.is(compiledQuery.query) || statement.columns().length > 0) {
if (supportsRepreparedAll) return { rows: statement.all(...parameters) };
const iterator = statement.iterate(...parameters);
const reader = retainSqliteReader(db, "kysely eager query");
let failed = false;
let failure;
const rows = [];
try {
for (const row of iterator) {
reader.progress();
rows.push(row);
}
} catch (error) {
failed = true;
failure = error;
}
releaseSqliteIterator(iterator, reader, failed, "SQLite query cleanup failed");
if (failed) throw toErrorObject(failure, "SQLite query failed");
return { rows };
}
statement.setReadBigInts(true);
let outcome;
try {
outcome = statement.run(...parameters);
} finally {
statement.setReadBigInts(false);
}
const { changes, lastInsertRowid } = outcome;
const result = {
numAffectedRows: BigInt(changes),
rows: []
};
if (InsertQueryNode.is(compiledQuery.query) && changes > 0) return {
...result,
insertId: BigInt(lastInsertRowid)
};
return result;
});
} catch (error) {
reportNodeSqliteKyselyQueryError(db, error);
throw error;
}
}
/** Compile and execute a Kysely query synchronously. */
function executeSqliteQuerySync(db, query) {
return executeCompiledSqliteQuerySync(db, query.compile());
}
/** Cache compiled query functions or bundles by native connection. */
function createSqliteQueryCache(create) {
const queriesByDatabase = /* @__PURE__ */ new WeakMap();
return (database) => {
let queries = queriesByDatabase.get(database);
if (queries === void 0) {
queries = create(database);
queriesByDatabase.set(database, queries);
}
return queries;
};
}
/** Compile fixed SQL and fresh bindings without taking ownership of a native statement. */
function compileSqliteQueryBindings(build) {
const bindings = /* @__PURE__ */ new Map();
const compiled = build((read) => {
const marker = Symbol("sqlite-query-parameter");
bindings.set(marker, read);
return sql`${marker}`;
}).compile();
const readers = compiled.parameters.map((value) => bindings.get(value) ?? (() => value));
return {
compiled,
bind: (params) => readers.map((read) => read(params))
};
}
/** Compile a fixed query once; bind fresh values through the normal sync executor on each call. */
function prepareSqliteQuerySync(db, build) {
const { compiled, bind } = compileSqliteQueryBindings(build);
return (params) => executeCompiledSqliteQuerySync(db, compiled, false, bind(params));
}
/** Compile and lazily iterate a Kysely query synchronously against node:sqlite. */
function iterateSqliteQuerySync(db, query) {
const owner = captureSqliteReaderOwner();
return (function* () {
const compiledQuery = query.compile();
try {
const statement = db.prepare(compiledQuery.sql);
if (!SelectQueryNode.is(compiledQuery.query) && statement.columns().length === 0) return;
const parameters = compiledQuery.parameters;
const iterator = statement.iterate(...parameters);
const reader = retainSqliteReader(db, "kysely iterator", owner);
let failed = false;
try {
for (const row of iterator) {
reader.progress();
yield row;
}
} catch (error) {
failed = true;
throw toErrorObject(error, "SQLite iterator failed");
} finally {
releaseSqliteIterator(iterator, reader, failed, "SQLite iterator cleanup failed");
}
} catch (error) {
reportNodeSqliteKyselyQueryError(db, error);
throw error;
}
})();
}
/** Execute a Kysely query synchronously and return its first row. */
function executeSqliteQueryTakeFirstSync(db, query) {
return executeCompiledSqliteQuerySync(db, query.compile(), true).rows[0];
}
//#endregion
//#region src/infra/sqlite-schema-sql.ts
const TABLE_CONSTRAINT_KEYWORDS = /* @__PURE__ */ new Set([
"CHECK",
"FOREIGN",
"PRIMARY",
"UNIQUE"
]);
/** Select canonical DDL without opening a database or changing its installation lifecycle. */
function extractSqliteTableSchema(schema, table, options = {}) {
const start = schema.indexOf(`CREATE TABLE IF NOT EXISTS ${table} (`);
const endMarker = options.endMarker ?? "\n) STRICT;";
const end = schema.indexOf(endMarker, start);
if (start < 0 || end < start) throw new Error(options.errorMessage ?? `Canonical schema markers are missing for ${table}`);
return schema.slice(start, end + (options.includeEndMarker === false ? 0 : endMarker.length));
}
function readTableConstraintKeyword(sql, first) {
let token = first;
if (token.keyword === "CONSTRAINT") {
const name = readSqlToken(sql, token.end);
token = name ? readSqlToken(sql, name.end) : null;
}
return token?.keyword && TABLE_CONSTRAINT_KEYWORDS.has(token.keyword) ? token.keyword : null;
}
function readSqlToken(sql, start) {
let index = start;
while (index < sql.length && /\s/u.test(sql[index] ?? "")) index += 1;
const char = sql[index];
if (!char) return null;
if (char === "\"" || char === "`" || char === "[") {
const end = skipSqlQuoted(sql, index, char);
return {
end,
keyword: null,
raw: sql.slice(index, end)
};
}
let end = index;
while (end < sql.length && !/[\s(,]/u.test(sql[end] ?? "")) end += 1;
const raw = sql.slice(index, end);
return {
end,
keyword: raw.toUpperCase(),
raw
};
}
function normalizeSqlIdentifier(identifier) {
if (identifier.startsWith("\"") && identifier.endsWith("\"")) return identifier.slice(1, -1).replaceAll("\"\"", "\"").toLowerCase();
if (identifier.startsWith("`") && identifier.endsWith("`")) return identifier.slice(1, -1).replaceAll("``", "`").toLowerCase();
if (identifier.startsWith("[") && identifier.endsWith("]")) return identifier.slice(1, -1).toLowerCase();
return identifier.toLowerCase();
}
function normalizeSchemaSql(sql) {
if (sql === null) return null;
return normalizeSqlWhitespace(sql).replace(/;\s*$/u, "").trim().replace(/^(CREATE (?:TABLE|VIRTUAL TABLE|UNIQUE INDEX|INDEX|TRIGGER)) IF NOT EXISTS /iu, "$1 ");
}
function splitSqlList(sql) {
const items = [];
let depth = 0;
let start = 0;
for (const index of unquotedSqlIndexes(sql, 0)) {
const char = sql[index];
if (char === "(") depth += 1;
else if (char === ")") depth -= 1;
else if (char === "," && depth === 0) {
items.push(sql.slice(start, index));
start = index + 1;
}
}
items.push(sql.slice(start));
return items;
}
function findSqlCharacter(sql, character) {
for (const index of unquotedSqlIndexes(sql, 0)) if (sql[index] === character) return index;
return -1;
}
function findSqlClosingParenthesis(sql, open) {
let depth = 0;
for (const index of unquotedSqlIndexes(sql, open)) {
const char = sql[index];
if (char === "(") depth += 1;
else if (char === ")") {
depth -= 1;
if (depth === 0) return index;
}
}
throw new Error("SQLite schema contains an unterminated table definition.");
}
function normalizeSqlWhitespace(sql) {
let normalized = "";
let pendingSpace = false;
const segments = /[^\s'"`[/-]+|\s+|./gsu;
let segment;
while (segment = segments.exec(sql)) {
const index = segment.index;
const char = segment[0][0] ?? "";
const quoted = skipSqlQuoted(sql, index, char);
let text = segment[0];
if (quoted !== index) {
text = sql.slice(index, quoted);
segments.lastIndex = quoted;
} else {
const comment = skipSqlComment(sql, index);
if (comment !== index) {
pendingSpace = true;
segments.lastIndex = comment;
continue;
}
if (/\s/u.test(char)) {
pendingSpace = true;
continue;
}
}
if (pendingSpace && normalized.length > 0) normalized += " ";
normalized += text;
pendingSpace = false;
}
return normalized.trim();
}
function quoteSqliteIdentifier(identifier) {
return `"${identifier.replaceAll("\"", "\"\"")}"`;
}
function* unquotedSqlIndexes(sql, start) {
let index = start;
while (index < sql.length) {
const quoted = skipSqlQuoted(sql, index, sql[index] ?? "");
const next = quoted !== index ? quoted : skipSqlComment(sql, index);
if (next !== index) index = next;
else {
yield index;
index += 1;
}
}
}
function skipSqlQuoted(sql, index, quote) {
if (quote !== "'" && quote !== "\"" && quote !== "`" && quote !== "[") return index;
const closingQuote = quote === "[" ? "]" : quote;
let cursor = index + 1;
while (cursor < sql.length) {
if (sql[cursor] !== closingQuote) {
cursor += 1;
continue;
}
if (quote !== "[" && sql[cursor + 1] === closingQuote) {
cursor += 2;
continue;
}
return cursor + 1;
}
return sql.length;
}
function skipSqlComment(sql, index) {
if (sql.startsWith("--", index)) {
const newline = sql.indexOf("\n", index + 2);
return newline === -1 ? sql.length : newline + 1;
}
if (sql.startsWith("/*", index)) {
const close = sql.indexOf("*/", index + 2);
return close === -1 ? sql.length : close + 2;
}
return index;
}
//#endregion
//#region src/infra/sqlite-schema-mutation.ts
function changesSchema(sql) {
if (!/\b(?:CREATE|ALTER|DROP|REINDEX|VACUUM)\b|\bPRAGMA\b[\s\S]*\b(?:user_version|schema_version|writable_schema)\b[\s\S]*[=(]/i.test(sql)) return false;
return changesOnlyTemporaryTable(sql) ? "temp" : true;
}
function changesOnlyTemporaryTable(sql) {
const normalized = normalizeSqlWhitespace(sql);
const end = findSqlCharacter(normalized, ";");
if (end >= 0 && normalized.slice(end + 1).trim() !== "") return false;
return /^CREATE\s+(?:TEMP|TEMPORARY)\s+TABLE\b/iu.test(normalized) || /^DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:temp|"temp"|`temp`|\[temp\])\s*\./iu.test(normalized);
}
function changesData(sql) {
return /\b(?:INSERT|UPDATE|DELETE|REPLACE)\b/i.test(sql);
}
const transactionControlPrefix = /^(?:\s|;|--[^\n]*(?:\n|$)|\/\*(?:[^*]|\*(?!\/))*\*\/)*(BEGIN|SAVEPOINT|COMMIT|END|RELEASE|ROLLBACK)\b/i;
function batchTransactionControl(sql) {
let control;
let statements = 0;
let remaining = sql;
while (remaining) {
if (remaining.trim()) statements += 1;
const next = transactionControlPrefix.exec(remaining)?.[1]?.toUpperCase();
if (next === "ROLLBACK") control = next;
control ||= next;
const end = remaining.includes(";") ? findSqlCharacter(remaining, ";") : -1;
if (end < 0) break;
remaining = remaining.slice(end + 1);
}
return control ? {
kind: control,
single: statements === 1
} : void 0;
}
function canPreserveTransactionSnapshot(control, inTransaction) {
return Boolean(inTransaction && control?.single && (control.kind === "SAVEPOINT" || control.kind === "RELEASE" || control.kind === "ROLLBACK"));
}
/** Mutation hints preserve the schema owner's policy; they never certify a committed write set. */
function classifySqliteMutation(sql, mode) {
const controlKind = transactionControlPrefix.exec(sql)?.[1]?.toUpperCase();
return {
schemaChange: changesSchema(sql),
dataChange: changesData(sql),
control: mode === "batch" ? batchTransactionControl(sql) : controlKind ? {
kind: controlKind,
single: true
} : void 0
};
}
//#endregion
//#region src/infra/sqlite-native-observer.ts
const pending = resolveGlobalSingleton(Symbol.for("openclaw.sqliteNativeExecution"), () => /* @__PURE__ */ new WeakMap());
const iteratorFinalizer = new FinalizationRegistry((lifetimes) => {
for (const lifetime of lifetimes) lifetime.finish(false, true);
});
function createIteratorLifetime(active, activeIterators) {
const lifetime = {
finished: false,
expire: () => lifetime.observed?.expire?.(),
pause: () => lifetime.stopObservation(),
invalidate: () => {
lifetime.finish();
},
stopObservation: (succeeded = false, abandoned = false) => {
const previous = lifetime.observed;
lifetime.observed = void 0;
activeIterators.delete(lifetime);
previous?.finish(succeeded, abandoned);
},
finish: (succeeded = false, abandoned = false) => {
if (!lifetime.finished) {
lifetime.finished = true;
active.delete(lifetime);
lifetime.stopObservation(succeeded, abandoned);
}
}
};
active.add(lifetime);
return lifetime;
}
const bindingMutation = {
schemaChange: false,
dataChange: false,
control: void 0
};
function refusedBeforeReset(error) {
return error instanceof Error && hasErrnoCode(error, "ERR_INVALID_STATE") && [
"statement has been finalized",
"database cannot be accessed from an authorizer callback",
"statement is already being executed",
"iterator was invalidated"
].includes(error.message);
}
/** Dispose callbacks may refuse close; native custody expires only after SQLite has closed. */
function observeSqliteNativeClose(database, onClose) {
for (const method of ["close", Symbol.dispose]) {
if (typeof database[method] !== "function") continue;
const close = database[method].bind(database);
database[method] = () => {
try {
close();
} finally {
if (!database.isOpen) onClose();
}
};
}
}
function callStatement(method, bindings) {
return Reflect.apply(method, void 0, bindings);
}
/** Native callbacks can mutate from any SQL, including SELECT and retained prepared statements. */
function observeSqliteNativeOperations(database, native, observe) {
const begin = (mutation, phase) => {
const observed = observe(mutation, phase);
pending.set(database, (pending.get(database) ?? 0) + 1);
return {
...observed,
finish(succeeded, abandoned) {
try {
observed.finish(succeeded, abandoned);
} finally {
const remaining = (pending.get(database) ?? 1) - 1;
if (remaining === 0) pending.delete(database);
else pending.set(database, remaining);
}
}
};
};
const activeIterators = /* @__PURE__ */ new Set();
const failures = [];
const isPreResetRefusal = (error) => refusedBeforeReset(error) && !failures.at(-1)?.has(error);
const propagate = (error) => {
if (failures.length > 1) (failures[failures.length - 2] ??= /* @__PURE__ */ new Set()).add(error);
};
const execute = (operation, mutation, phase = "execute") => {
const observed = begin(mutation, phase);
let succeeded = false;
failures.push(void 0);
try {
const result = operation();
if (phase !== "bind") observed.stepped?.();
succeeded = true;
return result;
} catch (error) {
propagate(error);
throw error;
} finally {
failures.pop();
observed.finish(succeeded);
}
};
database.exec = (sql) => execute(() => native.DatabaseSync.prototype.exec.call(database, sql), classifySqliteMutation(sql, "batch"), "exec");
database.prepare = (...prepareArgs) => {
const statement = native.DatabaseSync.prototype.prepare.call(database, ...prepareArgs);
const mutation = classifySqliteMutation(prepareArgs[0], "statement");
const run = Object.hasOwn(statement, "run") ? statement.run.bind(statement) : void 0;
const get = Object.hasOwn(statement, "get") ? statement.get.bind(statement) : void 0;
const all = Object.hasOwn(statement, "all") ? statement.all.bind(statement) : void 0;
const iterate = Object.hasOwn(statement, "iterate") ? statement.iterate.bind(statement) : void 0;
const active = /* @__PURE__ */ new Set();
let registered = false;
const reset = (operation, kind = "reuse") => {
if (active.size === 0) return operation();
const previous = [...active];
for (const lifetime of previous) lifetime.expire();
let result;
try {
result = operation();
} catch (error) {
if (kind === "reuse" && !isPreResetRefusal(error)) for (const lifetime of previous) try {
lifetime.invalidate();
} catch {}
throw error;
}
for (const lifetime of previous) lifetime.invalidate();
return result;
};
const wrap = (resolve) => (...bindings) => execute(() => reset(() => callStatement(resolve(), bindings)), mutation);
statement.run = wrap(() => run ?? native.StatementSync.prototype.run.bind(statement));
statement.get = wrap(() => get ?? native.StatementSync.prototype.get.bind(statement));
statement.all = wrap(() => all ?? native.StatementSync.prototype.all.bind(statement));
statement.iterate = (...bindings) => {
if (!registered) {
iteratorFinalizer.register(statement, active);
registered = true;
}
const rows = execute(() => reset(() => callStatement(iterate ?? native.StatementSync.prototype.iterate.bind(statement), bindings)), bindingMutation, "bind");
const reference = new WeakRef(rows);
const lifetime = createIteratorLifetime(active, activeIterators);
const nativeNext = rows.next;
const nativeReturn = rows.return;
rows.next = function(...args) {
if (this !== reference.deref() || !database.isOpen) return nativeNext.apply(this, args);
if (lifetime.finished) {
lifetime.finished = false;
active.add(lifetime);
}
const alreadyObserved = lifetime.observed !== void 0;
lifetime.observed ??= begin(mutation, "iterate");
activeIterators.add(lifetime);
failures.push(void 0);
try {
const result = nativeNext.apply(this, args);
if (result.done) lifetime.finish(true);
else lifetime.observed?.stepped?.();
return result;
} catch (error) {
if (!alreadyObserved && isPreResetRefusal(error)) lifetime.pause();
propagate(error);
throw error;
} finally {
failures.pop();
}
};
if (nativeReturn) rows.return = function(...args) {
if (this !== reference.deref() || !database.isOpen) return nativeReturn.apply(this, args);
const result = execute(() => nativeReturn.apply(this, args), bindingMutation, "bind");
lifetime.finish();
return result;
};
return rows;
};
for (const method of ["close", Symbol.dispose]) {
const close = statement[method]?.bind(statement);
if (close) statement[method] = () => reset(close, "finalize");
}
return statement;
};
observeSqliteNativeClose(database, () => {
for (const lifetime of activeIterators) lifetime.invalidate();
});
return execute;
}
//#endregion
//#region src/infra/sqlite-pinned-read-snapshot.ts
const snapshots = resolveGlobalSingleton(Symbol.for("openclaw.sqlitePinnedReadSnapshots"), () => /* @__PURE__ */ new WeakMap());
function getSqlitePinnedReadSnapshot(db) {
return snapshots.get(db);
}
function readSqliteVersionObservation(database, previousDataVersion) {
const parameters = [previousDataVersion, previousDataVersion];
const row = executeWithCachedStatement(database, `SELECT data_version,
CASE WHEN data_version <> ? THEN
(SELECT schema_version FROM main.pragma_schema_version()) END AS schema_version,
CASE WHEN data_version <> ? THEN
(SELECT user_version FROM main.pragma_user_version()) END AS user_version
FROM main.pragma_data_version()`, parameters, (statement) => statement.get(...parameters));
if (typeof row?.data_version !== "number") throw new Error("SQLite did not return a numeric PRAGMA data_version");
return {
dataVersion: row.data_version,
schemaVersion: row.schema_version,
userVersion: row.user_version
};
}
function readChangedSqliteSchemaMarkers(database, facts, observation) {
if (observation) return facts.schemaVersion === observation.schemaVersion && facts.userVersion === observation.userVersion ? void 0 : {
schemaVersion: Number(observation.schemaVersion),
userVersion: Number(observation.userVersion)
};
return runSqlitePinnedReadSnapshotSync(database, (schemaVersion) => {
const userVersion = executeWithCachedStatement(database, "PRAGMA user_version", [], (s) => s.get());
return facts.schemaVersion === schemaVersion && facts.userVersion === userVersion?.user_version ? void 0 : {
schemaVersion,
userVersion: Number(userVersion?.user_version)
};
});
}
/** Pin an implicit read snapshot without requiring transaction-control authorization. */
function runSqlitePinnedReadSnapshotSync(db, operation) {
const parent = snapshots.get(db);
snapshots.set(db, parent ?? {});
try {
return executeWithCachedStatement(db, "PRAGMA schema_version", [], (statement) => {
const snapshot = statement.iterate();
try {
const first = snapshot.next();
if (first.done) throw new Error("SQLite schema version query returned no row");
return operation(Number(first.value.schema_version));
} finally {
snapshot.return?.();
}
});
} finally {
if (!parent) snapshots.delete(db);
}
}
//#endregion
//#region src/infra/sqlite-temp-generation-schema.ts
function prepareSqliteTempTrackingSchema(database, schema) {
const { sql, objects } = schema.kind === "generation" ? prepareGenerationSchema(schema) : prepareTranscriptIndexSchema(schema);
const definitions = new Map(objects.map((object) => [object.name.toLowerCase(), object]));
const names = objects.map((object) => object.name);
return {
sql,
unexpected: executeWithCachedStatement(database, `SELECT type, name, tbl_name, sql FROM temp.sqlite_schema WHERE name COLLATE NOCASE IN (${names.map(() => "?").join(",")})`, names, (statement) => statement.all(...names)).some((row) => {
const declared = typeof row.name === "string" ? definitions.get(row.name.toLowerCase()) : void 0;
return !declared || row.type !== declared.type || row.tbl_name !== declared.table || typeof row.sql !== "string" || normalizeSchemaSql(row.sql) !== normalizeSchemaSql(declared.sql);
})
};
}
function prepareGenerationSchema(schema) {
const table = quoteSqliteIdentifier(schema.table);
const counter = `${table} (id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1), generation INTEGER NOT NULL) STRICT`;
const increment = `UPDATE ${table} SET generation = generation + 1 WHERE id = 1;`;
const triggers = schema.triggers.map((trigger) => ({
...trigger,
definition: `TRIGGER ${quoteSqliteIdentifier(trigger.name)} AFTER ${trigger.operation} ON main.${quoteSqliteIdentifier(trigger.table)} BEGIN ${increment} END`
}));
return {
sql: `
CREATE TEMP TABLE IF NOT EXISTS ${counter};
INSERT OR IGNORE INTO temp.${table} (id, generation) VALUES (1, 0);
${schema.advance ? `UPDATE temp.${table} SET generation = generation + 1 WHERE id = 1;` : ""}
${triggers.map(({ name, definition, enabled }) => {
return `DROP TRIGGER IF EXISTS temp.${quoteSqliteIdentifier(name)};${enabled ? `CREATE TEMP ${definition};` : ""}`;
}).join("\n")}
`,
objects: [{
name: schema.table,
type: "table",
table: schema.table,
sql: `CREATE TABLE ${counter}`
}, ...triggers.map((trigger) => ({
name: trigger.name,
type: "trigger",
table: trigger.table,
sql: `CREATE ${trigger.definition}`
}))]
};
}
function prepareTranscriptIndexSchema(schema) {
const status = quoteSqliteIdentifier(schema.statusTable);
const pending = quoteSqliteIdentifier(schema.pendingTable);
const index = quoteSqliteIdentifier(schema.pendingIndex);
const statusTable = `${status} (
id INTEGER PRIMARY KEY CHECK (id = 1), data_version INTEGER NOT NULL,
schema_version INTEGER NOT NULL, complete INTEGER NOT NULL, after_session TEXT,
completed_traversals INTEGER NOT NULL
) STRICT`;
const pendingTable = `${pending} (session_id TEXT PRIMARY KEY, state INTEGER NOT NULL) STRICT`;
const pendingIndex = `${index} ON ${pending}(state, session_id)`;
const triggers = schema.observedTables.flatMap((table) => [
"INSERT",
"UPDATE",
"DELETE"
].map((operation) => {
const name = `openclaw_${table}_projection_${operation.toLowerCase()}`;
const sources = operation === "UPDATE" ? ["OLD", "NEW"] : [operation === "DELETE" ? "OLD" : "NEW"];
return {
name,
table,
definition: `TRIGGER ${quoteSqliteIdentifier(name)} AFTER ${operation} ON main.${quoteSqliteIdentifier(table)}
WHEN ${sources.map((source) => `NOT EXISTS (
SELECT 1 FROM ${pending}
WHERE session_id = ${source}.session_id AND state = 0
)`).join(" OR ")} BEGIN
${sources.map((source) => `
UPDATE ${pending} SET state = 0 WHERE session_id = ${source}.session_id;
INSERT INTO ${pending}
SELECT ${source}.session_id, 0 WHERE NOT EXISTS (
SELECT 1 FROM ${pending} WHERE session_id = ${source}.session_id
);`).join("\n")}
END`
};
}));
return {
sql: `
CREATE TEMP TABLE IF NOT EXISTS ${statusTable};
INSERT OR IGNORE INTO temp.${status} VALUES (1, -1, -1, 0, NULL, 0);
UPDATE temp.${status} SET data_version = -1;
CREATE TEMP TABLE IF NOT EXISTS ${pendingTable};
CREATE INDEX IF NOT EXISTS temp.${pendingIndex};
${triggers.map(({ name, definition }) => `DROP TRIGGER IF EXISTS temp.${quoteSqliteIdentifier(name)};CREATE TEMP ${definition};`).join("\n")}
`,
objects: [
{
name: schema.statusTable,
type: "table",
table: schema.statusTable,
sql: `CREATE TABLE ${statusTable}`
},
{
name: schema.pendingTable,
type: "table",
table: schema.pendingTable,
sql: `CREATE TABLE ${pendingTable}`
},
{
name: schema.pendingIndex,
type: "index",
table: schema.pendingTable,
sql: `CREATE INDEX ${pendingIndex}`
},
...triggers.map((trigger) => ({
name: trigger.name,
type: "trigger",
table: trigger.table,
sql: `CREATE ${trigger.definition}`
}))
]
};
}
//#endregion
//#region src/infra/sqlite-schema-facts.ts
const scopes = resolveGlobalSingleton(Symbol.for("openclaw.sqliteSchemaScopes"), () => {
const byIdentity = /* @__PURE__ */ new Map();
const release = (scope) => {
scope.users -= 1;
if (scope.users === 0 && scope.key && byIdentity.get(scope.key) === scope) byIdentity.delete(scope.key);
};
return {
byIdentity,
release,
finalizer: new FinalizationRegistry(release)
};
});
const owners = resolveGlobalSingleton(Symbol.for("openclaw.sqliteSchemaFacts"), () => /* @__PURE__ */ new WeakMap());
function invalidate(owner) {
owner.revision += 1;
owner.facts = void 0;
}
function notifySchemaMutation(owner, observed) {
for (const listener of owner.mutationListeners ?? []) listener(observed);
}
function observeTransactionState(database, owner) {
const inTransaction = database.isTransaction;
if (owner.transactionOpen !== inTransaction) {
owner.readDataVersion = void 0;
if (owner.transactionOpen) owner.mutationRevision += 1;
owner.transactionOpen = inTransaction;
}
}
function bindScope(database, owner) {
if (owner.scope) return owner.scope;
const location = database.location();
const key = location ? readDatabasePathIdentitySync(location).key : void 0;
const scope = key && scopes.byIdentity.get(key) || {
key,
revision: 0,
users: 0
};
if (key) scopes.byIdentity.set(key, scope);
scope.users += 1;
owner.scope = scope;
owner.scopeRevision = scope.revision;
scopes.finalizer.register(database, scope, owner);
return scope;
}
function publishSchemaChange(database, owner) {
const scope = bindScope(database, owner);
scope.revision += 1;
owner.scopeRevision = scope.revision;
}
/** Schema publications outside DDL (such as a deferred version marker) share this revision. */
function invalidateSqliteSchemaFacts(database) {
const owner = owners.get(database);
if (owner) {
notifySchemaMutation(owner);
bindScope(database, owner);
invalidate(owner);
owner.transactionalSchema ||= database.isTransaction;
if (!database.isTransaction) publishSchemaChange(database, owner);
}
}
/** MAIN mutations revoke before execution; TEMP-only table changes expire local facts instead. */
function registerSqliteSchemaMutationListener(database, listener) {
const owner = owners.get(database);
if (!owner) throw new Error("SQLite schema observation requires a tracked connection");
const listeners = owner.mutationListeners ??= /* @__PURE__ */ new Set();
listeners.add(listener);
return () => listeners.delete(listener);
}
function trackSchemaChanges(database, owner, native) {
const settle = (boundary = false) => {
if ((boundary || !database.isTransaction) && (owner.transactionalSchema || owner.transactionalTempSchema || owner.transactionalFacts)) {
if (owner.transactionalSchema) publishSchemaChange(database, owner);
invalidate(owner);
owner.transactionalSchema &&= database.isTransaction;
owner.transactionalTempSchema &&= database.isTransaction;
owner.transactionalFacts = false;
}
};
const finishReadScope = (wasTransaction, expiresRead, succeeded) => {
const inTransaction = database.isTransaction;
if (expiresRead || wasTransaction !== inTransaction) owner.readDataVersion = void 0;
if (!succeeded && wasTransaction && !inTransaction) owner.mutationRevision += 1;
owner.transactionOpen = inTransaction;
};
const beginMutation = (schemaChange, control, dataChange) => {
observeTransactionState(database, owner);
if (owner.transactionalSchema && !owner.scope) bindScope(database, owner);
settle();
const wasTransaction = database.isTransaction;
const expiresRead = Boolean(control) && !canPreserveTransactionSnapshot(control, wasTransaction);
const invalidatesMain = schemaChange === true || control?.kind === "ROLLBACK" && owner.transactionalSchema;
const invalidatesTemp = schemaChange === "temp" || control?.kind === "ROLLBACK" && owner.transactionalTempSchema;
const invalidateMutation = () => {
if (invalidatesMain) invalidateSqliteSchemaFacts(database);
else if (invalidatesTemp) {
invalidate(owner);
owner.transactionalTempSchema ||= database.isTransaction;
}
};
invalidateMutation();
if (dataChange || schemaChange === "temp" || control?.kind === "ROLLBACK") owner.mutationRevision += 1;
if (expiresRead) owner.readDataVersion = void 0;
const changesReadScope = schemaChange || dataChange || control !== void 0;
if (changesReadScope) owner.mutationDepth += 1;
return (succeeded) => {
if (changesReadScope) owner.mutationDepth -= 1;
if (!database.isOpen) return;
invalidateMutation();
settle(Boolean(control));
finishReadScope(wasTransaction, expiresRead, succeeded);
};
};
const execute = observeSqliteNativeOperations(database, native, (mutation, phase) => ({ finish: phase === "exec" || mutation.schemaChange || mutation.control || mutation.dataChange ? beginMutation(mutation.schemaChange, mutation.control, mutation.dataChange) : () => {} }));
owner.installTempTrackingSchema = (schema) => {
const { sql, unexpected } = prepareSqliteTempTrackingSchema(database, schema);
try {
execute(() => native.DatabaseSync.prototype.exec.call(database, sql), {
schemaChange: unexpected,
dataChange: true,
control: void 0
});
} catch (error) {
invalidateSqliteSchemaFacts(database);
throw error;
}
};
if (typeof database.setAuthorizer === "function") database.setAuthorizer = (callback) => {
native.DatabaseSync.prototype.setAuthorizer.call(database, callback);
owner.authorizerActive = callback !== null;
invalidate(owner);
};
registerNodeSqliteDisposeCallback(database, () => {
invalidate(owner);
owner.dataVersion = void 0;
owner.observedDataVersion = void 0;
owner.readDataVersion = void 0;
owner.readRevision = void 0;
if (owner.scope) {
scopes.finalizer.unregister(owner);
scopes.release(owner.scope);
owner.scope = void 0;
owner.scopeRevision = void 0;
}
});
}
/** Stable identity for row facts in the admitted operation's current SQLite snapshot. */
function getSqliteReadScopeRevision(database) {
const owner = owners.get(database);
if (owner) observeTransactionState(database, owner);
if (!owner?.admitted || !owner.facts || owner.authorizerActive || owner.readDepth === 0 || owner.readDataVersion === void 0 || owner.readDataVersion !== owner.observedDataVersion || owner.mutationDepth !== 0) return;
const snapshot = getSqlitePinnedReadSnapshot(database);
const previous = owner.readRevision;
if (previous?.schema === owner.facts && previous.dataVersion === owner.readDataVersion && previous.mutationRevision === owner.mutationRevision && previous.snapshot === snapshot) return previous;
return owner.readRevision = {
schema: owner.facts,
dataVersion: owner.readDataVersion,
mutationRevision: owner.mutationRevision,
snapshot
};
}
/** Share freshness only within this synchronous call stack, never across an await. */
function runSqliteReadOperationSync(database, operation, mode = "cached") {
const owner = owners.get(database);
if (!owner || owner.authorizerActive) return operation();
owner.readDepth += 1;
try {
if (owner.admitted) owner.readDataVersion = readSqliteCacheDataVersion(database, mode);
return operation();
} finally {
owner.readDepth -= 1;
if (owner.readDepth === 0) owner.readDataVersion = void 0;
}
}
/** Always execute a fresh probe; compare versions only on the same connection. */
function readSqliteDataVersion(database) {
const row = executeWithCachedStatement(database, "PRAGMA data_version", [], (statement) => statement.get());
if (typeof row?.data_version !== "number") throw new Error("SQLite did not return a numeric PRAGMA data_version");
const owner = owners.get(database);
if (owner) owner.observedDataVersion = row.data_version;
return row.data_version;
}
/** Admission observes foreign commits; explicit fresh reads never reuse an operation's probe. */
function readSqliteCacheDataVersion(database, mode = "cached") {
const tracked = owners.get(database);
if (tracked) observeTransactionState(database, tracked);
const owner = tracked?.admitted ? tracked : void 0;
if (mode === "cached" && owner && !owner.authorizerActive && owner.readDataVersion !== void 0) return owner.readDataVersion;
const observation = owner?.facts && owner.dataVersion !== void 0 && !owner.authorizerActive ? readSqliteVersionObservation(database, owner.dataVersion) : void 0;
const dataVersion = observation?.dataVersion ?? readSqliteDataVersion(database);
if (owner) {
owner.observedDataVersion = dataVersion;
if (owner.dataVersion !== dataVersion) {
const facts = owner.facts;
const changed = facts && readChangedSqliteSchemaMarkers(database, facts, observation);
if (!facts || changed) {
if (changed) notifySchemaMutation(owner, changed);
invalidate(owner);
}
owner.dataVersion = dataVersion;
}
if (mode === "cached" && owner.readDepth > 0 && !owner.authorizerActive) owner.readDataVersion = dataVersion;
}
return dataVersion;
}
/** Install at native open, before callers can retain statements or install an authorizer. */
function trackSqliteSchema(database, native) {
if (!owners.has(database)) {
const owner = {
admitted: false,
revision: 0,
readDepth: 0,
mutationRevision: 0,
mutationDepth: 0,
transactionOpen: database.isTransaction,
transactionalSchema: false,
transactionalTempSchema: false,
transactionalFacts: false,
authorizerActive: false
};
owners.set(database, owner);
trackSchemaChanges(database, owner, native);
}
}
/** Select first ordinary admission without querying or authorizing a connection. */
function isSqliteSchemaAdmissionCold(database) {
const owner = owners.get(database);
return Boolean(owner && !owner.admitted && !owner.authorizerActive);
}
/** Admission retains schema facts; its header validator must stay synchronous and read-free. */
function admitSqliteSchema(database, validateUserVersion) {
const owner = owners.get(database);
if (!owner) throw new Error("SQLite schema admission requires a connection tracked from native open");
owner.admitted = true;
readSqliteCacheDataVersion(database);
getAdmittedSqliteSchemaFacts(database, validateUserVersion);
}
function observeSchemaLifetime(database, owner, snapshot) {
if (owner.snapshot && owner.snapshot !== snapshot) {
invalidate(owner);
owner.snapshot = void 0;
}
const scope = bindScope(database, owner);
const scopeChanged = owner.scopeRevision !== scope.revision;
if (scopeChanged) {
invalidate(owner);
owner.scopeRevision = scope.revision;
}
if ((owner.transactionalSchema || owner.transactionalTempSchema || owner.transactionalFacts) && !database.isTransaction) {
if (owner.transactionalSchema) publishSchemaChange(database, owner);
invalidate(owner);
owner.transactionalSchema = false;
owner.transactionalTempSchema = false;
owner.transactionalFacts = false;
}
return scopeChanged;
}
/** Consume admitted facts; operation admission owns foreign-commit freshness. */
function getAdmittedSqliteSchemaFacts(database, validateUserVersion) {
const owner = owners.get(database);
if (!owner?.admitted || owner.authorizerActive) return;
const snapshot = getSqlitePinnedReadSnapshot(database);
const scopeChanged = observeSchemaLifetime(database, owner, snapshot);
if (!owner.facts) {
owner.snapshot = snapshot;
owner.transactionalFacts ||= database.isTransaction && (owner.readDepth === 0 || scopeChanged);
owner.facts = runSqlitePinnedReadSnapshotSync(database, (schemaVersion) => {
const version = executeWithCachedStatement(database, "PRAGMA user_version", [], (s) => s.get());
const userVersion = Number(version?.user_version ?? 0);
validateUserVersion?.(userVersion);
const objects = executeWithCachedStatement(database, "SELECT type, name, tbl_name, sql FROM main.sqlite_schema WHERE type IN ('table', 'index', 'trigger')", [], (s) => s.all());
const tables = /* @__PURE__ */ new Set();
const tableSql = /* @__PURE__ */ new Map();
const indexes = /* @__PURE__ */ new Set();
const triggers = /* @__PURE__ */ new Map();
for (const row of objects) {
if (typeof row.name !== "string") continue;
const sql = typeof row.sql === "string" ? row.sql : null;
if (row.type === "table") {
tables.add(row.name);
tableSql.set(row.name, sql);
} else if (row.type === "index") indexes.add(row.name);
else if (row.type === "trigger" && typeof row.tbl_name === "string") triggers.set(row.name, {
table: row.tbl_name,
sql
});
}
return {
revision: owner.revision,
userVersion,
schemaVersion,
tables,
tableSql,
indexes,
triggers
};
});
} else validateUserVersion?.(owner.facts.userVersion);
return owner.facts;
}
//#endregion
//#region src/infra/warning-filter.ts
const warningFilterKey = Symbol.for("openclaw.warning-filter");
function shouldIgnoreWarning(warning) {
return Boolean(warning.code === "DEP0040" && warning.message?.includes("punycode") || warning.code === "DEP0060" && warning.message?.includes("util._extend") || warning.name === "ExperimentalWarning" && warning.message?.includes("SQLite is an experimental feature"));
}
function normalizeWarningArgs(args) {
const warningArg = args[0];
const secondArg = args[1];
const thirdArg = args[2];
let name;
let code;
let message;
if (warningArg instanceof Error) {
name = warningArg.name;
message = warningArg.message;
code = warningArg.code;
} else if (typeof warningArg === "string") message = warningArg;
if (secondArg && typeof secondArg === "object" && !Array.isArray(secondArg)) {
const options = secondArg;
if (typeof options.type === "string") name = options.type;
if (typeof options.code === "string") code = options.code;
} else {
if (typeof secondArg === "string") name = secondArg;
if (typeof thirdArg === "string") code = thirdArg;
}
return {
name,
code,
message
};
}
/** Installs the global process warning filter once for the current JS realm. */
function installProcessWarningFilter() {
const state = resolveGlobalSingleton(warningFilterKey, () => ({ installed: false }));
if (state.installed) return;
const originalEmitWarning = process.emitWarning.bind(process);
const wrappedEmitWarning = ((...args) => {
if (shouldIgnoreWarning(normalizeWarningArgs(args))) return;
if (args[0] instanceof Error && args[1] && typeof args[1] === "object" && !Array.isArray(args[1])) {
const warning = args[0];
const emitted = Object.assign(new Error(warning.message), {
name: warning.name,
code: warning.code
});
process.emit("warning", emitted);
return;
}
Reflect.apply(originalEmitWarning, process, args);
});
process.emitWarning = wrappedEmitWarning;
state.installed = true;
}
//#endregion
//#region src/infra/node-sqlite.ts
const require = createRequire(import.meta.url);
let validatedSqliteModule;
function resolveSqliteFilesystemPath(pathname) {
if (process.platform !== "win32") return pathname;
return path.toNamespacedPath(path.resolve(pathname));
}
function resolveNodeSqliteLocation(location) {
if (location === "" || location === ":memory:" || location.startsWith("file:")) return location;
return resolveSqliteFilesystemPath(location);
}
/** Preserve native Windows path prefixes before adding SQLite URI parameters. */
function resolveSqliteFileUriPath(pathname, platform) {
if (platform === "win32") {
const namespacedPath = path.win32.toNamespacedPath(path.win32.resolve(pathname));
return `file:${encodeURIComponent(namespacedPath)}`;
}
return pathToFileURL(path.resolve(pathname)).href;
}
/** Open an existing writable database without SQLite's create-if-missing flag. */
function resolveExistingSqliteFileUri(pathname, platform = process.platform) {
return `${resolveSqliteFileUriPath(pathname, platform)}?mode=rw`;
}
function assertSqliteWalResetSafeVersion(version, nodeVersion) {
if (isSqliteWalResetSafeVersion(version)) return;
const variables = process.config?.variables;
const isShared = variables?.node_shared_sqlite === true || variables?.node_shared_sqlite === "true";
throw new Error(`OpenClaw requires SQLite 3.51.3+, 3.50.7+ within 3.50.x, or 3.44.6+ within 3.44.x for WAL safety; Node ${nodeVersion} ${isShared ? "uses shared system" : "embeds"} SQLite ${version}, which is affected by the upstream WAL-reset database corruption bug. ${isShared ? "Upgrade the system SQLite library to one of those safe versions, or use a Node build embedding a safe version." : "Upgrade to Node 24.22.1+ or 26.12.1+ before retrying."}`);
}
function sqliteNativeRuntimeIdentity() {
return {
pid: process.pid,
executable: process.execPath,
nodeVersion: process.versions.node,
bunVersion: process.versions.bun,
library: ensureSqliteLibrarySelected()
};
}
function parseSqliteNativeRuntimeAdmission(value) {
if (!isRecord(value) || value.format !== 1 || typeof value.version !== "string" || typeof value.extensionLoadingSupported !== "boolean") return;
const runtime = sqliteNativeRuntimeIdentity();
if (!isDeepStrictEqual(value.runtime, runtime)) return;
return {
format: 1,
runtime,
version: value.version,
extensionLoadingSupported: value.extensionLoadingSupported
};
}
function safeSqliteNativeRuntimeAdmission(value) {
try {
const admission = parseSqliteNativeRuntimeAdmission(value);
return admission && isSqliteWalResetSafeVersion(admission.version) ? admission : void 0;
} catch {
return;
}
}
/** Publish to later sibling workers; database authority remains with its existing owners. */
function installSqliteNativeRuntimeAdmission(value) {
const admission = safeSqliteNativeRuntimeAdmission(value);
if (admission) setEnvironmentData(SQLITE_NATIVE_RUNTIME_ADMISSION_KEY, admission);
}
function assertSafeSqliteRuntime(sqlite) {
if (validatedSqliteModule === sqlite) return;
const inherited = isMainThread ? void 0 : parseSqliteNativeRuntimeAdmission(getEnvironmentData(SQLITE_NATIVE_RUNTIME_ADMISSION_KEY));
if (inherited) {
assertSqliteWalResetSafeVersion(inherited.version, process.versions.node);
compareValidSemver(inherited.version, "3.45.0");
compareValidSemver(inherited.version, "3.53.0");
inherited.extensionLoadingSupported;
validatedSqliteModule = sqlite;
return;
}
const database = new sqlite.DatabaseSync(":memory:");
let version;
let extensions;
try {
const row = database.prepare("SELECT sqlite_version() AS version, sqlite_compileoption_used('OMIT_LOAD_EXTENSION') AS omitted").get();
version = typeof row?.version === "string" ? row.version : "unknown";
assertSqliteWalResetSafeVersion(version, process.versions.node);
extensions = row?.omitted === 0;
} finally {
database.close();
}
compareValidSemver(version, "3.45.0");
compareValidSemver(version, "3.53.0");
validatedSqliteModule = sqlite;
setEnvironmentData(SQLITE_NATIVE_RUNTIME_ADMISSION_KEY, {
format: 1,
runtime: sqliteNativeRuntimeIdentity(),
version,
extensionLoadingSupported: extensions
});
}
/** Load node:sqlite after installing the process warning filter. */
function requireNodeSqlite() {
installProcessWarningFilter();
try {
ensureSqliteLibrarySelected();
const sqlite = require("node:sqlite");
assertSafeSqliteRuntime(sqlite);
return sqlite;
} catch (err) {
const message = formatErrorMessage(err);
throw new Error(`SQLite support is unavailable or unsafe in this Node runtime. ${message}`, { cause: err });
}
}
/** Open node:sqlite through OpenClaw's runtime and filesystem-location boundary. */
function openNodeSqliteDatabase(location, options) {
const sqlite = requireNodeSqlite();
const resolvedLocation = resolveNodeSqliteLocation(location);
const database = new sqlite.DatabaseSync(resolvedLocation, options ?? {});
trackSqliteSchema(database, sqlite);
if (!getSqliteRuntimeCapabilities().explicitSqliteCloseReleasesNativeResources) registerNodeSqliteDisposeCallback(database, () => {});
registerSqliteReaderConnection(database);
return database;
}
//#endregion
//#region src/infra/sqlite-busy-timeout.ts
const lockFailureReportingByDatabase = /* @__PURE__ */ new WeakMap();
function normalizeSqliteNonNegativeInteger(value, label) {
if (!Number.isInteger(value) || value < 0) throw new Error(`${label} must be a non-negative integer`);
return value;
}
function readSqliteBusyTimeout(database) {
const row = database.prepare("PRAGMA busy_timeout").get();
const value = row?.busy_timeout ?? row?.timeout;
return typeof value === "bigint" ? Number(value) : Number(value ?? 0);
}
function setSqliteBusyTimeout(database, busyTimeoutMs) {
const normalizedTimeoutMs = normalizeSqliteNonNegativeInteger(busyTimeoutMs, "busyTimeoutMs");
database.exec(`PRAGMA busy_timeout = ${normalizedTimeoutMs}`);
}
function shouldReportSqliteLockFailure(database) {
return lockFailureReportingByDatabase.get(database) !== "suppress";
}
/** Run with a temporary busy policy; restore early when write admission finishes. */
function runWithSqliteBusyTimeout(database, busyTimeoutMs, operation, options = {}) {
const normalizedTimeoutMs = normalizeSqliteNonNegativeInteger(busyTimeoutMs, "busyTimeoutMs");
const previousBusyTimeoutMs = readSqliteBusyTimeout(database);
const previousLockFailureReporting = lockFailureReportingByDatabase.get(database);
if (options.lockFailureReporting) lockFailureReportingByDatabase.set(database, options.lockFailureReporting);
if (previousBusyTimeoutMs !== normalizedTimeoutMs) setSqliteBusyTimeout(database, normalizedTimeoutMs);
let restored = false;
const restore = () => {
if (restored) return;
if (database.isOpen && previousBusyTimeoutMs !== normalizedTimeoutMs) setSqliteBusyTimeout(database, previousBusyTimeoutMs);
if (previousLockFailureReporting) lockFailureReportingByDatabase.set(database, previousLockFailureReporting);
else lockFailureReportingByDatabase.delete(database);
restored = true;
};
try {
return operation(restore);
} finally {
restore();
}
}
var OpenClawStateDatabaseSchemaMigrationRequiredError = class extends StartupMaintenanceRequiredError {
constructor(kind, pathname) {
super(kind, `OpenClaw state database schema migration required (${kind}) at ${pathname}; run openclaw doctor --fix to migrate it.`);
this.kind = kind;
this.pathname = pathname;
this.name = "OpenClawStateDatabaseSchemaMigrationRequiredError";
}
};
/** Runtime readers report malformed legacy state without entering repair mode. */
function normalizeOpenClawStateSchemaReadError(error, pathname) {
if (error instanceof Error && error.message.startsWith(`malformed database schema (idx_skill_workshop_collection_reviews_workspace_time)`)) {
const required = new OpenClawStateDatabaseSchemaMigrationRequiredError("legacy-workshop-review-index", pathname);
required.cause = error;
return required;
}
return error;
}
//#endregion
export { splitSqlList as A, readSqliteReaderDiagnosticsForPath as B, findSqlClosingParenthesis as C, quoteSqliteIdentifier as D, normalizeSqlWhitespace as E, getNodeSqliteKysely as F, iterateSqliteQuerySync as I, prepareSqliteQuerySync as L, createSqliteQueryCache as M, executeSqliteQuerySync as N, readSqlToken as O, executeSqliteQueryTakeFirstSync as P, captureSqliteReaderOwner as R, findSqlCharacter as S, normalizeSqlIdentifier as T, sqliteReaderDatabasePathKey as V, isSqliteSchemaAdmissionCold as _, runWithSqliteBusyTimeout as a, runSqlitePinnedReadSnapshotSync as b, installSqliteNativeRuntimeAdmission as c, resolveExistingSqliteFileUri as d, resolveSqliteFilesystemPath as f, invalidateSqliteSchemaFacts as g, getSqliteReadScopeRevision as h, readSqliteBusyTimeout as i, compileSqliteQueryBindings as j, readTableConstraintKeyword as k, openNodeSqliteDatabase as l, getAdmittedSqliteSchemaFacts as m, normalizeOpenClawStateSchemaReadError as n, setSqliteBusyTimeout as o, admitSqliteSchema as p, normalizeSqliteNonNegativeInteger as r, shouldReportSqliteLockFailure as s, OpenClawStateDatabaseSchemaMigrationRequiredError as t, requireNodeSqlite as u, registerSqliteSchemaMutationListener as v, normalizeSchemaSql as w, extractSqliteTableSchema as x, runSqliteReadOperationSync as y, currentSqliteOperationTiming as z };