@openlimiter/core 0.2.0 → 1.2.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.
@@ -0,0 +1,393 @@
1
+ /**
2
+ * The connection lifecycle, as one pure function and two human tables.
3
+ *
4
+ * A parser is not a connection. The product's central honesty problem is that
5
+ * an implemented parser has been able to look like a live account, so the state
6
+ * a connection is in has to be a value the whole product agrees on rather than
7
+ * a sentence each surface invents. Everything here is data and arithmetic: no
8
+ * clock, no network, no storage, so a surface can replay a connection's whole
9
+ * history and get the same answer every time.
10
+ */
11
+ export const CONNECTION_STATES = [
12
+ "NOT_CONFIGURED",
13
+ "DETECTED",
14
+ "NEEDS_AUTH",
15
+ "READY_TO_ENABLE",
16
+ "CONNECTING",
17
+ "CONNECTED",
18
+ "DEGRADED",
19
+ "STALE",
20
+ "AUTH_EXPIRED",
21
+ "IMPORT_ONLY",
22
+ "MANUAL",
23
+ "UNSUPPORTED",
24
+ "ERROR"
25
+ ];
26
+ /**
27
+ * How many consecutive network failures turn a degraded connection into a
28
+ * broken one. One timeout is a bad moment; three in a row is a fault worth
29
+ * showing a person.
30
+ */
31
+ export const NETWORK_FAILURE_ERROR_THRESHOLD = 3;
32
+ /**
33
+ * States that describe something the product declared rather than something it
34
+ * attempted. A response cannot arrive in any of them, so a stray one is ignored
35
+ * instead of quietly promoting an import only source to Connected.
36
+ */
37
+ const declaredStates = new Set([
38
+ "NOT_CONFIGURED",
39
+ "IMPORT_ONLY",
40
+ "MANUAL",
41
+ "UNSUPPORTED"
42
+ ]);
43
+ function afterResponse(state, status, parsed, context) {
44
+ if (declaredStates.has(state))
45
+ return state;
46
+ if (status === 401 || status === 403) {
47
+ return context.everConnected ? "AUTH_EXPIRED" : "NEEDS_AUTH";
48
+ }
49
+ if (status === 429 || (status >= 500 && status <= 599))
50
+ return "DEGRADED";
51
+ if (status < 200 || status > 299)
52
+ return "ERROR";
53
+ /*
54
+ * The response arrived and was refused by our own parser. The connection is
55
+ * healthy and our understanding of it is not, which is the exact failure the
56
+ * Claude statusline contract mismatch was, so it reads as an error rather
57
+ * than as a provider problem.
58
+ */
59
+ return parsed ? "CONNECTED" : "ERROR";
60
+ }
61
+ /**
62
+ * Advance a connection by one event.
63
+ *
64
+ * Total and pure: every state, event and context triple has an answer, and an
65
+ * event that makes no sense where it arrived leaves the state exactly as it was
66
+ * rather than inventing a transition. The context is required rather than
67
+ * defaulted, because a caller that forgets it would silently get the wrong
68
+ * authentication answer, and a compiler error is cheaper than that.
69
+ */
70
+ export function nextConnectionState(state, event, context) {
71
+ switch (event.kind) {
72
+ case "disconnected":
73
+ return "NOT_CONFIGURED";
74
+ case "declared_import_only":
75
+ return "IMPORT_ONLY";
76
+ case "declared_manual":
77
+ return "MANUAL";
78
+ case "declared_unsupported":
79
+ return "UNSUPPORTED";
80
+ case "detected":
81
+ return state === "NOT_CONFIGURED" ? "DETECTED" : state;
82
+ case "credential_required":
83
+ return state === "NOT_CONFIGURED" ||
84
+ state === "DETECTED" ||
85
+ state === "READY_TO_ENABLE"
86
+ ? "NEEDS_AUTH"
87
+ : state;
88
+ case "credential_stored":
89
+ return state === "NEEDS_AUTH" ||
90
+ state === "AUTH_EXPIRED" ||
91
+ state === "DETECTED"
92
+ ? "READY_TO_ENABLE"
93
+ : state;
94
+ case "enable_requested":
95
+ /*
96
+ * A connection missing its credential cannot start collecting no matter
97
+ * how many times the button is pressed, so those two states hold.
98
+ */
99
+ return state === "NEEDS_AUTH" ||
100
+ state === "AUTH_EXPIRED" ||
101
+ declaredStates.has(state)
102
+ ? state
103
+ : "CONNECTING";
104
+ case "http_response":
105
+ return afterResponse(state, event.status, event.parsed, context);
106
+ case "local_read":
107
+ if (declaredStates.has(state))
108
+ return state;
109
+ return event.parsed ? "CONNECTED" : "ERROR";
110
+ case "network_failure":
111
+ if (declaredStates.has(state))
112
+ return state;
113
+ return event.consecutive >= NETWORK_FAILURE_ERROR_THRESHOLD
114
+ ? "ERROR"
115
+ : "DEGRADED";
116
+ case "expiry_passed":
117
+ /*
118
+ * Stale means the last reading is too old to trust, which only makes
119
+ * sense once a reading exists. Nothing else ages.
120
+ */
121
+ return state === "CONNECTED" || state === "DEGRADED" ? "STALE" : state;
122
+ default: {
123
+ const unreachable = event;
124
+ return unreachable;
125
+ }
126
+ }
127
+ }
128
+ /** One sentence per state, written for a person rather than for a log. */
129
+ export const connectionSentence = {
130
+ NOT_CONFIGURED: "Not connected.",
131
+ DETECTED: "Found on this machine but not collecting yet.",
132
+ NEEDS_AUTH: "Waiting for a credential.",
133
+ READY_TO_ENABLE: "Ready to start collecting.",
134
+ CONNECTING: "Connecting.",
135
+ CONNECTED: "Connected and collecting.",
136
+ DEGRADED: "The provider is refusing reads for now.",
137
+ STALE: "The last reading is older than it should be.",
138
+ AUTH_EXPIRED: "The stored credential stopped working.",
139
+ IMPORT_ONLY: "Reads only what you import. Nothing is collected automatically.",
140
+ MANUAL: "Shows the plan you entered yourself.",
141
+ UNSUPPORTED: "No safe automatic source exists for this product yet.",
142
+ ERROR: "This connection failed and needs a look."
143
+ };
144
+ /** The one thing to do next, in the words of the button that does it. */
145
+ export const connectionNextAction = {
146
+ NOT_CONFIGURED: "Connect",
147
+ DETECTED: "Enable local integration",
148
+ NEEDS_AUTH: "Add credential",
149
+ READY_TO_ENABLE: "Enable",
150
+ CONNECTING: "Wait",
151
+ CONNECTED: "Refresh now",
152
+ DEGRADED: "Wait for the next retry",
153
+ STALE: "Refresh now",
154
+ AUTH_EXPIRED: "Reconnect",
155
+ IMPORT_ONLY: "Import a payload",
156
+ MANUAL: "Edit plan",
157
+ UNSUPPORTED: "Add manual plan",
158
+ ERROR: "View diagnostics"
159
+ };
160
+ /**
161
+ * Why a connection is not simply working, in a closed vocabulary.
162
+ *
163
+ * A state says WHAT a connection is. A reason says why it got there, and it is
164
+ * the half a person actually needs: "stale" is a shrug, "stale because the
165
+ * stored credential stopped working" is something they can act on. Closed
166
+ * rather than free text, because a reason is rendered next to a button and a
167
+ * provider must never be able to write either one.
168
+ */
169
+ export const CONNECTION_REASONS = [
170
+ "token_expired",
171
+ "reading_expired",
172
+ "tool_not_running",
173
+ "provider_refusing",
174
+ "shape_mismatch",
175
+ "network_unreachable",
176
+ "no_credential"
177
+ ];
178
+ /** One sentence per reason, written for a person rather than for a log. */
179
+ export const connectionReasonSentence = {
180
+ token_expired: "The credential this connection uses stopped working.",
181
+ reading_expired: "The newest reading is older than the window it describes.",
182
+ tool_not_running: "The local tool has not written a reading yet.",
183
+ provider_refusing: "The provider refused the last read.",
184
+ shape_mismatch: "The answer did not match the shape this build reads.",
185
+ network_unreachable: "The last read never reached the provider.",
186
+ no_credential: "No credential has been stored for this connection."
187
+ };
188
+ /**
189
+ * The instruction for a reason, in the words of the thing the person does.
190
+ *
191
+ * Every one of these is an action a HUMAN takes. OpenLimiter never refreshes a
192
+ * command line tool's token, never signs anyone in, and never renews a
193
+ * credential it did not create, so no instruction here can be read as something
194
+ * this app is about to do by itself. That restraint is the product: a meter
195
+ * that quietly reauthenticated a coding agent would be a much larger thing than
196
+ * a meter, and it would be one nobody asked for.
197
+ */
198
+ function reasonInstruction(reason, tool) {
199
+ switch (reason) {
200
+ case "token_expired":
201
+ return tool === null
202
+ ? "Sign in again where this credential was created."
203
+ : "Open " + tool + " to refresh";
204
+ case "reading_expired":
205
+ return tool === null
206
+ ? "Refresh to read this connection again."
207
+ : "Open " + tool + " to refresh";
208
+ case "tool_not_running":
209
+ return tool === null
210
+ ? "Start the tool that writes this reading."
211
+ : "Open " + tool + " once so it writes a reading";
212
+ case "provider_refusing":
213
+ return "Wait for the next retry.";
214
+ case "shape_mismatch":
215
+ return tool === null
216
+ ? "Reconnect, then report this if it keeps happening."
217
+ : "Reconnect " + tool + ", then report this if it keeps happening";
218
+ case "network_unreachable":
219
+ return "Check the network, then retry.";
220
+ case "no_credential":
221
+ return "Add the credential this connection needs.";
222
+ default: {
223
+ const unreachable = reason;
224
+ return unreachable;
225
+ }
226
+ }
227
+ }
228
+ /**
229
+ * One connection status, from a state and an optional reason.
230
+ *
231
+ * Pure, total, and the only place an instruction is chosen. A reason wins the
232
+ * instruction whenever it has one, because it is the more specific fact; with
233
+ * no reason the state's own next action stands. Nothing here reads a clock, a
234
+ * network or a disk, so a surface and a test see the same answer.
235
+ */
236
+ export function connectionStatus(state, reason = null, tool = null) {
237
+ return {
238
+ state,
239
+ reason,
240
+ instruction: reason === null ? connectionNextAction[state] : reasonInstruction(reason, tool)
241
+ };
242
+ }
243
+ /** The seven providers on the Connect and See catalogue surface. */
244
+ export const CATALOGUE_PROVIDER_IDS = [
245
+ "claude",
246
+ "openrouter",
247
+ "codex",
248
+ "antigravity",
249
+ "opencode",
250
+ "grok",
251
+ "kimi"
252
+ ];
253
+ const supported = {
254
+ mode: "automatic",
255
+ maturity: "supported",
256
+ label: "Supported"
257
+ };
258
+ const eventDriven = {
259
+ mode: "event_driven",
260
+ maturity: "supported",
261
+ label: "Event driven"
262
+ };
263
+ const manual = {
264
+ mode: "manual",
265
+ maturity: "supported",
266
+ label: "Manual"
267
+ };
268
+ const manualExperimental = {
269
+ mode: "manual",
270
+ maturity: "experimental",
271
+ label: "Manual experimental"
272
+ };
273
+ const catalogueCapabilities = {
274
+ claude: { windows: eventDriven, macos: eventDriven, linux: eventDriven },
275
+ openrouter: { windows: supported, macos: supported, linux: supported },
276
+ codex: { windows: supported, macos: supported, linux: supported },
277
+ // The label states what this UI can do today; discovery returns only when its result reaches this surface.
278
+ antigravity: { windows: manualExperimental, macos: manual, linux: manual },
279
+ opencode: {
280
+ windows: manualExperimental,
281
+ macos: manualExperimental,
282
+ linux: manualExperimental
283
+ },
284
+ grok: { windows: supported, macos: supported, linux: supported },
285
+ kimi: { windows: supported, macos: supported, linux: supported }
286
+ };
287
+ function isCatalogueProviderId(value) {
288
+ return typeof value === "string" &&
289
+ CATALOGUE_PROVIDER_IDS.includes(value);
290
+ }
291
+ function isCatalogueAuthMode(value) {
292
+ return value === "existing_local_cli" || value === "api_key" || value === "manual";
293
+ }
294
+ function catalogueState(value) {
295
+ return typeof value === "string" &&
296
+ CONNECTION_STATES.includes(value)
297
+ ? value
298
+ : "NOT_CONFIGURED";
299
+ }
300
+ function actionFor(providerId, state) {
301
+ if (providerId === "codex" && (state === "NEEDS_AUTH" || state === "AUTH_EXPIRED")) {
302
+ return "Run codex login";
303
+ }
304
+ if (providerId === "grok" && (state === "NEEDS_AUTH" || state === "AUTH_EXPIRED")) {
305
+ return "Run grok login";
306
+ }
307
+ if (providerId === "kimi" && (state === "NEEDS_AUTH" || state === "AUTH_EXPIRED")) {
308
+ return "Run kimi login";
309
+ }
310
+ return connectionNextAction[state];
311
+ }
312
+ /**
313
+ * Join generated provider facts to the closed connection state model.
314
+ *
315
+ * The generated document supplies identity, display name, auth mode and the
316
+ * platform set. Product capability maturity is the frozen overlay above. No
317
+ * site or local application is inspected at runtime.
318
+ */
319
+ export function queryProviderCatalogue(generated, states = {}) {
320
+ if (!Array.isArray(generated.providers))
321
+ return [];
322
+ const found = new Map();
323
+ for (const candidate of generated.providers) {
324
+ if (candidate === null || typeof candidate !== "object")
325
+ continue;
326
+ const entry = candidate;
327
+ const providerId = entry.honesty?.connectorId;
328
+ if (!isCatalogueProviderId(providerId))
329
+ continue;
330
+ found.set(providerId, entry);
331
+ }
332
+ const catalogue = [];
333
+ for (const providerId of CATALOGUE_PROVIDER_IDS) {
334
+ const source = found.get(providerId);
335
+ if (source === undefined || typeof source.displayName !== "string")
336
+ continue;
337
+ if (!Array.isArray(source.authModes) || !isCatalogueAuthMode(source.authModes[0]))
338
+ continue;
339
+ const platforms = source.platforms;
340
+ if (!Array.isArray(platforms))
341
+ continue;
342
+ const hasEveryPlatform = ["windows", "macos", "linux"]
343
+ .every((platform) => platforms.includes(platform));
344
+ if (!hasEveryPlatform)
345
+ continue;
346
+ const connectionState = catalogueState(states[providerId]);
347
+ catalogue.push({
348
+ providerId,
349
+ displayName: source.displayName,
350
+ connectionState,
351
+ capabilities: catalogueCapabilities[providerId],
352
+ authMode: source.authModes[0],
353
+ action: actionFor(providerId, connectionState)
354
+ });
355
+ }
356
+ return catalogue;
357
+ }
358
+ /**
359
+ * The connections surface showing connectable providers and planned products.
360
+ *
361
+ * The product catalogue presents every supported or planned provider in document
362
+ * order after the connectable set. Planned rows carry no connection state because
363
+ * no background collector or credential exists for them.
364
+ */
365
+ export function queryCatalogueRows(generated, states = {}) {
366
+ const connectableRows = queryProviderCatalogue(generated, states).map((entry) => ({
367
+ availability: "connectable",
368
+ ...entry
369
+ }));
370
+ if (!Array.isArray(generated.providers))
371
+ return connectableRows;
372
+ const plannedRows = [];
373
+ for (const candidate of generated.providers) {
374
+ if (candidate === null || typeof candidate !== "object")
375
+ continue;
376
+ const entry = candidate;
377
+ const connectorId = entry.honesty?.connectorId;
378
+ if (connectorId === "manual")
379
+ continue;
380
+ if (isCatalogueProviderId(connectorId))
381
+ continue;
382
+ if (typeof entry.id !== "string" || typeof entry.displayName !== "string")
383
+ continue;
384
+ plannedRows.push({
385
+ availability: "planned",
386
+ specId: entry.id,
387
+ displayName: entry.displayName,
388
+ action: "Planned"
389
+ });
390
+ }
391
+ return [...connectableRows, ...plannedRows];
392
+ }
393
+ //# sourceMappingURL=connection-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-state.js","sourceRoot":"","sources":["../src/connection-state.ts"],"names":[],"mappings":"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"}
@@ -16,11 +16,12 @@ import type { ConnectorResult, ProviderCode } from "./types.js";
16
16
  * SESSION_EXPIRED the connector reported its credential no longer works
