skalpel 1.0.4 → 1.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.
package/README.md CHANGED
@@ -1,116 +1,196 @@
1
- # Skalpel SDK
1
+ # Skalpel
2
2
 
3
- Optimize your OpenAI and Anthropic API calls with Skalpel AI. The SDK transparently reroutes requests through the Skalpel proxy, adding cost optimization, caching, and intelligent model routing — without changing your existing code.
3
+ Optimize Claude Code prompts to reduce credit usage. Skalpel sits between Claude Code and the Anthropic API, optimizing prompts before they reach the providersaving you money without changing how you work.
4
4
 
5
- ## Installation
5
+ ## How It Works
6
6
 
7
- ```bash
8
- npm install skalpel
9
7
  ```
8
+ Claude Code --> Skalpel Proxy (local) --> Skalpel Backend (AWS) --> Anthropic API
9
+ Intercepts requests Optimizes prompts Returns response
10
+ Adds tracking headers Reduces token usage
11
+ ```
12
+
13
+ 1. A local proxy runs on your machine (port 18100)
14
+ 2. Claude Code is configured to send API requests to the proxy instead of `api.anthropic.com`
15
+ 3. The proxy forwards requests to the Skalpel backend, which optimizes prompts for lower token usage
16
+ 4. The optimized request is sent to Anthropic, and the response streams back to Claude Code
17
+
18
+ Your Anthropic API key stays in the request chain — Skalpel never stores it.
10
19
 
11
20
  ## Quick Start
12
21
 
13
- ### Wrapper Pattern
22
+ One command to install, detect Claude Code, and start optimizing:
14
23
 
15
- Wrap your existing client to route all calls through Skalpel with automatic fallback:
24
+ ```bash
25
+ npx skalpel --api-key sk-skalpel-YOUR_KEY --auto
26
+ ```
16
27
 
17
- ```typescript
18
- import OpenAI from 'openai';
19
- import { createSkalpelClient } from 'skalpel';
28
+ This will:
29
+ - Start the local proxy on ports 18100 (Anthropic) and 18101 (OpenAI)
30
+ - Detect Claude Code on your machine
31
+ - Configure `~/.claude/settings.json` with `ANTHROPIC_BASE_URL=http://localhost:18100`
32
+ - Set shell environment variables in your `.bashrc`/`.zshrc`
33
+ - Begin optimizing all Claude Code API traffic immediately
20
34
 
21
- const openai = createSkalpelClient(new OpenAI(), {
22
- apiKey: process.env.SKALPEL_API_KEY!,
23
- workspace: 'my-workspace',
24
- });
35
+ ### Interactive Setup
25
36
 
26
- // Use exactly like the OpenAI client
27
- const response = await openai.chat.completions.create({
28
- model: 'gpt-4o',
29
- messages: [{ role: 'user', content: 'Hello' }],
30
- });
37
+ For a guided setup with prompts:
38
+
39
+ ```bash
40
+ npx skalpel
31
41
  ```
32
42
 
33
- ### URL Swap Pattern
43
+ The wizard walks you through API key entry, agent detection, and proxy configuration.
34
44
 
35
- The simplest integration — one line change:
45
+ ### Manual Setup
36
46
 
37
- ```typescript
38
- import { createSkalpelOpenAI } from 'skalpel';
47
+ ```bash
48
+ # 1. Start the proxy
49
+ npx skalpel start
39
50
 
40
- const openai = await createSkalpelOpenAI({
41
- apiKey: process.env.SKALPEL_API_KEY!,
42
- });
51
+ # 2. Run the setup wizard to configure Claude Code
52
+ npx skalpel setup
43
53
 
44
- const response = await openai.chat.completions.create({
45
- model: 'gpt-4o',
46
- messages: [{ role: 'user', content: 'Hello' }],
47
- });
54
+ # 3. Verify everything is working
55
+ npx skalpel doctor
56
+ ```
57
+
58
+ ## What Gets Configured
59
+
60
+ ### Claude Code (`~/.claude/settings.json`)
61
+
62
+ Skalpel adds an environment override so Claude Code routes API calls through the local proxy:
63
+
64
+ ```json
65
+ {
66
+ "env": {
67
+ "ANTHROPIC_BASE_URL": "http://localhost:18100"
68
+ }
69
+ }
48
70
  ```
49
71
 
50
- ## CLI Setup
72
+ ### Shell Environment (`~/.bashrc`, `~/.zshrc`)
73
+
74
+ Environment variables are added for tools that read them directly:
51
75
 
52
76
  ```bash
