@askalf/dario 1.0.7 → 1.0.9
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 +17 -4
- package/dist/oauth.d.ts +1 -0
- package/dist/oauth.js +6 -5
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +44 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import { homedir } from 'node:os';
|
|
|
16
16
|
import { startOAuthFlow, exchangeCode, getStatus, refreshTokens } from './oauth.js';
|
|
17
17
|
import { startProxy } from './proxy.js';
|
|
18
18
|
const args = process.argv.slice(2);
|
|
19
|
-
const command = args[0] ?? 'proxy';
|
|
19
|
+
const command = args[0] ?? (process.stdin.isTTY ? 'proxy' : 'proxy');
|
|
20
20
|
function ask(question) {
|
|
21
21
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
22
22
|
return new Promise(resolve => {
|
|
@@ -83,10 +83,18 @@ async function status() {
|
|
|
83
83
|
console.log(' ─────────────');
|
|
84
84
|
console.log('');
|
|
85
85
|
if (!s.authenticated) {
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
if (s.status === 'expired' && s.canRefresh) {
|
|
87
|
+
console.log(' Status: Expired (will auto-refresh when proxy starts)');
|
|
88
|
+
console.log(' Run `dario refresh` to refresh now, or `dario proxy` to start.');
|
|
89
|
+
}
|
|
90
|
+
else if (s.status === 'none') {
|
|
91
|
+
console.log(' Status: Not authenticated');
|
|
88
92
|
console.log(' Run `dario login` to authenticate.');
|
|
89
93
|
}
|
|
94
|
+
else {
|
|
95
|
+
console.log(` Status: ${s.status}`);
|
|
96
|
+
console.log(' Run `dario login` to re-authenticate.');
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
else {
|
|
92
100
|
console.log(` Status: ${s.status}`);
|
|
@@ -124,7 +132,9 @@ async function proxy() {
|
|
|
124
132
|
process.exit(1);
|
|
125
133
|
}
|
|
126
134
|
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
127
|
-
|
|
135
|
+
const modelArg = args.find(a => a.startsWith('--model='));
|
|
136
|
+
const model = modelArg ? modelArg.split('=')[1] : undefined;
|
|
137
|
+
await startProxy({ port, verbose, model });
|
|
128
138
|
}
|
|
129
139
|
async function help() {
|
|
130
140
|
console.log(`
|
|
@@ -138,6 +148,9 @@ async function help() {
|
|
|
138
148
|
dario logout Remove saved credentials
|
|
139
149
|
|
|
140
150
|
Proxy options:
|
|
151
|
+
--model=MODEL Force a model for all requests
|
|
152
|
+
Shortcuts: opus, sonnet, haiku
|
|
153
|
+
Default: passthrough (client decides)
|
|
141
154
|
--port=PORT Port to listen on (default: 3456)
|
|
142
155
|
--verbose, -v Log all requests
|
|
143
156
|
|
package/dist/oauth.d.ts
CHANGED
package/dist/oauth.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Handles authorization, token exchange, storage, and auto-refresh.
|
|
6
6
|
*/
|
|
7
7
|
import { randomBytes, createHash } from 'node:crypto';
|
|
8
|
-
import { readFile, writeFile, mkdir, chmod } from 'node:fs/promises';
|
|
8
|
+
import { readFile, writeFile, mkdir, chmod, rename } from 'node:fs/promises';
|
|
9
9
|
import { dirname, join } from 'node:path';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
11
|
// Claude CLI's public OAuth client (PKCE, no secret needed)
|
|
@@ -58,7 +58,6 @@ async function saveCredentials(creds) {
|
|
|
58
58
|
// Write atomically: write to temp file, then rename
|
|
59
59
|
const tmpPath = `${path}.tmp.${Date.now()}`;
|
|
60
60
|
await writeFile(tmpPath, JSON.stringify(creds, null, 2), { mode: 0o600 });
|
|
61
|
-
const { rename } = await import('node:fs/promises');
|
|
62
61
|
await rename(tmpPath, path);
|
|
63
62
|
// Set permissions (best-effort — no-op on Windows where mode is ignored)
|
|
64
63
|
try {
|
|
@@ -105,10 +104,10 @@ export async function exchangeCode(code, codeVerifier) {
|
|
|
105
104
|
redirect_uri: OAUTH_REDIRECT_URI,
|
|
106
105
|
code_verifier: codeVerifier,
|
|
107
106
|
}),
|
|
107
|
+
signal: AbortSignal.timeout(30000),
|
|
108
108
|
});
|
|
109
109
|
if (!res.ok) {
|
|
110
|
-
|
|
111
|
-
throw new Error(`Token exchange failed (${res.status}): ${err}`);
|
|
110
|
+
throw new Error(`Token exchange failed (${res.status}). Check your authorization code and try again.`);
|
|
112
111
|
}
|
|
113
112
|
const data = await res.json();
|
|
114
113
|
const tokens = {
|
|
@@ -203,7 +202,9 @@ export async function getStatus() {
|
|
|
203
202
|
const { expiresAt } = creds.claudeAiOauth;
|
|
204
203
|
const now = Date.now();
|
|
205
204
|
if (expiresAt < now) {
|
|
206
|
-
|
|
205
|
+
// Expired but has refresh token — can be refreshed
|
|
206
|
+
const canRefresh = !!creds.claudeAiOauth.refreshToken;
|
|
207
|
+
return { authenticated: false, status: 'expired', expiresAt, canRefresh };
|
|
207
208
|
}
|
|
208
209
|
const ms = expiresAt - now;
|
|
209
210
|
const hours = Math.floor(ms / 3600000);
|
package/dist/proxy.d.ts
CHANGED
package/dist/proxy.js
CHANGED
|
@@ -39,6 +39,22 @@ function getOsName() {
|
|
|
39
39
|
}
|
|
40
40
|
// Persistent session ID per proxy lifetime (like Claude Code does per session)
|
|
41
41
|
const SESSION_ID = randomUUID();
|
|
42
|
+
// Detect @anthropic-ai/sdk version from installed package
|
|
43
|
+
function detectSdkVersion() {
|
|
44
|
+
try {
|
|
45
|
+
const pkg = require('@anthropic-ai/sdk/package.json');
|
|
46
|
+
return pkg.version ?? '0.81.0';
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return '0.81.0';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Model shortcuts — users can pass short names
|
|
53
|
+
const MODEL_ALIASES = {
|
|
54
|
+
'opus': 'claude-opus-4-6',
|
|
55
|
+
'sonnet': 'claude-sonnet-4-6',
|
|
56
|
+
'haiku': 'claude-haiku-4-5',
|
|
57
|
+
};
|
|
42
58
|
function sanitizeError(err) {
|
|
43
59
|
const msg = err instanceof Error ? err.message : String(err);
|
|
44
60
|
// Never leak tokens in error messages
|
|
@@ -54,6 +70,8 @@ export async function startProxy(opts = {}) {
|
|
|
54
70
|
process.exit(1);
|
|
55
71
|
}
|
|
56
72
|
const cliVersion = detectClaudeVersion();
|
|
73
|
+
const sdkVersion = detectSdkVersion();
|
|
74
|
+
const modelOverride = opts.model ? (MODEL_ALIASES[opts.model] ?? opts.model) : null;
|
|
57
75
|
let requestCount = 0;
|
|
58
76
|
let tokenCostEstimate = 0;
|
|
59
77
|
const server = createServer(async (req, res) => {
|
|
@@ -109,7 +127,6 @@ export async function startProxy(opts = {}) {
|
|
|
109
127
|
// Proxy to Anthropic
|
|
110
128
|
try {
|
|
111
129
|
const accessToken = await getAccessToken();
|
|
112
|
-
requestCount++;
|
|
113
130
|
// Read request body with size limit
|
|
114
131
|
const chunks = [];
|
|
115
132
|
let totalBytes = 0;
|
|
@@ -124,8 +141,19 @@ export async function startProxy(opts = {}) {
|
|
|
124
141
|
chunks.push(buf);
|
|
125
142
|
}
|
|
126
143
|
const body = Buffer.concat(chunks);
|
|
144
|
+
// Override model in request body if --model flag was set
|
|
145
|
+
let finalBody = body.length > 0 ? body : undefined;
|
|
146
|
+
if (modelOverride && body.length > 0) {
|
|
147
|
+
try {
|
|
148
|
+
const parsed = JSON.parse(body.toString());
|
|
149
|
+
parsed.model = modelOverride;
|
|
150
|
+
finalBody = Buffer.from(JSON.stringify(parsed));
|
|
151
|
+
}
|
|
152
|
+
catch { /* not JSON, send as-is */ }
|
|
153
|
+
}
|
|
127
154
|
if (verbose) {
|
|
128
|
-
|
|
155
|
+
const modelInfo = modelOverride ? ` (model: ${modelOverride})` : '';
|
|
156
|
+
console.log(`[dario] #${requestCount} ${req.method} ${req.url}${modelInfo}`);
|
|
129
157
|
}
|
|
130
158
|
// Build target URL from allowlist (no user input in URL construction)
|
|
131
159
|
const targetUrl = targetBase;
|
|
@@ -158,7 +186,7 @@ export async function startProxy(opts = {}) {
|
|
|
158
186
|
'x-stainless-arch': arch,
|
|
159
187
|
'x-stainless-lang': 'js',
|
|
160
188
|
'x-stainless-os': getOsName(),
|
|
161
|
-
'x-stainless-package-version':
|
|
189
|
+
'x-stainless-package-version': sdkVersion,
|
|
162
190
|
'x-stainless-retry-count': '0',
|
|
163
191
|
'x-stainless-runtime': 'node',
|
|
164
192
|
'x-stainless-runtime-version': nodeVersion,
|
|
@@ -167,10 +195,8 @@ export async function startProxy(opts = {}) {
|
|
|
167
195
|
const upstream = await fetch(targetUrl, {
|
|
168
196
|
method: req.method ?? 'POST',
|
|
169
197
|
headers,
|
|
170
|
-
body:
|
|
198
|
+
body: finalBody ? new Uint8Array(finalBody) : undefined,
|
|
171
199
|
signal: AbortSignal.timeout(UPSTREAM_TIMEOUT_MS),
|
|
172
|
-
// @ts-expect-error — duplex needed for streaming
|
|
173
|
-
duplex: 'half',
|
|
174
200
|
});
|
|
175
201
|
// Detect streaming from content-type (reliable) or body (fallback)
|
|
176
202
|
const contentType = upstream.headers.get('content-type') ?? '';
|
|
@@ -186,6 +212,7 @@ export async function startProxy(opts = {}) {
|
|
|
186
212
|
if (v)
|
|
187
213
|
responseHeaders[h] = v;
|
|
188
214
|
}
|
|
215
|
+
requestCount++;
|
|
189
216
|
res.writeHead(upstream.status, responseHeaders);
|
|
190
217
|
if (isStream && upstream.body) {
|
|
191
218
|
// Stream SSE chunks through
|
|
@@ -229,8 +256,18 @@ export async function startProxy(opts = {}) {
|
|
|
229
256
|
res.end(JSON.stringify({ error: 'Proxy error', message: 'Failed to reach upstream API' }));
|
|
230
257
|
}
|
|
231
258
|
});
|
|
259
|
+
server.on('error', (err) => {
|
|
260
|
+
if (err.code === 'EADDRINUSE') {
|
|
261
|
+
console.error(`[dario] Port ${port} is already in use. Is another dario proxy running?`);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
console.error(`[dario] Server error: ${err.message}`);
|
|
265
|
+
}
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
|
232
268
|
server.listen(port, LOCALHOST, () => {
|
|
233
269
|
const oauthLine = `OAuth: ${status.status} (expires in ${status.expiresIn})`;
|
|
270
|
+
const modelLine = modelOverride ? `Model: ${modelOverride} (all requests)` : 'Model: passthrough (client decides)';
|
|
234
271
|
console.log('');
|
|
235
272
|
console.log(` dario — http://localhost:${port}`);
|
|
236
273
|
console.log('');
|
|
@@ -241,6 +278,7 @@ export async function startProxy(opts = {}) {
|
|
|
241
278
|
console.log(' ANTHROPIC_API_KEY=dario');
|
|
242
279
|
console.log('');
|
|
243
280
|
console.log(` ${oauthLine}`);
|
|
281
|
+
console.log(` ${modelLine}`);
|
|
244
282
|
console.log('');
|
|
245
283
|
});
|
|
246
284
|
// Periodic token refresh (every 15 minutes)
|