@askalf/dario 6.0.1 → 6.0.3
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 +2 -2
- package/dist/claude-model.d.ts +12 -0
- package/dist/claude-model.js +70 -0
- package/dist/codex-backend.d.ts +2 -1
- package/dist/codex-backend.js +26 -9
- package/dist/proxy.js +61 -18
- package/docs/multi-account-pool.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -177,7 +177,7 @@ That is a **chain**, read left to right, and each provider takes the first entry
|
|
|
177
177
|
|
|
178
178
|
A single-entry chain is one-way and means what it always meant, so an existing config is unaffected. Failover is entirely opt-in: without `--pool-fallback`, a drained pool still returns its honest 429/503.
|
|
179
179
|
|
|
180
|
-
The Claude entry
|
|
180
|
+
The Claude entry has to be a model the pool can actually serve. "Not a GPT model" is not the same thing, and swapping in a typo would trade a recoverable 429 for an unrecoverable 404 — so each entry is checked positively against the live model catalog and skipped if it fails, letting the real error surface. Canonical ids (`claude-sonnet-5`), long-context variants (`claude-sonnet-5[1m]`), catalog shorthands (`opus`, `sonnet1m`) and explicit provider prefixes (`claude:opus`, `anthropic:sonnet`) all qualify.
|
|
181
181
|
|
|
182
182
|
Only a **429 or 5xx** fails over. A 400 surfaces to you, because a bad request that fails over just reproduces itself on the other provider and buries the real cause.
|
|
183
183
|
|
|
@@ -303,7 +303,7 @@ The split isn't live, but it was announced once on short notice and could return
|
|
|
303
303
|
|
|
304
304
|
| Signal | Status |
|
|
305
305
|
|---|---|
|
|
306
|
-
| Source | **~29k** lines of TypeScript across **
|
|
306
|
+
| Source | **~29k** lines of TypeScript across **59** files — auditable in a weekend (v5 removed shim; the pool is the one code path) |
|
|
307
307
|
| Dependencies | **0 runtime.** Verify: `npm ls --production` |
|
|
308
308
|
| Provenance | Every release [SLSA-attested](https://www.npmjs.com/package/@askalf/dario) via GitHub Actions + Sigstore |
|
|
309
309
|
| Scanning | [CodeQL](https://github.com/askalf/dario/actions/workflows/codeql.yml) on every push and weekly |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Maps a bare name to a canonical id, or returns it unchanged. */
|
|
2
|
+
export type ModelResolver = (model: string) => string;
|
|
3
|
+
/**
|
|
4
|
+
* The canonical id the Claude pool would serve `model` as, or null when it
|
|
5
|
+
* cannot serve it. `bases` is the catalog base set — `getCachedBases()` at a
|
|
6
|
+
* call site, a fixture in a test. `resolve` is the alias pipeline; the default
|
|
7
|
+
* knows only catalog family shorthands, so pass the proxy's full resolver
|
|
8
|
+
* (operator aliases + pinned aliases) where one exists.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveClaudeServable(model: string, bases?: readonly string[], resolve?: ModelResolver): string | null;
|
|
11
|
+
/** Whether the Claude pool can serve `model`. See resolveClaudeServable. */
|
|
12
|
+
export declare function isClaudeServableModel(model: string, bases?: readonly string[], resolve?: ModelResolver): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Can the Claude pool actually serve this model name?" — the positive test
|
|
3
|
+
* the reverse half of failover needs, and WHAT it resolves to.
|
|
4
|
+
*
|
|
5
|
+
* Chain selection used to answer this by elimination: anything absent from the
|
|
6
|
+
* Codex slugs was handed to the Claude pool. That is not a capability test, it
|
|
7
|
+
* is a default, and it is wrong for every name that belongs to neither
|
|
8
|
+
* provider. `--pool-fallback=gpt-5.6-sol,gpt-4o` on a rate-limited
|
|
9
|
+
* subscription re-pointed the request at Anthropic as `gpt-4o` and turned a
|
|
10
|
+
* 429 into a 400 — and a typo'd slug (`claude-sonnet-6`, `gtp-5.6-sol`) did
|
|
11
|
+
* the same, silently, because a misspelling is also "absent from the slugs".
|
|
12
|
+
* A discovery outage makes it worse: `getCodexModelSlugs` degrades to an EMPTY
|
|
13
|
+
* set, at which point elimination calls EVERY chain entry Claude-servable.
|
|
14
|
+
*
|
|
15
|
+
* So the rule is positive, in two steps that mirror the request path:
|
|
16
|
+
*
|
|
17
|
+
* 1. RESOLVE the entry the way a request model is resolved — an explicit
|
|
18
|
+
* `claude:` / `anthropic:` prefix is stripped, then operator
|
|
19
|
+
* `--model-alias` values, pinned aliases (`opus48`) and catalog family
|
|
20
|
+
* shorthands (`opus`, `sonnet1m`) map to a canonical id. The caller
|
|
21
|
+
* supplies that resolver so classification and forwarding cannot
|
|
22
|
+
* disagree.
|
|
23
|
+
* 2. VALIDATE the resolved id against the catalog base set. A `claude-`
|
|
24
|
+
* prefix on its own proves nothing — `claude-sonnet-6` has the prefix and
|
|
25
|
+
* does not exist — so the id (sans `[1m]`) must be a base the pool can
|
|
26
|
+
* forward. An explicit provider prefix can force the route; it cannot
|
|
27
|
+
* make an unknown model servable.
|
|
28
|
+
*
|
|
29
|
+
* The RESOLVED id is what the caller swaps into the body. The swap happens
|
|
30
|
+
* after the proxy's own alias pass, so returning the entry as written would
|
|
31
|
+
* send an alias upstream raw and Anthropic would 400 it.
|
|
32
|
+
*
|
|
33
|
+
* Pure over the base set it is handed, so the whole selection stays testable
|
|
34
|
+
* without a socket. `bases` defaults to the baked catalog, never to empty: a
|
|
35
|
+
* cold catalog must still admit real ids, and must still refuse typos.
|
|
36
|
+
*/
|
|
37
|
+
import { BAKED_BASE_MODELS, resolveAliasAgainst } from './model-catalog.js';
|
|
38
|
+
/** Provider prefixes that force the Claude path (mirrors proxy.ts's PROVIDER_PREFIXES). */
|
|
39
|
+
const CLAUDE_PREFIXES = new Set(['claude', 'anthropic']);
|
|
40
|
+
/**
|
|
41
|
+
* The canonical id the Claude pool would serve `model` as, or null when it
|
|
42
|
+
* cannot serve it. `bases` is the catalog base set — `getCachedBases()` at a
|
|
43
|
+
* call site, a fixture in a test. `resolve` is the alias pipeline; the default
|
|
44
|
+
* knows only catalog family shorthands, so pass the proxy's full resolver
|
|
45
|
+
* (operator aliases + pinned aliases) where one exists.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveClaudeServable(model, bases = BAKED_BASE_MODELS, resolve) {
|
|
48
|
+
let m = model.trim().toLowerCase();
|
|
49
|
+
if (!m)
|
|
50
|
+
return null;
|
|
51
|
+
const idx = m.indexOf(':');
|
|
52
|
+
if (idx > 0) {
|
|
53
|
+
const prefix = m.slice(0, idx);
|
|
54
|
+
// A recognized non-Claude prefix is a definite NO, not a fall-through to
|
|
55
|
+
// the name test below — `openai:claude-sonnet-5` is the operator pointing
|
|
56
|
+
// somewhere else on purpose.
|
|
57
|
+
if (!CLAUDE_PREFIXES.has(prefix))
|
|
58
|
+
return null;
|
|
59
|
+
m = m.slice(idx + 1);
|
|
60
|
+
if (!m)
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const resolved = (resolve ? resolve(m) : (resolveAliasAgainst(m, bases) ?? m)).trim().toLowerCase();
|
|
64
|
+
const base = resolved.endsWith('[1m]') ? resolved.slice(0, -4) : resolved;
|
|
65
|
+
return bases.some((b) => b.toLowerCase() === base) ? resolved : null;
|
|
66
|
+
}
|
|
67
|
+
/** Whether the Claude pool can serve `model`. See resolveClaudeServable. */
|
|
68
|
+
export function isClaudeServableModel(model, bases = BAKED_BASE_MODELS, resolve) {
|
|
69
|
+
return resolveClaudeServable(model, bases, resolve) !== null;
|
|
70
|
+
}
|
package/dist/codex-backend.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
25
25
|
import type { CodexAccountCredentials } from './codex-accounts.js';
|
|
26
|
+
import { type ModelResolver } from './claude-model.js';
|
|
26
27
|
export declare const CODEX_BACKEND_BASE_URL: string;
|
|
27
28
|
/**
|
|
28
29
|
* Client version sent on the model-discovery call. The backend REQUIRES the
|
|
@@ -85,7 +86,7 @@ export declare function isCodexModel(model: string, slugs: readonly string[]): b
|
|
|
85
86
|
* written before this release behave identically.
|
|
86
87
|
*/
|
|
87
88
|
export declare function pickCodexFallback(models: readonly string[], slugs: readonly string[]): string | null;
|
|
88
|
-
export declare function pickClaudeFallback(models: readonly string[], slugs: readonly string[]): string | null;
|
|
89
|
+
export declare function pickClaudeFallback(models: readonly string[], slugs: readonly string[], bases?: readonly string[], resolve?: ModelResolver): string | null;
|
|
89
90
|
/**
|
|
90
91
|
* Pull `chatgpt_account_id` out of the id_token's `https://api.openai.com/auth`
|
|
91
92
|
* claim. Payload only — this is reading our own token for a routing header, not
|
package/dist/codex-backend.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { anthropicToResponsesRequest, createResponsesSSEParser, formatResponsesAnthropicSSE, createAnthropicMessageAssembler, responsesStreamToAnthropicSSE, } from './anthropic-responses-translate.js';
|
|
2
|
+
import { resolveClaudeServable } from './claude-model.js';
|
|
3
|
+
import { BAKED_BASE_MODELS } from './model-catalog.js';
|
|
2
4
|
export const CODEX_BACKEND_BASE_URL = process.env.DARIO_CODEX_BASE_URL || 'https://chatgpt.com/backend-api/codex';
|
|
3
5
|
/** Originator string the codex CLI identifies itself with. */
|
|
4
6
|
const CODEX_ORIGINATOR = 'codex_cli_rs';
|
|
@@ -109,21 +111,36 @@ export function isCodexModel(model, slugs) {
|
|
|
109
111
|
export function pickCodexFallback(models, slugs) {
|
|
110
112
|
return models.find(m => isCodexModel(m, slugs)) ?? null;
|
|
111
113
|
}
|
|
112
|
-
export function pickClaudeFallback(models, slugs) {
|
|
114
|
+
export function pickClaudeFallback(models, slugs, bases = BAKED_BASE_MODELS, resolve) {
|
|
113
115
|
// "Not a codex slug" is NOT the same as "the Claude pool can serve it". A
|
|
114
116
|
// typo, a retired model, or an entry meant for some third provider would all
|
|
115
117
|
// pass that test, and the request would be swapped to a model Anthropic 404s
|
|
116
118
|
// on — trading a recoverable 429 for an unrecoverable 404, which is strictly
|
|
117
119
|
// worse than not failing over at all.
|
|
118
120
|
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
121
|
+
// v6.0.0 shipped this as `/^claude/i`, which was the right DIRECTION (fail
|
|
122
|
+
// closed) and the wrong TEST. It rejected `anthropic:opus`, where the operator
|
|
123
|
+
// named the provider explicitly, and every catalog shorthand (`opus`,
|
|
124
|
+
// `sonnet1m`) — so a legitimate chain entry silently never failed over, which
|
|
125
|
+
// is the same class of quiet wrongness it was written to prevent. It also did
|
|
126
|
+
// nothing about the case where discovery degrades and `slugs` arrives EMPTY,
|
|
127
|
+
// at which point elimination calls every entry Claude-servable.
|
|
122
128
|
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
|
|
129
|
+
// `isClaudeServableModel` is the positive capability test instead, resolved
|
|
130
|
+
// against the live catalog, so aliases work and the limitation the old comment
|
|
131
|
+
// documented as accepted is simply gone.
|
|
132
|
+
//
|
|
133
|
+
// Returns the RESOLVED canonical id, not the entry as written: the caller
|
|
134
|
+
// swaps it into the body after the proxy's own alias pass has already run,
|
|
135
|
+
// so an alias returned raw would reach Anthropic unresolved and 400.
|
|
136
|
+
for (const m of models) {
|
|
137
|
+
if (isCodexModel(m, slugs))
|
|
138
|
+
continue;
|
|
139
|
+
const resolved = resolveClaudeServable(m, bases, resolve);
|
|
140
|
+
if (resolved)
|
|
141
|
+
return resolved;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
127
144
|
}
|
|
128
145
|
/**
|
|
129
146
|
* Pull `chatgpt_account_id` out of the id_token's `https://api.openai.com/auth`
|
|
@@ -718,7 +735,7 @@ export async function forwardToCodex(req, res, body, creds, corsOrigin, security
|
|
|
718
735
|
}
|
|
719
736
|
catch { /* already closed */ }
|
|
720
737
|
}
|
|
721
|
-
report(502, null,
|
|
738
|
+
report(502, null, clientWantsStream, model);
|
|
722
739
|
return true;
|
|
723
740
|
}
|
|
724
741
|
finally {
|
package/dist/proxy.js
CHANGED
|
@@ -1863,6 +1863,42 @@ export async function startProxy(opts = {}) {
|
|
|
1863
1863
|
const hasNextOption = openaiBackend !== null && shape === 'openai';
|
|
1864
1864
|
return await forwardToCodex(req, res, fallbackBody, creds, corsOrigin, { ...SECURITY_HEADERS, 'x-dario-pool-fallback': fallbackModel }, upstreamTimeoutMs, verbose, shape, fetch, hasNextOption);
|
|
1865
1865
|
};
|
|
1866
|
+
/**
|
|
1867
|
+
* Mid-flight 429, no peer account left to retry — the pool is genuinely
|
|
1868
|
+
* exhausted mid-request, not just at selection. Tries every fallback in
|
|
1869
|
+
* order (subscription, then api-key backend) and returns true once one has
|
|
1870
|
+
* written the response; false means nothing could serve it and the caller
|
|
1871
|
+
* writes its own honest 429.
|
|
1872
|
+
*
|
|
1873
|
+
* A SINGLE function for BOTH mid-flight 429 sites in the request handler
|
|
1874
|
+
* (the non-passthrough recovery-loop path and the general/passthrough
|
|
1875
|
+
* path), by design. Those two sites used to carry independent, hand-copied
|
|
1876
|
+
* versions of this logic, and v6.0.0 wired the fallback into only ONE of
|
|
1877
|
+
* them. The other kept returning the raw upstream 429 — and that unwired
|
|
1878
|
+
* site is the one reached by every non-passthrough request, i.e. everything
|
|
1879
|
+
* except a literal Claude Code CLI session, forge's SDK Engine included.
|
|
1880
|
+
* That is what left the whole fleet unable to fail over during the live
|
|
1881
|
+
* outage on 2026-08-30 while `dario doctor` reported failover armed.
|
|
1882
|
+
*
|
|
1883
|
+
* One function reachable from both call sites is what makes that class of
|
|
1884
|
+
* bug structurally impossible to reintroduce: a future third call site gets
|
|
1885
|
+
* this by construction, not by remembering to copy six lines correctly.
|
|
1886
|
+
*/
|
|
1887
|
+
const attemptPoolFallbackOn429 = async (req, res, body, isOpenAI) => {
|
|
1888
|
+
if (await tryCodexPoolFallback(req, res, body, poolFallbackModels, isOpenAI ? 'openai' : 'anthropic', 'pool exhausted mid-flight (429, no peer)')) {
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
if (isOpenAI && poolFallbackModel && openaiBackend) {
|
|
1892
|
+
const fallbackBody = buildPoolFallbackBody(body, poolFallbackModel);
|
|
1893
|
+
if (fallbackBody) {
|
|
1894
|
+
console.log(`[dario] #${requestCount} pool exhausted mid-flight (429, no peer) → ${openaiBackend.name} as ${poolFallbackModel}`);
|
|
1895
|
+
requestCount++;
|
|
1896
|
+
await forwardToOpenAI(req, res, fallbackBody, openaiBackend, corsOrigin, { ...SECURITY_HEADERS, 'x-dario-pool-fallback': poolFallbackModel }, upstreamTimeoutMs, verbose);
|
|
1897
|
+
return true;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return false;
|
|
1901
|
+
};
|
|
1866
1902
|
const server = createServer(async (req, res) => {
|
|
1867
1903
|
if (req.method === 'OPTIONS') {
|
|
1868
1904
|
res.writeHead(204, CORS_HEADERS);
|
|
@@ -2706,7 +2742,12 @@ export async function startProxy(opts = {}) {
|
|
|
2706
2742
|
// and pick the request back up on the Claude path below. Before
|
|
2707
2743
|
// this, a rate-limited ChatGPT plan was terminal for a gpt-bound
|
|
2708
2744
|
// request even with an idle Claude pool sitting right beside it.
|
|
2709
|
-
|
|
2745
|
+
// Resolve chain entries exactly as a request model is resolved —
|
|
2746
|
+
// operator --model-alias first, then pinned + catalog aliases — so
|
|
2747
|
+
// `backup` or `opus48` in the chain works the way it does on a
|
|
2748
|
+
// request, and the classifier validates the id that would actually
|
|
2749
|
+
// be forwarded.
|
|
2750
|
+
const claudeTarget = pickClaudeFallback(poolFallbackModels, codexModels, getCachedBases(), (m) => resolveClaudeAlias(applyModelAlias(m, modelAliases) ?? m));
|
|
2710
2751
|
const canDefer = claudeTarget !== null && pool.size > 0 && !upstreamApiKey;
|
|
2711
2752
|
const codexReq = requestCount;
|
|
2712
2753
|
const served = await forwardToCodex(req, res, body, codexCreds, corsOrigin, SECURITY_HEADERS, upstreamTimeoutMs, verbose, isOpenAI ? 'openai' : 'anthropic', fetch, canDefer,
|
|
@@ -3585,6 +3626,19 @@ export async function startProxy(opts = {}) {
|
|
|
3585
3626
|
continue dispatchLoop;
|
|
3586
3627
|
}
|
|
3587
3628
|
}
|
|
3629
|
+
// Pool-exhausted fallback, mid-flight: no peer left to fail over
|
|
3630
|
+
// to. THE BUG (live incident, 2026-08-30) and its independent
|
|
3631
|
+
// rediscovery: this branch — reached by every non-passthrough
|
|
3632
|
+
// request, i.e. everything except a literal Claude Code CLI session
|
|
3633
|
+
// — never called the fallback wired into the OTHER, structurally
|
|
3634
|
+
// near-identical 429 site below. #1153 fixed it here with its own
|
|
3635
|
+
// inline copy; that copy is collapsed into the single shared
|
|
3636
|
+
// attemptPoolFallbackOn429 below, so the file goes back to having
|
|
3637
|
+
// exactly ONE copy of this logic instead of two near-identical ones
|
|
3638
|
+
// — which is the exact shape of bug this incident already was.
|
|
3639
|
+
if (await attemptPoolFallbackOn429(req, res, body, isOpenAI)) {
|
|
3640
|
+
return;
|
|
3641
|
+
}
|
|
3588
3642
|
const enriched = enrich429(peekedBody, upstream.headers);
|
|
3589
3643
|
const responseHeaders = {
|
|
3590
3644
|
'Content-Type': 'application/json',
|
|
@@ -3675,25 +3729,14 @@ export async function startProxy(opts = {}) {
|
|
|
3675
3729
|
continue dispatchLoop;
|
|
3676
3730
|
}
|
|
3677
3731
|
}
|
|
3678
|
-
// Pool-exhausted fallback: no peer left to fail over to.
|
|
3679
|
-
//
|
|
3680
|
-
//
|
|
3681
|
-
//
|
|
3682
|
-
//
|
|
3683
|
-
|
|
3684
|
-
// selection-time path.
|
|
3685
|
-
if (await tryCodexPoolFallback(req, res, body, poolFallbackModels, isOpenAI ? 'openai' : 'anthropic', 'pool exhausted mid-flight (429, no peer)')) {
|
|
3732
|
+
// Pool-exhausted fallback: no peer left to fail over to. `body`
|
|
3733
|
+
// still holds the client's own bytes as received — the Anthropic
|
|
3734
|
+
// translation (OpenAI-shape clients) went into finalBody, never back
|
|
3735
|
+
// into body. See attemptPoolFallbackOn429's doc comment for why this
|
|
3736
|
+
// is one shared function rather than an inline copy.
|
|
3737
|
+
if (await attemptPoolFallbackOn429(req, res, body, isOpenAI)) {
|
|
3686
3738
|
return;
|
|
3687
3739
|
}
|
|
3688
|
-
if (isOpenAI && poolFallbackModel && openaiBackend) {
|
|
3689
|
-
const fallbackBody = buildPoolFallbackBody(body, poolFallbackModel);
|
|
3690
|
-
if (fallbackBody) {
|
|
3691
|
-
console.log(`[dario] #${requestCount} pool exhausted mid-flight (429, no peer) — /v1/chat/completions → ${openaiBackend.name} as ${poolFallbackModel}`);
|
|
3692
|
-
requestCount++;
|
|
3693
|
-
await forwardToOpenAI(req, res, fallbackBody, openaiBackend, corsOrigin, { ...SECURITY_HEADERS, 'x-dario-pool-fallback': poolFallbackModel }, upstreamTimeoutMs, verbose);
|
|
3694
|
-
return;
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
3740
|
const errBody = await upstream.text().catch(() => '');
|
|
3698
3741
|
const enriched = enrich429(errBody, upstream.headers);
|
|
3699
3742
|
const responseHeaders = {
|
|
@@ -59,7 +59,7 @@ Neither subscription hitting its ceiling can take the deployment down on its own
|
|
|
59
59
|
Deliberate limits:
|
|
60
60
|
|
|
61
61
|
- **Only a 429 or 5xx fails over.** A 400 surfaces to the client. A bad request that fails over just reproduces itself on the other provider and buries the real cause.
|
|
62
|
-
- **The Claude entry
|
|
62
|
+
- **The Claude entry is validated positively, against the live catalog.** "Not a codex slug" would also match a typo, or a model meant for a third provider, and swapping that in trades a recoverable 429 for an unrecoverable 404. Worse, when model discovery degrades the codex slug list arrives EMPTY, at which point elimination would call *every* entry Claude-servable. So each entry is tested for what it is rather than what it isn't: canonical ids, `[1m]` variants, catalog shorthands (`opus`, `sonnet1m`) and explicit `claude:` / `anthropic:` prefixes all qualify; anything else is skipped and the real error surfaces.
|
|
63
63
|
- **The api-key backend is still OpenAI-shape only.** There is no Messages translation on that route. A Codex account has one, which is why it is preferred.
|
|
64
64
|
- **Never silent.** Every substituted response carries `x-dario-pool-fallback: <model>`. A quietly swapped model is exactly the surprise this project exists to avoid.
|
|
65
65
|
- **Empty pool still errors.** A pool with zero accounts is a setup mistake (`dario login` never ran); that returns the usual 503 rather than silently re-billing every request to another provider.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.3",
|
|
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": {
|