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 +146 -66
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,116 +1,196 @@
|
|
|
1
|
-
# Skalpel
|
|
1
|
+
# Skalpel
|
|
2
2
|
|
|
3
|
-
Optimize
|
|
3
|
+
Optimize Claude Code prompts to reduce credit usage. Skalpel sits between Claude Code and the Anthropic API, optimizing prompts before they reach the provider — saving you money without changing how you work.
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
|
|
22
|
+
One command to install, detect Claude Code, and start optimizing:
|
|
14
23
|
|
|
15
|
-
|
|
24
|
+
```bash
|
|
25
|
+
npx skalpel --api-key sk-skalpel-YOUR_KEY --auto
|
|
26
|
+
```
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
apiKey: process.env.SKALPEL_API_KEY!,
|
|
23
|
-
workspace: 'my-workspace',
|
|
24
|
-
});
|
|
35
|
+
### Interactive Setup
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
37
|
+
For a guided setup with prompts:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx skalpel
|
|
31
41
|
```
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
The wizard walks you through API key entry, agent detection, and proxy configuration.
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
### Manual Setup
|
|
36
46
|
|
|
37
|
-
```
|
|
38
|
-
|
|
47
|
+
```bash
|
|
48
|
+
# 1. Start the proxy
|
|
49
|
+
npx skalpel start
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
51
|
+
# 2. Run the setup wizard to configure Claude Code
|
|
52
|
+
npx skalpel setup
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
72
|
+
### Shell Environment (`~/.bashrc`, `~/.zshrc`)
|
|
73
|
+
|
|
74
|
+
Environment variables are added for tools that read them directly:
|
|
51
75
|
|
|
52
76
|
```bash
|
|
53
|
-
|
|
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
|
-
|
|
83
|
+
### Proxy Config (`~/.skalpel/config.json`)
|
|
57
84
|
|
|
58
|
-
|
|
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
|
-
|
|
94
|
+
## CLI Commands
|
|
61
95
|
|
|
62
|
-
|
|
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
|
-
###
|
|
107
|
+
### Flags
|
|
65
108
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
+
## Headers
|
|
71
126
|
|
|
72
|
-
|
|
127
|
+
The proxy adds these headers to every request forwarded to the Skalpel backend:
|
|
73
128
|
|
|
74
|
-
|
|
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
|
-
|
|
136
|
+
The original `x-api-key` header (your Anthropic key) is forwarded as-is.
|
|
77
137
|
|
|
78
|
-
|
|
138
|
+
## Codex Support
|
|
79
139
|
|
|
80
|
-
|
|
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
|
-
|
|
142
|
+
## SDK Integration
|
|
86
143
|
|
|
87
|
-
|
|
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:
|
|
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
|
-
##
|
|
105
|
-
|
|
106
|
-
The SDK is written in TypeScript and ships with full type declarations. All types are exported:
|
|
190
|
+
## Uninstall
|
|
107
191
|
|
|
108
|
-
```
|
|
109
|
-
|
|
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
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEO,SAAS,gBACd,SACwB;AACxB,QAAM,MAAM,CAAC,SAAgC;AAC3C,QAAI,mBAAmB,SAAS;AAC9B,aAAO,QAAQ,IAAI,IAAI;AAAA,IACzB;AACA,WAAO,QAAQ,IAAI,KAAK;AAAA,EAC1B;AAEA,QAAM,YAAY,IAAI,sBAAsB;AAC5C,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO;AAAA,IACL;AAAA,IACA,cAAe,IAAI,wBAAwB,KAAyC;AAAA,IACpF,eAAe,IAAI,0BAA0B,KAAK;AAAA,IAClD,aAAa,IAAI,wBAAwB,KAAK;AAAA,IAC9C,YAAY,WAAW,IAAI,uBAAuB,KAAK,GAAG;AAAA,IAC1D,UAAU,IAAI,qBAAqB,MAAM;AAAA,IACzC,WAAW,SAAS,IAAI,sBAAsB,KAAK,KAAK,EAAE;AAAA,EAC5D;AACF;;;ACxBO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACE,SACA,MACA,YACA,YACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,aAAa;AAAA,EACpB;AACF;AAEO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAY,UAAU,iCAAiC;AACrD,UAAM,SAAS,uBAAuB,GAAG;AACzC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,aAAa;AAAA,EACpD,YAAY,UAAU,6BAA6B;AACjD,UAAM,SAAS,iBAAiB;AAChC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAY,UAAU,+BAA+B,YAAqB;AACxE,UAAM,SAAS,wBAAwB,KAAK,UAAU;AACtD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,0BAAN,cAAsC,aAAa;AAAA,EACxD,YACE,UAAU,+BACV,YACA;AACA,UAAM,SAAS,uBAAuB,UAAU;AAChD,SAAK,OAAO;AAAA,EACd;AACF;;;AChCA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEO,SAAS,cAAc,KAA4B;AACxD,MAAI,eAAe,aAAc,QAAO;AAExC,QAAM,QAAQ;AACd,QAAM,SAAS,MAAM,UAAU,MAAM;AACrC,QAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,WAAW,KAAK;AAClB,WAAO,IAAI,iBAAiB,OAAO;AAAA,EACrC;AACA,MAAI,WAAW,KAAK;AAClB,UAAM,aAAa,MAAM,UAAU,aAAa,IAC5C,SAAS,MAAM,QAAQ,aAAa,GAAG,EAAE,IACzC;AACJ,WAAO,IAAI,sBAAsB,SAAS,UAAU;AAAA,EACtD;AACA,MAAI,MAAM,SAAS,eAAe,MAAM,SAAS,aAAa,MAAM,SAAS,2BAA2B;AACtG,WAAO,IAAI,oBAAoB,OAAO;AAAA,EACxC;AACA,MAAI,UAAU,UAAU,KAAK;AAC3B,WAAO,IAAI,wBAAwB,SAAS,MAAM;AAAA,EACpD;AAEA,SAAO,IAAI,wBAAwB,SAAS,MAAM;AACpD;AAEA,eAAsB,aACpB,WACA,YACA,UAA2B,CAAC,GAChB;AACZ,QAAM,EAAE,UAAU,GAAG,UAAU,OAAO,YAAY,WAAW,WAAW,kBAAkB,KAAK,IAAI;AAEnG,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACnD,QAAI;AACF,aAAO,MAAM,UAAU;AAAA,IACzB,SAAS,KAAK;AACZ,kBAAY,cAAc,GAAG;AAG7B,UAAI,qBAAqB,kBAAkB;AACzC,cAAM;AAAA,MACR;AAGA,UAAI,qBAAqB,yBAAyB,UAAU,cAAc,UAAU,SAAS;AAC3F,cAAM,MAAM,UAAU,aAAa,GAAI;AACvC;AAAA,MACF;AAGA,UAAI,UAAU,SAAS;AACrB,cAAM,MAAM,KAAK,IAAI,GAAG,OAAO,IAAI,GAAI;AACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,iBAAiB;AACnB,QAAI,WAAW,WAAW;AACxB,cAAQ,KAAK,oCAAoC,QAAQ,UAAU,UAAU,OAAO,EAAE;AAAA,IACxF;AACA,QAAI,cAAc,WAAW;AAC3B,iBAAW,WAAW,QAAQ;AAAA,IAChC;AACA,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM;AACR;;;AC5FO,IAAM,UAAU;;;ACKvB,SAAS,cAAc,SAA+B;AACpD,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ,WAAW;AAAA,IAC5B,WAAW,QAAQ;AAAA,IACnB,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW,CAAC;AAAA,IAC7B,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,EACtB;AACF;AAEA,SAAS,oBAAoB,QAAkE;AAC7F,QAAM,UAAkC;AAAA,IACtC,iBAAiB,UAAU,OAAO,MAAM;AAAA,IACxC,yBAAyB;AAAA,IACzB,GAAG,OAAO;AAAA,EACZ;AACA,MAAI,OAAO,WAAW;AACpB,YAAQ,qBAAqB,IAAI,OAAO;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,4BAA4B,UAAmB,QAAgD;AACtG,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU;AAC/C,QAAM,OAAO;AAGb,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,UAAU;AACxD,UAAM,MAAM,KAAK;AACjB,QAAI,IAAI,SAAS;AACf,YAAM,WAAW,gBAAgB,IAAI,OAAO;AAC5C,UAAI,UAAU;AACZ,eAAO,aAAa,QAAQ;AAC5B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,UAAU;AACxD,UAAM,KAAK,KAAK;AAChB,UAAM,WAA4B;AAAA,MAChC,WAAY,GAAG,cAAyB;AAAA,MACxC,cAAe,GAAG,gBAAoD;AAAA,MACtE,eAAgB,GAAG,kBAA6B;AAAA,MAChD,aAAc,GAAG,gBAA2B;AAAA,MAC5C,YAAY,OAAO,GAAG,eAAe,CAAC;AAAA,MACtC,UAAU,QAAQ,GAAG,SAAS;AAAA,MAC9B,WAAW,OAAO,GAAG,cAAc,CAAC;AAAA,IACtC;AACA,WAAO,aAAa,QAAQ;AAAA,EAC9B;AACF;AAEA,SAAS,eAAe,QAA0B;AAChD,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,SACE,EAAE,SAAS,UACX,OAAO,EAAE,SAAS,YAClB,EAAE,SAAS,QACX,iBAAkB,EAAE;AAExB;AAEA,SAAS,kBAAkB,QAA0B;AACnD,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,SACE,EAAE,aAAa,UACf,OAAO,EAAE,aAAa,YACtB,EAAE,aAAa,QACf,OAAQ,EAAE,SAAqC,WAAW;AAE9D;AAEA,SAAS,WAAc,QAAW,QAA6C;AAC7E,QAAM,IAAI;AACV,QAAM,iBAAiB,oBAAoB,MAAM;AACjD,QAAM,kBAAkB,EAAE;AAE1B,WAAS,kBACP,QACA,YAC0C;AAC1C,UAAM,iBAAiB,OAAO,UAAU;AAExC,WAAO,kBAAmB,MAAiB;AACzC,YAAM,YAAY,YAAY;AAC5B,UAAE,UAAU,GAAG,OAAO,OAAO;AAG7B,cAAM,cAAc,KAAK,CAAC;AAC1B,cAAM,eAAe;AAErB,YAAI;AACJ,YAAI,KAAK,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACvE,gBAAM,OAAO,KAAK,CAAC;AACnB,eAAK,UAAU,EAAE,GAAG,cAAc,GAAI,KAAK,QAA+C;AAC1F,qBAAW,CAAC,aAAa,MAAM,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACjD,OAAO;AACL,qBAAW,CAAC,aAAa,EAAE,SAAS,aAAa,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACtE;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,QAAQ;AAC1D,sCAA4B,QAAQ,MAAM;AAC1C,YAAE,UAAU;AACZ,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,YAAE,UAAU;AACZ,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,aAAa,YAAY;AAC7B,UAAE,UAAU;AACZ,eAAO,eAAe,MAAM,QAAQ,IAAI;AAAA,MAC1C;AAEA,aAAO,aAAa,WAAW,YAAY;AAAA,QACzC,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,UAAU;AAAA,QACV,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,WAAS,qBAAqB,WAA6D;AACzF,WAAO,IAAI,MAAM,WAAW;AAAA,MAC1B,IAAI,QAAQ,MAAM,UAAU;AAC1B,cAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,YAAI,OAAO,UAAU,eAAe,SAAS,YAAY,SAAS,WAAW;AAC3E,iBAAO,kBAAkB,QAAQ,IAAc;AAAA,QACjD;AACA,YAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,iBAAO,qBAAqB,KAAgC;AAAA,QAC9D;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,MAAM,QAAkB;AAAA,IACjC,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UACE,OAAO,UAAU,YACjB,UAAU,SACT,SAAS,UAAU,SAAS,iBAAiB,SAAS,eACvD;AACA,eAAO,qBAAqB,KAAgC;AAAA,MAC9D;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAiB,QAAW,QAA6C;AAChF,QAAM,IAAI;AACV,QAAM,iBAAiB,oBAAoB,MAAM;AACjD,QAAM,kBAAkB,EAAE,WAAY,EAAE,SAAqC;AAE7E,WAAS,kBACP,QACA,YAC0C;AAC1C,UAAM,iBAAiB,OAAO,UAAU;AAExC,WAAO,kBAAmB,MAAiB;AAEzC,YAAM,YAAY,YAAY;AAG5B,cAAM,WAAW,OAAO;AACxB,YAAI,aAAa,EAAG,GAAE,UAAU;AAChC,YAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAE7D,cAAM,cAAc,KAAK,CAAC;AAC1B,cAAM,eAAe;AAErB,YAAI;AACJ,YAAI,KAAK,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACvE,gBAAM,OAAO,KAAK,CAAC;AACnB,eAAK,UAAU,EAAE,GAAG,cAAc,GAAI,KAAK,QAA+C;AAC1F,qBAAW,CAAC,aAAa,MAAM,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACjD,OAAO;AACL,qBAAW,CAAC,aAAa,EAAE,SAAS,aAAa,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACtE;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,QAAQ;AAC1D,sCAA4B,QAAQ,MAAM;AAC1C,cAAI,aAAa,EAAG,GAAE,UAAU;AAChC,cAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,cAAI,aAAa,EAAG,GAAE,UAAU;AAChC,cAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,aAAa,YAAY;AAC7B,YAAI,aAAa,EAAG,GAAE,UAAU;AAChC,YAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,eAAO,eAAe,MAAM,QAAQ,IAAI;AAAA,MAC1C;AAEA,aAAO,aAAa,WAAW,YAAY;AAAA,QACzC,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,UAAU;AAAA,QACV,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,QAAkB;AAAA,IACjC,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UAAI,SAAS,cAAc,OAAO,UAAU,YAAY,UAAU,MAAM;AACtE,eAAO,IAAI,MAAM,OAAiB;AAAA,UAChC,IAAI,WAAW,SAAS,aAAa;AACnC,kBAAM,WAAW,QAAQ,IAAI,WAAW,SAAS,WAAW;AAC5D,gBAAI,OAAO,aAAa,eAAe,YAAY,YAAY,YAAY,WAAW;AACpF,qBAAO,kBAAkB,WAAsC,OAAiB;AAAA,YAClF;AACA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEO,SAAS,oBAAuB,QAAW,SAAkC;AAClF,QAAM,SAAS,cAAc,OAAO;AAEpC,MAAI,eAAe,MAAM,GAAG;AAC1B,WAAO,WAAW,QAAQ,MAAM;AAAA,EAClC;AAEA,MAAI,kBAAkB,MAAM,GAAG;AAC7B,WAAO,cAAc,QAAQ,MAAM;AAAA,EACrC;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AA2EA,eAAe,eACb,QACAA,OACA,MACY;AACZ,QAAM,MAAM,GAAG,OAAO,OAAO,kBAAkBA,KAAI;AACnD,QAAM,UAAU,oBAAoB,MAAM;AAC1C,UAAQ,cAAc,IAAI;AAE1B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO,OAAO;AAErE,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,KAAK,IAAI,EAAE;AAAA,IACzE;AACA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B,UAAE;AACA,iBAAa,SAAS;AAAA,EACxB;AACF;AAEA,eAAsB,eACpB,SACA,QAC+B;AAC/B,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAwC,QAAQ,cAAc;AAAA,IAC9E,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,UAAU,OAAO,YAAY;AAAA,EAC/B,CAAC;AACD,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,aAAa,OAAO,IAAI,YAAY;AAAA,IACpC,cAAc,OAAO,IAAI,aAAa;AAAA,IACtC,UAAW,IAAI,YAAwC,CAAC;AAAA,IACxD,UAAW,IAAI,YAAwC;AAAA,IACvD,WAAW,OAAO,IAAI,UAAU;AAAA,EAClC;AACF;AAEA,eAAsB,aACpB,SACA,QACwB;AACxB,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAqD,QAAQ,WAAW;AAAA,IACxF,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO;AAAA,IACpB,QAAQ,OAAO,OAAO,IAAI,CAAC,OAAO;AAAA,MAChC,MAAM,EAAE;AAAA,MACR,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,QAAQ,EAAE,UAAU;AAAA,MACpB,UAAU,EAAE,YAAY;AAAA,MACxB,aAAa,EAAE,cAAc;AAAA,MAC7B,UAAU,EAAE,YAAY;AAAA,IAC1B,EAAE;AAAA,EACJ,CAAC;AACD,UAAQ,IAAI,SAAS,CAAC,GAAG,IAAI,CAAC,UAAU;AAAA,IACtC,IAAI,OAAO,KAAK,EAAE;AAAA,IAClB,aAAa,OAAO,KAAK,YAAY;AAAA,IACrC,YAAY,OAAO,KAAK,WAAW;AAAA,IACnC,MAAM,OAAO,KAAK,IAAI;AAAA,IACtB,WAAW,OAAO,KAAK,UAAU;AAAA,IACjC,QAAQ,KAAK,UAAU,OAAO,OAAO,KAAK,MAAM,IAAI;AAAA,IACpD,UAAU,KAAK,YAAY,OAAO,OAAO,KAAK,QAAQ,IAAI;AAAA,IAC1D,YAAY,OAAO,KAAK,eAAe,CAAC;AAAA,IACxC,UAAW,KAAK,YAAwC;AAAA,IACxD,WAAW,OAAO,KAAK,UAAU;AAAA,EACnC,EAAE;AACJ;AAEA,eAAsB,eACpB,SACA,QAC+B;AAC/B,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAwC,QAAQ,oBAAoB;AAAA,IACpF,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO,cAAc;AAAA,IAClC,OAAO,OAAO;AAAA,IACd,eAAe,OAAO,gBAAgB,CAAC;AAAA,IACvC,OAAO,OAAO,SAAS;AAAA,EACzB,CAAC;AACD,QAAM,QAAS,IAAI,SAAuC,CAAC;AAC3D,SAAO;AAAA,IACL,aAAa,OAAO,IAAI,YAAY;AAAA,IACpC,YAAY,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IAChE,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,MAC1B,MAAM,OAAO,KAAK,IAAI;AAAA,MACtB,QAAQ,KAAK,UAAU,OAAO,OAAO,KAAK,MAAM,IAAI;AAAA,MACpD,UAAU,KAAK,YAAY,OAAO,OAAO,KAAK,QAAQ,IAAI;AAAA,MAC1D,YAAY,OAAO,KAAK,eAAe,CAAC;AAAA,MACxC,OAAO,OAAO,KAAK,SAAS,CAAC;AAAA,MAC7B,QAAQ,OAAO,KAAK,UAAU,UAAU;AAAA,MACxC,UAAW,KAAK,YAAwC;AAAA,MACxD,SAAS,OAAO,KAAK,WAAW,EAAE;AAAA,IACpC,EAAE;AAAA,EACJ;AACF;;;AC7bA,SAAS,aAAa,SAAuD;AAC3E,QAAM,UAAkC;AAAA,IACtC,yBAAyB;AAAA,IACzB,GAAG,QAAQ;AAAA,EACb;AACA,MAAI,QAAQ,WAAW;AACrB,YAAQ,qBAAqB,IAAI,QAAQ;AAAA,EAC3C;AACA,SAAO;AACT;AAEA,eAAsB,oBACpB,SACmC;AACnC,QAAM,EAAE,SAAS,OAAO,IAAI,MAAM,OAAO,QAAQ;AACjD,QAAM,UAAU,GAAG,QAAQ,WAAW,wBAAwB;AAC9D,SAAO,IAAI,OAAO;AAAA,IAChB;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,gBAAgB,aAAa,OAAO;AAAA,IACpC,SAAS,QAAQ,WAAW;AAAA,IAC5B,YAAY,QAAQ,WAAW;AAAA,EACjC,CAAC;AACH;AAEA,eAAsB,uBACpB,SAC8C;AAC9C,QAAM,EAAE,SAAS,UAAU,IAAI,MAAM,OAAO,mBAAmB;AAE/D,QAAM,UAAU,QAAQ,WAAW;AACnC,SAAO,IAAI,UAAU;AAAA,IACnB;AAAA,IACA,QAAQ,QAAQ,kBAAkB,QAAQ;AAAA,IAC1C,gBAAgB;AAAA,MACd,GAAG,aAAa,OAAO;AAAA,MACvB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,IAC3C;AAAA,IACA,SAAS,QAAQ,WAAW;AAAA,IAC5B,YAAY,QAAQ,WAAW;AAAA,EACjC,CAAC;AACH;;;AC5CA,uBAAiB;;;ACIjB,eAAsB,uBACpB,MACA,KACA,SACA,SACA,MACA,YACA,gBACA,QACe;AACf,QAAM,aAAa,oBAAI,IAAI;AAAA,IACzB;AAAA,IAAc;AAAA,IAAc;AAAA,IAAsB;AAAA,IAClD;AAAA,IAAM;AAAA,IAAW;AAAA,IAAqB;AAAA,EACxC,CAAC;AAED,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,YAAY;AAAA,MACjC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,2BAA4B,IAAc,OAAO,EAAE;AAChE,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB,CAAC;AACD,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAQ,IAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,CAAM;AACxF,QAAI,IAAI;AACR;AAAA,EACF;AAGA,MAAI,SAAS,UAAU,KAAK;AAC1B,UAAM,YAAY,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAC1D,WAAO,MAAM,oCAAoC,SAAS,MAAM,SAAS,UAAU,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE;AAC7G,UAAM,qBAA6C,CAAC;AACpD,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,UAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AACxB,2BAAmB,GAAG,IAAI;AAAA,MAC5B;AAAA,IACF;AACA,QAAI,UAAU,SAAS,QAAQ,kBAAkB;AACjD,QAAI,IAAI,SAAS;AACjB;AAAA,EACF;AAGA,QAAM,aAAqC,CAAC;AAC5C,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,QAAI,CAAC,WAAW,IAAI,GAAG,KAAK,QAAQ,kBAAkB,QAAQ,kBAAkB;AAC9E,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AACA,aAAW,cAAc,IAAI;AAC7B,aAAW,eAAe,IAAI;AAC9B,MAAI,UAAU,SAAS,QAAQ,UAAU;AAEzC,MAAI,CAAC,SAAS,MAAM;AAClB,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAO,mBAAmB,CAAC,CAAC;AAAA;AAAA,CAAM;AACpF,QAAI,IAAI;AACR;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAU,SAAS,KAAoC,UAAU;AACvE,UAAM,UAAU,IAAI,YAAY;AAEhC,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,YAAM,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AACpD,UAAI,MAAM,KAAK;AAAA,IACjB;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,oBAAqB,IAAc,OAAO,EAAE;AACzD,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAQ,IAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,CAAM;AAAA,EAC1F;AAEA,MAAI,IAAI;AACV;;;AChFA,SAAS,YAAY,KAAuC;AAC1D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC,CAAC;AAC7D,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,eAAsB,cACpB,KACA,KACA,QACA,QACA,QACe;AACf,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAMC,QAAO,IAAI,OAAO;AAExB,MAAI;AACF,UAAM,OAAO,MAAM,YAAY,GAAG;AAClC,UAAM,aAAa,GAAG,OAAO,aAAa,GAAGA,KAAI;AAEjD,UAAM,iBAAyC,CAAC;AAChD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACtD,UAAI,UAAU,QAAW;AACvB,uBAAe,GAAG,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,MAClE;AAAA,IACF;AACA,WAAO,eAAe,MAAM;AAC5B,WAAO,eAAe,YAAY;AAElC,mBAAe,mBAAmB,IAAI,OAAO;AAC7C,mBAAe,kBAAkB,IAAI;AACrC,mBAAe,sBAAsB,IAAI;AACzC,mBAAe,uBAAuB,IAAI;AAK1C,QAAI,WAAW,iBAAiB,WAAW,SAAS;AAClD,qBAAe,qBAAqB,IAAI;AAAA,IAC1C;AAMA,QAAI,WAAW,iBAAiB,CAAC,eAAe,WAAW,GAAG;AAC5D,YAAM,aAAa,eAAe,eAAe,KAAK;AACtD,UAAI,WAAW,YAAY,EAAE,WAAW,SAAS,GAAG;AAClD,uBAAe,WAAW,IAAI,WAAW,MAAM,CAAC,EAAE,KAAK;AAAA,MACzD;AAAA,IACF;AAEA,QAAI,cAAc;AAClB,QAAI,MAAM;AACR,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,sBAAc,OAAO,WAAW;AAAA,MAClC,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,aAAa;AACf,YAAM,uBAAuB,KAAK,KAAK,QAAQ,QAAQ,MAAM,YAAY,gBAAgB,MAAM;AAC/F,aAAO,KAAK,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,sBAAsB,KAAK,IAAI,IAAI,KAAK,IAAI;AAC1F;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC;AAAA,MACA,SAAS;AAAA,MACT,MAAM,WAAW,SAAS,WAAW,SAAS,OAAO;AAAA,IACvD,CAAC;AAGD,UAAM,aAAa,oBAAI,IAAI;AAAA,MACzB;AAAA,MAAc;AAAA,MAAc;AAAA,MAAsB;AAAA,MAClD;AAAA,MAAM;AAAA,MAAW;AAAA,MAAqB;AAAA,IACxC,CAAC;AACD,UAAM,kBAA0C,CAAC;AACjD,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,UAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AACxB,wBAAgB,GAAG,IAAI;AAAA,MACzB;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,QAAQ,eAAe;AAC9C,UAAM,eAAe,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAC7D,QAAI,IAAI,YAAY;AAEpB,WAAO,KAAK,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,WAAW,SAAS,MAAM,YAAY,KAAK,IAAI,IAAI,KAAK,IAAI;AAAA,EAC5G,SAAS,KAAK;AACZ,WAAO,MAAM,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,UAAW,IAAc,OAAO,EAAE;AACjF,QAAI,CAAC,IAAI,aAAa;AACpB,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,SAAU,IAAc,QAAQ,CAAC,CAAC;AAAA,IACnF;AAAA,EACF;AACF;;;ACxGO,SAAS,oBACd,KACA,QACA,WACM;AACN,QAAM,OAAO,KAAK,UAAU;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ,KAAK,IAAI,IAAI;AAAA,IACrB,OAAO;AAAA,MACL,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,IACjB;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AAED,MAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,MAAI,IAAI,IAAI;AACd;;;ACpBA,qBAAe;AACf,uBAAiB;AAEV,SAAS,SAAS,SAAuB;AAC9C,iBAAAC,QAAG,UAAU,iBAAAC,QAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,iBAAAD,QAAG,cAAc,SAAS,OAAO,QAAQ,GAAG,CAAC;AAC/C;AAEO,SAAS,QAAQ,SAAgC;AACtD,MAAI;AACF,UAAM,MAAM,eAAAA,QAAG,aAAa,SAAS,OAAO,EAAE,KAAK;AACnD,UAAM,MAAM,SAAS,KAAK,EAAE;AAC5B,QAAI,MAAM,GAAG,EAAG,QAAO;AACvB,WAAO,UAAU,GAAG,IAAI,MAAM;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,KAAsB;AAC9C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,SAAuB;AAC/C,MAAI;AACF,mBAAAA,QAAG,WAAW,OAAO;AAAA,EACvB,QAAQ;AAAA,EAER;AACF;;;AClCA,IAAAE,kBAAe;AACf,IAAAC,oBAAiB;AAEjB,IAAM,WAAW,IAAI,OAAO;AAC5B,IAAM,gBAAgB;AAEtB,IAAM,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE;AAE/C,IAAM,SAAN,MAAa;AAAA,EACV;AAAA,EACA;AAAA,EAER,YAAY,SAAiB,QAA6B,QAAQ;AAChE,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,oBAAAC,QAAG,UAAU,kBAAAC,QAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,KAAmB;AAAE,SAAK,IAAI,SAAS,GAAG;AAAA,EAAG;AAAA,EACnD,KAAK,KAAmB;AAAE,SAAK,IAAI,QAAQ,GAAG;AAAA,EAAG;AAAA,EACjD,KAAK,KAAmB;AAAE,SAAK,IAAI,QAAQ,GAAG;AAAA,EAAG;AAAA,EACjD,MAAM,KAAmB;AAAE,SAAK,IAAI,SAAS,GAAG;AAAA,EAAG;AAAA,EAE3C,IAAI,OAA4B,KAAmB;AACzD,QAAI,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,EAAG;AAExC,UAAM,OAAO,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,MAAM,YAAY,CAAC,KAAK,GAAG;AAAA;AAExE,QAAI,UAAU,WAAW,UAAU,SAAS;AAC1C,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B;AAEA,QAAI;AACF,WAAK,OAAO;AACZ,sBAAAD,QAAG,eAAe,KAAK,SAAS,IAAI;AAAA,IACtC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,QAAI;AACF,YAAM,OAAO,gBAAAA,QAAG,SAAS,KAAK,OAAO;AACrC,UAAI,KAAK,OAAO,SAAU;AAAA,IAC5B,QAAQ;AACN;AAAA,IACF;AAEA,aAAS,IAAI,eAAe,KAAK,GAAG,KAAK;AACvC,YAAM,MAAM,MAAM,IAAI,KAAK,UAAU,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC;AAC7D,YAAM,MAAM,GAAG,KAAK,OAAO,IAAI,CAAC;AAChC,UAAI;AACF,wBAAAA,QAAG,WAAW,KAAK,GAAG;AAAA,MACxB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;ALnDA,IAAI,iBAAiB;AAEd,SAAS,WAAW,QAAkF;AAC3G,QAAM,SAAS,IAAI,OAAO,OAAO,SAAS,OAAO,QAAQ;AACzD,QAAM,YAAY,KAAK,IAAI;AAC3B,mBAAiB,KAAK,IAAI;AAE1B,QAAM,kBAAkB,iBAAAE,QAAK,aAAa,CAAC,KAAK,QAAQ;AACtD,QAAI,IAAI,QAAQ,aAAa,IAAI,WAAW,OAAO;AACjD,0BAAoB,KAAK,QAAQ,SAAS;AAC1C;AAAA,IACF;AACA,kBAAc,KAAK,KAAK,QAAQ,eAAe,MAAM;AAAA,EACvD,CAAC;AAED,QAAM,eAAe,iBAAAA,QAAK,aAAa,CAAC,KAAK,QAAQ;AACnD,QAAI,IAAI,QAAQ,aAAa,IAAI,WAAW,OAAO;AACjD,0BAAoB,KAAK,QAAQ,SAAS;AAC1C;AAAA,IACF;AACA,kBAAc,KAAK,KAAK,QAAQ,SAAS,MAAM;AAAA,EACjD,CAAC;AAED,kBAAgB,OAAO,OAAO,eAAe,MAAM;AACjD,WAAO,KAAK,qCAAqC,OAAO,aAAa,EAAE;AAAA,EACzE,CAAC;AAED,eAAa,OAAO,OAAO,YAAY,MAAM;AAC3C,WAAO,KAAK,kCAAkC,OAAO,UAAU,EAAE;AAAA,EACnE,CAAC;AAED,WAAS,OAAO,OAAO;AACvB,SAAO,KAAK,sBAAsB,QAAQ,GAAG,WAAW,OAAO,aAAa,IAAI,OAAO,UAAU,EAAE;AAEnG,QAAM,UAAU,MAAM;AACpB,WAAO,KAAK,wBAAwB;AACpC,oBAAgB,MAAM;AACtB,iBAAa,MAAM;AACnB,cAAU,OAAO,OAAO;AACxB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,WAAW,OAAO;AAC7B,UAAQ,GAAG,UAAU,OAAO;AAE5B,SAAO,EAAE,iBAAiB,aAAa;AACzC;AAEO,SAAS,UAAU,QAA8B;AACtD,QAAM,MAAM,QAAQ,OAAO,OAAO;AAClC,MAAI,QAAQ,KAAM,QAAO;AAEzB,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAAA,EAC7B,QAAQ;AAAA,EAER;AAEA,YAAU,OAAO,OAAO;AACxB,SAAO;AACT;AAEO,SAAS,eAAe,QAAkC;AAC/D,QAAM,MAAM,QAAQ,OAAO,OAAO;AAClC,SAAO;AAAA,IACL,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,QAAQ,iBAAiB,IAAI,KAAK,IAAI,IAAI,iBAAiB;AAAA,IAC3D,eAAe,OAAO;AAAA,IACtB,YAAY,OAAO;AAAA,EACrB;AACF;;;AM9EA,IAAAC,kBAAe;AACf,IAAAC,oBAAiB;AACjB,qBAAe;AAGf,SAAS,WAAW,UAA0B;AAC5C,MAAI,SAAS,WAAW,GAAG,GAAG;AAC5B,WAAO,kBAAAC,QAAK,KAAK,eAAAC,QAAG,QAAQ,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,EAClD;AACA,SAAO;AACT;AAEA,IAAM,WAAwB;AAAA,EAC5B,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AACd;AAEO,SAAS,WAAW,YAAkC;AAC3D,QAAM,WAAW,WAAW,cAAc,SAAS,UAAU;AAC7D,MAAI,aAAmC,CAAC;AAExC,MAAI;AACF,UAAM,MAAM,gBAAAC,QAAG,aAAa,UAAU,OAAO;AAC7C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AAAA,EAER;AAEA,SAAO;AAAA,IACL,QAAQ,WAAW,UAAU,SAAS;AAAA,IACtC,eAAe,WAAW,iBAAiB,SAAS;AAAA,IACpD,eAAe,WAAW,iBAAiB,SAAS;AAAA,IACpD,YAAY,WAAW,cAAc,SAAS;AAAA,IAC9C,UAAU,WAAW,YAAY,SAAS;AAAA,IAC1C,SAAS,WAAW,WAAW,WAAW,SAAS,OAAO;AAAA,IAC1D,SAAS,WAAW,WAAW,WAAW,SAAS,OAAO;AAAA,IAC1D,YAAY;AAAA,EACd;AACF;AAEO,SAAS,WAAW,QAA2B;AACpD,QAAM,MAAM,kBAAAF,QAAK,QAAQ,OAAO,UAAU;AAC1C,kBAAAE,QAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,kBAAAA,QAAG,cAAc,OAAO,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAC5E;","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
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":";AAEO,SAAS,gBACd,SACwB;AACxB,QAAM,MAAM,CAAC,SAAgC;AAC3C,QAAI,mBAAmB,SAAS;AAC9B,aAAO,QAAQ,IAAI,IAAI;AAAA,IACzB;AACA,WAAO,QAAQ,IAAI,KAAK;AAAA,EAC1B;AAEA,QAAM,YAAY,IAAI,sBAAsB;AAC5C,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO;AAAA,IACL;AAAA,IACA,cAAe,IAAI,wBAAwB,KAAyC;AAAA,IACpF,eAAe,IAAI,0BAA0B,KAAK;AAAA,IAClD,aAAa,IAAI,wBAAwB,KAAK;AAAA,IAC9C,YAAY,WAAW,IAAI,uBAAuB,KAAK,GAAG;AAAA,IAC1D,UAAU,IAAI,qBAAqB,MAAM;AAAA,IACzC,WAAW,SAAS,IAAI,sBAAsB,KAAK,KAAK,EAAE;AAAA,EAC5D;AACF;;;ACxBO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACE,SACA,MACA,YACA,YACA;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,aAAa;AAAA,EACpB;AACF;AAEO,IAAM,mBAAN,cAA+B,aAAa;AAAA,EACjD,YAAY,UAAU,iCAAiC;AACrD,UAAM,SAAS,uBAAuB,GAAG;AACzC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,aAAa;AAAA,EACpD,YAAY,UAAU,6BAA6B;AACjD,UAAM,SAAS,iBAAiB;AAChC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAY,UAAU,+BAA+B,YAAqB;AACxE,UAAM,SAAS,wBAAwB,KAAK,UAAU;AACtD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,0BAAN,cAAsC,aAAa;AAAA,EACxD,YACE,UAAU,+BACV,YACA;AACA,UAAM,SAAS,uBAAuB,UAAU;AAChD,SAAK,OAAO;AAAA,EACd;AACF;;;AChCA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEO,SAAS,cAAc,KAA4B;AACxD,MAAI,eAAe,aAAc,QAAO;AAExC,QAAM,QAAQ;AACd,QAAM,SAAS,MAAM,UAAU,MAAM;AACrC,QAAM,UAAU,MAAM,WAAW;AAEjC,MAAI,WAAW,KAAK;AAClB,WAAO,IAAI,iBAAiB,OAAO;AAAA,EACrC;AACA,MAAI,WAAW,KAAK;AAClB,UAAM,aAAa,MAAM,UAAU,aAAa,IAC5C,SAAS,MAAM,QAAQ,aAAa,GAAG,EAAE,IACzC;AACJ,WAAO,IAAI,sBAAsB,SAAS,UAAU;AAAA,EACtD;AACA,MAAI,MAAM,SAAS,eAAe,MAAM,SAAS,aAAa,MAAM,SAAS,2BAA2B;AACtG,WAAO,IAAI,oBAAoB,OAAO;AAAA,EACxC;AACA,MAAI,UAAU,UAAU,KAAK;AAC3B,WAAO,IAAI,wBAAwB,SAAS,MAAM;AAAA,EACpD;AAEA,SAAO,IAAI,wBAAwB,SAAS,MAAM;AACpD;AAEA,eAAsB,aACpB,WACA,YACA,UAA2B,CAAC,GAChB;AACZ,QAAM,EAAE,UAAU,GAAG,UAAU,OAAO,YAAY,WAAW,WAAW,kBAAkB,KAAK,IAAI;AAEnG,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACnD,QAAI;AACF,aAAO,MAAM,UAAU;AAAA,IACzB,SAAS,KAAK;AACZ,kBAAY,cAAc,GAAG;AAG7B,UAAI,qBAAqB,kBAAkB;AACzC,cAAM;AAAA,MACR;AAGA,UAAI,qBAAqB,yBAAyB,UAAU,cAAc,UAAU,SAAS;AAC3F,cAAM,MAAM,UAAU,aAAa,GAAI;AACvC;AAAA,MACF;AAGA,UAAI,UAAU,SAAS;AACrB,cAAM,MAAM,KAAK,IAAI,GAAG,OAAO,IAAI,GAAI;AACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,iBAAiB;AACnB,QAAI,WAAW,WAAW;AACxB,cAAQ,KAAK,oCAAoC,QAAQ,UAAU,UAAU,OAAO,EAAE;AAAA,IACxF;AACA,QAAI,cAAc,WAAW;AAC3B,iBAAW,WAAW,QAAQ;AAAA,IAChC;AACA,WAAO,WAAW;AAAA,EACpB;AAEA,QAAM;AACR;;;AC5FO,IAAM,UAAU;;;ACKvB,SAAS,cAAc,SAA+B;AACpD,SAAO;AAAA,IACL,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ,WAAW;AAAA,IAC5B,WAAW,QAAQ;AAAA,IACnB,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW;AAAA,IAC5B,SAAS,QAAQ,WAAW,CAAC;AAAA,IAC7B,YAAY,QAAQ;AAAA,IACpB,YAAY,QAAQ;AAAA,EACtB;AACF;AAEA,SAAS,oBAAoB,QAAkE;AAC7F,QAAM,UAAkC;AAAA,IACtC,iBAAiB,UAAU,OAAO,MAAM;AAAA,IACxC,yBAAyB;AAAA,IACzB,GAAG,OAAO;AAAA,EACZ;AACA,MAAI,OAAO,WAAW;AACpB,YAAQ,qBAAqB,IAAI,OAAO;AAAA,EAC1C;AACA,SAAO;AACT;AAEA,SAAS,4BAA4B,UAAmB,QAAgD;AACtG,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU;AAC/C,QAAM,OAAO;AAGb,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,UAAU;AACxD,UAAM,MAAM,KAAK;AACjB,QAAI,IAAI,SAAS;AACf,YAAM,WAAW,gBAAgB,IAAI,OAAO;AAC5C,UAAI,UAAU;AACZ,eAAO,aAAa,QAAQ;AAC5B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,KAAK,aAAa,OAAO,KAAK,cAAc,UAAU;AACxD,UAAM,KAAK,KAAK;AAChB,UAAM,WAA4B;AAAA,MAChC,WAAY,GAAG,cAAyB;AAAA,MACxC,cAAe,GAAG,gBAAoD;AAAA,MACtE,eAAgB,GAAG,kBAA6B;AAAA,MAChD,aAAc,GAAG,gBAA2B;AAAA,MAC5C,YAAY,OAAO,GAAG,eAAe,CAAC;AAAA,MACtC,UAAU,QAAQ,GAAG,SAAS;AAAA,MAC9B,WAAW,OAAO,GAAG,cAAc,CAAC;AAAA,IACtC;AACA,WAAO,aAAa,QAAQ;AAAA,EAC9B;AACF;AAEA,SAAS,eAAe,QAA0B;AAChD,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,SACE,EAAE,SAAS,UACX,OAAO,EAAE,SAAS,YAClB,EAAE,SAAS,QACX,iBAAkB,EAAE;AAExB;AAEA,SAAS,kBAAkB,QAA0B;AACnD,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AACV,SACE,EAAE,aAAa,UACf,OAAO,EAAE,aAAa,YACtB,EAAE,aAAa,QACf,OAAQ,EAAE,SAAqC,WAAW;AAE9D;AAEA,SAAS,WAAc,QAAW,QAA6C;AAC7E,QAAM,IAAI;AACV,QAAM,iBAAiB,oBAAoB,MAAM;AACjD,QAAM,kBAAkB,EAAE;AAE1B,WAAS,kBACP,QACA,YAC0C;AAC1C,UAAM,iBAAiB,OAAO,UAAU;AAExC,WAAO,kBAAmB,MAAiB;AACzC,YAAM,YAAY,YAAY;AAC5B,UAAE,UAAU,GAAG,OAAO,OAAO;AAG7B,cAAM,cAAc,KAAK,CAAC;AAC1B,cAAM,eAAe;AAErB,YAAI;AACJ,YAAI,KAAK,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACvE,gBAAM,OAAO,KAAK,CAAC;AACnB,eAAK,UAAU,EAAE,GAAG,cAAc,GAAI,KAAK,QAA+C;AAC1F,qBAAW,CAAC,aAAa,MAAM,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACjD,OAAO;AACL,qBAAW,CAAC,aAAa,EAAE,SAAS,aAAa,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACtE;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,QAAQ;AAC1D,sCAA4B,QAAQ,MAAM;AAC1C,YAAE,UAAU;AACZ,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,YAAE,UAAU;AACZ,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,aAAa,YAAY;AAC7B,UAAE,UAAU;AACZ,eAAO,eAAe,MAAM,QAAQ,IAAI;AAAA,MAC1C;AAEA,aAAO,aAAa,WAAW,YAAY;AAAA,QACzC,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,UAAU;AAAA,QACV,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,WAAS,qBAAqB,WAA6D;AACzF,WAAO,IAAI,MAAM,WAAW;AAAA,MAC1B,IAAI,QAAQ,MAAM,UAAU;AAC1B,cAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,YAAI,OAAO,UAAU,eAAe,SAAS,YAAY,SAAS,WAAW;AAC3E,iBAAO,kBAAkB,QAAQ,IAAc;AAAA,QACjD;AACA,YAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,iBAAO,qBAAqB,KAAgC;AAAA,QAC9D;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,MAAM,QAAkB;AAAA,IACjC,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UACE,OAAO,UAAU,YACjB,UAAU,SACT,SAAS,UAAU,SAAS,iBAAiB,SAAS,eACvD;AACA,eAAO,qBAAqB,KAAgC;AAAA,MAC9D;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAiB,QAAW,QAA6C;AAChF,QAAM,IAAI;AACV,QAAM,iBAAiB,oBAAoB,MAAM;AACjD,QAAM,kBAAkB,EAAE,WAAY,EAAE,SAAqC;AAE7E,WAAS,kBACP,QACA,YAC0C;AAC1C,UAAM,iBAAiB,OAAO,UAAU;AAExC,WAAO,kBAAmB,MAAiB;AAEzC,YAAM,YAAY,YAAY;AAG5B,cAAM,WAAW,OAAO;AACxB,YAAI,aAAa,EAAG,GAAE,UAAU;AAChC,YAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAE7D,cAAM,cAAc,KAAK,CAAC;AAC1B,cAAM,eAAe;AAErB,YAAI;AACJ,YAAI,KAAK,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AACvE,gBAAM,OAAO,KAAK,CAAC;AACnB,eAAK,UAAU,EAAE,GAAG,cAAc,GAAI,KAAK,QAA+C;AAC1F,qBAAW,CAAC,aAAa,MAAM,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACjD,OAAO;AACL,qBAAW,CAAC,aAAa,EAAE,SAAS,aAAa,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,QACtE;AAEA,YAAI;AACF,gBAAM,SAAS,MAAM,eAAe,MAAM,QAAQ,QAAQ;AAC1D,sCAA4B,QAAQ,MAAM;AAC1C,cAAI,aAAa,EAAG,GAAE,UAAU;AAChC,cAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,cAAI,aAAa,EAAG,GAAE,UAAU;AAChC,cAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,gBAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,aAAa,YAAY;AAC7B,YAAI,aAAa,EAAG,GAAE,UAAU;AAChC,YAAI,EAAE,WAAW,aAAa,EAAE,QAAS,GAAE,QAAQ,UAAU;AAC7D,eAAO,eAAe,MAAM,QAAQ,IAAI;AAAA,MAC1C;AAEA,aAAO,aAAa,WAAW,YAAY;AAAA,QACzC,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,UAAU;AAAA,QACV,iBAAiB,OAAO;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,QAAkB;AAAA,IACjC,IAAI,QAAQ,MAAM,UAAU;AAC1B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAChD,UAAI,SAAS,cAAc,OAAO,UAAU,YAAY,UAAU,MAAM;AACtE,eAAO,IAAI,MAAM,OAAiB;AAAA,UAChC,IAAI,WAAW,SAAS,aAAa;AACnC,kBAAM,WAAW,QAAQ,IAAI,WAAW,SAAS,WAAW;AAC5D,gBAAI,OAAO,aAAa,eAAe,YAAY,YAAY,YAAY,WAAW;AACpF,qBAAO,kBAAkB,WAAsC,OAAiB;AAAA,YAClF;AACA,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEO,SAAS,oBAAuB,QAAW,SAAkC;AAClF,QAAM,SAAS,cAAc,OAAO;AAEpC,MAAI,eAAe,MAAM,GAAG;AAC1B,WAAO,WAAW,QAAQ,MAAM;AAAA,EAClC;AAEA,MAAI,kBAAkB,MAAM,GAAG;AAC7B,WAAO,cAAc,QAAQ,MAAM;AAAA,EACrC;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AA2EA,eAAe,eACb,QACAA,OACA,MACY;AACZ,QAAM,MAAM,GAAG,OAAO,OAAO,kBAAkBA,KAAI;AACnD,QAAM,UAAU,oBAAoB,MAAM;AAC1C,UAAQ,cAAc,IAAI;AAE1B,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO,OAAO;AAErE,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,WAAW;AAAA,IACrB,CAAC;AACD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,KAAK,IAAI,EAAE;AAAA,IACzE;AACA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B,UAAE;AACA,iBAAa,SAAS;AAAA,EACxB;AACF;AAEA,eAAsB,eACpB,SACA,QAC+B;AAC/B,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAwC,QAAQ,cAAc;AAAA,IAC9E,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,UAAU,OAAO,YAAY;AAAA,EAC/B,CAAC;AACD,SAAO;AAAA,IACL,IAAI,OAAO,IAAI,EAAE;AAAA,IACjB,aAAa,OAAO,IAAI,YAAY;AAAA,IACpC,cAAc,OAAO,IAAI,aAAa;AAAA,IACtC,UAAW,IAAI,YAAwC,CAAC;AAAA,IACxD,UAAW,IAAI,YAAwC;AAAA,IACvD,WAAW,OAAO,IAAI,UAAU;AAAA,EAClC;AACF;AAEA,eAAsB,aACpB,SACA,QACwB;AACxB,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAqD,QAAQ,WAAW;AAAA,IACxF,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO;AAAA,IACpB,QAAQ,OAAO,OAAO,IAAI,CAAC,OAAO;AAAA,MAChC,MAAM,EAAE;AAAA,MACR,YAAY,EAAE;AAAA,MACd,YAAY,EAAE;AAAA,MACd,QAAQ,EAAE,UAAU;AAAA,MACpB,UAAU,EAAE,YAAY;AAAA,MACxB,aAAa,EAAE,cAAc;AAAA,MAC7B,UAAU,EAAE,YAAY;AAAA,IAC1B,EAAE;AAAA,EACJ,CAAC;AACD,UAAQ,IAAI,SAAS,CAAC,GAAG,IAAI,CAAC,UAAU;AAAA,IACtC,IAAI,OAAO,KAAK,EAAE;AAAA,IAClB,aAAa,OAAO,KAAK,YAAY;AAAA,IACrC,YAAY,OAAO,KAAK,WAAW;AAAA,IACnC,MAAM,OAAO,KAAK,IAAI;AAAA,IACtB,WAAW,OAAO,KAAK,UAAU;AAAA,IACjC,QAAQ,KAAK,UAAU,OAAO,OAAO,KAAK,MAAM,IAAI;AAAA,IACpD,UAAU,KAAK,YAAY,OAAO,OAAO,KAAK,QAAQ,IAAI;AAAA,IAC1D,YAAY,OAAO,KAAK,eAAe,CAAC;AAAA,IACxC,UAAW,KAAK,YAAwC;AAAA,IACxD,WAAW,OAAO,KAAK,UAAU;AAAA,EACnC,EAAE;AACJ;AAEA,eAAsB,eACpB,SACA,QAC+B;AAC/B,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,MAAM,MAAM,eAAwC,QAAQ,oBAAoB;AAAA,IACpF,cAAc,OAAO;AAAA,IACrB,aAAa,OAAO,cAAc;AAAA,IAClC,OAAO,OAAO;AAAA,IACd,eAAe,OAAO,gBAAgB,CAAC;AAAA,IACvC,OAAO,OAAO,SAAS;AAAA,EACzB,CAAC;AACD,QAAM,QAAS,IAAI,SAAuC,CAAC;AAC3D,SAAO;AAAA,IACL,aAAa,OAAO,IAAI,YAAY;AAAA,IACpC,YAAY,IAAI,eAAe,OAAO,OAAO,IAAI,WAAW,IAAI;AAAA,IAChE,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,MAC1B,MAAM,OAAO,KAAK,IAAI;AAAA,MACtB,QAAQ,KAAK,UAAU,OAAO,OAAO,KAAK,MAAM,IAAI;AAAA,MACpD,UAAU,KAAK,YAAY,OAAO,OAAO,KAAK,QAAQ,IAAI;AAAA,MAC1D,YAAY,OAAO,KAAK,eAAe,CAAC;AAAA,MACxC,OAAO,OAAO,KAAK,SAAS,CAAC;AAAA,MAC7B,QAAQ,OAAO,KAAK,UAAU,UAAU;AAAA,MACxC,UAAW,KAAK,YAAwC;AAAA,MACxD,SAAS,OAAO,KAAK,WAAW,EAAE;AAAA,IACpC,EAAE;AAAA,EACJ;AACF;;;AC7bA,SAAS,aAAa,SAAuD;AAC3E,QAAM,UAAkC;AAAA,IACtC,yBAAyB;AAAA,IACzB,GAAG,QAAQ;AAAA,EACb;AACA,MAAI,QAAQ,WAAW;AACrB,YAAQ,qBAAqB,IAAI,QAAQ;AAAA,EAC3C;AACA,SAAO;AACT;AAEA,eAAsB,oBACpB,SACmC;AACnC,QAAM,EAAE,SAAS,OAAO,IAAI,MAAM,OAAO,QAAQ;AACjD,QAAM,UAAU,GAAG,QAAQ,WAAW,wBAAwB;AAC9D,SAAO,IAAI,OAAO;AAAA,IAChB;AAAA,IACA,QAAQ,QAAQ;AAAA,IAChB,gBAAgB,aAAa,OAAO;AAAA,IACpC,SAAS,QAAQ,WAAW;AAAA,IAC5B,YAAY,QAAQ,WAAW;AAAA,EACjC,CAAC;AACH;AAEA,eAAsB,uBACpB,SAC8C;AAC9C,QAAM,EAAE,SAAS,UAAU,IAAI,MAAM,OAAO,mBAAmB;AAE/D,QAAM,UAAU,QAAQ,WAAW;AACnC,SAAO,IAAI,UAAU;AAAA,IACnB;AAAA,IACA,QAAQ,QAAQ,kBAAkB,QAAQ;AAAA,IAC1C,gBAAgB;AAAA,MACd,GAAG,aAAa,OAAO;AAAA,MACvB,iBAAiB,UAAU,QAAQ,MAAM;AAAA,IAC3C;AAAA,IACA,SAAS,QAAQ,WAAW;AAAA,IAC5B,YAAY,QAAQ,WAAW;AAAA,EACjC,CAAC;AACH;;;AC5CA,OAAO,UAAU;;;ACIjB,eAAsB,uBACpB,MACA,KACA,SACA,SACA,MACA,YACA,gBACA,QACe;AACf,QAAM,aAAa,oBAAI,IAAI;AAAA,IACzB;AAAA,IAAc;AAAA,IAAc;AAAA,IAAsB;AAAA,IAClD;AAAA,IAAM;AAAA,IAAW;AAAA,IAAqB;AAAA,EACxC,CAAC;AAED,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,YAAY;AAAA,MACjC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,2BAA4B,IAAc,OAAO,EAAE;AAChE,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IACnB,CAAC;AACD,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAQ,IAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,CAAM;AACxF,QAAI,IAAI;AACR;AAAA,EACF;AAGA,MAAI,SAAS,UAAU,KAAK;AAC1B,UAAM,YAAY,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAC1D,WAAO,MAAM,oCAAoC,SAAS,MAAM,SAAS,UAAU,SAAS,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE;AAC7G,UAAM,qBAA6C,CAAC;AACpD,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,UAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AACxB,2BAAmB,GAAG,IAAI;AAAA,MAC5B;AAAA,IACF;AACA,QAAI,UAAU,SAAS,QAAQ,kBAAkB;AACjD,QAAI,IAAI,SAAS;AACjB;AAAA,EACF;AAGA,QAAM,aAAqC,CAAC;AAC5C,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,QAAI,CAAC,WAAW,IAAI,GAAG,KAAK,QAAQ,kBAAkB,QAAQ,kBAAkB;AAC9E,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AACA,aAAW,cAAc,IAAI;AAC7B,aAAW,eAAe,IAAI;AAC9B,MAAI,UAAU,SAAS,QAAQ,UAAU;AAEzC,MAAI,CAAC,SAAS,MAAM;AAClB,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAO,mBAAmB,CAAC,CAAC;AAAA;AAAA,CAAM;AACpF,QAAI,IAAI;AACR;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAU,SAAS,KAAoC,UAAU;AACvE,UAAM,UAAU,IAAI,YAAY;AAEhC,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,YAAM,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AACpD,UAAI,MAAM,KAAK;AAAA,IACjB;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,oBAAqB,IAAc,OAAO,EAAE;AACzD,QAAI,MAAM;AAAA,QAAuB,KAAK,UAAU,EAAE,OAAQ,IAAc,QAAQ,CAAC,CAAC;AAAA;AAAA,CAAM;AAAA,EAC1F;AAEA,MAAI,IAAI;AACV;;;AChFA,SAAS,YAAY,KAAuC;AAC1D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAmB,CAAC;AAC1B,QAAI,GAAG,QAAQ,CAAC,UAAkB,OAAO,KAAK,KAAK,CAAC;AACpD,QAAI,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,CAAC,CAAC;AAC7D,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,eAAsB,cACpB,KACA,KACA,QACA,QACA,QACe;AACf,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,SAAS,IAAI,UAAU;AAC7B,QAAMC,QAAO,IAAI,OAAO;AAExB,MAAI;AACF,UAAM,OAAO,MAAM,YAAY,GAAG;AAClC,UAAM,aAAa,GAAG,OAAO,aAAa,GAAGA,KAAI;AAEjD,UAAM,iBAAyC,CAAC;AAChD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,OAAO,GAAG;AACtD,UAAI,UAAU,QAAW;AACvB,uBAAe,GAAG,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,MAClE;AAAA,IACF;AACA,WAAO,eAAe,MAAM;AAC5B,WAAO,eAAe,YAAY;AAElC,mBAAe,mBAAmB,IAAI,OAAO;AAC7C,mBAAe,kBAAkB,IAAI;AACrC,mBAAe,sBAAsB,IAAI;AACzC,mBAAe,uBAAuB,IAAI;AAK1C,QAAI,WAAW,iBAAiB,WAAW,SAAS;AAClD,qBAAe,qBAAqB,IAAI;AAAA,IAC1C;AAMA,QAAI,WAAW,iBAAiB,CAAC,eAAe,WAAW,GAAG;AAC5D,YAAM,aAAa,eAAe,eAAe,KAAK;AACtD,UAAI,WAAW,YAAY,EAAE,WAAW,SAAS,GAAG;AAClD,uBAAe,WAAW,IAAI,WAAW,MAAM,CAAC,EAAE,KAAK;AAAA,MACzD;AAAA,IACF;AAEA,QAAI,cAAc;AAClB,QAAI,MAAM;AACR,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,sBAAc,OAAO,WAAW;AAAA,MAClC,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,QAAI,aAAa;AACf,YAAM,uBAAuB,KAAK,KAAK,QAAQ,QAAQ,MAAM,YAAY,gBAAgB,MAAM;AAC/F,aAAO,KAAK,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,sBAAsB,KAAK,IAAI,IAAI,KAAK,IAAI;AAC1F;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,MAAM,YAAY;AAAA,MACvC;AAAA,MACA,SAAS;AAAA,MACT,MAAM,WAAW,SAAS,WAAW,SAAS,OAAO;AAAA,IACvD,CAAC;AAGD,UAAM,aAAa,oBAAI,IAAI;AAAA,MACzB;AAAA,MAAc;AAAA,MAAc;AAAA,MAAsB;AAAA,MAClD;AAAA,MAAM;AAAA,MAAW;AAAA,MAAqB;AAAA,IACxC,CAAC;AACD,UAAM,kBAA0C,CAAC;AACjD,eAAW,CAAC,KAAK,KAAK,KAAK,SAAS,QAAQ,QAAQ,GAAG;AACrD,UAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AACxB,wBAAgB,GAAG,IAAI;AAAA,MACzB;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,QAAQ,eAAe;AAC9C,UAAM,eAAe,OAAO,KAAK,MAAM,SAAS,YAAY,CAAC;AAC7D,QAAI,IAAI,YAAY;AAEpB,WAAO,KAAK,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,WAAW,SAAS,MAAM,YAAY,KAAK,IAAI,IAAI,KAAK,IAAI;AAAA,EAC5G,SAAS,KAAK;AACZ,WAAO,MAAM,GAAG,MAAM,IAAIA,KAAI,WAAW,MAAM,UAAW,IAAc,OAAO,EAAE;AACjF,QAAI,CAAC,IAAI,aAAa;AACpB,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,eAAe,SAAU,IAAc,QAAQ,CAAC,CAAC;AAAA,IACnF;AAAA,EACF;AACF;;;ACxGO,SAAS,oBACd,KACA,QACA,WACM;AACN,QAAM,OAAO,KAAK,UAAU;AAAA,IAC1B,QAAQ;AAAA,IACR,QAAQ,KAAK,IAAI,IAAI;AAAA,IACrB,OAAO;AAAA,MACL,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,IACjB;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AAED,MAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,MAAI,IAAI,IAAI;AACd;;;ACpBA,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,SAAS,SAAuB;AAC9C,KAAG,UAAU,KAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,KAAG,cAAc,SAAS,OAAO,QAAQ,GAAG,CAAC;AAC/C;AAEO,SAAS,QAAQ,SAAgC;AACtD,MAAI;AACF,UAAM,MAAM,GAAG,aAAa,SAAS,OAAO,EAAE,KAAK;AACnD,UAAM,MAAM,SAAS,KAAK,EAAE;AAC5B,QAAI,MAAM,GAAG,EAAG,QAAO;AACvB,WAAO,UAAU,GAAG,IAAI,MAAM;AAAA,EAChC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,KAAsB;AAC9C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,UAAU,SAAuB;AAC/C,MAAI;AACF,OAAG,WAAW,OAAO;AAAA,EACvB,QAAQ;AAAA,EAER;AACF;;;AClCA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAEjB,IAAM,WAAW,IAAI,OAAO;AAC5B,IAAM,gBAAgB;AAEtB,IAAM,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE;AAE/C,IAAM,SAAN,MAAa;AAAA,EACV;AAAA,EACA;AAAA,EAER,YAAY,SAAiB,QAA6B,QAAQ;AAChE,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,IAAAD,IAAG,UAAUC,MAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,KAAmB;AAAE,SAAK,IAAI,SAAS,GAAG;AAAA,EAAG;AAAA,EACnD,KAAK,KAAmB;AAAE,SAAK,IAAI,QAAQ,GAAG;AAAA,EAAG;AAAA,EACjD,KAAK,KAAmB;AAAE,SAAK,IAAI,QAAQ,GAAG;AAAA,EAAG;AAAA,EACjD,MAAM,KAAmB;AAAE,SAAK,IAAI,SAAS,GAAG;AAAA,EAAG;AAAA,EAE3C,IAAI,OAA4B,KAAmB;AACzD,QAAI,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,EAAG;AAExC,UAAM,OAAO,IAAG,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,MAAM,YAAY,CAAC,KAAK,GAAG;AAAA;AAExE,QAAI,UAAU,WAAW,UAAU,SAAS;AAC1C,cAAQ,OAAO,MAAM,IAAI;AAAA,IAC3B;AAEA,QAAI;AACF,WAAK,OAAO;AACZ,MAAAD,IAAG,eAAe,KAAK,SAAS,IAAI;AAAA,IACtC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,QAAI;AACF,YAAM,OAAOA,IAAG,SAAS,KAAK,OAAO;AACrC,UAAI,KAAK,OAAO,SAAU;AAAA,IAC5B,QAAQ;AACN;AAAA,IACF;AAEA,aAAS,IAAI,eAAe,KAAK,GAAG,KAAK;AACvC,YAAM,MAAM,MAAM,IAAI,KAAK,UAAU,GAAG,KAAK,OAAO,IAAI,IAAI,CAAC;AAC7D,YAAM,MAAM,GAAG,KAAK,OAAO,IAAI,CAAC;AAChC,UAAI;AACF,QAAAA,IAAG,WAAW,KAAK,GAAG;AAAA,MACxB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;ALnDA,IAAI,iBAAiB;AAEd,SAAS,WAAW,QAAkF;AAC3G,QAAM,SAAS,IAAI,OAAO,OAAO,SAAS,OAAO,QAAQ;AACzD,QAAM,YAAY,KAAK,IAAI;AAC3B,mBAAiB,KAAK,IAAI;AAE1B,QAAM,kBAAkB,KAAK,aAAa,CAAC,KAAK,QAAQ;AACtD,QAAI,IAAI,QAAQ,aAAa,IAAI,WAAW,OAAO;AACjD,0BAAoB,KAAK,QAAQ,SAAS;AAC1C;AAAA,IACF;AACA,kBAAc,KAAK,KAAK,QAAQ,eAAe,MAAM;AAAA,EACvD,CAAC;AAED,QAAM,eAAe,KAAK,aAAa,CAAC,KAAK,QAAQ;AACnD,QAAI,IAAI,QAAQ,aAAa,IAAI,WAAW,OAAO;AACjD,0BAAoB,KAAK,QAAQ,SAAS;AAC1C;AAAA,IACF;AACA,kBAAc,KAAK,KAAK,QAAQ,SAAS,MAAM;AAAA,EACjD,CAAC;AAED,kBAAgB,OAAO,OAAO,eAAe,MAAM;AACjD,WAAO,KAAK,qCAAqC,OAAO,aAAa,EAAE;AAAA,EACzE,CAAC;AAED,eAAa,OAAO,OAAO,YAAY,MAAM;AAC3C,WAAO,KAAK,kCAAkC,OAAO,UAAU,EAAE;AAAA,EACnE,CAAC;AAED,WAAS,OAAO,OAAO;AACvB,SAAO,KAAK,sBAAsB,QAAQ,GAAG,WAAW,OAAO,aAAa,IAAI,OAAO,UAAU,EAAE;AAEnG,QAAM,UAAU,MAAM;AACpB,WAAO,KAAK,wBAAwB;AACpC,oBAAgB,MAAM;AACtB,iBAAa,MAAM;AACnB,cAAU,OAAO,OAAO;AACxB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,WAAW,OAAO;AAC7B,UAAQ,GAAG,UAAU,OAAO;AAE5B,SAAO,EAAE,iBAAiB,aAAa;AACzC;AAEO,SAAS,UAAU,QAA8B;AACtD,QAAM,MAAM,QAAQ,OAAO,OAAO;AAClC,MAAI,QAAQ,KAAM,QAAO;AAEzB,MAAI;AACF,YAAQ,KAAK,KAAK,SAAS;AAAA,EAC7B,QAAQ;AAAA,EAER;AAEA,YAAU,OAAO,OAAO;AACxB,SAAO;AACT;AAEO,SAAS,eAAe,QAAkC;AAC/D,QAAM,MAAM,QAAQ,OAAO,OAAO;AAClC,SAAO;AAAA,IACL,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,QAAQ,iBAAiB,IAAI,KAAK,IAAI,IAAI,iBAAiB;AAAA,IAC3D,eAAe,OAAO;AAAA,IACtB,YAAY,OAAO;AAAA,EACrB;AACF;;;AM9EA,OAAOE,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,QAAQ;AAGf,SAAS,WAAW,UAA0B;AAC5C,MAAI,SAAS,WAAW,GAAG,GAAG;AAC5B,WAAOA,MAAK,KAAK,GAAG,QAAQ,GAAG,SAAS,MAAM,CAAC,CAAC;AAAA,EAClD;AACA,SAAO;AACT;AAEA,IAAM,WAAwB;AAAA,EAC5B,QAAQ;AAAA,EACR,eAAe;AAAA,EACf,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AACd;AAEO,SAAS,WAAW,YAAkC;AAC3D,QAAM,WAAW,WAAW,cAAc,SAAS,UAAU;AAC7D,MAAI,aAAmC,CAAC;AAExC,MAAI;AACF,UAAM,MAAMD,IAAG,aAAa,UAAU,OAAO;AAC7C,iBAAa,KAAK,MAAM,GAAG;AAAA,EAC7B,QAAQ;AAAA,EAER;AAEA,SAAO;AAAA,IACL,QAAQ,WAAW,UAAU,SAAS;AAAA,IACtC,eAAe,WAAW,iBAAiB,SAAS;AAAA,IACpD,eAAe,WAAW,iBAAiB,SAAS;AAAA,IACpD,YAAY,WAAW,cAAc,SAAS;AAAA,IAC9C,UAAU,WAAW,YAAY,SAAS;AAAA,IAC1C,SAAS,WAAW,WAAW,WAAW,SAAS,OAAO;AAAA,IAC1D,SAAS,WAAW,WAAW,WAAW,SAAS,OAAO;AAAA,IAC1D,YAAY;AAAA,EACd;AACF;AAEO,SAAS,WAAW,QAA2B;AACpD,QAAM,MAAMC,MAAK,QAAQ,OAAO,UAAU;AAC1C,EAAAD,IAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,EAAAA,IAAG,cAAc,OAAO,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAC5E;","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.
|
|
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"
|