UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

147 lines (146 loc) 6.52 kB
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 };