@askalf/dario 5.0.1 → 5.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 +1 -1
- package/dist/cli.js +19 -1
- package/dist/live-fingerprint.d.ts +1 -1
- package/dist/live-fingerprint.js +1 -1
- package/dist/oauth.d.ts +9 -0
- package/dist/oauth.js +28 -3
- package/dist/proxy.d.ts +24 -0
- package/dist/proxy.js +68 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p>
|
|
8
8
|
<a href="https://www.npmjs.com/package/@askalf/dario"><img src="https://img.shields.io/npm/v/@askalf/dario?color=6f42c1&label=npm&logo=npm" alt="npm version"></a>
|
|
9
|
-
<a href="https://github.com/askalf/dario/releases"><img src="https://img.shields.io/
|
|
9
|
+
<a href="https://github.com/askalf/dario/releases"><img src="https://img.shields.io/github/v/release/askalf/dario?color=6f42c1&label=release&logo=github" alt="Latest release"></a>
|
|
10
10
|
<a href="https://github.com/askalf/dario/actions/workflows/ci.yml"><img src="https://github.com/askalf/dario/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
11
11
|
<a href="https://github.com/askalf/dario/actions/workflows/codeql.yml"><img src="https://github.com/askalf/dario/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
|
|
12
12
|
<a href="https://scorecard.dev/viewer/?uri=github.com/askalf/dario"><img src="https://img.shields.io/ossf-scorecard/github.com/askalf/dario?label=OpenSSF%20Scorecard&color=6f42c1" alt="OpenSSF Scorecard"></a>
|
package/dist/cli.js
CHANGED
|
@@ -348,8 +348,16 @@ async function proxy() {
|
|
|
348
348
|
// proxy presents to Anthropic is Bun's BoringSSL ClientHello, not
|
|
349
349
|
// Node's OpenSSL one. v3.23 (direction #3).
|
|
350
350
|
const strictTls = args.includes('--strict-tls');
|
|
351
|
+
// --no-claude-auth: don't load/refresh the Claude OAuth pool (OpenAI-only
|
|
352
|
+
// proxies). Stops dario rotating a shared refresh token out from under an
|
|
353
|
+
// interactive Claude Code on the same machine.
|
|
354
|
+
const noClaudeAuth = args.includes('--no-claude-auth');
|
|
351
355
|
const modelArg = args.find(a => a.startsWith('--model='));
|
|
352
356
|
const model = modelArg ? modelArg.split('=')[1] : undefined;
|
|
357
|
+
// --fast-model=MODEL: route Haiku-tier (CC sub-agent) requests to this
|
|
358
|
+
// model instead of the forced --model, so sub-agents stay cheap.
|
|
359
|
+
const fastModelArg = args.find(a => a.startsWith('--fast-model='));
|
|
360
|
+
const fastModel = fastModelArg ? fastModelArg.split('=')[1] : undefined;
|
|
353
361
|
// --pace-min=MS / --pace-jitter=MS (v3.24, direction #6 — behavioral
|
|
354
362
|
// smoothing). Inter-request gap floor + optional uniform-random jitter.
|
|
355
363
|
// v4: ~/.dario/config.json's `pacing.{minMs,jitterMs}` is the fallback
|
|
@@ -569,7 +577,7 @@ async function proxy() {
|
|
|
569
577
|
console.error(`[dario] Override (not recommended): pass --unsafe-no-auth if you have out-of-band network controls and accept the risk.`);
|
|
570
578
|
process.exit(1);
|
|
571
579
|
}
|
|
572
|
-
await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, thinkTimeBaseMs, thinkTimePerTokenMs, thinkTimeJitterMs, thinkTimeMaxMs, sessionStartMinMs, sessionStartJitterMs, stealth, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas, skipFields, systemPrompt, overageGuardEnabled, overageGuardBehavior, overageGuardCooldownMs, overageGuardNotifyOs, honorClientThinking, preserveOutputFormat });
|
|
580
|
+
await startProxy({ port, host, verbose, verboseBodies, model, fastModel, noClaudeAuth, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, thinkTimeBaseMs, thinkTimePerTokenMs, thinkTimeJitterMs, thinkTimeMaxMs, sessionStartMinMs, sessionStartJitterMs, stealth, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas, skipFields, systemPrompt, overageGuardEnabled, overageGuardBehavior, overageGuardCooldownMs, overageGuardNotifyOs, honorClientThinking, preserveOutputFormat });
|
|
573
581
|
}
|
|
574
582
|
/**
|
|
575
583
|
* Parse `--system-prompt=<verbatim|partial|aggressive|filepath>` (or the
|
|
@@ -1198,6 +1206,16 @@ async function help() {
|
|
|
1198
1206
|
Provider prefix: openai:gpt-4o, groq:llama-3.3-70b,
|
|
1199
1207
|
claude:opus, local:qwen-coder (forces backend)
|
|
1200
1208
|
Default: passthrough (client decides)
|
|
1209
|
+
--fast-model=MODEL Route Haiku-tier sub-agent requests to MODEL
|
|
1210
|
+
instead of --model, so Claude Code's cheap
|
|
1211
|
+
sub-agents aren't upgraded to the forced model.
|
|
1212
|
+
Same MODEL forms as --model. No effect unless set.
|
|
1213
|
+
--no-claude-auth Don't load or refresh the Claude OAuth token —
|
|
1214
|
+
for OpenAI-only proxies (e.g. --model=openai:...).
|
|
1215
|
+
Prevents dario rotating a shared refresh token out
|
|
1216
|
+
from under an interactive Claude Code on the same
|
|
1217
|
+
machine. Claude-bound requests then return a clean
|
|
1218
|
+
unauthenticated error.
|
|
1201
1219
|
--passthrough, --thin Thin proxy — OAuth swap only, no injection
|
|
1202
1220
|
--preserve-tools Forward client tool schemas unchanged
|
|
1203
1221
|
Loses subscription routing; use for custom agents
|
|
@@ -282,7 +282,7 @@ export declare function _resetInstalledVersionProbeForTest(): void;
|
|
|
282
282
|
*/
|
|
283
283
|
export declare const SUPPORTED_CC_RANGE: {
|
|
284
284
|
readonly min: "1.0.0";
|
|
285
|
-
readonly maxTested: "2.1.
|
|
285
|
+
readonly maxTested: "2.1.209";
|
|
286
286
|
};
|
|
287
287
|
/**
|
|
288
288
|
* Compare two dotted-numeric version strings. Returns negative if `a<b`,
|
package/dist/live-fingerprint.js
CHANGED
|
@@ -806,7 +806,7 @@ export function _resetInstalledVersionProbeForTest() {
|
|
|
806
806
|
*/
|
|
807
807
|
export const SUPPORTED_CC_RANGE = {
|
|
808
808
|
min: '1.0.0',
|
|
809
|
-
maxTested: '2.1.
|
|
809
|
+
maxTested: '2.1.209',
|
|
810
810
|
};
|
|
811
811
|
/**
|
|
812
812
|
* Compare two dotted-numeric version strings. Returns negative if `a<b`,
|
package/dist/oauth.d.ts
CHANGED
|
@@ -137,6 +137,15 @@ export declare function readLineFromStdin(prompt: string): Promise<string>;
|
|
|
137
137
|
* Retries with exponential backoff on transient failures.
|
|
138
138
|
* Uses a mutex to prevent concurrent refresh races.
|
|
139
139
|
*/
|
|
140
|
+
/**
|
|
141
|
+
* A refresh failure that retrying cannot recover: the refresh token is
|
|
142
|
+
* invalid, revoked, or rotated out (e.g. a second dario on the same account
|
|
143
|
+
* consumed the single-use token). Anthropic signals this as HTTP 401/403, or
|
|
144
|
+
* as a 400 with an `invalid_grant` body ("Refresh token not found or
|
|
145
|
+
* invalid"). Terminal -> fail fast with a re-login prompt instead of burning
|
|
146
|
+
* doomed retries and masking as healthy.
|
|
147
|
+
*/
|
|
148
|
+
export declare function isTerminalRefreshFailure(status: number, body: string): boolean;
|
|
140
149
|
export declare function refreshTokens(): Promise<OAuthTokens>;
|
|
141
150
|
/**
|
|
142
151
|
* Get a valid access token, refreshing if needed.
|
package/dist/oauth.js
CHANGED
|
@@ -39,6 +39,12 @@ const REFRESH_COOLDOWN_MS = 60 * 1000;
|
|
|
39
39
|
let consecutiveRefreshFailures = 0;
|
|
40
40
|
let lastRefreshError;
|
|
41
41
|
const REFRESH_BROKEN_THRESHOLD = 3;
|
|
42
|
+
// Set the moment a refresh fails TERMINALLY (invalid_grant / 401 / 403 — a
|
|
43
|
+
// dead, revoked, or rotated-out refresh token). Unlike the count-based
|
|
44
|
+
// threshold, one terminal failure is conclusive, so surface `broken`
|
|
45
|
+
// immediately instead of masking as healthy for THRESHOLD x cooldown. Cleared
|
|
46
|
+
// whenever new credentials are saved (a successful refresh or `dario login`).
|
|
47
|
+
let refreshTokenDead = false;
|
|
42
48
|
// In-memory credential cache — avoids disk reads on every request
|
|
43
49
|
let credentialsCache = null;
|
|
44
50
|
let credentialsCacheTime = 0;
|
|
@@ -401,6 +407,9 @@ export function pickFreshestCredentials(candidates) {
|
|
|
401
407
|
return best;
|
|
402
408
|
}
|
|
403
409
|
async function saveCredentials(creds) {
|
|
410
|
+
// New credentials (a successful refresh or a fresh `dario login`) mean the
|
|
411
|
+
// token is live again — clear the terminal-dead flag.
|
|
412
|
+
refreshTokenDead = false;
|
|
404
413
|
const path = getDarioCredentialsPath();
|
|
405
414
|
await mkdir(dirname(path), { recursive: true });
|
|
406
415
|
// Write atomically: write to temp file, then rename
|
|
@@ -688,6 +697,17 @@ export async function readLineFromStdin(prompt) {
|
|
|
688
697
|
* Retries with exponential backoff on transient failures.
|
|
689
698
|
* Uses a mutex to prevent concurrent refresh races.
|
|
690
699
|
*/
|
|
700
|
+
/**
|
|
701
|
+
* A refresh failure that retrying cannot recover: the refresh token is
|
|
702
|
+
* invalid, revoked, or rotated out (e.g. a second dario on the same account
|
|
703
|
+
* consumed the single-use token). Anthropic signals this as HTTP 401/403, or
|
|
704
|
+
* as a 400 with an `invalid_grant` body ("Refresh token not found or
|
|
705
|
+
* invalid"). Terminal -> fail fast with a re-login prompt instead of burning
|
|
706
|
+
* doomed retries and masking as healthy.
|
|
707
|
+
*/
|
|
708
|
+
export function isTerminalRefreshFailure(status, body) {
|
|
709
|
+
return status === 401 || status === 403 || /invalid_grant/i.test(body);
|
|
710
|
+
}
|
|
691
711
|
export async function refreshTokens() {
|
|
692
712
|
// Prevent concurrent refreshes — if one is already in progress, wait for it
|
|
693
713
|
if (refreshInProgress)
|
|
@@ -723,8 +743,13 @@ async function doRefreshTokens() {
|
|
|
723
743
|
if (!res.ok) {
|
|
724
744
|
const errBody = await res.text().catch(() => '');
|
|
725
745
|
console.error(`[dario] Refresh attempt ${attempt + 1}/3 failed: HTTP ${res.status} — ${redactSecrets(errBody.slice(0, 200))}`);
|
|
726
|
-
|
|
727
|
-
|
|
746
|
+
// Terminal failures (invalid_grant / 401 / 403) can't be retried away —
|
|
747
|
+
// the refresh token is dead. Flag it so /status, /health, and doctor
|
|
748
|
+
// report `broken` immediately (not after THRESHOLD x cooldown), and fail
|
|
749
|
+
// fast with an actionable message instead of a vague "3 attempts" throw.
|
|
750
|
+
if (isTerminalRefreshFailure(res.status, errBody)) {
|
|
751
|
+
refreshTokenDead = true;
|
|
752
|
+
throw new Error(`Refresh token invalid or revoked (HTTP ${res.status}) — run \`dario login\` to re-authenticate.`);
|
|
728
753
|
}
|
|
729
754
|
continue;
|
|
730
755
|
}
|
|
@@ -795,7 +820,7 @@ export async function getStatus() {
|
|
|
795
820
|
}
|
|
796
821
|
const { expiresAt } = creds.claudeAiOauth;
|
|
797
822
|
const now = Date.now();
|
|
798
|
-
const broken = consecutiveRefreshFailures >= REFRESH_BROKEN_THRESHOLD;
|
|
823
|
+
const broken = refreshTokenDead || consecutiveRefreshFailures >= REFRESH_BROKEN_THRESHOLD;
|
|
799
824
|
if (expiresAt < now) {
|
|
800
825
|
// Expired but has refresh token — can be refreshed (unless refresh itself is dead)
|
|
801
826
|
const canRefresh = !!creds.claudeAiOauth.refreshToken && !broken;
|
package/dist/proxy.d.ts
CHANGED
|
@@ -37,6 +37,21 @@ export declare function buildBillingTag(cliVersion: string, cch: string | null):
|
|
|
37
37
|
* that needs to Just Work.
|
|
38
38
|
*/
|
|
39
39
|
export declare function resolveClaudeAlias(model: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Pick the per-request model override under a forced `--model`.
|
|
42
|
+
*
|
|
43
|
+
* A forced `--model` normally rewrites the model on *every* request. But
|
|
44
|
+
* Claude Code dispatches its throwaway sub-agent (Explore/Task) turns on the
|
|
45
|
+
* cheap Haiku tier, so forcing a frontier model silently upgrades those
|
|
46
|
+
* sub-agents too — quietly multiplying cost. When `--fast-model` is set, a
|
|
47
|
+
* Haiku-tier inbound request routes there instead, keeping sub-agents cheap
|
|
48
|
+
* while the main conversation stays on `--model`.
|
|
49
|
+
*
|
|
50
|
+
* No-op unless `fastModelOverride` is set: with it null, this returns
|
|
51
|
+
* `modelOverride` for every request (the pre-existing behavior), so the
|
|
52
|
+
* change is inert until the operator opts in.
|
|
53
|
+
*/
|
|
54
|
+
export declare function selectModelOverride(incomingModel: string, modelOverride: string | null, fastModelOverride: string | null): string | null;
|
|
40
55
|
export declare function parseProviderPrefix(model: string): {
|
|
41
56
|
provider: 'openai' | 'claude';
|
|
42
57
|
model: string;
|
|
@@ -209,12 +224,21 @@ export declare const OPENAI_MODELS_LIST: {
|
|
|
209
224
|
owned_by: string;
|
|
210
225
|
}>;
|
|
211
226
|
};
|
|
227
|
+
/**
|
|
228
|
+
* Whether dario must have a Claude login to start. False (an empty pool is
|
|
229
|
+
* expected, not a fatal "run dario login") for the modes that serve requests
|
|
230
|
+
* without the Claude OAuth pool: admin-bootstrap, upstream-api-key, and
|
|
231
|
+
* --no-claude-auth. Pure so the startup gate is unit-testable.
|
|
232
|
+
*/
|
|
233
|
+
export declare function requiresClaudeLogin(poolSize: number, adminEnabled: boolean, hasUpstreamApiKey: boolean, noClaudeAuth: boolean): boolean;
|
|
212
234
|
interface ProxyOptions {
|
|
213
235
|
port?: number;
|
|
214
236
|
host?: string;
|
|
215
237
|
verbose?: boolean;
|
|
216
238
|
verboseBodies?: boolean;
|
|
217
239
|
model?: string;
|
|
240
|
+
fastModel?: string;
|
|
241
|
+
noClaudeAuth?: boolean;
|
|
218
242
|
passthrough?: boolean;
|
|
219
243
|
preserveTools?: boolean;
|
|
220
244
|
hybridTools?: boolean;
|
package/dist/proxy.js
CHANGED
|
@@ -235,6 +235,25 @@ const MODEL_ALIASES = {
|
|
|
235
235
|
export function resolveClaudeAlias(model) {
|
|
236
236
|
return resolveAliasAgainst(model, getCachedBases()) ?? MODEL_ALIASES[model] ?? model;
|
|
237
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Pick the per-request model override under a forced `--model`.
|
|
240
|
+
*
|
|
241
|
+
* A forced `--model` normally rewrites the model on *every* request. But
|
|
242
|
+
* Claude Code dispatches its throwaway sub-agent (Explore/Task) turns on the
|
|
243
|
+
* cheap Haiku tier, so forcing a frontier model silently upgrades those
|
|
244
|
+
* sub-agents too — quietly multiplying cost. When `--fast-model` is set, a
|
|
245
|
+
* Haiku-tier inbound request routes there instead, keeping sub-agents cheap
|
|
246
|
+
* while the main conversation stays on `--model`.
|
|
247
|
+
*
|
|
248
|
+
* No-op unless `fastModelOverride` is set: with it null, this returns
|
|
249
|
+
* `modelOverride` for every request (the pre-existing behavior), so the
|
|
250
|
+
* change is inert until the operator opts in.
|
|
251
|
+
*/
|
|
252
|
+
export function selectModelOverride(incomingModel, modelOverride, fastModelOverride) {
|
|
253
|
+
if (fastModelOverride && /haiku/i.test(incomingModel))
|
|
254
|
+
return fastModelOverride;
|
|
255
|
+
return modelOverride;
|
|
256
|
+
}
|
|
238
257
|
// Provider prefix in the `model` field — `<provider>:<model>`. Forces
|
|
239
258
|
// routing regardless of model-name regex. Only recognized prefixes are
|
|
240
259
|
// parsed, so ollama-style `llama3:8b` (without a recognized prefix)
|
|
@@ -683,6 +702,15 @@ export function createOpenAIStreamTranslator() {
|
|
|
683
702
|
// autodetected catalog (model-catalog.ts); `[1m]` variants are GENERATED by
|
|
684
703
|
// the one shared long-context rule, never hand-listed per model.
|
|
685
704
|
export const OPENAI_MODELS_LIST = buildOpenAIModelsList(withLongContextVariants(BAKED_BASE_MODELS));
|
|
705
|
+
/**
|
|
706
|
+
* Whether dario must have a Claude login to start. False (an empty pool is
|
|
707
|
+
* expected, not a fatal "run dario login") for the modes that serve requests
|
|
708
|
+
* without the Claude OAuth pool: admin-bootstrap, upstream-api-key, and
|
|
709
|
+
* --no-claude-auth. Pure so the startup gate is unit-testable.
|
|
710
|
+
*/
|
|
711
|
+
export function requiresClaudeLogin(poolSize, adminEnabled, hasUpstreamApiKey, noClaudeAuth) {
|
|
712
|
+
return poolSize === 0 && !adminEnabled && !hasUpstreamApiKey && !noClaudeAuth;
|
|
713
|
+
}
|
|
686
714
|
/**
|
|
687
715
|
* Append a JSON-ND line to the proxy log file. No-op when stream is
|
|
688
716
|
* null (logFile not configured). Errors are swallowed — log writes
|
|
@@ -1060,17 +1088,29 @@ export async function startProxy(opts = {}) {
|
|
|
1060
1088
|
console.error(`[dario] overage-guard resumed (${info.reason}). Normal request handling restored.`);
|
|
1061
1089
|
});
|
|
1062
1090
|
let status;
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1091
|
+
// --no-claude-auth: don't populate the Claude OAuth pool. An empty pool makes
|
|
1092
|
+
// the background refresh a no-op, so dario never touches (and never rotates)
|
|
1093
|
+
// the shared Claude refresh token — the fix for a local OpenAI-only proxy
|
|
1094
|
+
// logging out an interactive Claude Code on the same machine (dario#737 class,
|
|
1095
|
+
// locally). OpenAI-compat requests use their own backend key, unaffected.
|
|
1096
|
+
if (opts.noClaudeAuth) {
|
|
1097
|
+
console.error('[dario] --no-claude-auth: Claude OAuth pool NOT loaded — serving OpenAI-compatible backends only; the Claude refresh token is never touched. Claude-bound requests return an unauthenticated error.');
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
for (const acc of accountsList) {
|
|
1101
|
+
pool.add(acc.alias, {
|
|
1102
|
+
accessToken: acc.accessToken,
|
|
1103
|
+
refreshToken: acc.refreshToken,
|
|
1104
|
+
expiresAt: acc.expiresAt,
|
|
1105
|
+
deviceId: acc.deviceId,
|
|
1106
|
+
accountUuid: acc.accountUuid,
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1071
1109
|
}
|
|
1072
1110
|
// Background refresh — keep every account's token fresh without blocking requests
|
|
1073
1111
|
const refreshInterval = setInterval(async () => {
|
|
1112
|
+
if (opts.noClaudeAuth)
|
|
1113
|
+
return; // never touch the Claude token in OpenAI-only mode
|
|
1074
1114
|
for (const acc of pool.all()) {
|
|
1075
1115
|
if (acc.expiresAt < Date.now() + 45 * 60 * 1000) {
|
|
1076
1116
|
try {
|
|
@@ -1098,7 +1138,7 @@ export async function startProxy(opts = {}) {
|
|
|
1098
1138
|
// dead-but-refreshable token (a container restarted right after a normal
|
|
1099
1139
|
// expiry, gap #1) is refreshed, then back-filled so the recovered login
|
|
1100
1140
|
// becomes the pool-of-one it should be — rather than crash-looping on exit(1).
|
|
1101
|
-
if (pool.size
|
|
1141
|
+
if (requiresClaudeLogin(pool.size, adminEnabled, !!upstreamApiKey, opts.noClaudeAuth ?? false)) {
|
|
1102
1142
|
const single = await resolveSingleAccountStartupStatus();
|
|
1103
1143
|
if (!single.authenticated) {
|
|
1104
1144
|
console.error('[dario] Not authenticated. Run `dario login` first.');
|
|
@@ -1150,6 +1190,12 @@ export async function startProxy(opts = {}) {
|
|
|
1150
1190
|
const cliModelRaw = modelPrefix ? modelPrefix.model : opts.model;
|
|
1151
1191
|
const cliProviderOverride = modelPrefix ? modelPrefix.provider : null;
|
|
1152
1192
|
const modelOverride = cliModelRaw ? resolveClaudeAlias(cliModelRaw) : null;
|
|
1193
|
+
// --fast-model: the model that Haiku-tier (Claude Code sub-agent) requests
|
|
1194
|
+
// route to instead of the forced `--model`, so sub-agents stay cheap. Parsed
|
|
1195
|
+
// like --model (alias + optional provider prefix). Null = disabled.
|
|
1196
|
+
const fastModelPrefix = opts.fastModel ? parseProviderPrefix(opts.fastModel) : null;
|
|
1197
|
+
const fastModelRaw = fastModelPrefix ? fastModelPrefix.model : opts.fastModel;
|
|
1198
|
+
const fastModelOverride = fastModelRaw ? resolveClaudeAlias(fastModelRaw) : null;
|
|
1153
1199
|
const identity = loadClaudeIdentity();
|
|
1154
1200
|
if (identity.deviceId) {
|
|
1155
1201
|
console.log(' Device identity: detected');
|
|
@@ -2034,7 +2080,13 @@ export async function startProxy(opts = {}) {
|
|
|
2034
2080
|
const parsed = parsedBody ?? JSON.parse(body.toString());
|
|
2035
2081
|
// Strip orchestration tags from messages (Aider, Cursor, etc.)
|
|
2036
2082
|
sanitizeMessages(parsed, opts.preserveOrchestrationTags);
|
|
2037
|
-
|
|
2083
|
+
// Tier-aware routing: under a forced --model, a Haiku-tier sub-agent
|
|
2084
|
+
// request routes to --fast-model (when set) instead of the forced
|
|
2085
|
+
// model, so CC's cheap sub-agents aren't silently upgraded. Inert
|
|
2086
|
+
// when --fast-model is unset (effectiveOverride === modelOverride).
|
|
2087
|
+
const incomingModel = typeof parsed.model === 'string' ? parsed.model : '';
|
|
2088
|
+
const effectiveOverride = selectModelOverride(incomingModel, modelOverride, fastModelOverride);
|
|
2089
|
+
const result = isOpenAI ? openaiToAnthropic(parsed, effectiveOverride) : (effectiveOverride ? { ...parsed, model: effectiveOverride } : parsed);
|
|
2038
2090
|
const r = result;
|
|
2039
2091
|
requestModel = (r.model || '').toLowerCase();
|
|
2040
2092
|
// Suspended-model guard. Empty by default (Fable 5 returned globally
|
|
@@ -3364,7 +3416,10 @@ export async function startProxy(opts = {}) {
|
|
|
3364
3416
|
const modeLine = passthrough
|
|
3365
3417
|
? 'Mode: passthrough (OAuth swap only, no injection)'
|
|
3366
3418
|
: `OAuth: ${status.status} (expires in ${status.expiresIn})`;
|
|
3367
|
-
const
|
|
3419
|
+
const fastNote = fastModelOverride ? `, ${fastModelOverride} (Haiku-tier sub-agents)` : '';
|
|
3420
|
+
const modelLine = modelOverride
|
|
3421
|
+
? `Model: ${modelOverride} (all requests)${fastNote}`
|
|
3422
|
+
: `Model: passthrough (client decides)${fastNote}`;
|
|
3368
3423
|
// Pool line surfaces the account state on every startup. The pool is the
|
|
3369
3424
|
// one credential model now (v5.0): a plain `dario login` shows as a pool of
|
|
3370
3425
|
// one. An empty pool is only reachable in admin-bootstrap / api-key mode.
|
|
@@ -3388,6 +3443,8 @@ export async function startProxy(opts = {}) {
|
|
|
3388
3443
|
console.log('');
|
|
3389
3444
|
console.log(` ${modeLine}`);
|
|
3390
3445
|
console.log(` ${modelLine}`);
|
|
3446
|
+
if (opts.noClaudeAuth)
|
|
3447
|
+
console.log(' Claude auth: disabled (--no-claude-auth) — OpenAI-compatible backends only; Claude token untouched');
|
|
3391
3448
|
console.log(` ${poolLine}`);
|
|
3392
3449
|
if (!isLoopbackHost(host)) {
|
|
3393
3450
|
console.log('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "Use your Claude Pro/Max subscription in any tool — Cursor, Cline, Aider, the Agent SDK, your scripts — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|