/** * Computer control relay: the server's runner socket <-> the computer executor. * * Path of one command (packages/desktop-contract/src/computer.ts): * server dispatchComputerCommand * -> socket "computer:command" { id, action, deadlineMs, allowedAppIds, allowedApps } + ack * -> this relay -> host channel { type: "computer.command", ... } (desktop app main process) * <- { type: "computer.result", id, result } -> ack(result) * * Who executes: * - inside the Melaya desktop app (isHosted()): the app's executor, over the * utilityProcess parent port (hostChannel.ts); * - plain `npx @melaya/runner --computer` on a headless machine / VM: the CLI * computer host (computerHostCli.ts, createCliComputerHost), when enabled; * - otherwise: every command is answered `unsupported`. * * The relay enforces the deadline on its side too: a command that has no * result by `deadlineMs` is acked as a timeout, and a late result is dropped. * `computer:stop` is forwarded at once. Capabilities are reported on every * (re)connect and whenever the executor reports a change. * * Message types MIRROR the contract (this package cannot import it). */ import { type HostToRunnerMessage, type RunnerToHostMessage } from "./hostChannel.js"; export declare const COMPUTER_EVENTS: { readonly command: "computer:command"; readonly stop: "computer:stop"; readonly capabilities: "computer:capabilities"; /** server -> runner: show a native approval dialog { approvalId, summary, effect, appName?, expiresAt } */ readonly approval: "computer:approval"; /** server -> runner: close it (decided elsewhere or expired) { approvalId } */ readonly approvalClosed: "computer:approval-closed"; /** runner -> server: the user's answer in the dialog { approvalId, decision } */ readonly approvalAnswer: "computer:approval-answer"; }; export declare const UNSUPPORTED_MESSAGE = "Computer control needs the Melaya desktop app on this computer."; /** The parts of a socket.io-client Socket the relay uses (tests pass a fake). */ export interface RelaySocket { connected?: boolean; on(event: string, listener: (...args: any[]) => void): unknown; emit(event: string, ...args: any[]): unknown; } /** The computer executor the runner CLI hosts on a headless machine * (packages/runner/src/computerHostCli.ts). Results may come back from * `handle` or through the `send` callback given at creation. */ export interface CliComputerHost { handle(msg: RunnerToHostMessage): unknown; capabilities(): unknown; stopAll(reason: string): void; dispose(): void; } export interface ComputerRelayDeps { isHosted(): boolean; sendToHost(msg: RunnerToHostMessage): void; onHostMessage(cb: (msg: HostToRunnerMessage) => void): () => void; /** Create the CLI host (null when computer control is not enabled for the CLI). */ createCliHost(opts: { send: (msg: HostToRunnerMessage) => void; verbose?: boolean; }): Promise; log(line: string): void; } /** * Attach the relay to the runner's server socket. Call once, right after the * socket is created (before it connects). Returns a disposer. */ export declare function attachComputerRelay(socket: RelaySocket, opts?: { verbose?: boolean; deps?: Partial; }): { dispose(): void; pendingCount(): number; };