@codeproxy/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/proxy.ts"],"names":["createResponsesFetch","http","resolve","Readable","mkdirSync","join","writeFileSync"],"mappings":"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","file":"index.cjs","sourcesContent":["// ==============================================================================\n// Helpers\n// ==============================================================================\n\n/**\n * Local HTTP proxy that exposes the Responses API and forwards translated\n * requests to the configured upstream API format.\n */\n\nimport http, { type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\n// ==============================================================================\n// Helpers\n// ==============================================================================\nfunction fmtTime(date: Date): string {\n return date.toLocaleTimeString('en-US', { hour12: false });\n}\n\nfunction fmtDuration(ms: number): string {\n // ==============================================================================\n // Server\n // ==============================================================================\n\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n if (ms < 60000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.round((ms % 60000) / 1000);\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\nimport { createResponsesFetch, type CreateResponsesFetchOptions } from '@codeproxy/core';\n\nexport interface StartProxyOptions extends Omit<CreateResponsesFetchOptions, 'passthroughFetch'> {\n /** Host to bind to. Defaults to `127.0.0.1`. */\n host?: string;\n /** Port to listen on. Defaults to `8787`; pass `0` for a random free port. */\n port?: number;\n /** Enable permissive CORS (useful for local browser dev). Defaults to true. */\n cors?: boolean;\n /** Optional logger. Defaults to `console`. Pass `null` to silence. */\n logger?: Pick<Console, 'log' | 'warn' | 'error'> | null;\n /** Optional callback to receive cache statistics after each request completes. */\n onCacheStats?: (stats: {\n cachedTokens: number;\n cacheCreationTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n method?: string;\n url?: string;\n durationMs?: number;\n }) => void;\n}\n\nexport interface RunningProxy {\n host: string;\n port: number;\n url: string;\n server: Server;\n close: () => Promise<void>;\n}\n\nexport async function startProxy(options: StartProxyOptions): Promise<RunningProxy> {\n const host = options.host ?? '127.0.0.1';\n const port = options.port ?? 8787;\n const cors = options.cors ?? true;\n const logger = options.logger === null ? null : (options.logger ?? console);\n\n // Rolling average for request duration coloring (last 50 requests)\n const durationHistory: number[] = [];\n function updateRollingAverage(ms: number) {\n durationHistory.push(ms);\n if (durationHistory.length > 50) {\n durationHistory.shift();\n }\n return durationHistory.reduce((sum, val) => sum + val, 0) / durationHistory.length;\n }\n\n // Centralized status line for all active requests\n const activeRequests = new Map<string, { method: string; url: string; startTime: number }>();\n // ==============================================================================\n // Request Handler\n // ==============================================================================\n\n let statusTimerId: ReturnType<typeof setInterval> | null = null;\n\n function drawStatusLine() {\n if (activeRequests.size === 0) {\n return;\n }\n const parts = Array.from(activeRequests.entries()).map(([, req]) => {\n const elapsed = Date.now() - req.startTime;\n return `[${fmtDuration(elapsed)}]`;\n });\n process.stdout.write(`\\r\\x1b[K⏳ ${parts.join(', ')}`);\n }\n\n const requestTracker = {\n add(method: string, url: string): string {\n const id = `${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;\n activeRequests.set(id, { method, url, startTime: Date.now() });\n drawStatusLine();\n if (!statusTimerId) {\n statusTimerId = setInterval(drawStatusLine, 150);\n }\n return id;\n },\n remove(id: string) {\n activeRequests.delete(id);\n if (activeRequests.size === 0) {\n process.stdout.write('\\r\\x1b[K');\n if (statusTimerId) {\n clearInterval(statusTimerId);\n statusTimerId = null;\n }\n }\n },\n };\n\n const requestInfo = { method: '', url: '', startTime: 0, resultLog: '' };\n\n const upstreamCapture: {\n request?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n response?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n } = {};\n\n const baseFetch = options.fetch ?? globalThis.fetch;\n const capturingFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? input?.method ?? 'GET').toUpperCase();\n const reqHeaders = headersInitToObject(init?.headers);\n let reqBody: unknown = undefined;\n if (init?.body != null) {\n if (typeof init.body === 'string') {\n reqBody = tryParseJson(init.body);\n } else if (init.body instanceof ArrayBuffer) {\n reqBody = tryParseJson(new TextDecoder().decode(init.body));\n } else if (ArrayBuffer.isView(init.body)) {\n reqBody = tryParseJson(new TextDecoder().decode(init.body));\n } else {\n reqBody = String(init.body);\n }\n }\n upstreamCapture.request = { url, method, headers: reqHeaders, body: reqBody };\n\n const resp = await baseFetch(input, init);\n\n if (!resp.ok) {\n const clone = resp.clone();\n const text = await clone.text().catch(() => '');\n upstreamCapture.response = {\n status: resp.status,\n statusText: resp.statusText,\n headers: headersToObject(resp.headers),\n body: tryParseJson(text),\n };\n } else {\n upstreamCapture.response = undefined;\n }\n return resp;\n };\n\n const apiFetch = createResponsesFetch({\n upstreamFormat: options.upstreamFormat,\n baseUrl: options.baseUrl,\n apiVersion: options.apiVersion,\n model: options.model,\n defaultHeaders: options.defaultHeaders,\n timeoutMs: options.timeoutMs,\n dropImages: options.dropImages,\n fallbackUpstream: options.fallbackUpstream,\n fetch: capturingFetch,\n passthroughFetch: async () =>\n new Response(JSON.stringify({ error: { message: 'Not found' } }), {\n status: 404,\n headers: { 'content-type': 'application/json' },\n }),\n onCacheStats: (stats) => {\n const durationMs = requestInfo.startTime ? Date.now() - requestInfo.startTime : 0;\n const billedTokens = stats.inputTokens + stats.outputTokens - stats.cachedTokens;\n const parts = [\n `total=${stats.totalTokens}`,\n `input=${stats.inputTokens}`,\n `output=${stats.outputTokens}`,\n `cached=${stats.cachedTokens}`,\n `billed=${billedTokens}`,\n ];\n if (stats.cacheCreationTokens > 0) {\n parts.push(`cache_creation=${stats.cacheCreationTokens}`);\n }\n const avg = updateRollingAverage(durationMs);\n const ratio = avg > 0 ? durationMs / avg : 1;\n const color = ratio < 0.8 ? '\\x1b[32m' : ratio < 1.5 ? '\\x1b[33m' : '\\x1b[31m';\n const reset = '\\x1b[0m';\n const logMsg = `[${fmtTime(new Date())}] -> 200 (${color}${fmtDuration(durationMs)}${reset} avg=${fmtDuration(Math.round(avg))}) [${parts.join(', ')}]`;\n requestInfo.resultLog =\n stats.cachedTokens < 1024 && billedTokens > 0 ? `⚠️ NO CACHE -- ${logMsg}` : logMsg;\n\n if (options.onCacheStats) {\n options.onCacheStats({\n ...stats,\n method: requestInfo.method || undefined,\n url: requestInfo.url || undefined,\n durationMs: durationMs || undefined,\n });\n }\n },\n });\n\n const server = http.createServer(async (req, res) => {\n const start = Date.now();\n requestInfo.method = req.method ?? 'POST';\n requestInfo.url = req.url ?? '/v1/responses';\n requestInfo.startTime = start;\n\n const timeoutMs = options.timeoutMs;\n let timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n if (timeoutMs && timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n logger?.warn(`[timeout] request exceeded ${timeoutMs}ms, aborting`);\n res.destroy();\n req.destroy();\n }, timeoutMs);\n }\n\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n await handleRequest(req, res, {\n apiFetch,\n cors,\n logger,\n method: req.method ?? 'POST',\n url: req.url ?? '/',\n upstreamCapture,\n requestInfo,\n requestTracker,\n });\n } catch (err) {\n logger?.error('[proxy-error]', err);\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n if (!res.headersSent) {\n res.writeHead(500, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ error: { message: 'Internal server error' } }));\n }\n } catch {\n // ignore\n }\n } finally {\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n }\n }\n });\n\n return new Promise((resolve, reject) => {\n server.listen(port, host, () => {\n const actualPort = (() => {\n // eslint-disable-next-line no-restricted-syntax -- net.Server.address() returns string | AddressInfo | null\n const addr = server.address() as { port: number } | null;\n return addr.port;\n })();\n const url = `http://${host}:${actualPort}`;\n logger?.log(`Proxy listening on ${url}`);\n logger?.log(`Upstream format: ${options.upstreamFormat}`);\n logger?.log(`Upstream URL: ${options.baseUrl}`);\n resolve({\n host,\n port: actualPort,\n url,\n server,\n close: () =>\n new Promise((res) => {\n server.close((err) => {\n if (err) {\n logger?.warn('Error closing server:', err);\n }\n res();\n });\n }),\n });\n });\n server.once('error', reject);\n });\n}\n\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n opts: {\n apiFetch: typeof fetch;\n cors: boolean;\n logger: Pick<Console, 'log' | 'warn' | 'error'> | null;\n method: string;\n url: string;\n upstreamCapture: {\n request?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n response?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n };\n requestInfo: { resultLog: string };\n requestTracker: { add: (method: string, url: string) => string; remove: (id: string) => void };\n },\n): Promise<void> {\n if (opts.cors) {\n setCorsHeaders(res);\n }\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const method = req.method ?? 'GET';\n const urlPath = req.url ?? '/';\n const headers = flattenIncomingHeaders(req.headers);\n\n let body: Buffer | undefined;\n if (method !== 'GET' && method !== 'HEAD') {\n body = await readIncomingBody(req);\n }\n\n if (!/^\\/v1\\/responses\\/?(?:\\?|$)/.test(urlPath)) {\n res.writeHead(404, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ error: { message: `Not found: ${method} ${urlPath}` } }));\n return;\n }\n\n const requestBodyText = body ? body.toString('utf8') : undefined;\n\n const requestStart = Date.now();\n const requestId = opts.requestTracker.add(method, urlPath);\n\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n const response = await opts.apiFetch(`http://local${urlPath}`, {\n method,\n headers,\n body: body ? new Uint8Array(body) : undefined,\n });\n\n // Consume response body so onCacheStats fires (for streaming responses)\n const responseBodyText = response.body ? await response.clone().text() : '';\n\n // Remove from active requests and write final result\n opts.requestTracker.remove(requestId);\n if (opts.logger) {\n if (response.status >= 400) {\n process.stdout.write(\n `\\r\\x1b[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})\\n`,\n );\n } else if (opts.requestInfo.resultLog) {\n process.stdout.write(`\\r\\x1b[K${opts.requestInfo.resultLog}\\n`);\n } else {\n process.stdout.write(\n `\\r\\x1b[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})\\n`,\n );\n }\n }\n if (response.status >= 400) {\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n const filePath = saveErrorDump({\n method: opts.method,\n url: opts.url,\n clientRequest: {\n headers,\n body: tryParseJson(requestBodyText ?? ''),\n },\n upstreamRequest: opts.upstreamCapture.request,\n upstreamResponse: opts.upstreamCapture.response,\n proxyResponse: {\n status: response.status,\n headers: headersToObject(response.headers),\n body: tryParseJson(responseBodyText),\n },\n });\n opts.logger?.error(`[proxy-failure] full exchange saved to ${filePath}`);\n } catch (dumpErr) {\n opts.logger?.error('[proxy-failure] failed to persist error dump', dumpErr);\n }\n }\n\n const outHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n outHeaders[key] = value;\n });\n if (opts.cors) {\n Object.assign(outHeaders, corsHeaders());\n }\n\n res.writeHead(response.status, outHeaders);\n\n if (!response.body) {\n res.end();\n return;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- fetch response.body is not typed as node stream\n const typedBody = response.body! as unknown as import('stream/web').ReadableStream<Uint8Array>;\n const nodeStream = Readable.fromWeb(typedBody);\n nodeStream.pipe(res);\n await new Promise<void>((resolve, reject) => {\n nodeStream.once('end', resolve);\n nodeStream.once('error', reject);\n res.once('close', resolve);\n });\n } catch (err) {\n opts.requestTracker.remove(requestId);\n throw err;\n }\n}\n\nfunction readIncomingBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk) => {\n const buf: Buffer = chunk;\n chunks.push(buf);\n });\n req.on('end', () => resolve(Buffer.concat(chunks)));\n req.on('error', reject);\n });\n}\n\nfunction flattenIncomingHeaders(headers: IncomingMessage['headers']): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) {\n continue;\n }\n out[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value);\n }\n return out;\n}\n\nfunction headersToObject(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n out[key] = value;\n });\n return out;\n}\n\nfunction setCorsHeaders(res: ServerResponse): void {\n const headers = corsHeaders();\n for (const [key, value] of Object.entries(headers)) {\n res.setHeader(key, value);\n }\n}\n\nfunction corsHeaders(): Record<string, string> {\n return {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET,POST,OPTIONS',\n 'access-control-allow-headers':\n 'authorization,content-type,x-api-key,anthropic-version,anthropic-beta,anthropic-dangerous-direct-browser-access',\n 'access-control-expose-headers': 'content-type',\n };\n}\n\nfunction tryParseJson(str: string | undefined | null): unknown {\n if (!str) {\n return str ?? null;\n }\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n return JSON.parse(str);\n } catch {\n return str;\n }\n}\n\nfunction headersInitToObject(headersInit: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n if (!headersInit) {\n return out;\n }\n if (typeof Headers !== 'undefined' && headersInit instanceof Headers) {\n headersInit.forEach((value, key) => {\n out[key.toLowerCase()] = value;\n });\n return out;\n }\n if (Array.isArray(headersInit)) {\n for (const [key, value] of headersInit) {\n out[String(key).toLowerCase()] = String(value);\n }\n return out;\n }\n for (const [key, value] of Object.entries(headersInit)) {\n out[key.toLowerCase()] = String(value);\n }\n return out;\n}\n\nfunction saveErrorDump(dump: {\n method: string;\n url: string;\n clientRequest: { headers: Record<string, string>; body: unknown };\n upstreamRequest?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n upstreamResponse?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n proxyResponse: { status: number; headers: Record<string, string>; body: unknown };\n}): string {\n const dir = resolve(process.cwd(), 'logs');\n mkdirSync(dir, { recursive: true });\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const status = dump.upstreamResponse?.status ?? dump.proxyResponse.status;\n const filename = `proxy-error-${ts}-${status}.json`;\n const filePath = join(dir, filename);\n const payload = {\n timestamp: new Date().toISOString(),\n ...dump,\n };\n redactAuth(payload.clientRequest?.headers);\n redactAuth(payload.upstreamRequest?.headers);\n writeFileSync(filePath, JSON.stringify(payload, null, 2));\n return filePath;\n}\n\nfunction redactAuth(headers: Record<string, string> | undefined): void {\n if (!headers) {\n return;\n }\n for (const key of Object.keys(headers)) {\n const lowerKey = key.toLowerCase();\n if (\n lowerKey === 'authorization' ||\n lowerKey === 'x-api-key' ||\n lowerKey === 'api-key' ||\n lowerKey === 'cookie'\n ) {\n headers[key] = '[REDACTED]';\n }\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { CreateResponsesFetchOptions } from '@codeproxy/core';
2
+ export { AnthropicContentBlock, AnthropicMessage, AnthropicRequest, AnthropicResponse, AnthropicStreamEvent, AnthropicTool, AnthropicToolChoice, AnthropicUsage, CacheStats, CreateResponsesFetchOptions, OpenAiChatMessage, OpenAiChatRequest, OpenAiChatResponse, OpenAiChatStreamChunk, OpenAiChatTool, OpenAiChatToolCall, ResponsesContentPart, ResponsesInputItem, ResponsesOutputFunctionCall, ResponsesOutputItem, ResponsesOutputMessage, ResponsesOutputReasoning, ResponsesRequest, ResponsesResponse, ResponsesStreamEvent, ResponsesTool, ResponsesToolChoice, ResponsesUsage, SseMessage, UpstreamFormat, createResponsesFetch, encodeSseEvent, parseSseStream } from '@codeproxy/core';
3
+ import { Server } from 'node:http';
4
+
5
+ /**
6
+ * Local HTTP proxy that exposes the Responses API and forwards translated
7
+ * requests to the configured upstream API format.
8
+ */
9
+
10
+ interface StartProxyOptions extends Omit<CreateResponsesFetchOptions, 'passthroughFetch'> {
11
+ /** Host to bind to. Defaults to `127.0.0.1`. */
12
+ host?: string;
13
+ /** Port to listen on. Defaults to `8787`; pass `0` for a random free port. */
14
+ port?: number;
15
+ /** Enable permissive CORS (useful for local browser dev). Defaults to true. */
16
+ cors?: boolean;
17
+ /** Optional logger. Defaults to `console`. Pass `null` to silence. */
18
+ logger?: Pick<Console, 'log' | 'warn' | 'error'> | null;
19
+ /** Optional callback to receive cache statistics after each request completes. */
20
+ onCacheStats?: (stats: {
21
+ cachedTokens: number;
22
+ cacheCreationTokens: number;
23
+ inputTokens: number;
24
+ outputTokens: number;
25
+ totalTokens: number;
26
+ method?: string;
27
+ url?: string;
28
+ durationMs?: number;
29
+ }) => void;
30
+ }
31
+ interface RunningProxy {
32
+ host: string;
33
+ port: number;
34
+ url: string;
35
+ server: Server;
36
+ close: () => Promise<void>;
37
+ }
38
+ declare function startProxy(options: StartProxyOptions): Promise<RunningProxy>;
39
+
40
+ export { type RunningProxy, type StartProxyOptions, startProxy };
@@ -0,0 +1,40 @@
1
+ import { CreateResponsesFetchOptions } from '@codeproxy/core';
2
+ export { AnthropicContentBlock, AnthropicMessage, AnthropicRequest, AnthropicResponse, AnthropicStreamEvent, AnthropicTool, AnthropicToolChoice, AnthropicUsage, CacheStats, CreateResponsesFetchOptions, OpenAiChatMessage, OpenAiChatRequest, OpenAiChatResponse, OpenAiChatStreamChunk, OpenAiChatTool, OpenAiChatToolCall, ResponsesContentPart, ResponsesInputItem, ResponsesOutputFunctionCall, ResponsesOutputItem, ResponsesOutputMessage, ResponsesOutputReasoning, ResponsesRequest, ResponsesResponse, ResponsesStreamEvent, ResponsesTool, ResponsesToolChoice, ResponsesUsage, SseMessage, UpstreamFormat, createResponsesFetch, encodeSseEvent, parseSseStream } from '@codeproxy/core';
3
+ import { Server } from 'node:http';
4
+
5
+ /**
6
+ * Local HTTP proxy that exposes the Responses API and forwards translated
7
+ * requests to the configured upstream API format.
8
+ */
9
+
10
+ interface StartProxyOptions extends Omit<CreateResponsesFetchOptions, 'passthroughFetch'> {
11
+ /** Host to bind to. Defaults to `127.0.0.1`. */
12
+ host?: string;
13
+ /** Port to listen on. Defaults to `8787`; pass `0` for a random free port. */
14
+ port?: number;
15
+ /** Enable permissive CORS (useful for local browser dev). Defaults to true. */
16
+ cors?: boolean;
17
+ /** Optional logger. Defaults to `console`. Pass `null` to silence. */
18
+ logger?: Pick<Console, 'log' | 'warn' | 'error'> | null;
19
+ /** Optional callback to receive cache statistics after each request completes. */
20
+ onCacheStats?: (stats: {
21
+ cachedTokens: number;
22
+ cacheCreationTokens: number;
23
+ inputTokens: number;
24
+ outputTokens: number;
25
+ totalTokens: number;
26
+ method?: string;
27
+ url?: string;
28
+ durationMs?: number;
29
+ }) => void;
30
+ }
31
+ interface RunningProxy {
32
+ host: string;
33
+ port: number;
34
+ url: string;
35
+ server: Server;
36
+ close: () => Promise<void>;
37
+ }
38
+ declare function startProxy(options: StartProxyOptions): Promise<RunningProxy>;
39
+
40
+ export { type RunningProxy, type StartProxyOptions, startProxy };
package/dist/index.js ADDED
@@ -0,0 +1,414 @@
1
+ import { createResponsesFetch } from '@codeproxy/core';
2
+ export { createResponsesFetch, encodeSseEvent, parseSseStream } from '@codeproxy/core';
3
+ import http from 'http';
4
+ import { Readable } from 'stream';
5
+ import { mkdirSync, writeFileSync } from 'fs';
6
+ import { resolve, join } from 'path';
7
+
8
+ // src/index.ts
9
+ function fmtTime(date) {
10
+ return date.toLocaleTimeString("en-US", { hour12: false });
11
+ }
12
+ function fmtDuration(ms) {
13
+ if (ms < 1e3) {
14
+ return `${Math.round(ms)}ms`;
15
+ }
16
+ if (ms < 6e4) {
17
+ return `${(ms / 1e3).toFixed(1)}s`;
18
+ }
19
+ const minutes = Math.floor(ms / 6e4);
20
+ const seconds = Math.round(ms % 6e4 / 1e3);
21
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
22
+ }
23
+ async function startProxy(options) {
24
+ const host = options.host ?? "127.0.0.1";
25
+ const port = options.port ?? 8787;
26
+ const cors = options.cors ?? true;
27
+ const logger = options.logger === null ? null : options.logger ?? console;
28
+ const durationHistory = [];
29
+ function updateRollingAverage(ms) {
30
+ durationHistory.push(ms);
31
+ if (durationHistory.length > 50) {
32
+ durationHistory.shift();
33
+ }
34
+ return durationHistory.reduce((sum, val) => sum + val, 0) / durationHistory.length;
35
+ }
36
+ const activeRequests = /* @__PURE__ */ new Map();
37
+ let statusTimerId = null;
38
+ function drawStatusLine() {
39
+ if (activeRequests.size === 0) {
40
+ return;
41
+ }
42
+ const parts = Array.from(activeRequests.entries()).map(([, req]) => {
43
+ const elapsed = Date.now() - req.startTime;
44
+ return `[${fmtDuration(elapsed)}]`;
45
+ });
46
+ process.stdout.write(`\r\x1B[K\u23F3 ${parts.join(", ")}`);
47
+ }
48
+ const requestTracker = {
49
+ add(method, url) {
50
+ const id = `${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
51
+ activeRequests.set(id, { method, url, startTime: Date.now() });
52
+ drawStatusLine();
53
+ if (!statusTimerId) {
54
+ statusTimerId = setInterval(drawStatusLine, 150);
55
+ }
56
+ return id;
57
+ },
58
+ remove(id) {
59
+ activeRequests.delete(id);
60
+ if (activeRequests.size === 0) {
61
+ process.stdout.write("\r\x1B[K");
62
+ if (statusTimerId) {
63
+ clearInterval(statusTimerId);
64
+ statusTimerId = null;
65
+ }
66
+ }
67
+ }
68
+ };
69
+ const requestInfo = { method: "", url: "", startTime: 0, resultLog: "" };
70
+ const upstreamCapture = {};
71
+ const baseFetch = options.fetch ?? globalThis.fetch;
72
+ const capturingFetch = async (input, init) => {
73
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
74
+ const method = (init?.method ?? input?.method ?? "GET").toUpperCase();
75
+ const reqHeaders = headersInitToObject(init?.headers);
76
+ let reqBody = void 0;
77
+ if (init?.body != null) {
78
+ if (typeof init.body === "string") {
79
+ reqBody = tryParseJson(init.body);
80
+ } else if (init.body instanceof ArrayBuffer) {
81
+ reqBody = tryParseJson(new TextDecoder().decode(init.body));
82
+ } else if (ArrayBuffer.isView(init.body)) {
83
+ reqBody = tryParseJson(new TextDecoder().decode(init.body));
84
+ } else {
85
+ reqBody = String(init.body);
86
+ }
87
+ }
88
+ upstreamCapture.request = { url, method, headers: reqHeaders, body: reqBody };
89
+ const resp = await baseFetch(input, init);
90
+ if (!resp.ok) {
91
+ const clone = resp.clone();
92
+ const text = await clone.text().catch(() => "");
93
+ upstreamCapture.response = {
94
+ status: resp.status,
95
+ statusText: resp.statusText,
96
+ headers: headersToObject(resp.headers),
97
+ body: tryParseJson(text)
98
+ };
99
+ } else {
100
+ upstreamCapture.response = void 0;
101
+ }
102
+ return resp;
103
+ };
104
+ const apiFetch = createResponsesFetch({
105
+ upstreamFormat: options.upstreamFormat,
106
+ baseUrl: options.baseUrl,
107
+ apiVersion: options.apiVersion,
108
+ model: options.model,
109
+ defaultHeaders: options.defaultHeaders,
110
+ timeoutMs: options.timeoutMs,
111
+ dropImages: options.dropImages,
112
+ fallbackUpstream: options.fallbackUpstream,
113
+ fetch: capturingFetch,
114
+ passthroughFetch: async () => new Response(JSON.stringify({ error: { message: "Not found" } }), {
115
+ status: 404,
116
+ headers: { "content-type": "application/json" }
117
+ }),
118
+ onCacheStats: (stats) => {
119
+ const durationMs = requestInfo.startTime ? Date.now() - requestInfo.startTime : 0;
120
+ const billedTokens = stats.inputTokens + stats.outputTokens - stats.cachedTokens;
121
+ const parts = [
122
+ `total=${stats.totalTokens}`,
123
+ `input=${stats.inputTokens}`,
124
+ `output=${stats.outputTokens}`,
125
+ `cached=${stats.cachedTokens}`,
126
+ `billed=${billedTokens}`
127
+ ];
128
+ if (stats.cacheCreationTokens > 0) {
129
+ parts.push(`cache_creation=${stats.cacheCreationTokens}`);
130
+ }
131
+ const avg = updateRollingAverage(durationMs);
132
+ const ratio = avg > 0 ? durationMs / avg : 1;
133
+ const color = ratio < 0.8 ? "\x1B[32m" : ratio < 1.5 ? "\x1B[33m" : "\x1B[31m";
134
+ const reset = "\x1B[0m";
135
+ const logMsg = `[${fmtTime(/* @__PURE__ */ new Date())}] -> 200 (${color}${fmtDuration(durationMs)}${reset} avg=${fmtDuration(Math.round(avg))}) [${parts.join(", ")}]`;
136
+ requestInfo.resultLog = stats.cachedTokens < 1024 && billedTokens > 0 ? `\u26A0\uFE0F NO CACHE -- ${logMsg}` : logMsg;
137
+ if (options.onCacheStats) {
138
+ options.onCacheStats({
139
+ ...stats,
140
+ method: requestInfo.method || void 0,
141
+ url: requestInfo.url || void 0,
142
+ durationMs: durationMs || void 0
143
+ });
144
+ }
145
+ }
146
+ });
147
+ const server = http.createServer(async (req, res) => {
148
+ const start = Date.now();
149
+ requestInfo.method = req.method ?? "POST";
150
+ requestInfo.url = req.url ?? "/v1/responses";
151
+ requestInfo.startTime = start;
152
+ const timeoutMs = options.timeoutMs;
153
+ let timeoutTimer;
154
+ if (timeoutMs && timeoutMs > 0) {
155
+ timeoutTimer = setTimeout(() => {
156
+ logger?.warn(`[timeout] request exceeded ${timeoutMs}ms, aborting`);
157
+ res.destroy();
158
+ req.destroy();
159
+ }, timeoutMs);
160
+ }
161
+ try {
162
+ await handleRequest(req, res, {
163
+ apiFetch,
164
+ cors,
165
+ logger,
166
+ method: req.method ?? "POST",
167
+ url: req.url ?? "/",
168
+ upstreamCapture,
169
+ requestInfo,
170
+ requestTracker
171
+ });
172
+ } catch (err) {
173
+ logger?.error("[proxy-error]", err);
174
+ try {
175
+ if (!res.headersSent) {
176
+ res.writeHead(500, { "content-type": "application/json" });
177
+ res.end(JSON.stringify({ error: { message: "Internal server error" } }));
178
+ }
179
+ } catch {
180
+ }
181
+ } finally {
182
+ if (timeoutTimer) {
183
+ clearTimeout(timeoutTimer);
184
+ }
185
+ }
186
+ });
187
+ return new Promise((resolve2, reject) => {
188
+ server.listen(port, host, () => {
189
+ const actualPort = (() => {
190
+ const addr = server.address();
191
+ return addr.port;
192
+ })();
193
+ const url = `http://${host}:${actualPort}`;
194
+ logger?.log(`Proxy listening on ${url}`);
195
+ logger?.log(`Upstream format: ${options.upstreamFormat}`);
196
+ logger?.log(`Upstream URL: ${options.baseUrl}`);
197
+ resolve2({
198
+ host,
199
+ port: actualPort,
200
+ url,
201
+ server,
202
+ close: () => new Promise((res) => {
203
+ server.close((err) => {
204
+ if (err) {
205
+ logger?.warn("Error closing server:", err);
206
+ }
207
+ res();
208
+ });
209
+ })
210
+ });
211
+ });
212
+ server.once("error", reject);
213
+ });
214
+ }
215
+ async function handleRequest(req, res, opts) {
216
+ if (opts.cors) {
217
+ setCorsHeaders(res);
218
+ }
219
+ if (req.method === "OPTIONS") {
220
+ res.writeHead(204);
221
+ res.end();
222
+ return;
223
+ }
224
+ const method = req.method ?? "GET";
225
+ const urlPath = req.url ?? "/";
226
+ const headers = flattenIncomingHeaders(req.headers);
227
+ let body;
228
+ if (method !== "GET" && method !== "HEAD") {
229
+ body = await readIncomingBody(req);
230
+ }
231
+ if (!/^\/v1\/responses\/?(?:\?|$)/.test(urlPath)) {
232
+ res.writeHead(404, { "content-type": "application/json" });
233
+ res.end(JSON.stringify({ error: { message: `Not found: ${method} ${urlPath}` } }));
234
+ return;
235
+ }
236
+ const requestBodyText = body ? body.toString("utf8") : void 0;
237
+ const requestStart = Date.now();
238
+ const requestId = opts.requestTracker.add(method, urlPath);
239
+ try {
240
+ const response = await opts.apiFetch(`http://local${urlPath}`, {
241
+ method,
242
+ headers,
243
+ body: body ? new Uint8Array(body) : void 0
244
+ });
245
+ const responseBodyText = response.body ? await response.clone().text() : "";
246
+ opts.requestTracker.remove(requestId);
247
+ if (opts.logger) {
248
+ if (response.status >= 400) {
249
+ process.stdout.write(
250
+ `\r\x1B[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})
251
+ `
252
+ );
253
+ } else if (opts.requestInfo.resultLog) {
254
+ process.stdout.write(`\r\x1B[K${opts.requestInfo.resultLog}
255
+ `);
256
+ } else {
257
+ process.stdout.write(
258
+ `\r\x1B[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})
259
+ `
260
+ );
261
+ }
262
+ }
263
+ if (response.status >= 400) {
264
+ try {
265
+ const filePath = saveErrorDump({
266
+ method: opts.method,
267
+ url: opts.url,
268
+ clientRequest: {
269
+ headers,
270
+ body: tryParseJson(requestBodyText ?? "")
271
+ },
272
+ upstreamRequest: opts.upstreamCapture.request,
273
+ upstreamResponse: opts.upstreamCapture.response,
274
+ proxyResponse: {
275
+ status: response.status,
276
+ headers: headersToObject(response.headers),
277
+ body: tryParseJson(responseBodyText)
278
+ }
279
+ });
280
+ opts.logger?.error(`[proxy-failure] full exchange saved to ${filePath}`);
281
+ } catch (dumpErr) {
282
+ opts.logger?.error("[proxy-failure] failed to persist error dump", dumpErr);
283
+ }
284
+ }
285
+ const outHeaders = {};
286
+ response.headers.forEach((value, key) => {
287
+ outHeaders[key] = value;
288
+ });
289
+ if (opts.cors) {
290
+ Object.assign(outHeaders, corsHeaders());
291
+ }
292
+ res.writeHead(response.status, outHeaders);
293
+ if (!response.body) {
294
+ res.end();
295
+ return;
296
+ }
297
+ const typedBody = response.body;
298
+ const nodeStream = Readable.fromWeb(typedBody);
299
+ nodeStream.pipe(res);
300
+ await new Promise((resolve2, reject) => {
301
+ nodeStream.once("end", resolve2);
302
+ nodeStream.once("error", reject);
303
+ res.once("close", resolve2);
304
+ });
305
+ } catch (err) {
306
+ opts.requestTracker.remove(requestId);
307
+ throw err;
308
+ }
309
+ }
310
+ function readIncomingBody(req) {
311
+ return new Promise((resolve2, reject) => {
312
+ const chunks = [];
313
+ req.on("data", (chunk) => {
314
+ const buf = chunk;
315
+ chunks.push(buf);
316
+ });
317
+ req.on("end", () => resolve2(Buffer.concat(chunks)));
318
+ req.on("error", reject);
319
+ });
320
+ }
321
+ function flattenIncomingHeaders(headers) {
322
+ const out = {};
323
+ for (const [key, value] of Object.entries(headers)) {
324
+ if (value == null) {
325
+ continue;
326
+ }
327
+ out[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : String(value);
328
+ }
329
+ return out;
330
+ }
331
+ function headersToObject(headers) {
332
+ const out = {};
333
+ headers.forEach((value, key) => {
334
+ out[key] = value;
335
+ });
336
+ return out;
337
+ }
338
+ function setCorsHeaders(res) {
339
+ const headers = corsHeaders();
340
+ for (const [key, value] of Object.entries(headers)) {
341
+ res.setHeader(key, value);
342
+ }
343
+ }
344
+ function corsHeaders() {
345
+ return {
346
+ "access-control-allow-origin": "*",
347
+ "access-control-allow-methods": "GET,POST,OPTIONS",
348
+ "access-control-allow-headers": "authorization,content-type,x-api-key,anthropic-version,anthropic-beta,anthropic-dangerous-direct-browser-access",
349
+ "access-control-expose-headers": "content-type"
350
+ };
351
+ }
352
+ function tryParseJson(str) {
353
+ if (!str) {
354
+ return str ?? null;
355
+ }
356
+ try {
357
+ return JSON.parse(str);
358
+ } catch {
359
+ return str;
360
+ }
361
+ }
362
+ function headersInitToObject(headersInit) {
363
+ const out = {};
364
+ if (!headersInit) {
365
+ return out;
366
+ }
367
+ if (typeof Headers !== "undefined" && headersInit instanceof Headers) {
368
+ headersInit.forEach((value, key) => {
369
+ out[key.toLowerCase()] = value;
370
+ });
371
+ return out;
372
+ }
373
+ if (Array.isArray(headersInit)) {
374
+ for (const [key, value] of headersInit) {
375
+ out[String(key).toLowerCase()] = String(value);
376
+ }
377
+ return out;
378
+ }
379
+ for (const [key, value] of Object.entries(headersInit)) {
380
+ out[key.toLowerCase()] = String(value);
381
+ }
382
+ return out;
383
+ }
384
+ function saveErrorDump(dump) {
385
+ const dir = resolve(process.cwd(), "logs");
386
+ mkdirSync(dir, { recursive: true });
387
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
388
+ const status = dump.upstreamResponse?.status ?? dump.proxyResponse.status;
389
+ const filename = `proxy-error-${ts}-${status}.json`;
390
+ const filePath = join(dir, filename);
391
+ const payload = {
392
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
393
+ ...dump
394
+ };
395
+ redactAuth(payload.clientRequest?.headers);
396
+ redactAuth(payload.upstreamRequest?.headers);
397
+ writeFileSync(filePath, JSON.stringify(payload, null, 2));
398
+ return filePath;
399
+ }
400
+ function redactAuth(headers) {
401
+ if (!headers) {
402
+ return;
403
+ }
404
+ for (const key of Object.keys(headers)) {
405
+ const lowerKey = key.toLowerCase();
406
+ if (lowerKey === "authorization" || lowerKey === "x-api-key" || lowerKey === "api-key" || lowerKey === "cookie") {
407
+ headers[key] = "[REDACTED]";
408
+ }
409
+ }
410
+ }
411
+
412
+ export { startProxy };
413
+ //# sourceMappingURL=index.js.map
414
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/proxy.ts"],"names":["resolve"],"mappings":"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","file":"index.js","sourcesContent":["// ==============================================================================\n// Helpers\n// ==============================================================================\n\n/**\n * Local HTTP proxy that exposes the Responses API and forwards translated\n * requests to the configured upstream API format.\n */\n\nimport http, { type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport { Readable } from 'node:stream';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\n// ==============================================================================\n// Helpers\n// ==============================================================================\nfunction fmtTime(date: Date): string {\n return date.toLocaleTimeString('en-US', { hour12: false });\n}\n\nfunction fmtDuration(ms: number): string {\n // ==============================================================================\n // Server\n // ==============================================================================\n\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n if (ms < 60000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.round((ms % 60000) / 1000);\n return `${minutes}:${String(seconds).padStart(2, '0')}`;\n}\nimport { createResponsesFetch, type CreateResponsesFetchOptions } from '@codeproxy/core';\n\nexport interface StartProxyOptions extends Omit<CreateResponsesFetchOptions, 'passthroughFetch'> {\n /** Host to bind to. Defaults to `127.0.0.1`. */\n host?: string;\n /** Port to listen on. Defaults to `8787`; pass `0` for a random free port. */\n port?: number;\n /** Enable permissive CORS (useful for local browser dev). Defaults to true. */\n cors?: boolean;\n /** Optional logger. Defaults to `console`. Pass `null` to silence. */\n logger?: Pick<Console, 'log' | 'warn' | 'error'> | null;\n /** Optional callback to receive cache statistics after each request completes. */\n onCacheStats?: (stats: {\n cachedTokens: number;\n cacheCreationTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalTokens: number;\n method?: string;\n url?: string;\n durationMs?: number;\n }) => void;\n}\n\nexport interface RunningProxy {\n host: string;\n port: number;\n url: string;\n server: Server;\n close: () => Promise<void>;\n}\n\nexport async function startProxy(options: StartProxyOptions): Promise<RunningProxy> {\n const host = options.host ?? '127.0.0.1';\n const port = options.port ?? 8787;\n const cors = options.cors ?? true;\n const logger = options.logger === null ? null : (options.logger ?? console);\n\n // Rolling average for request duration coloring (last 50 requests)\n const durationHistory: number[] = [];\n function updateRollingAverage(ms: number) {\n durationHistory.push(ms);\n if (durationHistory.length > 50) {\n durationHistory.shift();\n }\n return durationHistory.reduce((sum, val) => sum + val, 0) / durationHistory.length;\n }\n\n // Centralized status line for all active requests\n const activeRequests = new Map<string, { method: string; url: string; startTime: number }>();\n // ==============================================================================\n // Request Handler\n // ==============================================================================\n\n let statusTimerId: ReturnType<typeof setInterval> | null = null;\n\n function drawStatusLine() {\n if (activeRequests.size === 0) {\n return;\n }\n const parts = Array.from(activeRequests.entries()).map(([, req]) => {\n const elapsed = Date.now() - req.startTime;\n return `[${fmtDuration(elapsed)}]`;\n });\n process.stdout.write(`\\r\\x1b[K⏳ ${parts.join(', ')}`);\n }\n\n const requestTracker = {\n add(method: string, url: string): string {\n const id = `${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;\n activeRequests.set(id, { method, url, startTime: Date.now() });\n drawStatusLine();\n if (!statusTimerId) {\n statusTimerId = setInterval(drawStatusLine, 150);\n }\n return id;\n },\n remove(id: string) {\n activeRequests.delete(id);\n if (activeRequests.size === 0) {\n process.stdout.write('\\r\\x1b[K');\n if (statusTimerId) {\n clearInterval(statusTimerId);\n statusTimerId = null;\n }\n }\n },\n };\n\n const requestInfo = { method: '', url: '', startTime: 0, resultLog: '' };\n\n const upstreamCapture: {\n request?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n response?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n } = {};\n\n const baseFetch = options.fetch ?? globalThis.fetch;\n const capturingFetch: typeof fetch = async (input, init) => {\n const url =\n typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? input?.method ?? 'GET').toUpperCase();\n const reqHeaders = headersInitToObject(init?.headers);\n let reqBody: unknown = undefined;\n if (init?.body != null) {\n if (typeof init.body === 'string') {\n reqBody = tryParseJson(init.body);\n } else if (init.body instanceof ArrayBuffer) {\n reqBody = tryParseJson(new TextDecoder().decode(init.body));\n } else if (ArrayBuffer.isView(init.body)) {\n reqBody = tryParseJson(new TextDecoder().decode(init.body));\n } else {\n reqBody = String(init.body);\n }\n }\n upstreamCapture.request = { url, method, headers: reqHeaders, body: reqBody };\n\n const resp = await baseFetch(input, init);\n\n if (!resp.ok) {\n const clone = resp.clone();\n const text = await clone.text().catch(() => '');\n upstreamCapture.response = {\n status: resp.status,\n statusText: resp.statusText,\n headers: headersToObject(resp.headers),\n body: tryParseJson(text),\n };\n } else {\n upstreamCapture.response = undefined;\n }\n return resp;\n };\n\n const apiFetch = createResponsesFetch({\n upstreamFormat: options.upstreamFormat,\n baseUrl: options.baseUrl,\n apiVersion: options.apiVersion,\n model: options.model,\n defaultHeaders: options.defaultHeaders,\n timeoutMs: options.timeoutMs,\n dropImages: options.dropImages,\n fallbackUpstream: options.fallbackUpstream,\n fetch: capturingFetch,\n passthroughFetch: async () =>\n new Response(JSON.stringify({ error: { message: 'Not found' } }), {\n status: 404,\n headers: { 'content-type': 'application/json' },\n }),\n onCacheStats: (stats) => {\n const durationMs = requestInfo.startTime ? Date.now() - requestInfo.startTime : 0;\n const billedTokens = stats.inputTokens + stats.outputTokens - stats.cachedTokens;\n const parts = [\n `total=${stats.totalTokens}`,\n `input=${stats.inputTokens}`,\n `output=${stats.outputTokens}`,\n `cached=${stats.cachedTokens}`,\n `billed=${billedTokens}`,\n ];\n if (stats.cacheCreationTokens > 0) {\n parts.push(`cache_creation=${stats.cacheCreationTokens}`);\n }\n const avg = updateRollingAverage(durationMs);\n const ratio = avg > 0 ? durationMs / avg : 1;\n const color = ratio < 0.8 ? '\\x1b[32m' : ratio < 1.5 ? '\\x1b[33m' : '\\x1b[31m';\n const reset = '\\x1b[0m';\n const logMsg = `[${fmtTime(new Date())}] -> 200 (${color}${fmtDuration(durationMs)}${reset} avg=${fmtDuration(Math.round(avg))}) [${parts.join(', ')}]`;\n requestInfo.resultLog =\n stats.cachedTokens < 1024 && billedTokens > 0 ? `⚠️ NO CACHE -- ${logMsg}` : logMsg;\n\n if (options.onCacheStats) {\n options.onCacheStats({\n ...stats,\n method: requestInfo.method || undefined,\n url: requestInfo.url || undefined,\n durationMs: durationMs || undefined,\n });\n }\n },\n });\n\n const server = http.createServer(async (req, res) => {\n const start = Date.now();\n requestInfo.method = req.method ?? 'POST';\n requestInfo.url = req.url ?? '/v1/responses';\n requestInfo.startTime = start;\n\n const timeoutMs = options.timeoutMs;\n let timeoutTimer: ReturnType<typeof setTimeout> | undefined;\n if (timeoutMs && timeoutMs > 0) {\n timeoutTimer = setTimeout(() => {\n logger?.warn(`[timeout] request exceeded ${timeoutMs}ms, aborting`);\n res.destroy();\n req.destroy();\n }, timeoutMs);\n }\n\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n await handleRequest(req, res, {\n apiFetch,\n cors,\n logger,\n method: req.method ?? 'POST',\n url: req.url ?? '/',\n upstreamCapture,\n requestInfo,\n requestTracker,\n });\n } catch (err) {\n logger?.error('[proxy-error]', err);\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n if (!res.headersSent) {\n res.writeHead(500, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ error: { message: 'Internal server error' } }));\n }\n } catch {\n // ignore\n }\n } finally {\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n }\n }\n });\n\n return new Promise((resolve, reject) => {\n server.listen(port, host, () => {\n const actualPort = (() => {\n // eslint-disable-next-line no-restricted-syntax -- net.Server.address() returns string | AddressInfo | null\n const addr = server.address() as { port: number } | null;\n return addr.port;\n })();\n const url = `http://${host}:${actualPort}`;\n logger?.log(`Proxy listening on ${url}`);\n logger?.log(`Upstream format: ${options.upstreamFormat}`);\n logger?.log(`Upstream URL: ${options.baseUrl}`);\n resolve({\n host,\n port: actualPort,\n url,\n server,\n close: () =>\n new Promise((res) => {\n server.close((err) => {\n if (err) {\n logger?.warn('Error closing server:', err);\n }\n res();\n });\n }),\n });\n });\n server.once('error', reject);\n });\n}\n\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n opts: {\n apiFetch: typeof fetch;\n cors: boolean;\n logger: Pick<Console, 'log' | 'warn' | 'error'> | null;\n method: string;\n url: string;\n upstreamCapture: {\n request?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n response?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n };\n requestInfo: { resultLog: string };\n requestTracker: { add: (method: string, url: string) => string; remove: (id: string) => void };\n },\n): Promise<void> {\n if (opts.cors) {\n setCorsHeaders(res);\n }\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const method = req.method ?? 'GET';\n const urlPath = req.url ?? '/';\n const headers = flattenIncomingHeaders(req.headers);\n\n let body: Buffer | undefined;\n if (method !== 'GET' && method !== 'HEAD') {\n body = await readIncomingBody(req);\n }\n\n if (!/^\\/v1\\/responses\\/?(?:\\?|$)/.test(urlPath)) {\n res.writeHead(404, { 'content-type': 'application/json' });\n res.end(JSON.stringify({ error: { message: `Not found: ${method} ${urlPath}` } }));\n return;\n }\n\n const requestBodyText = body ? body.toString('utf8') : undefined;\n\n const requestStart = Date.now();\n const requestId = opts.requestTracker.add(method, urlPath);\n\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n const response = await opts.apiFetch(`http://local${urlPath}`, {\n method,\n headers,\n body: body ? new Uint8Array(body) : undefined,\n });\n\n // Consume response body so onCacheStats fires (for streaming responses)\n const responseBodyText = response.body ? await response.clone().text() : '';\n\n // Remove from active requests and write final result\n opts.requestTracker.remove(requestId);\n if (opts.logger) {\n if (response.status >= 400) {\n process.stdout.write(\n `\\r\\x1b[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})\\n`,\n );\n } else if (opts.requestInfo.resultLog) {\n process.stdout.write(`\\r\\x1b[K${opts.requestInfo.resultLog}\\n`);\n } else {\n process.stdout.write(\n `\\r\\x1b[K<-- ${response.status} (${fmtDuration(Date.now() - requestStart)})\\n`,\n );\n }\n }\n if (response.status >= 400) {\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n const filePath = saveErrorDump({\n method: opts.method,\n url: opts.url,\n clientRequest: {\n headers,\n body: tryParseJson(requestBodyText ?? ''),\n },\n upstreamRequest: opts.upstreamCapture.request,\n upstreamResponse: opts.upstreamCapture.response,\n proxyResponse: {\n status: response.status,\n headers: headersToObject(response.headers),\n body: tryParseJson(responseBodyText),\n },\n });\n opts.logger?.error(`[proxy-failure] full exchange saved to ${filePath}`);\n } catch (dumpErr) {\n opts.logger?.error('[proxy-failure] failed to persist error dump', dumpErr);\n }\n }\n\n const outHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n outHeaders[key] = value;\n });\n if (opts.cors) {\n Object.assign(outHeaders, corsHeaders());\n }\n\n res.writeHead(response.status, outHeaders);\n\n if (!response.body) {\n res.end();\n return;\n }\n\n // eslint-disable-next-line no-restricted-syntax -- fetch response.body is not typed as node stream\n const typedBody = response.body! as unknown as import('stream/web').ReadableStream<Uint8Array>;\n const nodeStream = Readable.fromWeb(typedBody);\n nodeStream.pipe(res);\n await new Promise<void>((resolve, reject) => {\n nodeStream.once('end', resolve);\n nodeStream.once('error', reject);\n res.once('close', resolve);\n });\n } catch (err) {\n opts.requestTracker.remove(requestId);\n throw err;\n }\n}\n\nfunction readIncomingBody(req: IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk) => {\n const buf: Buffer = chunk;\n chunks.push(buf);\n });\n req.on('end', () => resolve(Buffer.concat(chunks)));\n req.on('error', reject);\n });\n}\n\nfunction flattenIncomingHeaders(headers: IncomingMessage['headers']): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (value == null) {\n continue;\n }\n out[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : String(value);\n }\n return out;\n}\n\nfunction headersToObject(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n headers.forEach((value, key) => {\n out[key] = value;\n });\n return out;\n}\n\nfunction setCorsHeaders(res: ServerResponse): void {\n const headers = corsHeaders();\n for (const [key, value] of Object.entries(headers)) {\n res.setHeader(key, value);\n }\n}\n\nfunction corsHeaders(): Record<string, string> {\n return {\n 'access-control-allow-origin': '*',\n 'access-control-allow-methods': 'GET,POST,OPTIONS',\n 'access-control-allow-headers':\n 'authorization,content-type,x-api-key,anthropic-version,anthropic-beta,anthropic-dangerous-direct-browser-access',\n 'access-control-expose-headers': 'content-type',\n };\n}\n\nfunction tryParseJson(str: string | undefined | null): unknown {\n if (!str) {\n return str ?? null;\n }\n // eslint-disable-next-line no-restricted-syntax -- try/catch needed for server-side HTTP error handling\n try {\n return JSON.parse(str);\n } catch {\n return str;\n }\n}\n\nfunction headersInitToObject(headersInit: HeadersInit | undefined): Record<string, string> {\n const out: Record<string, string> = {};\n if (!headersInit) {\n return out;\n }\n if (typeof Headers !== 'undefined' && headersInit instanceof Headers) {\n headersInit.forEach((value, key) => {\n out[key.toLowerCase()] = value;\n });\n return out;\n }\n if (Array.isArray(headersInit)) {\n for (const [key, value] of headersInit) {\n out[String(key).toLowerCase()] = String(value);\n }\n return out;\n }\n for (const [key, value] of Object.entries(headersInit)) {\n out[key.toLowerCase()] = String(value);\n }\n return out;\n}\n\nfunction saveErrorDump(dump: {\n method: string;\n url: string;\n clientRequest: { headers: Record<string, string>; body: unknown };\n upstreamRequest?: { url: string; method: string; headers: Record<string, string>; body: unknown };\n upstreamResponse?: {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: unknown;\n };\n proxyResponse: { status: number; headers: Record<string, string>; body: unknown };\n}): string {\n const dir = resolve(process.cwd(), 'logs');\n mkdirSync(dir, { recursive: true });\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const status = dump.upstreamResponse?.status ?? dump.proxyResponse.status;\n const filename = `proxy-error-${ts}-${status}.json`;\n const filePath = join(dir, filename);\n const payload = {\n timestamp: new Date().toISOString(),\n ...dump,\n };\n redactAuth(payload.clientRequest?.headers);\n redactAuth(payload.upstreamRequest?.headers);\n writeFileSync(filePath, JSON.stringify(payload, null, 2));\n return filePath;\n}\n\nfunction redactAuth(headers: Record<string, string> | undefined): void {\n if (!headers) {\n return;\n }\n for (const key of Object.keys(headers)) {\n const lowerKey = key.toLowerCase();\n if (\n lowerKey === 'authorization' ||\n lowerKey === 'x-api-key' ||\n lowerKey === 'api-key' ||\n lowerKey === 'cookie'\n ) {\n headers[key] = '[REDACTED]';\n }\n }\n}\n"]}