@codeproxy/cli 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -19,7 +19,7 @@ Point your Responses-API client at `http://127.0.0.1:8787`:
19
19
  ```bash
20
20
  curl -N http://127.0.0.1:8787/v1/responses \
21
21
  -H 'content-type: application/json' \
22
- -H "authorization: Bearer $API_KEY" \
22
+ -H "authorization: Bearer \$API_KEY" \
23
23
  -d '{"model":"deepseek-v4-pro","input":"Hello!","stream":true}'
24
24
  ```
25
25
 
@@ -47,7 +47,7 @@ See [config.example.json](./config.example.json) for a full example.
47
47
 
48
48
  | Field | Type | Description |
49
49
  |---|---|---|
50
- | `format` | `"anthropic"` `|` `"openai-chat"` | Upstream API format. If omitted, inferred from `baseUrl` (path ending in `/messages` \u2192 `anthropic`, `/chat/completions` \u2192 `openai-chat`, otherwise falls back to `openai-chat`). The proper path suffix is appended automatically |
50
+ | `format` | `"anthropic"` `|` `"openai-chat"` | Upstream API format. If omitted, inferred from `baseUrl` (path ending in `/messages` `anthropic`, `/chat/completions` `openai-chat`, otherwise falls back to `openai-chat`). The proper path suffix is appended automatically |
51
51
  | `baseUrl` | `string` | **Required.** Upstream endpoint URL |
52
52
  | `apiKey` | `string` | Upstream API key. Sent as `Authorization: Bearer <key>` (Anthropic: rewritten to `x-api-key`) |
53
53
  | `model` | `string` | Override the `model` field in all incoming requests |
@@ -67,8 +67,7 @@ CLI flags > per-upstream fields > top-level fields > built-in defaults
67
67
 
68
68
  #### Example: auto-fallback for text-only models
69
69
 
70
- When `deepseek` has `dropImages: true` and the user sends an image, the proxy
71
- automatically routes to `deepseek-vision` (which supports vision):
70
+ When `deepseek` has `dropImages: true` and the user sends an image, the proxy automatically routes to `kimi-vision` (uses Kimi K2.6):
72
71
 
73
72
  ```json
74
73
  {
@@ -76,17 +75,95 @@ automatically routes to `deepseek-vision` (which supports vision):
76
75
  "upstreams": {
77
76
  "deepseek": {
78
77
  "baseUrl": "https://api.deepseek.com/v1",
78
+ "apiKey": "sk-...",
79
79
  "model": "deepseek-v4-pro",
80
80
  "dropImages": true,
81
- "fallback": "deepseek-vision"
81
+ "fallback": "kimi-vision"
82
82
  },
83
- "deepseek-vision": {
83
+ "kimi-vision": {
84
+ "baseUrl": "https://api.moonshot.cn/v1",
85
+ "apiKey": "sk-...",
86
+ "model": "kimi-k2.6",
87
+ "headers": { "x-llm-api-key": "sk-..." }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Codex Configuration
94
+
95
+ Codex `0.128.0+` requires custom providers to speak the Responses API. `@codeproxy/cli` bridges this gap for any Chat Completions or Anthropic Messages upstream.
96
+
97
+ ### Quick setup
98
+
99
+ 1. Start the proxy:
100
+
101
+ ```bash
102
+ npx @codeproxy/cli --upstream-format openai-chat \
103
+ --base-url https://api.deepseek.com/v1 \
104
+ --apikey sk-your-key
105
+ ```
106
+
107
+ 2. Add a custom provider in `~/.codex/config.toml`:
108
+
109
+ ```toml
110
+ [model_providers.deepseek]
111
+ name = "DeepSeek"
112
+ base_url = "http://127.0.0.1:8787/v1"
113
+ wire_api = "responses"
114
+
115
+ [profiles.deepseek-pro]
116
+ model = "deepseek-v4-pro"
117
+ model_provider = "deepseek"
118
+ ```
119
+
120
+ ### With reasoning effort
121
+
122
+ ```toml
123
+ [model_providers.deepseek]
124
+ name = "DeepSeek"
125
+ base_url = "http://127.0.0.1:8787/v1"
126
+ wire_api = "responses"
127
+
128
+ [profiles.deepseek-pro]
129
+ model = "deepseek-v4-pro"
130
+ model_provider = "deepseek"
131
+ model_reasoning_effort = "high"
132
+ ```
133
+
134
+ `@codeproxy/cli` automatically maps `reasoning.effort` to the upstream's native format (e.g., `reasoning_effort` for OpenAI Chat, `thinking` blocks for Anthropic).
135
+
136
+ ### Multiple upstreams via config file
137
+
138
+ ```json
139
+ {
140
+ "currentUpstream": "deepseek-chat",
141
+ "upstreams": {
142
+ "deepseek-chat": {
84
143
  "baseUrl": "https://api.deepseek.com/v1",
85
- "model": "deepseek-v4-vision"
144
+ "apiKey": "sk-...",
145
+ "model": "deepseek-v4-pro",
146
+ "dropImages": true,
147
+ "fallback": "kimi-vision"
148
+ },
149
+ "kimi-vision": {
150
+ "baseUrl": "https://api.moonshot.cn/v1",
151
+ "apiKey": "sk-...",
152
+ "model": "kimi-k2.6",
153
+ "headers": { "x-llm-api-key": "sk-..." }
154
+ },
155
+ "claude": {
156
+ "format": "anthropic",
157
+ "baseUrl": "https://api.anthropic.com/v1",
158
+ "apiKey": "sk-ant-...",
159
+ "model": "claude-sonnet-4-20250514"
86
160
  }
87
161
  }
88
162
  }
