/** * UsageProxyServer — a loopback HTTP reverse proxy that sits between a * subprocess CLI and its configured LLM API base URL, purely to observe * token usage. It is NOT part of the request/response path functionally — * every request/response is relayed byte-for-byte unchanged; usage parsing * is a side-channel that never affects what the CLI sees. * * Why this exists: some subprocess-backed AgentRunners (CrushAgentRunner, * CopilotAgentRunner) drive CLIs whose non-interactive output doesn't * self-report token usage the way codex/kimi/qwen/grok's JSON event streams * do. Those CLIs DO support pointing at a custom OpenAI-compatible (or * Anthropic-compatible) base URL for BYOK/self-hosted use — this proxy uses * that existing knob rather than needing any CLI-side cooperation: the * runner starts a UsageProxyServer pointed at the real upstream, sets the * CLI's base-url env var to `proxy.url()`, and reads `proxy.totals()` after * each turn. * * Deliberately generic — not tied to crush/copilot specifically. Any future * subprocess runner whose CLI doesn't self-report usage can reuse this. */ export interface UsageTotals { inputTokens: number; outputTokens: number; } export type UsageApiStyle = 'openai' | 'anthropic'; export interface UsageProxyOptions { /** The real API base URL requests are relayed to, e.g. https://api.openai.com. */ upstreamBaseUrl: string; /** Which response-body shape to look for usage in. */ apiStyle: UsageApiStyle; } /** Parse a (possibly SSE) response body buffer for the usage it carries, * merging per-field across every JSON document / SSE chunk found (not * "last chunk wins" as a whole object — see mergeUsage). Handles both a * single JSON document and `data: {...}\n\n` SSE framing. Exported for unit * testing without spinning up a real server. */ export declare function parseUsageFromResponseBody(body: string, apiStyle: UsageApiStyle): UsageTotals | undefined; export declare class UsageProxyServer { private opts; private server; private boundPort; private totalsAcc; private readonly upstream; constructor(opts: UsageProxyOptions); start(): Promise; url(): string; totals(): UsageTotals; reset(): void; stop(): Promise; private handle; } //# sourceMappingURL=usage-proxy.d.ts.map