17
17
  * PAYLOAD_UNREADABLE the connector could not recognise what it was given
18
18
  * VALIDATION_REJECTED the connector recognised it, the normalizer refused it
19
+ * PROVIDER_DRIFT a live response changed and withdrew earlier rows
19
20
  *
20
21
  * Nothing finer is claimed, because nothing finer can be told apart without
21
22
  * reading provider text, which is the one thing this module exists to prevent.
22
23
  */
23
- export declare const FAILURE_CATEGORIES: readonly ["NOT_CONFIGURED", "SESSION_EXPIRED", "PAYLOAD_UNREADABLE", "VALIDATION_REJECTED"];
24
+ export declare const FAILURE_CATEGORIES: readonly ["NOT_CONFIGURED", "SESSION_EXPIRED", "PAYLOAD_UNREADABLE", "VALIDATION_REJECTED", "PROVIDER_DRIFT"];
24
25
  export type FailureCategory = (typeof FAILURE_CATEGORIES)[number];
25
26
  /**
26
27
  * The one sentence each category is ever shown as.
@@ -1 +1 @@
1
- {"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../src/failures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,kBAAkB,6FAKrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAK3D,CAAC;AAEF,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,GACxD,eAAe,CAIjB;AAgBD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,SAAS,eAAe,EAAE,CAS5B"}
1
+ {"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../src/failures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,kBAAkB,+GAMrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAM3D,CAAC;AAEF,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,CAAC,eAAe,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,GACxD,eAAe,CAIjB;AAiBD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,SAAS,eAAe,EAAE,CAS5B"}
package/dist/failures.js CHANGED
@@ -15,6 +15,7 @@
15
15
  * SESSION_EXPIRED the connector reported its credential no longer works
16
16
  * PAYLOAD_UNREADABLE the connector could not recognise what it was given
17
17
  * VALIDATION_REJECTED the connector recognised it, the normalizer refused it
18
+ * PROVIDER_DRIFT a live response changed and withdrew earlier rows
18
19
  *
19
20
  * Nothing finer is claimed, because nothing finer can be told apart without
20
21
  * reading provider text, which is the one thing this module exists to prevent.
@@ -23,7 +24,8 @@ export const FAILURE_CATEGORIES = [
23
24
  "NOT_CONFIGURED",
24
25
  "SESSION_EXPIRED",
25
26
  "PAYLOAD_UNREADABLE",
26
- "VALIDATION_REJECTED"
27
+ "VALIDATION_REJECTED",
28
+ "PROVIDER_DRIFT"
27
29
  ];
28
30
  /**
29
31
  * The one sentence each category is ever shown as.
@@ -35,7 +37,8 @@ export const failureSentence = {
35
37
  NOT_CONFIGURED: "Not set up yet, so nothing was read.",
36
38
  SESSION_EXPIRED: "Session expired.",
37
39
  PAYLOAD_UNREADABLE: "Payload could not be read, so nothing is claimed.",
38
- VALIDATION_REJECTED: "Payload failed validation, kept the last good reading."
40
+ VALIDATION_REJECTED: "Payload failed validation, kept the last good reading.",
41
+ PROVIDER_DRIFT: "Provider response changed, so usage is unknown."
39
42
  };
40
43
  /**
41
44
  * Map a connector's own refusal onto the shared vocabulary.
@@ -61,7 +64,8 @@ const severity = {
61
64
  NOT_CONFIGURED: 0,
62
65
  PAYLOAD_UNREADABLE: 1,
63
66
  SESSION_EXPIRED: 2,
64
- VALIDATION_REJECTED: 3
67
+ VALIDATION_REJECTED: 3,
68
+ PROVIDER_DRIFT: 4
65
69
  };
66
70
  export function dedupeFailures(failures) {
67
71
  const byProvider = new Map();
@@ -1 +1 @@
1
- {"version":3,"file":"failures.js","sourceRoot":"","sources":["../src/failures.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;CACb,CAAC;AAIX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoC;IAC9D,cAAc,EAAE,sCAAsC;IACtD,eAAe,EAAE,kBAAkB;IACnC,kBAAkB,EAAE,mDAAmD;IACvE,mBAAmB,EAAE,wDAAwD;CAC9E,CAAC;AAQF;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAyD;IAEzD,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IACzD,IAAI,MAAM,KAAK,aAAa;QAAE,OAAO,iBAAiB,CAAC;IACvD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,QAAQ,GAAoC;IAChD,cAAc,EAAE,CAAC;IACjB,kBAAkB,EAAE,CAAC;IACrB,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,UAAU,cAAc,CAC5B,QAAoC;IAEpC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC5D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/E,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"failures.js","sourceRoot":"","sources":["../src/failures.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,gBAAgB;IAChB,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;IACrB,gBAAgB;CACR,CAAC;AAIX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoC;IAC9D,cAAc,EAAE,sCAAsC;IACtD,eAAe,EAAE,kBAAkB;IACnC,kBAAkB,EAAE,mDAAmD;IACvE,mBAAmB,EAAE,wDAAwD;IAC7E,cAAc,EAAE,iDAAiD;CAClE,CAAC;AAQF;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAyD;IAEzD,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IACzD,IAAI,MAAM,KAAK,aAAa;QAAE,OAAO,iBAAiB,CAAC;IACvD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,QAAQ,GAAoC;IAChD,cAAc,EAAE,CAAC;IACjB,kBAAkB,EAAE,CAAC;IACrB,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,CAAC;IACtB,cAAc,EAAE,CAAC;CAClB,CAAC;AAEF,MAAM,UAAU,cAAc,CAC5B,QAAoC;IAEpC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC5D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,IAAI,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/E,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AAClC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./cache.js";
2
+ export * from "./collection.js";
3
+ export * from "./connection-state.js";
2
4
  export * from "./failures.js";
3
5
  export * from "./forecast.js";
4
6
  export * from "./format.js";
@@ -6,5 +8,6 @@ export * from "./freshness.js";
6
8
  export * from "./merge.js";
7
9
  export * from "./normalizer.js";
8
10
  export * from "./policy.js";
11
+ export * from "./schedule.js";
9
12
  export * from "./types.js";
10
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from "./cache.js";
2
+ export * from "./collection.js";
3
+ export * from "./connection-state.js";
2
4
  export * from "./failures.js";
3
5
  export * from "./forecast.js";
4
6
  export * from "./format.js";
@@ -6,5 +8,6 @@ export * from "./freshness.js";
6
8
  export * from "./merge.js";
7
9
  export * from "./normalizer.js";
8
10
  export * from "./policy.js";
11
+ export * from "./schedule.js";
9
12
  export * from "./types.js";
10
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC"}
package/dist/merge.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Snapshot } from "./types.js";
2
2
  /** Hard ceiling on how many snapshots one cache file may carry. */
