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
JavaScript
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
};