@askalf/dario 4.8.117 → 4.8.118
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 -6
- package/dist/health-response.d.ts +2 -0
- package/dist/health-response.js +3 -0
- package/dist/proxy.js +5 -2
- package/dist/tui/tabs/accounts.d.ts +17 -5
- package/dist/tui/tabs/accounts.js +99 -20
- package/dist/version.d.ts +3 -0
- package/dist/version.js +32 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
|
|
19
19
|
> 🗓️ **2026-06-15 — Anthropic splits Claude billing.** Agent-SDK and `claude -p` (headless) traffic stops counting against your subscription pool and moves to a small separate monthly credit ($20 / $100 / $200 by plan), then metered per-token API rates. Most proxies forward your requests in exactly the shape that gets reclassified into that bucket. dario rewrites every request into interactive Claude Code wire-shape before it leaves your machine, so your traffic stays in the subscription pool you already pay for — same install, no config change for the cliff. **[What changes, and how to verify it on your own machine →](#the-deadline-2026-06-15)**
|
|
20
20
|
|
|
21
|
-
> ⛔ **Claude Fable 5 / Mythos 5 — temporarily suspended for all Anthropic customers.** On 2026-06-12 a US-government legal directive disabled Fable 5 and Mythos 5 for **every** Anthropic plan and tier — `api.anthropic.com` now returns `not_found` for them, and no account or proxy can route around it ([details](https://www.anthropic.com/news/fable-mythos-access)). As of **v4.8.71**, dario filters both families out of `/v1/models` and rejects any spelling (`fable`, `fable1m`, `claude-fable-5`, `claude-fable-5[1m]`, `claude:fable`) up front with a clean `404` pointing at `claude-opus-4-8` / `claude-sonnet-5` — instead of forwarding into a confusing upstream error. Reversible without a code change once access is restored: set `DARIO_SUSPENDED_MODELS=` (empty) on the instance. `npm install -g @askalf/dario@latest`
|
|
22
|
-
>
|
|
23
21
|
> ⚠️ Still on a version **before 4.8.39**? Upgrade now — those could silently corrupt code/structured content routed through the proxy (the identifier scrub stripped tokens like the JS `continue` keyword). **[Details →](https://github.com/askalf/dario/issues/457)**
|
|
24
22
|
|
|
25
23
|
---
|
|
@@ -194,7 +192,7 @@ You point every tool at one URL. dario reads each request, decides which backend
|
|
|
194
192
|
|
|
195
193
|
The tool doesn't know. The backend doesn't know. dario is the seam.
|
|
196
194
|
|
|
197
|
-
**The full Claude lineup, autodetected.** Opus 4.8, Sonnet 5, and Haiku 4.5 — plus `[1m]` long-context variants, generated by one rule for every family — by full id (`claude-opus-4-8`) or shortcut (`opus` / `sonnet` / `haiku`, append `1m` for the long-context form). `GET /v1/models` asks Anthropic's live catalog (TTL-cached, baked fallback when offline), and the family shortcuts track it — a new model shows up and resolves the day it lands, no dario release needed; the model-specific wire shape (effort level, beta set, thinking config) is applied automatically. **
|
|
195
|
+
**The full Claude lineup, autodetected.** Opus 4.8, Sonnet 5, and Haiku 4.5 — plus `[1m]` long-context variants, generated by one rule for every family — by full id (`claude-opus-4-8`) or shortcut (`opus` / `sonnet` / `haiku`, append `1m` for the long-context form). `GET /v1/models` asks Anthropic's live catalog (TTL-cached, baked fallback when offline), and the family shortcuts track it — a new model shows up and resolves the day it lands, no dario release needed; the model-specific wire shape (effort level, beta set, thinking config) is applied automatically. **Suspended families are filtered out** of both the live catalog and the baked fallback, so `/v1/models` never advertises a model that 404s upstream and suspended ids are rejected locally with an actionable error. Nothing is suspended by default; the mechanism is retained via `DARIO_SUSPENDED_MODELS` (comma-separated families, every spelling caught) for if a family is ever pulled upstream again.
|
|
198
196
|
|
|
199
197
|
---
|
|
200
198
|
|
|
@@ -224,6 +222,7 @@ Tune via `~/.dario/config.json` → `overageGuard`, or CLI flags: `--overage-beh
|
|
|
224
222
|
## Capabilities
|
|
225
223
|
|
|
226
224
|
- **Multi-account pool.** Drop Claude accounts in `~/.dario/accounts/` and pool mode auto-activates — one is enough to serve, so `dario accounts add` alone bootstraps a proxy with no `dario login` step. With more, every request routes to the account with the most headroom, multi-turn sessions pin to one account so the prompt cache survives, in-flight 429s fail over to a peer before your client sees an error. → [`docs/multi-account-pool.md`](./docs/multi-account-pool.md)
|
|
225
|
+
- **Headless admin API (`DARIO_ADMIN=1`).** Provision and manage pool accounts entirely over HTTP — start with zero accounts, `POST /admin/login/start`, paste the code back, and the account is routable the moment the `200` lands (live pool hot-reload, no restart). Token-gated even on loopback, audit-logged, rate-limited; `GET /admin/accounts` reports live per-account headroom. Built for Docker / k8s / Raspberry-Pi deployments where a console is the awkward part. → [`docs/admin-api.md`](./docs/admin-api.md)
|
|
227
226
|
- **Behavioral stealth (`--stealth`).** Static wire fidelity covers *what* the request looks like; `--stealth` adds *when* it arrives — response-length-correlated think time and 1.2–4.2s session-start latency, the inter-arrival pattern real interactive sessions have and agent loops don't. → [`docs/wire-fidelity.md`](./docs/wire-fidelity.md)
|
|
228
227
|
- **Runs any non-Claude-Code agent.** A 64-entry schema-verified `TOOL_MAP` pre-maps Cline, Roo, Kilo, Cursor, Windsurf, Continue, Copilot, OpenHands, OpenClaw, Hermes, [hands](https://github.com/askalf/hands) tool names to CC's native set. No flag, no validator errors. → [`docs/integrations/agent-compat.md`](./docs/integrations/agent-compat.md)
|
|
229
228
|
- **Recover output capability.** `dario proxy --system-prompt=partial` strips CC's tone/verbosity/no-comments constraints for 1.2–2.8× more output on open-ended work — empirically without flipping billing (the classifier doesn't read that slot). [Discussion #183](https://github.com/askalf/dario/discussions/183) has the per-prompt receipts. → [`docs/system-prompt.md`](./docs/system-prompt.md)
|
|
@@ -302,9 +301,6 @@ No. `five_hour` and `seven_day` are both subscription billing — different acco
|
|
|
302
301
|
**Will the 2026-06-15 split break my setup? / What if Anthropic ships another silent change?**
|
|
303
302
|
No, and it's caught automatically — see [The deadline](#the-deadline-2026-06-15) and [How it stays working](#how-it-works-and-how-it-stays-working). dario rewrites every request to interactive-CC shape before it reaches `api.anthropic.com`, and the three-class drift watcher picks up new changes (npm-release hourly, remote-config every 30 min, classifier-rule daily). v3.38.5 + v3.38.6 — 13 minutes apart, same day as v2.1.142's silent drops — are the prior art.
|
|
304
303
|
|
|
305
|
-
**I'm getting `404 not_found` for `claude-fable-5` / `fable`. Did dario break?**
|
|
306
|
-
No — Claude Fable 5 and Mythos 5 were disabled for **all** Anthropic customers by a US-government legal directive on 2026-06-12 (every plan and tier; [details](https://www.anthropic.com/news/fable-mythos-access)). `api.anthropic.com` returns `not_found` for them, so no subscription or proxy can route them. Since v4.8.71 dario drops both families from `/v1/models` and rejects them locally with a clear 404 pointing at `claude-opus-4-8` / `claude-sonnet-5`, instead of forwarding into a confusing upstream error. When access is restored, set `DARIO_SUSPENDED_MODELS=` (empty) on the instance to re-enable — no upgrade needed.
|
|
307
|
-
|
|
308
304
|
Full FAQ: [`docs/faq.md`](./docs/faq.md)
|
|
309
305
|
|
|
310
306
|
---
|
|
@@ -20,6 +20,8 @@ export interface HealthStatusLike {
|
|
|
20
20
|
expiresIn?: string;
|
|
21
21
|
refreshFailures?: number;
|
|
22
22
|
lastRefreshError?: string;
|
|
23
|
+
/** dario's package version, surfaced to internal callers on /health (#640). */
|
|
24
|
+
version?: string;
|
|
23
25
|
}
|
|
24
26
|
export interface HealthResponse {
|
|
25
27
|
httpStatus: number;
|
package/dist/health-response.js
CHANGED
|
@@ -69,6 +69,9 @@ export function buildHealthResponse(s, requestCount, viaPublicTunnel) {
|
|
|
69
69
|
? liveness
|
|
70
70
|
: {
|
|
71
71
|
...liveness,
|
|
72
|
+
// Version for internal callers only — an update-check signal (#640),
|
|
73
|
+
// withheld from the public-tunnel view alongside the OAuth internals.
|
|
74
|
+
...(s.version ? { version: s.version } : {}),
|
|
72
75
|
oauth: s.status,
|
|
73
76
|
expiresIn: s.expiresIn,
|
|
74
77
|
requests: requestCount,
|
package/dist/proxy.js
CHANGED
|
@@ -8,6 +8,7 @@ import { setDefaultResultOrder } from 'node:dns';
|
|
|
8
8
|
import { arch, platform } from 'node:process';
|
|
9
9
|
import { getAccessToken, getStatus } from './oauth.js';
|
|
10
10
|
import { buildHealthResponse, derivePoolStatus } from './health-response.js';
|
|
11
|
+
import { darioVersion } from './version.js';
|
|
11
12
|
import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, CC_TEMPLATE } from './cc-template.js';
|
|
12
13
|
import { stampCch } from './cch.js';
|
|
13
14
|
import { describeTemplate, detectDrift, checkCCCompat } from './live-fingerprint.js';
|
|
@@ -1271,7 +1272,7 @@ export async function startProxy(opts = {}) {
|
|
|
1271
1272
|
// Public requests arrive through the Cloudflare tunnel (the edge stamps
|
|
1272
1273
|
// `cf-ray`); they get only the liveness verdict, never the OAuth internals.
|
|
1273
1274
|
// See buildHealthResponse for the full rationale.
|
|
1274
|
-
const { httpStatus, body } = buildHealthResponse(s, requestCount, req.headers['cf-ray'] !== undefined);
|
|
1275
|
+
const { httpStatus, body } = buildHealthResponse({ ...s, version: darioVersion() }, requestCount, req.headers['cf-ray'] !== undefined);
|
|
1275
1276
|
res.writeHead(httpStatus, JSON_HEADERS);
|
|
1276
1277
|
res.end(JSON.stringify(body));
|
|
1277
1278
|
return;
|
|
@@ -1376,8 +1377,10 @@ export async function startProxy(opts = {}) {
|
|
|
1376
1377
|
// Status endpoint
|
|
1377
1378
|
if (urlPath === '/status') {
|
|
1378
1379
|
const s = await currentStatus();
|
|
1380
|
+
// Version for auto-update checks (#640) — /status is key-gated (loopback
|
|
1381
|
+
// or DARIO_API_KEY), so no public-disclosure concern like /health has.
|
|
1379
1382
|
res.writeHead(200, JSON_HEADERS);
|
|
1380
|
-
res.end(JSON.stringify(s));
|
|
1383
|
+
res.end(JSON.stringify({ version: darioVersion(), ...s }));
|
|
1381
1384
|
return;
|
|
1382
1385
|
}
|
|
1383
1386
|
// Pool status endpoint — shows loaded accounts, headroom, and the
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Accounts tab — list of OAuth subscription accounts in the pool.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Source of truth is the RUNNING PROXY's live pool (`GET /accounts`), not a
|
|
5
|
+
* local disk read: the TUI is its own process, and in a containerized / admin
|
|
6
|
+
* (#599) / login-less-pool (#630) deployment the accounts live in the proxy's
|
|
7
|
+
* volume, so reading `~/.dario/accounts/` in the TUI process comes up empty
|
|
8
|
+
* while the proxy serves several accounts fine (#641). We fall back to the disk
|
|
9
|
+
* read only when the proxy is unreachable, and flag it so the user knows the
|
|
10
|
+
* view may be stale.
|
|
11
|
+
*
|
|
12
|
+
* Read-mostly. Mutations (add/remove) require the CLI or the admin API — the
|
|
13
|
+
* tab shows the relevant command in the footer.
|
|
6
14
|
*
|
|
7
15
|
* Layout:
|
|
8
16
|
*
|
|
@@ -16,17 +24,21 @@
|
|
|
16
24
|
* To add: `dario accounts add <alias>`
|
|
17
25
|
* To remove: `dario accounts remove <alias>`
|
|
18
26
|
*/
|
|
19
|
-
import type { Tab } from '../tab.js';
|
|
27
|
+
import type { Tab, TabContext } from '../tab.js';
|
|
20
28
|
export interface AccountsState {
|
|
21
29
|
loading: boolean;
|
|
22
30
|
accounts: Array<{
|
|
23
31
|
alias: string;
|
|
24
32
|
expiresAt: number;
|
|
25
|
-
/**
|
|
33
|
+
/** Live pool fields — present when sourced from the proxy's `/accounts`. */
|
|
26
34
|
util5h?: number;
|
|
27
35
|
util7d?: number;
|
|
36
|
+
status?: string;
|
|
28
37
|
}>;
|
|
29
38
|
error: string | null;
|
|
39
|
+
/** Where the list came from: the running proxy's pool, the proxy's
|
|
40
|
+
* single-account mode, or a local disk fallback when the proxy is down. */
|
|
41
|
+
source?: 'pool' | 'single-account' | 'disk';
|
|
30
42
|
}
|
|
31
43
|
export declare const AccountsTab: Tab<AccountsState>;
|
|
32
|
-
export declare function refreshAccounts(): Promise<AccountsState>;
|
|
44
|
+
export declare function refreshAccounts(ctx?: TabContext<AccountsState>): Promise<AccountsState>;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Accounts tab — list of OAuth subscription accounts in the pool.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Source of truth is the RUNNING PROXY's live pool (`GET /accounts`), not a
|
|
5
|
+
* local disk read: the TUI is its own process, and in a containerized / admin
|
|
6
|
+
* (#599) / login-less-pool (#630) deployment the accounts live in the proxy's
|
|
7
|
+
* volume, so reading `~/.dario/accounts/` in the TUI process comes up empty
|
|
8
|
+
* while the proxy serves several accounts fine (#641). We fall back to the disk
|
|
9
|
+
* read only when the proxy is unreachable, and flag it so the user knows the
|
|
10
|
+
* view may be stale.
|
|
11
|
+
*
|
|
12
|
+
* Read-mostly. Mutations (add/remove) require the CLI or the admin API — the
|
|
13
|
+
* tab shows the relevant command in the footer.
|
|
6
14
|
*
|
|
7
15
|
* Layout:
|
|
8
16
|
*
|
|
@@ -25,8 +33,8 @@ export const AccountsTab = {
|
|
|
25
33
|
initialState() {
|
|
26
34
|
return { loading: true, accounts: [], error: null };
|
|
27
35
|
},
|
|
28
|
-
async onMount(_state,
|
|
29
|
-
return refreshAccounts();
|
|
36
|
+
async onMount(_state, ctx) {
|
|
37
|
+
return refreshAccounts(ctx);
|
|
30
38
|
},
|
|
31
39
|
onKey(state, key) {
|
|
32
40
|
if (key.name === 'printable' && key.ch === 'r' && !key.ctrl) {
|
|
@@ -34,6 +42,18 @@ export const AccountsTab = {
|
|
|
34
42
|
}
|
|
35
43
|
return undefined;
|
|
36
44
|
},
|
|
45
|
+
onTick(state, ctx) {
|
|
46
|
+
// onKey can only return new state, not run async work — so a manual
|
|
47
|
+
// refresh ('r') just sets loading:true and this tick drives the refetch.
|
|
48
|
+
// `refreshInFlight` guards against the 250ms tick stacking overlapping
|
|
49
|
+
// fetches while one is already running.
|
|
50
|
+
if (state.loading && !refreshInFlight) {
|
|
51
|
+
refreshInFlight = true;
|
|
52
|
+
void refreshAccounts(ctx)
|
|
53
|
+
.then((next) => ctx.setState(next))
|
|
54
|
+
.finally(() => { refreshInFlight = false; });
|
|
55
|
+
}
|
|
56
|
+
},
|
|
37
57
|
render(state, dimv) {
|
|
38
58
|
const lines = [];
|
|
39
59
|
const w = dimv.cols;
|
|
@@ -45,18 +65,42 @@ export const AccountsTab = {
|
|
|
45
65
|
}
|
|
46
66
|
if (state.accounts.length === 0) {
|
|
47
67
|
lines.push('');
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
if (state.source === 'single-account') {
|
|
69
|
+
lines.push(' ' + dim('Single-account mode (`dario login`) — no pool.'));
|
|
70
|
+
lines.push(' ' + 'Start a pool: ' + fg('cyan', 'dario accounts add <alias>'));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
lines.push(' ' + dim('No accounts in the pool.'));
|
|
74
|
+
lines.push(' ' + 'Add one: ' + fg('cyan', 'dario accounts add <alias>'));
|
|
75
|
+
}
|
|
50
76
|
return lines.join('\n');
|
|
51
77
|
}
|
|
78
|
+
// Live pool data (from /accounts) carries utilization; the disk fallback
|
|
79
|
+
// doesn't, so show the util columns only when the pool populated them.
|
|
80
|
+
const hasUtil = state.accounts.some((a) => a.util5h !== undefined);
|
|
81
|
+
if (state.source === 'disk') {
|
|
82
|
+
lines.push(' ' + fg('yellow', 'proxy unreachable — showing on-disk accounts (may be stale)'));
|
|
83
|
+
}
|
|
52
84
|
// Header row
|
|
53
|
-
lines.push(' ' + dim(
|
|
54
|
-
|
|
85
|
+
lines.push(' ' + dim(hasUtil
|
|
86
|
+
? pad('alias', 20) + pad('expires', 14) + pad('util5h', 9) + pad('util7d', 9) + pad('status', 14)
|
|
87
|
+
: pad('alias', 20) + pad('expires', 16) + pad('source', 24)));
|
|
88
|
+
lines.push(' ' + dim('─'.repeat(Math.min(w - 4, 66))));
|
|
55
89
|
for (const acc of state.accounts) {
|
|
56
90
|
const aliasCol = pad(acc.alias, 20);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
91
|
+
if (hasUtil) {
|
|
92
|
+
const expiresCol = pad(formatExpiry(acc.expiresAt), 14);
|
|
93
|
+
const u5 = pad(acc.util5h !== undefined ? `${Math.round(acc.util5h * 100)}%` : '—', 9);
|
|
94
|
+
const u7 = pad(acc.util7d !== undefined ? `${Math.round(acc.util7d * 100)}%` : '—', 9);
|
|
95
|
+
const statusCol = acc.status ?? '—';
|
|
96
|
+
const statusFg = statusCol === 'auth-cooldown' ? fg('yellow', statusCol) : dim(statusCol);
|
|
97
|
+
lines.push(' ' + aliasCol + expiresCol + u5 + u7 + statusFg);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const expiresCol = pad(formatExpiry(acc.expiresAt), 16);
|
|
101
|
+
const sourceCol = '~/.dario/accounts/' + acc.alias + '.json';
|
|
102
|
+
lines.push(' ' + aliasCol + expiresCol + dim(sourceCol));
|
|
103
|
+
}
|
|
60
104
|
}
|
|
61
105
|
lines.push('');
|
|
62
106
|
lines.push(' ' + dim('Mutations via CLI:'));
|
|
@@ -69,27 +113,62 @@ export const AccountsTab = {
|
|
|
69
113
|
return lines.join('\n');
|
|
70
114
|
},
|
|
71
115
|
};
|
|
72
|
-
|
|
116
|
+
/** Guards the onTick refetch against overlapping in-flight fetches. */
|
|
117
|
+
let refreshInFlight = false;
|
|
118
|
+
export async function refreshAccounts(ctx) {
|
|
119
|
+
// Preferred source: the running proxy's live pool. This is what actually
|
|
120
|
+
// serves traffic, and it works regardless of which process/host/volume the
|
|
121
|
+
// TUI itself runs on — the fix for #641, where a containerized proxy held
|
|
122
|
+
// the accounts and the TUI's local disk read came up empty.
|
|
123
|
+
if (ctx) {
|
|
124
|
+
try {
|
|
125
|
+
const r = await ctx.client.getJson('/accounts');
|
|
126
|
+
if (r.mode === 'single-account') {
|
|
127
|
+
return { loading: false, accounts: [], error: null, source: 'single-account' };
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(r.accounts)) {
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
return {
|
|
132
|
+
loading: false,
|
|
133
|
+
source: 'pool',
|
|
134
|
+
accounts: r.accounts.map((a) => ({
|
|
135
|
+
alias: a.alias,
|
|
136
|
+
expiresAt: now + (a.expiresInMs ?? 0),
|
|
137
|
+
util5h: a.util5h,
|
|
138
|
+
util7d: a.util7d,
|
|
139
|
+
status: a.status,
|
|
140
|
+
})),
|
|
141
|
+
error: null,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Unknown shape — fall through to the disk read below.
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Proxy unreachable (not running, wrong port, missing key) — fall back
|
|
148
|
+
// to the on-disk view so a standalone TUI still shows something, flagged
|
|
149
|
+
// stale so the user knows it isn't the live pool.
|
|
150
|
+
return diskFallback();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return diskFallback();
|
|
154
|
+
}
|
|
155
|
+
async function diskFallback() {
|
|
73
156
|
try {
|
|
74
157
|
const { listAccountAliases, loadAllAccounts } = await import('../../accounts.js');
|
|
75
158
|
const aliases = await listAccountAliases();
|
|
76
159
|
if (aliases.length === 0) {
|
|
77
|
-
|
|
78
|
-
// entry sourced from ~/.dario/credentials.json or keychain.
|
|
79
|
-
return { loading: false, accounts: [], error: null };
|
|
160
|
+
return { loading: false, accounts: [], error: null, source: 'disk' };
|
|
80
161
|
}
|
|
81
162
|
const all = await loadAllAccounts();
|
|
82
163
|
return {
|
|
83
164
|
loading: false,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expiresAt: a.expiresAt,
|
|
87
|
-
})),
|
|
165
|
+
source: 'disk',
|
|
166
|
+
accounts: all.map((a) => ({ alias: a.alias, expiresAt: a.expiresAt })),
|
|
88
167
|
error: null,
|
|
89
168
|
};
|
|
90
169
|
}
|
|
91
170
|
catch (e) {
|
|
92
|
-
return { loading: false, accounts: [], error: e.message };
|
|
171
|
+
return { loading: false, accounts: [], error: e.message, source: 'disk' };
|
|
93
172
|
}
|
|
94
173
|
}
|
|
95
174
|
function formatExpiry(expiresAt) {
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dario's own package version, read once from the bundled package.json.
|
|
3
|
+
*
|
|
4
|
+
* Surfaced on `/status` and `/health` (#640) so a headless operator can confirm
|
|
5
|
+
* an auto-update actually rolled the running proxy — `curl /health | jq .version`
|
|
6
|
+
* beats exec-ing into the container to read package.json.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
let cached = null;
|
|
12
|
+
export function darioVersion() {
|
|
13
|
+
if (cached !== null)
|
|
14
|
+
return cached;
|
|
15
|
+
let v = 'unknown';
|
|
16
|
+
try {
|
|
17
|
+
// dist/version.js → ../package.json (same layout the MCP server + CLI use).
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(here, '..', 'package.json'), 'utf-8'));
|
|
20
|
+
if (typeof pkg.version === 'string')
|
|
21
|
+
v = pkg.version;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// package.json missing/malformed — keep 'unknown', never throw.
|
|
25
|
+
}
|
|
26
|
+
cached = v;
|
|
27
|
+
return v;
|
|
28
|
+
}
|
|
29
|
+
/** Test-only: clear the memoized version. */
|
|
30
|
+
export function _resetVersionCacheForTest() {
|
|
31
|
+
cached = null;
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.118",
|
|
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": {
|