import { TerminalCapability } from "@opengeni/sdk"; /** The ttyd connection lifecycle as surfaced to the component. */ export type TerminalStreamStatus = "connecting" | "open" | "closed" | "error"; export type UseTerminalStreamOptions = { /** The Terminal cell of the negotiated capabilities (`capabilities.Terminal`). * The stream connects ONLY when `transport === "pty-ws"` and `url` is set; on a * cold box (`transport === "sse-events"` / no url) it stays idle and the caller * falls back to the Channel-A firehose. Explicit PTY-capable cold cells may * buffer input, but cannot connect or flush until the grant arrives. */ capability: (Pick & Partial>) | null; /** Called for each OUTPUT payload from ttyd (write verbatim into xterm). */ onOutput?: ((data: string) => void) | undefined; /** Called when ttyd sends a SET_WINDOW_TITLE frame. */ onTitle?: ((title: string) => void) | undefined; /** Requests a fresh negotiated terminal grant after an unexpected transport * failure. Intentional disconnects and effect cleanup never call it. */ onReconnectNeeded?: (() => void) | undefined; /** Initial PTY size to seed the ttyd auth frame + first resize. */ initialCols?: number | undefined; initialRows?: number | undefined; }; export type UseTerminalStreamResult = { /** True when input can be sent. Legacy ttyd does not guarantee shell readiness. */ connected: boolean; status: TerminalStreamStatus; /** `waiting` requires explicit shell readiness; `legacy` has no such guarantee. */ inputReadiness: "pending" | "waiting" | "ready" | "legacy"; /** Deliberate escape for startup prompts/no-Readline shells. Clears queued * typing without executing it, then enables unguaranteed manual input. */ useLegacyInput: () => void; /** Pipe a keystroke/paste to PTY stdin. Input during CONNECTING is bounded and * replayed after negotiated readiness (or legacy ttyd preferences). */ write: (data: string) => void; /** Tell ttyd the PTY window changed size (on xterm fit/resize). */ resize: (cols: number, rows: number) => void; /** Tear the socket down (the effect also tears down on unmount / url change). */ disconnect: () => void; }; export declare const MAX_PENDING_TERMINAL_INPUT_CODE_UNITS: number; /** A bearer must remain valid long enough to finish a new websocket handshake. * Existing open sockets are unaffected: this is evaluated only when the * credential identity changes and the connection effect attempts a new socket. */ export declare function terminalStreamCredentialUsable(capability: Pick | null | undefined, nowMs?: number): boolean; /** * Drive a ttyd PTY-over-websocket connection from a `pty-ws` Terminal capability, * symmetric with `use-desktop-stream` (the noVNC-over-tunnel hook). The scoped * stream token is already embedded in the minted tunnel `url`; the WebSocket is * opened with the REQUIRED ttyd subprotocol "tty". * * ttyd wire protocol (see `@opengeni/sdk/terminal`): * - first frame: `JSON.stringify({ AuthToken: "" })` (+ optional columns/rows). * - client→server: INPUT = "0"+data ; RESIZE = "1"+JSON({columns,rows}). * - server→client: "0" = OUTPUT (→ xterm) ; "1" = SET_WINDOW_TITLE ; * "2" = SET_PREFERENCES (readiness negotiation). Binary frames decode identically. * * On a `url`/`token` rotation (a box rollover folds a fresh address into the cell) * the effect re-runs: the old socket closes and a fresh one connects — a brief * terminal blink, acceptable on rollover (mirrors the desktop's RFB hot-swap). * SSR-safe: the socket open lives in `useEffect`, so a server render is a no-op. */ export declare function useTerminalStream(options: UseTerminalStreamOptions): UseTerminalStreamResult;