skalpel 2.0.4 → 2.0.6

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.
@@ -35,22 +35,28 @@ data: ${JSON.stringify({ error: err.message })}
35
35
  res.end();
36
36
  return;
37
37
  }
38
+ const STRIP_HEADERS = /* @__PURE__ */ new Set([
39
+ ...HOP_BY_HOP,
40
+ "content-encoding",
41
+ "content-length"
42
+ ]);
38
43
  if (response.status >= 300) {
39
44
  const errorBody = Buffer.from(await response.arrayBuffer());
40
45
  logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);
41
46
  const passthroughHeaders = {};
42
47
  for (const [key, value] of response.headers.entries()) {
43
- if (!HOP_BY_HOP.has(key)) {
48
+ if (!STRIP_HEADERS.has(key)) {
44
49
  passthroughHeaders[key] = value;
45
50
  }
46
51
  }
52
+ passthroughHeaders["content-length"] = String(errorBody.length);
47
53
  res.writeHead(response.status, passthroughHeaders);
48
54
  res.end(errorBody);
49
55
  return;
50
56
  }
51
57
  const sseHeaders = {};
52
58
  for (const [key, value] of response.headers.entries()) {
53
- if (!HOP_BY_HOP.has(key) && key !== "content-type" && key !== "content-length") {
59
+ if (!STRIP_HEADERS.has(key) && key !== "content-type") {
54
60
  sseHeaders[key] = value;
55
61
  }
56
62
  }
@@ -96,7 +102,8 @@ function collectBody(req) {
96
102
  }
97
103
  function shouldRouteToSkalpel(path4, source) {
98
104
  if (source !== "claude-code") return true;
99
- return SKALPEL_EXACT_PATHS.has(path4);
105
+ const pathname = path4.split("?")[0];
106
+ return SKALPEL_EXACT_PATHS.has(pathname);
100
107
  }
101
108
  async function handleRequest(req, res, config2, source, logger) {
102
109
  const start = Date.now();
@@ -147,7 +154,7 @@ async function handleRequest(req, res, config2, source, logger) {
147
154
  headers: forwardHeaders,
148
155
  body: method !== "GET" && method !== "HEAD" ? body : void 0
149
156
  });
150
- const HOP_BY_HOP = /* @__PURE__ */ new Set([
157
+ const STRIP_HEADERS = /* @__PURE__ */ new Set([
151
158
  "connection",
152
159
  "keep-alive",
153
160
  "proxy-authenticate",
@@ -155,16 +162,19 @@ async function handleRequest(req, res, config2, source, logger) {
155
162
  "te",
156
163
  "trailer",
157
164
  "transfer-encoding",
158
- "upgrade"
165
+ "upgrade",
166
+ "content-encoding",
167
+ "content-length"
159
168
  ]);
160
169
  const responseHeaders = {};
161
170
  for (const [key, value] of response.headers.entries()) {
162
- if (!HOP_BY_HOP.has(key)) {
171
+ if (!STRIP_HEADERS.has(key)) {
163
172
  responseHeaders[key] = value;
164
173
  }
165
174
  }
166
- res.writeHead(response.status, responseHeaders);
167
175
  const responseBody = Buffer.from(await response.arrayBuffer());
176
+ responseHeaders["content-length"] = String(responseBody.length);
177
+ res.writeHead(response.status, responseHeaders);
168
178
  res.end(responseBody);
169
179
  logger.info(`${method} ${path4} source=${source} status=${response.status} latency=${Date.now() - start}ms`);
170
180
  } catch (err) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/proxy/server.ts","../../src/proxy/streaming.ts","../../src/proxy/handler.ts","../../src/proxy/health.ts","../../src/proxy/pid.ts","../../src/proxy/logger.ts","../../src/proxy/config.ts","../../src/cli/proxy-runner.ts"],"sourcesContent":["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 { Logger } from './logger.js';\n\nlet proxyStartTime = 0;\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: 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);\n return;\n }\n handleRequest(req, res, config, 'claude-code', logger);\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime);\n return;\n }\n handleRequest(req, res, config, 'codex', logger);\n });\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n });\n\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort}`);\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n return { anthropicServer, openaiServer };\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 function getProxyStatus(config: ProxyConfig): ProxyStatus {\n const pid = readPid(config.pidFile);\n return {\n running: pid !== null,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n };\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n forwardUrl: string,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n\n let response: Response;\n try {\n response = await fetch(forwardUrl, {\n method: 'POST',\n headers: forwardHeaders,\n body,\n });\n } catch (err) {\n logger.error(`streaming fetch failed: ${(err as Error).message}`);\n res.writeHead(502, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n res.end();\n return;\n }\n\n // For non-2xx responses, pass through as-is so the SDK can parse error bodies correctly\n if (response.status >= 300) {\n const errorBody = Buffer.from(await response.arrayBuffer());\n logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);\n const passthroughHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key)) {\n passthroughHeaders[key] = value;\n }\n }\n res.writeHead(response.status, passthroughHeaders);\n res.end(errorBody);\n return;\n }\n\n // Build SSE headers, stripping hop-by-hop and normalizing content-type\n const sseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key) && key !== 'content-type' && key !== 'content-length') {\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\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n }\n\n res.end();\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport type { Logger } from './logger.js';\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 return SKALPEL_EXACT_PATHS.has(path);\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\n try {\n const body = await collectBody(req);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n const forwardUrl = `${useSkalpel ? config.remoteBaseUrl : config.anthropicDirectUrl}${path}`;\n\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value !== undefined) {\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n }\n delete forwardHeaders['host'];\n delete forwardHeaders['connection'];\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\n // By default, requests go through Skalpel's optimization pipeline.\n // The backend resolves provider credentials from its KeyVault/DB.\n // Passthrough mode (skipping optimization) can be enabled per-request\n // by setting the X-Skalpel-Auth-Mode header on the client side.\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 forwardHeaders['x-api-key'] = token;\n }\n // else: OAuth token — leave Authorization: Bearer intact\n }\n }\n }\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\n if (isStreaming) {\n await handleStreamingRequest(req, res, config, source, body, forwardUrl, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const response = await fetch(forwardUrl, {\n method,\n headers: forwardHeaders,\n body: method !== 'GET' && method !== 'HEAD' ? body : undefined,\n });\n\n // Strip hop-by-hop headers that should not be forwarded by a proxy\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n const responseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key)) {\n responseHeaders[key] = value;\n }\n }\n\n res.writeHead(response.status, responseHeaders);\n const responseBody = Buffer.from(await response.arrayBuffer());\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${(err as Error).message}`);\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'proxy_error', message: (err as Error).message }));\n }\n }\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n): void {\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\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';\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n fs.writeFileSync(pidFile, String(process.pid));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\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 removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\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\n constructor(logFile: string, level: keyof typeof LEVELS = 'info') {\n this.logFile = logFile;\n this.level = level;\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 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()}] ${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 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 anthropicPort: 18100,\n openaiPort: 18101,\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\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 anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(config.configFile, JSON.stringify(config, null, 2) + '\\n');\n}\n","import { startProxy } from '../proxy/server.js';\nimport { loadConfig } from '../proxy/config.js';\n\nconst config = loadConfig();\nstartProxy(config);\n"],"mappings":"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","names":["path","config","config","fs","path","config","fs","path"]}
1
+ {"version":3,"sources":["../../src/proxy/server.ts","../../src/proxy/streaming.ts","../../src/proxy/handler.ts","../../src/proxy/health.ts","../../src/proxy/pid.ts","../../src/proxy/logger.ts","../../src/proxy/config.ts","../../src/cli/proxy-runner.ts"],"sourcesContent":["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 { Logger } from './logger.js';\n\nlet proxyStartTime = 0;\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: 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);\n return;\n }\n handleRequest(req, res, config, 'claude-code', logger);\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime);\n return;\n }\n handleRequest(req, res, config, 'codex', logger);\n });\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n });\n\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort}`);\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n return { anthropicServer, openaiServer };\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 function getProxyStatus(config: ProxyConfig): ProxyStatus {\n const pid = readPid(config.pidFile);\n return {\n running: pid !== null,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n };\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n forwardUrl: string,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n\n let response: Response;\n try {\n response = await fetch(forwardUrl, {\n method: 'POST',\n headers: forwardHeaders,\n body,\n });\n } catch (err) {\n logger.error(`streaming fetch failed: ${(err as Error).message}`);\n res.writeHead(502, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n res.end();\n return;\n }\n\n // Headers to strip: hop-by-hop + content-encoding/content-length (fetch\n // auto-decompresses, so forwarding the original encoding header causes\n // the client to try decompressing already-decompressed data → ZlibError)\n const STRIP_HEADERS = new Set([\n ...HOP_BY_HOP,\n 'content-encoding', 'content-length',\n ]);\n\n // For non-2xx responses, pass through as-is so the SDK can parse error bodies correctly\n if (response.status >= 300) {\n const errorBody = Buffer.from(await response.arrayBuffer());\n logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);\n const passthroughHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key)) {\n passthroughHeaders[key] = value;\n }\n }\n passthroughHeaders['content-length'] = String(errorBody.length);\n res.writeHead(response.status, passthroughHeaders);\n res.end(errorBody);\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\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n }\n\n res.end();\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport type { Logger } from './logger.js';\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\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\n try {\n const body = await collectBody(req);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n const forwardUrl = `${useSkalpel ? config.remoteBaseUrl : config.anthropicDirectUrl}${path}`;\n\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value !== undefined) {\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n }\n delete forwardHeaders['host'];\n delete forwardHeaders['connection'];\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\n // By default, requests go through Skalpel's optimization pipeline.\n // The backend resolves provider credentials from its KeyVault/DB.\n // Passthrough mode (skipping optimization) can be enabled per-request\n // by setting the X-Skalpel-Auth-Mode header on the client side.\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 forwardHeaders['x-api-key'] = token;\n }\n // else: OAuth token — leave Authorization: Bearer intact\n }\n }\n }\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\n if (isStreaming) {\n await handleStreamingRequest(req, res, config, source, body, forwardUrl, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const response = await fetch(forwardUrl, {\n method,\n headers: forwardHeaders,\n body: method !== 'GET' && method !== 'HEAD' ? body : undefined,\n });\n\n // Strip hop-by-hop headers that should not be forwarded by a proxy.\n // Also strip 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 (Claude Code) to\n // try to decompress plain text → ZlibError.\n const STRIP_HEADERS = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n 'content-encoding', 'content-length',\n ]);\n const responseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key)) {\n responseHeaders[key] = value;\n }\n }\n\n const responseBody = Buffer.from(await response.arrayBuffer());\n responseHeaders['content-length'] = String(responseBody.length);\n res.writeHead(response.status, responseHeaders);\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${(err as Error).message}`);\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'proxy_error', message: (err as Error).message }));\n }\n }\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n): void {\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\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';\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n fs.writeFileSync(pidFile, String(process.pid));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\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 removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\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\n constructor(logFile: string, level: keyof typeof LEVELS = 'info') {\n this.logFile = logFile;\n this.level = level;\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 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()}] ${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 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 anthropicPort: 18100,\n openaiPort: 18101,\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\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 anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(config.configFile, JSON.stringify(config, null, 2) + '\\n');\n}\n","import { startProxy } from '../proxy/server.js';\nimport { loadConfig } from '../proxy/config.js';\n\nconst config = loadConfig();\nstartProxy(config);\n"],"mappings":"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","names":["path","config","config","fs","path","config","fs","path"]}
package/dist/index.cjs CHANGED
@@ -540,22 +540,28 @@ data: ${JSON.stringify({ error: err.message })}
540
540
  res.end();
541
541
  return;
542
542
  }