3
3
  export declare const MAX_CACHE_ENTRIES = 64;
4
+ export declare function snapshotIdentity(snapshot: Snapshot): string;
4
5
  /**
5
6
  * Combine cached snapshots with freshly observed ones.
6
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,mEAAmE;AACnE,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAqBpC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,SAAoB,GACxB,QAAQ,EAAE,CAUZ"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,mEAAmE;AACnE,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAYpC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAG3D;AAoCD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,QAAQ,EAAE,SAAS,QAAQ,EAAE,EAC7B,KAAK,SAAoB,GACxB,QAAQ,EAAE,CAaZ"}
package/dist/merge.js CHANGED
@@ -1,12 +1,20 @@
1
1
  /** Hard ceiling on how many snapshots one cache file may carry. */
2
2
  export const MAX_CACHE_ENTRIES = 64;
3
3
  /*
4
- * A provider code and a meter name are both closed uppercase identifiers with
5
- * no spaces, so joining them with one space cannot collide.
4
+ * A provider code, a meter name and an account id are all closed identifiers
5
+ * with no spaces, so joining them with one space cannot collide.
6
+ *
7
+ * The account id joins the key only when the row states one. A row without an
8
+ * account therefore keys exactly as it always did, which is what lets a cache
9
+ * written before accounts existed merge with one written after without any row
10
+ * changing identity or duplicating itself. Two rows for the same meter under
11
+ * different accounts are two rows, which is the whole point of the field.
6
12
  */
