@askalf/dario 3.1.1 → 3.2.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 CHANGED
@@ -9,6 +9,29 @@
9
9
  * dario refresh — Force token refresh
10
10
  * dario logout — Remove saved credentials
11
11
  */
12
+ // ── Bun auto-relaunch ──
13
+ // Bun's TLS fingerprint matches Claude Code's runtime (both use Bun/BoringSSL).
14
+ // If Bun is installed and we're running on Node, relaunch under Bun for
15
+ // network-level fingerprint fidelity.
16
+ if (!('Bun' in globalThis) && !process.env.DARIO_NO_BUN) {
17
+ try {
18
+ const { execFileSync } = await import('node:child_process');
19
+ // Check if bun exists
20
+ execFileSync('bun', ['--version'], { stdio: 'ignore', timeout: 3000 });
21
+ // Relaunch under bun
22
+ const { spawn } = await import('node:child_process');
23
+ const child = spawn('bun', ['run', ...process.argv.slice(1)], {
24
+ stdio: 'inherit',
25
+ env: { ...process.env, DARIO_NO_BUN: '1' },
26
+ });
27
+ child.on('exit', (code) => process.exit(code ?? 0));
28
+ // Prevent this process from continuing
29
+ await new Promise(() => { });
30
+ }
31
+ catch {
32
+ // Bun not available, continue with Node
33
+ }
34
+ }
12
35
  import { unlink } from 'node:fs/promises';
13
36
  import { join } from 'node:path';
14
37
  import { homedir } from 'node:os';
package/dist/proxy.js CHANGED
@@ -143,7 +143,9 @@ function sendCliResponse(res, cliResult, clientWantsStream, isOpenAI, corsOrigin
143
143
  res.writeHead(cliResult.status, { 'Content-Type': cliResult.contentType, ...headers });
144
144
  res.end(cliResult.body);
145
145
  }
146
- const SESSION_ID = randomUUID();
146
+ // Session ID rotates per request — each CC --print invocation creates a new session.
147
+ // A persistent session ID across many requests is a detection signal.
148
+ let SESSION_ID = randomUUID();
147
149
  const OS_NAME = platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'MacOS' : 'Linux';
148
150
  // Claude Code device identity — required for Max plan billing classification.
149
151
  // Without metadata.user_id, Anthropic classifies requests as third-party and
@@ -520,7 +522,6 @@ export async function startProxy(opts = {}) {
520
522
  'anthropic-dangerous-direct-browser-access': 'true',
521
523
  'user-agent': `claude-cli/${cliVersion} (external, cli)`,
522
524
  'x-app': 'cli',
523
- 'x-claude-code-session-id': SESSION_ID,
524
525
  'x-stainless-arch': arch,
525
526
  'x-stainless-lang': 'js',
526
527
  'x-stainless-os': OS_NAME,
@@ -533,6 +534,11 @@ export async function startProxy(opts = {}) {
533
534
  const useCli = opts.cliBackend ?? false;
534
535
  let requestCount = 0;
535
536
  const semaphore = new Semaphore(MAX_CONCURRENT);
537
+ // Rate governor: CC --print takes ~2-3s per invocation.
538
+ // Rapid-fire requests from one "session" is an inhuman pattern.
539
+ // Minimum 500ms between requests — fast enough for agents, slow enough to not flag.
540
+ let lastRequestTime = 0;
541
+ const MIN_REQUEST_INTERVAL_MS = parseInt(process.env.DARIO_MIN_INTERVAL_MS || '500', 10);
536
542
  // Optional proxy authentication — pre-encode key buffer for performance
537
543
  const apiKey = process.env.DARIO_API_KEY;
538
544
  const apiKeyBuf = apiKey ? Buffer.from(apiKey) : null;
@@ -739,15 +745,24 @@ export async function startProxy(opts = {}) {
739
745
  beta += ',' + filtered;
740
746
  }
741
747
  }
748
+ // Rate governor — prevent inhuman request cadence
749
+ const now = Date.now();
750
+ const elapsed = now - lastRequestTime;
751
+ if (elapsed < MIN_REQUEST_INTERVAL_MS && lastRequestTime > 0) {
752
+ await new Promise(r => setTimeout(r, MIN_REQUEST_INTERVAL_MS - elapsed));
753
+ }
754
+ lastRequestTime = Date.now();
755
+ // Rotate session ID per request — CC --print creates a new session each invocation
756
+ SESSION_ID = randomUUID();
742
757
  const headers = {
743
758
  ...staticHeaders,
744
759
  'Authorization': `Bearer ${accessToken}`,
760
+ 'x-claude-code-session-id': SESSION_ID,
745
761
  'anthropic-version': passthrough ? (req.headers['anthropic-version'] || '2023-06-01') : '2023-06-01',
746
762
  'anthropic-beta': beta,
747
- // Real Claude Code adds x-client-request-id for firstParty + api.anthropic.com
748
763
  'x-client-request-id': randomUUID(),
749
- // Real Claude Code sends 600 on first request, 300 on subsequent
750
- 'x-stainless-timeout': requestCount <= 1 ? '600' : '300',
764
+ // CC sends 600 on first request per session. With rotation, every request is "first"
765
+ 'x-stainless-timeout': '600',
751
766
  };
752
767
  const upstream = await fetch(targetBase, {
753
768
  method: req.method ?? 'POST',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "Use your Claude subscription as an API. No API key needed. Local proxy for Claude Max/Pro subscriptions.",
5
5
  "type": "module",
6
6
  "bin": {