@askalf/dario 4.8.135 → 4.8.137
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/analytics.js +12 -0
- package/dist/cc-template.d.ts +22 -1
- package/dist/cc-template.js +32 -6
- package/dist/notify.d.ts +16 -0
- package/dist/notify.js +56 -38
- package/dist/proxy.js +5 -5
- package/package.json +1 -1
package/dist/analytics.js
CHANGED
|
@@ -25,6 +25,16 @@ export function billingBucketFromClaim(claim) {
|
|
|
25
25
|
switch (claim) {
|
|
26
26
|
case 'five_hour':
|
|
27
27
|
case 'seven_day':
|
|
28
|
+
// `*_overage_included` — the plan's INCLUDED overage credit, observed live
|
|
29
|
+
// 2026-07-05 on a Max account at 7d 82% with the `7d_oi` bucket at 99%:
|
|
30
|
+
// fable answered normally (genuine model echo, stop_reason end_turn),
|
|
31
|
+
// status=allowed_warning, overage-utilization 0 — $0 out of pocket, so it
|
|
32
|
+
// is subscription billing, not extra usage. Real paid overage still
|
|
33
|
+
// arrives as `overage` and still halts the guard. Pre-classification the
|
|
34
|
+
// guard's halt-on-unknown design 503'd the proxy on every such claim
|
|
35
|
+
// (30-min cooldown loops) exactly when the weekly window tightens.
|
|
36
|
+
case 'five_hour_overage_included':
|
|
37
|
+
case 'seven_day_overage_included':
|
|
28
38
|
return 'subscription';
|
|
29
39
|
case 'five_hour_fallback':
|
|
30
40
|
case 'seven_day_fallback':
|
|
@@ -49,6 +59,8 @@ export const SUBSCRIPTION_CLAIMS = new Set([
|
|
|
49
59
|
'seven_day',
|
|
50
60
|
'five_hour_fallback',
|
|
51
61
|
'seven_day_fallback',
|
|
62
|
+
'five_hour_overage_included',
|
|
63
|
+
'seven_day_overage_included',
|
|
52
64
|
]);
|
|
53
65
|
/**
|
|
54
66
|
* The sentinel `claim` dario assigns when a response carried no rate-limit
|
package/dist/cc-template.d.ts
CHANGED
|
@@ -56,12 +56,33 @@ export declare const CC_TOOL_DEFINITIONS: {
|
|
|
56
56
|
description: string;
|
|
57
57
|
input_schema: Record<string, unknown>;
|
|
58
58
|
}[];
|
|
59
|
+
/** The UNFILTERED bundled union — every tool the bake knows across platforms
|
|
60
|
+
* (PLATFORM_ONLY_TOOLS keeps the bundle a superset). The identity-mapping,
|
|
61
|
+
* detection, and advertise paths intersect with what the CLIENT declared,
|
|
62
|
+
* and the client's declaration already encodes its platform — a Linux CC
|
|
63
|
+
* never declares PowerShell. Filtering those paths by the PROXY HOST's
|
|
64
|
+
* process.platform (pre-v4.8.136) made a Linux-hosted dario treat a win32
|
|
65
|
+
* client's PowerShell/Glob/Grep as non-native: PowerShell fell to the
|
|
66
|
+
* unmapped round-robin, and all three were dropped from the advertised
|
|
67
|
+
* array (Glob/Grep translated via lowercase aliases but were never sent
|
|
68
|
+
* upstream). Host-filtered CC_TOOL_DEFINITIONS stays correct for the paths
|
|
69
|
+
* with no client declaration to mirror: the full-template fallback, the
|
|
70
|
+
* merge-mode base array, and Fable's no-tools shape. */
|
|
71
|
+
export declare const CC_TOOL_DEFINITIONS_UNION: {
|
|
72
|
+
name: string;
|
|
73
|
+
description: string;
|
|
74
|
+
input_schema: Record<string, unknown>;
|
|
75
|
+
}[];
|
|
76
|
+
export declare const CC_NATIVE_NAMES_UNION: Set<string>;
|
|
59
77
|
/** CC's own tool names, EXACT case ("Read", "Bash", "Agent", …). A CC client's
|
|
60
78
|
* tools identity-map to themselves and OVERRIDE TOOL_MAP — whose lowercase
|
|
61
79
|
* cross-client aliases ('read' → {path}/{filePath}) would otherwise mistranslate
|
|
62
80
|
* a CC tool (Read's file_path → path). Exact case is the discriminator: CC sends
|
|
63
81
|
* PascalCase, the {path}-style clients send lowercase/snake, so a non-CC `read`
|
|
64
|
-
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake).
|
|
82
|
+
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake).
|
|
83
|
+
* HOST-filtered — the routing, detection, and advertise paths use the
|
|
84
|
+
* _UNION variants below so a client on a different platform than the proxy
|
|
85
|
+
* host still identity-maps (v4.8.136). */
|
|
65
86
|
export declare const CC_NATIVE_NAMES: Set<string>;
|
|
66
87
|
/** MCP tools attached to a CC session are namespaced `mcp__<server>__<tool>`.
|
|
67
88
|
* Real CC advertises them VERBATIM after its built-ins — the schemas are
|
package/dist/cc-template.js
CHANGED
|
@@ -66,12 +66,29 @@ export const INTERACTIVE_ONLY_TOOLS = new Set([
|
|
|
66
66
|
]);
|
|
67
67
|
/** CC's exact tool definitions for the current platform — filtered from the bundled union. */
|
|
68
68
|
export const CC_TOOL_DEFINITIONS = filterToolsForPlatform(TEMPLATE.tools, process.platform);
|
|
69
|
+
/** The UNFILTERED bundled union — every tool the bake knows across platforms
|
|
70
|
+
* (PLATFORM_ONLY_TOOLS keeps the bundle a superset). The identity-mapping,
|
|
71
|
+
* detection, and advertise paths intersect with what the CLIENT declared,
|
|
72
|
+
* and the client's declaration already encodes its platform — a Linux CC
|
|
73
|
+
* never declares PowerShell. Filtering those paths by the PROXY HOST's
|
|
74
|
+
* process.platform (pre-v4.8.136) made a Linux-hosted dario treat a win32
|
|
75
|
+
* client's PowerShell/Glob/Grep as non-native: PowerShell fell to the
|
|
76
|
+
* unmapped round-robin, and all three were dropped from the advertised
|
|
77
|
+
* array (Glob/Grep translated via lowercase aliases but were never sent
|
|
78
|
+
* upstream). Host-filtered CC_TOOL_DEFINITIONS stays correct for the paths
|
|
79
|
+
* with no client declaration to mirror: the full-template fallback, the
|
|
80
|
+
* merge-mode base array, and Fable's no-tools shape. */
|
|
81
|
+
export const CC_TOOL_DEFINITIONS_UNION = TEMPLATE.tools;
|
|
82
|
+
export const CC_NATIVE_NAMES_UNION = new Set(TEMPLATE.tools.map((t) => String(t.name)));
|
|
69
83
|
/** CC's own tool names, EXACT case ("Read", "Bash", "Agent", …). A CC client's
|
|
70
84
|
* tools identity-map to themselves and OVERRIDE TOOL_MAP — whose lowercase
|
|
71
85
|
* cross-client aliases ('read' → {path}/{filePath}) would otherwise mistranslate
|
|
72
86
|
* a CC tool (Read's file_path → path). Exact case is the discriminator: CC sends
|
|
73
87
|
* PascalCase, the {path}-style clients send lowercase/snake, so a non-CC `read`
|
|
74
|
-
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake).
|
|
88
|
+
* still routes through TOOL_MAP. Tracks the live bundle (refreshed each bake).
|
|
89
|
+
* HOST-filtered — the routing, detection, and advertise paths use the
|
|
90
|
+
* _UNION variants below so a client on a different platform than the proxy
|
|
91
|
+
* host still identity-maps (v4.8.136). */
|
|
75
92
|
export const CC_NATIVE_NAMES = new Set(CC_TOOL_DEFINITIONS.map((t) => String(t.name)));
|
|
76
93
|
/** MCP tools attached to a CC session are namespaced `mcp__<server>__<tool>`.
|
|
77
94
|
* Real CC advertises them VERBATIM after its built-ins — the schemas are
|
|
@@ -551,7 +568,9 @@ export function detectNonCCByTools(clientTools) {
|
|
|
551
568
|
// routing agree. An ALL-mcp surface now scores ratio 0 and stays in default
|
|
552
569
|
// mode — safe, because the advertise path sends an mcp-only declaration
|
|
553
570
|
// verbatim rather than falling back to the full CC template.
|
|
554
|
-
|
|
571
|
+
// Union set, not the host-filtered one: a win32 client's PowerShell is
|
|
572
|
+
// native regardless of the platform dario itself runs on (v4.8.136).
|
|
573
|
+
if (!TOOL_MAP[rawName.toLowerCase()] && !CC_NATIVE_NAMES_UNION.has(rawName) && !isMcpToolName(rawName))
|
|
555
574
|
unmapped++;
|
|
556
575
|
}
|
|
557
576
|
const ratio = unmapped / clientTools.length;
|
|
@@ -1360,7 +1379,10 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1360
1379
|
// Exact case is the discriminator (CC sends PascalCase; {path}-style clients
|
|
1361
1380
|
// send lowercase/snake) so a genuine non-CC `read` still routes via TOOL_MAP.
|
|
1362
1381
|
// Tracks the live bundle, so future CC tools are covered after the next bake.
|
|
1363
|
-
|
|
1382
|
+
// UNION set: identity must hold for a win32 client's PowerShell/Glob/Grep
|
|
1383
|
+
// even when dario itself runs on Linux (v4.8.136) — the exact-case check
|
|
1384
|
+
// still keeps a non-CC lowercase `glob`/`grep` on its TOOL_MAP alias.
|
|
1385
|
+
const mapping = CC_NATIVE_NAMES_UNION.has(tool.name) || isMcpToolName(tool.name)
|
|
1364
1386
|
? { ccTool: tool.name, translateArgs: (a) => a, translateBack: (a) => a }
|
|
1365
1387
|
: TOOL_MAP[name];
|
|
1366
1388
|
if (mapping) {
|
|
@@ -1398,8 +1420,8 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1398
1420
|
const CC_FALLBACK_TOOLS = ['Bash', 'Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch'];
|
|
1399
1421
|
for (const tool of clientTools) {
|
|
1400
1422
|
const name = (tool.name || '').toLowerCase();
|
|
1401
|
-
if (
|
|
1402
|
-
continue; // CC-native / MCP (identity in pass 1) or mapped
|
|
1423
|
+
if (CC_NATIVE_NAMES_UNION.has(tool.name) || isMcpToolName(tool.name) || TOOL_MAP[name])
|
|
1424
|
+
continue; // CC-native (union) / MCP (identity in pass 1) or mapped
|
|
1403
1425
|
unmappedTools.push(tool.name);
|
|
1404
1426
|
if (opts.hybridTools)
|
|
1405
1427
|
continue; // dropped — see comment above
|
|
@@ -1592,7 +1614,11 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1592
1614
|
const clientToolNames = new Set(clientTools
|
|
1593
1615
|
.map((t) => t.name?.toLowerCase())
|
|
1594
1616
|
.filter((n) => Boolean(n)));
|
|
1595
|
-
|
|
1617
|
+
// Intersect against the UNION, not the host-filtered set: the client's
|
|
1618
|
+
// declaration encodes the client's platform, so a win32 CC declaring
|
|
1619
|
+
// PowerShell/Glob/Grep gets their canonical defs even from a Linux-hosted
|
|
1620
|
+
// dario. The host filter only governs the no-declaration fallbacks.
|
|
1621
|
+
const availableCC = CC_TOOL_DEFINITIONS_UNION.filter((t) => clientToolNames.has(t.name.toLowerCase()));
|
|
1596
1622
|
const mcpTools = clientTools.filter((t) => isMcpToolName(t.name));
|
|
1597
1623
|
ccRequest.tools = availableCC.length > 0 || mcpTools.length > 0
|
|
1598
1624
|
? [...availableCC, ...mcpTools]
|
package/dist/notify.d.ts
CHANGED
|
@@ -22,6 +22,22 @@
|
|
|
22
22
|
*
|
|
23
23
|
* See dario#288 — overage-guard.
|
|
24
24
|
*/
|
|
25
|
+
/**
|
|
26
|
+
* Spawn a detached, output-ignored child and swallow EVERY failure mode.
|
|
27
|
+
*
|
|
28
|
+
* The subtle one: a missing binary does NOT throw from `spawn()` — it
|
|
29
|
+
* surfaces as an ASYNC `'error'` event on the ChildProcess, and with no
|
|
30
|
+
* handler attached that's an uncaught exception that kills the whole
|
|
31
|
+
* process. Seen live 2026-07-05 (#672): `spawn notify-send` ENOENT inside
|
|
32
|
+
* the Docker image crashed the proxy on every overage-guard event — the
|
|
33
|
+
* guard fired, notify killed the process, docker restarted it, ~15s of
|
|
34
|
+
* refused connections per event. The existing try/catch only covered the
|
|
35
|
+
* sync-throw path (EMFILE etc.), which is also kept here.
|
|
36
|
+
*
|
|
37
|
+
* Exported for test/notify.mjs, which spawns a guaranteed-nonexistent
|
|
38
|
+
* binary and asserts the process survives the async error event.
|
|
39
|
+
*/
|
|
40
|
+
export declare function spawnFireAndForget(cmd: string, args: string[]): void;
|
|
25
41
|
/**
|
|
26
42
|
* Fire a native notification. Returns immediately — the underlying spawn
|
|
27
43
|
* is fire-and-forget. Errors (missing binary, permission denied, no
|
package/dist/notify.js
CHANGED
|
@@ -24,6 +24,32 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import { spawn } from 'node:child_process';
|
|
26
26
|
import { platform } from 'node:os';
|
|
27
|
+
/**
|
|
28
|
+
* Spawn a detached, output-ignored child and swallow EVERY failure mode.
|
|
29
|
+
*
|
|
30
|
+
* The subtle one: a missing binary does NOT throw from `spawn()` — it
|
|
31
|
+
* surfaces as an ASYNC `'error'` event on the ChildProcess, and with no
|
|
32
|
+
* handler attached that's an uncaught exception that kills the whole
|
|
33
|
+
* process. Seen live 2026-07-05 (#672): `spawn notify-send` ENOENT inside
|
|
34
|
+
* the Docker image crashed the proxy on every overage-guard event — the
|
|
35
|
+
* guard fired, notify killed the process, docker restarted it, ~15s of
|
|
36
|
+
* refused connections per event. The existing try/catch only covered the
|
|
37
|
+
* sync-throw path (EMFILE etc.), which is also kept here.
|
|
38
|
+
*
|
|
39
|
+
* Exported for test/notify.mjs, which spawns a guaranteed-nonexistent
|
|
40
|
+
* binary and asserts the process survives the async error event.
|
|
41
|
+
*/
|
|
42
|
+
export function spawnFireAndForget(cmd, args) {
|
|
43
|
+
try {
|
|
44
|
+
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
45
|
+
child.on('error', () => { });
|
|
46
|
+
child.unref();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Sync throw path (EMFILE and friends); silent — same contract as
|
|
50
|
+
// the notification itself: best-effort, never load-bearing.
|
|
51
|
+
}
|
|
52
|
+
}
|
|
27
53
|
/**
|
|
28
54
|
* Fire a native notification. Returns immediately — the underlying spawn
|
|
29
55
|
* is fire-and-forget. Errors (missing binary, permission denied, no
|
|
@@ -47,46 +73,38 @@ export function notify(title, message) {
|
|
|
47
73
|
const safeTitle = sanitize(title);
|
|
48
74
|
const safeMessage = sanitize(message);
|
|
49
75
|
const plat = platform();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
], { detached: true, stdio: 'ignore' }).unref();
|
|
60
|
-
}
|
|
61
|
-
else if (plat === 'linux') {
|
|
62
|
-
spawn('notify-send', [safeTitle, safeMessage], {
|
|
63
|
-
detached: true,
|
|
64
|
-
stdio: 'ignore',
|
|
65
|
-
}).unref();
|
|
66
|
-
}
|
|
67
|
-
else if (plat === 'win32') {
|
|
68
|
-
// BurntToast is the cleanest path — single-line PowerShell, real
|
|
69
|
-
// Windows toast notification. If BurntToast isn't installed the
|
|
70
|
-
// command fails silently (stdio: ignore swallows the error
|
|
71
|
-
// output), which is the desired behavior.
|
|
72
|
-
//
|
|
73
|
-
// We don't probe-then-spawn; the cost of one failed BurntToast
|
|
74
|
-
// attempt is the same as one probe attempt, and probing makes the
|
|
75
|
-
// hot path slower for the success case.
|
|
76
|
-
const ps = `try { Import-Module BurntToast -ErrorAction Stop; New-BurntToastNotification -Text '${safeTitle}', '${safeMessage}' } catch { exit 1 }`;
|
|
77
|
-
spawn('powershell.exe', [
|
|
78
|
-
'-NoProfile',
|
|
79
|
-
'-NonInteractive',
|
|
80
|
-
'-Command',
|
|
81
|
-
ps,
|
|
82
|
-
], { detached: true, stdio: 'ignore' }).unref();
|
|
83
|
-
}
|
|
84
|
-
// freebsd / openbsd / aix / sunos / android — BEL only. There's no
|
|
85
|
-
// single "right" native notification on these platforms.
|
|
76
|
+
if (plat === 'darwin') {
|
|
77
|
+
// AppleScript single-quote inside the script body would need
|
|
78
|
+
// escaping; sanitize() strips them so the literal substitution
|
|
79
|
+
// below stays safe. Spawned via array argv to avoid a shell
|
|
80
|
+
// entirely.
|
|
81
|
+
spawnFireAndForget('osascript', [
|
|
82
|
+
'-e',
|
|
83
|
+
`display notification "${safeMessage}" with title "${safeTitle}"`,
|
|
84
|
+
]);
|
|
86
85
|
}
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
else if (plat === 'linux') {
|
|
87
|
+
spawnFireAndForget('notify-send', [safeTitle, safeMessage]);
|
|
88
|
+
}
|
|
89
|
+
else if (plat === 'win32') {
|
|
90
|
+
// BurntToast is the cleanest path — single-line PowerShell, real
|
|
91
|
+
// Windows toast notification. If BurntToast isn't installed the
|
|
92
|
+
// command fails silently (stdio: ignore swallows the error
|
|
93
|
+
// output), which is the desired behavior.
|
|
94
|
+
//
|
|
95
|
+
// We don't probe-then-spawn; the cost of one failed BurntToast
|
|
96
|
+
// attempt is the same as one probe attempt, and probing makes the
|
|
97
|
+
// hot path slower for the success case.
|
|
98
|
+
const ps = `try { Import-Module BurntToast -ErrorAction Stop; New-BurntToastNotification -Text '${safeTitle}', '${safeMessage}' } catch { exit 1 }`;
|
|
99
|
+
spawnFireAndForget('powershell.exe', [
|
|
100
|
+
'-NoProfile',
|
|
101
|
+
'-NonInteractive',
|
|
102
|
+
'-Command',
|
|
103
|
+
ps,
|
|
104
|
+
]);
|
|
89
105
|
}
|
|
106
|
+
// freebsd / openbsd / aix / sunos / android — BEL only. There's no
|
|
107
|
+
// single "right" native notification on these platforms.
|
|
90
108
|
}
|
|
91
109
|
/**
|
|
92
110
|
* Strip characters that would break the embedded shell/AppleScript
|
package/dist/proxy.js
CHANGED
|
@@ -13,7 +13,7 @@ import { buildCCRequest, applyCcPromptCaching, parseEffortSuffix, reverseMapResp
|
|
|
13
13
|
import { stampCch, hasCchSeed } from './cch.js';
|
|
14
14
|
import { describeTemplate, detectDrift, checkCCCompat } from './live-fingerprint.js';
|
|
15
15
|
import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs, reconcilePoolAccounts } from './pool.js';
|
|
16
|
-
import { Analytics, billingBucketFromClaim } from './analytics.js';
|
|
16
|
+
import { Analytics, billingBucketFromClaim, SUBSCRIPTION_CLAIMS } from './analytics.js';
|
|
17
17
|
import { OverageGuard, buildHaltErrorBody } from './overage-guard.js';
|
|
18
18
|
import { notify as osNotify } from './notify.js';
|
|
19
19
|
import { loadAllAccounts, loadAccount, refreshAccountToken, resyncLoginFromCredentialsIfStale, ensureLoginCredentialsInPool } from './accounts.js';
|
|
@@ -2897,10 +2897,10 @@ export async function startProxy(opts = {}) {
|
|
|
2897
2897
|
if (overageUtil !== null) {
|
|
2898
2898
|
overagePct = `${Math.round(parseFloat(overageUtil) * 100)}%`;
|
|
2899
2899
|
}
|
|
2900
|
-
else if (billingClaim
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2900
|
+
else if (billingClaim && SUBSCRIPTION_CLAIMS.has(billingClaim)) {
|
|
2901
|
+
// Any subscription-side claim (incl. *_overage_included) with no
|
|
2902
|
+
// overage-utilization header means $0 extra usage — same sync
|
|
2903
|
+
// source as the guard so this list can't drift again.
|
|
2904
2904
|
overagePct = '0%';
|
|
2905
2905
|
}
|
|
2906
2906
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.137",
|
|
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": {
|