import { type SystemctlRunner } from './systemd-scope.js'; export interface SystemdSpawnParams { claudeBin: string; claudeArgs: readonly string[]; /** Env stamped onto the scope via `--setenv=KEY=VAL` flags. Manager-side * vars (XDG_RUNTIME_DIR, DBUS_SESSION_BUS_ADDRESS) needed to reach user * systemd are read from process.env separately. */ env: Readonly>; cwd: string; unitToken: string; /** Path the scope's inner `script(1)` writes its typescript to — * retrievable after the scope is reaped. The parent directory is * created (mkdir -p) before systemd-run is spawned so script(1) can * open the file without ENOENT. */ captureFilePath: string; /** systemctl runner — production uses spawnSync; tests inject a stub. */ runSystemctl?: SystemctlRunner; /** Reads `cgroup.procs` at /sys/fs/cgroup. Returns the * list of pids in the cgroup, or empty when the file does not exist * or contains no pids. Tests inject a stub. */ readCgroupProcs?: (controlGroup: string) => number[]; /** Reads the tail of the script(1) typescript file for inclusion in * activation-gate failure messages. Tests inject a stub; production * uses the module's own `readCaptureFile`. */ readCaptureTail?: (captureFilePath: string, maxBytes: number) => Promise; /** Injected child-process spawner for tests. Production uses node:child_process spawn. */ spawnChild?: (cmd: string, args: readonly string[], opts: SpawnOpts) => SpawnedChild; /** Maximum wait for the scope to activate with at least one pid in its * cgroup. Default 5000 ms. */ activationTimeoutMs?: number; /** Inter-poll interval. Default 50 ms. */ activationPollIntervalMs?: number; } export interface SpawnOpts { cwd: string; env: NodeJS.ProcessEnv; stdio: 'ignore'; detached: boolean; } export interface SpawnedChild { pid: number | undefined; unref(): void; on(event: 'error', cb: (err: Error) => void): void; } export interface ScopeExit { /** systemd's Result string: "success", "exit-code", "signal", "timeout", "core-dump", "watchdog", "start-limit-hit", "resources". */ result: string; /** ExecMainStatus — process exit code or signal number depending on `result`. */ exitCode: number | null; /** ExecMainCode — "exited" / "killed" / "dumped", from systemd's process-exit classification. */ code: string | null; } export interface WaitForExitOpts { pollIntervalMs?: number; } export interface SystemdScopeHandle { pid: number; unitToken: string; /** The script(1) typescript file written by the scope's inner wrapper. * Holds the child's stdout/stderr; remains on disk after the scope is * reaped so a post-mortem investigator can read claude's real exit * message without re-running anything. */ captureFilePath: string; /** Resolves once the scope unit's `ActiveState` is no longer in the * set { 'active', 'activating', 'reloading' }. Reads the unit's * `Result`, `ExecMainStatus`, and `ExecMainCode` to populate * the returned `ScopeExit`. */ waitForExit(opts?: WaitForExitOpts): Promise; /** Read the tail of the per-spawn capture file (the script(1) * typescript). Trimmed to `maxBytes` from the end so the line * preceding exit wins. */ readCaptureTail(maxBytes: number): Promise; /** Stop the scope via `systemctl --user stop --no-block` (delegates to * systemd-scope.stopScopeUnit so the existing escalation path applies). */ stop(): Promise; } /** Spawn `systemd-run --user --scope ... ` so the * manager holds no file descriptors for the resulting claude process. * Returns once cgroup.procs is non-empty AND ActiveState is `active`. * On terminal failure, stops the partially-registered scope and rejects * with one of three distinct error tokens. */ export declare function systemdSpawn(p: SystemdSpawnParams): Promise; //# sourceMappingURL=systemd-spawn.d.ts.map