53
- npx skalpel init
77
+ # BEGIN SKALPEL PROXY - do not edit manually
78
+ export ANTHROPIC_BASE_URL="http://localhost:18100"
79
+ export OPENAI_BASE_URL="http://localhost:18101"
80
+ # END SKALPEL PROXY
54
81
  ```
55
82
 
56
- Interactive wizard that detects your project type, AI SDKs, and generates the integration code.
83
+ ### Proxy Config (`~/.skalpel/config.json`)
57
84
 
58
- ## API Reference
85
+ ```json
86
+ {
87
+ "apiKey": "sk-skalpel-YOUR_KEY",
88
+ "remoteBaseUrl": "http://skalpel-production-554359744.us-west-2.elb.amazonaws.com",
89
+ "anthropicPort": 18100,
90
+ "openaiPort": 18101
91
+ }
92
+ ```
59
93
 
60
- ### `createSkalpelClient<T>(client: T, options: SkalpelClientOptions): T`
94
+ ## CLI Commands
61
95
 
62
- Wraps an existing OpenAI or Anthropic client instance. All API calls are transparently routed through the Skalpel proxy. Supports automatic fallback to the direct provider on errors.
96
+ | Command | Description |
97
+ |---|---|
98
+ | `npx skalpel` | Run the setup wizard |
99
+ | `npx skalpel start` | Start the proxy |
100
+ | `npx skalpel stop` | Stop the proxy |
101
+ | `npx skalpel status` | Show proxy status and uptime |
102
+ | `npx skalpel doctor` | Verify configuration and connectivity |
103
+ | `npx skalpel logs` | View proxy logs |
104
+ | `npx skalpel logs -f` | Follow proxy logs in real time |
105
+ | `npx skalpel uninstall` | Remove proxy, configs, and shell modifications |
63
106
 
64
- ### `createSkalpelOpenAI(options): Promise<OpenAI>`
107
+ ### Flags
65
108
 
66
- Factory function that creates a pre-configured OpenAI client pointing at the Skalpel proxy.
109
+ | Flag | Description |
110
+ |---|---|
111
+ | `--api-key <key>` | Provide Skalpel API key (skips interactive prompt) |
112
+ | `--auto` | Non-interactive mode (requires `--api-key`) |
67
113
 
68
- ### `createSkalpelAnthropic(options): Promise<Anthropic>`
114
+ ## Streaming Support
115
+
116
+ Skalpel fully supports Anthropic's SSE streaming protocol. When Claude Code sends a streaming request (`"stream": true`), the proxy:
117
+
118
+ - Detects the streaming flag in the request body
119
+ - Forwards to the backend with all original headers
120
+ - Pipes SSE events (`message_start`, `content_block_delta`, `message_stop`) directly back to Claude Code
121
+ - Preserves upstream headers like `anthropic-request-id`
122
+
123
+ No buffering, no modification of the stream — chunks flow through in real time.
69
124
 
70
- Factory function that creates a pre-configured Anthropic client pointing at the Skalpel proxy.
125
+ ## Headers
71
126
 
72
- ### `extractMetadata(headers): SkalpelMetadata | null`
127
+ The proxy adds these headers to every request forwarded to the Skalpel backend:
73
128
 
74
- Extracts optimization metadata from Skalpel response headers (`x-skalpel-*`).
129
+ | Header | Value | Purpose |
130
+ |---|---|---|
131
+ | `X-Skalpel-API-Key` | Your Skalpel key | Backend authentication |
132
+ | `X-Skalpel-Source` | `claude-code` | Traffic source identification |
133
+ | `X-Skalpel-Agent-Type` | `claude-code` | Agent type for optimization routing |
134
+ | `X-Skalpel-SDK-Version` | `proxy-1.0.0` | SDK version tracking |
75
135
 
76
- ## Error Handling
136
+ The original `x-api-key` header (your Anthropic key) is forwarded as-is.
77
137
 
78
- The SDK provides typed error classes:
138
+ ## Codex Support
79
139
 
80
- - `SkalpelAuthError` (401) authentication failed. Never falls back.
81
- - `SkalpelTimeoutError` — request timed out.
82
- - `SkalpelRateLimitError` (429) — rate limit exceeded, includes `retryAfter`.
83
- - `SkalpelUnavailableError` (5xx) — proxy unavailable.
140
+ Skalpel also supports OpenAI Codex on port 18101. The same `--auto` flow detects and configures both agents if present.
84
141
 
85
- When `fallbackOnError: true` (default), the SDK retries with exponential backoff, then falls back to the direct provider. Auth errors always throw immediately.
142
+ ## SDK Integration
86
143
 
87
- ## Configuration
144
+ For programmatic use in your own applications:
145
+
146
+ ```typescript
147
+ import { createSkalpelClient } from 'skalpel';
148
+ import Anthropic from '@anthropic-ai/sdk';
149
+
150
+ const client = createSkalpelClient(new Anthropic(), {
151
+ apiKey: process.env.SKALPEL_API_KEY!,
152
+ });
153
+
154
+ const response = await client.messages.create({
155
+ model: 'claude-sonnet-4-20250514',
156
+ max_tokens: 1024,
157
+ messages: [{ role: 'user', content: 'Hello' }],
158
+ });
159
+ ```
160
+
161
+ See the [API Reference](#api-reference) for all SDK options.
162
+
163
+ ## API Reference
164
+
165
+ ### `createSkalpelClient<T>(client: T, options: SkalpelClientOptions): T`
166
+
167
+ Wraps an existing OpenAI or Anthropic client. All API calls route through the Skalpel proxy with automatic fallback on errors.
168
+
169
+ ### `createSkalpelAnthropic(options): Promise<Anthropic>`
170
+
171
+ Creates a pre-configured Anthropic client pointing at the Skalpel proxy.
172
+
173
+ ### `createSkalpelOpenAI(options): Promise<OpenAI>`
174
+
175
+ Creates a pre-configured OpenAI client pointing at the Skalpel proxy.
176
+
177
+ ### Configuration Options
88
178
 
89
179
  ```typescript
