UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

629 lines (628 loc) 24.1 kB
import { n as MAX_TIMER_TIMEOUT_MS } from "./number-coercion-CJQ8TR--.js"; import "./fs-safe-defaults-B7hUN42l.js"; import { n as createFileLockManager } from "./file-lock-D6zEe-ZP.js"; import { n as isPidAlive, t as getProcessStartTime } from "./pid-alive-C4bVUgUC.js"; import { r as readGatewayProcessArgsSync } from "./gateway-processes-cAJXq3M3.js"; import { n as SessionWriteLockTimeoutError, t as SessionWriteLockStaleError } from "./session-write-lock-error-CYOzPsPk.js"; import path from "node:path"; import fs from "node:fs/promises"; //#region src/agents/session-write-lock.ts function isValidLockNumber(value) { return typeof value === "number" && Number.isInteger(value) && value >= 0; } const CLEANUP_SIGNALS = [ "SIGINT", "SIGTERM", "SIGQUIT", "SIGABRT" ]; const CLEANUP_STATE_KEY = Symbol.for("openclaw.sessionWriteLockCleanupState"); const WATCHDOG_STATE_KEY = Symbol.for("openclaw.sessionWriteLockWatchdogState"); const DEFAULT_SESSION_WRITE_LOCK_STALE_MS = 1800 * 1e3; const DEFAULT_SESSION_WRITE_LOCK_MAX_HOLD_MS = 300 * 1e3; const DEFAULT_SESSION_WRITE_LOCK_ACQUIRE_TIMEOUT_MS = 6e4; const DEFAULT_WATCHDOG_INTERVAL_MS = 6e4; const DEFAULT_TIMEOUT_GRACE_MS = 120 * 1e3; const REPORT_ONLY_STALE_LOCK_REASONS = new Set(["too-old", "hold-exceeded"]); /** * Yield control to the event loop so other sessions can make progress * while lock contention callbacks run synchronous I/O. */ function yieldEventLoop() { return new Promise((resolve) => { setImmediate(resolve); }); } const ORPHAN_LOCK_PAYLOAD_GRACE_MS = 3e4; const SHORT_TIMEOUT_ORPHAN_LOCK_PAYLOAD_GRACE_MS = 5e3; const SESSION_LOCKS = createFileLockManager("openclaw.session-write-lock"); let resolveProcessStartTimeForLock = getProcessStartTime; function isFileLockError(error, code) { return error?.code === code; } const SESSION_WRITE_LOCK_ENV = { acquireTimeoutMs: "OPENCLAW_SESSION_WRITE_LOCK_ACQUIRE_TIMEOUT_MS", staleMs: "OPENCLAW_SESSION_WRITE_LOCK_STALE_MS", maxHoldMs: "OPENCLAW_SESSION_WRITE_LOCK_MAX_HOLD_MS" }; function readPositiveMsEnv(env, key, opts = {}) { const raw = env[key]?.trim(); if (!raw) return; if (raw === "Infinity") return opts.allowInfinity ? Number.POSITIVE_INFINITY : void 0; if (!/^\d+$/.test(raw)) return; return parsePositiveMs(Number(raw), opts); } function parsePositiveMs(value, opts = {}) { if (typeof value !== "number" || Number.isNaN(value) || value <= 0) return; if (value === Number.POSITIVE_INFINITY) return opts.allowInfinity ? value : void 0; if (!Number.isFinite(value)) return; if (!Number.isSafeInteger(value)) return; return value; } function resolveSessionWriteLockMs(params) { const opts = { allowInfinity: params.allowInfinity }; return readPositiveMsEnv(params.env ?? process.env, SESSION_WRITE_LOCK_ENV[params.key], opts) ?? parsePositiveMs(params.config?.session?.writeLock?.[params.key], opts) ?? params.fallback; } function resolveSessionWriteLockAcquireTimeoutMs(config, env) { return resolveSessionWriteLockMs({ config, env, key: "acquireTimeoutMs", fallback: DEFAULT_SESSION_WRITE_LOCK_ACQUIRE_TIMEOUT_MS, allowInfinity: true }); } function resolveSessionWriteLockStaleMs(config, env) { return resolveSessionWriteLockMs({ config, env, key: "staleMs", fallback: DEFAULT_SESSION_WRITE_LOCK_STALE_MS }); } function resolveSessionWriteLockMaxHoldMs(config, params = {}) { return resolveSessionWriteLockMs({ config, env: params.env, key: "maxHoldMs", fallback: params.fallback ?? 3e5 }); } function resolveSessionWriteLockOptions(config, params = {}) { return { timeoutMs: resolveSessionWriteLockAcquireTimeoutMs(config, params.env), staleMs: resolveSessionWriteLockStaleMs(config, params.env), maxHoldMs: resolveSessionWriteLockMaxHoldMs(config, { env: params.env, fallback: params.maxHoldMsFallback }) }; } function resolveCleanupState() { const proc = process; if (!proc[CLEANUP_STATE_KEY]) proc[CLEANUP_STATE_KEY] = { registered: false, exitHandler: void 0, cleanupHandlers: /* @__PURE__ */ new Map() }; return proc[CLEANUP_STATE_KEY]; } function resolveWatchdogState() { const proc = process; if (!proc[WATCHDOG_STATE_KEY]) proc[WATCHDOG_STATE_KEY] = { started: false, intervalMs: DEFAULT_WATCHDOG_INTERVAL_MS }; return proc[WATCHDOG_STATE_KEY]; } function resolvePositiveMs(value, fallback, opts = {}) { if (typeof value !== "number" || Number.isNaN(value) || value <= 0) return fallback; if (value === Number.POSITIVE_INFINITY) return opts.allowInfinity ? value : fallback; if (!Number.isFinite(value)) return fallback; return value; } function resolveSessionLockMaxHoldFromTimeout(params) { const minMs = resolvePositiveMs(params.minMs, DEFAULT_SESSION_WRITE_LOCK_MAX_HOLD_MS); const timeoutMs = resolvePositiveMs(params.timeoutMs, minMs, { allowInfinity: true }); if (timeoutMs === Number.POSITIVE_INFINITY) return MAX_TIMER_TIMEOUT_MS; const graceMs = resolvePositiveMs(params.graceMs, DEFAULT_TIMEOUT_GRACE_MS); return Math.min(MAX_TIMER_TIMEOUT_MS, Math.max(minMs, timeoutMs + graceMs)); } /** * Synchronously release all held locks. * Used during process exit when async operations aren't reliable. */ function releaseAllLocksSync() { SESSION_LOCKS.reset(); stopWatchdogTimer(); } async function runLockWatchdogCheck(nowMs = Date.now()) { let released = 0; for (const held of SESSION_LOCKS.heldEntries()) { const maxHoldMs = typeof held.metadata.maxHoldMs === "number" ? held.metadata.maxHoldMs : DEFAULT_SESSION_WRITE_LOCK_MAX_HOLD_MS; const heldForMs = nowMs - held.acquiredAt; if (heldForMs <= maxHoldMs) continue; process.stderr.write(`[session-write-lock] releasing lock held for ${heldForMs}ms (max=${maxHoldMs}ms): ${held.lockPath}\n`); if (await held.forceRelease()) released += 1; } return released; } function stopWatchdogTimer() { const watchdogState = resolveWatchdogState(); if (watchdogState.timer) { clearInterval(watchdogState.timer); watchdogState.timer = void 0; } watchdogState.started = false; } function shouldStartBackgroundWatchdog() { return process.env.VITEST !== "true" || process.env.OPENCLAW_TEST_SESSION_LOCK_WATCHDOG === "1"; } function ensureWatchdogStarted(intervalMs) { if (!shouldStartBackgroundWatchdog()) return; const watchdogState = resolveWatchdogState(); if (watchdogState.started) return; watchdogState.started = true; watchdogState.intervalMs = intervalMs; watchdogState.timer = setInterval(() => { runLockWatchdogCheck().catch(() => {}); }, intervalMs); watchdogState.timer.unref?.(); } function handleTerminationSignal(signal) { releaseAllLocksSync(); const cleanupState = resolveCleanupState(); if (process.listenerCount(signal) === 1) { const handler = cleanupState.cleanupHandlers.get(signal); if (handler) { process.off(signal, handler); cleanupState.cleanupHandlers.delete(signal); } try { process.kill(process.pid, signal); } catch {} } } function registerCleanupHandlers() { const cleanupState = resolveCleanupState(); cleanupState.registered = true; if (!cleanupState.exitHandler) { cleanupState.exitHandler = () => { releaseAllLocksSync(); }; process.on("exit", cleanupState.exitHandler); } ensureWatchdogStarted(DEFAULT_WATCHDOG_INTERVAL_MS); for (const signal of CLEANUP_SIGNALS) { if (cleanupState.cleanupHandlers.has(signal)) continue; try { const handler = () => handleTerminationSignal(signal); cleanupState.cleanupHandlers.set(signal, handler); process.on(signal, handler); } catch {} } } function unregisterCleanupHandlers() { const cleanupState = resolveCleanupState(); if (cleanupState.exitHandler) { process.off("exit", cleanupState.exitHandler); cleanupState.exitHandler = void 0; } for (const [signal, handler] of cleanupState.cleanupHandlers) process.off(signal, handler); cleanupState.cleanupHandlers.clear(); cleanupState.registered = false; } function parseLockPayload(raw) { const parsed = JSON.parse(raw); const payload = {}; if (isValidLockNumber(parsed.pid) && parsed.pid > 0) payload.pid = parsed.pid; if (typeof parsed.createdAt === "string") payload.createdAt = parsed.createdAt; if (isValidLockNumber(parsed.starttime)) payload.starttime = parsed.starttime; if (isValidLockNumber(parsed.maxHoldMs) && parsed.maxHoldMs > 0) payload.maxHoldMs = parsed.maxHoldMs; return payload; } async function readLockPayload(lockPath) { try { return parseLockPayload(await fs.readFile(lockPath, "utf8")); } catch { return null; } } async function readLockPayloadForDiagnostics(lockPath) { try { return { payload: parseLockPayload(await fs.readFile(lockPath, "utf8")), missing: false }; } catch (error) { return { payload: null, missing: error?.code === "ENOENT" }; } } async function resolveNormalizedSessionFile(sessionFile) { const resolvedSessionFile = path.resolve(sessionFile); const sessionDir = path.dirname(resolvedSessionFile); try { const normalizedDir = await fs.realpath(sessionDir); return path.join(normalizedDir, path.basename(resolvedSessionFile)); } catch { return resolvedSessionFile; } } function normalizeOwnerProcessArg(arg) { return arg.trim().replaceAll("\\", "/").toLowerCase(); } function isOpenClawSessionOwnerArgv(args) { const normalized = args.map(normalizeOwnerProcessArg).filter(Boolean); if (normalized.length === 0) return false; const exe = (normalized[0] ?? "").replace(/\.(bat|cmd|exe)$/i, ""); if (exe === "openclaw" || exe.endsWith("/openclaw") || exe.endsWith("/openclaw-gateway")) return true; if (normalized.some((arg) => arg === "openclaw" || arg.endsWith("/openclaw") || arg === "openclaw.mjs" || arg.endsWith("/openclaw.mjs"))) return true; const entryCandidates = [ "dist/index.js", "dist/entry.js", "scripts/run-node.mjs", "src/entry.ts", "src/index.ts" ]; const hasOpenClawCommandToken = normalized.some((arg) => arg === "gateway" || arg === "agent"); return normalized.some((arg) => entryCandidates.some((entry) => arg.endsWith(entry)) && hasOpenClawCommandToken); } function readOwnerProcessArgs(reader, pid) { try { const args = reader(pid); return Array.isArray(args) ? args : null; } catch { return null; } } function inspectLockPayload(payload, staleMs, nowMs, opts = {}) { const pid = isValidLockNumber(payload?.pid) && payload.pid > 0 ? payload.pid : null; const pidAlive = pid !== null ? isPidAlive(pid) : false; const createdAt = typeof payload?.createdAt === "string" ? payload.createdAt : null; const createdAtMs = createdAt ? Date.parse(createdAt) : NaN; const ageMs = Number.isFinite(createdAtMs) ? Math.max(0, nowMs - createdAtMs) : null; const storedStarttime = isValidLockNumber(payload?.starttime) ? payload.starttime : null; const pidRecycled = pidAlive && pid !== null && storedStarttime !== null ? (() => { const currentStarttime = resolveProcessStartTimeForLock(pid); return currentStarttime !== null && currentStarttime !== storedStarttime; })() : false; const staleReasons = []; if (pid === null) staleReasons.push("missing-pid"); else if (!pidAlive) staleReasons.push("dead-pid"); else if (pidRecycled) staleReasons.push("recycled-pid"); if (ageMs === null) staleReasons.push("invalid-createdAt"); else if (ageMs > staleMs) staleReasons.push("too-old"); const holderMaxHoldMs = isValidLockNumber(payload?.maxHoldMs) && payload.maxHoldMs > 0 ? payload.maxHoldMs : void 0; if (opts.respectMaxHold === true && typeof holderMaxHoldMs === "number" && ageMs !== null && ageMs > holderMaxHoldMs) staleReasons.push("hold-exceeded"); return { pid, pidAlive, createdAt, ageMs, stale: staleReasons.length > 0, staleReasons }; } function shouldTreatAsNonOpenClawOwner(params) { if (params.inspected.pid === null || !params.inspected.pidAlive) return false; if (params.inspected.staleReasons.includes("recycled-pid")) return false; if (params.inspected.pid === process.pid && params.heldByThisProcess) return false; if (!isValidLockNumber(params.payload?.pid) || params.payload.pid <= 0) return false; const args = readOwnerProcessArgs(params.readOwnerProcessArgs, params.payload.pid); if (!args || args.every((arg) => !arg.trim())) return false; return !isOpenClawSessionOwnerArgv(args); } function lockInspectionNeedsMtimeStaleFallback(details) { return details.stale && details.staleReasons.every((reason) => reason === "missing-pid" || reason === "invalid-createdAt"); } async function shouldReportContendedLockStale(params) { if (!params.details.stale) return false; if (params.heldByThisProcess) return false; if (lockInspectionNeedsMtimeStaleFallback(params.details)) try { const stat = await fs.stat(params.lockPath); return Math.max(0, params.nowMs - stat.mtimeMs) > Math.min(params.staleMs, params.orphanPayloadGraceMs); } catch (error) { return error?.code !== "ENOENT"; } return true; } async function shouldRemoveContendedLockFile(lockPath, details, staleMs, nowMs, orphanPayloadGraceMs = ORPHAN_LOCK_PAYLOAD_GRACE_MS) { if (!details.stale) return false; if (details.staleReasons.every((reason) => REPORT_ONLY_STALE_LOCK_REASONS.has(reason))) return false; if (!lockInspectionNeedsMtimeStaleFallback(details)) return true; try { const stat = await fs.stat(lockPath); return Math.max(0, nowMs - stat.mtimeMs) > Math.min(staleMs, orphanPayloadGraceMs); } catch (error) { return error?.code !== "ENOENT"; } } function resolveOrphanLockPayloadGraceMs(timeoutMs) { if (timeoutMs < ORPHAN_LOCK_PAYLOAD_GRACE_MS) return SHORT_TIMEOUT_ORPHAN_LOCK_PAYLOAD_GRACE_MS; return ORPHAN_LOCK_PAYLOAD_GRACE_MS; } function resolveRemainingAcquireTimeoutMs(timeoutMs, startedAtMs, nowMs) { if (timeoutMs === Number.POSITIVE_INFINITY) return Number.POSITIVE_INFINITY; const elapsedMs = Math.max(0, nowMs - startedAtMs); return Math.max(0, timeoutMs - elapsedMs); } async function shouldRetryStaleAcquireFailure(params) { if (params.lockMissingAtDiagnostics) return true; return !await shouldReportContendedLockStale({ lockPath: params.lockPath, details: params.inspected, heldByThisProcess: params.heldByThisProcess, staleMs: params.staleMs, nowMs: params.nowMs, orphanPayloadGraceMs: params.orphanPayloadGraceMs }); } async function shouldRemoveLockDuringCleanup(lockPath, details, staleMs, nowMs) { if (!details.stale) return false; return await shouldRemoveContendedLockFile(lockPath, details, staleMs, nowMs); } function sessionLockHeldByThisProcess(normalizedSessionFile) { return SESSION_LOCKS.heldEntries().some((entry) => entry.normalizedTargetPath === normalizedSessionFile); } function shouldTreatAsOrphanSelfLock(params) { if ((isValidLockNumber(params.payload?.pid) ? params.payload.pid : null) !== process.pid) return false; if (params.heldByThisProcess) return false; const storedStarttime = isValidLockNumber(params.payload?.starttime) ? params.payload.starttime : null; if (storedStarttime === null) return params.reclaimLockWithoutStarttime; const currentStarttime = resolveProcessStartTimeForLock(process.pid); return currentStarttime !== null && currentStarttime === storedStarttime; } function describeLockOwnerForError(params) { const parts = []; if (params.inspected.pid !== null) { parts.push(`pid=${params.inspected.pid}`); parts.push(`alive=${params.inspected.pidAlive ? "true" : "false"}`); } else if (typeof params.payload?.pid === "number") parts.push(`pid=${params.payload.pid}`); else parts.push("owner=unknown"); if (typeof params.inspected.ageMs === "number") parts.push(`ageMs=${Math.floor(params.inspected.ageMs)}`); return parts.join(" "); } function inspectLockPayloadForSession(params) { const inspected = inspectLockPayload(params.payload, params.staleMs, params.nowMs, { respectMaxHold: params.respectMaxHold }); if (shouldTreatAsOrphanSelfLock({ payload: params.payload, heldByThisProcess: params.heldByThisProcess, reclaimLockWithoutStarttime: params.reclaimLockWithoutStarttime })) return { ...inspected, stale: true, staleReasons: inspected.staleReasons.includes("orphan-self-pid") ? inspected.staleReasons : [...inspected.staleReasons, "orphan-self-pid"] }; if (shouldTreatAsNonOpenClawOwner({ payload: params.payload, inspected, heldByThisProcess: params.heldByThisProcess, readOwnerProcessArgs: params.readOwnerProcessArgs })) return { ...inspected, stale: true, staleReasons: [...inspected.staleReasons, "non-openclaw-owner"] }; return inspected; } async function cleanStaleLockFiles(params) { const sessionsDir = path.resolve(params.sessionsDir); const staleMs = resolvePositiveMs(params.staleMs, resolveSessionWriteLockStaleMs(params.config, params.env)); const removeStale = params.removeStale !== false; const nowMs = params.nowMs ?? Date.now(); const baseOwnerProcessArgsReader = params.readOwnerProcessArgs ?? readGatewayProcessArgsSync; const ownerArgsByPid = /* @__PURE__ */ new Map(); const ownerProcessArgsReader = (pid) => { const cached = ownerArgsByPid.get(pid); if (cached !== void 0) return cached; const args = baseOwnerProcessArgsReader(pid); ownerArgsByPid.set(pid, args); return args; }; let entries; try { entries = await fs.readdir(sessionsDir, { withFileTypes: true }); } catch (err) { if (err.code === "ENOENT") return { locks: [], cleaned: [] }; throw err; } const locks = []; const cleaned = []; const lockEntries = entries.filter((entry) => entry.name.endsWith(".jsonl.lock")).toSorted((a, b) => a.name.localeCompare(b.name)); for (const entry of lockEntries) { await new Promise((resolve) => { setImmediate(resolve); }); const lockPath = path.join(sessionsDir, entry.name); const lockInfo = { lockPath, ...inspectLockPayloadForSession({ payload: await readLockPayload(lockPath), staleMs, nowMs, heldByThisProcess: false, reclaimLockWithoutStarttime: false, readOwnerProcessArgs: ownerProcessArgsReader }), removed: false }; if (removeStale && await shouldRemoveLockDuringCleanup(lockPath, lockInfo, staleMs, nowMs)) { await fs.rm(lockPath, { force: true }); lockInfo.removed = true; cleaned.push(lockInfo); params.log?.warn?.(`removed stale session lock: ${lockPath} (${lockInfo.staleReasons.join(", ") || "unknown"})`); } locks.push(lockInfo); } return { locks, cleaned }; } async function acquireSessionWriteLock(params) { registerCleanupHandlers(); const allowReentrant = params.allowReentrant ?? false; const defaultOptions = resolveSessionWriteLockOptions(); const timeoutMs = resolvePositiveMs(params.timeoutMs, defaultOptions.timeoutMs, { allowInfinity: true }); const staleMs = resolvePositiveMs(params.staleMs, defaultOptions.staleMs); const maxHoldMs = resolvePositiveMs(params.maxHoldMs, defaultOptions.maxHoldMs); const orphanPayloadGraceMs = resolveOrphanLockPayloadGraceMs(timeoutMs); const sessionFile = path.resolve(params.sessionFile); const sessionDir = path.dirname(sessionFile); const normalizedSessionFile = await resolveNormalizedSessionFile(sessionFile); const lockPath = `${normalizedSessionFile}.lock`; await fs.mkdir(sessionDir, { recursive: true }); const startedAtMs = Date.now(); while (true) { const remainingTimeoutMs = resolveRemainingAcquireTimeoutMs(timeoutMs, startedAtMs, Date.now()); if (remainingTimeoutMs <= 0) { const payload = await readLockPayload(lockPath); const nowMs = Date.now(); const heldByThisProcess = sessionLockHeldByThisProcess(normalizedSessionFile); throw new SessionWriteLockTimeoutError({ timeoutMs, owner: describeLockOwnerForError({ payload, inspected: inspectLockPayloadForSession({ payload, staleMs, nowMs, heldByThisProcess, reclaimLockWithoutStarttime: true, readOwnerProcessArgs: readGatewayProcessArgsSync, respectMaxHold: !heldByThisProcess }) }), lockPath }); } try { return { release: (await SESSION_LOCKS.acquire(sessionFile, { staleMs, timeoutMs: remainingTimeoutMs, retry: { minTimeout: 50, maxTimeout: 1e3, factor: 1 }, staleRecovery: "remove-if-unchanged", allowReentrant, metadata: { maxHoldMs }, payload: () => { const createdAt = (/* @__PURE__ */ new Date()).toISOString(); const starttime = resolveProcessStartTimeForLock(process.pid); const lockPayload = { pid: process.pid, createdAt, maxHoldMs }; if (starttime !== null) lockPayload.starttime = starttime; return lockPayload; }, shouldReclaim: async ({ payload, nowMs, heldByThisProcess }) => { await yieldEventLoop(); return await shouldReportContendedLockStale({ lockPath, details: inspectLockPayloadForSession({ payload, staleMs, nowMs, heldByThisProcess, reclaimLockWithoutStarttime: true, readOwnerProcessArgs: readGatewayProcessArgsSync, respectMaxHold: !heldByThisProcess }), heldByThisProcess, staleMs, nowMs, orphanPayloadGraceMs }); }, shouldRemoveStaleLock: async ({ lockPath: lockPathLocal, normalizedTargetPath, payload }) => { await yieldEventLoop(); const nowMs = Date.now(); const heldByThisProcess = sessionLockHeldByThisProcess(normalizedTargetPath); return await shouldRemoveContendedLockFile(lockPathLocal, inspectLockPayloadForSession({ payload, staleMs, nowMs, heldByThisProcess, reclaimLockWithoutStarttime: true, readOwnerProcessArgs: readGatewayProcessArgsSync, respectMaxHold: !heldByThisProcess }), staleMs, nowMs, orphanPayloadGraceMs); } })).release }; } catch (err) { if (!isFileLockError(err, "file_lock_timeout") && !isFileLockError(err, "file_lock_stale")) throw err; const errorLockPath = err.lockPath ?? lockPath; const { payload, missing: lockMissingAtDiagnostics } = await readLockPayloadForDiagnostics(errorLockPath); const nowMs = Date.now(); const heldByThisProcess = sessionLockHeldByThisProcess(normalizedSessionFile); const inspected = inspectLockPayloadForSession({ payload, staleMs, nowMs, heldByThisProcess, reclaimLockWithoutStarttime: true, readOwnerProcessArgs: readGatewayProcessArgsSync, respectMaxHold: !heldByThisProcess }); const owner = describeLockOwnerForError({ payload, inspected }); if (isFileLockError(err, "file_lock_stale")) { if (resolveRemainingAcquireTimeoutMs(timeoutMs, startedAtMs, Date.now()) > 0 && await shouldRetryStaleAcquireFailure({ lockPath: errorLockPath, lockMissingAtDiagnostics, inspected, heldByThisProcess, staleMs, nowMs, orphanPayloadGraceMs })) continue; throw new SessionWriteLockStaleError({ owner, lockPath: errorLockPath, staleReasons: inspected.staleReasons }); } throw new SessionWriteLockTimeoutError({ timeoutMs, owner, lockPath: errorLockPath }); } } } const testing = { cleanupSignals: [...CLEANUP_SIGNALS], handleTerminationSignal, inspectLockPayloadForTest: inspectLockPayload, releaseAllLocksSync, runLockWatchdogCheck, resolveRemainingAcquireTimeoutMs, setProcessStartTimeResolverForTest(resolver) { resolveProcessStartTimeForLock = resolver ?? getProcessStartTime; } }; async function drainSessionWriteLockStateForTest() { await SESSION_LOCKS.drain(); stopWatchdogTimer(); unregisterCleanupHandlers(); } function resetSessionWriteLockStateForTest() { releaseAllLocksSync(); stopWatchdogTimer(); unregisterCleanupHandlers(); resolveProcessStartTimeForLock = getProcessStartTime; } //#endregion export { cleanStaleLockFiles as a, resolveSessionLockMaxHoldFromTimeout as c, resolveSessionWriteLockOptions as d, resolveSessionWriteLockStaleMs as f, acquireSessionWriteLock as i, resolveSessionWriteLockAcquireTimeoutMs as l, DEFAULT_SESSION_WRITE_LOCK_MAX_HOLD_MS as n, drainSessionWriteLockStateForTest as o, testing as p, DEFAULT_SESSION_WRITE_LOCK_STALE_MS as r, resetSessionWriteLockStateForTest as s, DEFAULT_SESSION_WRITE_LOCK_ACQUIRE_TIMEOUT_MS as t, resolveSessionWriteLockMaxHoldMs as u };