/** * The cockpit boot/orchestration layer — S8. * * Wires the whole page together from injected capabilities, with **no** direct * dependency on the browser, a socket implementation, or xterm.js — everything is * passed in ({@link CockpitEnv}). That is what makes the page render *identically* * embedded (console App View) and standalone (the two shells differ only in the * `host` element and the concrete capabilities they inject), and what makes the * live-refresh + drill-in + resume-on-reconnect path unit-testable on Node. * * Responsibilities: * - a **self-scheduling** poll of the S4 demand×supply report that re-renders the * matrix/lights each pass (a slow fetch can't overlap the next — mirrors the * nano-workforce poller discipline); * - **drill-into-a-worker**: open a {@link RelayChannelClient} + {@link TerminalSession} * for the selected stream, mount its output into a *persistent* terminal region * (so a matrix refresh never wipes it), and re-attach on every reconnect so the * terminal **survives a cockpit reconnect** via resume-from-offset. */ import type { DemandSupplyReport } from "../demand/index.ts"; import { type Scheduler, type SocketFactory } from "./relay-client.ts"; import { type DocumentLike, type ElementLike } from "./render.ts"; import { type StructuredSink, type TerminalSink } from "./terminal-session.ts"; /** Mounts a terminal into `host` and returns the sink relay output is written to. */ export type CreateTerminal = (host: ElementLike) => TerminalSink; /** Mounts a structured (ACP) view into `host` and returns the sink decoded transcript events are routed to. */ export type CreateStructured = (host: ElementLike) => StructuredSink; /** An opaque poll-timer handle (a Node `Timeout` or a browser timer id). */ export type TimerHandle = unknown; export interface CockpitEnv { /** The element the cockpit renders into (standalone: `document.body`; embedded: the App View host). */ readonly host: ElementLike; /** The document the renderer creates elements from. */ readonly doc: DocumentLike; /** Fetches the latest S4 demand×supply report (e.g. over HTTP or the channel). */ readonly fetchReport: () => Promise; /** Opens a socket to the app relay channel (one per drill-in connection). */ readonly connectRelay: SocketFactory; /** Mounts the terminal widget (xterm.js in the browser) and returns its write sink. */ readonly createTerminal: CreateTerminal; /** * Mounts the structured (ACP) view widget and returns its event sink. Optional: * when omitted the drill-in uses the built-in {@link createStructuredSink} DOM * renderer over {@link doc}, so marker-tagged chunks are decoded and routed to * that structured surface while raw bytes still flow to the {@link createTerminal} * sink. Provide your own to override the built-in renderer. */ readonly createStructured?: CreateStructured; /** Reconnect scheduler for the relay client. Default `setTimeout(run, 0)`. */ readonly schedule?: Scheduler; /** Poll scheduler. Default `setTimeout`. Injected so tests drive it by hand. Must be paired with {@link clearTimer}. */ readonly setTimer?: (run: () => void, ms: number) => TimerHandle; /** Cancels a poll timer. Default `clearTimeout`. Must be paired with {@link setTimer}. */ readonly clearTimer?: (handle: TimerHandle) => void; /** Poll interval in ms. Default 2000. */ readonly refreshMs?: number; /** Bulk credit granted per terminal (re)subscribe. Default 1024. */ readonly credit?: number; /** Notified of a fetch/render/relay error (the poll keeps going). */ readonly onError?: (err: unknown) => void; } /** The running cockpit; dispose to stop polling and tear down the terminal. */ export interface CockpitHandle { /** Run one fetch→render pass now (also the poll body). Resolves when rendered. */ refresh(): Promise; /** Start the self-scheduling poll loop (runs one pass immediately). */ start(): void; /** Stop the poll loop (leaves the last render in place). */ stop(): void; /** Drill into a worker's relay stream, opening a resumable live terminal. */ drill(stream: string): void; /** The stream currently drilled into, if any. */ readonly currentStream: string | undefined; /** Stop everything and release the terminal connection. */ dispose(): void; } /** Boot the cockpit against an injected environment. Call {@link CockpitHandle.start} to poll. */ export declare function bootCockpit(env: CockpitEnv): CockpitHandle;