openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
147 lines (146 loc) • 6.52 kB
JavaScript
import { n as resolvePathViaExistingAncestorSync } from "./boundary-path-DMNeww4q.js";
import { o as tryAcquireExclusiveSqliteCoordinator } from "./node-sqlite-BpQX3W0e.js";
import { t as applyPrivateModeSync } from "./private-mode-B6dWGRb2.js";
import { o as sha256HexPrefixCore } from "./crypto-digest-C4hqTb_e.js";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
//#region src/infra/sqlite-coordinator.ts
var SqliteCoordinatorError = class extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = "SqliteCoordinatorError";
}
};
function createSqliteLifecycleAggregateError(errors, message, cause) {
return new AggregateError(errors, message, { cause });
}
function runWithSqliteCoordinator(coordinator, operationLabel, operation) {
let result;
try {
result = operation();
if (result && typeof result.then === "function") throw new SqliteCoordinatorError(`${operationLabel} must remain synchronous`);
} catch (operationError) {
let releaseFailed = false;
let releaseError;
try {
coordinator.release();
} catch (error) {
releaseFailed = true;
releaseError = error;
}
if (releaseFailed) throw createSqliteLifecycleAggregateError([operationError, releaseError], `${operationLabel} and coordinator release both failed`, operationError);
throw operationError;
}
try {
coordinator.release();
} catch (releaseError) {
throw new SqliteCoordinatorError(`${operationLabel} completed, but releasing its coordinator failed`, releaseError);
}
return result;
}
function ensurePrivateSqliteCoordinatorDirectory(directoryPath, coordinatorLabel) {
try {
fs.mkdirSync(directoryPath, {
mode: 448,
recursive: true
});
} catch (error) {
if (error.code !== "EEXIST") throw error;
}
const stats = fs.lstatSync(directoryPath);
if (stats.isSymbolicLink() || !stats.isDirectory()) throw new SqliteCoordinatorError(`${coordinatorLabel} directory must be a real directory`);
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
if (uid !== void 0 && stats.uid !== uid) throw new SqliteCoordinatorError(`${coordinatorLabel} directory belongs to another user`);
if (process.platform !== "win32") {
if ((stats.mode & 4095) !== 448) applyPrivateModeSync(directoryPath, 448);
const secured = fs.lstatSync(directoryPath);
if (secured.isSymbolicLink() || !secured.isDirectory() || (secured.mode & 63) !== 0) throw new SqliteCoordinatorError(`${coordinatorLabel} directory permissions are not private`);
}
}
//#endregion
//#region src/infra/state-database-coordinator.ts
const heldCoordinators = /* @__PURE__ */ new Map();
var StateDatabaseCoordinatorContentionError = class extends SqliteCoordinatorError {
constructor(family) {
super(`another OpenClaw process owns ${family}`);
this.name = "StateDatabaseCoordinatorContentionError";
}
};
var StateSchemaMutationConflictError = class extends SqliteCoordinatorError {
constructor(databasePath, cause) {
super(`OpenClaw refused shared state schema mutation at ${databasePath} because another Gateway owns that state directory. Stop that Gateway or perform the update through its managed restart path, then retry.`, cause);
this.name = "StateSchemaMutationConflictError";
}
};
function resolveStateLifecycleRuntimeDirectory() {
return process.platform === "win32" ? path.join(os.homedir(), "AppData", "Local", "OpenClaw", "locks") : "/tmp";
}
function resolveLifecycleCoordinatorPath(family, params) {
const canonicalDatabasePath = resolvePathViaExistingAncestorSync(params.databasePath);
const canonicalRuntimeDirectory = resolvePathViaExistingAncestorSync(params.runtimeDirectory);
const suffix = params.uid === void 0 ? "openclaw-state-locks" : `openclaw-state-locks-${params.uid}`;
return path.join(canonicalRuntimeDirectory, suffix, `${family}.${sha256HexPrefixCore(canonicalDatabasePath, 8)}.lock.sqlite`);
}
function resolveStateDatabaseCoordinatorPath(params) {
return resolveLifecycleCoordinatorPath("state-lifecycle", params);
}
function acquireLifecycleCoordinator(family, params) {
const coordinatorPath = params.coordinatorPath ?? resolveLifecycleCoordinatorPath(family, {
databasePath: params.databasePath,
runtimeDirectory: params.runtimeDirectory ?? resolveStateLifecycleRuntimeDirectory(),
uid: params.uid ?? (typeof process.getuid === "function" ? process.getuid() : void 0)
});
const held = heldCoordinators.get(coordinatorPath);
if (held) held.references += 1;
else {
ensurePrivateSqliteCoordinatorDirectory(path.dirname(coordinatorPath), `${family} coordinator`);
const coordinator = tryAcquireExclusiveSqliteCoordinator(coordinatorPath, { busyTimeoutMs: params.busyTimeoutMs });
if (!coordinator) throw new StateDatabaseCoordinatorContentionError(family);
heldCoordinators.set(coordinatorPath, {
coordinator,
references: 1
});
}
let released = false;
return {
path: coordinatorPath,
release: () => {
if (released) return;
released = true;
const current = heldCoordinators.get(coordinatorPath);
if (!current) return;
current.references -= 1;
if (current.references > 0) return;
heldCoordinators.delete(coordinatorPath);
try {
current.coordinator.release();
} catch (error) {
throw new SqliteCoordinatorError(`failed to release ${family} coordinator`, error);
}
}
};
}
function acquireGatewayLifecycleCoordinator(params) {
return acquireLifecycleCoordinator("gateway-lifecycle", params);
}
function acquireStateDatabaseCoordinator(params) {
return acquireLifecycleCoordinator("state-lifecycle", params);
}
/** Fence schema mutation against another process's live Gateway owner. */
function withStateSchemaFence(params, operation) {
let coordinator;
try {
coordinator = acquireGatewayLifecycleCoordinator({
...params,
busyTimeoutMs: 0
});
} catch (error) {
if (error instanceof StateDatabaseCoordinatorContentionError) throw new StateSchemaMutationConflictError(params.databasePath, error);
throw error;
}
return runWithSqliteCoordinator(coordinator, "state schema mutation", operation);
}
//#endregion
export { resolveStateLifecycleRuntimeDirectory as a, createSqliteLifecycleAggregateError as c, resolveStateDatabaseCoordinatorPath as i, ensurePrivateSqliteCoordinatorDirectory as l, acquireGatewayLifecycleCoordinator as n, withStateSchemaFence as o, acquireStateDatabaseCoordinator as r, SqliteCoordinatorError as s, StateDatabaseCoordinatorContentionError as t, runWithSqliteCoordinator as u };