90
180
  interface SkalpelClientOptions {
91
181
  apiKey: string; // Skalpel API key (sk-skalpel-*)
92
182
  workspace?: string; // Workspace ID
93
- baseURL?: string; // Proxy URL (default: https://api.skalpel.ai)
183
+ baseURL?: string; // Proxy URL (default: production ALB)
94
184
  fallbackOnError?: boolean; // Fall back to direct provider (default: true)
95
185
  timeout?: number; // Request timeout in ms (default: 30000)
96
186
  retries?: number; // Max retries (default: 2)
97
- headers?: Record<string, string>;// Additional custom headers
98
- verbose?: boolean; // Log fallback events (default: false)
99
- onFallback?: (error, provider) => void; // Callback on fallback
100
- onMetadata?: (metadata) => void; // Callback with optimization info
101
187
  }
102
188
  ```
103
189
 
104
- ## TypeScript
105
-
106
- The SDK is written in TypeScript and ships with full type declarations. All types are exported:
190
+ ## Uninstall
107
191
 
108
- ```typescript
109
- import type {
110
- SkalpelClientOptions,
111
- SkalpelMetadata,
112
- SkalpelConfig,
113
- SupportedProvider,
114
- InitConfig,
115
- } from 'skalpel';
192
+ ```bash
193
+ npx skalpel uninstall
116
194
  ```
195
+
196
+ This removes the proxy, restores `~/.claude/settings.json` from backup, and cleans up shell environment variables.
package/dist/index.cjs CHANGED
@@ -167,7 +167,7 @@ async function withFallback(primaryFn, fallbackFn, options = {}) {
167
167
  }
168
168
 
169
169
  // src/version.ts
170
- var VERSION = "1.0.4";
170
+ var VERSION = "1.0.5";
171
171
 
172
172
  // src/client.ts
