/** * Pi binary wrapper — low-level launcher for the Pi coding agent CLI. * * Handles prompt delivery, process spawning, stdin/stdout/stderr wiring, * and SIGTERM-then-SIGKILL cleanup. This module is the only place in * cleo-os that touches `child_process.spawn` directly for Pi. * * @remarks * Pi receives its prompt via **file mode**: the prompt text is written to a * temporary file under `/tmp/` and passed as a positional CLI argument * (`pi `). This mirrors the pattern used by `PiSpawnProvider` in * `packages/adapters/` and avoids stdin complexity for non-interactive runs. * * CleoOS extension injection follows the same mechanism as `cli.ts`: each * discovered extension is prepended as `--extension ` so Pi loads * bridges (CANT bridge, hooks bridge, etc.) even in non-interactive mode. * * Environment variable overrides (all optional): * - `CLEO_PI_BINARY` — path to the `pi` binary (default: `pi` from PATH) * - `CLEO_TERMINATE_GRACE_MS` — SIGTERM grace window in ms (default: `5000`) * - `CLEO_HARNESS_OUTPUT_BUFFER` — ring buffer capacity per process (default: `500`) * * OpenClaw patterns adopted: * - Extension injection via `--extension ` flags. * - Prompt written to `/tmp/` with a unique suffix; cleaned up on exit. * - `PI_TELEMETRY=0` injected by default to suppress telemetry in CI. * * @packageDocumentation */ import type { ChildProcess } from 'node:child_process'; import type { HarnessOutputLine, HarnessProcessState, HarnessProcessStatus } from './types.js'; /** * Resolve the `pi` binary path. * * Honours `CLEO_PI_BINARY` env var when set (absolute path or bare name), * otherwise defaults to `pi` (expects the binary on PATH). * * @returns Resolved binary path string. * * @public */ export declare function getPiBinaryPath(): string; /** * Return the SIGTERM grace window in milliseconds. * * Reads `CLEO_TERMINATE_GRACE_MS` from the environment; falls back to * {@link DEFAULT_TERMINATE_GRACE_MS} when absent or non-positive. * * @public */ export declare function getTerminateGraceMs(): number; /** * Return the output ring buffer capacity. * * Reads `CLEO_HARNESS_OUTPUT_BUFFER` from the environment; falls back to * {@link DEFAULT_OUTPUT_BUFFER_SIZE} when absent or non-positive. * * @public */ export declare function getOutputBufferSize(): number; /** * Collect CleoOS extension paths that exist on disk. * * Resolves the standard extension list relative to the compiled package root * (`extensions/` directory adjacent to `dist/`). Only paths that exist on * disk are returned — missing extensions are skipped silently so the adapter * degrades gracefully when extensions are not installed. * * Mirrors `collectExtensionPaths()` in `cli.ts` but does not depend on * `@cleocode/core` to keep this module free of external package imports. * * @returns Array of absolute `.js` extension paths. * * @public */ export declare function resolveExtensionPaths(): string[]; /** * Internal state tracked for each spawned Pi process. */ export interface PiProcessEntry { /** OS process ID (or null if spawn failed before assigning one). */ pid: number | null; /** Live child process handle (null after the process exits). */ child: ChildProcess | null; /** Task ID from the caller. */ taskId: string; /** Stable instance ID. */ instanceId: string; /** ISO-8601 spawn timestamp. */ startedAt: string; /** Current lifecycle state. */ state: HarnessProcessState; /** Exit code (populated on exit). */ exitCode: number | null; /** Signal used to terminate (populated on kill). */ terminatingSignal: NodeJS.Signals | null; /** ISO-8601 end timestamp. */ endedAt: string | null; /** Error message for 'failed' state. */ error: string | null; /** Bounded output ring buffer. */ outputBuffer: HarnessOutputLine[]; /** Resolve callback for the exitPromise. */ resolveExit: (status: HarnessProcessStatus) => void; /** Path of the temporary prompt file (cleaned up on exit). */ tmpFile: string | null; /** SIGKILL timer handle (set during the grace window). */ killTimer: ReturnType | null; } /** * Snapshot the current state of a process entry as a {@link HarnessProcessStatus}. * * @param entry - The process tracking entry. * @returns Immutable status snapshot. * * @public */ export declare function buildStatus(entry: PiProcessEntry): HarnessProcessStatus; /** * Create a new process tracking entry for a given instance and task. * * The entry starts in a transitional `'failed'` state that will be * immediately overwritten by {@link PiWrapper.start} on success. * * @param instanceId - Stable instance identifier. * @param taskId - CLEO task ID. * @param resolveExit - Promise resolve callback wired to the exit promise. * @returns Initialised (pre-spawn) tracking entry. * * @public */ export declare function createProcessEntry(instanceId: string, taskId: string, resolveExit: (status: HarnessProcessStatus) => void): PiProcessEntry; /** * Low-level Pi process launcher. * * Manages the full lifecycle of Pi CLI processes: spawning via * {@link start}, output buffering into a ring buffer, and * SIGTERM-then-SIGKILL termination via {@link terminate}. Process state * is tracked in a {@link PiProcessEntry} held by the caller * ({@link PiCodingAgentAdapter}). * * @public */ export declare class PiWrapper { /** * Spawn a Pi CLI process for the given task. * * Writes the prompt to a temporary file in `/tmp/`, builds the argument * list (prepending `--extension ` for each available CleoOS * extension), and spawns Pi as a child process. Stdout and stderr are * line-buffered into `entry.outputBuffer`. * * When spawn fails (e.g. binary not found), `entry.state` is set to * `'failed'` and `entry.resolveExit` is called before returning. * * @param entry - Pre-allocated process tracking entry. Mutated in place. * @param prompt - Prompt text to deliver to Pi. * @param cwd - Working directory for the child process. * @param env - Environment variable overrides merged atop the current env. * @returns The mutated `entry` (for chaining convenience). * * @public */ start(entry: PiProcessEntry, prompt: string, cwd: string, env: Record): Promise; /** * Terminate a Pi process via SIGTERM-then-SIGKILL. * * Sends SIGTERM immediately. If the process has not exited within the * configured grace window ({@link getTerminateGraceMs}), sends SIGKILL. * Idempotent — subsequent calls when the process is already dead are no-ops. * * @param entry - The process tracking entry to terminate. * * @public */ terminate(entry: PiProcessEntry): void; } //# sourceMappingURL=pi-wrapper.d.ts.map