@openlimiter/connectors 2.0.3 → 2.1.0

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.
@@ -1,83 +1,19 @@
1
1
  import type { ConnectionTool, ConnectorContract, RawMeter } from "@openlimiter/core";
2
- /**
3
- * The Antigravity quota reader.
4
- *
5
- * The shape below is the one a working reader observed against a real Google AI
6
- * Pro account on 2026-08-07, recorded in
7
- * `Product Idea/reference-implementation`. Design evidence, not verification
8
- * evidence: Google publishes nothing about this surface, so the provider stays
9
- * UNVERIFIED whatever we observe.
10
- *
11
- * The response is not a percentage in a field. It is a list of GROUPS, each
12
- * holding a list of BUCKETS, each bucket carrying `remainingFraction` between
13
- * zero and one, a window name, and an RFC3339 reset. Usage is one minus that
14
- * fraction. The reader used to parse `quota.used_percent`, a shape from an
15
- * early prototype that has never been seen on the wire.
16
- */
2
+ /** Antigravity documents this payload as the standard input to its status line. */
17
3
  export declare const antigravityLabels: {
18
4
  readonly credentialOrigin: "official-local-tool";
19
- readonly dataInterfaceStatus: "internal-endpoint";
20
- readonly automationRisk: "high";
5
+ readonly dataInterfaceStatus: "native-statusline-payload";
6
+ readonly automationRisk: "low";
21
7
  readonly verification: "UNVERIFIED";
22
8
  };
23
9
  export declare const antigravityInput: {
24
- readonly kind: "provider_managed_payload";
25
- readonly pathTemplate: "{providerState}/quota.json";
10
+ readonly kind: "native_statusline_payload";
11
+ readonly pathTemplate: "standard input";
26
12
  readonly readMode: "read_only";
27
13
  };
28
- /** What this reader reads. JSON, because the endpoint answers JSON. */
29
14
  export declare const antigravityEncoding: "json";
30
- /**
31
- * The bucket prefix naming the pool this provider tracks.
32
- *
33
- * Matched against the prefix of `bucketId` EXACTLY, never as a substring. A
34
- * hypothetical `3p-gemini-fallback` bucket would satisfy a naive containment
35
- * test and let a third party pool overwrite the real one, which is a wrong
36
- * number rather than a missing one.
37
- */
38
- export declare const ANTIGRAVITY_TRACKED_POOL = "gemini";
39
- /**
40
- * The window names this build understands, and how long each one is.
41
- *
42
- * Google spells them "5h" and "weekly". A window name outside this table is not
43
- * dropped: the whole payload is refused. Dropping it would remove a window that
44
- * might be the binding one and leave the meter confidently reporting a LOWER
45
- * number than the truth, which is the exact failure this product exists to
46
- * prevent.
47
- */
48
- export declare const ANTIGRAVITY_WINDOWS: Readonly<Record<string, number>>;
49
- /**
50
- * Every documented window in the tracked pool, as its own meter.
51
- *
52
- * The shell may still derive one binding percentage for advice and the tray,
53
- * but the dashboard must not discard the other window to get there.
54
- *
55
- * The third party pool that rides the same subscription is deliberately not
56
- * modelled. It is a real second wallet, and reporting it under this provider's
57
- * single meter would conflate two pools into one number. It needs its own
58
- * identity in the registry before it can have one here.
59
- */
60
- export declare function parseAntigravityPayload(payload: unknown, now: string): RawMeter[] | null;
61
- /**
62
- * The same Code Assist quota document, read under this provider's name.
63
- *
64
- * Antigravity and Gemini CLI sit on one Google Code Assist backend and answer
65
- * the same per model quota shape, so `parseGeminiCliPayload` above already
66
- * knows how to read it. What differs is WHOSE credential asked and therefore
67
- * which row a person is looking at, and that is a provider code, not a parser.
68
- *
69
- * The rename is exact rather than approximate: the two label sets are field for
70
- * field identical, so nothing about the reading's honesty changes when the
71
- * provider does. Duplicating the bucket reader to achieve the same thing would
72
- * mean two parsers to keep in step with one backend, which is how one of them
73
- * ends up silently behind.
74
- *
75
- * This is the shape the network path reads. The group and bucket summary the
76
- * `parseAntigravityPayload` reader above handles is a different document from
77
- * the same family and stays exactly as it was.
78
- */
79
- export declare function parseAntigravityCodeAssistPayload(payload: unknown, now: string): RawMeter[] | null;
80
- /** The local application that owns this credential. */
15
+ /** Parse only the documented quota map. Unknown bucket identities are omitted. */
16
+ export declare function parseAntigravityPayload(payload: unknown, observedAt: string): RawMeter[] | null;
81
17
  export declare const ANTIGRAVITY_TOOL: ConnectionTool;