543
+ const STRIP_HEADERS = /* @__PURE__ */ new Set([
544
+ ...HOP_BY_HOP,
545
+ "content-encoding",
546
+ "content-length"
547
+ ]);
543
548
  if (response.status >= 300) {
544
549
  const errorBody = Buffer.from(await response.arrayBuffer());
545
550
  logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);
546
551
  const passthroughHeaders = {};
547
552
  for (const [key, value] of response.headers.entries()) {
548
- if (!HOP_BY_HOP.has(key)) {
553
+ if (!STRIP_HEADERS.has(key)) {
549
554
  passthroughHeaders[key] = value;
550
555
  }
551
556
  }
557
+ passthroughHeaders["content-length"] = String(errorBody.length);
552
558
  res.writeHead(response.status, passthroughHeaders);
553
559
  res.end(errorBody);
554
560
  return;
555
561
  }
556
562
  const sseHeaders = {};
557
563
  for (const [key, value] of response.headers.entries()) {
558
- if (!HOP_BY_HOP.has(key) && key !== "content-type" && key !== "content-length") {
564
+ if (!STRIP_HEADERS.has(key) && key !== "content-type") {
559
565
  sseHeaders[key] = value;
560
566
  }
561
567
  }
@@ -601,7 +607,8 @@ function collectBody(req) {
601
607
  }
602
608
  function shouldRouteToSkalpel(path4, source) {
603
609
  if (source !== "claude-code") return true;
604
- return SKALPEL_EXACT_PATHS.has(path4);
610
+ const pathname = path4.split("?")[0];
611
+ return SKALPEL_EXACT_PATHS.has(pathname);
605
612
  }
606
613
  async function handleRequest(req, res, config, source, logger) {
607
614
  const start = Date.now();
@@ -652,7 +659,7 @@ async function handleRequest(req, res, config, source, logger) {
652
659
  headers: forwardHeaders,
653
660
  body: method !== "GET" && method !== "HEAD" ? body : void 0
654
661
  });
655
- const HOP_BY_HOP = /* @__PURE__ */ new Set([
662
+ const STRIP_HEADERS = /* @__PURE__ */ new Set([
656
663
  "connection",
657
664
  "keep-alive",
658
665
  "proxy-authenticate",
@@ -660,16 +667,19 @@ async function handleRequest(req, res, config, source, logger) {
660
667
  "te",
661
668
  "trailer",
662
669
  "transfer-encoding",
663
- "upgrade"
670
+ "upgrade",
671
+ "content-encoding",
672
+ "content-length"
664
673
  ]);
665
674
  const responseHeaders = {};
666
675
  for (const [key, value] of response.headers.entries()) {
667
- if (!HOP_BY_HOP.has(key)) {
676
+ if (!STRIP_HEADERS.has(key)) {
668
677
  responseHeaders[key] = value;
669
678
  }
670
679
  }
671
- res.writeHead(response.status, responseHeaders);
672
680
  const responseBody = Buffer.from(await response.arrayBuffer());
681
+ responseHeaders["content-length"] = String(responseBody.length);
682
+ res.writeHead(response.status, responseHeaders);
673
683
  res.end(responseBody);
674
684
  logger.info(`${method} ${path4} source=${source} status=${response.status} latency=${Date.now() - start}ms`);
675
685
  } catch (err) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../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/streaming.ts","../src/proxy/handler.ts","../src/proxy/health.ts","../src/proxy/pid.ts","../src/proxy/logger.ts","../src/proxy/config.ts"],"sourcesContent":["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","import {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n} from './errors.js';\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 (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\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 // 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';\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\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 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 c.baseURL = originalBaseURL;\n return result;\n } catch (err) {\n c.baseURL = originalBaseURL;\n throw err;\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\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 // 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 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 result;\n } catch (err) {\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 throw err;\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 throw new Error(`Skalpel context API error ${response.status}: ${text}`);\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\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\nexport async function createSkalpelOpenAI(\n options: SkalpelClientOptions,\n): Promise<import('openai').default> {\n const { default: OpenAI } = await import('openai');\n const baseURL = `${options.baseURL ?? 'https://api.skalpel.ai'}/v1`;\n return new OpenAI({\n baseURL,\n apiKey: options.apiKey,\n defaultHeaders: buildHeaders(options),\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\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 return new Anthropic({\n baseURL,\n apiKey: options.providerApiKey ?? options.apiKey,\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 { Logger } from './logger.js';\n\nlet proxyStartTime = 0;\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: 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);\n return;\n }\n handleRequest(req, res, config, 'claude-code', logger);\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime);\n return;\n }\n handleRequest(req, res, config, 'codex', logger);\n });\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n });\n\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort}`);\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n return { anthropicServer, openaiServer };\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 function getProxyStatus(config: ProxyConfig): ProxyStatus {\n const pid = readPid(config.pidFile);\n return {\n running: pid !== null,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n };\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n forwardUrl: string,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n\n let response: Response;\n try {\n response = await fetch(forwardUrl, {\n method: 'POST',\n headers: forwardHeaders,\n body,\n });\n } catch (err) {\n logger.error(`streaming fetch failed: ${(err as Error).message}`);\n res.writeHead(502, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n res.end();\n return;\n }\n\n // For non-2xx responses, pass through as-is so the SDK can parse error bodies correctly\n if (response.status >= 300) {\n const errorBody = Buffer.from(await response.arrayBuffer());\n logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);\n const passthroughHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key)) {\n passthroughHeaders[key] = value;\n }\n }\n res.writeHead(response.status, passthroughHeaders);\n res.end(errorBody);\n return;\n }\n\n // Build SSE headers, stripping hop-by-hop and normalizing content-type\n const sseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key) && key !== 'content-type' && key !== 'content-length') {\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\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n }\n\n res.end();\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport type { Logger } from './logger.js';\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 return SKALPEL_EXACT_PATHS.has(path);\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\n try {\n const body = await collectBody(req);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n const forwardUrl = `${useSkalpel ? config.remoteBaseUrl : config.anthropicDirectUrl}${path}`;\n\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value !== undefined) {\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n }\n delete forwardHeaders['host'];\n delete forwardHeaders['connection'];\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\n // By default, requests go through Skalpel's optimization pipeline.\n // The backend resolves provider credentials from its KeyVault/DB.\n // Passthrough mode (skipping optimization) can be enabled per-request\n // by setting the X-Skalpel-Auth-Mode header on the client side.\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 forwardHeaders['x-api-key'] = token;\n }\n // else: OAuth token — leave Authorization: Bearer intact\n }\n }\n }\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\n if (isStreaming) {\n await handleStreamingRequest(req, res, config, source, body, forwardUrl, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const response = await fetch(forwardUrl, {\n method,\n headers: forwardHeaders,\n body: method !== 'GET' && method !== 'HEAD' ? body : undefined,\n });\n\n // Strip hop-by-hop headers that should not be forwarded by a proxy\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n const responseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!HOP_BY_HOP.has(key)) {\n responseHeaders[key] = value;\n }\n }\n\n res.writeHead(response.status, responseHeaders);\n const responseBody = Buffer.from(await response.arrayBuffer());\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${(err as Error).message}`);\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'proxy_error', message: (err as Error).message }));\n }\n }\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n): void {\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\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';\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n fs.writeFileSync(pidFile, String(process.pid));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\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 removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\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\n constructor(logFile: string, level: keyof typeof LEVELS = 'info') {\n this.logFile = logFile;\n this.level = level;\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 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()}] ${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 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 anthropicPort: 18100,\n openaiPort: 18101,\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\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 anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(config.configFile, JSON.stringify(config, null, 2) + '\\n');\n}\n"],"mappings":"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","names":["path","path","fs","path","import_node_fs","import_node_path","fs","path","http","import_node_fs","import_node_path","path","os","fs"]}
1
+ {"version":3,"sources":["../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/streaming.ts","../src/proxy/handler.ts","../src/proxy/health.ts","../src/proxy/pid.ts","../src/proxy/logger.ts","../src/proxy/config.ts"],"sourcesContent":["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","import {\n SkalpelError,\n SkalpelAuthError,\n SkalpelTimeoutError,\n SkalpelRateLimitError,\n SkalpelUnavailableError,\n} from './errors.js';\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 (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\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 // 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';\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\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 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 c.baseURL = originalBaseURL;\n return result;\n } catch (err) {\n c.baseURL = originalBaseURL;\n throw err;\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\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 // 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 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 result;\n } catch (err) {\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 throw err;\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 throw new Error(`Skalpel context API error ${response.status}: ${text}`);\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\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\nexport async function createSkalpelOpenAI(\n options: SkalpelClientOptions,\n): Promise<import('openai').default> {\n const { default: OpenAI } = await import('openai');\n const baseURL = `${options.baseURL ?? 'https://api.skalpel.ai'}/v1`;\n return new OpenAI({\n baseURL,\n apiKey: options.apiKey,\n defaultHeaders: buildHeaders(options),\n timeout: options.timeout ?? 30000,\n maxRetries: options.retries ?? 2,\n });\n}\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 return new Anthropic({\n baseURL,\n apiKey: options.providerApiKey ?? options.apiKey,\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 { Logger } from './logger.js';\n\nlet proxyStartTime = 0;\n\nexport function startProxy(config: ProxyConfig): { anthropicServer: http.Server; openaiServer: 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);\n return;\n }\n handleRequest(req, res, config, 'claude-code', logger);\n });\n\n const openaiServer = http.createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n handleHealthRequest(res, config, startTime);\n return;\n }\n handleRequest(req, res, config, 'codex', logger);\n });\n\n anthropicServer.listen(config.anthropicPort, () => {\n logger.info(`Anthropic proxy listening on port ${config.anthropicPort}`);\n });\n\n openaiServer.listen(config.openaiPort, () => {\n logger.info(`OpenAI proxy listening on port ${config.openaiPort}`);\n });\n\n writePid(config.pidFile);\n logger.info(`Proxy started (pid=${process.pid}) ports=${config.anthropicPort},${config.openaiPort}`);\n\n const cleanup = () => {\n logger.info('Shutting down proxy...');\n anthropicServer.close();\n openaiServer.close();\n removePid(config.pidFile);\n process.exit(0);\n };\n\n process.on('SIGTERM', cleanup);\n process.on('SIGINT', cleanup);\n\n return { anthropicServer, openaiServer };\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 function getProxyStatus(config: ProxyConfig): ProxyStatus {\n const pid = readPid(config.pidFile);\n return {\n running: pid !== null,\n pid,\n uptime: proxyStartTime > 0 ? Date.now() - proxyStartTime : 0,\n anthropicPort: config.anthropicPort,\n openaiPort: config.openaiPort,\n };\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport type { Logger } from './logger.js';\n\nexport async function handleStreamingRequest(\n _req: IncomingMessage,\n res: ServerResponse,\n _config: ProxyConfig,\n _source: string,\n body: string,\n forwardUrl: string,\n forwardHeaders: Record<string, string>,\n logger: Logger,\n): Promise<void> {\n const HOP_BY_HOP = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n ]);\n\n let response: Response;\n try {\n response = await fetch(forwardUrl, {\n method: 'POST',\n headers: forwardHeaders,\n body,\n });\n } catch (err) {\n logger.error(`streaming fetch failed: ${(err as Error).message}`);\n res.writeHead(502, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n });\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n res.end();\n return;\n }\n\n // Headers to strip: hop-by-hop + content-encoding/content-length (fetch\n // auto-decompresses, so forwarding the original encoding header causes\n // the client to try decompressing already-decompressed data → ZlibError)\n const STRIP_HEADERS = new Set([\n ...HOP_BY_HOP,\n 'content-encoding', 'content-length',\n ]);\n\n // For non-2xx responses, pass through as-is so the SDK can parse error bodies correctly\n if (response.status >= 300) {\n const errorBody = Buffer.from(await response.arrayBuffer());\n logger.error(`streaming upstream error: status=${response.status} body=${errorBody.toString().slice(0, 500)}`);\n const passthroughHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key)) {\n passthroughHeaders[key] = value;\n }\n }\n passthroughHeaders['content-length'] = String(errorBody.length);\n res.writeHead(response.status, passthroughHeaders);\n res.end(errorBody);\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\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n const chunk = decoder.decode(value, { stream: true });\n res.write(chunk);\n }\n } catch (err) {\n logger.error(`streaming error: ${(err as Error).message}`);\n res.write(`event: error\\ndata: ${JSON.stringify({ error: (err as Error).message })}\\n\\n`);\n }\n\n res.end();\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\nimport { handleStreamingRequest } from './streaming.js';\nimport type { Logger } from './logger.js';\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\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\n try {\n const body = await collectBody(req);\n const useSkalpel = shouldRouteToSkalpel(path, source);\n const forwardUrl = `${useSkalpel ? config.remoteBaseUrl : config.anthropicDirectUrl}${path}`;\n\n const forwardHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (value !== undefined) {\n forwardHeaders[key] = Array.isArray(value) ? value.join(', ') : value;\n }\n }\n delete forwardHeaders['host'];\n delete forwardHeaders['connection'];\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\n // By default, requests go through Skalpel's optimization pipeline.\n // The backend resolves provider credentials from its KeyVault/DB.\n // Passthrough mode (skipping optimization) can be enabled per-request\n // by setting the X-Skalpel-Auth-Mode header on the client side.\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 forwardHeaders['x-api-key'] = token;\n }\n // else: OAuth token — leave Authorization: Bearer intact\n }\n }\n }\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\n if (isStreaming) {\n await handleStreamingRequest(req, res, config, source, body, forwardUrl, forwardHeaders, logger);\n logger.info(`${method} ${path} source=${source} streaming latency=${Date.now() - start}ms`);\n return;\n }\n\n const response = await fetch(forwardUrl, {\n method,\n headers: forwardHeaders,\n body: method !== 'GET' && method !== 'HEAD' ? body : undefined,\n });\n\n // Strip hop-by-hop headers that should not be forwarded by a proxy.\n // Also strip 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 (Claude Code) to\n // try to decompress plain text → ZlibError.\n const STRIP_HEADERS = new Set([\n 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',\n 'te', 'trailer', 'transfer-encoding', 'upgrade',\n 'content-encoding', 'content-length',\n ]);\n const responseHeaders: Record<string, string> = {};\n for (const [key, value] of response.headers.entries()) {\n if (!STRIP_HEADERS.has(key)) {\n responseHeaders[key] = value;\n }\n }\n\n const responseBody = Buffer.from(await response.arrayBuffer());\n responseHeaders['content-length'] = String(responseBody.length);\n res.writeHead(response.status, responseHeaders);\n res.end(responseBody);\n\n logger.info(`${method} ${path} source=${source} status=${response.status} latency=${Date.now() - start}ms`);\n } catch (err) {\n logger.error(`${method} ${path} source=${source} error=${(err as Error).message}`);\n if (!res.headersSent) {\n res.writeHead(502, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'proxy_error', message: (err as Error).message }));\n }\n }\n}\n","import type { ServerResponse } from 'node:http';\nimport type { ProxyConfig } from './types.js';\n\nexport function handleHealthRequest(\n res: ServerResponse,\n config: ProxyConfig,\n startTime: number,\n): void {\n const body = JSON.stringify({\n status: 'ok',\n uptime: Date.now() - startTime,\n ports: {\n anthropic: config.anthropicPort,\n openai: config.openaiPort,\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';\n\nexport function writePid(pidFile: string): void {\n fs.mkdirSync(path.dirname(pidFile), { recursive: true });\n fs.writeFileSync(pidFile, String(process.pid));\n}\n\nexport function readPid(pidFile: string): number | null {\n try {\n const raw = fs.readFileSync(pidFile, 'utf-8').trim();\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 removePid(pidFile: string): void {\n try {\n fs.unlinkSync(pidFile);\n } catch {\n // Already removed\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\n constructor(logFile: string, level: keyof typeof LEVELS = 'info') {\n this.logFile = logFile;\n this.level = level;\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 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()}] ${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 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 anthropicPort: 18100,\n openaiPort: 18101,\n logLevel: 'info',\n logFile: '~/.skalpel/logs/proxy.log',\n pidFile: '~/.skalpel/proxy.pid',\n configFile: '~/.skalpel/config.json',\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 anthropicPort: fileConfig.anthropicPort ?? DEFAULTS.anthropicPort,\n openaiPort: fileConfig.openaiPort ?? DEFAULTS.openaiPort,\n logLevel: fileConfig.logLevel ?? DEFAULTS.logLevel,\n logFile: expandHome(fileConfig.logFile ?? DEFAULTS.logFile),\n pidFile: expandHome(fileConfig.pidFile ?? DEFAULTS.pidFile),\n configFile: filePath,\n };\n}\n\nexport function saveConfig(config: ProxyConfig): void {\n const dir = path.dirname(config.configFile);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(config.configFile, JSON.stringify(config, null, 2) + '\\n');\n}\n"],"mappings":"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","names":["path","path","fs","path","import_node_fs","import_node_path","fs","path","http","import_node_fs","import_node_path","path","os","fs"]}