/** * 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'; import type { AccountingRecord } from './accounting.js'; /** 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. * * 16999 -- immediately below ROUTE_BASE, and below EPHEMERAL_FLOOR for the reason routePort * records. The previous default, 45710, was chosen because it "sits in the IANA dynamic range"; * that is the range the kernel allocates outbound source ports from, so it was the defect rather * than the justification. */ 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; } 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[]; unpublished?: string; } | null>; /** One listener's window on what it did, as the control endpoint reports it. */ export interface TransformationWindow { readonly port: number; readonly upstream?: string; readonly project?: string; readonly held: number; readonly dropped: number; readonly records: readonly AccountingRecord[]; } /** * What the running proxies recently did, or null when nothing is listening. * * NULL IS A REAL ANSWER HERE, not an error to be papered over: no supervisor * means no transformations, and the caller has something specific to say about * that -- which is why this returns the same null `supervisorHealth` does * rather than throwing or inventing an empty window. */ export declare function supervisorTransformations(env?: NodeJS.ProcessEnv, options?: { readonly last?: number; readonly port?: number; }): Promise; /** * 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; decommissioned: 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