import type { Logger } from '../types.js'; import type { FederationEnvelope, NodeManifest, PeerInfo, StreamFrame } from './types.js'; export type RpcHandler = (env: FederationEnvelope, remote: string) => Promise; export interface MeshServerOptions { host?: string; port: number; /** Optional shared token; requests must present it as Bearer auth. */ token?: string; getManifest: () => NodeManifest; /** Optional: full mesh view (self + peers) for the GET /fed/nodes probe. */ getNodes?: () => unknown; /** Connected peers and their reachable mesh URLs. */ getPeers?: () => PeerInfo[]; /** Optional: consolidated fleet snapshot (self + nodes + hardware + roles + * runs + recent activity) for the dashboard and `robopark fleet` (GET /fed/status). */ getStatus?: () => unknown; /** Optional: run a kernel slash-command on this node (POST /fed/command). * Powers the in-TUI mesh command palette + the dashboard's action buttons. */ runCommand?: (input: string) => Promise; /** Optional gate on POST /fed/command inputs. Returns {ok:false,reason} to * reject — used to keep an unauthenticated, network-exposed node read-only. */ commandGate?: (input: string) => { ok: boolean; reason?: string; }; /** Optional: publish this node's shared config to the fleet (POST /fed/apply). */ onApply?: () => Promise | unknown; /** Optional: static HTML for the control dashboard, served at GET / and /ui. */ dashboardHtml?: string; /** Optional TLS — when set, the node serves HTTPS/WSS instead of plain HTTP. */ tls?: { cert: Buffer; key: Buffer; }; /** Optional: base URL of the RoboPark scheduler. When set, the node proxies * GET/POST /robopark/* to it so the dashboard can show sessions + telemetry * (product data) alongside the mesh. */ schedulerUrl?: string; /** Optional bearer token to present to the scheduler on proxied requests. */ schedulerToken?: string; /** When true, only GET/HEAD are proxied to the scheduler — write actions * (end-session, production_mode toggle, trigger) are refused. Set for a * network-exposed, tokenless node so the operator console stays read-only. */ schedulerReadOnly?: boolean; onRpc: RpcHandler; logger: Logger; } export declare class MeshServer { private opts; private server?; private sse; private seq; private management; private screenTickets; constructor(opts: MeshServerOptions); get streamCount(): number; private streamHttp; private streamLocalRoboVision; private streamPeerRoboVision; private peerFor; private proxyScreenHttp; private tunnelUpgrade; private handleUpgrade; start(): Promise; /** Proxy a dashboard request to the RoboPark scheduler (product data). */ private proxyToScheduler; /** Proxy the scheduler's full Control Center and its same-origin assets. */ private proxySchedulerPath; /** Protected local runtime controls for the RoboPark operator dashboard. */ private runtimeDocker; /** Push a frame to every connected stream subscriber. */ broadcast(frame: StreamFrame): void; /** Token presented on a request — Bearer header (peers/CLI) OR ?token= query * (browsers, which can't set headers on navigation or EventSource). */ private presentedToken; private authOk; /** A robot uses this header only when it talks to the hub's scheduler proxy. * Its device token remains the Authorization header and is forwarded to the * scheduler. Requiring both keeps arbitrary Tailnet callers out. */ private schedulerProxyAuthOk; private publicVoiceAuthOk; private handle; private openStream; private readBody; stop(): Promise; } export interface MeshClientOptions { token?: string; timeoutMs?: number; } export declare class MeshClient { private opts; constructor(opts?: MeshClientOptions); get token(): string | undefined; private headers; /** Normalize a base URL so a trailing slash doesn't produce `//fed/...` (→ 404). */ private base; fetchManifest(baseUrl: string): Promise; rpc(baseUrl: string, env: FederationEnvelope): Promise; /** * Open an SSE stream to a peer. Returns a stop() fn. Reconnection is the * caller's job (peer-mesh handles it on heartbeat failure). */ openStream(baseUrl: string, onFrame: (f: StreamFrame) => void, onClose?: () => void): () => void; }