89
163
  ```
164
+
165
+ Switch upstreams by changing `currentUpstream` and restarting the proxy — no Codex config changes needed.
166
+
90
167
  ## Install
91
168
 
92
169
  ```bash
package/README.zh-CN.md CHANGED
@@ -19,7 +19,7 @@ npx @codeproxy/cli --upstream-format openai-chat \
19
19
  ```bash
20
20
  curl -N http://127.0.0.1:8787/v1/responses \
21
21
  -H 'content-type: application/json' \
22
- -H "authorization: Bearer $API_KEY" \
22
+ -H "authorization: Bearer \$API_KEY" \
23
23
  -d '{"model":"deepseek-v4-pro","input":"Hello!","stream":true}'
24
24
  ```
25
25
 
@@ -47,7 +47,7 @@ npx @codeproxy/cli --config config.json
47
47
 
48
48
  | 字段 | 类型 | 说明 |
49
49
  |---|---|---|
50
- | `format` | `"anthropic"` `|` `"openai-chat"` | 上游 API 格式。省略时从 `baseUrl` 推断(路径结尾是 `/messages` \u2192 `anthropic`,`/chat/completions` \u2192 `openai-chat`,否则回退到 `openai-chat`)。路径后缀会自动补全 |
50
+ | `format` | `"anthropic"` `|` `"openai-chat"` | 上游 API 格式。省略时从 `baseUrl` 推断(路径结尾是 `/messages` `anthropic`,`/chat/completions` `openai-chat`,否则回退到 `openai-chat`)。路径后缀会自动补全 |
51
51
  | `baseUrl` | `string` | **必需。** 上游端点 URL |
52
52
  | `apiKey` | `string` | 上游 API 密钥。作为 `Authorization: Bearer <key>` 发送(Anthropic 会转为 `x-api-key`) |
53
53
  | `model` | `string` | 覆盖所有传入请求中的 `model` 字段 |
