/** * A long-lived proxy supervisor, so compression is on by default rather than only inside * `token-optimizer-run`. * * WHY THIS EXISTS. `startProxy` binds port 0 and lives as long as the launcher that started it, so * the only way to have a client routed was to launch it through us. A client started any other way * -- the documented `/plugin` install, an IDE, a shell alias -- talked straight to the provider and * saved nothing, while `install_doctor` reported that as a failure of the installation. * * ONE LISTENER PER UPSTREAM, NOT ONE PORT FOR EVERYTHING. A proxy forwards to a single upstream and * refuses paths that upstream does not serve (see `defaultUpstreamServes`), and the ten routable * clients do not share a provider: Claude Code speaks to Anthropic, Codex and friends to an * OpenAI-compatible endpoint, Gemini to Google, Copilot and Amp to their own. A single shared port * would therefore have to guess, and guessing wrong does not degrade politely -- it forwards one * provider's credentials to another. Callers name the upstream they are already using and get back * a loopback URL bound to it. * * THE CALLER'S CURRENT ENDPOINT IS THE UPSTREAM. A user who has already pointed a client at Azure, * a corporate gateway or a local model must keep reaching it; we insert ourselves in front of * whatever they configured, never in place of it. * * FAIL OPEN, ALWAYS. Every entry point here answers `null` when the supervisor cannot be reached or * started. A caller that gets `null` leaves the client's endpoint alone, so the worst outcome is * the behaviour users have today -- uncompressed traffic -- and never a client pointed at a port * with nothing behind it. */ import { type Server } from 'node:http'; /** Where the supervisor records what it is serving, for callers and for the doctor. */ export declare function supervisorStateFile(env?: NodeJS.ProcessEnv): string; /** * The control port. * * FIXED, because a caller has to find the supervisor without being told where it is, and an * ephemeral port would put that answer only in a file a stale reader could mis-read. 45710 sits in * the IANA dynamic range and is not a registered service. */ export declare function controlPort(env?: NodeJS.ProcessEnv): number; export interface SupervisorRoute { readonly upstream: string; readonly url: string; readonly port: number; /** * The project this route serves knowledge for, or null. * * TRUSTED BECAUSE THE CALLER IS OURS. It arrives over the loopback control API from a launcher * that was started inside that directory; it is never read out of a model request. A request can * say anything, and a request that could choose a project could read another project's findings * into its own prompt and send them to the provider. */ readonly project: string | null; } /** * The port a given upstream should be served on, derived from the upstream itself. * * WHY NOT PORT 0. An ephemeral port is fine while the only consumer is a launcher that learns it at * startup. It is not fine once a client's own configuration names the URL: that file outlives the * supervisor, so a restart on a fresh port would leave a client pointed at a port nothing is * listening on -- the one failure mode worse than saving nothing, because the client cannot reach * its provider at all. * * Derived rather than assigned so it survives the state file being lost, and taken from the IANA * dynamic range just above the control port. A collision with something else on the machine is * handled by the caller, which falls back to any free port and republishes. */ export declare function routePort(upstream: string, env?: NodeJS.ProcessEnv): number; export interface SupervisorState { readonly schema: 1; readonly pid: number; readonly startedAt: string; readonly controlUrl: string; readonly routes: readonly SupervisorRoute[]; } export declare function readSupervisorState(env?: NodeJS.ProcessEnv): SupervisorState | null; /** The supervisor's own report, or null when nothing is listening. */ export declare function supervisorHealth(env?: NodeJS.ProcessEnv): Promise<{ ok: true; pid: number; routes: SupervisorRoute[]; } | null>; /** * Serve the control API and the per-upstream proxies until the process is stopped. * * Refuses to start a second time: an already-answering supervisor owns the port, and two of them * would each publish a different route for the same upstream. */ export declare function runSupervisor(env?: NodeJS.ProcessEnv): Promise<{ server: Server; port: number; close: () => Promise; } | null>; /** * May we start a background supervisor on this machine? * * Someone who does not want a long-lived local service gets to say so, and a test that must not * leave one behind says the same thing. An already-running supervisor is still used either way. */ export declare function autostartAllowed(env?: NodeJS.ProcessEnv): boolean; /** Spawn a detached supervisor and wait for it to answer, or give up. */ export declare function ensureSupervisor(env?: NodeJS.ProcessEnv, waitMs?: number): Promise; /** * The loopback URL a client should use to reach `upstream` through compression, or null. * * Null is the fail-open answer: the caller leaves the client pointed where it already was. */ export declare function ensureRoute(upstream: string, env?: NodeJS.ProcessEnv, project?: string | null): Promise; //# sourceMappingURL=supervisor.d.ts.map