@askalf/dario 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +3 -1
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +103 -2
- package/package.json +1 -1
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}`);
|