/** * Cross-Process Advisory Lock * * File-based advisory lock preventing overlapping runs of the same workflow * across PROCESSES — e.g. a cron-scheduled `pluginator auto-update` starting * while the previous run is still going, or the schedule daemon and an * interactive TUI session mutating the same server directory simultaneously * (June 9 2026 audit, workflows-infra 2.1/4: `workflowLock` is in-process * only and nothing cross-process guards the workflows). * * Differences from the older `pid-lock.ts`: * - Atomic acquisition via the `wx` open flag — no check-then-write race * between two processes acquiring simultaneously. * - Staleness by AGE as well as dead PID: a lock older than `staleMs` is * taken over even if its PID is alive (covers PID reuse and wedged * processes that will never release). * - Optional bounded wait (`timeoutMs`) instead of immediate failure. * - Best-effort cleanup of held locks on process exit. * * Lock files use the `.plock` suffix (in ~/.pluginator/recovery/locks/) so * they never collide with pid-lock's `.lock` files or its stale-sweeper. * * NOT wired into workflows in this batch — the Phase 4 SafeApply agent owns * the wiring. Export + tests only. * * @since Phase 4 (Universal Update Automation) */ /** Locks older than this are stale even if the owner PID is alive. */ export declare const DEFAULT_STALE_MS: number; export interface ProcessLockOptions { /** * How long to wait for a busy lock before throwing. * Default 0 — fail fast (the right behavior for overlapping cron runs: * the previous run is still working, this one should bow out). */ timeoutMs?: number; /** * Locks older than this are considered stale and taken over even when the * owner PID is alive. Default {@link DEFAULT_STALE_MS}. */ staleMs?: number; /** Poll interval while waiting (default 200 ms). */ pollIntervalMs?: number; /** Lock directory override (tests). Default ~/.pluginator/recovery/locks. */ dir?: string; } export interface ProcessLockHandle { /** The lock name as passed to {@link acquireProcessLock}. */ name: string; /** Absolute path of the lock file. */ lockPath: string; /** * Release the lock. Idempotent; never removes a lock that has since been * taken over by another process. */ release: () => void; } /** * Acquire a cross-process advisory lock. * * Acquisition loop: atomic `wx` create → on EEXIST, take over the lock if * stale (dead PID / too old / corrupt) and retry, otherwise wait and poll * until `timeoutMs` is exhausted, then throw a {@link WorkflowError}. * * @param name Lock name — one per guarded operation (e.g. 'auto-update'). * @returns A handle whose `release()` must be called on completion. Locks * still held at process exit are swept best-effort. * @throws WorkflowError when the lock is held by a live process and * `timeoutMs` elapses (code 'UNEXPECTED_ERROR', message identifies the * holding PID). */ export declare function acquireProcessLock(name: string, options?: ProcessLockOptions): Promise; //# sourceMappingURL=process-lock.d.ts.map