/** * A real host pseudo-terminal, or nothing. * * `exec` runs a command and hands back what it printed. That is the whole * shape of it, and a large class of work does not fit: an interactive * installer waiting on a prompt, a REPL, `git rebase -i`, anything that * draws with escape codes, anything that asks for a password. Every one of * those needs a pseudo-terminal, and a pipe is not one. * * **The refusal is the design.** A pseudo-terminal needs a native binding * this kernel deliberately does not depend on — it would make every install * build C++ for a capability most runs never use. So the terminal is * optional, and where it is unavailable it is REFUSED rather than * substituted with a pipe. A pipe would work: `spawn` would run, bytes * would flow, and every program that calls `isatty` would take its * non-interactive branch. The prompt never appears, the REPL exits * immediately, the progress bar prints ten thousand lines, and nothing says * why. That is the same rule `Sandbox.setNetworkPolicy` states for itself: * a capability accepted and not applied is worse than one never offered, * because the caller stops looking. * * Nothing in this module creates a sandbox or owns a complete descendant * process tree. `openTerminalWith` starts exactly the program, arguments and * working directory it is handed through the supplied binding. A sandbox or * durable terminal backend must wrap that argv under its own confinement and * make teardown await every process it owns before using this primitive. */ export interface TerminalSize { readonly cols: number; readonly rows: number; } export interface OpenTerminalOptions { /** The program to run. Defaults to the caller-supplied shell. */ readonly command?: string; readonly args?: readonly string[]; readonly cwd?: string; readonly env?: Readonly>; /** * The initial size. * * Required rather than defaulted, because a program that draws to the * screen asks the terminal how big it is before it draws anything, and a * default that is wrong produces a first frame nobody can read. A caller * with no window to measure passes its own choice, deliberately. */ readonly size: TerminalSize; } export interface TerminalSession { /** Send keystrokes. Bytes, not lines — the program decides what a line is. */ write(data: string): void; /** * Tell the program the window changed. * * A separate call rather than a settable property, because it has to * reach the child as SIGWINCH: a program redraws on the signal, not on * our bookkeeping. */ resize(size: TerminalSize): void; /** Everything the program printed, as it arrives. Returns an unsubscribe. */ onData(listener: (chunk: string) => void): () => void; /** Resolves when the program exits. */ readonly exited: Promise<{ exitCode: number; signal?: number; }>; /** Ask the backing binding to stop its top-level terminal process. */ kill(signal?: string): void; } /** A terminal asked for where none can be provided. */ export declare class TerminalUnavailableError extends Error { readonly details: { specifier: string; reason: 'absent' | 'broken'; cause?: string; }; constructor(details: { specifier: string; reason: 'absent' | 'broken'; cause?: string; }); } /** * The binding this looks for. * * Named rather than searched for: probing several candidates would make the * capability's behaviour depend on which one happened to be installed, and * a refusal that could not say what to install is a refusal nobody can act * on. */ export declare const PTY_SPECIFIER = "node-pty"; /** The slice of the binding this uses. Structural, so a test needs no native build. */ export interface PtyModule { spawn(file: string, args: readonly string[], options: { name?: string; cols: number; rows: number; cwd?: string; env?: Record; }): PtyProcess; } export interface PtyProcess { write(data: string): void; resize(cols: number, rows: number): void; onData(listener: (data: string) => void): { dispose(): void; }; onExit(listener: (event: { exitCode: number; signal?: number; }) => void): { dispose(): void; }; kill(signal?: string): void; } export type PtyLoader = () => Promise; /** * Load the binding, or say precisely why not. * * The two failures are kept apart because the fixes are different: `absent` * is "install it", `broken` is almost always a native build compiled * against a different Node version, and telling somebody to install a thing * they already installed is the least useful message available. */ export declare function loadPty(loader?: PtyLoader): Promise; /** * Open a host terminal, given a loaded binding. * * Separated from `loadPty` so the refusal and the session are testable * apart: one is about a missing dependency, the other about wiring a * process that exists. This helper does not add confinement or descendant * cleanup; those belong to the backend that supplies its argv and owns the * returned session. */ export declare function openTerminalWith(pty: PtyModule, options: OpenTerminalOptions, defaults: { readonly shell: string; readonly cwd: string; }): TerminalSession; //# sourceMappingURL=terminal.d.ts.map