/** * PID / Daemon State File Manager * Issue #96: npm install CLI support * Issue #136: Phase 2 - Task 2.4 - Added factory functions for Issue number support * Issue #1354/#1355/#1358: the file now records the daemon's version, effective settings, and * a process-identity signature, not just the PID. See DaemonState. * Issue #1632: the file is written in a hybrid format (bare PID on line 1, JSON on line 2) so * that CLIs predating #1354 can still recover the PID from it. See formatState(). * SF-1: SRP - Separated from daemon.ts for single responsibility * MF-SEC-2: TOCTOU protection with O_EXCL atomic writes */ import { ProcessStartTimeReader } from './process-inspector'; /** * The daemon state persisted alongside the PID. * * Only `pid` is guaranteed: files written by older versions held just the PID as a bare integer, * so every other field is optional and readState() tolerates their absence (backward compat). */ export interface DaemonState { /** Process ID of the daemon */ pid: number; /** Package version the daemon was started with (Issue #1354) */ version?: string; /** Effective port the server listens on (Issue #1355) */ port?: number; /** Effective bind address, as configured (Issue #1355) */ bind?: string; /** Effective protocol the server speaks (Issue #1355) */ protocol?: 'http' | 'https'; /** Whether token authentication was enabled at startup (Issue #1355) */ auth?: boolean; /** ISO timestamp of when this record was written */ startedAt?: string; /** OS-reported process start time, used to detect PID reuse (Issue #1358) */ startTime?: string; } /** * PID file manager for daemon process tracking */ export declare class PidManager { private readonly pidFilePath; private readonly readStartTime; /** * @param pidFilePath - Path to the state file for this server * @param readStartTime - Injectable process-identity reader (Issue #1358); defaults to `ps`. * Overridden in tests to avoid spawning a real process. */ constructor(pidFilePath: string, readStartTime?: ProcessStartTimeReader); /** * Check if the state file exists */ exists(): boolean; /** * Read the daemon state from file. * * Understands all three formats this file has ever had (Issue #1632): * - hybrid (current): bare PID on line 1, JSON state on line 2 * - JSON only (#1354 .. #1632): the full state as one JSON object * - bare integer (pre-#1354): just the PID, returned as `{ pid }` * * Reading the JSON of the hybrid format is not optional: falling back to the line-1 PID * would silently drop `version`, disabling the CLI↔server version mismatch warning added * by #1354, and drop `port`/`protocol`/`bind`, sending status back to re-deriving the URL * from a possibly-diverged .env (#1355). * * @returns The daemon state, or null when the file is missing or invalid */ readState(): DaemonState | null; /** * Read PID from the state file * @returns PID number or null if file doesn't exist or is invalid */ readPid(): number | null; /** * Write the daemon state to file atomically * MF-SEC-2: Uses O_EXCL to prevent TOCTOU race conditions * Issue #1632: written in the hybrid format (see formatState) * * @returns true if successful, false if file already exists * @throws Error for other filesystem errors */ writeState(state: DaemonState): boolean; /** * Read a live process's start-time signature via the injected reader. * Used by start() to record the daemon's identity at launch (Issue #1358). * * @param pid - Process ID to inspect * @returns The start-time signature, or null when it cannot be read */ getStartTime(pid: number): string | null; /** * Remove the state file */ removePid(): void; /** * Check if the process recorded in the state file is the daemon we started. * NTH-1: ISP - Lightweight process check API * * Issue #1358: `process.kill(pid, 0)` alone only proves the PID is in use. When the state * records a start-time signature, the live process's is compared to catch PID reuse; a * mismatch is treated as not-running so stop() never kills, and start() never defers to, an * unrelated process. EPERM (the PID was reused by a process we don't own) is likewise treated * as stale rather than thrown, so a reused PID no longer crashes the command. * * @returns true if the daemon is running, false otherwise */ isProcessRunning(): boolean; } /** * Factory function to create PidManager instance * Issue #136: Uses PidPathResolver for path resolution * * @param issueNo - Optional issue number for worktree-specific PID * @returns PidManager instance * * @example * ```typescript * // Main server PID manager * const mainManager = createPidManager(); * * // Worktree-specific PID manager * const issueManager = createPidManager(135); * ``` */ export declare function createPidManager(issueNo?: number): PidManager; /** * Factory function to create PidManager for a specific issue * Issue #136: Convenience function for worktree PID management * * @param issueNo - Issue number * @returns PidManager instance for the specified issue * * @example * ```typescript * const manager = createIssuePidManager(135); * if (manager.isProcessRunning()) { * console.log('Worktree server for issue #135 is running'); * } * ``` */ export declare function createIssuePidManager(issueNo: number): PidManager; //# sourceMappingURL=pid-manager.d.ts.map