173
173
  function resolveConfig(options) {
@@ -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.4';\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\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 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 forwardUrl = `${config.remoteBaseUrl}${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 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 // Agent integrations (Claude Code, Codex) forward the client's own\n // provider auth (e.g. x-api-key from Claude Code's OAuth). Tell\n // the backend to skip stored-key lookups and use it directly.\n if (source === 'claude-code' || source === 'codex') {\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n }\n\n // Claude Code with OAuth sends Authorization: Bearer <token> instead\n // of x-api-key. Ensure x-api-key is set for the Anthropic source so\n // the backend adapter can find it regardless of which header format\n // the client used.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n forwardHeaders['x-api-key'] = authHeader.slice(7).trim();\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 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 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 // 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\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 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 forwardUrl = `${config.remoteBaseUrl}${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 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 // Agent integrations (Claude Code, Codex) forward the client's own\n // provider auth (e.g. x-api-key from Claude Code's OAuth). Tell\n // the backend to skip stored-key lookups and use it directly.\n if (source === 'claude-code' || source === 'codex') {\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n }\n\n // Claude Code with OAuth sends Authorization: Bearer <token> instead\n // of x-api-key. Ensure x-api-key is set for the Anthropic source so\n // the backend adapter can find it regardless of which header format\n // the client used.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n forwardHeaders['x-api-key'] = authHeader.slice(7).trim();\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 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 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"]}
package/dist/index.js CHANGED
@@ -115,7 +115,7 @@ async function withFallback(primaryFn, fallbackFn, options = {}) {
115
115
  }
116
116
 
117
117
  // src/version.ts
118
- var VERSION = "1.0.4";
118
+ var VERSION = "1.0.5";
119
119
 
120
120
  // src/client.ts
121
121
  function resolveConfig(options) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../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":["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.4';\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\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 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 forwardUrl = `${config.remoteBaseUrl}${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 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 // Agent integrations (Claude Code, Codex) forward the client's own\n // provider auth (e.g. x-api-key from Claude Code's OAuth). Tell\n // the backend to skip stored-key lookups and use it directly.\n if (source === 'claude-code' || source === 'codex') {\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n }\n\n // Claude Code with OAuth sends Authorization: Bearer <token> instead\n // of x-api-key. Ensure x-api-key is set for the Anthropic source so\n // the backend adapter can find it regardless of which header format\n // the client used.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n forwardHeaders['x-api-key'] = authHeader.slice(7).trim();\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 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 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","fs","path"]}
1
+ {"version":3,"sources":["../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":["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\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 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 forwardUrl = `${config.remoteBaseUrl}${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 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 // Agent integrations (Claude Code, Codex) forward the client's own\n // provider auth (e.g. x-api-key from Claude Code's OAuth). Tell\n // the backend to skip stored-key lookups and use it directly.\n if (source === 'claude-code' || source === 'codex') {\n forwardHeaders['X-Skalpel-Auth-Mode'] = 'passthrough';\n }\n\n // Claude Code with OAuth sends Authorization: Bearer <token> instead\n // of x-api-key. Ensure x-api-key is set for the Anthropic source so\n // the backend adapter can find it regardless of which header format\n // the client used.\n if (source === 'claude-code' && !forwardHeaders['x-api-key']) {\n const authHeader = forwardHeaders['authorization'] ?? '';\n if (authHeader.toLowerCase().startsWith('bearer ')) {\n forwardHeaders['x-api-key'] = authHeader.slice(7).trim();\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 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 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","fs","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skalpel",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "Skalpel AI SDK — optimize your OpenAI and Anthropic API calls",
6
6
  "main": "./dist/index.cjs",
@@ -24,6 +24,7 @@
24
24
  "test": "vitest run",
25
25
  "test:watch": "vitest",
26
26
  "typecheck": "tsc --noEmit",
27
+ "seed-demo-key": "node scripts/seed-demo-key.mjs",
27
28
  "prepublishOnly": "npm run build"
28
29
  },
29
30
  "peerDependencies": {
@@ -42,6 +43,7 @@
42
43
  "@anthropic-ai/sdk": "^0.30.0",
43
44
  "@vitest/coverage-v8": "^2.0.0",
44
45
  "openai": "^4.0.0",
46
+ "pg": "^8.13.0",
45
47
  "tsup": "^8.0.0",
46
48
  "typescript": "^5.4.0",
47
49
  "vitest": "^2.0.0"