7
- function identity(snapshot) {
8
- return snapshot.provider + " " + snapshot.meter;
13
+ export function snapshotIdentity(snapshot) {
14
+ const base = snapshot.provider + " " + snapshot.meter;
15
+ return snapshot.accountId === undefined ? base : base + " " + snapshot.accountId;
9
16
  }
17
+ const identity = snapshotIdentity;
10
18
  function compareIdentity(left, right) {
11
19
  const leftKey = identity(left);
12
20
  const rightKey = identity(right);
@@ -16,6 +24,21 @@ function observedMilliseconds(snapshot) {
16
24
  const parsed = Date.parse(snapshot.observedAt);
17
25
  return Number.isFinite(parsed) ? parsed : 0;
18
26
  }
27
+ function isClaudeStatuslineFallback(snapshot) {
28
+ return snapshot.provider === "CLAUDE" &&
29
+ snapshot.accountId === undefined &&
30
+ snapshot.provenance?.sourceKind === "statusline_payload" &&
31
+ snapshot.provenance.observedVia === "claude_code_statusline";
32
+ }
33
+ function scopedClaudeCoversFallback(candidate, snapshots) {
34
+ if (!isClaudeStatuslineFallback(candidate))
35
+ return false;
36
+ const fallbackObserved = observedMilliseconds(candidate);
37
+ return snapshots.some((snapshot) => snapshot.provider === "CLAUDE" &&
38
+ snapshot.meter === candidate.meter &&
39
+ snapshot.accountId !== undefined &&
40
+ Date.parse(snapshot.expiresAt) > fallbackObserved);
41
+ }
19
42
  /**
20
43
  * Combine cached snapshots with freshly observed ones.
21
44
  *
@@ -29,7 +52,8 @@ export function mergeSnapshots(existing, incoming, limit = MAX_CACHE_ENTRIES) {
29
52
  byIdentity.set(identity(snapshot), snapshot);
30
53
  for (const snapshot of incoming)
31
54
  byIdentity.set(identity(snapshot), snapshot);
32
- const merged = [...byIdentity.values()];
55
+ const combined = [...byIdentity.values()];
56
+ const merged = combined.filter((snapshot) => !scopedClaudeCoversFallback(snapshot, combined));
33
57
  if (merged.length <= limit)
34
58
  return merged.sort(compareIdentity);
35
59
  return merged
package/dist/merge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAEA,mEAAmE;AACnE,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC;;;GAGG;AACH,SAAS,QAAQ,CAAC,QAAkB;IAClC,OAAO,QAAQ,CAAC,QAAQ,GAAG,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC;AAClD,CAAC;AAED,SAAS,eAAe,CAAC,IAAc,EAAE,KAAe;IACtD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAkB;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,QAA6B,EAC7B,QAA6B,EAC7B,KAAK,GAAG,iBAAiB;IAEzB,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,QAAQ,IAAI,QAAQ;QAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9E,KAAK,MAAM,QAAQ,IAAI,QAAQ;QAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChE,OAAO,MAAM;SACV,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;SAC/E,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;SACf,IAAI,CAAC,eAAe,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,QAAQ,EAOd,MAAM,YAAY,CAAC;AAkGpB,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAqC7D;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAK1E;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,sEAAsE;IACtE,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAClC,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,CAgBjB;AAUD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
1
+ {"version":3,"file":"normalizer.d.ts","sourceRoot":"","sources":["../src/normalizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,QAAQ,EAUd,MAAM,YAAY,CAAC;AAwIpB,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI,CA0C7D;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,QAAQ,EAAE,CAK1E;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,sEAAsE;IACtE,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAClC,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAC7B,eAAe,CAgBjB;AAUD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}