@askalf/dario 1.0.9 → 1.1.1
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 +101 -23
- package/dist/cli.js +3 -1
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +103 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<a href="#quick-start">Quick Start</a> •
|
|
11
11
|
<a href="#how-it-works">How It Works</a> •
|
|
12
12
|
<a href="#usage-examples">Examples</a> •
|
|
13
|
+
<a href="#cli-backend">CLI Backend</a> •
|
|
13
14
|
<a href="#faq">FAQ</a>
|
|
14
15
|
</p>
|
|
15
16
|
|
|
@@ -32,7 +33,9 @@ That's it. Any tool that speaks the Anthropic API now uses your subscription.
|
|
|
32
33
|
|
|
33
34
|
You pay $100-200/mo for Claude Max or Pro. But that subscription only works on claude.ai and Claude Code. If you want to use Claude with **any other tool** — OpenClaw, Cursor, Continue, Aider, your own scripts — you need a separate API key with separate billing.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
Worse: Anthropic silently throttles subscription users with hidden weekly rate limits. When you hit the wall, Opus and Sonnet return a generic "Error" with no explanation. Only Haiku keeps working.
|
|
37
|
+
|
|
38
|
+
**dario fixes both problems.** It creates a local proxy that translates API key auth into your subscription's OAuth tokens — and with `--cli` mode, routes through the Claude Code binary to bypass rate limits entirely.
|
|
36
39
|
|
|
37
40
|
## Quick Start
|
|
38
41
|
|
|
@@ -64,7 +67,7 @@ dario proxy
|
|
|
64
67
|
```
|
|
65
68
|
|
|
66
69
|
```
|
|
67
|
-
dario
|
|
70
|
+
dario — http://localhost:3456
|
|
68
71
|
|
|
69
72
|
Your Claude subscription is now an API.
|
|
70
73
|
|
|
@@ -73,6 +76,7 @@ Usage:
|
|
|
73
76
|
ANTHROPIC_API_KEY=dario
|
|
74
77
|
|
|
75
78
|
OAuth: healthy (expires in 11h 42m)
|
|
79
|
+
Model: passthrough (client decides)
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
### Use it
|
|
@@ -89,6 +93,59 @@ continue # in VS Code, set base URL in config
|
|
|
89
93
|
python my_script.py
|
|
90
94
|
```
|
|
91
95
|
|
|
96
|
+
## CLI Backend
|
|
97
|
+
|
|
98
|
+
If you're getting rate limited on Opus or Sonnet, use `--cli` mode. This routes requests through the Claude Code binary instead of hitting the API directly — and Claude Code has priority routing that bypasses subscription rate limits.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
dario proxy --cli # Opus works even when rate limited
|
|
102
|
+
dario proxy --cli --model=opus # Force Opus + CLI backend
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
dario — http://localhost:3456
|
|
107
|
+
|
|
108
|
+
Your Claude subscription is now an API.
|
|
109
|
+
|
|
110
|
+
Usage:
|
|
111
|
+
ANTHROPIC_BASE_URL=http://localhost:3456
|
|
112
|
+
ANTHROPIC_API_KEY=dario
|
|
113
|
+
|
|
114
|
+
Backend: Claude CLI (bypasses rate limits)
|
|
115
|
+
Model: claude-opus-4-6 (all requests)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Requirements:** Claude Code must be installed (`npm install -g @anthropic-ai/claude-code` or already installed via the desktop app).
|
|
119
|
+
|
|
120
|
+
**Trade-offs vs direct API mode:**
|
|
121
|
+
|
|
122
|
+
| | Direct API (default) | CLI Backend (`--cli`) |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| Streaming | Yes | No (full response) |
|
|
125
|
+
| Tool use passthrough | Yes | No |
|
|
126
|
+
| Latency | Low | Higher (process spawn) |
|
|
127
|
+
| Rate limits | Subject to weekly quota | Bypassed |
|
|
128
|
+
| Opus when throttled | Blocked | **Works** |
|
|
129
|
+
|
|
130
|
+
## Model Selection
|
|
131
|
+
|
|
132
|
+
Force a specific model for all requests — useful when your tool doesn't let you configure the model:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dario proxy --model=opus # Force Opus 4.6
|
|
136
|
+
dario proxy --model=sonnet # Force Sonnet 4.6
|
|
137
|
+
dario proxy --model=haiku # Force Haiku 4.5
|
|
138
|
+
dario proxy # Passthrough (client decides)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Full model IDs also work: `--model=claude-opus-4-6`
|
|
142
|
+
|
|
143
|
+
Combine with `--cli` for rate-limit-proof Opus:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
dario proxy --cli --model=opus
|
|
147
|
+
```
|
|
148
|
+
|
|
92
149
|
## Usage Examples
|
|
93
150
|
|
|
94
151
|
### curl
|
|
@@ -139,7 +196,7 @@ const message = await client.messages.create({
|
|
|
139
196
|
});
|
|
140
197
|
```
|
|
141
198
|
|
|
142
|
-
### Streaming
|
|
199
|
+
### Streaming (direct API mode only)
|
|
143
200
|
|
|
144
201
|
```bash
|
|
145
202
|
curl http://localhost:3456/v1/messages \
|
|
@@ -157,7 +214,7 @@ curl http://localhost:3456/v1/messages \
|
|
|
157
214
|
|
|
158
215
|
```bash
|
|
159
216
|
# OpenClaw
|
|
160
|
-
ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario openclaw
|
|
217
|
+
ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario openclaw
|
|
161
218
|
|
|
162
219
|
# Aider
|
|
163
220
|
ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario aider --model claude-opus-4-6
|
|
@@ -168,6 +225,8 @@ ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario your-tool-here
|
|
|
168
225
|
|
|
169
226
|
## How It Works
|
|
170
227
|
|
|
228
|
+
### Direct API Mode (default)
|
|
229
|
+
|
|
171
230
|
```
|
|
172
231
|
┌──────────┐ ┌─────────────────┐ ┌──────────────────┐
|
|
173
232
|
│ Your App │ ──> │ dario (proxy) │ ──> │ api.anthropic.com│
|
|
@@ -178,15 +237,23 @@ ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario your-tool-here
|
|
|
178
237
|
└──────────┘ └─────────────────┘ └──────────────────┘
|
|
179
238
|
```
|
|
180
239
|
|
|
240
|
+
### CLI Backend Mode (`--cli`)
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
┌──────────┐ ┌─────────────────┐ ┌──────────────────┐
|
|
244
|
+
│ Your App │ ──> │ dario (proxy) │ ──> │ claude --print │
|
|
245
|
+
│ │ │ localhost:3456 │ │ (Claude Code) │
|
|
246
|
+
│ sends │ │ extracts prompt│ │ │
|
|
247
|
+
│ API │ │ spawns CLI │ │ has priority │
|
|
248
|
+
│ request │ │ wraps response │ │ routing │
|
|
249
|
+
└──────────┘ └─────────────────┘ └──────────────────┘
|
|
250
|
+
```
|
|
251
|
+
|
|
181
252
|
1. **`dario login`** — Standard PKCE OAuth flow. Opens Claude's auth page in your browser. You authorize, dario stores the tokens locally in `~/.dario/credentials.json`. No server involved, no secrets leave your machine.
|
|
182
253
|
|
|
183
|
-
2. **`dario proxy`** — Starts an HTTP server on localhost that implements the
|
|
184
|
-
- Strips the incoming API key (you can use any string)
|
|
185
|
-
- Injects your OAuth access token as a Bearer header
|
|
186
|
-
- Forwards the request to `api.anthropic.com`
|
|
187
|
-
- Streams the response back to your app
|
|
254
|
+
2. **`dario proxy`** — Starts an HTTP server on localhost that implements the Anthropic Messages API. In direct mode, it swaps your API key for an OAuth bearer token. In CLI mode, it routes through the Claude Code binary.
|
|
188
255
|
|
|
189
|
-
3. **Auto-refresh** — OAuth tokens expire. Dario refreshes them automatically in the background every 15 minutes. Refresh tokens rotate on each use.
|
|
256
|
+
3. **Auto-refresh** — OAuth tokens expire. Dario refreshes them automatically in the background every 15 minutes. Refresh tokens rotate on each use.
|
|
190
257
|
|
|
191
258
|
## Commands
|
|
192
259
|
|
|
@@ -203,22 +270,28 @@ ANTHROPIC_BASE_URL=http://localhost:3456 ANTHROPIC_API_KEY=dario your-tool-here
|
|
|
203
270
|
|
|
204
271
|
| Flag | Description | Default |
|
|
205
272
|
|------|-------------|---------|
|
|
273
|
+
| `--cli` | Use Claude CLI as backend (bypasses rate limits) | off |
|
|
274
|
+
| `--model=MODEL` | Force a model (`opus`, `sonnet`, `haiku`, or full ID) | passthrough |
|
|
206
275
|
| `--port=PORT` | Port to listen on | `3456` |
|
|
207
|
-
| `--verbose` / `-v` | Log every request
|
|
276
|
+
| `--verbose` / `-v` | Log every request | off |
|
|
208
277
|
|
|
209
278
|
## Supported Features
|
|
210
279
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- All Claude models (`opus-4-6`, `sonnet-4-6`, `haiku-4-5`)
|
|
280
|
+
### Direct API Mode
|
|
281
|
+
- All Claude models (Opus 4.6, Sonnet 4.6, Haiku 4.5)
|
|
214
282
|
- Streaming and non-streaming
|
|
215
283
|
- Tool use / function calling
|
|
216
284
|
- System prompts and multi-turn conversations
|
|
217
|
-
- Prompt caching
|
|
218
|
-
- Extended thinking
|
|
285
|
+
- Prompt caching and extended thinking
|
|
219
286
|
- All `anthropic-beta` features (headers pass through)
|
|
220
287
|
- CORS enabled (works from browser apps on localhost)
|
|
221
288
|
|
|
289
|
+
### CLI Backend Mode
|
|
290
|
+
- All Claude models — including Opus when rate limited
|
|
291
|
+
- Non-streaming responses
|
|
292
|
+
- System prompts and multi-turn conversations (via context injection)
|
|
293
|
+
- Bypasses subscription rate limits
|
|
294
|
+
|
|
222
295
|
## Endpoints
|
|
223
296
|
|
|
224
297
|
| Path | Description |
|
|
@@ -249,14 +322,16 @@ curl http://localhost:3456/health
|
|
|
249
322
|
| Credential storage | `~/.dario/credentials.json` with `0600` permissions (owner-only) |
|
|
250
323
|
| OAuth flow | PKCE (Proof Key for Code Exchange) — no client secret needed |
|
|
251
324
|
| Token transmission | OAuth tokens never leave localhost. Only forwarded to `api.anthropic.com` over HTTPS |
|
|
252
|
-
| Network exposure | Proxy binds to `
|
|
325
|
+
| Network exposure | Proxy binds to `127.0.0.1` only — not accessible from other machines |
|
|
326
|
+
| SSRF protection | Allowlisted API paths only. Internal networks and cloud metadata blocked |
|
|
253
327
|
| Token rotation | Refresh tokens rotate on every use (single-use) |
|
|
328
|
+
| Error sanitization | Token patterns redacted from all error messages |
|
|
254
329
|
| Data collection | Zero. No telemetry, no analytics, no phoning home |
|
|
255
330
|
|
|
256
331
|
## FAQ
|
|
257
332
|
|
|
258
333
|
**Does this violate Anthropic's terms of service?**
|
|
259
|
-
Dario uses the same public OAuth client ID and PKCE flow that Claude Code uses. It authenticates you as you, with your subscription, through Anthropic's official OAuth endpoints.
|
|
334
|
+
Dario uses the same public OAuth client ID and PKCE flow that Claude Code uses. It authenticates you as you, with your subscription, through Anthropic's official OAuth endpoints. The `--cli` mode literally uses Claude Code itself as the backend.
|
|
260
335
|
|
|
261
336
|
**What subscription plans work?**
|
|
262
337
|
Claude Max and Claude Pro. Any plan that lets you use Claude Code.
|
|
@@ -267,12 +342,12 @@ Should work if your plan includes Claude Code access. Not tested yet — please
|
|
|
267
342
|
**What happens when my token expires?**
|
|
268
343
|
Dario auto-refreshes tokens 30 minutes before expiry. You should never see an auth error in normal use. If something goes wrong, `dario refresh` forces an immediate refresh, or `dario login` to re-authenticate.
|
|
269
344
|
|
|
345
|
+
**I'm getting rate limited on Opus. What do I do?**
|
|
346
|
+
Use `--cli` mode: `dario proxy --cli`. This routes through the Claude Code binary, which has priority access that bypasses subscription rate limits.
|
|
347
|
+
|
|
270
348
|
**Can I run this on a server?**
|
|
271
349
|
Dario binds to localhost by default. For server use, you'd need to handle the initial browser-based login on a machine with a browser, then copy `~/.dario/credentials.json` to your server. Auto-refresh will keep it alive from there.
|
|
272
350
|
|
|
273
|
-
**What's the rate limit?**
|
|
274
|
-
Whatever your subscription plan provides. Dario doesn't add any limits — it's a transparent proxy.
|
|
275
|
-
|
|
276
351
|
**Why "dario"?**
|
|
277
352
|
Named after [Dario Amodei](https://en.wikipedia.org/wiki/Dario_Amodei), CEO of Anthropic.
|
|
278
353
|
|
|
@@ -286,6 +361,9 @@ import { startProxy, getAccessToken, getStatus } from "@askalf/dario";
|
|
|
286
361
|
// Start the proxy programmatically
|
|
287
362
|
await startProxy({ port: 3456, verbose: true });
|
|
288
363
|
|
|
364
|
+
// CLI backend mode
|
|
365
|
+
await startProxy({ port: 3456, cliBackend: true, model: "opus" });
|
|
366
|
+
|
|
289
367
|
// Or just get a raw access token
|
|
290
368
|
const token = await getAccessToken();
|
|
291
369
|
|
|
@@ -296,12 +374,12 @@ console.log(status.expiresIn); // "11h 42m"
|
|
|
296
374
|
|
|
297
375
|
## Contributing
|
|
298
376
|
|
|
299
|
-
PRs welcome. The codebase is ~
|
|
377
|
+
PRs welcome. The codebase is ~700 lines of TypeScript across 4 files:
|
|
300
378
|
|
|
301
379
|
| File | Purpose |
|
|
302
380
|
|------|---------|
|
|
303
381
|
| `src/oauth.ts` | PKCE flow, token storage, refresh logic |
|
|
304
|
-
| `src/proxy.ts` | HTTP proxy server |
|
|
382
|
+
| `src/proxy.ts` | HTTP proxy server + CLI backend |
|
|
305
383
|
| `src/cli.ts` | CLI entry point |
|
|
306
384
|
| `src/index.ts` | Library exports |
|
|
307
385
|
|
package/dist/cli.js
CHANGED
|
@@ -132,9 +132,10 @@ async function proxy() {
|
|
|
132
132
|
process.exit(1);
|
|
133
133
|
}
|
|
134
134
|
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
135
|
+
const cliBackend = args.includes('--cli');
|
|
135
136
|
const modelArg = args.find(a => a.startsWith('--model='));
|
|
136
137
|
const model = modelArg ? modelArg.split('=')[1] : undefined;
|
|
137
|
-
await startProxy({ port, verbose, model });
|
|
138
|
+
await startProxy({ port, verbose, model, cliBackend });
|
|
138
139
|
}
|
|
139
140
|
async function help() {
|
|
140
141
|
console.log(`
|
|
@@ -151,6 +152,7 @@ async function help() {
|
|
|
151
152
|
--model=MODEL Force a model for all requests
|
|
152
153
|
Shortcuts: opus, sonnet, haiku
|
|
153
154
|
Default: passthrough (client decides)
|
|
155
|
+
--cli Use Claude CLI as backend (bypasses rate limits)
|
|
154
156
|
--port=PORT Port to listen on (default: 3456)
|
|
155
157
|
--verbose, -v Log all requests
|
|
156
158
|
|
package/dist/proxy.d.ts
CHANGED
package/dist/proxy.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createServer } from 'node:http';
|
|
11
11
|
import { randomUUID } from 'node:crypto';
|
|
12
|
-
import { execSync } from 'node:child_process';
|
|
12
|
+
import { execSync, spawn } from 'node:child_process';
|
|
13
13
|
import { arch, platform, version as nodeVersion } from 'node:process';
|
|
14
14
|
import { getAccessToken, getStatus } from './oauth.js';
|
|
15
15
|
const ANTHROPIC_API = 'https://api.anthropic.com';
|
|
@@ -60,6 +60,95 @@ function sanitizeError(err) {
|
|
|
60
60
|
// Never leak tokens in error messages
|
|
61
61
|
return msg.replace(/sk-ant-[a-zA-Z0-9_-]+/g, '[REDACTED]');
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* CLI Backend: route requests through `claude --print` instead of direct API.
|
|
65
|
+
* This bypasses rate limiting because Claude Code's binary has priority routing.
|
|
66
|
+
*/
|
|
67
|
+
async function handleViaCli(body, model, verbose) {
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(body.toString());
|
|
70
|
+
// Extract the last user message as the prompt
|
|
71
|
+
const messages = parsed.messages ?? [];
|
|
72
|
+
const lastUser = [...messages].reverse().find(m => m.role === 'user');
|
|
73
|
+
if (!lastUser) {
|
|
74
|
+
return { status: 400, body: JSON.stringify({ error: 'No user message' }), contentType: 'application/json' };
|
|
75
|
+
}
|
|
76
|
+
const effectiveModel = model ?? parsed.model ?? 'claude-opus-4-6';
|
|
77
|
+
const prompt = typeof lastUser.content === 'string'
|
|
78
|
+
? lastUser.content
|
|
79
|
+
: JSON.stringify(lastUser.content);
|
|
80
|
+
// Build claude --print command
|
|
81
|
+
const args = ['--print', '--model', effectiveModel];
|
|
82
|
+
// Build system prompt from messages context
|
|
83
|
+
let systemPrompt = parsed.system ?? '';
|
|
84
|
+
// Include conversation history as context
|
|
85
|
+
const history = messages.slice(0, -1);
|
|
86
|
+
if (history.length > 0) {
|
|
87
|
+
const historyText = history.map(m => `${m.role}: ${typeof m.content === 'string' ? m.content : JSON.stringify(m.content)}`).join('\n');
|
|
88
|
+
systemPrompt = systemPrompt ? `${systemPrompt}\n\nConversation history:\n${historyText}` : `Conversation history:\n${historyText}`;
|
|
89
|
+
}
|
|
90
|
+
if (systemPrompt) {
|
|
91
|
+
args.push('--append-system-prompt', systemPrompt);
|
|
92
|
+
}
|
|
93
|
+
if (verbose) {
|
|
94
|
+
console.log(`[dario:cli] model=${effectiveModel} prompt=${prompt.substring(0, 60)}...`);
|
|
95
|
+
}
|
|
96
|
+
// Spawn claude --print
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
const child = spawn('claude', args, {
|
|
99
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
100
|
+
timeout: 300_000,
|
|
101
|
+
});
|
|
102
|
+
let stdout = '';
|
|
103
|
+
let stderr = '';
|
|
104
|
+
child.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
105
|
+
child.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
106
|
+
child.stdin.write(prompt);
|
|
107
|
+
child.stdin.end();
|
|
108
|
+
child.on('close', (code) => {
|
|
109
|
+
if (code !== 0 || !stdout.trim()) {
|
|
110
|
+
resolve({
|
|
111
|
+
status: 502,
|
|
112
|
+
body: JSON.stringify({ type: 'error', error: { type: 'api_error', message: stderr.substring(0, 200) || 'CLI backend failed' } }),
|
|
113
|
+
contentType: 'application/json',
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Build a proper Messages API response
|
|
118
|
+
const text = stdout.trim();
|
|
119
|
+
const estimatedTokens = Math.ceil(text.length / 4);
|
|
120
|
+
const response = {
|
|
121
|
+
id: `msg_${randomUUID().replace(/-/g, '').substring(0, 24)}`,
|
|
122
|
+
type: 'message',
|
|
123
|
+
role: 'assistant',
|
|
124
|
+
model: effectiveModel,
|
|
125
|
+
content: [{ type: 'text', text }],
|
|
126
|
+
stop_reason: 'end_turn',
|
|
127
|
+
stop_sequence: null,
|
|
128
|
+
usage: {
|
|
129
|
+
input_tokens: Math.ceil(prompt.length / 4),
|
|
130
|
+
output_tokens: estimatedTokens,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
resolve({ status: 200, body: JSON.stringify(response), contentType: 'application/json' });
|
|
134
|
+
});
|
|
135
|
+
child.on('error', (err) => {
|
|
136
|
+
resolve({
|
|
137
|
+
status: 502,
|
|
138
|
+
body: JSON.stringify({ type: 'error', error: { type: 'api_error', message: 'Claude CLI not found. Install Claude Code first.' } }),
|
|
139
|
+
contentType: 'application/json',
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
return {
|
|
146
|
+
status: 400,
|
|
147
|
+
body: JSON.stringify({ type: 'error', error: { type: 'invalid_request_error', message: 'Invalid request body' } }),
|
|
148
|
+
contentType: 'application/json',
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
63
152
|
export async function startProxy(opts = {}) {
|
|
64
153
|
const port = opts.port ?? DEFAULT_PORT;
|
|
65
154
|
const verbose = opts.verbose ?? false;
|
|
@@ -72,6 +161,7 @@ export async function startProxy(opts = {}) {
|
|
|
72
161
|
const cliVersion = detectClaudeVersion();
|
|
73
162
|
const sdkVersion = detectSdkVersion();
|
|
74
163
|
const modelOverride = opts.model ? (MODEL_ALIASES[opts.model] ?? opts.model) : null;
|
|
164
|
+
const useCli = opts.cliBackend ?? false;
|
|
75
165
|
let requestCount = 0;
|
|
76
166
|
let tokenCostEstimate = 0;
|
|
77
167
|
const server = createServer(async (req, res) => {
|
|
@@ -141,6 +231,17 @@ export async function startProxy(opts = {}) {
|
|
|
141
231
|
chunks.push(buf);
|
|
142
232
|
}
|
|
143
233
|
const body = Buffer.concat(chunks);
|
|
234
|
+
// CLI backend mode: route through claude --print
|
|
235
|
+
if (useCli && rawPath === '/v1/messages' && req.method === 'POST' && body.length > 0) {
|
|
236
|
+
const cliResult = await handleViaCli(body, modelOverride, verbose);
|
|
237
|
+
requestCount++;
|
|
238
|
+
res.writeHead(cliResult.status, {
|
|
239
|
+
'Content-Type': cliResult.contentType,
|
|
240
|
+
'Access-Control-Allow-Origin': CORS_ORIGIN,
|
|
241
|
+
});
|
|
242
|
+
res.end(cliResult.body);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
144
245
|
// Override model in request body if --model flag was set
|
|
145
246
|
let finalBody = body.length > 0 ? body : undefined;
|
|
146
247
|
if (modelOverride && body.length > 0) {
|
|
@@ -266,7 +367,7 @@ export async function startProxy(opts = {}) {
|
|
|
266
367
|
process.exit(1);
|
|
267
368
|
});
|
|
268
369
|
server.listen(port, LOCALHOST, () => {
|
|
269
|
-
const oauthLine = `OAuth: ${status.status} (expires in ${status.expiresIn})`;
|
|
370
|
+
const oauthLine = useCli ? 'Backend: Claude CLI (bypasses rate limits)' : `OAuth: ${status.status} (expires in ${status.expiresIn})`;
|
|
270
371
|
const modelLine = modelOverride ? `Model: ${modelOverride} (all requests)` : 'Model: passthrough (client decides)';
|
|
271
372
|
console.log('');
|
|
272
373
|
console.log(` dario — http://localhost:${port}`);
|