/** * v0.7 โ€” concurrent-uninstall guard. * Per docs/plan/v0.7-uninstall-lifecycle.md ยง5.1 precheck + P1 #6. * * A lockfile at `/.solosquad/uninstall.lock` prevents two * `solosquad uninstall` invocations from racing each other on the same * workspace. The file records the holder's PID and start timestamp, so * stale locks (PID dead) are detected and cleared automatically. * * Atomic acquisition uses `O_CREAT | O_EXCL` (Node `wx` flag), which is * race-safe across POSIX and Win32. */ export interface LockInfo { pid: number; startTs: string; hostname: string; } export interface LockHandle { /** Absolute path of the lockfile on disk. */ path: string; /** Release the lock (delete the file). Idempotent. */ release(): void; } export declare class LockHeldError extends Error { readonly info: LockInfo; readonly lockPath: string; constructor(info: LockInfo, lockPath: string); } /** * Cross-platform "is this PID alive?" probe. Uses `process.kill(pid, 0)` โ€” * signal 0 sends no signal but throws ESRCH if the process is gone. * * On Win32, Node implements signal 0 by calling OpenProcess; permission * issues throw EPERM, which we treat as "alive but not ours" (still alive). */ export declare function isProcessAlive(pid: number): boolean; export declare function readLock(lockPath: string): LockInfo | null; /** * Returns true if the lockfile exists but its holder process is gone. * Falsy if the lockfile is absent, or holder is alive, or content is * unparseable (in which case treat as held to avoid false-clean). */ export declare function isStaleLock(lockPath: string): boolean; export interface AcquireOptions { /** Override pid (tests). */ pid?: number; /** Override hostname (tests). */ hostname?: string; /** Override start ts (tests). */ startTs?: string; /** * If true and the existing lockfile is stale (holder dead), silently * delete it and proceed. Default true โ€” non-stale locks still throw. */ clearStale?: boolean; } /** * Acquire the lock or throw `LockHeldError` if a live holder exists. The * caller is responsible for invoking `release()` in finally blocks. */ export declare function acquireLock(lockPath: string, options?: AcquireOptions): LockHandle; /** * Convenience: get the conventional lock path under a workspace. */ export declare function uninstallLockPath(workspace: string): string;