Implements acquireLock() in src/runtime/lockfile.ts that writes a PID-stamped lock file under workspaces_root, detects live vs stale locks via process.kill(pid, 0), and returns a release function. src/index.ts acquires the lock before DB init and releases it in the graceful-shutdown handler. Four unit tests cover acquire, duplicate- acquire rejection, stale-lock recovery, and release.
57 lines
1.2 KiB
TypeScript
57 lines
1.2 KiB
TypeScript
import * as fs from "fs";
|
|
import * as path from "path";
|
|
|
|
interface LockData {
|
|
pid: number;
|
|
started: string;
|
|
}
|
|
|
|
function isProcessRunning(pid: number): boolean {
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function acquireLock(lockPath: string): Promise<() => void> {
|
|
if (fs.existsSync(lockPath)) {
|
|
const raw = fs.readFileSync(lockPath, "utf-8");
|
|
let data: LockData;
|
|
try {
|
|
data = JSON.parse(raw) as LockData;
|
|
} catch {
|
|
// Corrupted lock file — treat as stale, overwrite
|
|
data = { pid: 0, started: "" };
|
|
}
|
|
|
|
if (data.pid && isProcessRunning(data.pid)) {
|
|
throw new Error(
|
|
`DisClaw bereits aktiv (PID ${data.pid}). Lockfile: ${lockPath}`
|
|
);
|
|
}
|
|
// Stale lock — fall through and overwrite
|
|
}
|
|
|
|
// Ensure parent directory exists
|
|
const dir = path.dirname(lockPath);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
|
|
const lockData: LockData = {
|
|
pid: process.pid,
|
|
started: new Date().toISOString(),
|
|
};
|
|
|
|
fs.writeFileSync(lockPath, JSON.stringify(lockData), "utf-8");
|
|
|
|
const release = (): void => {
|
|
if (fs.existsSync(lockPath)) {
|
|
fs.unlinkSync(lockPath);
|
|
}
|
|
};
|
|
|
|
return release;
|
|
}
|