UNPKG

trellis

Version:

Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications

100 lines (97 loc) 2.64 kB
import { __esm } from "./chunk-2ESYSVXG.js"; // src/vcs/promote-lock.ts import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs"; import { join } from "path"; function lockPath(trellisDir) { return join(trellisDir, "locks", "promote.lock"); } function isProcessAlive(pid) { try { process.kill(pid, 0); return true; } catch (err) { return err?.code === "EPERM"; } } function readLock(trellisDir) { const path = lockPath(trellisDir); if (!existsSync(path)) return null; try { return JSON.parse(readFileSync(path, "utf-8")); } catch { return null; } } function isStale(record, staleMs) { const age = Date.now() - new Date(record.acquiredAt).getTime(); if (age > staleMs) return true; return !isProcessAlive(record.pid); } function acquirePromoteLock(trellisDir, laneId, opts) { const staleMs = opts?.staleMs ?? DEFAULT_STALE_MS; mkdirSync(join(trellisDir, "locks"), { recursive: true }); const path = lockPath(trellisDir); const existing = readLock(trellisDir); if (existing && !isStale(existing, staleMs) && !opts?.force) { throw new Error( `Promote lock held by lane ${existing.laneId} (pid ${existing.pid} since ${existing.acquiredAt}). Retry when the other promote finishes, or use --force-lock if stale.` ); } if (existing && opts?.force && existsSync(path)) { unlinkSync(path); } const record = { pid: process.pid, laneId, acquiredAt: (/* @__PURE__ */ new Date()).toISOString(), hostname: process.env.HOSTNAME ?? process.env.USER }; writeFileSync(path, JSON.stringify(record, null, 2) + "\n"); } function releasePromoteLock(trellisDir, laneId) { const path = lockPath(trellisDir); if (!existsSync(path)) return; const existing = readLock(trellisDir); if (!existing) { unlinkSync(path); return; } if (existing.pid === process.pid && existing.laneId === laneId) { unlinkSync(path); } } function getPromoteLockStatus(trellisDir, staleMs = DEFAULT_STALE_MS) { const record = readLock(trellisDir); if (!record) return { locked: false }; if (isStale(record, staleMs)) { return { locked: false, record, stale: true }; } return { locked: true, record }; } function clearPromoteLock(trellisDir) { const path = lockPath(trellisDir); if (!existsSync(path)) return false; unlinkSync(path); return true; } var DEFAULT_STALE_MS; var init_promote_lock = __esm({ "src/vcs/promote-lock.ts"() { DEFAULT_STALE_MS = 5 * 60 * 1e3; } }); export { acquirePromoteLock, releasePromoteLock, getPromoteLockStatus, clearPromoteLock, init_promote_lock };