openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
104 lines (103 loc) • 4 kB
JavaScript
import "./fs-safe-defaults-B7hUN42l.js";
import { i as resetFileLockManagerForTest, r as drainFileLockManagerForTest, t as acquireFileLock$1 } from "./file-lock-D6zEe-ZP.js";
import { r as isPidDefinitelyDead, t as getProcessStartTime } from "./pid-alive-C4bVUgUC.js";
//#region src/infra/stale-lock-file.ts
function readLockFileOwnerPayload(payload) {
if (!payload) return null;
return {
pid: typeof payload.pid === "number" ? payload.pid : void 0,
createdAt: typeof payload.createdAt === "string" ? payload.createdAt : void 0,
starttime: typeof payload.starttime === "number" ? payload.starttime : void 0
};
}
function shouldRemoveDeadOwnerOrExpiredLock(params) {
const payload = readLockFileOwnerPayload(params.payload);
if (payload?.pid) {
if (payload.starttime !== void 0) {
const currentStarttime = (params.getProcessStartTime ?? getProcessStartTime)(payload.pid);
if (currentStarttime !== null && currentStarttime !== payload.starttime) return true;
}
return (params.isPidDefinitelyDead ?? isPidDefinitelyDead)(payload.pid);
}
if (payload?.createdAt) {
const createdAt = Date.parse(payload.createdAt);
if (!Number.isFinite(createdAt) || (params.nowMs ?? Date.now()) - createdAt > params.staleMs) return true;
}
return false;
}
//#endregion
//#region src/plugin-sdk/file-lock.ts
/** Stable error code used when lock acquisition retries are exhausted. */
const FILE_LOCK_TIMEOUT_ERROR_CODE = "file_lock_timeout";
/** Stable error code used when stale lock recovery cannot proceed safely. */
const FILE_LOCK_STALE_ERROR_CODE = "file_lock_stale";
const FILE_LOCK_MANAGER_KEY = "openclaw.plugin-sdk.file-lock";
async function shouldReclaimPluginLock(params) {
return shouldRemoveDeadOwnerOrExpiredLock({
payload: params.payload,
staleMs: params.staleMs,
nowMs: params.nowMs
});
}
function normalizeLockError(err) {
if (err.code === "file_lock_timeout") throw Object.assign(new Error(err.message), {
code: FILE_LOCK_TIMEOUT_ERROR_CODE,
lockPath: err.lockPath ?? ""
});
if (err.code === "file_lock_stale") throw Object.assign(new Error(err.message), {
code: FILE_LOCK_STALE_ERROR_CODE,
lockPath: err.lockPath ?? ""
});
throw err;
}
/** Reset process-local file-lock state for tests that isolate lock managers. */
function resetFileLockStateForTest() {
resetFileLockManagerForTest(FILE_LOCK_MANAGER_KEY, FILE_LOCK_MANAGER_KEY);
}
/** Wait for process-local file-lock state to drain before test teardown. */
async function drainFileLockStateForTest() {
await drainFileLockManagerForTest(FILE_LOCK_MANAGER_KEY, FILE_LOCK_MANAGER_KEY);
}
/** Acquire a re-entrant process-local file lock backed by a `.lock` sidecar file. */
async function acquireFileLock(filePath, options) {
try {
const lock = await acquireFileLock$1(filePath, {
managerKey: FILE_LOCK_MANAGER_KEY,
staleMs: options.stale,
retry: options.retries,
staleRecovery: "remove-if-unchanged",
allowReentrant: true,
payload: () => {
const payload = {
pid: process.pid,
createdAt: (/* @__PURE__ */ new Date()).toISOString()
};
const starttime = getProcessStartTime(process.pid);
if (starttime !== null) payload.starttime = starttime;
return payload;
},
shouldReclaim: shouldReclaimPluginLock,
shouldRemoveStaleLock: (snapshot) => shouldRemoveDeadOwnerOrExpiredLock({
payload: snapshot.payload,
staleMs: options.stale
})
});
return {
lockPath: lock.lockPath,
release: lock.release
};
} catch (err) {
return normalizeLockError(err);
}
}
/** Run an async callback while holding a file lock, always releasing the lock afterward. */
async function withFileLock(filePath, options, fn) {
const lock = await acquireFileLock(filePath, options);
try {
return await fn();
} finally {
await lock.release();
}
}
//#endregion
export { resetFileLockStateForTest as a, drainFileLockStateForTest as i, FILE_LOCK_TIMEOUT_ERROR_CODE as n, withFileLock as o, acquireFileLock as r, FILE_LOCK_STALE_ERROR_CODE as t };