@askalf/dario 5.5.28 → 5.5.29

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.
@@ -77,6 +77,17 @@ export interface AdminAccountRecord {
77
77
  export interface AdminAccountLive {
78
78
  util5h: number;
79
79
  util7d: number;
80
+ /**
81
+ * When `util5h` / `util7d` were last observed, epoch ms — or `null` if this
82
+ * account has never served a response. The utilisation figures are a
83
+ * snapshot of the last response the account served, not a live gauge: while
84
+ * an account is parked nothing refreshes them, so without a timestamp a
85
+ * consumer cannot tell a current reading from one frozen minutes ago
86
+ * (dario#1032).
87
+ */
88
+ lastObservedAt: number | null;
89
+ /** Age of that reading in ms, or `null` when never observed. */
90
+ utilAgeMs: number | null;
80
91
  claim: string;
81
92
  status: string;
82
93
  requestCount: number;
package/dist/pool.d.ts CHANGED
@@ -40,6 +40,31 @@ export interface RateLimitSnapshot {
40
40
  updatedAt: number;
41
41
  }
42
42
  export declare const EMPTY_SNAPSHOT: RateLimitSnapshot;
43
+ /** Freshness of an account's utilisation reading — see `utilFreshness`. */
44
+ export interface UtilFreshness {
45
+ /** When util5h/util7d were last observed, epoch ms; null if never. */
46
+ lastObservedAt: number | null;
47
+ /** Age of that reading in ms; null if never observed. */
48
+ utilAgeMs: number | null;
49
+ }
50
+ /**
51
+ * Derive how old an account's utilisation reading is (dario#1032).
52
+ *
53
+ * `util5h` / `util7d` are a SNAPSHOT of the last response the account served.
54
+ * They do not tick on their own, and nothing refreshes them while an account is
55
+ * parked (rejected, or in auth cooldown) — so they stay frozen at whatever they
56
+ * read at the moment it was parked. The pool does return parked accounts to
57
+ * service on its own and the value corrects itself when it does, which is what
58
+ * makes this a REPORTING problem rather than a routing one: the payload carried
59
+ * no timestamp, so no consumer could tell a current reading from one frozen
60
+ * minutes ago, and a dashboard rendered "5-hour window full" for an account
61
+ * that had since reset and was free.
62
+ *
63
+ * `updatedAt` was already on the snapshot; it was simply never surfaced. An
64
+ * `updatedAt` of 0 is EMPTY_SNAPSHOT's "never observed", which must report as
65
+ * null rather than as an age of ~56 years since epoch.
66
+ */
67
+ export declare function utilFreshness(rl: RateLimitSnapshot, now: number): UtilFreshness;
43
68
  export interface PoolAccount {
44
69
  alias: string;
45
70
  accessToken: string;
package/dist/pool.js CHANGED
@@ -34,6 +34,30 @@ export const EMPTY_SNAPSHOT = {
34
34
  fallbackPct: 0,
35
35
  updatedAt: 0,
36
36
  };
37
+ /**
38
+ * Derive how old an account's utilisation reading is (dario#1032).
39
+ *
40
+ * `util5h` / `util7d` are a SNAPSHOT of the last response the account served.
41
+ * They do not tick on their own, and nothing refreshes them while an account is
42
+ * parked (rejected, or in auth cooldown) — so they stay frozen at whatever they
43
+ * read at the moment it was parked. The pool does return parked accounts to
44
+ * service on its own and the value corrects itself when it does, which is what
45
+ * makes this a REPORTING problem rather than a routing one: the payload carried
46
+ * no timestamp, so no consumer could tell a current reading from one frozen
47
+ * minutes ago, and a dashboard rendered "5-hour window full" for an account
48
+ * that had since reset and was free.
49
+ *
50
+ * `updatedAt` was already on the snapshot; it was simply never surfaced. An
51
+ * `updatedAt` of 0 is EMPTY_SNAPSHOT's "never observed", which must report as
52
+ * null rather than as an age of ~56 years since epoch.
53
+ */
54
+ export function utilFreshness(rl, now) {
55
+ const lastObservedAt = rl.updatedAt || null;
56
+ return {
57
+ lastObservedAt,
58
+ utilAgeMs: lastObservedAt === null ? null : Math.max(0, now - lastObservedAt),
59
+ };
60
+ }
37
61
  /**
38
62
  * Cool-down schedule after auth failures. First failure: 60s. Each
39
63
  * consecutive failure doubles the window up to 30 minutes. Cleared
package/dist/proxy.js CHANGED
@@ -12,7 +12,7 @@ import { darioVersion } from './version.js';
12
12
  import { buildCCRequest, applyCcPromptCaching, 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, reconcilePoolAccounts, resolvePoolStrategy } from './pool.js';
15
+ import { AccountPool, computeStickyKey, parseRateLimits, modelFamily, isInAuthCooldown, authCooldownMs, reconcilePoolAccounts, resolvePoolStrategy, utilFreshness } from './pool.js';
16
16
  import { Analytics, billingBucketFromClaim, formatUsageLogLine, SUBSCRIPTION_CLAIMS } from './analytics.js';
17
17
  import { OverageGuard, buildHaltErrorBody } from './overage-guard.js';
18
18
  import { notify as osNotify } from './notify.js';
@@ -1834,6 +1834,10 @@ export async function startProxy(opts = {}) {
1834
1834
  snap.set(a.alias, {
1835
1835
  util5h: a.rateLimit.util5h,
1836
1836
  util7d: a.rateLimit.util7d,
1837
+ // Same freshness fields GET /accounts exposes (#1032) — this
1838
+ // surface documents itself as reporting the same snapshot, so it
1839
+ // must not be the one place a stale reading still looks current.
1840
+ ...utilFreshness(a.rateLimit, snapNow),
1837
1841
  claim: a.rateLimit.claim,
1838
1842
  status: isInAuthCooldown(a, snapNow) ? 'auth-cooldown' : a.rateLimit.status,
1839
1843
  requestCount: a.requestCount,
@@ -1913,10 +1917,25 @@ export async function startProxy(opts = {}) {
1913
1917
  const cooldownMs = inCooldown && a.lastAuthFailureAt
1914
1918
  ? Math.max(0, authCooldownMs(a.consecutiveAuthFailures) - (now - a.lastAuthFailureAt))
1915
1919
  : 0;
1920
+ // Freshness of the utilisation reading (#1032). util5h/util7d are a
1921
+ // SNAPSHOT of the last response this account served — they do not tick
1922
+ // on their own. While an account is parked (rejected, or in auth
1923
+ // cooldown) nothing refreshes them, so they stay frozen at whatever
1924
+ // they read at the moment it was parked, and a consumer sees "5-hour
1925
+ // window full" for an account that has since reset and is free.
1926
+ //
1927
+ // The pool does return parked accounts to service on its own and the
1928
+ // value corrects itself the moment it does, so this is a reporting
1929
+ // problem, not a routing one: nothing downstream could tell a current
1930
+ // reading from one frozen minutes ago, because the payload carried no
1931
+ // timestamp at all. `updatedAt` was already on the snapshot; it was
1932
+ // simply never surfaced. null means "never observed" (no response has
1933
+ // been served on this account yet) rather than "observed at epoch 0".
1916
1934
  return {
1917
1935
  alias: a.alias,
1918
1936
  util5h: a.rateLimit.util5h,
1919
1937
  util7d: a.rateLimit.util7d,
1938
+ ...utilFreshness(a.rateLimit, now),
1920
1939
  claim: a.rateLimit.claim,
1921
1940
  status: inCooldown ? 'auth-cooldown' : a.rateLimit.status,
1922
1941
  requestCount: a.requestCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "5.5.28",
3
+ "version": "5.5.29",
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": {