82
18
  export declare const antigravityConnector: ConnectorContract;
83
19
  //# sourceMappingURL=antigravity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"antigravity.d.ts","sourceRoot":"","sources":["../src/antigravity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAGjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAY3B;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,iBAAiB;;;;;CAKM,CAAC;AAErC,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,EAAG,MAAe,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,WAAW,CAAC;AAEjD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAGhE,CAAC;AA6DF;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,GACV,QAAQ,EAAE,GAAG,IAAI,CAkCnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,GACV,QAAQ,EAAE,GAAG,IAAI,CAQnB;AAED,uDAAuD;AACvD,eAAO,MAAM,gBAAgB,EAAE,cAA8B,CAAC;AAE9D,eAAO,MAAM,oBAAoB,EAAE,iBAmBlC,CAAC"}
1
+ {"version":3,"file":"antigravity.d.ts","sourceRoot":"","sources":["../src/antigravity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAGjB,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAI3B,mFAAmF;AACnF,eAAO,MAAM,iBAAiB;;;;;CAKM,CAAC;AAErC,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAG,MAAe,CAAC;AAuBnD,kFAAkF;AAClF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CA0B/F;AAED,eAAO,MAAM,gBAAgB,EAAE,cAA8B,CAAC;AAE9D,eAAO,MAAM,oBAAoB,EAAE,iBAelC,CAAC"}
@@ -1,201 +1,75 @@
1
- import { parseGeminiCliPayload } from "./gemini-cli.js";
2
- import { boundedFraction, connectorConnection, futureInstantFromRfc3339, plausibleResetHorizon, rawMeter, record, shortExpiry } from "./shared.js";
3
- /**
4
- * The Antigravity quota reader.
5
- *
6
- * The shape below is the one a working reader observed against a real Google AI
7
- * Pro account on 2026-08-07, recorded in
8
- * `Product Idea/reference-implementation`. Design evidence, not verification
9
- * evidence: Google publishes nothing about this surface, so the provider stays
10
- * UNVERIFIED whatever we observe.
11
- *
12
- * The response is not a percentage in a field. It is a list of GROUPS, each
13
- * holding a list of BUCKETS, each bucket carrying `remainingFraction` between
14
- * zero and one, a window name, and an RFC3339 reset. Usage is one minus that
15
- * fraction. The reader used to parse `quota.used_percent`, a shape from an
16
- * early prototype that has never been seen on the wire.
17
- */
1
+ import { antigravityMeter } from "@openlimiter/core";
2
+ import { connectorConnection, rawMeter, record, shortExpiry } from "./shared.js";
3
+ /** Antigravity documents this payload as the standard input to its status line. */
18
4
  export const antigravityLabels = {
19
5
  credentialOrigin: "official-local-tool",
20
- dataInterfaceStatus: "internal-endpoint",
21
- automationRisk: "high",
6
+ dataInterfaceStatus: "native-statusline-payload",
7
+ automationRisk: "low",
22
8
  verification: "UNVERIFIED"
23
9
  };
24
10
  export const antigravityInput = {
25
- kind: "provider_managed_payload",
26
- pathTemplate: "{providerState}/quota.json",
11
+ kind: "native_statusline_payload",
12
+ pathTemplate: "standard input",
27
13
  readMode: "read_only"
28
14
  };
29
- /** What this reader reads. JSON, because the endpoint answers JSON. */
30
15
  export const antigravityEncoding = "json";
31
- /**
32
- * The bucket prefix naming the pool this provider tracks.
33
- *
34
- * Matched against the prefix of `bucketId` EXACTLY, never as a substring. A
35
- * hypothetical `3p-gemini-fallback` bucket would satisfy a naive containment
36
- * test and let a third party pool overwrite the real one, which is a wrong
37
- * number rather than a missing one.
38
- */
39
- export const ANTIGRAVITY_TRACKED_POOL = "gemini";
40
- /**
41
- * The window names this build understands, and how long each one is.
42
- *
43
- * Google spells them "5h" and "weekly". A window name outside this table is not
44
- * dropped: the whole payload is refused. Dropping it would remove a window that
45
- * might be the binding one and leave the meter confidently reporting a LOWER
46
- * number than the truth, which is the exact failure this product exists to
47
- * prevent.
48
- */
49
- export const ANTIGRAVITY_WINDOWS = {
50
- "5h": 18_000,
51
- weekly: 604_800
52
- };
53
- /**
54
- * Every bucket of one group, or null if any one of them cannot be believed.
55
- *
56
- * Whole rather than per bucket, for the reason above: a partially read group is
57
- * a group whose worst window may be the one that failed to read.
58
- */
59
- function readBuckets(buckets, now) {
60
- const windows = [];
61
- for (const entry of buckets) {
62
- const bucket = record(entry);
63
- if (bucket === null)
64
- return null;
65
- const fraction = boundedFraction(bucket["remainingFraction"]);
66
- if (fraction === null)
67
- return null;
68
- const name = bucket["window"];
69
- if (typeof name !== "string")
70
- return null;
71
- const seconds = ANTIGRAVITY_WINDOWS[name.toLowerCase()];
72
- if (seconds === undefined)
73
- return null;
74
- const resetAt = futureInstantFromRfc3339(bucket["resetTime"], now, plausibleResetHorizon(seconds));
75
- if (resetAt === null)
76
- return null;
77
- /* One decimal, which is the precision the reference reader keeps and more
78
- than a quota bar can show. Clamped, because floating point subtraction
79
- of a fraction can land a hair outside the range. */
80
- const percent = Math.round(Math.max(0, Math.min(100, (1 - fraction) * 100)) * 10) / 10;
81
- windows.push({
82
- meter: name.toLowerCase() === "5h" ? "FIVE_HOUR" : "SEVEN_DAY",
83
- percent,
84
- seconds,
85
- resetAt
86
- });
87
- }
88
- return windows.length === 0 ? null : windows;
16
+ function remainingFraction(bucket) {
17
+ const value = typeof bucket["remaining_fraction"] === "number"
18
+ ? bucket["remaining_fraction"]
19
+ : typeof bucket["remainingFraction"] === "number"
20
+ ? bucket["remainingFraction"]
21
+ : null;
22
+ return value !== null && Number.isFinite(value) && value >= 0 && value <= 1
23
+ ? value
24
+ : null;
89
25
  }
90
- /** The prefix of every bucket id in a group, or null if any is unreadable. */
91
- function poolPrefixes(buckets) {
92
- const prefixes = new Set();
93
- for (const entry of buckets) {
94
- const bucket = record(entry);
95
- if (bucket === null)
96
- return null;
97
- const id = bucket["bucketId"];
98
- if (typeof id !== "string")
99
- return null;
100
- const separator = id.indexOf("-");
101
- if (separator <= 0)
102
- return null;
103
- prefixes.add(id.slice(0, separator));
104
- }
105
- return prefixes;
26
+ function resetInstant(bucket) {
27
+ const value = typeof bucket["reset_time"] === "string"
28
+ ? bucket["reset_time"]
29
+ : typeof bucket["resetTime"] === "string"
30
+ ? bucket["resetTime"]
31
+ : null;
32
+ if (value === null || !Number.isFinite(Date.parse(value)))
33
+ return null;
34
+ return new Date(value).toISOString();
106
35
  }
107
- /**
108
- * Every documented window in the tracked pool, as its own meter.
109
- *
110
- * The shell may still derive one binding percentage for advice and the tray,
111
- * but the dashboard must not discard the other window to get there.
112
- *
113
- * The third party pool that rides the same subscription is deliberately not
114
- * modelled. It is a real second wallet, and reporting it under this provider's
115
- * single meter would conflate two pools into one number. It needs its own
116
- * identity in the registry before it can have one here.
117
- */
118
- export function parseAntigravityPayload(payload, now) {
36
+ /** Parse only the documented quota map. Unknown bucket identities are omitted. */
37
+ export function parseAntigravityPayload(payload, observedAt) {
119
38
  const root = record(payload);
120
- const groups = root?.["groups"];
121
- if (!Array.isArray(groups))
39
+ const quota = record(root?.["quota"]);
40
+ const expiresAt = shortExpiry(observedAt);
41
+ if (quota === null || expiresAt === null)
122
42
  return null;
123
- let tracked = null;
124
- for (const entry of groups) {
125
- const group = record(entry);
126
- if (group === null)
127
- return null;
128
- const buckets = group["buckets"];
129
- if (!Array.isArray(buckets))
130
- return null;
131
- const prefixes = poolPrefixes(buckets);
132
- if (prefixes === null)
133
- return null;
134
- if (!prefixes.has(ANTIGRAVITY_TRACKED_POOL))
43
+ const meters = [];
44
+ for (const [bucketId, value] of Object.entries(quota)) {
45
+ const bucket = record(value);
46
+ const remaining = bucket === null ? null : remainingFraction(bucket);
47
+ const mapped = antigravityMeter(bucketId.toLowerCase());
48
+ if (bucket === null || remaining === null || mapped === null)
135
49
  continue;
136
- /* Two groups claiming the same pool is ambiguous, and picking one would be
137
- picking at random. */
138
- if (tracked !== null)
139
- return null;
140
- tracked = readBuckets(buckets, now);
141
- if (tracked === null)
142
- return null;
50
+ meters.push(rawMeter({
51
+ provider: "ANTIGRAVITY",
52
+ meter: mapped.meter,
53
+ value: Math.round((1 - remaining) * 1_000) / 10,
54
+ window: { kind: "rolling", durationSeconds: mapped.durationSeconds },
55
+ resetAt: resetInstant(bucket),
56
+ source: "native_payload",
57
+ precision: "estimated",
58
+ observedAt,
59
+ expiresAt,
60
+ labels: antigravityLabels
61
+ }));
143
62
  }
144
- if (tracked === null)
145
- return null;
146
- const expiresAt = shortExpiry(now);
147
- if (expiresAt === null)
148
- return null;
149
- return tracked.map((candidate) => rawMeter({
150
- provider: "ANTIGRAVITY",
151
- meter: candidate.meter,
152
- value: candidate.percent,
153
- window: { kind: "rolling", durationSeconds: candidate.seconds },
154
- resetAt: candidate.resetAt,
155
- source: "internal_payload",
156
- precision: "estimated",
157
- observedAt: now,
158
- expiresAt,
159
- labels: antigravityLabels
160
- }));
161
- }
162
- /**
163
- * The same Code Assist quota document, read under this provider's name.
164
- *
165
- * Antigravity and Gemini CLI sit on one Google Code Assist backend and answer
166
- * the same per model quota shape, so `parseGeminiCliPayload` above already
167
- * knows how to read it. What differs is WHOSE credential asked and therefore
168
- * which row a person is looking at, and that is a provider code, not a parser.
169
- *
170
- * The rename is exact rather than approximate: the two label sets are field for
171
- * field identical, so nothing about the reading's honesty changes when the
172
- * provider does. Duplicating the bucket reader to achieve the same thing would
173
- * mean two parsers to keep in step with one backend, which is how one of them
174
- * ends up silently behind.
175
- *
176
- * This is the shape the network path reads. The group and bucket summary the
177
- * `parseAntigravityPayload` reader above handles is a different document from
178
- * the same family and stays exactly as it was.
179
- */
180
- export function parseAntigravityCodeAssistPayload(payload, now) {
181
- const meters = parseGeminiCliPayload(payload, now);
182
- if (meters === null)
183
- return null;
184
- return meters.map((meter) => ({
185
- ...meter,
186
- provider: "ANTIGRAVITY",
187
- labels: antigravityLabels
188
- }));
63
+ return meters.length === 0 ? null : meters;
189
64
  }
190
- /** The local application that owns this credential. */
191
65
  export const ANTIGRAVITY_TOOL = "Antigravity";
192
66
  export const antigravityConnector = {
193
67
  id: "antigravity",
194
68
  displayName: "Antigravity",
195
- encoding: "json",
69
+ encoding: antigravityEncoding,
196
70
  labels: antigravityLabels,
197
71
  detect(environment) {
198
- return environment["ANTIGRAVITY_USAGE_PAYLOAD"] === "1";
72
+ return environment["ANTIGRAVITY_STATUSLINE_PAYLOAD"] === "1";
199
73
  },
200
74
  async read(context) {
201
75
  const meters = parseAntigravityPayload(context.payload, context.now);
@@ -1 +1 @@
1
- {"version":3,"file":"antigravity.js","sourceRoot":"","sources":["../src/antigravity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"antigravity.js","sourceRoot":"","sources":["../src/antigravity.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAGjB,QAAQ,EAIT,MAAM,mBAAmB,CAAC;AAY3B,eAAO,MAAM,YAAY;;;;;CAKW,CAAC;AAErC,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAKX;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,UAAY,CAAC;AAgCvD,sEAAsE;AACtE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAkY9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmCnF;AAED,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,cAA8B,CAAC;AAEzD,eAAO,MAAM,eAAe,EAAE,iBAmB7B,CAAC"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAGjB,QAAQ,EAIT,MAAM,mBAAmB,CAAC;AAa3B,eAAO,MAAM,YAAY;;;;;CAKW,CAAC;AAErC,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAMX;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,UAAY,CAAC;AA0BvD,sEAAsE;AACtE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,KAAK,CAAC;AA4X9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,CAmCnF;AAED,uDAAuD;AACvD,eAAO,MAAM,WAAW,EAAE,cAA8B,CAAC;AAEzD,eAAO,MAAM,eAAe,EAAE,iBAmB7B,CAAC"}
package/dist/claude.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { boundedNumber, connectorConnection, futureInstantFromEpochSeconds, futureInstantFromRfc3339, plausibleResetHorizon, rawMeter, record, shortExpiry } from "./shared.js";
2
+ import { CLOCK_SKEW_SECONDS } from "./shared.js";
2
3
  export const claudeLabels = {
3
4
  credentialOrigin: "official-local-tool",
4
5
  dataInterfaceStatus: "native-statusline-payload",
@@ -12,6 +13,7 @@ export const claudeInput = {
12
13
  };
13
14
  const FIVE_HOURS = 18_000;
14
15
  const SEVEN_DAYS = 604_800;
16
+ const CLAUDE_WEEKLY_FAMILIES = ["fable", "opus", "sonnet", "haiku"];
15
17
  /**
16
18
  * The longest a window this reader was never told the length of may run.
17
19
  *
@@ -24,18 +26,13 @@ export const CLAUDE_UNKNOWN_WINDOW_SECONDS = 2_678_400;
24
26
  /**
25
27
  * The buckets whose names this build knows, and how long each one runs.
26
28
  *
27
- * Being on this list buys one thing: a fixed meter code that never moves, so a
28
- * surface can style the weekly Opus bar and keep styling it. It is NOT what
29
- * makes a bucket readable. Everything else in the table is read too, under a
30
- * code derived from its own key, because the failure this parser is fixing was
31
- * a frozen two entry table silently dropping every bucket Anthropic added
32
- * after it was written.
29
+ * Only the root keys Anthropic's Usage screen exposes become meters. Unknown
30
+ * weekly names are deliberately ignored, because a provider supplied name is
31
+ * not evidence that it belongs to a user facing allowance.
33
32
  */
34
33
  const KNOWN_WINDOWS = {
35
34
  five_hour: { meter: "FIVE_HOUR", durationSeconds: FIVE_HOURS },
36
35
  seven_day: { meter: "SEVEN_DAY", durationSeconds: SEVEN_DAYS },
37
- seven_day_opus: { meter: "SEVEN_DAY_OPUS", durationSeconds: SEVEN_DAYS },
38
- seven_day_sonnet: { meter: "SEVEN_DAY_SONNET", durationSeconds: SEVEN_DAYS },
39
36
  seven_day_oauth_apps: {
40
37
  meter: "SEVEN_DAY_OAUTH_APPS",
41
38
  durationSeconds: SEVEN_DAYS
@@ -43,7 +40,6 @@ const KNOWN_WINDOWS = {
43
40
  };
44
41
  /** Keys that carry something other than one window, so never read as one. */
45
42
  const RESERVED_KEYS = new Set([
46
- "model_scoped",
47
43
  "limits",
48
44
  "extra_usage"
49
45
  ]);
@@ -73,7 +69,19 @@ function meterCode(value) {
73
69
  .toUpperCase()
74
70
  .replace(/[^A-Z0-9]+/gu, "_")
75
71
  .replace(/^_+|_+$/gu, "");
76
- return /^[A-Z][A-Z0-9_]{0,47}$/u.test(code) ? code : null;
72
+ return /^[A-Z][A-Z0-9_]{0,31}$/u.test(code) ? code : null;
73
+ }
74
+ function claudeWeeklyKey(key) {
75
+ if (key === "seven_day" || key === "seven_day_oauth_apps")
76
+ return true;
77
+ return /^seven_day_(fable|opus|sonnet|haiku)(?:[_-][a-z0-9]+)*$/iu.test(key);
78
+ }
79
+ function claudeFamilyCode(value) {
80
+ const code = meterCode(value);
81
+ if (code === null)
82
+ return null;
83
+ const family = CLAUDE_WEEKLY_FAMILIES.find((candidate) => code === candidate.toUpperCase() || code.startsWith(candidate.toUpperCase() + "_"));
84
+ return family === undefined ? null : code;
77
85
  }
78
86
  /**
79
87
  * The share of a window consumed, however this document happens to state it.
@@ -148,9 +156,10 @@ function resetInstant(value, now, maxAheadSeconds) {
148
156
  * and an honestly unknown window rather than an invented five hours.
149
157
  */
150
158
  function durationForKey(key) {
151
- if (key.startsWith("seven_day"))
159
+ const normalized = key.toLowerCase();
160
+ if (claudeWeeklyKey(normalized))
152
161
  return SEVEN_DAYS;
153
- if (key.startsWith("five_hour"))
162
+ if (normalized.startsWith("five_hour"))
154
163
  return FIVE_HOURS;
155
164
  return null;
156
165
  }
@@ -186,7 +195,9 @@ function parseWindow(value, now, meter, durationSeconds) {
186
195
  if (input === null)
187
196
  return null;
188
197
  const percent = statedPercent(input);
189
- const resetAt = resetInstant(input["resets_at"], now, plausibleResetHorizon(durationSeconds ?? CLAUDE_UNKNOWN_WINDOW_SECONDS));
198
+ const resetAt = resetInstant(input["resets_at"], now, durationSeconds === SEVEN_DAYS
199
+ ? SEVEN_DAYS + CLOCK_SKEW_SECONDS
200
+ : plausibleResetHorizon(durationSeconds ?? CLAUDE_UNKNOWN_WINDOW_SECONDS));
190
201
  if (percent === null || resetAt === null)
191
202
  return null;
192
203
  return { meter, percent, durationSeconds, resetAt };
@@ -208,58 +219,35 @@ function parseWindow(value, now, meter, durationSeconds) {
208
219
  * could produce two different meter lists on two consecutive reads.
209
220
  */
210
221
  function orderedWindowKeys(table) {
211
- const remaining = new Set(Object.keys(table).filter((key) => !RESERVED_KEYS.has(key)));
222
+ const remaining = new Map(Object.keys(table).filter((key) => {
223
+ const normalized = key.toLowerCase();
224
+ return !RESERVED_KEYS.has(normalized) &&
225
+ (normalized === "five_hour" || claudeWeeklyKey(normalized));
226
+ }).map((key) => [key.toLowerCase(), key]));
212
227
  const ordered = [];
213
228
  /* The buckets this build knows lead, shortest window first, which is the
214
229
  order a person reads them in and the order every surface ranks them in. */
215
230
  for (const key of Object.keys(KNOWN_WINDOWS)) {
216
- if (!remaining.delete(key))
231
+ const original = remaining.get(key);
232
+ if (original === undefined)
217
233
  continue;
218
- ordered.push(key);
234
+ remaining.delete(key);
235
+ ordered.push(original);
219
236
  }
220
- return [...ordered, ...[...remaining].sort()];
237
+ return [...ordered, ...[...remaining.entries()]
238
+ .sort(([left], [right]) => left.localeCompare(right))
239
+ .map(([, original]) => original)];
221
240
  }
222
241
  function parseWindowTable(table, now, seen) {
223
242
  const found = [];
224
243
  for (const key of orderedWindowKeys(table)) {
225
244
  const value = table[key];
226
- const known = KNOWN_WINDOWS[key];
245
+ const normalizedKey = key.toLowerCase();
246
+ const known = KNOWN_WINDOWS[normalizedKey];
227
247
  const meter = known?.meter ?? meterCode(key);
228
248
  if (meter === null || seen.has(meter))
229
249
  continue;
230
- const parsed = parseWindow(value, now, meter, known?.durationSeconds ?? durationForKey(key));
231
- if (parsed === null)
232
- continue;
233
- seen.add(meter);
234
- found.push(parsed);
235
- }
236
- return found;
237
- }
238
- /**
239
- * The model scoped weekly buckets the statusline adds beside the root table.
240
- *
241
- * Each entry names a model in words a human reads, so the code is built from
242
- * that name and prefixed with the cadence it belongs to: a Fable 5 entry is
243
- * SEVEN_DAY_FABLE_5. Deduplicated against whatever the root table already
244
- * reported, because Opus arrives twice on a payload that carries both
245
- * `seven_day_opus` and a model scoped Opus entry, and two bars for one pool is
246
- * the same lie as no bar at all.
247
- */
248
- function parseModelScoped(value, now, seen) {
249
- if (!Array.isArray(value))
250
- return [];
251
- const found = [];
252
- for (const entry of value) {
253
- const scoped = record(entry);
254
- if (scoped === null)
255
- continue;
256
- const name = meterCode(scoped["display_name"]);
257
- if (name === null)
258
- continue;
259
- const meter = CLAUDE_MODEL_WEEKLY_PREFIX + name;
260
- if (seen.has(meter))
261
- continue;
262
- const parsed = parseWindow(scoped, now, meter, SEVEN_DAYS);
250
+ const parsed = parseWindow(value, now, meter, known?.durationSeconds ?? durationForKey(normalizedKey));
263
251
  if (parsed === null)
264
252
  continue;
265
253
  seen.add(meter);
@@ -285,7 +273,7 @@ function parseScopedLimits(value, now, seen) {
285
273
  continue;
286
274
  const scope = record(limit["scope"]);
287
275
  const model = record(scope?.["model"]);
288
- const name = meterCode(model?.["display_name"]);
276
+ const name = claudeFamilyCode(model?.["display_name"]);
289
277
  if (name === null)
290
278
  continue;
291
279
  const meter = CLAUDE_MODEL_WEEKLY_PREFIX + name;
@@ -384,11 +372,10 @@ function parseExtraUsage(value, now) {
384
372
  function looksLikeUsageDocument(root) {
385
373
  if (Array.isArray(root["limits"]))
386
374
  return true;
387
- if (Array.isArray(root["model_scoped"]))
388
- return true;
389
375
  if (record(root["extra_usage"]) !== null)
390
376
  return true;
391
- return Object.keys(KNOWN_WINDOWS).some((key) => record(root[key]) !== null);
377
+ return Object.keys(root).some((key) => (key.toLowerCase() === "five_hour" || claudeWeeklyKey(key.toLowerCase())) &&
378
+ record(root[key]) !== null);
392
379
  }
393
380
  /**
394
381
  * Parse a Claude usage document, in either shape the same credential produces.
@@ -469,15 +456,15 @@ export function parseClaudePayload(payload, now) {
469
456
  * is skipped rather than drawn twice.
470
457
  */
471
458
  const seen = new Set();
472
- const inputs = [
473
- ...parseWindowTable(table, now, seen),
474
- ...parseModelScoped(table["model_scoped"], now, seen),
475
- ...parseScopedLimits(table["limits"], now, seen)
476
- ];
477
- const extra = parseExtraUsage(table["extra_usage"], now);
478
- if (extra !== null && !seen.has(extra.meter)) {
479
- seen.add(extra.meter);
480
- inputs.push(extra);
459
+ const inputs = statusline
460
+ ? parseWindowTable({ five_hour: table["five_hour"] }, now, seen)
461
+ : parseWindowTable(table, now, seen).concat(parseScopedLimits(table["limits"], now, seen));
462
+ if (!statusline) {
463
+ const extra = parseExtraUsage(table["extra_usage"], now);
464
+ if (extra !== null && !seen.has(extra.meter)) {
465
+ seen.add(extra.meter);
466
+ inputs.push(extra);
467
+ }
481
468
  }
482
469
  if (inputs.length === 0)
483
470
  return null;