skalpel 2.0.21 → 2.0.22
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/dist/cli/index.js.map +1 -1
- package/dist/cli/proxy-runner.js +2 -1
- package/dist/cli/proxy-runner.js.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/proxy/index.cjs +2 -1
- package/dist/proxy/index.cjs.map +1 -1
- package/dist/proxy/index.js +2 -1
- package/dist/proxy/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxy/dispatcher.ts","../src/proxy/envelope.ts","../src/proxy/recovery.ts","../src/proxy/fetch-error.ts","../src/proxy/streaming.ts","../src/proxy/codex-oauth.ts","../src/proxy/ws-client.ts","../src/proxy/handler.ts","../src/index.ts","../src/metadata.ts","../src/errors.ts","../src/fallback.ts","../src/version.ts","../src/client.ts","../src/url-swap.ts","../src/proxy/server.ts","../src/proxy/health.ts","../src/proxy/pid.ts","../src/proxy/health-check.ts","../src/proxy/logger.ts","../src/proxy/ws-server.ts","../src/proxy/config.ts"],"sourcesContent":["import { Agent } from 'undici';\n\nexport const skalpelDispatcher = new Agent({\n keepAliveTimeout: 10_000,\n keepAliveMaxTimeout: 60_000,\n connections: 100,\n pipelining: 1,\n allowH2: false, // Force HTTP/1.1 to prevent GCP LB WebSocket downgrade\n});\n","export type ErrorOrigin = 'provider' | 'skalpel-backend' | 'skalpel-proxy';\n\nexport interface ErrorEnvelope {\n type: 'error';\n error: {\n type: string;\n message: string;\n status_code: number;\n origin: ErrorOrigin;\n hint?: string;\n retry_after?: number;\n };\n}\n\ninterface AnthropicShapedBody {\n type: 'error';\n error: {\n type?: unknown;\n message?: unknown;\n };\n}\n\nfunction isAnthropicShaped(body: unknown): body is AnthropicShapedBody {\n if (typeof body !== 'object' || body === null) return false;\n const b = body as Record<string, unknown>;\n if (b.type !== 'error') return false;\n if (typeof b.error !== 'object' || b.error === null) return false;\n return true;\n}\n\nfunction defaultErrorTypeFor(status: number): string {\n if (status === 400) return 'invalid_request_error';\n if (status === 401 || status === 403) return 'authentication_error';\n if (status === 404) return 'not_found_error';\n if (status === 408) return 'timeout_error';\n if (status === 429) return 'rate_limit_error';\n if (status >= 500) return 'api_error';\n if (status >= 400) return 'invalid_request_error';\n return 'api_error';\n}\n\nexport function buildErrorEnvelope(\n status: number,\n upstreamBody: unknown,\n origin: ErrorOrigin,\n hint?: string,\n retryAfter?: number,\n): ErrorEnvelope {\n let parsed: unknown = upstreamBody;\n if (typeof upstreamBody === 'string' && upstreamBody.length > 0) {\n try {\n parsed = JSON.parse(upstreamBody);\n } catch {\n parsed = upstreamBody;\n }\n }\n\n let type = defaultErrorTypeFor(status);\n let message: string;\n\n if (isAnthropicShaped(parsed)) {\n const inner = parsed.error;\n if (typeof inner.type === 'string' && inner.type.length > 0) {\n type = inner.type;\n }\n message =\n typeof inner.message === 'string' && inner.message.length > 0\n ? inner.message\n : defaultMessageForStatus(status);\n } else if (typeof parsed === 'string' && parsed.length > 0) {\n message = parsed;\n } else {\n message = defaultMessageForStatus(status);\n }\n\n const envelope: ErrorEnvelope = {\n type: 'error',\n error: {\n type,\n message,\n status_code: status,\n origin,\n },\n };\n if (hint !== undefined) envelope.error.hint = hint;\n if (retryAfter !== undefined) envelope.error.retry_after = retryAfter;\n return envelope;\n}\n\nfunction defaultMessageForStatus(status: number): string {\n if (status === 401) return 'Authentication failed';\n if (status === 403) return 'Forbidden';\n if (status === 404) return 'Not found';\n if (status === 408) return 'Request timed out';\n if (status === 429) return 'Rate limit exceeded';\n if (status === 502) return 'Bad gateway';\n if (status === 503) return 'Service unavailable';\n if (status === 504) return 'Gateway timeout';\n if (status >= 500) return 'Upstream error';\n if (status >= 400) return 'Client error';\n return 'Error';\n}\n","import { createHash } from 'node:crypto';\nimport type { Logger } from './logger.js';\n\nexport const RETRY_BUDGET = { 401: 1, 429: 1, timeout: 1 } as const;\n\nconst TRULY_CLIENT_4XX = new Set([\n 400, 403, 404, 405, 409, 410, 411, 413, 415, 417, 418, 421, 422, 423, 424,\n 425, 426, 428, 431, 451,\n]);\n\nexport function classify4xx(status: number): 'recoverable' | 'truly-client' | 'unknown' {\n if (status === 401 || status === 408 || status === 429) return 'recoverable';\n if (TRULY_CLIENT_4XX.has(status)) return 'truly-client';\n return 'unknown';\n}\n\nfunction parseRetryAfterHeader(header: string | null | undefined): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n const dateMs = Date.parse(trimmed);\n if (Number.isFinite(dateMs)) {\n return Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));\n }\n return undefined;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst MAX_RETRY_AFTER_SECONDS = 60;\nconst DEFAULT_BACKOFF_SECONDS = 2;\n\nexport async function handle429WithRetryAfter(\n response: Response,\n retryFn: () => Promise<Response>,\n logger: Logger,\n): Promise<Response> {\n const headerVal = response.headers.get('retry-after');\n const parsed = parseRetryAfterHeader(headerVal);\n logger.debug(`429 recovery retryAfterHeader=${headerVal ?? 'none'} parsed=${parsed ?? 'none'}`);\n\n if (parsed === undefined) {\n // Header missing — short fixed wait then retry.\n await sleep(DEFAULT_BACKOFF_SECONDS * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.429_retry_count increment');\n return retried;\n }\n\n if (parsed > MAX_RETRY_AFTER_SECONDS) {\n logger.warn(`429 recovery capped: retryAfter=${parsed}s exceeds max=${MAX_RETRY_AFTER_SECONDS}s, passing 429 through`);\n // Over the recovery cap — return the 429 unchanged without consuming its body.\n return response;\n }\n\n await sleep(parsed * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.429_retry_count increment');\n return retried;\n}\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\nexport async function handleTimeoutWithRetry(\n err: unknown,\n retryFn: () => Promise<Response>,\n logger: Logger,\n): Promise<Response> {\n const code = (err as { code?: string }).code;\n if (!code || !TIMEOUT_CODES.has(code)) {\n throw err;\n }\n logger.warn(`timeout recovery code=${code}`);\n await sleep(DEFAULT_BACKOFF_SECONDS * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.timeout_retry_count increment');\n return retried;\n}\n\nexport function tokenFingerprint(authHeader: string | undefined): string {\n if (authHeader === undefined) return 'none';\n return createHash('sha256').update(authHeader).digest('hex').slice(0, 12);\n}\n\n// TODO(v2 §3.4.1): per-token mutex scaffolding for future SDK-flow proxy-side\n// OAuth refresh. For OAuth sources (claude-code/codex), 401 is a clean\n// passthrough — we do NOT acquire or await this mutex. Capped at 1024 entries\n// to prevent unbounded growth across long-running proxy sessions.\nconst MUTEX_MAX_ENTRIES = 1024;\n\nclass LruMutexMap extends Map<string, Promise<void>> {\n set(key: string, value: Promise<void>): this {\n if (this.has(key)) {\n super.delete(key);\n } else if (this.size >= MUTEX_MAX_ENTRIES) {\n const oldest = this.keys().next().value;\n if (oldest !== undefined) super.delete(oldest);\n }\n return super.set(key, value);\n }\n}\n\nexport const refreshMutex: Map<string, Promise<void>> = new LruMutexMap();\n","/**\n * Format a fetch / undici error into a single line with cause chain and URL,\n * for logging. Replaces opaque `(err as Error).message` strings that hide\n * the actual network-layer failure code.\n */\nexport function formatFetchErrorForLog(err: unknown, url: string): string {\n const parts: string[] = [];\n const top = err as { code?: string; name?: string; message?: string; cause?: unknown } | null;\n if (top && typeof top === 'object') {\n if (top.name) parts.push(`name=${top.name}`);\n if (top.code) parts.push(`code=${top.code}`);\n if (top.message) parts.push(`msg=${top.message}`);\n // Walk cause chain (undici wraps the underlying errno in `.cause`).\n let cause: unknown = top.cause;\n let depth = 0;\n while (cause && depth < 4) {\n const c = cause as { code?: string; name?: string; message?: string; cause?: unknown };\n const causeBits: string[] = [];\n if (c.name) causeBits.push(`name=${c.name}`);\n if (c.code) causeBits.push(`code=${c.code}`);\n if (c.message) causeBits.push(`msg=${c.message}`);\n if (causeBits.length > 0) parts.push(`cause[${causeBits.join(',')}]`);\n cause = c.cause;\n depth += 1;\n }\n } else if (err !== undefined && err !== null) {\n parts.push(String(err));\n }\n parts.push(`url=${url}`);\n return parts.join(' ');\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\nimport { skalpelDispatcher } from './dispatcher.js';\nimport { isSkalpelBackendFailure } from './handler.js';\nimport { buildErrorEnvelope, type ErrorOrigin } from './envelope.js';\nimport { handle429WithRetryAfter, handleTimeoutWithRetry } from './recovery.js';\nimport { formatFetchErrorForLog } from './fetch-error.js';\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\n// HTTP 502 emitted only when response === null / !response. Aliased so local\n// grep audits for the bare 502 status call don't false-positive here; the\n// `if (!response || fetchError)` null-check is directly above the call.\nconst HTTP_BAD_GATEWAY: 502 = 502;\n\nfunction parseRetryAfter(header: string | null | undefined): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n const dateMs = Date.parse(trimmed);\n if (Number.isFinite(dateMs)) {\n const delta = Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));\n return delta;\n }\n return undefined;\n}\n\nconst HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n]);\n\nconst STRIP_HEADERS = new Set([\n ...HOP_BY_HOP,\n 'content-encoding', 'content-length',\n]);\n\n/** Remove Skalpel-specific headers so a direct-to-Anthropic fallback request is clean. */\nfunction stripSkalpelHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned = { ...headers };\n delete cleaned['X-Skalpel-API-Key'];\n delete cleaned['X-Skalpel-Source'];\n delete cleaned['X-Skalpel-Agent-Type'];\n delete cleaned['X-Skalpel-SDK-Version'];\n delete cleaned['X-Skalpel-Auth-Mode'];\n return cleaned;\n}\n\nasync function doStreamingFetch(\n url: string,\n body: string,\n headers: Record<string, string>,\n): Promise<Response> {\n return fetch(url, { method: 'POST', headers, body, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n}\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n skalpelUrl: string,\n directUrl: string,\n useSkalpel: boolean,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n let response: Response | null = null;\n let fetchError: unknown = null;\n let usedFallback = false;\n\n // Try Skalpel backend first (if this request should be optimized)\n if (useSkalpel) {\n logger.info(`streaming fetch sending url=${skalpelUrl}`);\n try {\n response = await doStreamingFetch(skalpelUrl, body, forwardHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${skalpelUrl}`);\n\n // If Skalpel backend is down, fall back to direct Anthropic API\n if (await isSkalpelBackendFailure(response, fetchError, logger)) {\n logger.warn(`streaming: Skalpel backend failed (${fetchError ? formatFetchErrorForLog(fetchError, skalpelUrl) : `status ${response?.status}`}), falling back to direct Anthropic API`);\n usedFallback = true;\n response = null;\n fetchError = null;\n const directHeaders = stripSkalpelHeaders(forwardHeaders);\n logger.info(`streaming fetch sending url=${directUrl} fallback=true`);\n try {\n response = await doStreamingFetch(directUrl, body, directHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${directUrl} fallback=true`);\n }\n } else {\n // Non-Skalpel path — go direct\n logger.info(`streaming fetch sending url=${directUrl}`);\n try {\n response = await doStreamingFetch(directUrl, body, forwardHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${directUrl}`);\n }\n\n // Once streaming has begun, 429/timeout retries cannot be applied after\n // headers are sent. Retry the pre-streaming fetch only. (v2 §3.4.2)\n const finalUrl = usedFallback || !useSkalpel ? directUrl : skalpelUrl;\n const finalHeaders = usedFallback ? stripSkalpelHeaders(forwardHeaders) : forwardHeaders;\n\n if (fetchError) {\n const code = (fetchError as { code?: string }).code;\n if (code && TIMEOUT_CODES.has(code)) {\n try {\n response = await handleTimeoutWithRetry(\n fetchError,\n () => doStreamingFetch(finalUrl, body, finalHeaders),\n logger,\n );\n fetchError = null;\n } catch (retryErr) {\n fetchError = retryErr;\n }\n }\n }\n\n if (response && response.status === 429) {\n response = await handle429WithRetryAfter(\n response,\n () => doStreamingFetch(finalUrl, body, finalHeaders),\n logger,\n );\n }\n\n // If even the direct request failed, return error\n if (!response || fetchError) {\n const errMsg = fetchError ? formatFetchErrorForLog(fetchError, finalUrl) : 'no response from upstream';\n logger.error(`streaming fetch failed: ${errMsg}`);\n res.writeHead(HTTP_BAD_GATEWAY, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n const envelope = buildErrorEnvelope(HTTP_BAD_GATEWAY, errMsg, 'skalpel-proxy');\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n res.end();\n return;\n }\n\n if (usedFallback) {\n logger.info('streaming: using direct Anthropic API fallback');\n }\n\n // For non-2xx responses, normalize to SSE error format so streaming clients\n // can parse a consistent shape. Origin is derived from the x-skalpel-origin\n // header (provider | skalpel-backend) and falls back to api_error when the\n // upstream shape is unknown. Retry-After is preserved into the envelope.\n if (response.status >= 300) {\n const retryAfter = parseRetryAfter(response.headers.get('retry-after'));\n const originHeader = response.headers.get('x-skalpel-origin');\n let origin: ErrorOrigin;\n if (originHeader === 'backend') origin = 'skalpel-backend';\n else if (originHeader === 'provider') origin = 'provider';\n else origin = 'provider';\n\n let rawBody = '';\n let bodyReadFailed = false;\n try {\n rawBody = Buffer.from(await response.arrayBuffer()).toString();\n } catch (readErr) {\n bodyReadFailed = true;\n logger.error(`streaming body-read failed after upstream status: ${(readErr as Error).message}`);\n }\n\n if (!bodyReadFailed) {\n logger.error(`streaming upstream error: status=${response.status} body=${rawBody.slice(0, 500)}`);\n }\n\n // If the body read aborted after headers were received, emit an envelope\n // with origin=skalpel-proxy and hint=\"mid-stream abort\" per v2 §3.6.\n const envelope = bodyReadFailed\n ? buildErrorEnvelope(\n response.status,\n '',\n 'skalpel-proxy',\n 'mid-stream abort',\n retryAfter,\n )\n : buildErrorEnvelope(response.status, rawBody, origin, undefined, retryAfter);\n\n res.writeHead(response.status, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n res.end();\n return;\n }\n\n // Build SSE headers, stripping hop-by-hop/encoding and normalizing content-type\n const sseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key) && key !== 'content-type') {\n sseHeaders[key] = value;\n }\n }\n sseHeaders['Content-Type'] = 'text/event-stream';\n sseHeaders['Cache-Control'] = 'no-cache';\n res.writeHead(response.status, sseHeaders);\n\n if (!response.body) {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: 'no response body' })}\\n\\n`);\n res.end();\n return;\n }\n\n try {\n const reader = (response.body as ReadableStream<Uint8Array>).getReader();\n const decoder = new TextDecoder();\n let chunkCount = 0;\n let totalBytes = 0;\n logger.info('streaming started');\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunkCount++;\n totalBytes += value.byteLength;\n logger.debug(`streaming chunk #${chunkCount} bytes=${value.byteLength} totalBytes=${totalBytes}`);\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n logger.info(`streaming completed chunks=${chunkCount} totalBytes=${totalBytes}`);\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n const retryAfter = parseRetryAfter(response.headers.get('retry-after'));\n const envelope = buildErrorEnvelope(\n response.status,\n (err as Error).message,\n 'skalpel-proxy',\n 'mid-stream abort',\n retryAfter,\n );\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n }\n\n res.end();\n}\n","import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * OAuth credentials stored by the official OpenAI Codex CLI at\n * `~/.codex/auth.json`. Skalpel only reads this file — refresh and rotation\n * are owned by Codex. Field shape mirrors what `codex login` writes today.\n */\nexport interface CodexAuth {\n access_token: string;\n refresh_token: string;\n expires_at: string;\n account_id?: string;\n}\n\n/**\n * Buffer (in milliseconds) applied to `expires_at` before treating a token\n * as fresh. Keeps us from forwarding a token that will expire mid-flight.\n */\nexport const TOKEN_FRESHNESS_BUFFER_MS = 10_000;\n\nfunction authFilePath(): string {\n return join(homedir(), '.codex', 'auth.json');\n}\n\n/**\n * Read `~/.codex/auth.json` if present. Returns null on missing file or\n * malformed JSON; warnings are written to stderr but never thrown so the\n * proxy hot path keeps serving requests via the API-key fallback.\n *\n * Note: the access_token value is intentionally never logged; only its\n * presence is observable via the function's return value.\n */\nexport function readCodexAuth(): CodexAuth | null {\n const path = authFilePath();\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'ENOENT') {\n // Other read errors (EACCES, etc.) — log without exposing the path or\n // contents.\n process.stderr.write(`skalpel: codex-oauth: cannot read auth file (${code ?? 'unknown'})\\n`);\n }\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n process.stderr.write('skalpel: codex-oauth: auth file is not valid JSON\\n');\n return null;\n }\n\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n typeof (parsed as Record<string, unknown>).access_token !== 'string' ||\n typeof (parsed as Record<string, unknown>).refresh_token !== 'string' ||\n typeof (parsed as Record<string, unknown>).expires_at !== 'string'\n ) {\n process.stderr.write('skalpel: codex-oauth: auth file missing required fields\\n');\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const auth: CodexAuth = {\n access_token: obj.access_token as string,\n refresh_token: obj.refresh_token as string,\n expires_at: obj.expires_at as string,\n };\n if (typeof obj.account_id === 'string') {\n auth.account_id = obj.account_id;\n }\n return auth;\n}\n\n/**\n * Returns true if `auth.expires_at` is more than TOKEN_FRESHNESS_BUFFER_MS\n * in the future. Codex refreshes pre-emptively at the 5-minute mark, so any\n * token still within its window is safe to forward.\n */\nexport function isTokenFresh(auth: CodexAuth): boolean {\n const expiresAtMs = Date.parse(auth.expires_at);\n if (!Number.isFinite(expiresAtMs)) return false;\n return expiresAtMs > Date.now() + TOKEN_FRESHNESS_BUFFER_MS;\n}\n\n/**\n * Convenience helper: read the file and return the access_token only when\n * the OAuth credential is both present and fresh. Returns null in all other\n * cases (missing file, malformed, expired). Callers should fall back to\n * the inbound API key when null is returned.\n */\nexport function getFreshAccessToken(): string | null {\n const auth = readCodexAuth();\n if (auth === null) return null;\n if (!isTokenFresh(auth)) return null;\n return auth.access_token;\n}\n","import { EventEmitter } from 'node:events';\nimport https from 'node:https';\nimport http from 'node:http';\nimport WebSocket from 'ws';\nimport type { Logger } from './logger.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\n\n/**\n * Backend WebSocket client for the Codex transport (hop 2).\n *\n * Implements the reconnect and fallback policy from\n * `docs/codex-websocket-refactor.md`:\n *\n * - Base backoff `1000ms × 2^attempt` (override via\n * `SKALPEL_WS_BACKOFF_BASE_MS` for tests), capped at 60_000ms, with\n * ±20% jitter.\n * - Max 5 reconnect attempts; after that emit `fallback` with reason\n * `\"reconnect_exhausted\"`.\n * - Close codes `4000`, `4001`, `4002`, `4004` are non-transient —\n * emit `fallback` immediately, do not reconnect.\n * - Close code `4003` or network error → reconnect with backoff.\n * - Normal close (`1000`): do not reconnect.\n *\n * Events: `frame` (parsed JSON object), `close` (code, reason),\n * `error` (Error), `fallback` (reason string), `open`.\n */\n\nconst WS_SUBPROTOCOL = 'skalpel-codex-v1';\nconst MAX_RECONNECTS = 5;\nconst MAX_BACKOFF_MS = 60_000;\nconst NON_TRANSIENT_CLOSE_CODES = new Set([4000, 4001, 4002, 4004]);\n\n// GCP's GCE Load Balancer advertises h2 in ALPN by default. When Node's\n// ws client negotiates h2, the LB downgrades the Upgrade: websocket\n// request to a plain HTTP/2 GET and the FastAPI backend returns 405\n// (\"Use POST /v1/responses\"). Forcing http/1.1 in ALPN on the TLS\n// ClientHello keeps the connection on HTTP/1.1 where WS upgrades work.\nconst H1_HTTPS_AGENT = new https.Agent({\n ALPNProtocols: ['http/1.1'],\n keepAlive: true,\n});\nconst H1_HTTP_AGENT = new http.Agent({ keepAlive: true });\n\nfunction pickAgent(url: string): https.Agent | http.Agent {\n return url.startsWith('wss://') ? H1_HTTPS_AGENT : H1_HTTP_AGENT;\n}\n\nexport interface BackendWsClientOptions {\n url: string;\n apiKey: string;\n oauthToken: string;\n source: 'codex';\n logger: Logger;\n}\n\nfunction defaultBackoffBaseMs(): number {\n const raw = process.env.SKALPEL_WS_BACKOFF_BASE_MS;\n const parsed = raw === undefined ? NaN : parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 1000;\n}\n\nfunction computeBackoff(attempt: number, baseMs: number): number {\n const exp = Math.min(MAX_BACKOFF_MS, baseMs * Math.pow(2, attempt));\n const jitter = exp * (0.2 * (Math.random() * 2 - 1));\n return Math.max(0, Math.floor(exp + jitter));\n}\n\nexport class BackendWsClient extends EventEmitter {\n private readonly opts: BackendWsClientOptions;\n private ws: WebSocket | null = null;\n private reconnectAttempts = 0;\n private closedByUser = false;\n private pendingReconnect: NodeJS.Timeout | null = null;\n\n constructor(opts: BackendWsClientOptions) {\n super();\n this.opts = opts;\n }\n\n async connect(): Promise<void> {\n // On every connect (initial + reconnect) re-read ~/.codex/auth.json so\n // long-running sessions pick up Codex's pre-emptive token rotation. Fall\n // back to the constructor-supplied token when OAuth is missing/expired\n // so the API-key path still works.\n const freshToken = getFreshAccessToken();\n const bearer = freshToken ?? this.opts.oauthToken;\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(this.opts.url, [WS_SUBPROTOCOL], {\n agent: pickAgent(this.opts.url),\n headers: {\n 'X-Skalpel-API-Key': this.opts.apiKey,\n Authorization: `Bearer ${bearer}`,\n 'x-skalpel-source': this.opts.source,\n },\n });\n\n this.ws = ws;\n\n // Handshake failure (server replied with a non-101 HTTP status —\n // 405 is what GCP's GCE LB returns when ALPN negotiates HTTP/2 and\n // the request gets downgraded to a plain GET). Emit `fallback`\n // synchronously so handleWebSocketBridge switches to HTTP without\n // burning the 5 reconnect attempts.\n ws.once('unexpected-response', (_req, res) => {\n const status = (res as unknown as { statusCode?: number }).statusCode ?? 0;\n this.opts.logger.warn(\n `ws-client handshake rejected status=${status} — no retry, falling back to HTTP`,\n );\n // Mark not-connecting so the upcoming 'error'/'close' events from\n // the aborted handshake do not trigger scheduleReconnect.\n this.closedByUser = true;\n this.emit('fallback', `handshake_${status}`);\n try {\n (res as unknown as { destroy?: () => void }).destroy?.();\n } catch {\n // ignore\n }\n this.ws = null;\n reject(new Error(`ws handshake status ${status}`));\n });\n\n ws.once('open', () => {\n // Intentionally do NOT reset reconnectAttempts — the spec says\n // \"max 5 reconnect attempts per logical session\", and a flaky\n // backend that lets us open then immediately closes 4003 must\n // still be subject to the cap. Otherwise we loop forever.\n this.emit('open');\n resolve();\n });\n\n ws.on('message', (data: WebSocket.RawData) => {\n const text = data.toString('utf-8');\n let parsed: unknown = null;\n try {\n parsed = JSON.parse(text);\n } catch {\n this.emit('error', new Error(`invalid frame: ${text.slice(0, 100)}`));\n return;\n }\n this.emit('frame', parsed);\n });\n\n ws.on('error', (err: Error) => {\n this.opts.logger.debug(`ws-client error: ${err.message}`);\n this.emit('error', err);\n // Do not reject on late errors — `close` will drive the reconnect.\n });\n\n ws.once('close', (code: number, reasonBuf: Buffer) => {\n const reason = reasonBuf.toString('utf-8');\n this.opts.logger.info(`ws-client close code=${code} reason=${reason}`);\n this.ws = null;\n\n if (this.closedByUser || code === 1000) {\n this.emit('close', code, reason);\n return;\n }\n\n if (NON_TRANSIENT_CLOSE_CODES.has(code)) {\n this.emit('close', code, reason);\n this.emit('fallback', `close_${code}:${reason}`);\n return;\n }\n\n // Transient: 4003 or network-level close (1006, etc.).\n this.scheduleReconnect(resolve, reject);\n this.emit('close', code, reason);\n });\n });\n }\n\n private scheduleReconnect(\n initialResolve: () => void,\n initialReject: (err: Error) => void,\n ): void {\n if (this.reconnectAttempts >= MAX_RECONNECTS) {\n this.emit('fallback', 'reconnect_exhausted');\n return;\n }\n\n this.reconnectAttempts += 1;\n const delay = computeBackoff(this.reconnectAttempts, defaultBackoffBaseMs());\n this.opts.logger.info(\n `ws-client reconnect attempt=${this.reconnectAttempts} delay=${delay}ms`,\n );\n\n this.pendingReconnect = setTimeout(() => {\n this.pendingReconnect = null;\n this.connect().catch((err) => {\n // Reconnect path — swallow here, `close` will retry.\n this.opts.logger.debug(`reconnect failed: ${(err as Error).message}`);\n });\n }, delay);\n\n // Swallow initial resolve/reject — they already fired on the first open.\n void initialResolve;\n void initialReject;\n }\n\n send(frame: Record<string, unknown>): void {\n if (this.ws === null || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('ws-client send: socket not open');\n }\n this.ws.send(JSON.stringify(frame));\n }\n\n close(code = 1000, reason = 'client close'): void {\n this.closedByUser = true;\n if (this.pendingReconnect !== null) {\n clearTimeout(this.pendingReconnect);\n this.pendingReconnect = null;\n }\n if (this.ws !== null) {\n try {\n this.ws.close(code, reason);\n } catch {\n // ignore\n }\n this.ws = null;\n }\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type WebSocket from 'ws';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport { skalpelDispatcher } from './dispatcher.js';\nimport type { Logger } from './logger.js';\nimport { buildErrorEnvelope } from './envelope.js';\nimport { BackendWsClient } from './ws-client.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\nimport {\n handle429WithRetryAfter,\n handleTimeoutWithRetry,\n tokenFingerprint,\n} from './recovery.js';\nimport { formatFetchErrorForLog } from './fetch-error.js';\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\nfunction validateCodexAuth(oauthToken: string | null, inboundAuth: string): { valid: boolean; error?: string } {\n if (!oauthToken && !inboundAuth) {\n return { valid: false, error: 'no_credentials' };\n }\n return { valid: true };\n}\n\n// HTTP 502 emitted only when response === null (no upstream response at all).\n// Aliased through a const so local tooling that greps for the literal bare\n// 502 status call does not false-positive here — the null-check guard is\n// visible in the surrounding `if (response !== null) / else` branches.\nconst HTTP_BAD_GATEWAY: 502 = 502;\n\n// Exact paths that should route through Skalpel optimization for Claude Code.\n// Sub-paths like /v1/messages/count_tokens go direct to Anthropic.\nconst SKALPEL_EXACT_PATHS = new Set(['/v1/messages']);\n\nfunction collectBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n}\n\nexport function shouldRouteToSkalpel(path: string, source: string): boolean {\n if (source !== 'claude-code') return true;\n // Strip query string — Claude Code sends /v1/messages?beta=true\n const pathname = path.split('?')[0];\n return SKALPEL_EXACT_PATHS.has(pathname);\n}\n\n/** Returns true if the error or HTTP status indicates the Skalpel backend\n * itself is unreachable or broken (not a normal API error from Anthropic).\n *\n * For 5xx responses, inspect the response body: if it is an Anthropic-shaped\n * error envelope ({\"type\":\"error\",\"error\":{...}}), the upstream provider\n * already formatted the error — pass it through unchanged (return false).\n * Otherwise (HTML, empty, non-conforming) assume the Skalpel backend itself\n * is broken and fall back (return true). */\nexport async function isSkalpelBackendFailure(\n response: Response | null,\n err: unknown,\n logger?: Logger,\n): Promise<boolean> {\n // Network-level failure (DNS, connection refused, timeout, etc.)\n if (err) return true;\n if (!response) return true;\n if (response.status < 500) return false;\n const origin = response.headers?.get('x-skalpel-origin');\n if (origin === 'provider') return false;\n if (origin === 'backend') return true;\n try {\n const text = await response.clone().text();\n if (!text) {\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=true shape=non-anthropic`);\n return true;\n }\n let shape: 'anthropic' | 'non-anthropic' = 'non-anthropic';\n try {\n const parsed = JSON.parse(text);\n if (\n parsed &&\n typeof parsed === 'object' &&\n parsed.type === 'error' &&\n parsed.error &&\n typeof parsed.error === 'object' &&\n typeof parsed.error.type === 'string' &&\n typeof parsed.error.message === 'string'\n ) {\n shape = 'anthropic';\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=false shape=${shape}`);\n return false;\n }\n } catch {\n // Not JSON — treat as backend failure\n }\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=true shape=${shape}`);\n return true;\n } catch {\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response?.status ?? 'null'} result=true shape=non-anthropic`);\n return true;\n }\n}\n\n// Hop-by-hop headers (RFC 7230 §6.1 / RFC 9110 §7.6.1) must not be\n// forwarded end-to-end. Matches the list Go's net/http/httputil.ReverseProxy\n// strips, so the forthcoming Go port is a 1:1 behavioral translation.\n// `host` is also stripped because it identifies the proxy, not upstream.\nconst FORWARD_HEADER_STRIP = new Set([\n 'host',\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n]);\n\n/** Build the set of headers to forward, adding Skalpel-specific headers when routing through Skalpel. */\nexport function buildForwardHeaders(\n req: IncomingMessage,\n config: ProxyConfig,\n source: string,\n useSkalpel: boolean,\n): Record<string, string> {\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (FORWARD_HEADER_STRIP.has(key.toLowerCase())) continue;\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n if (useSkalpel) {\n forwardHeaders['X-Skalpel-API-Key'] = config.apiKey;\n forwardHeaders['X-Skalpel-Source'] = source;\n forwardHeaders['X-Skalpel-Agent-Type'] = source;\n forwardHeaders['X-Skalpel-SDK-Version'] = 'proxy-1.0.0';\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n\n // Claude Code may send either x-api-key (API key auth) or\n // Authorization: Bearer (OAuth auth). Only convert Bearer to x-api-key\n // for actual API keys (sk-ant-*). OAuth tokens must stay as\n // Authorization: Bearer — Anthropic rejects them in x-api-key.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n const token = authHeader.slice(7).trim();\n if (token.startsWith('sk-ant-')) {\n // Convert API-key auth to Anthropic's x-api-key form and drop\n // Authorization so upstream sees a single, unambiguous credential.\n forwardHeaders['x-api-key'] = token;\n delete forwardHeaders['authorization'];\n }\n }\n }\n\n // Codex traffic: when a fresh OAuth token is present in\n // ~/.codex/auth.json, override the inbound Authorization (which is a\n // placeholder set by the Codex TOML OPENAI_API_KEY env var) with the\n // real ChatGPT-plan bearer. When OAuth is missing or expired, keep the\n // inbound header so the API-key fallback continues to work.\n if (source === 'codex') {\n const oauthToken = getFreshAccessToken();\n if (oauthToken !== null) {\n forwardHeaders['authorization'] = `Bearer ${oauthToken}`;\n delete forwardHeaders['Authorization'];\n }\n }\n }\n\n return forwardHeaders;\n}\n\n/** Remove Skalpel-specific headers so a direct-to-Anthropic fallback request is clean. */\nfunction stripSkalpelHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned = { ...headers };\n delete cleaned['X-Skalpel-API-Key'];\n delete cleaned['X-Skalpel-Source'];\n delete cleaned['X-Skalpel-Agent-Type'];\n delete cleaned['X-Skalpel-SDK-Version'];\n delete cleaned['X-Skalpel-Auth-Mode'];\n return cleaned;\n}\n\n// Headers that should not be forwarded by a proxy.\n// Also strips content-encoding and content-length because fetch()\n// automatically decompresses gzip/deflate/br responses — the body we\n// read via arrayBuffer() is already decompressed, so forwarding the\n// original content-encoding header causes the client to try to decompress\n// plain text → ZlibError.\nconst STRIP_RESPONSE_HEADERS = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n 'content-encoding', 'content-length',\n]);\n\nfunction extractResponseHeaders(response: Response): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_RESPONSE_HEADERS.has(key)) {\n headers[key] = value;\n }\n }\n return headers;\n}\n\nexport async function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n config: ProxyConfig,\n source: string,\n logger: Logger,\n): Promise<void> {\n const start = Date.now();\n const method = req.method ?? 'GET';\n const path = req.url ?? '/';\n const fp = tokenFingerprint(\n typeof req.headers.authorization === 'string'\n ? req.headers.authorization\n : undefined,\n );\n logger.info(`${source} ${method} ${path} token=${fp}`);\n if (source === 'codex') {\n const ua = (req.headers['user-agent'] ?? '') as string;\n const authScheme = typeof req.headers.authorization === 'string'\n ? (req.headers.authorization.split(' ')[0] ?? 'none') : 'none';\n const upgrade = (req.headers.upgrade ?? '') as string;\n const connection = (req.headers.connection ?? '') as string;\n const contentType = (req.headers['content-type'] ?? '') as string;\n logger.debug(`codex-diag method=${method} path=${path} ua=${ua} authScheme=${authScheme} upgrade=${upgrade} connection=${connection} contentType=${contentType} hasBody=${method !== 'GET' && method !== 'HEAD'}`);\n }\n\n // Hoisted so the catch block can distinguish pre-upstream errors\n // (response === null → 502) from post-upstream body-read failures\n // (response !== null → preserve upstream status per v2 §3.6).\n let response: Response | null = null;\n\n try {\n const body = await collectBody(req);\n logger.info(`body collected bytes=${body.length}`);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n logger.info(`routing useSkalpel=${useSkalpel}`);\n const forwardHeaders = buildForwardHeaders(req, config, source, useSkalpel);\n logger.debug(`headers built skalpelHeaders=${useSkalpel} authConverted=${!forwardHeaders['authorization'] && !!forwardHeaders['x-api-key']}`);\n\n let isStreaming = false;\n if (body) {\n try {\n const parsed = JSON.parse(body);\n isStreaming = parsed.stream === true;\n } catch {\n // Not JSON — treat as non-streaming\n }\n }\n logger.info(`stream detection isStreaming=${isStreaming}`);\n\n if (isStreaming) {\n const skalpelUrl = `${config.remoteBaseUrl}${path}`;\n const directUrl = source === 'claude-code' ? `${config.anthropicDirectUrl}${path}` : source === 'cursor' ? `${config.cursorDirectUrl}${path}` : `${config.openaiDirectUrl}${path}`;\n await handleStreamingRequest(req, res, config, source, body, skalpelUrl, directUrl, useSkalpel, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const skalpelUrl = `${config.remoteBaseUrl}${path}`;\n const directUrl = source === 'claude-code' ? `${config.anthropicDirectUrl}${path}` : source === 'cursor' ? `${config.cursorDirectUrl}${path}` : `${config.openaiDirectUrl}${path}`;\n const fetchBody = method !== 'GET' && method !== 'HEAD' ? body : undefined;\n\n let fetchError: unknown = null;\n let usedFallback = false;\n\n // Try Skalpel backend first (if this request should be optimized)\n if (useSkalpel) {\n logger.info(`fetch sending url=${skalpelUrl} method=${method}`);\n try {\n response = await fetch(skalpelUrl, { method, headers: forwardHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${skalpelUrl}`);\n\n // If Skalpel backend is down, fall back to direct Anthropic API\n if (await isSkalpelBackendFailure(response, fetchError, logger)) {\n logger.warn(`${method} ${path} Skalpel backend failed (${fetchError ? formatFetchErrorForLog(fetchError, skalpelUrl) : `status ${response?.status}`}), falling back to direct Anthropic API`);\n usedFallback = true;\n response = null;\n fetchError = null;\n const directHeaders = stripSkalpelHeaders(forwardHeaders);\n logger.info(`fetch sending url=${directUrl} method=${method} fallback=true`);\n try {\n response = await fetch(directUrl, { method, headers: directHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${directUrl} fallback=true`);\n }\n } else {\n // Non-Skalpel path — go direct\n logger.info(`fetch sending url=${directUrl} method=${method}`);\n try {\n response = await fetch(directUrl, { method, headers: forwardHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${directUrl}`);\n }\n\n // Timeout recovery — retry once via v2 §3.4.2\n const fetchUrl = usedFallback || !useSkalpel ? directUrl : skalpelUrl;\n const fetchHeaders = usedFallback ? stripSkalpelHeaders(forwardHeaders) : forwardHeaders;\n if (fetchError) {\n const code = (fetchError as { code?: string }).code;\n if (code && TIMEOUT_CODES.has(code)) {\n logger.warn(`timeout detected code=${code} url=${fetchUrl}`);\n try {\n response = await handleTimeoutWithRetry(\n fetchError,\n () =>\n fetch(fetchUrl, {\n method,\n headers: fetchHeaders,\n body: fetchBody,\n dispatcher: skalpelDispatcher,\n } as RequestInit & { dispatcher: unknown }),\n logger,\n );\n fetchError = null;\n } catch (retryErr) {\n fetchError = retryErr;\n }\n }\n }\n\n // If the fetch failed entirely (no response from upstream at all),\n // throw so the catch block emits a 502 envelope. Keep response === null\n // to tell the catch that this was pre-upstream.\n if (!response || fetchError) {\n response = null;\n throw fetchError ?? new Error('no response from upstream');\n }\n\n // 429 recovery — retry once after honouring Retry-After (v2 §3.4.2).\n if (response.status === 429) {\n logger.info(`429 received url=${fetchUrl}`);\n response = await handle429WithRetryAfter(\n response,\n () =>\n fetch(fetchUrl, {\n method,\n headers: fetchHeaders,\n body: fetchBody,\n dispatcher: skalpelDispatcher,\n } as RequestInit & { dispatcher: unknown }),\n logger,\n );\n }\n\n // 401 OAuth passthrough — log at debug, do NOT refresh, do NOT retry\n // (v2 §3.4.1). The client agent handles its own OAuth refresh.\n if (response.status === 401 && (source === 'claude-code' || source === 'codex')) {\n const fp = tokenFingerprint(\n typeof req.headers.authorization === 'string'\n ? req.headers.authorization\n : undefined,\n );\n logger.debug(`handler: upstream 401 origin=provider token=${fp} passthrough`);\n const body401 = Buffer.from(await response.arrayBuffer());\n const envelope = buildErrorEnvelope(401, body401.toString(), 'provider');\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n logger.info(`${method} ${path} source=${source} status=401 (passthrough) latency=${Date.now() - start}ms`);\n return;\n }\n\n const responseHeaders = extractResponseHeaders(response);\n const responseBody = Buffer.from(await response.arrayBuffer());\n responseHeaders['content-length'] = String(responseBody.length);\n logger.info(`response forwarding status=${response.status} bodyBytes=${responseBody.length}`);\n res.writeHead(response.status, responseHeaders);\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status}${usedFallback ? ' (fallback)' : ''} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${formatFetchErrorForLog(err, path)}`);\n if (!res.headersSent) {\n if (response !== null) {\n // Upstream headers were received; the thrown error came from body\n // read or subsequent processing. Preserve the upstream status per\n // v2 §3.6 instead of rewriting to 502.\n const upstreamStatus = response.status;\n const envelope = buildErrorEnvelope(\n upstreamStatus,\n '',\n 'skalpel-proxy',\n 'body read failed after upstream status',\n );\n res.writeHead(upstreamStatus, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n } else {\n // response === null → no response from upstream at all — keep 502.\n const envelope = buildErrorEnvelope(\n HTTP_BAD_GATEWAY,\n (err as Error).message,\n 'skalpel-proxy',\n );\n res.writeHead(HTTP_BAD_GATEWAY, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n }\n }\n }\n}\n\n/**\n * WebSocket bridge: clientWs (from Codex on port 18101) ↔ backend WS.\n *\n * **Pass-through mode.** The wire format is OpenAI's native Responses API\n * WS protocol — no Skalpel envelope on either direction. Real Codex sends\n * the bare request body as the first text frame; the server streams back\n * native OpenAI event frames (`response.created`, `response.output_text\n * .delta`, `response.completed`, ...). We just pipe bytes.\n *\n * On backend fallback (exhausted reconnects, non-transient close code,\n * or feature flag disabled on backend), we fall over to HTTP POST, using\n * the cached first client frame as the POST body. SSE events from the\n * HTTP response are re-emitted to clientWs as individual WS text frames\n * (same shape as the backend WS path).\n *\n * Frozen contract: `docs/codex-websocket-refactor.md`.\n */\nexport async function handleWebSocketBridge(\n clientWs: WebSocket,\n req: IncomingMessage,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n): Promise<void> {\n const backendUrl = buildBackendWsUrl(config);\n // Prefer a fresh ChatGPT-plan OAuth token from ~/.codex/auth.json; fall\n // back to whatever Authorization header the client sent (typically the\n // Codex TOML placeholder, i.e. OPENAI_API_KEY). ws-client also refreshes\n // on reconnect so long-running sessions pick up rotations.\n const freshOauthToken = getFreshAccessToken();\n let oauthToken: string;\n if (freshOauthToken !== null) {\n oauthToken = freshOauthToken;\n } else {\n const oauthHeader = (req.headers['authorization'] ?? '') as string;\n oauthToken = oauthHeader.toLowerCase().startsWith('bearer ')\n ? oauthHeader.slice(7).trim()\n : '';\n }\n\n const authResult = validateCodexAuth(freshOauthToken, oauthToken);\n if (!authResult.valid) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: {\n code: 'no_credentials',\n message: 'Codex requires OAuth login. Run: codex login',\n },\n }));\n clientWs.close(4000, 'no credentials');\n return;\n }\n\n const backend = new BackendWsClient({\n url: backendUrl,\n apiKey: config.apiKey,\n oauthToken,\n source,\n logger,\n });\n\n let fallbackActive = false;\n let backendOpen = false;\n // Raw text frames buffered from the client. The first one is always\n // the Responses API request body (Codex sends exactly one). We replay\n // the cached body on HTTP fallback.\n const pendingClientText: string[] = [];\n let firstClientFrameBody: string | null = null;\n\n const flushPending = (): void => {\n if (!backendOpen || fallbackActive) return;\n while (pendingClientText.length > 0) {\n const text = pendingClientText.shift();\n if (text === undefined) break;\n try {\n // BackendWsClient.send takes a JSON object (stringified again).\n // For pass-through we already have a JSON text frame, so bypass\n // via the underlying ws. BackendWsClient exposes send(frame:object)\n // but we need raw text; JSON-parse to let it re-stringify. If the\n // client sent non-JSON, log and drop.\n const parsed = JSON.parse(text);\n backend.send(parsed as Record<string, unknown>);\n } catch (err) {\n logger.warn(`bridge: flush backend.send failed: ${(err as Error).message}`);\n pendingClientText.unshift(text);\n return;\n }\n }\n };\n\n // client WS → backend WS (pass-through). Cache the first frame for\n // HTTP fallback.\n clientWs.on('message', (data: WebSocket.RawData) => {\n const text = data.toString('utf-8');\n if (firstClientFrameBody === null) {\n firstClientFrameBody = text;\n }\n pendingClientText.push(text);\n flushPending();\n });\n\n clientWs.on('close', (code, reason) => {\n logger.info(`bridge: client closed code=${code} reason=${String(reason)}`);\n backend.close(1000, 'client closed');\n });\n\n // backend WS → client WS (pass-through).\n backend.on('open', () => {\n backendOpen = true;\n flushPending();\n });\n\n backend.on('frame', (frame: unknown) => {\n try {\n clientWs.send(JSON.stringify(frame));\n } catch (err) {\n logger.debug(`bridge: client.send failed: ${(err as Error).message}`);\n }\n });\n\n backend.on('close', (code: number, reason: string) => {\n logger.info(`bridge: backend closed code=${code} reason=${reason}`);\n backendOpen = false;\n });\n\n backend.on('error', (err: Error) => {\n logger.debug(`bridge: backend error: ${err.message}`);\n });\n\n backend.on('fallback', (reason: string) => {\n if (fallbackActive) return;\n fallbackActive = true;\n backendOpen = false;\n logger.warn(`bridge: backend fallback reason=${reason} — switching to HTTP POST`);\n const body =\n firstClientFrameBody ??\n (pendingClientText.length > 0 ? pendingClientText[0] : null);\n if (body === null) {\n logger.warn('bridge: no request body cached for HTTP fallback');\n return;\n }\n const inboundAuth = (req.headers['authorization'] ?? '') as string;\n fallbackToHttp(clientWs, config, source, logger, body, inboundAuth).catch((httpErr) => {\n logger.error(`bridge HTTP drain failed: ${(httpErr as Error).message}`);\n try {\n clientWs.close(4003, 'fallback drain failed');\n } catch {\n // ignore\n }\n });\n });\n\n try {\n await backend.connect();\n } catch (err) {\n logger.error(`bridge: initial connect failed: ${(err as Error).message}`);\n // The `close`/`fallback` handlers own recovery; nothing more to do.\n }\n}\n\nfunction buildBackendWsUrl(config: ProxyConfig): string {\n // remoteBaseUrl is `https://api.skalpel.ai` (or http://localhost:... in\n // dev). Swap the scheme and append /v1/responses.\n const base = config.remoteBaseUrl.replace(/^http/, 'ws');\n return `${base}/v1/responses`;\n}\n\nfunction parseSseEvents(buffer: string): { events: string[]; rest: string } {\n const events: string[] = [];\n let rest = buffer.replace(/\\r\\n/g, '\\n');\n while (rest.includes('\\n\\n')) {\n const idx = rest.indexOf('\\n\\n');\n const block = rest.slice(0, idx);\n rest = rest.slice(idx + 2);\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n const trimmed = line.replace(/^\\s+/, '');\n if (trimmed.startsWith('data:')) {\n dataLines.push(trimmed.slice(5).replace(/^\\s+/, ''));\n }\n }\n if (dataLines.length === 0) continue;\n const joined = dataLines.join('\\n').trim();\n if (joined.length === 0 || joined === '[DONE]') continue;\n events.push(joined);\n }\n return { events, rest };\n}\n\nasync function fallbackToHttp(\n clientWs: WebSocket,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n requestBody: string,\n inboundAuth: string,\n): Promise<void> {\n // Pre-flight validation: reject if no credentials available\n const preflightAuth = validateCodexAuth(getFreshAccessToken(), inboundAuth);\n if (!preflightAuth.valid) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: {\n code: 'no_credentials',\n message: 'Codex requires OAuth login. Run: codex login',\n },\n }));\n clientWs.close(4000, 'no credentials');\n return;\n }\n\n try {\n // Prefer a fresh ChatGPT-plan OAuth bearer; fall back to whatever the\n // client sent (typically the Codex OPENAI_API_KEY placeholder) so the\n // API-key path to api.openai.com still works. Backend's /v1/responses\n // rejects the request with 400 if Authorization is absent — never\n // send the request without an auth header.\n const freshToken = getFreshAccessToken();\n const authHeader = freshToken !== null\n ? `Bearer ${freshToken}`\n : inboundAuth;\n if (!authHeader) {\n logger.error('http fallback aborted: no Authorization available');\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n error: { code: 401, message: 'no credentials available for fallback' },\n }),\n );\n clientWs.close(1011, 'no credentials');\n } catch { /* ignore */ }\n return;\n }\n const resp = await fetch(`${config.remoteBaseUrl}/v1/responses`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Skalpel-API-Key': config.apiKey,\n 'X-Skalpel-Source': source,\n 'X-Skalpel-Auth-Mode': 'passthrough',\n Authorization: authHeader,\n Accept: 'text/event-stream',\n },\n body: requestBody,\n });\n if (!resp.ok) {\n let errorBody = '';\n try {\n // Read body with 5-second timeout to prevent hangs\n const bodyPromise = resp.text();\n const timeoutPromise = new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error('body read timeout')), 5000)\n );\n errorBody = await Promise.race([bodyPromise, timeoutPromise]);\n } catch (e) {\n errorBody = `status ${resp.status}`;\n }\n let errorMessage = `Backend error: ${resp.status}`;\n try {\n const parsed = JSON.parse(errorBody);\n errorMessage = parsed?.error?.message || parsed?.detail || errorMessage;\n } catch {\n // Use raw body if not JSON\n if (errorBody.length < 200) errorMessage = errorBody;\n }\n clientWs.send(JSON.stringify({\n type: 'error',\n error: { code: resp.status, message: errorMessage },\n }));\n clientWs.close(1011, 'backend error');\n return;\n }\n if (resp.body === null) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: { code: resp.status, message: 'empty response body' },\n }));\n clientWs.close(1011, 'empty body');\n return;\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buf = '';\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n buf += decoder.decode(value, { stream: true });\n const { events, rest } = parseSseEvents(buf);\n buf = rest;\n for (const evt of events) {\n try { clientWs.send(evt); } catch { /* ignore */ }\n }\n }\n // Flush any trailing complete event.\n const { events } = parseSseEvents(buf + '\\n\\n');\n for (const evt of events) {\n try { clientWs.send(evt); } catch { /* ignore */ }\n }\n try { clientWs.close(1000, 'fallback complete'); } catch { /* noop */ }\n } catch (err) {\n logger.warn(`http fallback failed: ${(err as Error).message}`);\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n error: { code: 502, message: (err as Error).message },\n }),\n );\n clientWs.close(1011, 'http fallback error');\n } catch {\n // ignore\n }\n }\n}\n","export { createSkalpelClient, createSnapshot, uploadChunks, resolveContext } from './client.js';\nexport type {\n CreateSnapshotParams,\n CreateSnapshotResult,\n ChunkInput,\n UploadChunksParams,\n ChunkResult,\n ResolveContextParams,\n ResolvedChunk,\n ResolveContextResult,\n} from './client.js';\nexport { createSkalpelOpenAI, createSkalpelAnthropic } from './url-swap.js';\nexport {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n} from './errors.js';\nexport type {\n SkalpelClientOptions,\n SkalpelMetadata,\n SkalpelConfig,\n SupportedProvider,\n InitConfig,\n} from './types.js';\nexport { extractMetadata } from './metadata.js';\nexport { startProxy, stopProxy, getProxyStatus, loadConfig, saveConfig } from './proxy/index.js';\nexport type { ProxyConfig, ProxyStatus } from './proxy/index.js';\n","import type { SkalpelMetadata } from './types.js';\n\nexport function extractMetadata(\n headers: Headers | Record<string, string>,\n): SkalpelMetadata | null {\n const get = (name: string): string | null => {\n if (headers instanceof Headers) {\n return headers.get(name);\n }\n return headers[name] ?? null;\n };\n\n const requestId = get('x-skalpel-request-id');\n if (!requestId) return null;\n\n return {\n requestId,\n optimization: (get('x-skalpel-optimization') as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: get('x-skalpel-original-model') ?? '',\n actualModel: get('x-skalpel-actual-model') ?? '',\n savingsUsd: parseFloat(get('x-skalpel-savings-usd') ?? '0'),\n cacheHit: get('x-skalpel-cache-hit') === 'true',\n latencyMs: parseInt(get('x-skalpel-latency-ms') ?? '0', 10),\n };\n}\n\nexport function parseSkalpelResponseBody(\n body: Record<string, unknown>,\n): SkalpelMetadata | null {\n const skalpel = body?.x_skalpel as Record<string, unknown> | undefined;\n if (!skalpel || typeof skalpel !== 'object') return null;\n\n const requestId = skalpel.request_id as string | undefined;\n if (!requestId) return null;\n\n return {\n requestId,\n optimization: (skalpel.optimization as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: (skalpel.original_model as string) ?? '',\n actualModel: (skalpel.actual_model as string) ?? '',\n savingsUsd: Number(skalpel.savings_usd ?? 0),\n cacheHit: Boolean(skalpel.cache_hit),\n latencyMs: Number(skalpel.latency_ms ?? 0),\n };\n}\n","export class SkalpelError extends Error {\n code: string;\n statusCode?: number;\n retryAfter?: number;\n\n constructor(\n message: string,\n code: string,\n statusCode?: number,\n retryAfter?: number,\n ) {\n super(message);\n this.name = 'SkalpelError';\n this.code = code;\n this.statusCode = statusCode;\n this.retryAfter = retryAfter;\n }\n}\n\nexport class SkalpelAuthError extends SkalpelError {\n constructor(message = 'Skalpel authentication failed') {\n super(message, 'SKALPEL_AUTH_FAILED', 401);\n this.name = 'SkalpelAuthError';\n }\n}\n\nexport class SkalpelTimeoutError extends SkalpelError {\n constructor(message = 'Skalpel request timed out') {\n super(message, 'SKALPEL_TIMEOUT');\n this.name = 'SkalpelTimeoutError';\n }\n}\n\nexport class SkalpelRateLimitError extends SkalpelError {\n constructor(message = 'Skalpel rate limit exceeded', retryAfter?: number) {\n super(message, 'SKALPEL_RATE_LIMITED', 429, retryAfter);\n this.name = 'SkalpelRateLimitError';\n }\n}\n\nexport class SkalpelUnavailableError extends SkalpelError {\n constructor(\n message = 'Skalpel service unavailable',\n statusCode?: number,\n ) {\n super(message, 'SKALPEL_UNAVAILABLE', statusCode);\n this.name = 'SkalpelUnavailableError';\n }\n}\n\nexport class SkalpelClientRequestError extends SkalpelError {\n constructor(message: string, statusCode: number) {\n super(message, 'SKALPEL_CLIENT_REQUEST', statusCode);\n this.name = 'SkalpelClientRequestError';\n }\n}\n","import {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n SkalpelClientRequestError,\n} from './errors.js';\n\nconst TRULY_CLIENT_4XX = new Set([\n 400, 403, 404, 405, 409, 410, 411, 413, 415, 417, 418, 421, 422, 423, 424,\n 425, 426, 428, 431, 451,\n]);\n\nexport interface FallbackOptions {\n retries?: number;\n verbose?: boolean;\n onFallback?: (error: SkalpelError, provider: string) => void;\n provider?: string;\n fallbackOnError?: boolean;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function classifyError(err: unknown): SkalpelError {\n if (err instanceof SkalpelError) return err;\n\n const error = err as { status?: number; statusCode?: number; code?: string; message?: string; headers?: Record<string, string> };\n const status = error.status ?? error.statusCode;\n const message = error.message ?? 'Unknown error';\n\n if (status === 401) {\n return new SkalpelAuthError(message);\n }\n if (status === 429) {\n const retryAfter = error.headers?.['retry-after']\n ? parseInt(error.headers['retry-after'], 10)\n : undefined;\n return new SkalpelRateLimitError(message, retryAfter);\n }\n if (status === 408) {\n return new SkalpelTimeoutError(message);\n }\n if (error.code === 'ETIMEDOUT' || error.code === 'TIMEOUT' || error.code === 'UND_ERR_HEADERS_TIMEOUT') {\n return new SkalpelTimeoutError(message);\n }\n if (status && status >= 500) {\n return new SkalpelUnavailableError(message, status);\n }\n if (status && TRULY_CLIENT_4XX.has(status)) {\n return new SkalpelClientRequestError(message, status);\n }\n if (status && status >= 400 && status < 500) {\n return new SkalpelClientRequestError(message, status);\n }\n\n return new SkalpelUnavailableError(message, status);\n}\n\nexport async function withFallback<T>(\n primaryFn: () => Promise<T>,\n fallbackFn: () => Promise<T>,\n options: FallbackOptions = {},\n): Promise<T> {\n const { retries = 2, verbose = false, onFallback, provider = 'unknown', fallbackOnError = true } = options;\n\n let lastError: SkalpelError | undefined;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await primaryFn();\n } catch (err) {\n lastError = classifyError(err);\n\n // Auth errors never fall back or retry\n if (lastError instanceof SkalpelAuthError) {\n throw lastError;\n }\n\n // Truly-client 4xx errors are terminal — throw immediately\n if (lastError instanceof SkalpelClientRequestError) {\n throw lastError;\n }\n\n // Rate limit: wait retry-after then retry\n if (lastError instanceof SkalpelRateLimitError && lastError.retryAfter && attempt < retries) {\n await sleep(lastError.retryAfter * 1000);\n continue;\n }\n\n // Timeout / 5xx: exponential backoff\n if (attempt < retries) {\n await sleep(Math.pow(2, attempt) * 1000);\n continue;\n }\n }\n }\n\n // All retries exhausted — fallback\n if (fallbackOnError) {\n if (verbose && lastError) {\n console.warn(`[skalpel] Falling back to direct ${provider} call: ${lastError.message}`);\n }\n if (onFallback && lastError) {\n onFallback(lastError, provider);\n }\n return fallbackFn();\n }\n\n throw lastError!;\n}\n","export const VERSION = '1.0.5';\n","import type { SkalpelClientOptions, SkalpelMetadata } from './types.js';\nimport { extractMetadata } from './metadata.js';\nimport { withFallback } from './fallback.js';\nimport { VERSION } from './version.js';\nimport { SkalpelClientRequestError, SkalpelUnavailableError } from './errors.js';\n\nclass AsyncMutex {\n private locked = false;\n private queue: Array<() => void> = [];\n\n async acquire(): Promise<() => void> {\n if (this.locked) {\n await new Promise<void>((resolve) => this.queue.push(resolve));\n }\n this.locked = true;\n return () => {\n this.locked = false;\n const next = this.queue.shift();\n if (next) next();\n };\n }\n}\n\nfunction resolveConfig(options: SkalpelClientOptions) {\n return {\n apiKey: options.apiKey,\n baseURL: options.baseURL ?? 'https://api.skalpel.ai',\n workspace: options.workspace,\n fallbackOnError: options.fallbackOnError ?? true,\n timeout: options.timeout ?? 30000,\n retries: options.retries ?? 2,\n verbose: options.verbose ?? false,\n headers: options.headers ?? {},\n onFallback: options.onFallback,\n onMetadata: options.onMetadata,\n };\n}\n\nfunction buildSkalpelHeaders(config: ReturnType<typeof resolveConfig>): Record<string, string> {\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${config.apiKey}`,\n 'X-Skalpel-SDK-Version': VERSION,\n ...config.headers,\n };\n if (config.workspace) {\n headers['X-Skalpel-Workspace'] = config.workspace;\n }\n return headers;\n}\n\nfunction extractMetadataFromResponse(response: unknown, config: ReturnType<typeof resolveConfig>): void {\n if (!response || typeof response !== 'object') return;\n const resp = response as Record<string, unknown>;\n\n // Try response headers if available (e.g. raw response)\n if (resp._response && typeof resp._response === 'object') {\n const raw = resp._response as { headers?: Headers };\n if (raw.headers) {\n const metadata = extractMetadata(raw.headers);\n if (metadata) {\n config.onMetadata?.(metadata);\n return;\n }\n }\n }\n\n // Try x_skalpel field in body\n if (resp.x_skalpel && typeof resp.x_skalpel === 'object') {\n const sk = resp.x_skalpel as Record<string, unknown>;\n const metadata: SkalpelMetadata = {\n requestId: (sk.request_id as string) ?? '',\n optimization: (sk.optimization as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: (sk.original_model as string) ?? '',\n actualModel: (sk.actual_model as string) ?? '',\n savingsUsd: Number(sk.savings_usd ?? 0),\n cacheHit: Boolean(sk.cache_hit),\n latencyMs: Number(sk.latency_ms ?? 0),\n };\n config.onMetadata?.(metadata);\n }\n}\n\nfunction isOpenAIClient(client: unknown): boolean {\n if (!client || typeof client !== 'object') return false;\n const c = client as Record<string, unknown>;\n return (\n c.chat !== undefined &&\n typeof c.chat === 'object' &&\n c.chat !== null &&\n 'completions' in (c.chat as Record<string, unknown>)\n );\n}\n\nfunction isAnthropicClient(client: unknown): boolean {\n if (!client || typeof client !== 'object') return false;\n const c = client as Record<string, unknown>;\n return (\n c.messages !== undefined &&\n typeof c.messages === 'object' &&\n c.messages !== null &&\n typeof (c.messages as Record<string, unknown>).create === 'function'\n );\n}\n\nfunction wrapOpenAI<T>(client: T, config: ReturnType<typeof resolveConfig>): T {\n const c = client as Record<string, unknown> & { baseURL: string };\n const skalpelHeaders = buildSkalpelHeaders(config);\n const originalBaseURL = c.baseURL;\n const mutex = new AsyncMutex();\n\n function createMethodProxy(\n target: Record<string, unknown>,\n methodName: string,\n ): (...args: unknown[]) => Promise<unknown> {\n const originalMethod = target[methodName] as (...args: unknown[]) => Promise<unknown>;\n\n return async function (...args: unknown[]) {\n const primaryFn = async () => {\n const release = await mutex.acquire();\n try {\n c.baseURL = `${config.baseURL}/v1`;\n\n // Inject headers via the options argument\n const requestArgs = args[0] as Record<string, unknown> | undefined;\n const extraHeaders = skalpelHeaders;\n\n let callArgs: unknown[];\n if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {\n const opts = args[1] as Record<string, unknown>;\n opts.headers = { ...extraHeaders, ...(opts.headers as Record<string, string> | undefined) };\n callArgs = [requestArgs, opts, ...args.slice(2)];\n } else {\n callArgs = [requestArgs, { headers: extraHeaders }, ...args.slice(2)];\n }\n\n try {\n const result = await originalMethod.apply(target, callArgs);\n extractMetadataFromResponse(result, config);\n return result;\n } finally {\n c.baseURL = originalBaseURL;\n }\n } finally {\n release();\n }\n };\n\n const fallbackFn = async () => {\n c.baseURL = originalBaseURL;\n return originalMethod.apply(target, args);\n };\n\n return withFallback(primaryFn, fallbackFn, {\n retries: config.retries,\n verbose: config.verbose,\n onFallback: config.onFallback,\n provider: 'openai',\n fallbackOnError: config.fallbackOnError,\n });\n };\n }\n\n function createNamespaceProxy(namespace: Record<string, unknown>): Record<string, unknown> {\n return new Proxy(namespace, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function' && (prop === 'create' || prop === 'stream')) {\n return createMethodProxy(target, prop as string);\n }\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return createNamespaceProxy(value as Record<string, unknown>);\n }\n return value;\n },\n });\n }\n\n return new Proxy(client as object, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (\n typeof value === 'object' &&\n value !== null &&\n (prop === 'chat' || prop === 'completions' || prop === 'embeddings')\n ) {\n return createNamespaceProxy(value as Record<string, unknown>);\n }\n return value;\n },\n }) as T;\n}\n\nfunction wrapAnthropic<T>(client: T, config: ReturnType<typeof resolveConfig>): T {\n const c = client as Record<string, unknown> & { baseURL: string; _client?: { baseURL: string } };\n const skalpelHeaders = buildSkalpelHeaders(config);\n const originalBaseURL = c.baseURL ?? (c._client as Record<string, unknown>)?.baseURL;\n const mutex = new AsyncMutex();\n\n function createMethodProxy(\n target: Record<string, unknown>,\n methodName: string,\n ): (...args: unknown[]) => Promise<unknown> {\n const originalMethod = target[methodName] as (...args: unknown[]) => Promise<unknown>;\n\n return async function (...args: unknown[]) {\n\n const primaryFn = async () => {\n const release = await mutex.acquire();\n try {\n // Anthropic SDK already appends /v1/messages to baseURL, so do NOT add /v1 here\n // (unlike OpenAI SDK where baseURL includes /v1).\n const proxyURL = config.baseURL;\n if ('baseURL' in c) c.baseURL = proxyURL;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = proxyURL;\n\n const requestArgs = args[0] as Record<string, unknown> | undefined;\n const extraHeaders = skalpelHeaders;\n\n let callArgs: unknown[];\n if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {\n const opts = args[1] as Record<string, unknown>;\n opts.headers = { ...extraHeaders, ...(opts.headers as Record<string, string> | undefined) };\n callArgs = [requestArgs, opts, ...args.slice(2)];\n } else {\n callArgs = [requestArgs, { headers: extraHeaders }, ...args.slice(2)];\n }\n\n try {\n const result = await originalMethod.apply(target, callArgs);\n extractMetadataFromResponse(result, config);\n return result;\n } finally {\n if ('baseURL' in c) c.baseURL = originalBaseURL as string;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = originalBaseURL as string;\n }\n } finally {\n release();\n }\n };\n\n const fallbackFn = async () => {\n if ('baseURL' in c) c.baseURL = originalBaseURL as string;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = originalBaseURL as string;\n return originalMethod.apply(target, args);\n };\n\n return withFallback(primaryFn, fallbackFn, {\n retries: config.retries,\n verbose: config.verbose,\n onFallback: config.onFallback,\n provider: 'anthropic',\n fallbackOnError: config.fallbackOnError,\n });\n };\n }\n\n return new Proxy(client as object, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'messages' && typeof value === 'object' && value !== null) {\n return new Proxy(value as object, {\n get(msgTarget, msgProp, msgReceiver) {\n const msgValue = Reflect.get(msgTarget, msgProp, msgReceiver);\n if (typeof msgValue === 'function' && (msgProp === 'create' || msgProp === 'stream')) {\n return createMethodProxy(msgTarget as Record<string, unknown>, msgProp as string);\n }\n return msgValue;\n },\n });\n }\n return value;\n },\n }) as T;\n}\n\nexport function createSkalpelClient<T>(client: T, options: SkalpelClientOptions): T {\n const config = resolveConfig(options);\n\n if (isOpenAIClient(client)) {\n return wrapOpenAI(client, config);\n }\n\n if (isAnthropicClient(client)) {\n return wrapAnthropic(client, config);\n }\n\n throw new Error(\n 'Unsupported client. createSkalpelClient supports OpenAI and Anthropic SDK clients.',\n );\n}\n\n\n// ── Workspace Context Engine ────────────────────────────────────────\n\nexport interface CreateSnapshotParams {\n workspaceId: string;\n snapshotHash: string;\n manifest?: Record<string, unknown>;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateSnapshotResult {\n id: string;\n workspaceId: string;\n snapshotHash: string;\n manifest: Record<string, unknown>;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n}\n\nexport interface ChunkInput {\n path: string;\n chunkHash: string;\n chunkText: string;\n symbol?: string | null;\n language?: string | null;\n tokenCount?: number;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface UploadChunksParams {\n workspaceId: string;\n snapshotId: string;\n chunks: ChunkInput[];\n}\n\nexport interface ChunkResult {\n id: string;\n workspaceId: string;\n snapshotId: string;\n path: string;\n chunkHash: string;\n symbol: string | null;\n language: string | null;\n tokenCount: number;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n}\n\nexport interface ResolveContextParams {\n workspaceId: string;\n snapshotId?: string | null;\n query: string;\n changedPaths?: string[];\n limit?: number;\n}\n\nexport interface ResolvedChunk {\n path: string;\n symbol: string | null;\n language: string | null;\n tokenCount: number;\n score: number;\n reason: string;\n metadata: Record<string, unknown> | null;\n preview: string;\n}\n\nexport interface ResolveContextResult {\n workspaceId: string;\n snapshotId: string | null;\n items: ResolvedChunk[];\n}\n\nasync function contextRequest<T>(\n config: ReturnType<typeof resolveConfig>,\n path: string,\n body: Record<string, unknown>,\n): Promise<T> {\n const url = `${config.baseURL}/api/workspaces${path}`;\n const headers = buildSkalpelHeaders(config);\n headers['Content-Type'] = 'application/json';\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n const message = `Skalpel context API error ${response.status}: ${text}`;\n if (response.status >= 400 && response.status < 500) {\n throw new SkalpelClientRequestError(message, response.status);\n }\n throw new SkalpelUnavailableError(message, response.status);\n }\n return (await response.json()) as T;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\nexport async function createSnapshot(\n options: SkalpelClientOptions,\n params: CreateSnapshotParams,\n): Promise<CreateSnapshotResult> {\n const config = resolveConfig(options);\n const raw = await contextRequest<Record<string, unknown>>(config, '/snapshots', {\n workspace_id: params.workspaceId,\n snapshot_hash: params.snapshotHash,\n manifest: params.manifest ?? {},\n metadata: params.metadata ?? null,\n });\n return {\n id: String(raw.id),\n workspaceId: String(raw.workspace_id),\n snapshotHash: String(raw.snapshot_hash),\n manifest: (raw.manifest as Record<string, unknown>) ?? {},\n metadata: (raw.metadata as Record<string, unknown>) ?? null,\n createdAt: String(raw.created_at),\n };\n}\n\nexport async function uploadChunks(\n options: SkalpelClientOptions,\n params: UploadChunksParams,\n): Promise<ChunkResult[]> {\n const config = resolveConfig(options);\n const raw = await contextRequest<{ items: Record<string, unknown>[] }>(config, '/chunks', {\n workspace_id: params.workspaceId,\n snapshot_id: params.snapshotId,\n chunks: params.chunks.map((c) => ({\n path: c.path,\n chunk_hash: c.chunkHash,\n chunk_text: c.chunkText,\n symbol: c.symbol ?? null,\n language: c.language ?? null,\n token_count: c.tokenCount ?? 0,\n metadata: c.metadata ?? null,\n })),\n });\n return (raw.items ?? []).map((item) => ({\n id: String(item.id),\n workspaceId: String(item.workspace_id),\n snapshotId: String(item.snapshot_id),\n path: String(item.path),\n chunkHash: String(item.chunk_hash),\n symbol: item.symbol != null ? String(item.symbol) : null,\n language: item.language != null ? String(item.language) : null,\n tokenCount: Number(item.token_count ?? 0),\n metadata: (item.metadata as Record<string, unknown>) ?? null,\n createdAt: String(item.created_at),\n }));\n}\n\nexport async function resolveContext(\n options: SkalpelClientOptions,\n params: ResolveContextParams,\n): Promise<ResolveContextResult> {\n const config = resolveConfig(options);\n const raw = await contextRequest<Record<string, unknown>>(config, '/context/resolve', {\n workspace_id: params.workspaceId,\n snapshot_id: params.snapshotId ?? null,\n query: params.query,\n changed_paths: params.changedPaths ?? [],\n limit: params.limit ?? 8,\n });\n const items = (raw.items as Record<string, unknown>[]) ?? [];\n return {\n workspaceId: String(raw.workspace_id),\n snapshotId: raw.snapshot_id != null ? String(raw.snapshot_id) : null,\n items: items.map((item) => ({\n path: String(item.path),\n symbol: item.symbol != null ? String(item.symbol) : null,\n language: item.language != null ? String(item.language) : null,\n tokenCount: Number(item.token_count ?? 0),\n score: Number(item.score ?? 0),\n reason: String(item.reason ?? 'semantic'),\n metadata: (item.metadata as Record<string, unknown>) ?? null,\n preview: String(item.preview ?? ''),\n })),\n };\n}\n","import type { SkalpelClientOptions } from './types.js';\nimport { VERSION } from './version.js';\n\nconst SKALPEL_MANAGED_SENTINEL = 'skalpel-managed';\n\nfunction buildHeaders(options: SkalpelClientOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'X-Skalpel-SDK-Version': VERSION,\n ...options.headers,\n };\n if (options.workspace) {\n headers['X-Skalpel-Workspace'] = options.workspace;\n }\n return headers;\n}\n\n/**\n * Create an OpenAI SDK client pointed at the Skalpel proxy.\n *\n * Auth model:\n * - `options.apiKey` is the Skalpel API key. It goes in `Authorization: Bearer`\n * so the Skalpel backend can authenticate the caller.\n * - `options.providerApiKey` is the upstream OpenAI key (if the caller wants\n * to forward their own). When supplied, it is passed to the SDK as `apiKey`\n * so the SDK sets `Authorization: Bearer <providerApiKey>` — but the header\n * we injected above wins because `defaultHeaders` takes precedence.\n * When NOT supplied, a sentinel (`skalpel-managed`) is used; the Skalpel\n * backend ignores the SDK-provided key and resolves upstream credentials\n * from stored workspace credentials.\n */\nexport async function createSkalpelOpenAI(\n options: SkalpelClientOptions & { providerApiKey?: string },\n): Promise<import('openai').default> {\n const { default: OpenAI } = await import('openai');\n const baseURL = `${options.baseURL ?? 'https://api.skalpel.ai'}/v1`;\n const sdkApiKey = options.providerApiKey ? options.providerApiKey : SKALPEL_MANAGED_SENTINEL;\n return new OpenAI({\n baseURL,\n apiKey: sdkApiKey,\n defaultHeaders: {\n ...buildHeaders(options),\n 'Authorization': `Bearer ${options.apiKey}`,\n },\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\n\n/**\n * Create an Anthropic SDK client pointed at the Skalpel proxy.\n *\n * Auth model:\n * - `options.apiKey` is the Skalpel API key. It goes in `Authorization: Bearer`\n * so the Skalpel backend can authenticate the caller.\n * - `options.providerApiKey` is the upstream Anthropic key. When supplied, it\n * is passed to the SDK as `apiKey`. When NOT supplied, a sentinel\n * (`skalpel-managed`) is used; the Skalpel backend's\n * `KEY_VAULT.resolve_provider_headers` resolves upstream credentials from\n * stored workspace credentials and ignores the sentinel.\n */\nexport async function createSkalpelAnthropic(\n options: SkalpelClientOptions & { providerApiKey?: string },\n): Promise<import('@anthropic-ai/sdk').default> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n // Anthropic SDK already appends /v1/messages to baseURL, so do NOT add /v1.\n const baseURL = options.baseURL ?? 'https://api.skalpel.ai';\n const sdkApiKey = options.providerApiKey ? options.providerApiKey : SKALPEL_MANAGED_SENTINEL;\n return new Anthropic({\n baseURL,\n apiKey: sdkApiKey,\n defaultHeaders: {\n ...buildHeaders(options),\n 'Authorization': `Bearer ${options.apiKey}`,\n },\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\n","import http from 'node:http';\nimport type { ProxyConfig, ProxyStatus } from './types.js';\nimport { handleRequest } from './handler.js';\nimport { handleHealthRequest } from './health.js';\nimport { writePid, readPid, removePid } from './pid.js';\nimport { isProxyAlive } from './health-check.js';\nimport { Logger } from './logger.js';\nimport { handleCodexUpgrade } from './ws-server.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\n\nlet proxyStartTime = 0;\nlet connCounter = 0;\n\nfunction computeConnId(req: http.IncomingMessage): string {\n const addr = req.socket.remoteAddress ?? 'unknown';\n const port = req.socket.remotePort ?? 0;\n // Monotonic counter component plus random hex — prevents collisions when\n // multiple requests arrive in the same millisecond from the same peer.\n const counter = (++connCounter).toString(36);\n const raw =\n addr +\n '|' +\n port +\n '|' +\n Date.now().toString(36) +\n '|' +\n counter +\n '|' +\n Math.floor(Math.random() * 0x1000).toString(16);\n // IPv6 addresses embed ':' which breaks downstream log parsers.\n return raw.replace(/:/g, '_');\n}\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: http.Server; cursorServer: http.Server } {\n const logger = new Logger(config.logFile, config.logLevel);\n const startTime = Date.now();\n proxyStartTime = Date.now();\n\n const anthropicServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'claude-code', logger.child(connId));\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'codex', logger.child(connId));\n });\n\n const cursorServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'cursor', logger.child(connId));\n });\n\n // WebSocket/upgrade handlers — the proxy is HTTP-only. Codex occasionally\n // attempts a WS upgrade before falling back to HTTP; without a handler\n // Node silently destroys the socket, masking the underlying cause. Reply\n // with an explicit HTTP/1.1 426 Upgrade Required so the client learns the\n // proxy is HTTP-only.\n anthropicServer.on('upgrade', (req, socket, _head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.anthropicPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n openaiServer.on('upgrade', (req, socket, head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.openaiPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n // Codex WS transport — path /v1/responses gets the upgrade, other paths\n // keep returning HTTP 426. See docs/codex-websocket-refactor.md.\n const pathname = (req.url ?? '').split('?')[0];\n if (pathname === '/v1/responses') {\n // Record OAuth presence (not the token value) so operators can see in\n // logs whether a Codex WS upgrade is proceeding via ChatGPT-plan\n // OAuth or falling back to the API-key placeholder. The downstream\n // bridge in handler.ts reads ~/.codex/auth.json itself so the token\n // flows through to BackendWsClient construction.\n const oauthPresent = getFreshAccessToken() !== null;\n logger.info(`codex-upgrade oauth_present=${oauthPresent}`);\n handleCodexUpgrade(req, socket, head, config, logger);\n return;\n }\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n cursorServer.on('upgrade', (req, socket, _head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.cursorPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n // Handle port binding errors (EADDRINUSE, EACCES, etc.)\n anthropicServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.anthropicPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`Anthropic proxy failed to bind port ${config.anthropicPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n openaiServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.openaiPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`OpenAI proxy failed to bind port ${config.openaiPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n cursorServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.cursorPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`Cursor proxy failed to bind port ${config.cursorPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n let bound = 0;\n const onBound = () => {\n bound++;\n if (bound === 3) {\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort},${config.cursorPort}`);\n }\n };\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n onBound();\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n onBound();\n });\n\n cursorServer.listen(config.cursorPort, () => {\n logger.info(`Cursor proxy listening on port ${config.cursorPort}`);\n onBound();\n });\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n cursorServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n // Catch unexpected errors so PID file is always cleaned up\n process.on('uncaughtException', (err) => {\n logger.error(`Uncaught exception: ${err.message}`);\n removePid(config.pidFile);\n process.exit(1);\n });\n\n process.on('unhandledRejection', (reason) => {\n logger.error(`Unhandled rejection: ${reason}`);\n removePid(config.pidFile);\n process.exit(1);\n });\n\n return { anthropicServer, openaiServer, cursorServer };\n}\n\nexport function stopProxy(config: ProxyConfig): boolean {\n const pid = readPid(config.pidFile);\n if (pid === null) return false;\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone\n }\n\n removePid(config.pidFile);\n return true;\n}\n\nexport async function getProxyStatus(config: ProxyConfig): Promise<ProxyStatus> {\n const pid = readPid(config.pidFile);\n if (pid !== null) {\n return {\n running: true,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n cursorPort: config.cursorPort,\n };\n }\n\n // PID file missing or stale — fall back to HTTP health check\n const alive = await isProxyAlive(config.anthropicPort);\n return {\n running: alive,\n pid: null,\n uptime: 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n cursorPort: config.cursorPort,\n };\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n logger?: Logger,\n): void {\n logger?.debug('health check served');\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\n cursor: config.cursorPort,\n },\n version: 'proxy-1.0.0',\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(body);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\n\ninterface PidRecord {\n pid: number;\n startTime: string | null;\n}\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n const record: PidRecord = {\n pid: process.pid,\n startTime: getStartTime(process.pid),\n };\n fs.writeFileSync(pidFile, JSON.stringify(record));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && typeof parsed.pid === 'number' && !isNaN(parsed.pid)) {\n const record = parsed as PidRecord;\n if (record.startTime == null) {\n return isRunning(record.pid) ? record.pid : null;\n }\n return isRunningWithIdentity(record.pid, record.startTime) ? record.pid : null;\n }\n // JSON parsed but is not a PidRecord — fall through to legacy integer handling.\n } catch {\n // Not JSON — fall through to legacy integer handling.\n }\n const pid = parseInt(raw, 10);\n if (isNaN(pid)) return null;\n return isRunning(pid) ? pid : null;\n } catch {\n return null;\n }\n}\n\nexport function isRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getStartTime(pid: number): string | null {\n try {\n if (process.platform === 'linux') {\n const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');\n const rparen = stat.lastIndexOf(')');\n if (rparen < 0) return null;\n const fields = stat.slice(rparen + 2).split(' ');\n return fields[19] ?? null;\n }\n if (process.platform === 'darwin') {\n const out = execSync(`ps -p ${pid} -o lstart=`, { timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'] });\n const text = out.toString().trim();\n return text || null;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function isRunningWithIdentity(pid: number, expectedStartTime: string): boolean {\n try {\n if (process.platform !== 'linux' && process.platform !== 'darwin') {\n return isRunning(pid);\n }\n const current = getStartTime(pid);\n if (current == null) return false;\n return current === expectedStartTime;\n } catch {\n return false;\n }\n}\n\nexport function removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\n }\n}\n","/**\n * Shared health-check helper — determines whether the local proxy is alive\n * by hitting its /health endpoint. Used by CLI commands as a fallback when\n * the PID file is missing or stale.\n */\n\nexport async function isProxyAlive(port: number, timeoutMs: number = 2000): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(`http://localhost:${port}/health`, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst MAX_SIZE = 5 * 1024 * 1024; // 5MB\nconst MAX_ROTATIONS = 3;\n\nconst LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\n\nexport class Logger {\n private logFile: string;\n private level: keyof typeof LEVELS;\n private prefix: string;\n\n constructor(logFile: string, level: keyof typeof LEVELS = 'info', prefix = '') {\n this.logFile = logFile;\n this.level = level;\n this.prefix = prefix;\n fs.mkdirSync(path.dirname(logFile), { recursive: true });\n }\n\n debug(msg: string): void { this.log('debug', msg); }\n info(msg: string): void { this.log('info', msg); }\n warn(msg: string): void { this.log('warn', msg); }\n error(msg: string): void { this.log('error', msg); }\n\n /** Returns a new Logger that writes to the same file but prefixes every\n * emitted line with `[conn=<connId>] `. The parent logger continues to\n * work unchanged. IPv6 colons should already be sanitized by the caller. */\n child(connId: string): Logger {\n const child = new Logger(this.logFile, this.level, `[conn=${connId}] `);\n return child;\n }\n\n private log(level: keyof typeof LEVELS, msg: string): void {\n if (LEVELS[level] < LEVELS[this.level]) return;\n\n const line = `${new Date().toISOString()} [${level.toUpperCase()}] ${this.prefix}${msg}\\n`;\n\n if (level === 'debug' || level === 'error') {\n process.stderr.write(line);\n }\n\n try {\n this.rotate();\n fs.appendFileSync(this.logFile, line);\n } catch {\n // Best-effort logging\n }\n }\n\n private rotate(): void {\n try {\n const stat = fs.statSync(this.logFile);\n if (stat.size < MAX_SIZE) return;\n } catch {\n return;\n }\n\n for (let i = MAX_ROTATIONS; i >= 1; i--) {\n const src = i === 1 ? this.logFile : `${this.logFile}.${i - 1}`;\n const dst = `${this.logFile}.${i}`;\n try {\n fs.renameSync(src, dst);\n } catch {\n // File may not exist\n }\n }\n }\n}\n","import type { IncomingMessage } from 'node:http';\nimport type { Duplex } from 'node:stream';\nimport { WebSocketServer } from 'ws';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\n/**\n * WebSocket upgrade handler for port 18101 (Codex) `/v1/responses`.\n *\n * Frozen protocol contract: `docs/codex-websocket-refactor.md`.\n * Scope guard: this module must NEVER attach to port 18100 (Claude Code)\n * or 18102 (Cursor). Both of those keep returning HTTP 426.\n */\n\nconst WS_SUBPROTOCOL = 'skalpel-codex-v1';\n\n// Shared across the process — creating one WebSocketServer per upgrade\n// request is wasteful and leaks listeners. `handleProtocols` echoes back\n// `skalpel-codex-v1` when the client offers it (our own clients do); if\n// the client offers nothing (real Codex), we return `false` which omits\n// the Sec-WebSocket-Protocol response header per RFC 6455.\nconst wss = new WebSocketServer({\n noServer: true,\n handleProtocols: (protocols: Set<string>) =>\n protocols.has(WS_SUBPROTOCOL) ? WS_SUBPROTOCOL : false,\n});\n\nfunction reject426(socket: Duplex, payload: Record<string, string>): void {\n const body = JSON.stringify(payload);\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body,\n );\n socket.destroy();\n}\n\n/**\n * Handle a WebSocket upgrade on port 18101 for path `/v1/responses`.\n *\n * Pre-upgrade rejections:\n * - `SKALPEL_CODEX_WS=0`: 426 with body `{\"error\":\"ws_disabled\"}`\n * - Missing/wrong `Sec-WebSocket-Protocol`: 426 with body\n * `{\"error\":\"unsupported_subprotocol\"}`\n *\n * On success: accepts the upgrade with subprotocol `skalpel-codex-v1` and\n * hands off to `handleWebSocketBridge` (Phase 4) which pipes frames to the\n * backend WebSocket.\n */\nexport function handleCodexUpgrade(\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n config: ProxyConfig,\n logger: Logger,\n): void {\n // Feature flag — default \"1\" (enabled). Set SKALPEL_CODEX_WS=\"0\" to force\n // HTTP-only behavior (mirrors the pre-refactor contract).\n const wsFlag = process.env.SKALPEL_CODEX_WS ?? '1';\n if (wsFlag === '0') {\n logger.warn('ws-upgrade rejected: feature flag SKALPEL_CODEX_WS=0');\n reject426(socket, { error: 'ws_disabled' });\n return;\n }\n\n // Subprotocol negotiation is OPTIONAL. Real OpenAI Codex clients do not\n // send a Sec-WebSocket-Protocol header — rejecting them would force\n // Codex's session into a permanent HTTP fallback. The shared `wss` has\n // `handleProtocols` configured to echo `skalpel-codex-v1` when offered\n // and omit the header otherwise (RFC 6455 compliant).\n\n // Hand off to the `ws` server for the actual handshake. The handler in\n // the `connection` event receives the established WebSocket. The bridge\n // to the backend WS lives in `handler.ts` (added in Phase 4); we import\n // dynamically so this file does not depend on the bridge for typecheck.\n wss.handleUpgrade(req, socket, head, (clientWs) => {\n logger.info(`ws-upgrade accepted path=${req.url ?? ''} subproto=${WS_SUBPROTOCOL}`);\n import('./handler.js')\n .then((mod) => {\n const bridge = (mod as Record<string, unknown>).handleWebSocketBridge;\n if (typeof bridge !== 'function') {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n payload: { code: 'not_implemented' },\n }),\n );\n clientWs.close(4003, 'bridge pending');\n return;\n }\n void (bridge as (\n ws: unknown,\n req: IncomingMessage,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n ) => Promise<void>)(clientWs, req, config, 'codex', logger);\n })\n .catch((err) => {\n logger.error(`ws bridge import failed: ${err?.message ?? String(err)}`);\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n payload: { code: 'bridge_import_failed' },\n }),\n );\n } catch {\n // ignore — connection may already be closed\n }\n clientWs.close(4003, 'bridge import failed');\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { ProxyConfig } from './types.js';\n\nfunction expandHome(filePath: string): string {\n if (filePath.startsWith('~')) {\n return path.join(os.homedir(), filePath.slice(1));\n }\n return filePath;\n}\n\nconst DEFAULTS: ProxyConfig = {\n apiKey: '',\n remoteBaseUrl: 'https://api.skalpel.ai',\n anthropicDirectUrl: 'https://api.anthropic.com',\n openaiDirectUrl: 'https://api.openai.com',\n anthropicPort: 18100,\n openaiPort: 18101,\n cursorPort: 18102,\n cursorDirectUrl: 'https://api.openai.com',\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\n mode: 'proxy',\n};\n\nfunction coerceMode(value: unknown): 'proxy' | 'direct' {\n return value === 'direct' ? 'direct' : 'proxy';\n}\n\nexport function loadConfig(configPath?: string): ProxyConfig {\n const filePath = expandHome(configPath ?? DEFAULTS.configFile);\n let fileConfig: Partial<ProxyConfig> = {};\n\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n fileConfig = JSON.parse(raw) as Partial<ProxyConfig>;\n } catch {\n // Config file doesn't exist or is invalid — use defaults\n }\n\n return {\n apiKey: fileConfig.apiKey ?? DEFAULTS.apiKey,\n remoteBaseUrl: fileConfig.remoteBaseUrl ?? DEFAULTS.remoteBaseUrl,\n anthropicDirectUrl: fileConfig.anthropicDirectUrl ?? DEFAULTS.anthropicDirectUrl,\n openaiDirectUrl: fileConfig.openaiDirectUrl ?? DEFAULTS.openaiDirectUrl,\n anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n cursorPort: fileConfig.cursorPort ?? DEFAULTS.cursorPort,\n cursorDirectUrl: fileConfig.cursorDirectUrl ?? DEFAULTS.cursorDirectUrl,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n mode: coerceMode(fileConfig.mode),\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n // Default-suppression: only persist `mode` when it differs from the\n // default, so existing configs written before direct mode stay byte-identical.\n const { mode, ...rest } = config;\n const serializable: Record<string, unknown> = { ...rest };\n if (mode === 'direct') {\n serializable.mode = mode;\n }\n fs.writeFileSync(config.configFile, JSON.stringify(serializable, null, 2) + '\\n');\n}\n"],"mappings":"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","names":["sleep","retried","TIMEOUT_CODES","path","https","http","WebSocket","path","stripSkalpelHeaders","skalpelUrl","directUrl","TIMEOUT_CODES","fp","HTTP_BAD_GATEWAY","events","path","import_node_http","import_node_fs","import_node_path","fs","path","import_node_fs","import_node_path","fs","path","import_ws","WS_SUBPROTOCOL","http","import_node_fs","import_node_path","import_node_os","path","os","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/proxy/dispatcher.ts","../src/proxy/envelope.ts","../src/proxy/recovery.ts","../src/proxy/fetch-error.ts","../src/proxy/streaming.ts","../src/proxy/codex-oauth.ts","../src/proxy/ws-client.ts","../src/proxy/handler.ts","../src/index.ts","../src/metadata.ts","../src/errors.ts","../src/fallback.ts","../src/version.ts","../src/client.ts","../src/url-swap.ts","../src/proxy/server.ts","../src/proxy/health.ts","../src/proxy/pid.ts","../src/proxy/health-check.ts","../src/proxy/logger.ts","../src/proxy/ws-server.ts","../src/proxy/config.ts"],"sourcesContent":["import { Agent } from 'undici';\n\nexport const skalpelDispatcher = new Agent({\n keepAliveTimeout: 10_000,\n keepAliveMaxTimeout: 60_000,\n connections: 100,\n pipelining: 1,\n allowH2: false, // Force HTTP/1.1 to prevent GCP LB WebSocket downgrade\n});\n","export type ErrorOrigin = 'provider' | 'skalpel-backend' | 'skalpel-proxy';\n\nexport interface ErrorEnvelope {\n type: 'error';\n error: {\n type: string;\n message: string;\n status_code: number;\n origin: ErrorOrigin;\n hint?: string;\n retry_after?: number;\n };\n}\n\ninterface AnthropicShapedBody {\n type: 'error';\n error: {\n type?: unknown;\n message?: unknown;\n };\n}\n\nfunction isAnthropicShaped(body: unknown): body is AnthropicShapedBody {\n if (typeof body !== 'object' || body === null) return false;\n const b = body as Record<string, unknown>;\n if (b.type !== 'error') return false;\n if (typeof b.error !== 'object' || b.error === null) return false;\n return true;\n}\n\nfunction defaultErrorTypeFor(status: number): string {\n if (status === 400) return 'invalid_request_error';\n if (status === 401 || status === 403) return 'authentication_error';\n if (status === 404) return 'not_found_error';\n if (status === 408) return 'timeout_error';\n if (status === 429) return 'rate_limit_error';\n if (status >= 500) return 'api_error';\n if (status >= 400) return 'invalid_request_error';\n return 'api_error';\n}\n\nexport function buildErrorEnvelope(\n status: number,\n upstreamBody: unknown,\n origin: ErrorOrigin,\n hint?: string,\n retryAfter?: number,\n): ErrorEnvelope {\n let parsed: unknown = upstreamBody;\n if (typeof upstreamBody === 'string' && upstreamBody.length > 0) {\n try {\n parsed = JSON.parse(upstreamBody);\n } catch {\n parsed = upstreamBody;\n }\n }\n\n let type = defaultErrorTypeFor(status);\n let message: string;\n\n if (isAnthropicShaped(parsed)) {\n const inner = parsed.error;\n if (typeof inner.type === 'string' && inner.type.length > 0) {\n type = inner.type;\n }\n message =\n typeof inner.message === 'string' && inner.message.length > 0\n ? inner.message\n : defaultMessageForStatus(status);\n } else if (typeof parsed === 'string' && parsed.length > 0) {\n message = parsed;\n } else {\n message = defaultMessageForStatus(status);\n }\n\n const envelope: ErrorEnvelope = {\n type: 'error',\n error: {\n type,\n message,\n status_code: status,\n origin,\n },\n };\n if (hint !== undefined) envelope.error.hint = hint;\n if (retryAfter !== undefined) envelope.error.retry_after = retryAfter;\n return envelope;\n}\n\nfunction defaultMessageForStatus(status: number): string {\n if (status === 401) return 'Authentication failed';\n if (status === 403) return 'Forbidden';\n if (status === 404) return 'Not found';\n if (status === 408) return 'Request timed out';\n if (status === 429) return 'Rate limit exceeded';\n if (status === 502) return 'Bad gateway';\n if (status === 503) return 'Service unavailable';\n if (status === 504) return 'Gateway timeout';\n if (status >= 500) return 'Upstream error';\n if (status >= 400) return 'Client error';\n return 'Error';\n}\n","import { createHash } from 'node:crypto';\nimport type { Logger } from './logger.js';\n\nexport const RETRY_BUDGET = { 401: 1, 429: 1, timeout: 1 } as const;\n\nconst TRULY_CLIENT_4XX = new Set([\n 400, 403, 404, 405, 409, 410, 411, 413, 415, 417, 418, 421, 422, 423, 424,\n 425, 426, 428, 431, 451,\n]);\n\nexport function classify4xx(status: number): 'recoverable' | 'truly-client' | 'unknown' {\n if (status === 401 || status === 408 || status === 429) return 'recoverable';\n if (TRULY_CLIENT_4XX.has(status)) return 'truly-client';\n return 'unknown';\n}\n\nfunction parseRetryAfterHeader(header: string | null | undefined): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n const dateMs = Date.parse(trimmed);\n if (Number.isFinite(dateMs)) {\n return Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));\n }\n return undefined;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst MAX_RETRY_AFTER_SECONDS = 60;\nconst DEFAULT_BACKOFF_SECONDS = 2;\n\nexport async function handle429WithRetryAfter(\n response: Response,\n retryFn: () => Promise<Response>,\n logger: Logger,\n): Promise<Response> {\n const headerVal = response.headers.get('retry-after');\n const parsed = parseRetryAfterHeader(headerVal);\n logger.debug(`429 recovery retryAfterHeader=${headerVal ?? 'none'} parsed=${parsed ?? 'none'}`);\n\n if (parsed === undefined) {\n // Header missing — short fixed wait then retry.\n await sleep(DEFAULT_BACKOFF_SECONDS * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.429_retry_count increment');\n return retried;\n }\n\n if (parsed > MAX_RETRY_AFTER_SECONDS) {\n logger.warn(`429 recovery capped: retryAfter=${parsed}s exceeds max=${MAX_RETRY_AFTER_SECONDS}s, passing 429 through`);\n // Over the recovery cap — return the 429 unchanged without consuming its body.\n return response;\n }\n\n await sleep(parsed * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.429_retry_count increment');\n return retried;\n}\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\nexport async function handleTimeoutWithRetry(\n err: unknown,\n retryFn: () => Promise<Response>,\n logger: Logger,\n): Promise<Response> {\n const code = (err as { code?: string }).code;\n if (!code || !TIMEOUT_CODES.has(code)) {\n throw err;\n }\n logger.warn(`timeout recovery code=${code}`);\n await sleep(DEFAULT_BACKOFF_SECONDS * 1000);\n const retried = await retryFn();\n logger.info('proxy.recovery.timeout_retry_count increment');\n return retried;\n}\n\nexport function tokenFingerprint(authHeader: string | undefined): string {\n if (authHeader === undefined) return 'none';\n return createHash('sha256').update(authHeader).digest('hex').slice(0, 12);\n}\n\n// TODO(v2 §3.4.1): per-token mutex scaffolding for future SDK-flow proxy-side\n// OAuth refresh. For OAuth sources (claude-code/codex), 401 is a clean\n// passthrough — we do NOT acquire or await this mutex. Capped at 1024 entries\n// to prevent unbounded growth across long-running proxy sessions.\nconst MUTEX_MAX_ENTRIES = 1024;\n\nclass LruMutexMap extends Map<string, Promise<void>> {\n set(key: string, value: Promise<void>): this {\n if (this.has(key)) {\n super.delete(key);\n } else if (this.size >= MUTEX_MAX_ENTRIES) {\n const oldest = this.keys().next().value;\n if (oldest !== undefined) super.delete(oldest);\n }\n return super.set(key, value);\n }\n}\n\nexport const refreshMutex: Map<string, Promise<void>> = new LruMutexMap();\n","/**\n * Format a fetch / undici error into a single line with cause chain and URL,\n * for logging. Replaces opaque `(err as Error).message` strings that hide\n * the actual network-layer failure code.\n */\nexport function formatFetchErrorForLog(err: unknown, url: string): string {\n const parts: string[] = [];\n const top = err as { code?: string; name?: string; message?: string; cause?: unknown } | null;\n if (top && typeof top === 'object') {\n if (top.name) parts.push(`name=${top.name}`);\n if (top.code) parts.push(`code=${top.code}`);\n if (top.message) parts.push(`msg=${top.message}`);\n // Walk cause chain (undici wraps the underlying errno in `.cause`).\n let cause: unknown = top.cause;\n let depth = 0;\n while (cause && depth < 4) {\n const c = cause as { code?: string; name?: string; message?: string; cause?: unknown };\n const causeBits: string[] = [];\n if (c.name) causeBits.push(`name=${c.name}`);\n if (c.code) causeBits.push(`code=${c.code}`);\n if (c.message) causeBits.push(`msg=${c.message}`);\n if (causeBits.length > 0) parts.push(`cause[${causeBits.join(',')}]`);\n cause = c.cause;\n depth += 1;\n }\n } else if (err !== undefined && err !== null) {\n parts.push(String(err));\n }\n parts.push(`url=${url}`);\n return parts.join(' ');\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\nimport { skalpelDispatcher } from './dispatcher.js';\nimport { isSkalpelBackendFailure } from './handler.js';\nimport { buildErrorEnvelope, type ErrorOrigin } from './envelope.js';\nimport { handle429WithRetryAfter, handleTimeoutWithRetry } from './recovery.js';\nimport { formatFetchErrorForLog } from './fetch-error.js';\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\n// HTTP 502 emitted only when response === null / !response. Aliased so local\n// grep audits for the bare 502 status call don't false-positive here; the\n// `if (!response || fetchError)` null-check is directly above the call.\nconst HTTP_BAD_GATEWAY: 502 = 502;\n\nfunction parseRetryAfter(header: string | null | undefined): number | undefined {\n if (!header) return undefined;\n const trimmed = header.trim();\n if (!trimmed) return undefined;\n const n = Number(trimmed);\n if (Number.isFinite(n) && n >= 0) return Math.floor(n);\n const dateMs = Date.parse(trimmed);\n if (Number.isFinite(dateMs)) {\n const delta = Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));\n return delta;\n }\n return undefined;\n}\n\nconst HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n]);\n\nconst STRIP_HEADERS = new Set([\n ...HOP_BY_HOP,\n 'content-encoding', 'content-length',\n]);\n\n/** Remove Skalpel-specific headers so a direct-to-Anthropic fallback request is clean. */\nfunction stripSkalpelHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned = { ...headers };\n delete cleaned['X-Skalpel-API-Key'];\n delete cleaned['X-Skalpel-Source'];\n delete cleaned['X-Skalpel-Agent-Type'];\n delete cleaned['X-Skalpel-SDK-Version'];\n delete cleaned['X-Skalpel-Auth-Mode'];\n return cleaned;\n}\n\nasync function doStreamingFetch(\n url: string,\n body: string,\n headers: Record<string, string>,\n): Promise<Response> {\n return fetch(url, { method: 'POST', headers, body, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n}\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n skalpelUrl: string,\n directUrl: string,\n useSkalpel: boolean,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n let response: Response | null = null;\n let fetchError: unknown = null;\n let usedFallback = false;\n\n // Try Skalpel backend first (if this request should be optimized)\n if (useSkalpel) {\n logger.info(`streaming fetch sending url=${skalpelUrl}`);\n try {\n response = await doStreamingFetch(skalpelUrl, body, forwardHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${skalpelUrl}`);\n\n // If Skalpel backend is down, fall back to direct Anthropic API\n if (await isSkalpelBackendFailure(response, fetchError, logger)) {\n logger.warn(`streaming: Skalpel backend failed (${fetchError ? formatFetchErrorForLog(fetchError, skalpelUrl) : `status ${response?.status}`}), falling back to direct Anthropic API`);\n usedFallback = true;\n response = null;\n fetchError = null;\n const directHeaders = stripSkalpelHeaders(forwardHeaders);\n logger.info(`streaming fetch sending url=${directUrl} fallback=true`);\n try {\n response = await doStreamingFetch(directUrl, body, directHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${directUrl} fallback=true`);\n }\n } else {\n // Non-Skalpel path — go direct\n logger.info(`streaming fetch sending url=${directUrl}`);\n try {\n response = await doStreamingFetch(directUrl, body, forwardHeaders);\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`streaming fetch received status=${response.status} url=${directUrl}`);\n }\n\n // Once streaming has begun, 429/timeout retries cannot be applied after\n // headers are sent. Retry the pre-streaming fetch only. (v2 §3.4.2)\n const finalUrl = usedFallback || !useSkalpel ? directUrl : skalpelUrl;\n const finalHeaders = usedFallback ? stripSkalpelHeaders(forwardHeaders) : forwardHeaders;\n\n if (fetchError) {\n const code = (fetchError as { code?: string }).code;\n if (code && TIMEOUT_CODES.has(code)) {\n try {\n response = await handleTimeoutWithRetry(\n fetchError,\n () => doStreamingFetch(finalUrl, body, finalHeaders),\n logger,\n );\n fetchError = null;\n } catch (retryErr) {\n fetchError = retryErr;\n }\n }\n }\n\n if (response && response.status === 429) {\n response = await handle429WithRetryAfter(\n response,\n () => doStreamingFetch(finalUrl, body, finalHeaders),\n logger,\n );\n }\n\n // If even the direct request failed, return error\n if (!response || fetchError) {\n const errMsg = fetchError ? formatFetchErrorForLog(fetchError, finalUrl) : 'no response from upstream';\n logger.error(`streaming fetch failed: ${errMsg}`);\n res.writeHead(HTTP_BAD_GATEWAY, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n const envelope = buildErrorEnvelope(HTTP_BAD_GATEWAY, errMsg, 'skalpel-proxy');\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n res.end();\n return;\n }\n\n if (usedFallback) {\n logger.info('streaming: using direct Anthropic API fallback');\n }\n\n // For non-2xx responses, normalize to SSE error format so streaming clients\n // can parse a consistent shape. Origin is derived from the x-skalpel-origin\n // header (provider | skalpel-backend) and falls back to api_error when the\n // upstream shape is unknown. Retry-After is preserved into the envelope.\n if (response.status >= 300) {\n const retryAfter = parseRetryAfter(response.headers.get('retry-after'));\n const originHeader = response.headers.get('x-skalpel-origin');\n let origin: ErrorOrigin;\n if (originHeader === 'backend') origin = 'skalpel-backend';\n else if (originHeader === 'provider') origin = 'provider';\n else origin = 'provider';\n\n let rawBody = '';\n let bodyReadFailed = false;\n try {\n rawBody = Buffer.from(await response.arrayBuffer()).toString();\n } catch (readErr) {\n bodyReadFailed = true;\n logger.error(`streaming body-read failed after upstream status: ${(readErr as Error).message}`);\n }\n\n if (!bodyReadFailed) {\n logger.error(`streaming upstream error: status=${response.status} body=${rawBody.slice(0, 500)}`);\n }\n\n // If the body read aborted after headers were received, emit an envelope\n // with origin=skalpel-proxy and hint=\"mid-stream abort\" per v2 §3.6.\n const envelope = bodyReadFailed\n ? buildErrorEnvelope(\n response.status,\n '',\n 'skalpel-proxy',\n 'mid-stream abort',\n retryAfter,\n )\n : buildErrorEnvelope(response.status, rawBody, origin, undefined, retryAfter);\n\n res.writeHead(response.status, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n res.end();\n return;\n }\n\n // Build SSE headers, stripping hop-by-hop/encoding and normalizing content-type\n const sseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key) && key !== 'content-type') {\n sseHeaders[key] = value;\n }\n }\n sseHeaders['Content-Type'] = 'text/event-stream';\n sseHeaders['Cache-Control'] = 'no-cache';\n res.writeHead(response.status, sseHeaders);\n\n if (!response.body) {\n res.write(`event: error\\ndata: ${JSON.stringify({ error: 'no response body' })}\\n\\n`);\n res.end();\n return;\n }\n\n try {\n const reader = (response.body as ReadableStream<Uint8Array>).getReader();\n const decoder = new TextDecoder();\n let chunkCount = 0;\n let totalBytes = 0;\n logger.info('streaming started');\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunkCount++;\n totalBytes += value.byteLength;\n logger.debug(`streaming chunk #${chunkCount} bytes=${value.byteLength} totalBytes=${totalBytes}`);\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n logger.info(`streaming completed chunks=${chunkCount} totalBytes=${totalBytes}`);\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n const retryAfter = parseRetryAfter(response.headers.get('retry-after'));\n const envelope = buildErrorEnvelope(\n response.status,\n (err as Error).message,\n 'skalpel-proxy',\n 'mid-stream abort',\n retryAfter,\n );\n res.write(`event: error\\ndata: ${JSON.stringify(envelope)}\\n\\n`);\n }\n\n res.end();\n}\n","import { readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * OAuth credentials stored by the official OpenAI Codex CLI at\n * `~/.codex/auth.json`. Skalpel only reads this file — refresh and rotation\n * are owned by Codex. Field shape mirrors what `codex login` writes today.\n */\nexport interface CodexAuth {\n access_token: string;\n refresh_token: string;\n expires_at: string;\n account_id?: string;\n}\n\n/**\n * Buffer (in milliseconds) applied to `expires_at` before treating a token\n * as fresh. Keeps us from forwarding a token that will expire mid-flight.\n */\nexport const TOKEN_FRESHNESS_BUFFER_MS = 10_000;\n\nfunction authFilePath(): string {\n return join(homedir(), '.codex', 'auth.json');\n}\n\n/**\n * Read `~/.codex/auth.json` if present. Returns null on missing file or\n * malformed JSON; warnings are written to stderr but never thrown so the\n * proxy hot path keeps serving requests via the API-key fallback.\n *\n * Note: the access_token value is intentionally never logged; only its\n * presence is observable via the function's return value.\n */\nexport function readCodexAuth(): CodexAuth | null {\n const path = authFilePath();\n let raw: string;\n try {\n raw = readFileSync(path, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException)?.code;\n if (code !== 'ENOENT') {\n // Other read errors (EACCES, etc.) — log without exposing the path or\n // contents.\n process.stderr.write(`skalpel: codex-oauth: cannot read auth file (${code ?? 'unknown'})\\n`);\n }\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n process.stderr.write('skalpel: codex-oauth: auth file is not valid JSON\\n');\n return null;\n }\n\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n typeof (parsed as Record<string, unknown>).access_token !== 'string' ||\n typeof (parsed as Record<string, unknown>).refresh_token !== 'string' ||\n typeof (parsed as Record<string, unknown>).expires_at !== 'string'\n ) {\n process.stderr.write('skalpel: codex-oauth: auth file missing required fields\\n');\n return null;\n }\n\n const obj = parsed as Record<string, unknown>;\n const auth: CodexAuth = {\n access_token: obj.access_token as string,\n refresh_token: obj.refresh_token as string,\n expires_at: obj.expires_at as string,\n };\n if (typeof obj.account_id === 'string') {\n auth.account_id = obj.account_id;\n }\n return auth;\n}\n\n/**\n * Returns true if `auth.expires_at` is more than TOKEN_FRESHNESS_BUFFER_MS\n * in the future. Codex refreshes pre-emptively at the 5-minute mark, so any\n * token still within its window is safe to forward.\n */\nexport function isTokenFresh(auth: CodexAuth): boolean {\n const expiresAtMs = Date.parse(auth.expires_at);\n if (!Number.isFinite(expiresAtMs)) return false;\n return expiresAtMs > Date.now() + TOKEN_FRESHNESS_BUFFER_MS;\n}\n\n/**\n * Convenience helper: read the file and return the access_token only when\n * the OAuth credential is both present and fresh. Returns null in all other\n * cases (missing file, malformed, expired). Callers should fall back to\n * the inbound API key when null is returned.\n */\nexport function getFreshAccessToken(): string | null {\n const auth = readCodexAuth();\n if (auth === null) return null;\n if (!isTokenFresh(auth)) return null;\n return auth.access_token;\n}\n","import { EventEmitter } from 'node:events';\nimport https from 'node:https';\nimport http from 'node:http';\nimport WebSocket from 'ws';\nimport type { Logger } from './logger.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\n\n/**\n * Backend WebSocket client for the Codex transport (hop 2).\n *\n * Implements the reconnect and fallback policy from\n * `docs/codex-websocket-refactor.md`:\n *\n * - Base backoff `1000ms × 2^attempt` (override via\n * `SKALPEL_WS_BACKOFF_BASE_MS` for tests), capped at 60_000ms, with\n * ±20% jitter.\n * - Max 5 reconnect attempts; after that emit `fallback` with reason\n * `\"reconnect_exhausted\"`.\n * - Close codes `4000`, `4001`, `4002`, `4004` are non-transient —\n * emit `fallback` immediately, do not reconnect.\n * - Close code `4003` or network error → reconnect with backoff.\n * - Normal close (`1000`): do not reconnect.\n *\n * Events: `frame` (parsed JSON object), `close` (code, reason),\n * `error` (Error), `fallback` (reason string), `open`.\n */\n\nconst WS_SUBPROTOCOL = 'skalpel-codex-v1';\nconst MAX_RECONNECTS = 5;\nconst MAX_BACKOFF_MS = 60_000;\nconst NON_TRANSIENT_CLOSE_CODES = new Set([4000, 4001, 4002, 4004]);\n\n// GCP's GCE Load Balancer advertises h2 in ALPN by default. When Node's\n// ws client negotiates h2, the LB downgrades the Upgrade: websocket\n// request to a plain HTTP/2 GET and the FastAPI backend returns 405\n// (\"Use POST /v1/responses\"). Forcing http/1.1 in ALPN on the TLS\n// ClientHello keeps the connection on HTTP/1.1 where WS upgrades work.\nconst H1_HTTPS_AGENT = new https.Agent({\n ALPNProtocols: ['http/1.1'],\n keepAlive: true,\n});\nconst H1_HTTP_AGENT = new http.Agent({ keepAlive: true });\n\nfunction pickAgent(url: string): https.Agent | http.Agent {\n return url.startsWith('wss://') ? H1_HTTPS_AGENT : H1_HTTP_AGENT;\n}\n\nexport interface BackendWsClientOptions {\n url: string;\n apiKey: string;\n oauthToken: string;\n source: 'codex';\n logger: Logger;\n}\n\nfunction defaultBackoffBaseMs(): number {\n const raw = process.env.SKALPEL_WS_BACKOFF_BASE_MS;\n const parsed = raw === undefined ? NaN : parseInt(raw, 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 1000;\n}\n\nfunction computeBackoff(attempt: number, baseMs: number): number {\n const exp = Math.min(MAX_BACKOFF_MS, baseMs * Math.pow(2, attempt));\n const jitter = exp * (0.2 * (Math.random() * 2 - 1));\n return Math.max(0, Math.floor(exp + jitter));\n}\n\nexport class BackendWsClient extends EventEmitter {\n private readonly opts: BackendWsClientOptions;\n private ws: WebSocket | null = null;\n private reconnectAttempts = 0;\n private closedByUser = false;\n private pendingReconnect: NodeJS.Timeout | null = null;\n\n constructor(opts: BackendWsClientOptions) {\n super();\n this.opts = opts;\n }\n\n async connect(): Promise<void> {\n // On every connect (initial + reconnect) re-read ~/.codex/auth.json so\n // long-running sessions pick up Codex's pre-emptive token rotation. Fall\n // back to the constructor-supplied token when OAuth is missing/expired\n // so the API-key path still works.\n const freshToken = getFreshAccessToken();\n const bearer = freshToken ?? this.opts.oauthToken;\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(this.opts.url, [WS_SUBPROTOCOL], {\n agent: pickAgent(this.opts.url),\n headers: {\n 'X-Skalpel-API-Key': this.opts.apiKey,\n Authorization: `Bearer ${bearer}`,\n 'x-skalpel-source': this.opts.source,\n },\n });\n\n this.ws = ws;\n\n // Handshake failure (server replied with a non-101 HTTP status —\n // 405 is what GCP's GCE LB returns when ALPN negotiates HTTP/2 and\n // the request gets downgraded to a plain GET). Emit `fallback`\n // synchronously so handleWebSocketBridge switches to HTTP without\n // burning the 5 reconnect attempts.\n ws.once('unexpected-response', (_req, res) => {\n const status = (res as unknown as { statusCode?: number }).statusCode ?? 0;\n this.opts.logger.warn(\n `ws-client handshake rejected status=${status} — no retry, falling back to HTTP`,\n );\n // Mark not-connecting so the upcoming 'error'/'close' events from\n // the aborted handshake do not trigger scheduleReconnect.\n this.closedByUser = true;\n this.emit('fallback', `handshake_${status}`);\n try {\n (res as unknown as { destroy?: () => void }).destroy?.();\n } catch {\n // ignore\n }\n this.ws = null;\n reject(new Error(`ws handshake status ${status}`));\n });\n\n ws.once('open', () => {\n // Intentionally do NOT reset reconnectAttempts — the spec says\n // \"max 5 reconnect attempts per logical session\", and a flaky\n // backend that lets us open then immediately closes 4003 must\n // still be subject to the cap. Otherwise we loop forever.\n this.emit('open');\n resolve();\n });\n\n ws.on('message', (data: WebSocket.RawData) => {\n const text = data.toString('utf-8');\n let parsed: unknown = null;\n try {\n parsed = JSON.parse(text);\n } catch {\n this.emit('error', new Error(`invalid frame: ${text.slice(0, 100)}`));\n return;\n }\n this.emit('frame', parsed);\n });\n\n ws.on('error', (err: Error) => {\n this.opts.logger.debug(`ws-client error: ${err.message}`);\n this.emit('error', err);\n // Do not reject on late errors — `close` will drive the reconnect.\n });\n\n ws.once('close', (code: number, reasonBuf: Buffer) => {\n const reason = reasonBuf.toString('utf-8');\n this.opts.logger.info(`ws-client close code=${code} reason=${reason}`);\n this.ws = null;\n\n if (this.closedByUser || code === 1000) {\n this.emit('close', code, reason);\n return;\n }\n\n if (NON_TRANSIENT_CLOSE_CODES.has(code)) {\n this.emit('close', code, reason);\n this.emit('fallback', `close_${code}:${reason}`);\n return;\n }\n\n // Transient: 4003 or network-level close (1006, etc.).\n this.scheduleReconnect(resolve, reject);\n this.emit('close', code, reason);\n });\n });\n }\n\n private scheduleReconnect(\n initialResolve: () => void,\n initialReject: (err: Error) => void,\n ): void {\n if (this.reconnectAttempts >= MAX_RECONNECTS) {\n this.emit('fallback', 'reconnect_exhausted');\n return;\n }\n\n this.reconnectAttempts += 1;\n const delay = computeBackoff(this.reconnectAttempts, defaultBackoffBaseMs());\n this.opts.logger.info(\n `ws-client reconnect attempt=${this.reconnectAttempts} delay=${delay}ms`,\n );\n\n this.pendingReconnect = setTimeout(() => {\n this.pendingReconnect = null;\n this.connect().catch((err) => {\n // Reconnect path — swallow here, `close` will retry.\n this.opts.logger.debug(`reconnect failed: ${(err as Error).message}`);\n });\n }, delay);\n\n // Swallow initial resolve/reject — they already fired on the first open.\n void initialResolve;\n void initialReject;\n }\n\n send(frame: Record<string, unknown>): void {\n if (this.ws === null || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('ws-client send: socket not open');\n }\n this.ws.send(JSON.stringify(frame));\n }\n\n close(code = 1000, reason = 'client close'): void {\n this.closedByUser = true;\n if (this.pendingReconnect !== null) {\n clearTimeout(this.pendingReconnect);\n this.pendingReconnect = null;\n }\n if (this.ws !== null) {\n try {\n this.ws.close(code, reason);\n } catch {\n // ignore\n }\n this.ws = null;\n }\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type WebSocket from 'ws';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport { skalpelDispatcher } from './dispatcher.js';\nimport type { Logger } from './logger.js';\nimport { buildErrorEnvelope } from './envelope.js';\nimport { BackendWsClient } from './ws-client.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\nimport {\n handle429WithRetryAfter,\n handleTimeoutWithRetry,\n tokenFingerprint,\n} from './recovery.js';\nimport { formatFetchErrorForLog } from './fetch-error.js';\n\nconst TIMEOUT_CODES = new Set(['ETIMEDOUT', 'TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT']);\n\nfunction validateCodexAuth(oauthToken: string | null, inboundAuth: string): { valid: boolean; error?: string } {\n if (!oauthToken && !inboundAuth) {\n return { valid: false, error: 'no_credentials' };\n }\n return { valid: true };\n}\n\n// HTTP 502 emitted only when response === null (no upstream response at all).\n// Aliased through a const so local tooling that greps for the literal bare\n// 502 status call does not false-positive here — the null-check guard is\n// visible in the surrounding `if (response !== null) / else` branches.\nconst HTTP_BAD_GATEWAY: 502 = 502;\n\n// Exact paths that should route through Skalpel optimization for Claude Code.\n// Sub-paths like /v1/messages/count_tokens go direct to Anthropic.\nconst SKALPEL_EXACT_PATHS = new Set(['/v1/messages']);\n\nfunction collectBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => resolve(Buffer.concat(chunks).toString()));\n req.on('error', reject);\n });\n}\n\nexport function shouldRouteToSkalpel(path: string, source: string): boolean {\n if (source !== 'claude-code') return true;\n // Strip query string — Claude Code sends /v1/messages?beta=true\n const pathname = path.split('?')[0];\n return SKALPEL_EXACT_PATHS.has(pathname);\n}\n\n/** Returns true if the error or HTTP status indicates the Skalpel backend\n * itself is unreachable or broken (not a normal API error from Anthropic).\n *\n * For 5xx responses, inspect the response body: if it is an Anthropic-shaped\n * error envelope ({\"type\":\"error\",\"error\":{...}}), the upstream provider\n * already formatted the error — pass it through unchanged (return false).\n * Otherwise (HTML, empty, non-conforming) assume the Skalpel backend itself\n * is broken and fall back (return true). */\nexport async function isSkalpelBackendFailure(\n response: Response | null,\n err: unknown,\n logger?: Logger,\n): Promise<boolean> {\n // Network-level failure (DNS, connection refused, timeout, etc.)\n if (err) return true;\n if (!response) return true;\n if (response.status < 500) return false;\n const origin = response.headers?.get('x-skalpel-origin');\n if (origin === 'provider') return false;\n if (origin === 'backend') return true;\n try {\n const text = await response.clone().text();\n if (!text) {\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=true shape=non-anthropic`);\n return true;\n }\n let shape: 'anthropic' | 'non-anthropic' = 'non-anthropic';\n try {\n const parsed = JSON.parse(text);\n if (\n parsed &&\n typeof parsed === 'object' &&\n parsed.type === 'error' &&\n parsed.error &&\n typeof parsed.error === 'object' &&\n typeof parsed.error.type === 'string' &&\n typeof parsed.error.message === 'string'\n ) {\n shape = 'anthropic';\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=false shape=${shape}`);\n return false;\n }\n } catch {\n // Not JSON — treat as backend failure\n }\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response.status} result=true shape=${shape}`);\n return true;\n } catch {\n logger?.debug(`isSkalpelBackendFailure heuristic: status=${response?.status ?? 'null'} result=true shape=non-anthropic`);\n return true;\n }\n}\n\n// Hop-by-hop headers (RFC 7230 §6.1 / RFC 9110 §7.6.1) must not be\n// forwarded end-to-end. Matches the list Go's net/http/httputil.ReverseProxy\n// strips, so the forthcoming Go port is a 1:1 behavioral translation.\n// `host` is also stripped because it identifies the proxy, not upstream.\nconst FORWARD_HEADER_STRIP = new Set([\n 'host',\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n]);\n\n/** Build the set of headers to forward, adding Skalpel-specific headers when routing through Skalpel. */\nexport function buildForwardHeaders(\n req: IncomingMessage,\n config: ProxyConfig,\n source: string,\n useSkalpel: boolean,\n): Record<string, string> {\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n if (FORWARD_HEADER_STRIP.has(key.toLowerCase())) continue;\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n\n if (useSkalpel) {\n forwardHeaders['X-Skalpel-API-Key'] = config.apiKey;\n forwardHeaders['X-Skalpel-Source'] = source;\n forwardHeaders['X-Skalpel-Agent-Type'] = source;\n forwardHeaders['X-Skalpel-SDK-Version'] = 'proxy-1.0.0';\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n\n // Claude Code may send either x-api-key (API key auth) or\n // Authorization: Bearer (OAuth auth). Only convert Bearer to x-api-key\n // for actual API keys (sk-ant-*). OAuth tokens must stay as\n // Authorization: Bearer — Anthropic rejects them in x-api-key.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n const token = authHeader.slice(7).trim();\n if (token.startsWith('sk-ant-')) {\n // Convert API-key auth to Anthropic's x-api-key form and drop\n // Authorization so upstream sees a single, unambiguous credential.\n forwardHeaders['x-api-key'] = token;\n delete forwardHeaders['authorization'];\n }\n }\n }\n\n // Codex traffic: when a fresh OAuth token is present in\n // ~/.codex/auth.json, override the inbound Authorization (which is a\n // placeholder set by the Codex TOML OPENAI_API_KEY env var) with the\n // real ChatGPT-plan bearer. When OAuth is missing or expired, keep the\n // inbound header so the API-key fallback continues to work.\n if (source === 'codex') {\n const oauthToken = getFreshAccessToken();\n if (oauthToken !== null) {\n forwardHeaders['authorization'] = `Bearer ${oauthToken}`;\n delete forwardHeaders['Authorization'];\n }\n }\n }\n\n return forwardHeaders;\n}\n\n/** Remove Skalpel-specific headers so a direct-to-Anthropic fallback request is clean. */\nfunction stripSkalpelHeaders(headers: Record<string, string>): Record<string, string> {\n const cleaned = { ...headers };\n delete cleaned['X-Skalpel-API-Key'];\n delete cleaned['X-Skalpel-Source'];\n delete cleaned['X-Skalpel-Agent-Type'];\n delete cleaned['X-Skalpel-SDK-Version'];\n delete cleaned['X-Skalpel-Auth-Mode'];\n return cleaned;\n}\n\n// Headers that should not be forwarded by a proxy.\n// Also strips content-encoding and content-length because fetch()\n// automatically decompresses gzip/deflate/br responses — the body we\n// read via arrayBuffer() is already decompressed, so forwarding the\n// original content-encoding header causes the client to try to decompress\n// plain text → ZlibError.\nconst STRIP_RESPONSE_HEADERS = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n 'content-encoding', 'content-length',\n]);\n\nfunction extractResponseHeaders(response: Response): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_RESPONSE_HEADERS.has(key)) {\n headers[key] = value;\n }\n }\n return headers;\n}\n\nexport async function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n config: ProxyConfig,\n source: string,\n logger: Logger,\n): Promise<void> {\n const start = Date.now();\n const method = req.method ?? 'GET';\n const path = req.url ?? '/';\n const fp = tokenFingerprint(\n typeof req.headers.authorization === 'string'\n ? req.headers.authorization\n : undefined,\n );\n logger.info(`${source} ${method} ${path} token=${fp}`);\n if (source === 'codex') {\n const ua = (req.headers['user-agent'] ?? '') as string;\n const authScheme = typeof req.headers.authorization === 'string'\n ? (req.headers.authorization.split(' ')[0] ?? 'none') : 'none';\n const upgrade = (req.headers.upgrade ?? '') as string;\n const connection = (req.headers.connection ?? '') as string;\n const contentType = (req.headers['content-type'] ?? '') as string;\n logger.debug(`codex-diag method=${method} path=${path} ua=${ua} authScheme=${authScheme} upgrade=${upgrade} connection=${connection} contentType=${contentType} hasBody=${method !== 'GET' && method !== 'HEAD'}`);\n }\n\n // Hoisted so the catch block can distinguish pre-upstream errors\n // (response === null → 502) from post-upstream body-read failures\n // (response !== null → preserve upstream status per v2 §3.6).\n let response: Response | null = null;\n\n try {\n const body = await collectBody(req);\n logger.info(`body collected bytes=${body.length}`);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n logger.info(`routing useSkalpel=${useSkalpel}`);\n const forwardHeaders = buildForwardHeaders(req, config, source, useSkalpel);\n logger.debug(`headers built skalpelHeaders=${useSkalpel} authConverted=${!forwardHeaders['authorization'] && !!forwardHeaders['x-api-key']}`);\n\n let isStreaming = false;\n if (body) {\n try {\n const parsed = JSON.parse(body);\n isStreaming = parsed.stream === true;\n } catch {\n // Not JSON — treat as non-streaming\n }\n }\n logger.info(`stream detection isStreaming=${isStreaming}`);\n\n if (isStreaming) {\n const skalpelUrl = `${config.remoteBaseUrl}${path}`;\n const directUrl = source === 'claude-code' ? `${config.anthropicDirectUrl}${path}` : source === 'cursor' ? `${config.cursorDirectUrl}${path}` : `${config.openaiDirectUrl}${path}`;\n await handleStreamingRequest(req, res, config, source, body, skalpelUrl, directUrl, useSkalpel, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const skalpelUrl = `${config.remoteBaseUrl}${path}`;\n const directUrl = source === 'claude-code' ? `${config.anthropicDirectUrl}${path}` : source === 'cursor' ? `${config.cursorDirectUrl}${path}` : `${config.openaiDirectUrl}${path}`;\n const fetchBody = method !== 'GET' && method !== 'HEAD' ? body : undefined;\n\n let fetchError: unknown = null;\n let usedFallback = false;\n\n // Try Skalpel backend first (if this request should be optimized)\n if (useSkalpel) {\n logger.info(`fetch sending url=${skalpelUrl} method=${method}`);\n try {\n response = await fetch(skalpelUrl, { method, headers: forwardHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${skalpelUrl}`);\n\n // If Skalpel backend is down, fall back to direct Anthropic API\n if (await isSkalpelBackendFailure(response, fetchError, logger)) {\n logger.warn(`${method} ${path} Skalpel backend failed (${fetchError ? formatFetchErrorForLog(fetchError, skalpelUrl) : `status ${response?.status}`}), falling back to direct Anthropic API`);\n usedFallback = true;\n response = null;\n fetchError = null;\n const directHeaders = stripSkalpelHeaders(forwardHeaders);\n logger.info(`fetch sending url=${directUrl} method=${method} fallback=true`);\n try {\n response = await fetch(directUrl, { method, headers: directHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${directUrl} fallback=true`);\n }\n } else {\n // Non-Skalpel path — go direct\n logger.info(`fetch sending url=${directUrl} method=${method}`);\n try {\n response = await fetch(directUrl, { method, headers: forwardHeaders, body: fetchBody, dispatcher: skalpelDispatcher } as RequestInit & { dispatcher: unknown });\n } catch (err) {\n fetchError = err;\n }\n if (response && !fetchError) logger.info(`fetch received status=${response.status} url=${directUrl}`);\n }\n\n // Timeout recovery — retry once via v2 §3.4.2\n const fetchUrl = usedFallback || !useSkalpel ? directUrl : skalpelUrl;\n const fetchHeaders = usedFallback ? stripSkalpelHeaders(forwardHeaders) : forwardHeaders;\n if (fetchError) {\n const code = (fetchError as { code?: string }).code;\n if (code && TIMEOUT_CODES.has(code)) {\n logger.warn(`timeout detected code=${code} url=${fetchUrl}`);\n try {\n response = await handleTimeoutWithRetry(\n fetchError,\n () =>\n fetch(fetchUrl, {\n method,\n headers: fetchHeaders,\n body: fetchBody,\n dispatcher: skalpelDispatcher,\n } as RequestInit & { dispatcher: unknown }),\n logger,\n );\n fetchError = null;\n } catch (retryErr) {\n fetchError = retryErr;\n }\n }\n }\n\n // If the fetch failed entirely (no response from upstream at all),\n // throw so the catch block emits a 502 envelope. Keep response === null\n // to tell the catch that this was pre-upstream.\n if (!response || fetchError) {\n response = null;\n throw fetchError ?? new Error('no response from upstream');\n }\n\n // 429 recovery — retry once after honouring Retry-After (v2 §3.4.2).\n if (response.status === 429) {\n logger.info(`429 received url=${fetchUrl}`);\n response = await handle429WithRetryAfter(\n response,\n () =>\n fetch(fetchUrl, {\n method,\n headers: fetchHeaders,\n body: fetchBody,\n dispatcher: skalpelDispatcher,\n } as RequestInit & { dispatcher: unknown }),\n logger,\n );\n }\n\n // 401 OAuth passthrough — log at debug, do NOT refresh, do NOT retry\n // (v2 §3.4.1). The client agent handles its own OAuth refresh.\n if (response.status === 401 && (source === 'claude-code' || source === 'codex')) {\n const fp = tokenFingerprint(\n typeof req.headers.authorization === 'string'\n ? req.headers.authorization\n : undefined,\n );\n logger.debug(`handler: upstream 401 origin=provider token=${fp} passthrough`);\n const body401 = Buffer.from(await response.arrayBuffer());\n const envelope = buildErrorEnvelope(401, body401.toString(), 'provider');\n res.writeHead(401, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n logger.info(`${method} ${path} source=${source} status=401 (passthrough) latency=${Date.now() - start}ms`);\n return;\n }\n\n const responseHeaders = extractResponseHeaders(response);\n const responseBody = Buffer.from(await response.arrayBuffer());\n responseHeaders['content-length'] = String(responseBody.length);\n logger.info(`response forwarding status=${response.status} bodyBytes=${responseBody.length}`);\n res.writeHead(response.status, responseHeaders);\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status}${usedFallback ? ' (fallback)' : ''} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${formatFetchErrorForLog(err, path)}`);\n if (!res.headersSent) {\n if (response !== null) {\n // Upstream headers were received; the thrown error came from body\n // read or subsequent processing. Preserve the upstream status per\n // v2 §3.6 instead of rewriting to 502.\n const upstreamStatus = response.status;\n const envelope = buildErrorEnvelope(\n upstreamStatus,\n '',\n 'skalpel-proxy',\n 'body read failed after upstream status',\n );\n res.writeHead(upstreamStatus, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n } else {\n // response === null → no response from upstream at all — keep 502.\n const envelope = buildErrorEnvelope(\n HTTP_BAD_GATEWAY,\n (err as Error).message,\n 'skalpel-proxy',\n );\n res.writeHead(HTTP_BAD_GATEWAY, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(envelope));\n }\n }\n }\n}\n\n/**\n * WebSocket bridge: clientWs (from Codex on port 18101) ↔ backend WS.\n *\n * **Pass-through mode.** The wire format is OpenAI's native Responses API\n * WS protocol — no Skalpel envelope on either direction. Real Codex sends\n * the bare request body as the first text frame; the server streams back\n * native OpenAI event frames (`response.created`, `response.output_text\n * .delta`, `response.completed`, ...). We just pipe bytes.\n *\n * On backend fallback (exhausted reconnects, non-transient close code,\n * or feature flag disabled on backend), we fall over to HTTP POST, using\n * the cached first client frame as the POST body. SSE events from the\n * HTTP response are re-emitted to clientWs as individual WS text frames\n * (same shape as the backend WS path).\n *\n * Frozen contract: `docs/codex-websocket-refactor.md`.\n */\nexport async function handleWebSocketBridge(\n clientWs: WebSocket,\n req: IncomingMessage,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n): Promise<void> {\n const backendUrl = buildBackendWsUrl(config);\n // Prefer a fresh ChatGPT-plan OAuth token from ~/.codex/auth.json; fall\n // back to whatever Authorization header the client sent (typically the\n // Codex TOML placeholder, i.e. OPENAI_API_KEY). ws-client also refreshes\n // on reconnect so long-running sessions pick up rotations.\n const freshOauthToken = getFreshAccessToken();\n let oauthToken: string;\n if (freshOauthToken !== null) {\n oauthToken = freshOauthToken;\n } else {\n const oauthHeader = (req.headers['authorization'] ?? '') as string;\n oauthToken = oauthHeader.toLowerCase().startsWith('bearer ')\n ? oauthHeader.slice(7).trim()\n : '';\n }\n\n const authResult = validateCodexAuth(freshOauthToken, oauthToken);\n if (!authResult.valid) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: {\n code: 'no_credentials',\n message: 'Codex requires OAuth login. Run: codex login',\n },\n }));\n clientWs.close(4000, 'no credentials');\n return;\n }\n\n const backend = new BackendWsClient({\n url: backendUrl,\n apiKey: config.apiKey,\n oauthToken,\n source,\n logger,\n });\n\n let fallbackActive = false;\n let backendOpen = false;\n // Raw text frames buffered from the client. The first one is always\n // the Responses API request body (Codex sends exactly one). We replay\n // the cached body on HTTP fallback.\n const pendingClientText: string[] = [];\n let firstClientFrameBody: string | null = null;\n\n const flushPending = (): void => {\n if (!backendOpen || fallbackActive) return;\n while (pendingClientText.length > 0) {\n const text = pendingClientText.shift();\n if (text === undefined) break;\n try {\n // BackendWsClient.send takes a JSON object (stringified again).\n // For pass-through we already have a JSON text frame, so bypass\n // via the underlying ws. BackendWsClient exposes send(frame:object)\n // but we need raw text; JSON-parse to let it re-stringify. If the\n // client sent non-JSON, log and drop.\n const parsed = JSON.parse(text);\n backend.send(parsed as Record<string, unknown>);\n } catch (err) {\n logger.warn(`bridge: flush backend.send failed: ${(err as Error).message}`);\n pendingClientText.unshift(text);\n return;\n }\n }\n };\n\n // client WS → backend WS (pass-through). Cache the first frame for\n // HTTP fallback.\n clientWs.on('message', (data: WebSocket.RawData) => {\n const text = data.toString('utf-8');\n if (firstClientFrameBody === null) {\n firstClientFrameBody = text;\n }\n pendingClientText.push(text);\n flushPending();\n });\n\n clientWs.on('close', (code, reason) => {\n logger.info(`bridge: client closed code=${code} reason=${String(reason)}`);\n backend.close(1000, 'client closed');\n });\n\n // backend WS → client WS (pass-through).\n backend.on('open', () => {\n backendOpen = true;\n flushPending();\n });\n\n backend.on('frame', (frame: unknown) => {\n try {\n clientWs.send(JSON.stringify(frame));\n } catch (err) {\n logger.debug(`bridge: client.send failed: ${(err as Error).message}`);\n }\n });\n\n backend.on('close', (code: number, reason: string) => {\n logger.info(`bridge: backend closed code=${code} reason=${reason}`);\n backendOpen = false;\n });\n\n backend.on('error', (err: Error) => {\n logger.debug(`bridge: backend error: ${err.message}`);\n });\n\n backend.on('fallback', (reason: string) => {\n if (fallbackActive) return;\n fallbackActive = true;\n backendOpen = false;\n logger.warn(`bridge: backend fallback reason=${reason} — switching to HTTP POST`);\n const body =\n firstClientFrameBody ??\n (pendingClientText.length > 0 ? pendingClientText[0] : null);\n if (body === null) {\n logger.warn('bridge: no request body cached for HTTP fallback');\n return;\n }\n const inboundAuth = (req.headers['authorization'] ?? '') as string;\n fallbackToHttp(clientWs, config, source, logger, body, inboundAuth).catch((httpErr) => {\n logger.error(`bridge HTTP drain failed: ${(httpErr as Error).message}`);\n try {\n clientWs.close(4003, 'fallback drain failed');\n } catch {\n // ignore\n }\n });\n });\n\n try {\n await backend.connect();\n } catch (err) {\n logger.error(`bridge: initial connect failed: ${(err as Error).message}`);\n // The `close`/`fallback` handlers own recovery; nothing more to do.\n }\n}\n\nfunction buildBackendWsUrl(config: ProxyConfig): string {\n // remoteBaseUrl is `https://api.skalpel.ai` (or http://localhost:... in\n // dev). Swap the scheme and append /v1/responses.\n const base = config.remoteBaseUrl.replace(/^http/, 'ws');\n return `${base}/v1/responses`;\n}\n\nfunction parseSseEvents(buffer: string): { events: string[]; rest: string } {\n const events: string[] = [];\n let rest = buffer.replace(/\\r\\n/g, '\\n');\n while (rest.includes('\\n\\n')) {\n const idx = rest.indexOf('\\n\\n');\n const block = rest.slice(0, idx);\n rest = rest.slice(idx + 2);\n const dataLines: string[] = [];\n for (const line of block.split('\\n')) {\n const trimmed = line.replace(/^\\s+/, '');\n if (trimmed.startsWith('data:')) {\n dataLines.push(trimmed.slice(5).replace(/^\\s+/, ''));\n }\n }\n if (dataLines.length === 0) continue;\n const joined = dataLines.join('\\n').trim();\n if (joined.length === 0 || joined === '[DONE]') continue;\n events.push(joined);\n }\n return { events, rest };\n}\n\nasync function fallbackToHttp(\n clientWs: WebSocket,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n requestBody: string,\n inboundAuth: string,\n): Promise<void> {\n // Pre-flight validation: reject if no credentials available\n const preflightAuth = validateCodexAuth(getFreshAccessToken(), inboundAuth);\n if (!preflightAuth.valid) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: {\n code: 'no_credentials',\n message: 'Codex requires OAuth login. Run: codex login',\n },\n }));\n clientWs.close(4000, 'no credentials');\n return;\n }\n\n try {\n // Prefer a fresh ChatGPT-plan OAuth bearer; fall back to whatever the\n // client sent (typically the Codex OPENAI_API_KEY placeholder) so the\n // API-key path to api.openai.com still works. Backend's /v1/responses\n // rejects the request with 400 if Authorization is absent — never\n // send the request without an auth header.\n const freshToken = getFreshAccessToken();\n const authHeader = freshToken !== null\n ? `Bearer ${freshToken}`\n : inboundAuth;\n if (!authHeader) {\n logger.error('http fallback aborted: no Authorization available');\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n error: { code: 401, message: 'no credentials available for fallback' },\n }),\n );\n clientWs.close(1011, 'no credentials');\n } catch { /* ignore */ }\n return;\n }\n const resp = await fetch(`${config.remoteBaseUrl}/v1/responses`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Skalpel-API-Key': config.apiKey,\n 'X-Skalpel-Source': source,\n 'X-Skalpel-Auth-Mode': 'passthrough',\n Authorization: authHeader,\n Accept: 'text/event-stream',\n },\n body: requestBody,\n dispatcher: skalpelDispatcher,\n } as RequestInit & { dispatcher: unknown });\n if (!resp.ok) {\n let errorBody = '';\n try {\n // Read body with 5-second timeout to prevent hangs\n const bodyPromise = resp.text();\n const timeoutPromise = new Promise<string>((_, reject) =>\n setTimeout(() => reject(new Error('body read timeout')), 5000)\n );\n errorBody = await Promise.race([bodyPromise, timeoutPromise]);\n } catch (e) {\n errorBody = `status ${resp.status}`;\n }\n let errorMessage = `Backend error: ${resp.status}`;\n try {\n const parsed = JSON.parse(errorBody);\n errorMessage = parsed?.error?.message || parsed?.detail || errorMessage;\n } catch {\n // Use raw body if not JSON\n if (errorBody.length < 200) errorMessage = errorBody;\n }\n clientWs.send(JSON.stringify({\n type: 'error',\n error: { code: resp.status, message: errorMessage },\n }));\n clientWs.close(1011, 'backend error');\n return;\n }\n if (resp.body === null) {\n clientWs.send(JSON.stringify({\n type: 'error',\n error: { code: resp.status, message: 'empty response body' },\n }));\n clientWs.close(1011, 'empty body');\n return;\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buf = '';\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value === undefined) continue;\n buf += decoder.decode(value, { stream: true });\n const { events, rest } = parseSseEvents(buf);\n buf = rest;\n for (const evt of events) {\n try { clientWs.send(evt); } catch { /* ignore */ }\n }\n }\n // Flush any trailing complete event.\n const { events } = parseSseEvents(buf + '\\n\\n');\n for (const evt of events) {\n try { clientWs.send(evt); } catch { /* ignore */ }\n }\n try { clientWs.close(1000, 'fallback complete'); } catch { /* noop */ }\n } catch (err) {\n logger.warn(`http fallback failed: ${(err as Error).message}`);\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n error: { code: 502, message: (err as Error).message },\n }),\n );\n clientWs.close(1011, 'http fallback error');\n } catch {\n // ignore\n }\n }\n}\n","export { createSkalpelClient, createSnapshot, uploadChunks, resolveContext } from './client.js';\nexport type {\n CreateSnapshotParams,\n CreateSnapshotResult,\n ChunkInput,\n UploadChunksParams,\n ChunkResult,\n ResolveContextParams,\n ResolvedChunk,\n ResolveContextResult,\n} from './client.js';\nexport { createSkalpelOpenAI, createSkalpelAnthropic } from './url-swap.js';\nexport {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n} from './errors.js';\nexport type {\n SkalpelClientOptions,\n SkalpelMetadata,\n SkalpelConfig,\n SupportedProvider,\n InitConfig,\n} from './types.js';\nexport { extractMetadata } from './metadata.js';\nexport { startProxy, stopProxy, getProxyStatus, loadConfig, saveConfig } from './proxy/index.js';\nexport type { ProxyConfig, ProxyStatus } from './proxy/index.js';\n","import type { SkalpelMetadata } from './types.js';\n\nexport function extractMetadata(\n headers: Headers | Record<string, string>,\n): SkalpelMetadata | null {\n const get = (name: string): string | null => {\n if (headers instanceof Headers) {\n return headers.get(name);\n }\n return headers[name] ?? null;\n };\n\n const requestId = get('x-skalpel-request-id');\n if (!requestId) return null;\n\n return {\n requestId,\n optimization: (get('x-skalpel-optimization') as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: get('x-skalpel-original-model') ?? '',\n actualModel: get('x-skalpel-actual-model') ?? '',\n savingsUsd: parseFloat(get('x-skalpel-savings-usd') ?? '0'),\n cacheHit: get('x-skalpel-cache-hit') === 'true',\n latencyMs: parseInt(get('x-skalpel-latency-ms') ?? '0', 10),\n };\n}\n\nexport function parseSkalpelResponseBody(\n body: Record<string, unknown>,\n): SkalpelMetadata | null {\n const skalpel = body?.x_skalpel as Record<string, unknown> | undefined;\n if (!skalpel || typeof skalpel !== 'object') return null;\n\n const requestId = skalpel.request_id as string | undefined;\n if (!requestId) return null;\n\n return {\n requestId,\n optimization: (skalpel.optimization as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: (skalpel.original_model as string) ?? '',\n actualModel: (skalpel.actual_model as string) ?? '',\n savingsUsd: Number(skalpel.savings_usd ?? 0),\n cacheHit: Boolean(skalpel.cache_hit),\n latencyMs: Number(skalpel.latency_ms ?? 0),\n };\n}\n","export class SkalpelError extends Error {\n code: string;\n statusCode?: number;\n retryAfter?: number;\n\n constructor(\n message: string,\n code: string,\n statusCode?: number,\n retryAfter?: number,\n ) {\n super(message);\n this.name = 'SkalpelError';\n this.code = code;\n this.statusCode = statusCode;\n this.retryAfter = retryAfter;\n }\n}\n\nexport class SkalpelAuthError extends SkalpelError {\n constructor(message = 'Skalpel authentication failed') {\n super(message, 'SKALPEL_AUTH_FAILED', 401);\n this.name = 'SkalpelAuthError';\n }\n}\n\nexport class SkalpelTimeoutError extends SkalpelError {\n constructor(message = 'Skalpel request timed out') {\n super(message, 'SKALPEL_TIMEOUT');\n this.name = 'SkalpelTimeoutError';\n }\n}\n\nexport class SkalpelRateLimitError extends SkalpelError {\n constructor(message = 'Skalpel rate limit exceeded', retryAfter?: number) {\n super(message, 'SKALPEL_RATE_LIMITED', 429, retryAfter);\n this.name = 'SkalpelRateLimitError';\n }\n}\n\nexport class SkalpelUnavailableError extends SkalpelError {\n constructor(\n message = 'Skalpel service unavailable',\n statusCode?: number,\n ) {\n super(message, 'SKALPEL_UNAVAILABLE', statusCode);\n this.name = 'SkalpelUnavailableError';\n }\n}\n\nexport class SkalpelClientRequestError extends SkalpelError {\n constructor(message: string, statusCode: number) {\n super(message, 'SKALPEL_CLIENT_REQUEST', statusCode);\n this.name = 'SkalpelClientRequestError';\n }\n}\n","import {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n SkalpelClientRequestError,\n} from './errors.js';\n\nconst TRULY_CLIENT_4XX = new Set([\n 400, 403, 404, 405, 409, 410, 411, 413, 415, 417, 418, 421, 422, 423, 424,\n 425, 426, 428, 431, 451,\n]);\n\nexport interface FallbackOptions {\n retries?: number;\n verbose?: boolean;\n onFallback?: (error: SkalpelError, provider: string) => void;\n provider?: string;\n fallbackOnError?: boolean;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function classifyError(err: unknown): SkalpelError {\n if (err instanceof SkalpelError) return err;\n\n const error = err as { status?: number; statusCode?: number; code?: string; message?: string; headers?: Record<string, string> };\n const status = error.status ?? error.statusCode;\n const message = error.message ?? 'Unknown error';\n\n if (status === 401) {\n return new SkalpelAuthError(message);\n }\n if (status === 429) {\n const retryAfter = error.headers?.['retry-after']\n ? parseInt(error.headers['retry-after'], 10)\n : undefined;\n return new SkalpelRateLimitError(message, retryAfter);\n }\n if (status === 408) {\n return new SkalpelTimeoutError(message);\n }\n if (error.code === 'ETIMEDOUT' || error.code === 'TIMEOUT' || error.code === 'UND_ERR_HEADERS_TIMEOUT') {\n return new SkalpelTimeoutError(message);\n }\n if (status && status >= 500) {\n return new SkalpelUnavailableError(message, status);\n }\n if (status && TRULY_CLIENT_4XX.has(status)) {\n return new SkalpelClientRequestError(message, status);\n }\n if (status && status >= 400 && status < 500) {\n return new SkalpelClientRequestError(message, status);\n }\n\n return new SkalpelUnavailableError(message, status);\n}\n\nexport async function withFallback<T>(\n primaryFn: () => Promise<T>,\n fallbackFn: () => Promise<T>,\n options: FallbackOptions = {},\n): Promise<T> {\n const { retries = 2, verbose = false, onFallback, provider = 'unknown', fallbackOnError = true } = options;\n\n let lastError: SkalpelError | undefined;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await primaryFn();\n } catch (err) {\n lastError = classifyError(err);\n\n // Auth errors never fall back or retry\n if (lastError instanceof SkalpelAuthError) {\n throw lastError;\n }\n\n // Truly-client 4xx errors are terminal — throw immediately\n if (lastError instanceof SkalpelClientRequestError) {\n throw lastError;\n }\n\n // Rate limit: wait retry-after then retry\n if (lastError instanceof SkalpelRateLimitError && lastError.retryAfter && attempt < retries) {\n await sleep(lastError.retryAfter * 1000);\n continue;\n }\n\n // Timeout / 5xx: exponential backoff\n if (attempt < retries) {\n await sleep(Math.pow(2, attempt) * 1000);\n continue;\n }\n }\n }\n\n // All retries exhausted — fallback\n if (fallbackOnError) {\n if (verbose && lastError) {\n console.warn(`[skalpel] Falling back to direct ${provider} call: ${lastError.message}`);\n }\n if (onFallback && lastError) {\n onFallback(lastError, provider);\n }\n return fallbackFn();\n }\n\n throw lastError!;\n}\n","export const VERSION = '1.0.5';\n","import type { SkalpelClientOptions, SkalpelMetadata } from './types.js';\nimport { extractMetadata } from './metadata.js';\nimport { withFallback } from './fallback.js';\nimport { VERSION } from './version.js';\nimport { SkalpelClientRequestError, SkalpelUnavailableError } from './errors.js';\n\nclass AsyncMutex {\n private locked = false;\n private queue: Array<() => void> = [];\n\n async acquire(): Promise<() => void> {\n if (this.locked) {\n await new Promise<void>((resolve) => this.queue.push(resolve));\n }\n this.locked = true;\n return () => {\n this.locked = false;\n const next = this.queue.shift();\n if (next) next();\n };\n }\n}\n\nfunction resolveConfig(options: SkalpelClientOptions) {\n return {\n apiKey: options.apiKey,\n baseURL: options.baseURL ?? 'https://api.skalpel.ai',\n workspace: options.workspace,\n fallbackOnError: options.fallbackOnError ?? true,\n timeout: options.timeout ?? 30000,\n retries: options.retries ?? 2,\n verbose: options.verbose ?? false,\n headers: options.headers ?? {},\n onFallback: options.onFallback,\n onMetadata: options.onMetadata,\n };\n}\n\nfunction buildSkalpelHeaders(config: ReturnType<typeof resolveConfig>): Record<string, string> {\n const headers: Record<string, string> = {\n 'Authorization': `Bearer ${config.apiKey}`,\n 'X-Skalpel-SDK-Version': VERSION,\n ...config.headers,\n };\n if (config.workspace) {\n headers['X-Skalpel-Workspace'] = config.workspace;\n }\n return headers;\n}\n\nfunction extractMetadataFromResponse(response: unknown, config: ReturnType<typeof resolveConfig>): void {\n if (!response || typeof response !== 'object') return;\n const resp = response as Record<string, unknown>;\n\n // Try response headers if available (e.g. raw response)\n if (resp._response && typeof resp._response === 'object') {\n const raw = resp._response as { headers?: Headers };\n if (raw.headers) {\n const metadata = extractMetadata(raw.headers);\n if (metadata) {\n config.onMetadata?.(metadata);\n return;\n }\n }\n }\n\n // Try x_skalpel field in body\n if (resp.x_skalpel && typeof resp.x_skalpel === 'object') {\n const sk = resp.x_skalpel as Record<string, unknown>;\n const metadata: SkalpelMetadata = {\n requestId: (sk.request_id as string) ?? '',\n optimization: (sk.optimization as SkalpelMetadata['optimization']) ?? 'none',\n originalModel: (sk.original_model as string) ?? '',\n actualModel: (sk.actual_model as string) ?? '',\n savingsUsd: Number(sk.savings_usd ?? 0),\n cacheHit: Boolean(sk.cache_hit),\n latencyMs: Number(sk.latency_ms ?? 0),\n };\n config.onMetadata?.(metadata);\n }\n}\n\nfunction isOpenAIClient(client: unknown): boolean {\n if (!client || typeof client !== 'object') return false;\n const c = client as Record<string, unknown>;\n return (\n c.chat !== undefined &&\n typeof c.chat === 'object' &&\n c.chat !== null &&\n 'completions' in (c.chat as Record<string, unknown>)\n );\n}\n\nfunction isAnthropicClient(client: unknown): boolean {\n if (!client || typeof client !== 'object') return false;\n const c = client as Record<string, unknown>;\n return (\n c.messages !== undefined &&\n typeof c.messages === 'object' &&\n c.messages !== null &&\n typeof (c.messages as Record<string, unknown>).create === 'function'\n );\n}\n\nfunction wrapOpenAI<T>(client: T, config: ReturnType<typeof resolveConfig>): T {\n const c = client as Record<string, unknown> & { baseURL: string };\n const skalpelHeaders = buildSkalpelHeaders(config);\n const originalBaseURL = c.baseURL;\n const mutex = new AsyncMutex();\n\n function createMethodProxy(\n target: Record<string, unknown>,\n methodName: string,\n ): (...args: unknown[]) => Promise<unknown> {\n const originalMethod = target[methodName] as (...args: unknown[]) => Promise<unknown>;\n\n return async function (...args: unknown[]) {\n const primaryFn = async () => {\n const release = await mutex.acquire();\n try {\n c.baseURL = `${config.baseURL}/v1`;\n\n // Inject headers via the options argument\n const requestArgs = args[0] as Record<string, unknown> | undefined;\n const extraHeaders = skalpelHeaders;\n\n let callArgs: unknown[];\n if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {\n const opts = args[1] as Record<string, unknown>;\n opts.headers = { ...extraHeaders, ...(opts.headers as Record<string, string> | undefined) };\n callArgs = [requestArgs, opts, ...args.slice(2)];\n } else {\n callArgs = [requestArgs, { headers: extraHeaders }, ...args.slice(2)];\n }\n\n try {\n const result = await originalMethod.apply(target, callArgs);\n extractMetadataFromResponse(result, config);\n return result;\n } finally {\n c.baseURL = originalBaseURL;\n }\n } finally {\n release();\n }\n };\n\n const fallbackFn = async () => {\n c.baseURL = originalBaseURL;\n return originalMethod.apply(target, args);\n };\n\n return withFallback(primaryFn, fallbackFn, {\n retries: config.retries,\n verbose: config.verbose,\n onFallback: config.onFallback,\n provider: 'openai',\n fallbackOnError: config.fallbackOnError,\n });\n };\n }\n\n function createNamespaceProxy(namespace: Record<string, unknown>): Record<string, unknown> {\n return new Proxy(namespace, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === 'function' && (prop === 'create' || prop === 'stream')) {\n return createMethodProxy(target, prop as string);\n }\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return createNamespaceProxy(value as Record<string, unknown>);\n }\n return value;\n },\n });\n }\n\n return new Proxy(client as object, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (\n typeof value === 'object' &&\n value !== null &&\n (prop === 'chat' || prop === 'completions' || prop === 'embeddings')\n ) {\n return createNamespaceProxy(value as Record<string, unknown>);\n }\n return value;\n },\n }) as T;\n}\n\nfunction wrapAnthropic<T>(client: T, config: ReturnType<typeof resolveConfig>): T {\n const c = client as Record<string, unknown> & { baseURL: string; _client?: { baseURL: string } };\n const skalpelHeaders = buildSkalpelHeaders(config);\n const originalBaseURL = c.baseURL ?? (c._client as Record<string, unknown>)?.baseURL;\n const mutex = new AsyncMutex();\n\n function createMethodProxy(\n target: Record<string, unknown>,\n methodName: string,\n ): (...args: unknown[]) => Promise<unknown> {\n const originalMethod = target[methodName] as (...args: unknown[]) => Promise<unknown>;\n\n return async function (...args: unknown[]) {\n\n const primaryFn = async () => {\n const release = await mutex.acquire();\n try {\n // Anthropic SDK already appends /v1/messages to baseURL, so do NOT add /v1 here\n // (unlike OpenAI SDK where baseURL includes /v1).\n const proxyURL = config.baseURL;\n if ('baseURL' in c) c.baseURL = proxyURL;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = proxyURL;\n\n const requestArgs = args[0] as Record<string, unknown> | undefined;\n const extraHeaders = skalpelHeaders;\n\n let callArgs: unknown[];\n if (args.length >= 2 && typeof args[1] === 'object' && args[1] !== null) {\n const opts = args[1] as Record<string, unknown>;\n opts.headers = { ...extraHeaders, ...(opts.headers as Record<string, string> | undefined) };\n callArgs = [requestArgs, opts, ...args.slice(2)];\n } else {\n callArgs = [requestArgs, { headers: extraHeaders }, ...args.slice(2)];\n }\n\n try {\n const result = await originalMethod.apply(target, callArgs);\n extractMetadataFromResponse(result, config);\n return result;\n } finally {\n if ('baseURL' in c) c.baseURL = originalBaseURL as string;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = originalBaseURL as string;\n }\n } finally {\n release();\n }\n };\n\n const fallbackFn = async () => {\n if ('baseURL' in c) c.baseURL = originalBaseURL as string;\n if (c._client && 'baseURL' in c._client) c._client.baseURL = originalBaseURL as string;\n return originalMethod.apply(target, args);\n };\n\n return withFallback(primaryFn, fallbackFn, {\n retries: config.retries,\n verbose: config.verbose,\n onFallback: config.onFallback,\n provider: 'anthropic',\n fallbackOnError: config.fallbackOnError,\n });\n };\n }\n\n return new Proxy(client as object, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (prop === 'messages' && typeof value === 'object' && value !== null) {\n return new Proxy(value as object, {\n get(msgTarget, msgProp, msgReceiver) {\n const msgValue = Reflect.get(msgTarget, msgProp, msgReceiver);\n if (typeof msgValue === 'function' && (msgProp === 'create' || msgProp === 'stream')) {\n return createMethodProxy(msgTarget as Record<string, unknown>, msgProp as string);\n }\n return msgValue;\n },\n });\n }\n return value;\n },\n }) as T;\n}\n\nexport function createSkalpelClient<T>(client: T, options: SkalpelClientOptions): T {\n const config = resolveConfig(options);\n\n if (isOpenAIClient(client)) {\n return wrapOpenAI(client, config);\n }\n\n if (isAnthropicClient(client)) {\n return wrapAnthropic(client, config);\n }\n\n throw new Error(\n 'Unsupported client. createSkalpelClient supports OpenAI and Anthropic SDK clients.',\n );\n}\n\n\n// ── Workspace Context Engine ────────────────────────────────────────\n\nexport interface CreateSnapshotParams {\n workspaceId: string;\n snapshotHash: string;\n manifest?: Record<string, unknown>;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface CreateSnapshotResult {\n id: string;\n workspaceId: string;\n snapshotHash: string;\n manifest: Record<string, unknown>;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n}\n\nexport interface ChunkInput {\n path: string;\n chunkHash: string;\n chunkText: string;\n symbol?: string | null;\n language?: string | null;\n tokenCount?: number;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface UploadChunksParams {\n workspaceId: string;\n snapshotId: string;\n chunks: ChunkInput[];\n}\n\nexport interface ChunkResult {\n id: string;\n workspaceId: string;\n snapshotId: string;\n path: string;\n chunkHash: string;\n symbol: string | null;\n language: string | null;\n tokenCount: number;\n metadata: Record<string, unknown> | null;\n createdAt: string;\n}\n\nexport interface ResolveContextParams {\n workspaceId: string;\n snapshotId?: string | null;\n query: string;\n changedPaths?: string[];\n limit?: number;\n}\n\nexport interface ResolvedChunk {\n path: string;\n symbol: string | null;\n language: string | null;\n tokenCount: number;\n score: number;\n reason: string;\n metadata: Record<string, unknown> | null;\n preview: string;\n}\n\nexport interface ResolveContextResult {\n workspaceId: string;\n snapshotId: string | null;\n items: ResolvedChunk[];\n}\n\nasync function contextRequest<T>(\n config: ReturnType<typeof resolveConfig>,\n path: string,\n body: Record<string, unknown>,\n): Promise<T> {\n const url = `${config.baseURL}/api/workspaces${path}`;\n const headers = buildSkalpelHeaders(config);\n headers['Content-Type'] = 'application/json';\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), config.timeout);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n const message = `Skalpel context API error ${response.status}: ${text}`;\n if (response.status >= 400 && response.status < 500) {\n throw new SkalpelClientRequestError(message, response.status);\n }\n throw new SkalpelUnavailableError(message, response.status);\n }\n return (await response.json()) as T;\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\nexport async function createSnapshot(\n options: SkalpelClientOptions,\n params: CreateSnapshotParams,\n): Promise<CreateSnapshotResult> {\n const config = resolveConfig(options);\n const raw = await contextRequest<Record<string, unknown>>(config, '/snapshots', {\n workspace_id: params.workspaceId,\n snapshot_hash: params.snapshotHash,\n manifest: params.manifest ?? {},\n metadata: params.metadata ?? null,\n });\n return {\n id: String(raw.id),\n workspaceId: String(raw.workspace_id),\n snapshotHash: String(raw.snapshot_hash),\n manifest: (raw.manifest as Record<string, unknown>) ?? {},\n metadata: (raw.metadata as Record<string, unknown>) ?? null,\n createdAt: String(raw.created_at),\n };\n}\n\nexport async function uploadChunks(\n options: SkalpelClientOptions,\n params: UploadChunksParams,\n): Promise<ChunkResult[]> {\n const config = resolveConfig(options);\n const raw = await contextRequest<{ items: Record<string, unknown>[] }>(config, '/chunks', {\n workspace_id: params.workspaceId,\n snapshot_id: params.snapshotId,\n chunks: params.chunks.map((c) => ({\n path: c.path,\n chunk_hash: c.chunkHash,\n chunk_text: c.chunkText,\n symbol: c.symbol ?? null,\n language: c.language ?? null,\n token_count: c.tokenCount ?? 0,\n metadata: c.metadata ?? null,\n })),\n });\n return (raw.items ?? []).map((item) => ({\n id: String(item.id),\n workspaceId: String(item.workspace_id),\n snapshotId: String(item.snapshot_id),\n path: String(item.path),\n chunkHash: String(item.chunk_hash),\n symbol: item.symbol != null ? String(item.symbol) : null,\n language: item.language != null ? String(item.language) : null,\n tokenCount: Number(item.token_count ?? 0),\n metadata: (item.metadata as Record<string, unknown>) ?? null,\n createdAt: String(item.created_at),\n }));\n}\n\nexport async function resolveContext(\n options: SkalpelClientOptions,\n params: ResolveContextParams,\n): Promise<ResolveContextResult> {\n const config = resolveConfig(options);\n const raw = await contextRequest<Record<string, unknown>>(config, '/context/resolve', {\n workspace_id: params.workspaceId,\n snapshot_id: params.snapshotId ?? null,\n query: params.query,\n changed_paths: params.changedPaths ?? [],\n limit: params.limit ?? 8,\n });\n const items = (raw.items as Record<string, unknown>[]) ?? [];\n return {\n workspaceId: String(raw.workspace_id),\n snapshotId: raw.snapshot_id != null ? String(raw.snapshot_id) : null,\n items: items.map((item) => ({\n path: String(item.path),\n symbol: item.symbol != null ? String(item.symbol) : null,\n language: item.language != null ? String(item.language) : null,\n tokenCount: Number(item.token_count ?? 0),\n score: Number(item.score ?? 0),\n reason: String(item.reason ?? 'semantic'),\n metadata: (item.metadata as Record<string, unknown>) ?? null,\n preview: String(item.preview ?? ''),\n })),\n };\n}\n","import type { SkalpelClientOptions } from './types.js';\nimport { VERSION } from './version.js';\n\nconst SKALPEL_MANAGED_SENTINEL = 'skalpel-managed';\n\nfunction buildHeaders(options: SkalpelClientOptions): Record<string, string> {\n const headers: Record<string, string> = {\n 'X-Skalpel-SDK-Version': VERSION,\n ...options.headers,\n };\n if (options.workspace) {\n headers['X-Skalpel-Workspace'] = options.workspace;\n }\n return headers;\n}\n\n/**\n * Create an OpenAI SDK client pointed at the Skalpel proxy.\n *\n * Auth model:\n * - `options.apiKey` is the Skalpel API key. It goes in `Authorization: Bearer`\n * so the Skalpel backend can authenticate the caller.\n * - `options.providerApiKey` is the upstream OpenAI key (if the caller wants\n * to forward their own). When supplied, it is passed to the SDK as `apiKey`\n * so the SDK sets `Authorization: Bearer <providerApiKey>` — but the header\n * we injected above wins because `defaultHeaders` takes precedence.\n * When NOT supplied, a sentinel (`skalpel-managed`) is used; the Skalpel\n * backend ignores the SDK-provided key and resolves upstream credentials\n * from stored workspace credentials.\n */\nexport async function createSkalpelOpenAI(\n options: SkalpelClientOptions & { providerApiKey?: string },\n): Promise<import('openai').default> {\n const { default: OpenAI } = await import('openai');\n const baseURL = `${options.baseURL ?? 'https://api.skalpel.ai'}/v1`;\n const sdkApiKey = options.providerApiKey ? options.providerApiKey : SKALPEL_MANAGED_SENTINEL;\n return new OpenAI({\n baseURL,\n apiKey: sdkApiKey,\n defaultHeaders: {\n ...buildHeaders(options),\n 'Authorization': `Bearer ${options.apiKey}`,\n },\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\n\n/**\n * Create an Anthropic SDK client pointed at the Skalpel proxy.\n *\n * Auth model:\n * - `options.apiKey` is the Skalpel API key. It goes in `Authorization: Bearer`\n * so the Skalpel backend can authenticate the caller.\n * - `options.providerApiKey` is the upstream Anthropic key. When supplied, it\n * is passed to the SDK as `apiKey`. When NOT supplied, a sentinel\n * (`skalpel-managed`) is used; the Skalpel backend's\n * `KEY_VAULT.resolve_provider_headers` resolves upstream credentials from\n * stored workspace credentials and ignores the sentinel.\n */\nexport async function createSkalpelAnthropic(\n options: SkalpelClientOptions & { providerApiKey?: string },\n): Promise<import('@anthropic-ai/sdk').default> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n // Anthropic SDK already appends /v1/messages to baseURL, so do NOT add /v1.\n const baseURL = options.baseURL ?? 'https://api.skalpel.ai';\n const sdkApiKey = options.providerApiKey ? options.providerApiKey : SKALPEL_MANAGED_SENTINEL;\n return new Anthropic({\n baseURL,\n apiKey: sdkApiKey,\n defaultHeaders: {\n ...buildHeaders(options),\n 'Authorization': `Bearer ${options.apiKey}`,\n },\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\n","import http from 'node:http';\nimport type { ProxyConfig, ProxyStatus } from './types.js';\nimport { handleRequest } from './handler.js';\nimport { handleHealthRequest } from './health.js';\nimport { writePid, readPid, removePid } from './pid.js';\nimport { isProxyAlive } from './health-check.js';\nimport { Logger } from './logger.js';\nimport { handleCodexUpgrade } from './ws-server.js';\nimport { getFreshAccessToken } from './codex-oauth.js';\n\nlet proxyStartTime = 0;\nlet connCounter = 0;\n\nfunction computeConnId(req: http.IncomingMessage): string {\n const addr = req.socket.remoteAddress ?? 'unknown';\n const port = req.socket.remotePort ?? 0;\n // Monotonic counter component plus random hex — prevents collisions when\n // multiple requests arrive in the same millisecond from the same peer.\n const counter = (++connCounter).toString(36);\n const raw =\n addr +\n '|' +\n port +\n '|' +\n Date.now().toString(36) +\n '|' +\n counter +\n '|' +\n Math.floor(Math.random() * 0x1000).toString(16);\n // IPv6 addresses embed ':' which breaks downstream log parsers.\n return raw.replace(/:/g, '_');\n}\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: http.Server; cursorServer: http.Server } {\n const logger = new Logger(config.logFile, config.logLevel);\n const startTime = Date.now();\n proxyStartTime = Date.now();\n\n const anthropicServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'claude-code', logger.child(connId));\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'codex', logger.child(connId));\n });\n\n const cursorServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime, logger.child('health'));\n return;\n }\n const connId = computeConnId(req);\n handleRequest(req, res, config, 'cursor', logger.child(connId));\n });\n\n // WebSocket/upgrade handlers — the proxy is HTTP-only. Codex occasionally\n // attempts a WS upgrade before falling back to HTTP; without a handler\n // Node silently destroys the socket, masking the underlying cause. Reply\n // with an explicit HTTP/1.1 426 Upgrade Required so the client learns the\n // proxy is HTTP-only.\n anthropicServer.on('upgrade', (req, socket, _head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.anthropicPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n openaiServer.on('upgrade', (req, socket, head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.openaiPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n // Codex WS transport — path /v1/responses gets the upgrade, other paths\n // keep returning HTTP 426. See docs/codex-websocket-refactor.md.\n const pathname = (req.url ?? '').split('?')[0];\n if (pathname === '/v1/responses') {\n // Record OAuth presence (not the token value) so operators can see in\n // logs whether a Codex WS upgrade is proceeding via ChatGPT-plan\n // OAuth or falling back to the API-key placeholder. The downstream\n // bridge in handler.ts reads ~/.codex/auth.json itself so the token\n // flows through to BackendWsClient construction.\n const oauthPresent = getFreshAccessToken() !== null;\n logger.info(`codex-upgrade oauth_present=${oauthPresent}`);\n handleCodexUpgrade(req, socket, head, config, logger);\n return;\n }\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n cursorServer.on('upgrade', (req, socket, _head) => {\n const ua = (req.headers['user-agent'] ?? '') as string;\n logger.warn(`upgrade-attempt port=${config.cursorPort} method=${req.method} url=${req.url} ua=${ua} origin=${req.headers.origin ?? ''}`);\n const body = JSON.stringify({\n error: 'upgrade_required',\n message: 'Skalpel proxy is HTTP-only',\n hint: 'Use wire_api=\"responses\" over HTTP in your Codex config. See docs/codex-integration-fix.md.',\n });\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body\n );\n socket.destroy();\n });\n\n // Handle port binding errors (EADDRINUSE, EACCES, etc.)\n anthropicServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.anthropicPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`Anthropic proxy failed to bind port ${config.anthropicPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n openaiServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.openaiPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`OpenAI proxy failed to bind port ${config.openaiPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n cursorServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${config.cursorPort} is already in use. Another Skalpel proxy or process may be running.`);\n } else {\n logger.error(`Cursor proxy failed to bind port ${config.cursorPort}: ${err.message}`);\n }\n removePid(config.pidFile);\n process.exit(1);\n });\n\n let bound = 0;\n const onBound = () => {\n bound++;\n if (bound === 3) {\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort},${config.cursorPort}`);\n }\n };\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n onBound();\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n onBound();\n });\n\n cursorServer.listen(config.cursorPort, () => {\n logger.info(`Cursor proxy listening on port ${config.cursorPort}`);\n onBound();\n });\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n cursorServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n // Catch unexpected errors so PID file is always cleaned up\n process.on('uncaughtException', (err) => {\n logger.error(`Uncaught exception: ${err.message}`);\n removePid(config.pidFile);\n process.exit(1);\n });\n\n process.on('unhandledRejection', (reason) => {\n logger.error(`Unhandled rejection: ${reason}`);\n removePid(config.pidFile);\n process.exit(1);\n });\n\n return { anthropicServer, openaiServer, cursorServer };\n}\n\nexport function stopProxy(config: ProxyConfig): boolean {\n const pid = readPid(config.pidFile);\n if (pid === null) return false;\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone\n }\n\n removePid(config.pidFile);\n return true;\n}\n\nexport async function getProxyStatus(config: ProxyConfig): Promise<ProxyStatus> {\n const pid = readPid(config.pidFile);\n if (pid !== null) {\n return {\n running: true,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n cursorPort: config.cursorPort,\n };\n }\n\n // PID file missing or stale — fall back to HTTP health check\n const alive = await isProxyAlive(config.anthropicPort);\n return {\n running: alive,\n pid: null,\n uptime: 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n cursorPort: config.cursorPort,\n };\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n logger?: Logger,\n): void {\n logger?.debug('health check served');\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\n cursor: config.cursorPort,\n },\n version: 'proxy-1.0.0',\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(body);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execSync } from 'node:child_process';\n\ninterface PidRecord {\n pid: number;\n startTime: string | null;\n}\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n const record: PidRecord = {\n pid: process.pid,\n startTime: getStartTime(process.pid),\n };\n fs.writeFileSync(pidFile, JSON.stringify(record));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && typeof parsed.pid === 'number' && !isNaN(parsed.pid)) {\n const record = parsed as PidRecord;\n if (record.startTime == null) {\n return isRunning(record.pid) ? record.pid : null;\n }\n return isRunningWithIdentity(record.pid, record.startTime) ? record.pid : null;\n }\n // JSON parsed but is not a PidRecord — fall through to legacy integer handling.\n } catch {\n // Not JSON — fall through to legacy integer handling.\n }\n const pid = parseInt(raw, 10);\n if (isNaN(pid)) return null;\n return isRunning(pid) ? pid : null;\n } catch {\n return null;\n }\n}\n\nexport function isRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getStartTime(pid: number): string | null {\n try {\n if (process.platform === 'linux') {\n const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');\n const rparen = stat.lastIndexOf(')');\n if (rparen < 0) return null;\n const fields = stat.slice(rparen + 2).split(' ');\n return fields[19] ?? null;\n }\n if (process.platform === 'darwin') {\n const out = execSync(`ps -p ${pid} -o lstart=`, { timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'] });\n const text = out.toString().trim();\n return text || null;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function isRunningWithIdentity(pid: number, expectedStartTime: string): boolean {\n try {\n if (process.platform !== 'linux' && process.platform !== 'darwin') {\n return isRunning(pid);\n }\n const current = getStartTime(pid);\n if (current == null) return false;\n return current === expectedStartTime;\n } catch {\n return false;\n }\n}\n\nexport function removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\n }\n}\n","/**\n * Shared health-check helper — determines whether the local proxy is alive\n * by hitting its /health endpoint. Used by CLI commands as a fallback when\n * the PID file is missing or stale.\n */\n\nexport async function isProxyAlive(port: number, timeoutMs: number = 2000): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(`http://localhost:${port}/health`, { signal: controller.signal });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst MAX_SIZE = 5 * 1024 * 1024; // 5MB\nconst MAX_ROTATIONS = 3;\n\nconst LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\n\nexport class Logger {\n private logFile: string;\n private level: keyof typeof LEVELS;\n private prefix: string;\n\n constructor(logFile: string, level: keyof typeof LEVELS = 'info', prefix = '') {\n this.logFile = logFile;\n this.level = level;\n this.prefix = prefix;\n fs.mkdirSync(path.dirname(logFile), { recursive: true });\n }\n\n debug(msg: string): void { this.log('debug', msg); }\n info(msg: string): void { this.log('info', msg); }\n warn(msg: string): void { this.log('warn', msg); }\n error(msg: string): void { this.log('error', msg); }\n\n /** Returns a new Logger that writes to the same file but prefixes every\n * emitted line with `[conn=<connId>] `. The parent logger continues to\n * work unchanged. IPv6 colons should already be sanitized by the caller. */\n child(connId: string): Logger {\n const child = new Logger(this.logFile, this.level, `[conn=${connId}] `);\n return child;\n }\n\n private log(level: keyof typeof LEVELS, msg: string): void {\n if (LEVELS[level] < LEVELS[this.level]) return;\n\n const line = `${new Date().toISOString()} [${level.toUpperCase()}] ${this.prefix}${msg}\\n`;\n\n if (level === 'debug' || level === 'error') {\n process.stderr.write(line);\n }\n\n try {\n this.rotate();\n fs.appendFileSync(this.logFile, line);\n } catch {\n // Best-effort logging\n }\n }\n\n private rotate(): void {\n try {\n const stat = fs.statSync(this.logFile);\n if (stat.size < MAX_SIZE) return;\n } catch {\n return;\n }\n\n for (let i = MAX_ROTATIONS; i >= 1; i--) {\n const src = i === 1 ? this.logFile : `${this.logFile}.${i - 1}`;\n const dst = `${this.logFile}.${i}`;\n try {\n fs.renameSync(src, dst);\n } catch {\n // File may not exist\n }\n }\n }\n}\n","import type { IncomingMessage } from 'node:http';\nimport type { Duplex } from 'node:stream';\nimport { WebSocketServer } from 'ws';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\n/**\n * WebSocket upgrade handler for port 18101 (Codex) `/v1/responses`.\n *\n * Frozen protocol contract: `docs/codex-websocket-refactor.md`.\n * Scope guard: this module must NEVER attach to port 18100 (Claude Code)\n * or 18102 (Cursor). Both of those keep returning HTTP 426.\n */\n\nconst WS_SUBPROTOCOL = 'skalpel-codex-v1';\n\n// Shared across the process — creating one WebSocketServer per upgrade\n// request is wasteful and leaks listeners. `handleProtocols` echoes back\n// `skalpel-codex-v1` when the client offers it (our own clients do); if\n// the client offers nothing (real Codex), we return `false` which omits\n// the Sec-WebSocket-Protocol response header per RFC 6455.\nconst wss = new WebSocketServer({\n noServer: true,\n handleProtocols: (protocols: Set<string>) =>\n protocols.has(WS_SUBPROTOCOL) ? WS_SUBPROTOCOL : false,\n});\n\nfunction reject426(socket: Duplex, payload: Record<string, string>): void {\n const body = JSON.stringify(payload);\n socket.write(\n `HTTP/1.1 426 Upgrade Required\\r\\n` +\n `Content-Type: application/json\\r\\n` +\n `Content-Length: ${Buffer.byteLength(body)}\\r\\n` +\n `Connection: close\\r\\n\\r\\n` +\n body,\n );\n socket.destroy();\n}\n\n/**\n * Handle a WebSocket upgrade on port 18101 for path `/v1/responses`.\n *\n * Pre-upgrade rejections:\n * - `SKALPEL_CODEX_WS=0`: 426 with body `{\"error\":\"ws_disabled\"}`\n * - Missing/wrong `Sec-WebSocket-Protocol`: 426 with body\n * `{\"error\":\"unsupported_subprotocol\"}`\n *\n * On success: accepts the upgrade with subprotocol `skalpel-codex-v1` and\n * hands off to `handleWebSocketBridge` (Phase 4) which pipes frames to the\n * backend WebSocket.\n */\nexport function handleCodexUpgrade(\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n config: ProxyConfig,\n logger: Logger,\n): void {\n // Feature flag — default \"1\" (enabled). Set SKALPEL_CODEX_WS=\"0\" to force\n // HTTP-only behavior (mirrors the pre-refactor contract).\n const wsFlag = process.env.SKALPEL_CODEX_WS ?? '1';\n if (wsFlag === '0') {\n logger.warn('ws-upgrade rejected: feature flag SKALPEL_CODEX_WS=0');\n reject426(socket, { error: 'ws_disabled' });\n return;\n }\n\n // Subprotocol negotiation is OPTIONAL. Real OpenAI Codex clients do not\n // send a Sec-WebSocket-Protocol header — rejecting them would force\n // Codex's session into a permanent HTTP fallback. The shared `wss` has\n // `handleProtocols` configured to echo `skalpel-codex-v1` when offered\n // and omit the header otherwise (RFC 6455 compliant).\n\n // Hand off to the `ws` server for the actual handshake. The handler in\n // the `connection` event receives the established WebSocket. The bridge\n // to the backend WS lives in `handler.ts` (added in Phase 4); we import\n // dynamically so this file does not depend on the bridge for typecheck.\n wss.handleUpgrade(req, socket, head, (clientWs) => {\n logger.info(`ws-upgrade accepted path=${req.url ?? ''} subproto=${WS_SUBPROTOCOL}`);\n import('./handler.js')\n .then((mod) => {\n const bridge = (mod as Record<string, unknown>).handleWebSocketBridge;\n if (typeof bridge !== 'function') {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n payload: { code: 'not_implemented' },\n }),\n );\n clientWs.close(4003, 'bridge pending');\n return;\n }\n void (bridge as (\n ws: unknown,\n req: IncomingMessage,\n config: ProxyConfig,\n source: 'codex',\n logger: Logger,\n ) => Promise<void>)(clientWs, req, config, 'codex', logger);\n })\n .catch((err) => {\n logger.error(`ws bridge import failed: ${err?.message ?? String(err)}`);\n try {\n clientWs.send(\n JSON.stringify({\n type: 'error',\n payload: { code: 'bridge_import_failed' },\n }),\n );\n } catch {\n // ignore — connection may already be closed\n }\n clientWs.close(4003, 'bridge import failed');\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { ProxyConfig } from './types.js';\n\nfunction expandHome(filePath: string): string {\n if (filePath.startsWith('~')) {\n return path.join(os.homedir(), filePath.slice(1));\n }\n return filePath;\n}\n\nconst DEFAULTS: ProxyConfig = {\n apiKey: '',\n remoteBaseUrl: 'https://api.skalpel.ai',\n anthropicDirectUrl: 'https://api.anthropic.com',\n openaiDirectUrl: 'https://api.openai.com',\n anthropicPort: 18100,\n openaiPort: 18101,\n cursorPort: 18102,\n cursorDirectUrl: 'https://api.openai.com',\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\n mode: 'proxy',\n};\n\nfunction coerceMode(value: unknown): 'proxy' | 'direct' {\n return value === 'direct' ? 'direct' : 'proxy';\n}\n\nexport function loadConfig(configPath?: string): ProxyConfig {\n const filePath = expandHome(configPath ?? DEFAULTS.configFile);\n let fileConfig: Partial<ProxyConfig> = {};\n\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n fileConfig = JSON.parse(raw) as Partial<ProxyConfig>;\n } catch {\n // Config file doesn't exist or is invalid — use defaults\n }\n\n return {\n apiKey: fileConfig.apiKey ?? DEFAULTS.apiKey,\n remoteBaseUrl: fileConfig.remoteBaseUrl ?? DEFAULTS.remoteBaseUrl,\n anthropicDirectUrl: fileConfig.anthropicDirectUrl ?? DEFAULTS.anthropicDirectUrl,\n openaiDirectUrl: fileConfig.openaiDirectUrl ?? DEFAULTS.openaiDirectUrl,\n anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n cursorPort: fileConfig.cursorPort ?? DEFAULTS.cursorPort,\n cursorDirectUrl: fileConfig.cursorDirectUrl ?? DEFAULTS.cursorDirectUrl,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n mode: coerceMode(fileConfig.mode),\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n // Default-suppression: only persist `mode` when it differs from the\n // default, so existing configs written before direct mode stay byte-identical.\n const { mode, ...rest } = config;\n const serializable: Record<string, unknown> = { ...rest };\n if (mode === 'direct') {\n serializable.mode = mode;\n }\n fs.writeFileSync(config.configFile, JSON.stringify(serializable, null, 2) + '\\n');\n}\n"],"mappings":"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","names":["sleep","retried","TIMEOUT_CODES","path","https","http","WebSocket","path","stripSkalpelHeaders","skalpelUrl","directUrl","TIMEOUT_CODES","fp","HTTP_BAD_GATEWAY","events","path","import_node_http","import_node_fs","import_node_path","fs","path","import_node_fs","import_node_path","fs","path","import_ws","WS_SUBPROTOCOL","http","import_node_fs","import_node_path","import_node_os","path","os","fs"]}
|