@@ -75,17 +75,95 @@ CLI 标志 > 每个上游的字段 > 顶层字段 > 内置默认值
75
75
  "upstreams": {
76
76
  "deepseek": {
77
77
  "baseUrl": "https://api.deepseek.com/v1",
78
+ "apiKey": "sk-...",
78
79
  "model": "deepseek-v4-pro",
79
80
  "dropImages": true,
80
- "fallback": "deepseek-vision"
81
+ "fallback": "kimi-vision"
81
82
  },
82
- "deepseek-vision": {
83
+ "kimi-vision": {
84
+ "baseUrl": "https://api.moonshot.cn/v1",
85
+ "apiKey": "sk-...",
86
+ "model": "kimi-k2.6",
87
+ "headers": { "x-llm-api-key": "sk-..." }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Codex 配置
94
+
95
+ Codex `0.128.0+` 要求自定义 Provider 必须使用 Responses API。`@codeproxy/cli` 为任何 Chat Completions 或 Anthropic Messages 上游填补了这一差距。
96
+
97
+ ### 快速设置
98
+
99
+ 1. 启动代理:
100
+
101
+ ```bash
102
+ npx @codeproxy/cli --upstream-format openai-chat \
103
+ --base-url https://api.deepseek.com/v1 \
104
+ --apikey sk-your-key
105
+ ```
106
+
107
+ 2. 在 `~/.codex/config.toml` 中添加自定义 Provider:
108
+
109
+ ```toml
110
+ [model_providers.deepseek]
111
+ name = "DeepSeek"
112
+ base_url = "http://127.0.0.1:8787/v1"
113
+ wire_api = "responses"
114
+
115
+ [profiles.deepseek-pro]
116
+ model = "deepseek-v4-pro"
117
+ model_provider = "deepseek"
118
+ ```
119
+
120
+ ### 配置推理力度
121
+
122
+ ```toml
123
+ [model_providers.deepseek]
124
+ name = "DeepSeek"
125
+ base_url = "http://127.0.0.1:8787/v1"
126
+ wire_api = "responses"
127
+
128
+ [profiles.deepseek-pro]
129
+ model = "deepseek-v4-pro"
130
+ model_provider = "deepseek"
131
+ model_reasoning_effort = "high"
132
+ ```
133
+
134
+ `@codeproxy/cli` 会自动将 `reasoning.effort` 映射为上游原生的推理参数(如 OpenAI Chat 的 `reasoning_effort`、Anthropic 的 `thinking` 块)。
135
+
136
+ ### 多上游配置文件
137
+
138
+ ```json
139
+ {
140
+ "currentUpstream": "deepseek-chat",
141
+ "upstreams": {
142
+ "deepseek-chat": {
83
143
  "baseUrl": "https://api.deepseek.com/v1",
84
- "model": "deepseek-v4-vision"
144
+ "apiKey": "sk-...",
145
+ "model": "deepseek-v4-pro",
146
+ "dropImages": true,
147
+ "fallback": "kimi-vision"
148
+ },
149
+ "kimi-vision": {
150
+ "baseUrl": "https://api.moonshot.cn/v1",
151
+ "apiKey": "sk-...",
152
+ "model": "kimi-k2.6",
153
+ "headers": { "x-llm-api-key": "sk-..." }
154
+ },
155
+ "claude": {
156
+ "format": "anthropic",
157
+ "baseUrl": "https://api.anthropic.com/v1",
158
+ "apiKey": "sk-ant-...",
159
+ "model": "claude-sonnet-4-20250514"
85
160
  }
86
161
  }
87
162
  }
88
163
  ```
164
+
165
+ 修改 `currentUpstream` 并重启代理即可切换上游 — 无需改动 Codex 配置。
166
+
89
167
  ## 安装
90
168
 
91
169
  ```bash
@@ -99,12 +177,12 @@ npm install -g @codeproxy/cli
99
177
  | `--base-url <url>` | — | 上游端点(除非使用 `--config`,否则必需) |
100
178
  | `--upstream-format <fmt>` | 自动推断 | `anthropic` 或 `openai-chat` |
101
179
  | `--config <file>` | — | JSON 配置文件 |
102
- | `--host <host>` | `127.0.0.1` | 绑定地址 |
180
+ | `--host <host>` | `127.0.0.1` | 绑定主机 |
103
181
  | `-p, --port <port>` | `8787` | 绑定端口 |
104
182
  | `--api-version <ver>` | `2023-06-01` | 覆盖 Anthropic 版本头 |
105
183
  | `--apikey <key>` | — | 上游 API 密钥 |
106
- | `--model <name>` | — | 为所有请求覆盖模型 |
107
- | `--drop-images` | — | 移除图片部分(纯文本模型) |
184
+ | `--model <name>` | — | 覆盖所有请求的 model 字段 |
185
+ | `--drop-images` | — | 移除图片(纯文本模型) |
108
186
 
109
187
  ## 编程使用
110
188
 
@@ -118,6 +196,6 @@ const proxy = await startProxy({
118
196
  });
119
197
  ```
120
198
 
121
- ## 协议
199
+ ## License
122
200
 
123
201
  MIT
package/dist/index.cjs CHANGED
@@ -154,11 +154,13 @@ async function startProxy(options) {
154
154
  requestInfo.method = req.method ?? "POST";
155
155
  requestInfo.url = req.url ?? "/v1/responses";
156
156
  requestInfo.startTime = start;
157
+ const abortController = new AbortController();
157
158
  const timeoutMs = options.timeoutMs;
158
159
  let timeoutTimer;
159
160
  if (timeoutMs && timeoutMs > 0) {
160
161
  timeoutTimer = setTimeout(() => {
161
162
  logger?.warn(`[timeout] request exceeded ${timeoutMs}ms, aborting`);
163
+ abortController.abort();
162
164
  res.destroy();
163
165
  req.destroy();
164
166
  }, timeoutMs);
@@ -172,7 +174,8 @@ async function startProxy(options) {
172
174
  url: req.url ?? "/",
173
175
  upstreamCapture,
174
176
  requestInfo,
175
- requestTracker
177
+ requestTracker,
178
+ signal: abortController.signal
176
179
  });
177
180
  } catch (err) {
178
181
  logger?.error("[proxy-error]", err);
@@ -245,7 +248,8 @@ async function handleRequest(req, res, opts) {
245
248
  const response = await opts.apiFetch(`http://local${urlPath}`, {
246
249
  method,
247
250
  headers,
248
- body: body ? new Uint8Array(body) : void 0
251
+ body: body ? new Uint8Array(body) : void 0,
252
+ signal: opts.signal
249
253
  });
250
254
  const responseBodyText = response.body ? await response.clone().text() : "";
251
255
  opts.requestTracker.remove(requestId);
@@ -1 +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"]}
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 abortController = new AbortController();\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 abortController.abort();\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 signal: abortController.signal,\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 signal?: AbortSignal;\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 signal: opts.signal,\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"]}
package/dist/index.js CHANGED
@@ -149,11 +149,13 @@ async function startProxy(options) {
149
149
  requestInfo.method = req.method ?? "POST";
150
150
  requestInfo.url = req.url ?? "/v1/responses";
151
151
  requestInfo.startTime = start;
152
+ const abortController = new AbortController();
152
153
  const timeoutMs = options.timeoutMs;
153
154
  let timeoutTimer;
154
155
  if (timeoutMs && timeoutMs > 0) {
155
156
  timeoutTimer = setTimeout(() => {
156
157
  logger?.warn(`[timeout] request exceeded ${timeoutMs}ms, aborting`);
158
+ abortController.abort();
157
159
  res.destroy();
158
160
  req.destroy();
159
161
  }, timeoutMs);
@@ -167,7 +169,8 @@ async function startProxy(options) {
167
169
  url: req.url ?? "/",
168
170
  upstreamCapture,
169
171
  requestInfo,
170
- requestTracker
172
+ requestTracker,
173
+ signal: abortController.signal
171
174
  });
172
175
  } catch (err) {
173
176
  logger?.error("[proxy-error]", err);
@@ -240,7 +243,8 @@ async function handleRequest(req, res, opts) {
240
243
  const response = await opts.apiFetch(`http://local${urlPath}`, {
241
244
  method,
242
245
  headers,
243
- body: body ? new Uint8Array(body) : void 0
246
+ body: body ? new Uint8Array(body) : void 0,
247
+ signal: opts.signal
244
248
  });
245
249
  const responseBodyText = response.body ? await response.clone().text() : "";
246
250
  opts.requestTracker.remove(requestId);
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 abortController = new AbortController();\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 abortController.abort();\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 signal: abortController.signal,\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 signal?: AbortSignal;\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 signal: opts.signal,\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeproxy/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Local proxy server that converts any Chat Completions / Anthropic Messages API into OpenAI Responses API so Codex and Claude Code can use any LLM.",
5
5
  "keywords": [
6
6
  "openai",
@@ -69,6 +69,8 @@
69
69
  "build": "tsup",
70
70
  "typecheck": "tsc --noEmit",
71
71
  "test": "vitest run",
72
- "test:watch": "vitest"
72
+ "test:watch": "vitest",
73
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
74
+ "lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" --fix"
73
75
  }
74
76
  }