/** * Daemon socket + lockfile path helpers — issue #411. * * One shared `lattice sensor serve --mcp` daemon per project root means we need a * stable, project-keyed rendezvous between cooperating processes. The IPC * surface area is just two file paths: * * - `daemon.sock` — Unix domain socket / named pipe the daemon listens on. * - `daemon.pid` — atomic-create lockfile holding the daemon's pid + version. * * Both live under `.lattice/sensor/` so the project-scoped uninstall (`lattice sensor * uninit`) sweeps them up for free. * * Special-case: Unix domain socket paths have a hard length limit (~104 on * macOS, ~108 on Linux); when the in-project path exceeds it we fall back to * an absolute-path hash under `os.tmpdir()`. The pidfile always stays in the * project (it doesn't have a length limit) — and acts as the authoritative * pointer to the socket path the daemon chose. * * Second special-case (#997, #974): some filesystems can't host an AF_UNIX node * AT ALL — ExFAT/FAT external volumes, certain network mounts, WSL2 DrvFs — so * `listen()` throws ENOTSUP/EACCES regardless of path length. We can't cheaply * tell those apart from a normal volume up front, so instead of guessing we * expose an ORDERED candidate list (`getDaemonSocketCandidates`): the in-project * path first, the deterministic tmpdir path as the fallback of last resort. The * daemon binds the first that works (relocating past a capability error); the * proxy connects the first that answers. Both walk the SAME list, so they still * converge on whichever the daemon bound with zero coordination. */ /** * Ordered socket / named-pipe path candidates the daemon should try to bind (and * the proxy should try to connect) for `projectRoot`, most-preferred first. * Deterministic given a project root, so independent processes converge without * coordination — even when the preferred candidate is unusable and both fall * through to the same fallback. * * - Windows: a single named pipe (lives in the kernel pipe namespace, not on * the project FS, so neither the length nor the ExFAT hazard applies). * - Short in-project path: `[ .lattice/sensor/daemon.sock , ]` — try the * project first, fall back to tmpdir if its FS can't host a socket (#997). * - Long in-project path (deep monorepos, Bazel out dirs): `[ ]` only * — bind would throw ENAMETOOLONG, so we skip straight to tmpdir. */ export declare function getDaemonSocketCandidates(projectRoot: string): string[]; /** * The PREFERRED (primary) socket path — candidate 0. Use this only where a * single representative path is wanted (the lockfile's informational * `socketPath` field, status display). For binding/connecting, walk the full * {@link getDaemonSocketCandidates} list — the daemon may bind a fallback when * candidate 0 is unusable. */ export declare function getDaemonSocketPath(projectRoot: string): string; /** Absolute path to the daemon pid lockfile for `projectRoot`. */ export declare function getDaemonPidPath(projectRoot: string): string; /** Structured contents of the pid lockfile. */ export interface DaemonLockInfo { pid: number; version: string; socketPath: string; startedAt: number; } /** * Serialize a {@link DaemonLockInfo} for writing to the pidfile. JSON for * human readability — operators occasionally `cat` this when debugging. */ export declare function encodeLockInfo(info: DaemonLockInfo): string; /** * Parse a pidfile body. Tolerant of old-format pidfiles (plain decimal pid) so * a 0.10.x daemon doesn't trip over a 0.9.x lockfile if that ever happens — * we treat such a lockfile as "process is unknown version, refuse to share." */ export declare function decodeLockInfo(raw: string): DaemonLockInfo | null; //# sourceMappingURL=daemon-paths.d.ts.map