openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
784 lines (783 loc) • 33.4 kB
JavaScript
import { n as resolveForwardedExitCompilerArgs } from "./node-exit-safe-compilers-B7V3uapp.mjs";
import { D as isRecord, S as resolveGlobalSingleton, h as resolveTimerTimeoutMs } from "./number-coercion-CmpLVzZ6.mjs";
import { f as tryProcessCwd } from "./utils-BfTq4rUg.mjs";
import { A as hasErrnoCode, at as sliceUtf16Safe, st as expectDefined, v as resolveStateDir } from "./redact-TLu2QTmL.mjs";
import "./async-work-scope-6N1oTppj.mjs";
import { b as readDatabaseFileIdentity } from "./openclaw-state-db-schema-migration-required-DtltEO1E.mjs";
import { a as captureRetainedNativeWorkerSource, i as DEFAULT_WORKER_PENDING_BYTES, n as createOwnedWorkerTaskPool } from "./worker-task-pool-BXDBEv3e.mjs";
import { at as createSqliteLifecycleAggregateError, d as settleSqliteSnapshotRequest, f as startRemoveTempDirectory, i as registerRetainedSnapshotTempDirectory, t as SqliteSnapshotCleanupError, u as sealRetainedSnapshotTempDirectory, y as markPrivateDirectoryCreationRefused } from "./sqlite-readonly-location-cleanup-CmRNKiuM.mjs";
import { t as createSubsystemLogger } from "./subsystem-M2sQt_up.mjs";
import { n as runtimeProcessEntrypoints, t as SQLITE_READONLY_CHILD_ARG } from "./runtime-process-entrypoints-Dgz2IR4T.mjs";
import { f as captureRuntimeWorkerSource, t as createRetainedOperation } from "./retained-operation-Dsn-U2KQ.mjs";
import { i as resolveRuntimeWorkerUrl, n as resolveRuntimeWorkerArgv } from "./runtime-worker-url-QXcu2VO1.mjs";
import { a as hydrateOpenClawStateWorkerError, i as encodeOpenClawStateWorkerError, o as retainOpenClawStateWorkerErrorPayload, r as resolveNodeCompileCacheEnv } from "./sqlite-worker-transfer-DphMZ7LJ.mjs";
import { n as resolveRuntimeProcessEntrypointUrl } from "./runtime-process-url-C4UiB3n2.mjs";
import { n as getSpawnBroker } from "./spawn-diagnostics-C-w4nT88.mjs";
import "node:v8";
import { spawnSync } from "node:child_process";
import fsSync from "node:fs";
import path from "node:path";
import { tmpdir } from "node:os";
import { AsyncLocalStorage } from "node:async_hooks";
import { performance } from "node:perf_hooks";
import { MessageChannel, receiveMessageOnPort } from "node:worker_threads";
import { toUSVString } from "node:util";
//#region packages/normalization-core/src/format.ts
const BYTE_SIZE_UNITS = [
"byte",
"kilo",
"mega",
"giga",
"tera"
];
const BYTE_SIZE_STYLES = {
iec: {
base: 1024,
labels: [
"B",
"KiB",
"MiB",
"GiB",
"TiB"
]
},
"legacy-binary": {
base: 1024,
labels: [
"B",
"KB",
"MB",
"GB",
"TB"
]
}
};
/** Formats a byte count with caller-explicit scale, labels, precision, and unit cap. */
function formatByteSize(bytes, options) {
const { base, labels } = BYTE_SIZE_STYLES[options.style];
const maxUnitIndex = BYTE_SIZE_UNITS.indexOf(options.maxUnit);
let unitIndex = 0;
let value = bytes;
while (value >= base && unitIndex < maxUnitIndex) {
value /= base;
unitIndex += 1;
}
const unit = expectDefined(BYTE_SIZE_UNITS[unitIndex], "byte-size unit");
const label = expectDefined(labels[unitIndex], "byte-size label");
const fractionDigits = typeof options.fractionDigits === "function" ? options.fractionDigits(value, unit) : options.fractionDigits;
if (fractionDigits === null) return `${value}${options.separator}${label}`;
if (options.floorUnits?.includes(unit)) value = Math.floor(value * 10 ** fractionDigits) / 10 ** fractionDigits;
return `${value.toFixed(fractionDigits)}${options.separator}${label}`;
}
//#endregion
//#region src/infra/sqlite-readonly-worker-context.ts
const readOnlyWorkerScope = new AsyncLocalStorage();
//#endregion
//#region src/infra/sqlite-readonly-worker-protocol.ts
const SQLITE_READONLY_WORKER_MAX_BUFFER = 1048576;
function isSqliteSnapshotStagingMode(mode) {
return mode === "staging-create" || mode === "staging-create-legacy" || mode === "staging-reconcile" || mode === "staging-retire";
}
var SqliteReadOnlyInspectionContentionError = class extends Error {};
var SqliteSnapshotAllocationRefusedError = class extends Error {};
function sqliteReadOnlyWorkerRequestArgs(pathname, options) {
const args = [options.mode, path.resolve(pathname)];
const stagingRoot = options.stagingRoot && path.resolve(options.stagingRoot);
const expected = options.mode === "auth-profile-rows" || options.mode === "operation" ? void 0 : options.expectedSourceIdentity;
if (expected !== void 0) {
if (options.mode !== "sync") throw new Error("SQLite source identity is supported only for artifact-preserving sync copies");
args.push(stagingRoot ?? "", JSON.stringify(readDatabaseFileIdentity(expected)));
} else if (stagingRoot) args.push(stagingRoot);
return args;
}
function isSqliteReadOnlyWorkerResult(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
if (Object.keys(value).length !== 2 || !("ok" in value)) return false;
return value.ok === true && "location" in value && typeof value.location === "string" || value.ok === true && "contentVersion" in value && typeof value.contentVersion === "string" && /^(?:[a-f0-9]{64})?$/.test(value.contentVersion) || value.ok === true && "warnings" in value && Array.isArray(value.warnings) && value.warnings.every((warning) => typeof warning === "string") || value.ok === false && "message" in value && typeof value.message === "string";
}
function createSqliteReadOnlyWorkerError(message, stderr, options) {
const stderrTail = toUSVString(sliceUtf16Safe(stderr.trim(), -4e3));
return new Error(`SQLite read-only worker ${message}${stderrTail ? `\nstderr (tail): ${stderrTail}` : ""}`, options);
}
function parseSqliteReadOnlyWorkerResult(stdout, stderr) {
if (!stdout.trim()) throw createSqliteReadOnlyWorkerError("returned no JSON result", stderr);
let message;
try {
message = JSON.parse(stdout);
} catch {
throw createSqliteReadOnlyWorkerError("returned invalid JSON", stderr);
}
if (!isSqliteReadOnlyWorkerResult(message)) throw createSqliteReadOnlyWorkerError("returned an invalid result", stderr);
return message;
}
function readSqliteReadOnlyWorkerValue(params, mode) {
if (params.kind === "launch-failed") throw params.error;
let result;
try {
result = parseSqliteReadOnlyWorkerResult(params.stdout, params.stderr);
} catch (error) {
if (params.failure) throw createSqliteReadOnlyWorkerError(params.failure, params.stderr, { cause: params.cause });
throw error;
}
if (params.status !== 0 || params.failure || !result.ok) {
const contention = !result.ok && result.message.startsWith("Retryable SQLite inspection contention: ");
const message = !result.ok ? contention ? result.message.slice(40) : result.message : params.failure ?? `exited with code ${params.status}`;
const allocationRefused = params.failure === void 0 && !result.ok && (mode === "staging-create" || mode === "staging-create-legacy") && message.startsWith("SQLite snapshot directory creation refused: ");
const error = createSqliteReadOnlyWorkerError(allocationRefused ? message.slice(44) : message, params.stderr, { cause: params.cause });
if (contention) {
const failure = new SqliteReadOnlyInspectionContentionError(error.message);
throw allocationRefused ? markPrivateDirectoryCreationRefused(failure) : failure;
}
if (allocationRefused) throw new SqliteSnapshotAllocationRefusedError(error.message);
throw error;
}
if ((mode === "sync" || mode === "async" || mode === "consolidated" || isSqliteSnapshotStagingMode(mode)) && "location" in result) return result.location;
if (mode === "content-version" && "contentVersion" in result) return result.contentVersion;
if (mode === "reclaim" && "warnings" in result) return result.warnings;
throw createSqliteReadOnlyWorkerError("returned a result for a different operation", params.stderr);
}
//#endregion
//#region src/infra/sqlite-readonly-worker.ts
const SQLITE_INSPECTION_TIMEOUT_MS = 3e5;
const SQLITE_INSPECTION_BYTES_PER_SECOND = 33554432;
const log = createSubsystemLogger("state/sqlite");
function resolveSqliteInspectionBudget(operation, pathname, sizeBytes) {
const timeoutMs = resolveTimerTimeoutMs(SQLITE_INSPECTION_TIMEOUT_MS + Math.ceil(40 * Number(sizeBytes ?? 0) / SQLITE_INSPECTION_BYTES_PER_SECOND) * 1e3, SQLITE_INSPECTION_TIMEOUT_MS);
const size = sizeBytes === void 0 ? "unknown size" : formatByteSize(Number(sizeBytes), {
style: "iec",
maxUnit: "giga",
separator: " ",
fractionDigits: sizeBytes < 1024n ? 0 : 1
});
if (timeoutMs > SQLITE_INSPECTION_TIMEOUT_MS) log.debug(`SQLite ${operation} for ${pathname}: ${size}, budget ${timeoutMs / 1e3} seconds`);
return {
timeoutMs,
size
};
}
function readSqliteInspectionBudget(operation, pathname, mainSizeBytes) {
let sizeBytes = mainSizeBytes;
try {
sizeBytes ??= fsSync.statSync(pathname, { bigint: true }).size;
for (const suffix of [
"-wal",
"-shm",
"-journal"
]) try {
sizeBytes += fsSync.statSync(pathname + suffix, { bigint: true }).size;
} catch (error) {
if (!hasErrnoCode(error, "ENOENT")) throw error;
}
} catch {}
return resolveSqliteInspectionBudget(operation, pathname, sizeBytes);
}
function sqliteInspectionTimeoutError(operation, pathname, timeoutMs, size) {
return /* @__PURE__ */ new Error(`SQLite ${operation} timed out after ${timeoutMs / 1e3} seconds (budget for ${size}) for ${pathname}. Stop the Gateway service and other OpenClaw processes using this database, then retry; if already stopped, check storage performance.`);
}
/** A retained startup inspection is cancelled by its Gateway, not by its foreground wait. */
function isSqliteInspectionDeadlineOwnedByCaller() {
return readOnlyWorkerScope.getStore()?.deadlineOwnedByCaller === true;
}
function resolveSqliteInspectionSignal(signal) {
const scope = readOnlyWorkerScope.getStore();
return scope ? signal ? AbortSignal.any([signal, scope.controller.signal]) : scope.controller.signal : signal;
}
function sqliteReadOnlyWorkerArgv(pathname, options) {
const { moduleUrl, runtimeGeneration } = captureRuntimeWorkerSource(resolveRuntimeWorkerUrl(options.mode === "content-version" ? runtimeProcessEntrypoints.sqliteSourceRevision : runtimeProcessEntrypoints.sqliteReadOnly));
return {
runtimeGeneration,
argv: [
...resolveForwardedExitCompilerArgs(),
...resolveRuntimeWorkerArgv(moduleUrl),
SQLITE_READONLY_CHILD_ARG,
...sqliteReadOnlyWorkerRequestArgs(pathname, options)
]
};
}
/** Capture launch facts before awaiting another session's retirement. */
function captureSqliteReadOnlyWorkerLaunch(env, source) {
const broker = source === "canonical" ? getSpawnBroker() : void 0;
return {
runtimeGeneration: captureRuntimeWorkerSource(resolveRuntimeWorkerUrl(runtimeProcessEntrypoints.sqliteReadOnly)).runtimeGeneration,
env: {
...resolveNodeCompileCacheEnv(env),
OPENCLAW_STATE_DIR: resolveStateDir(env)
},
cwd: tryProcessCwd() ?? tmpdir(),
transport: broker ? {
kind: "broker",
owner: broker
} : { kind: "native" }
};
}
function runSqliteReadOnlyWorkerSync(pathname, stagingRoot, mode = "sync") {
const { timeoutMs, size } = readSqliteInspectionBudget("read-only snapshot", pathname);
const started = log.isEnabled("trace") ? performance.now() : void 0;
const result = spawnSync(process.execPath, sqliteReadOnlyWorkerArgv(pathname, {
mode,
stagingRoot
}).argv, {
encoding: "utf8",
env: resolveNodeCompileCacheEnv(),
maxBuffer: SQLITE_READONLY_WORKER_MAX_BUFFER,
timeout: timeoutMs,
killSignal: "SIGKILL"
});
if (started !== void 0) log.trace(`SQLite read-only snapshot child durationMs=${performance.now() - started}`);
return readSqliteReadOnlyWorkerValue(result.error ? {
kind: "launch-failed",
error: new Error(hasErrnoCode(result.error, "ETIMEDOUT") ? sqliteInspectionTimeoutError("read-only snapshot", pathname, timeoutMs, size).message : `SQLite read-only worker failed to start for ${pathname}: ${result.error.message}`, { cause: result.error })
} : {
kind: "launched",
stdout: result.stdout,
stderr: result.stderr,
status: result.status,
failure: result.status === 0 ? void 0 : `exited with ${result.signal ? `signal ${result.signal}` : `code ${result.status}`}`
}, mode);
}
//#endregion
//#region src/infra/sqlite-readonly-native-resource.client.ts
/** Host-port framing; the staging owner alone admits readers and retires directories. */
function createSqliteReadOnlyNativeResourceConnection(callbacks) {
const { port1, port2 } = new MessageChannel();
const owner = { disposed: false };
const receive = (value) => {
if (!isRecord(value) || typeof value.id !== "number" || !Number.isSafeInteger(value.id) || value.id < 1) throw new SqliteSnapshotCleanupError("SQLite snapshot native owner sent an invalid request");
let reply;
try {
if (typeof value.directory !== "string" || !value.directory || value.directory.includes("\0")) throw new SqliteSnapshotCleanupError("SQLite snapshot native owner sent an invalid directory");
let request;
if (value.type === "allocated") {
if (typeof value.preparationId !== "number" || !Number.isSafeInteger(value.preparationId) || value.preparationId < 1) throw new SqliteSnapshotCleanupError("SQLite snapshot has no preparation identity");
request = {
id: value.id,
type: value.type,
directory: value.directory,
preparationId: value.preparationId
};
} else if (value.type === "retire" || value.type === "removed") request = {
id: value.id,
type: value.type,
directory: value.directory
};
else throw new SqliteSnapshotCleanupError("SQLite snapshot native owner sent an unsupported request");
callbacks.receive(request, owner);
reply = {
id: value.id,
ok: true
};
} catch (error) {
const encoded = encodeOpenClawStateWorkerError(error, { includeOrdinary: true });
if (!encoded) throw error;
reply = {
id: value.id,
ok: false,
error: encoded
};
}
port1.postMessage(reply);
};
const fail = (error) => {
callbacks.onFailure(error);
port1.close();
};
const onMessage = (value) => {
try {
receive(value);
} catch (error) {
fail(error);
}
};
port1.on("message", onMessage);
port1.on("messageerror", fail);
return {
port: port2,
service() {
if (owner.disposed) return;
for (;;) {
const next = receiveMessageOnPort(port1);
if (!next) return;
try {
receive(next.message);
} catch (error) {
fail(error);
throw error;
}
}
},
decodeCloseError(payload) {
const remote = /* @__PURE__ */ new Error("SQLite snapshot staging failed");
retainOpenClawStateWorkerErrorPayload(remote, payload);
const error = hydrateOpenClawStateWorkerError(remote, { includeOrdinary: true });
return error instanceof AggregateError ? error : new SqliteSnapshotCleanupError(error.message, { cause: error });
},
dispose() {
owner.disposed = true;
callbacks.onDispose(owner);
port1.removeAllListeners();
port1.close();
port2.close();
}
};
}
//#endregion
//#region src/infra/sqlite-readonly-native-resource.types.ts
const SQLITE_NATIVE_RESOURCE_PORT = "sqliteSnapshotNativeResource";
//#endregion
//#region src/infra/sqlite-snapshot-staging-error.ts
function decodeSqliteSnapshotStagingError(payload) {
const remote = /* @__PURE__ */ new Error("SQLite snapshot staging failed");
retainOpenClawStateWorkerErrorPayload(remote, payload);
return hydrateOpenClawStateWorkerError(remote, { includeOrdinary: true });
}
//#endregion
//#region src/infra/sqlite-snapshot-staging-owner.ts
/** The existing staging owner retains its child; this transport only moves its event loop. */
function createStagingOwner(workerUrl, nativeSource) {
const nativeDirectories = /* @__PURE__ */ new Map();
const nativeResource = nativeSource.captureResource(resolveRuntimeProcessEntrypointUrl("sqliteReadOnlyNativeResource"), SQLITE_NATIVE_RESOURCE_PORT, void 0, connectNativeResource);
const pool = createOwnedWorkerTaskPool({
workerUrl,
maxWorkers: 1,
idleTimeoutMs: 0,
maxPendingTasks: 128,
maxPendingBytes: DEFAULT_WORKER_PENDING_BYTES
}, {
retainedTransport: true,
nativeSource,
nativeResource,
decodeResourceError: decodeSqliteSnapshotStagingError
});
const directories = /* @__PURE__ */ new Map();
const preparations = /* @__PURE__ */ new Map();
let preparationSequence = 0;
const activeRequests = /* @__PURE__ */ new Set();
let servicingRequests = false;
let requests = 0;
let admissionClosed = false;
let idleClose;
let unavailable;
function connectNativeResource() {
return createSqliteReadOnlyNativeResourceConnection({
receive(value, owner) {
const directory = value.directory;
const existing = nativeDirectories.get(directory);
if (value.type === "allocated") {
const preparation = preparations.get(value.preparationId);
if (!preparation?.isAdmitted()) throw new SqliteSnapshotCleanupError("SQLite snapshot preparation custody is unavailable");
preparation.acceptOwner(owner);
if (existing && (existing.owner !== owner || existing.preparationId !== value.preparationId)) throw new SqliteSnapshotCleanupError("SQLite snapshot changed its original native owner");
if (!existing) nativeDirectories.set(directory, {
owner,
preparationId: value.preparationId,
removed: false,
recovering: false
});
preparation.directories.add(directory);
retainDirectory(directory);
if (preparation.closeRequested) try {
sealRetainedSnapshotTempDirectory(directory);
} catch {}
} else {
if (existing?.owner !== owner) throw new SqliteSnapshotCleanupError("SQLite snapshot native custody is unavailable");
if (value.type === "retire") {
sealRetainedSnapshotTempDirectory(directory, { requireRequested: true });
existing.recovering = true;
} else if (value.type === "removed") existing.removed = true;
}
},
onFailure(error) {
unavailable ??= { error };
},
onDispose(owner) {
for (const [directory, record] of nativeDirectories) if (record.owner === owner && record.removed && !directories.has(directory)) nativeDirectories.delete(directory);
}
});
}
const serviceRequests = () => {
if (servicingRequests) return;
servicingRequests = true;
try {
for (const request of activeRequests) request.service();
for (const preparation of Array.from(preparations.values())) preparation.serviceClose();
} finally {
servicingRequests = false;
}
};
const closeWhenIdle = () => {
if (idleClose?.read().status === "pending") return idleClose;
let close;
let rotate;
const retained = createRetainedOperation(() => {
if (retained.operation.read().status !== "pending") return;
if (!close) {
if (requests > 0 || directories.size > 0) return retained.resolve(void 0);
close = pool.startCloseResources();
close.result.then(serviceIdleClose, serviceIdleClose);
}
close.service();
const closed = close.read();
if (closed.status === "pending") return;
if (closed.status === "rejected") return retained.reject(closed.error);
if (!rotate) {
if (requests > 0 || directories.size > 0) return retained.resolve(void 0);
rotate = pool.startRotate();
rotate.result.then(serviceIdleClose, serviceIdleClose);
}
rotate.service();
const rotated = rotate.read();
if (rotated.status === "fulfilled") {
unavailable = void 0;
retained.resolve(void 0);
} else if (rotated.status === "rejected") retained.reject(rotated.error);
});
const serviceIdleClose = retained.operation.service.bind(retained.operation);
idleClose = retained.operation;
retained.operation.service();
return retained.operation;
};
const retainDirectory = (directory) => {
const existing = directories.get(directory);
if (existing) return existing;
const preparationId = nativeDirectories.get(directory)?.preparationId;
let pending;
let removed = false;
const startRetire = () => {
if (pending?.read().status === "pending") return pending;
let cleanup;
let closing;
const retained = createRetainedOperation(() => {
if (retained.operation.read().status !== "pending") return;
if (!removed) {
const nativeDirectory = nativeDirectories.get(directory);
if (!nativeDirectory?.removed || !nativeDirectory.recovering) {
if (!cleanup) {
try {
sealRetainedSnapshotTempDirectory(directory);
cleanup = pool.startCloseResources(directory);
} catch (error) {
retained.reject(error);
return;
}
cleanup.result.then(serviceDirectoryClose, serviceDirectoryClose);
}
cleanup.service();
const outcome = cleanup.read();
if (outcome.status === "pending") return;
if (outcome.status === "rejected") {
retained.reject(outcome.error);
return;
}
if (!nativeDirectories.get(directory)?.removed) {
retained.reject(new SqliteSnapshotCleanupError(`SQLite snapshot native owner did not confirm directory removal: ${directory}`));
return;
}
}
removed = true;
directories.delete(directory);
if (!nativeDirectory?.recovering || nativeDirectory.owner.disposed) nativeDirectories.delete(directory);
}
if (!closing) {
closing = closeWhenIdle();
closing.result.then(serviceDirectoryClose, serviceDirectoryClose);
}
closing.service();
const outcome = closing.read();
if (outcome.status === "fulfilled") {
if (preparationId !== void 0) {
const preparation = preparations.get(preparationId);
preparation?.directories.delete(directory);
preparation?.releaseIfComplete();
}
retained.resolve(void 0);
} else if (outcome.status === "rejected") retained.reject(outcome.error);
});
const serviceDirectoryClose = retained.operation.service.bind(retained.operation);
pending = retained.operation;
retained.operation.service();
return retained.operation;
};
const owned = {
directory,
startRetire
};
directories.set(directory, owned);
registerRetainedSnapshotTempDirectory(directory, startRetire);
return owned;
};
const start = (command, signal) => {
if (admissionClosed) throw new Error("SQLite snapshot staging owner is closing");
const preparationId = ++preparationSequence;
const inputBytes = Buffer.byteLength(JSON.stringify({
...command,
preparationId
}));
const runInContext = AsyncLocalStorage.snapshot();
const priorClose = idleClose;
priorClose?.service();
const cancellation = command.type === "prepare" ? new MessageChannel() : void 0;
const abort = () => cancellation?.port1.postMessage({ type: "abort" });
signal?.addEventListener("abort", abort, { once: true });
if (signal?.aborted) abort();
const input = command.type === "prepare" && cancellation ? {
...command,
preparationId,
abortPort: cancellation.port2
} : {
...command,
preparationId
};
requests++;
let inputPrepared = false;
let task;
let admissionFailure;
let custodyReleased = false;
let pendingClose;
let closeService;
let admitting = true;
let originalNativeOwner;
let release;
let closing;
let observed = false;
let reply;
let refusedByCleanup;
const errors = [];
let servicing = false;
const retained = createRetainedOperation(() => runInContext(service));
const serviceRequest = retained.operation.service.bind(retained.operation);
const preparation = {
directories: /* @__PURE__ */ new Set(),
closeRequested: false,
isAdmitted: () => inputPrepared,
acceptOwner(owner) {
if (originalNativeOwner && originalNativeOwner !== owner) throw new SqliteSnapshotCleanupError("SQLite preparation changed its original native owner");
originalNativeOwner = owner;
},
startClose,
serviceClose: () => closeService?.(),
releaseIfComplete
};
preparations.set(preparationId, preparation);
try {
task = pool.startTask(() => {
if (unavailable !== void 0) throw unavailable.error;
signal?.throwIfAborted();
inputPrepared = true;
return input;
}, {
inputBytes,
transferList: () => cancellation ? [cancellation.port2] : []
});
} catch (error) {
admissionFailure = { error };
requests--;
signal?.removeEventListener("abort", abort);
cancellation?.port1.close();
cancellation?.port2.close();
}
admitting = false;
function service() {
if (admitting || servicing || retained.operation.read().status !== "pending") return;
servicing = true;
try {
priorClose?.service();
const previous = priorClose?.read();
const failure = unavailable ?? (previous?.status === "rejected" ? previous : void 0);
if (failure && !inputPrepared && task?.read().status === "pending") {
refusedByCleanup = new SqliteSnapshotCleanupError("SQLite snapshot staging cleanup failed before admission", { cause: failure.error });
release = task.release();
}
if (!task) {
retained.reject(admissionFailure?.error ?? /* @__PURE__ */ new Error("SQLite staging task is unavailable"));
return;
}
task.service();
const result = task.read();
if (result.status === "pending") return;
if (!observed) {
observed = true;
if (result.status === "rejected") {
errors.push(refusedByCleanup ?? result.error);
if (inputPrepared && directories.size > 0) unavailable = { error: result.error };
} else if (result.value.type === "failed") {
if (result.value.directory && !preparation.directories.has(result.value.directory)) errors.push(new SqliteSnapshotCleanupError("SQLite staging failure lost its original directory custody"));
const error = decodeSqliteSnapshotStagingError(result.value.error);
errors.push(result.value.cleanupFailure && !(error instanceof AggregateError) ? new SqliteSnapshotCleanupError(String(error), { cause: error }) : error);
} else if (!preparation.directories.has(result.value.directory)) errors.push(new SqliteSnapshotCleanupError("SQLite staging result lost its original directory custody"));
else reply = result.value;
release ??= task.release();
release.result.then(serviceRequest, serviceRequest);
}
release.service();
const released = release.read();
if (released.status === "pending") return;
if (!closing) {
signal?.removeEventListener("abort", abort);
cancellation?.port1.close();
cancellation?.port2.close();
requests--;
if (released.status === "rejected") errors.push(released.error);
closing = closeWhenIdle();
closing.result.then(serviceRequest, serviceRequest);
}
closing.service();
const closed = closing.read();
if (closed.status === "pending") return;
if (closed.status === "rejected") errors.push(closed.error);
if (errors.length > 1) retained.reject(createSqliteLifecycleAggregateError(errors, "SQLite snapshot staging failed", errors[0]));
else if (errors.length === 1) retained.reject(errors[0]);
else if (reply) retained.resolve(reply);
else retained.reject(/* @__PURE__ */ new Error("SQLite snapshot staging returned no outcome"));
} finally {
servicing = false;
if (retained.operation.read().status !== "pending") {
activeRequests.delete(retained.operation);
releaseIfComplete();
}
}
}
function releaseIfComplete() {
if (retained.operation.read().status !== "pending" && pendingClose?.read().status !== "pending" && preparation.directories.size === 0 && release?.read().status === "fulfilled" && closing?.read().status === "fulfilled") {
custodyReleased = true;
preparations.delete(preparationId);
}
}
function startClose() {
preparation.closeRequested = true;
if (pendingClose?.read().status === "pending") return pendingClose;
let rawRelease;
const removals = /* @__PURE__ */ new Map();
const removalFailures = /* @__PURE__ */ new Map();
let servicingClose = false;
const completion = createRetainedOperation(() => runInContext(() => {
if (admitting) return;
if (servicingClose || completion.operation.read().status !== "pending") return;
servicingClose = true;
try {
if (custodyReleased) {
completion.resolve();
return;
}
if (!task) throw new SqliteSnapshotCleanupError("SQLite staging admission did not return its original task custody", { cause: admissionFailure?.error });
for (;;) {
for (const directory of preparation.directories) try {
sealRetainedSnapshotTempDirectory(directory);
} catch {}
serviceRequests();
task.service();
if (task.read().status === "pending") return;
const failures = [];
for (const directory of preparation.directories) {
let removal = removals.get(directory);
if (!removal) {
removal = startRemoveTempDirectory(directory, (error) => removalFailures.set(directory, error));
removals.set(directory, removal);
removal.result.then(serviceRequestClose, serviceRequestClose);
}
}
for (const [directory, removal] of removals) {
removal.service();
const removed = removal.read();
if (removed.status === "pending") return;
if (removed.status === "rejected") failures.push(removed.error);
else if (!removed.value) failures.push(removalFailures.get(directory) ?? new SqliteSnapshotCleanupError(`SQLite staging preparation cleanup failed: ${directory}`));
}
if (failures.length) throw createSqliteLifecycleAggregateError(failures, "SQLite staging preparation cleanup failed", failures[0]);
if (!rawRelease) {
rawRelease = task.release();
rawRelease.result.then(serviceRequestClose, serviceRequestClose);
}
rawRelease.service();
const joined = rawRelease.read();
if (joined.status === "pending") return;
if (joined.status === "rejected") throw joined.error;
if (preparation.directories.size > 0) {
if ([...preparation.directories].some((directory) => !removals.has(directory))) continue;
throw new SqliteSnapshotCleanupError("SQLite staging directory custody was not released");
}
service();
if (retained.operation.read().status === "pending") return;
const idle = closing?.read();
if (inputPrepared && idle?.status === "rejected" && removals.size === 0) throw idle.error;
custodyReleased = true;
preparations.delete(preparationId);
completion.resolve();
return;
}
} catch (error) {
completion.reject(error);
} finally {
servicingClose = false;
}
}));
const serviceRequestClose = completion.operation.service.bind(completion.operation);
closeService = serviceRequestClose;
pendingClose = {
...completion.operation,
service: serviceRequests
};
if (task) task.result.then(serviceRequestClose, serviceRequestClose);
retained.operation.result.then(serviceRequestClose, serviceRequestClose);
completion.operation.service();
return pendingClose;
}
priorClose?.result.then(serviceRequest, serviceRequest);
if (task) task.result.then(serviceRequest, serviceRequest);
activeRequests.add(retained.operation);
retained.operation.service();
closeService?.();
return {
...retained.operation,
service: serviceRequests,
startClose
};
};
const owner = {
start,
retainDirectory
};
nativeSource.retain(owner, async () => {
admissionClosed = true;
for (const preparation of preparations.values()) preparation.closeRequested = true;
await Promise.allSettled([...activeRequests].map((request) => request.result));
for (const directory of directories.keys()) try {
sealRetainedSnapshotTempDirectory(directory);
} catch {}
const closures = Array.from(preparations.values()).map((preparation) => preparation.startClose());
const failures = (await Promise.allSettled(closures.map((close) => close.result))).flatMap((outcome) => outcome.status === "rejected" ? [outcome.reason] : []);
if (failures.length) throw createSqliteLifecycleAggregateError(failures, "SQLite snapshot generation cleanup failed", failures[0]);
await closeWhenIdle().result;
await pool.close();
});
return owner;
}
function captureSqliteSnapshotStagingOwner() {
const { moduleUrl, runtimeGeneration } = captureRuntimeWorkerSource(resolveRuntimeProcessEntrypointUrl("sqliteSnapshotStaging"));
const nativeSource = captureRetainedNativeWorkerSource({ runtimeGeneration });
const owners = resolveGlobalSingleton(Symbol.for("openclaw.sqliteSnapshotStagingOwner"), () => /* @__PURE__ */ new WeakMap());
let owner = owners.get(nativeSource);
if (!owner) {
owner = createStagingOwner(moduleUrl, nativeSource);
owners.set(nativeSource, owner);
}
return owner;
}
async function allocateWorkerOwnedSqliteSnapshotDirectory(inputRoot, allowLegacyWorker, signal) {
const root = path.resolve(inputRoot);
const { env, cwd } = captureSqliteReadOnlyWorkerLaunch();
const owner = captureSqliteSnapshotStagingOwner();
const request = owner.start({
type: "allocate",
root,
allowLegacyWorker,
launch: {
env,
cwd,
transport: { kind: "native" }
}
}, signal);
const reply = await settleSqliteSnapshotRequest(request);
return owner.retainDirectory(reply.directory);
}
//#endregion
export { resolveSqliteInspectionSignal as a, isSqliteInspectionDeadlineOwnedByCaller as i, captureSqliteSnapshotStagingOwner as n, runSqliteReadOnlyWorkerSync as o, captureSqliteReadOnlyWorkerLaunch as r, allocateWorkerOwnedSqliteSnapshotDirectory as t };