@askalf/dario 6.0.29 → 6.0.30
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/pool.d.ts +35 -0
- package/dist/pool.js +47 -1
- package/dist/proxy.js +3 -3
- package/package.json +1 -1
package/dist/pool.d.ts
CHANGED
|
@@ -118,6 +118,41 @@ export type AccountIneligibility = 'rate-limited' | 'token-expired' | 'auth-cool
|
|
|
118
118
|
* need to name the failure, and a boolean forces each of them to re-derive it
|
|
119
119
|
* and drift again.
|
|
120
120
|
*/
|
|
121
|
+
/**
|
|
122
|
+
* Has the window that produced a `rejected` reading rolled over?
|
|
123
|
+
*
|
|
124
|
+
* A rejection is a verdict with an expiry date: `anthropic-ratelimit-unified-reset`
|
|
125
|
+
* names the moment the window that refused the request resets. Past it, the old
|
|
126
|
+
* reading says nothing about the account any more.
|
|
127
|
+
*
|
|
128
|
+
* This matters because a rejected account is filtered out of `select()`, so it
|
|
129
|
+
* is sent no further requests, so `updateRateLimits` never runs for it and its
|
|
130
|
+
* snapshot never refreshes. The only routes back into rotation were the
|
|
131
|
+
* all-exhausted fallback in `select()` and a proxy restart. Observed on the
|
|
132
|
+
* fleet box (2026-09-06): one seat sat parked on a 106% five-hour reading while
|
|
133
|
+
* a second subscription carried every request, and it would have stayed parked
|
|
134
|
+
* past its own reset for as long as the other seat held out.
|
|
135
|
+
*
|
|
136
|
+
* `reset` is epoch SECONDS — the header's own unit, the same one `formatReset`
|
|
137
|
+
* scales — so it is converted here. A snapshot with no reset (0) keeps its
|
|
138
|
+
* rejection: with no stated rollover there is nothing to expire, and guessing
|
|
139
|
+
* would push a genuinely throttled account back into rotation.
|
|
140
|
+
*/
|
|
141
|
+
export declare function rateLimitWindowPassed(rl: RateLimitSnapshot, now?: number): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* The status string the operator-facing surfaces report for one account —
|
|
144
|
+
* `GET /accounts` and `GET /admin/accounts`, which must agree with each other
|
|
145
|
+
* and with what routing actually does.
|
|
146
|
+
*
|
|
147
|
+
* Auth cool-down outranks the rate-limit reading: a 401 streak is both the more
|
|
148
|
+
* urgent fact and the one the rate-limit headers cannot describe, since 401
|
|
149
|
+
* responses carry none. An expired rejection degrades to `unknown` rather than
|
|
150
|
+
* `allowed` — the window rolled over, but nothing has measured the account
|
|
151
|
+
* since, and reporting `allowed` would assert a serving capacity no request has
|
|
152
|
+
* demonstrated. `unknown` is what a never-used account already reports, which is
|
|
153
|
+
* exactly the state this is: no current observation.
|
|
154
|
+
*/
|
|
155
|
+
export declare function reportedAccountStatus(account: PoolAccount, now?: number): string;
|
|
121
156
|
export declare function accountIneligibility(account: PoolAccount, now?: number): AccountIneligibility | null;
|
|
122
157
|
/** Boolean form of `accountIneligibility` — the router's eligibility filter. */
|
|
123
158
|
export declare function isAccountEligible(account: PoolAccount, now?: number): boolean;
|
package/dist/pool.js
CHANGED
|
@@ -100,8 +100,54 @@ export const TOKEN_EXPIRY_MARGIN_MS = 30_000;
|
|
|
100
100
|
* need to name the failure, and a boolean forces each of them to re-derive it
|
|
101
101
|
* and drift again.
|
|
102
102
|
*/
|
|
103
|
+
/**
|
|
104
|
+
* Has the window that produced a `rejected` reading rolled over?
|
|
105
|
+
*
|
|
106
|
+
* A rejection is a verdict with an expiry date: `anthropic-ratelimit-unified-reset`
|
|
107
|
+
* names the moment the window that refused the request resets. Past it, the old
|
|
108
|
+
* reading says nothing about the account any more.
|
|
109
|
+
*
|
|
110
|
+
* This matters because a rejected account is filtered out of `select()`, so it
|
|
111
|
+
* is sent no further requests, so `updateRateLimits` never runs for it and its
|
|
112
|
+
* snapshot never refreshes. The only routes back into rotation were the
|
|
113
|
+
* all-exhausted fallback in `select()` and a proxy restart. Observed on the
|
|
114
|
+
* fleet box (2026-09-06): one seat sat parked on a 106% five-hour reading while
|
|
115
|
+
* a second subscription carried every request, and it would have stayed parked
|
|
116
|
+
* past its own reset for as long as the other seat held out.
|
|
117
|
+
*
|
|
118
|
+
* `reset` is epoch SECONDS — the header's own unit, the same one `formatReset`
|
|
119
|
+
* scales — so it is converted here. A snapshot with no reset (0) keeps its
|
|
120
|
+
* rejection: with no stated rollover there is nothing to expire, and guessing
|
|
121
|
+
* would push a genuinely throttled account back into rotation.
|
|
122
|
+
*/
|
|
123
|
+
export function rateLimitWindowPassed(rl, now = Date.now()) {
|
|
124
|
+
return rl.reset > 0 && rl.reset * 1000 <= now;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The status string the operator-facing surfaces report for one account —
|
|
128
|
+
* `GET /accounts` and `GET /admin/accounts`, which must agree with each other
|
|
129
|
+
* and with what routing actually does.
|
|
130
|
+
*
|
|
131
|
+
* Auth cool-down outranks the rate-limit reading: a 401 streak is both the more
|
|
132
|
+
* urgent fact and the one the rate-limit headers cannot describe, since 401
|
|
133
|
+
* responses carry none. An expired rejection degrades to `unknown` rather than
|
|
134
|
+
* `allowed` — the window rolled over, but nothing has measured the account
|
|
135
|
+
* since, and reporting `allowed` would assert a serving capacity no request has
|
|
136
|
+
* demonstrated. `unknown` is what a never-used account already reports, which is
|
|
137
|
+
* exactly the state this is: no current observation.
|
|
138
|
+
*/
|
|
139
|
+
export function reportedAccountStatus(account, now = Date.now()) {
|
|
140
|
+
if (isInAuthCooldown(account, now))
|
|
141
|
+
return 'auth-cooldown';
|
|
142
|
+
if (account.rateLimit.status === 'rejected' && rateLimitWindowPassed(account.rateLimit, now))
|
|
143
|
+
return 'unknown';
|
|
144
|
+
return account.rateLimit.status;
|
|
145
|
+
}
|
|
103
146
|
export function accountIneligibility(account, now = Date.now()) {
|
|
104
|
-
|
|
147
|
+
// A rejection outlives its own window unless it is allowed to expire:
|
|
148
|
+
// nothing refreshes a parked account's snapshot, because being parked is
|
|
149
|
+
// what stops it being sent requests.
|
|
150
|
+
if (account.rateLimit.status === 'rejected' && !rateLimitWindowPassed(account.rateLimit, now))
|
|
105
151
|
return 'rate-limited';
|
|
106
152
|
if (account.expiresAt <= now + TOKEN_EXPIRY_MARGIN_MS)
|
|
107
153
|
return 'token-expired';
|
package/dist/proxy.js
CHANGED
|
@@ -12,7 +12,7 @@ import { darioVersion } from './version.js';
|
|
|
12
12
|
import { buildCCRequest, applyCcPromptCaching, isGenuineCCClient, parseEffortSuffix, reverseMapResponse, createStreamingReverseMapper, orderHeadersForOutbound, overlayTemplateHeaderValues, forwardClientCCIdentityHeaders, isMcpToolName, CC_TEMPLATE, effectiveCacheControl, withForced1hBeta } from './cc-template.js';
|
|
13
13
|
import { stampCch, hasCchSeed } from './cch.js';
|
|
14
14
|
import { describeTemplate, detectDrift, checkCCCompat, probeInstalledCCVersion } from './live-fingerprint.js';
|
|
15
|
-
import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs, accountIneligibility, reconcilePoolAccounts, resolvePoolStrategy, utilFreshness } from './pool.js';
|
|
15
|
+
import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs, accountIneligibility, reportedAccountStatus, reconcilePoolAccounts, resolvePoolStrategy, utilFreshness } from './pool.js';
|
|
16
16
|
import { Analytics, billingBucketFromClaim, formatUsageLogLine, SUBSCRIPTION_CLAIMS, CODEX_CLAIM } from './analytics.js';
|
|
17
17
|
import { OverageGuard, buildHaltErrorBody } from './overage-guard.js';
|
|
18
18
|
import { notify as osNotify } from './notify.js';
|
|
@@ -2101,7 +2101,7 @@ export async function startProxy(opts = {}) {
|
|
|
2101
2101
|
// must not be the one place a stale reading still looks current.
|
|
2102
2102
|
...utilFreshness(a.rateLimit, snapNow),
|
|
2103
2103
|
claim: a.rateLimit.claim,
|
|
2104
|
-
status:
|
|
2104
|
+
status: reportedAccountStatus(a, snapNow),
|
|
2105
2105
|
requestCount: a.requestCount,
|
|
2106
2106
|
// Raw streak, not just the cooldown boolean: a single 401 also
|
|
2107
2107
|
// shows `auth-cooldown` for 60s, indistinguishable from a
|
|
@@ -2200,7 +2200,7 @@ export async function startProxy(opts = {}) {
|
|
|
2200
2200
|
util7d: a.rateLimit.util7d,
|
|
2201
2201
|
...utilFreshness(a.rateLimit, now),
|
|
2202
2202
|
claim: a.rateLimit.claim,
|
|
2203
|
-
status:
|
|
2203
|
+
status: reportedAccountStatus(a, now),
|
|
2204
2204
|
requestCount: a.requestCount,
|
|
2205
2205
|
expiresInMs: Math.max(0, a.expiresAt - now),
|
|
2206
2206
|
// Refresh-token grant age (refresh-grant.ts): the wall a token
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.30",
|
|
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": {
|