@eddyskywalker/dsh-chatgpt-subscription 0.8.5 → 0.9.2

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +94 -38
  3. package/lib/client.js +2126 -1693
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +7935 -2956
  6. package/lib/types/client/claude/ClaudeComposerQuota.d.ts +33 -0
  7. package/lib/types/client/claude/ClaudeComposerQuota.d.ts.map +1 -0
  8. package/lib/types/client/{zhipu/ZhipuSection.d.ts → claude/ClaudeSection.d.ts} +2 -2
  9. package/lib/types/client/claude/ClaudeSection.d.ts.map +1 -0
  10. package/lib/types/client/claude/locales.d.ts +444 -0
  11. package/lib/types/client/claude/locales.d.ts.map +1 -0
  12. package/lib/types/client/claude/styles.d.ts +15 -0
  13. package/lib/types/client/claude/styles.d.ts.map +1 -0
  14. package/lib/types/client/index.d.ts +1 -1
  15. package/lib/types/client/index.d.ts.map +1 -1
  16. package/lib/types/host/claude/__probe-assert.d.ts +2 -0
  17. package/lib/types/host/claude/__probe-assert.d.ts.map +1 -0
  18. package/lib/types/host/claude/account-pool.d.ts +580 -0
  19. package/lib/types/host/claude/account-pool.d.ts.map +1 -0
  20. package/lib/types/host/claude/adapter.d.ts +267 -0
  21. package/lib/types/host/claude/adapter.d.ts.map +1 -0
  22. package/lib/types/host/claude/adopt.d.ts +292 -0
  23. package/lib/types/host/claude/adopt.d.ts.map +1 -0
  24. package/lib/types/host/claude/client.d.ts +513 -0
  25. package/lib/types/host/claude/client.d.ts.map +1 -0
  26. package/lib/types/host/claude/mapper.d.ts +510 -0
  27. package/lib/types/host/claude/mapper.d.ts.map +1 -0
  28. package/lib/types/host/claude/model-catalog.d.ts +365 -0
  29. package/lib/types/host/claude/model-catalog.d.ts.map +1 -0
  30. package/lib/types/host/claude/oauth.d.ts +393 -0
  31. package/lib/types/host/claude/oauth.d.ts.map +1 -0
  32. package/lib/types/host/claude/routes.d.ts +128 -0
  33. package/lib/types/host/claude/routes.d.ts.map +1 -0
  34. package/lib/types/host/claude/token-store.d.ts +312 -0
  35. package/lib/types/host/claude/token-store.d.ts.map +1 -0
  36. package/lib/types/host/claude/types.d.ts +237 -0
  37. package/lib/types/host/claude/types.d.ts.map +1 -0
  38. package/lib/types/host/common/account-pool.d.ts +40 -0
  39. package/lib/types/host/common/account-pool.d.ts.map +1 -1
  40. package/lib/types/host/kimi-code/adapter.d.ts.map +1 -1
  41. package/lib/types/host/kimi-code/client.d.ts +28 -0
  42. package/lib/types/host/kimi-code/client.d.ts.map +1 -1
  43. package/lib/types/host/kimi-code/routes.d.ts.map +1 -1
  44. package/lib/types/index.d.ts +10 -7
  45. package/lib/types/index.d.ts.map +1 -1
  46. package/lib/types/shared/__probe-claude-import.d.ts +2 -0
  47. package/lib/types/shared/__probe-claude-import.d.ts.map +1 -0
  48. package/lib/types/shared/claude-contracts.d.ts +337 -0
  49. package/lib/types/shared/claude-contracts.d.ts.map +1 -0
  50. package/package.json +1 -1
  51. package/lib/types/client/zhipu/ZhipuComposerQuota.d.ts +0 -26
  52. package/lib/types/client/zhipu/ZhipuComposerQuota.d.ts.map +0 -1
  53. package/lib/types/client/zhipu/ZhipuSection.d.ts.map +0 -1
  54. package/lib/types/client/zhipu/locales.d.ts +0 -275
  55. package/lib/types/client/zhipu/locales.d.ts.map +0 -1
  56. package/lib/types/client/zhipu/styles.d.ts +0 -12
  57. package/lib/types/client/zhipu/styles.d.ts.map +0 -1
  58. package/lib/types/host/zhipu/account-pool.d.ts +0 -43
  59. package/lib/types/host/zhipu/account-pool.d.ts.map +0 -1
  60. package/lib/types/host/zhipu/adapter.d.ts +0 -93
  61. package/lib/types/host/zhipu/adapter.d.ts.map +0 -1
  62. package/lib/types/host/zhipu/client.d.ts +0 -153
  63. package/lib/types/host/zhipu/client.d.ts.map +0 -1
  64. package/lib/types/host/zhipu/mapper.d.ts +0 -111
  65. package/lib/types/host/zhipu/mapper.d.ts.map +0 -1
  66. package/lib/types/host/zhipu/model-catalog.d.ts +0 -73
  67. package/lib/types/host/zhipu/model-catalog.d.ts.map +0 -1
  68. package/lib/types/host/zhipu/routes.d.ts +0 -33
  69. package/lib/types/host/zhipu/routes.d.ts.map +0 -1
  70. package/lib/types/host/zhipu/token-store.d.ts +0 -112
  71. package/lib/types/host/zhipu/token-store.d.ts.map +0 -1
  72. package/lib/types/host/zhipu/types.d.ts +0 -125
  73. package/lib/types/host/zhipu/types.d.ts.map +0 -1
  74. package/lib/types/shared/zhipu-contracts.d.ts +0 -164
  75. package/lib/types/shared/zhipu-contracts.d.ts.map +0 -1
@@ -0,0 +1,513 @@
1
+ /**
2
+ * Upstream HTTP surface of the Claude subscription line.
3
+ *
4
+ * WHAT THIS MODULE IS. Everything between "we hold a subscription OAuth
5
+ * credential" and "a response came back": the request headers, the model
6
+ * listing, the two independent sources of quota numbers, a one-shot
7
+ * connectivity probe, and the classification of a failure. It owns no
8
+ * credential lifecycle — refreshing is `oauth.ts`'s job, storage is
9
+ * `token-store.ts`'s — so every entry point here takes an ALREADY-CURRENT
10
+ * access token and never writes one back.
11
+ *
12
+ * Each behavioural claim below names the evidence behind it. Where the evidence
13
+ * is a reading of the locally installed reference implementation it says so;
14
+ * where it is this module's own judgement it says that instead, because a later
15
+ * reviewer has to be able to tell the two apart. Nothing here asserts what an
16
+ * endpoint returns without a fixture or a live read behind it.
17
+ *
18
+ * ---------------------------------------------------------------------------
19
+ * 1. THE HEADERS, AND WHY `x-api-key` MUST NOT BE THERE
20
+ * ---------------------------------------------------------------------------
21
+ *
22
+ * The subscription is not the Console API. The Console authenticates with
23
+ * `x-api-key`; the subscription authenticates with an OAuth access token in
24
+ * `Authorization: Bearer`. Sending BOTH is a documented 401 cause — the gateway
25
+ * sees an API-key-shaped credential on a route that does not serve API keys and
26
+ * refuses the request. So the builder below emits `authorization` and actively
27
+ * removes any `x-api-key` that a caller's `extra` map tried to smuggle in:
28
+ * a caller copying another provider's header map is exactly how that bug would
29
+ * re-enter this codebase, and a silent 401 is a bad way to find out.
30
+ *
31
+ * The `user-agent` is load-bearing rather than cosmetic. Upstream enforces a
32
+ * MINIMUM REPORTED CLIENT VERSION on this path: when the model being asked for
33
+ * is newer than the version we claim to be, the request is refused with HTTP 400
34
+ * and the body code `claude_code_version_too_old`
35
+ * ({@link ERROR_CODE_CLIENT_VERSION_TOO_OLD}). The version therefore comes from
36
+ * {@link claudeCliVersion} at CALL TIME rather than from a module constant, so a
37
+ * `setClaudeCliVersion` pin or a `DSH_CLAUDE_CLI_VERSION` in the environment
38
+ * takes effect on the very next request without a restart.
39
+ *
40
+ * `anthropic-beta` is cumulative: one request carries every marker that
41
+ * applies. Three sources contribute, and only one of them is conditional on the
42
+ * model — the reference's own rule is that a HAIKU model does NOT receive
43
+ * `claude-code-20250219` while every other model does. The only offline signal
44
+ * for that is the family token in the id itself.
45
+ *
46
+ * ---------------------------------------------------------------------------
47
+ * 2. THE MODEL LISTING OVERRIDES THE CONTEXT WINDOW AND NOTHING ELSE
48
+ * ---------------------------------------------------------------------------
49
+ *
50
+ * This is the invariant most worth protecting in this file. `GET /v1/models`
51
+ * states which ids the ACCOUNT can call and how much context each one takes; it
52
+ * says nothing about image support, about which thinking form a model needs, or
53
+ * about the reasoning ladder it exposes. Those are per-model facts that
54
+ * `model-catalog.ts` carries and that the family name does not decide — a
55
+ * listing-driven merge that wrote capability fields would silently replace them
56
+ * with `undefined` and produce requests the model rejects.
57
+ *
58
+ * So {@link loadCatalog} produces each entry as `{ ...catalogEntry,
59
+ * contextWindow }`: ONE written field, everything else copied verbatim from the
60
+ * frozen table (or from its conservative stub, for an id the table does not
61
+ * know). A test asserts that a listing cannot change any other field.
62
+ *
63
+ * The failure path matters as much as the success path. DSH 0.1.7 makes "the
64
+ * model must appear in the provider's catalog" a HARD GATE, so returning an
65
+ * empty list when the listing call fails would not degrade the picker — it would
66
+ * block model switching entirely, and it would do so while the account is
67
+ * perfectly usable. Every failure therefore yields {@link FALLBACK_MODELS}.
68
+ * An empty-but-successful listing is treated the same way, for the same reason.
69
+ *
70
+ * ---------------------------------------------------------------------------
71
+ * 3. TWO QUOTA SOURCES IN TWO DIFFERENT UNITS — THE BUG THIS FILE EXISTS TO STOP
72
+ * ---------------------------------------------------------------------------
73
+ *
74
+ * (a) PRIMARY, `GET /api/oauth/usage`. Each window is
75
+ * `{ utilization, resets_at }` and `utilization` is a PERCENT USED in
76
+ * 0-100. `utilization: 0` means nothing has been used — the ordinary state
77
+ * of a healthy account — and is NOT an error and NOT "no quota left".
78
+ *
79
+ * (b) SECONDARY, the `anthropic-ratelimit-unified-*` response headers on ANY
80
+ * `/v1/messages` response including 200s.
81
+ * `...-5h-utilization` is a FRACTION in 0-1. Same word, different unit:
82
+ * `0.25` here and `25` there both mean "a quarter of the window is gone".
83
+ * `...-5h-reset` is epoch SECONDS — not milliseconds, and not ISO — while
84
+ * `resets_at` in (a) IS ISO 8601. A parser that treats the two as one
85
+ * number under-reports by 100x or over-reports by 100x depending on which
86
+ * direction it guessed, and the card shows a confident wrong number either
87
+ * way.
88
+ *
89
+ * The conversion happens at exactly TWO boundaries and nowhere else: the
90
+ * header parser multiplies by 100 once, on the way in, and the body parser
91
+ * divides by 100 once, on the way in. After that both are the same
92
+ * {@link ClaudeUsageWindow}, and a test asserts that `0.25` in a header and
93
+ * `25` in a body produce byte-identical windows.
94
+ *
95
+ * Header data is allowed to satisfy the composer badge — the badge only needs
96
+ * the tightest window — but it must NOT permanently replace a full read. The
97
+ * cache therefore tracks two instants: when any source last wrote
98
+ * (`observedAt`, gated by {@link QUOTA_CACHE_TTL_MS}) and when a FULL read last
99
+ * succeeded (`fetchedAt`, gated by {@link QUOTA_FULL_REFRESH_MS}). Header
100
+ * updates advance only the first, so a stream of model calls cannot keep the
101
+ * card warm forever behind stale numbers.
102
+ *
103
+ * Unknown keys in the usage payload are IGNORED. The response's key set varies
104
+ * by account type and carries churning feature-flag codenames; a key this
105
+ * module does not know is not a window, and a missing known key is not zero.
106
+ *
107
+ * ---------------------------------------------------------------------------
108
+ * 4. FAILURE CLASSIFICATION
109
+ * ---------------------------------------------------------------------------
110
+ *
111
+ * Anthropic reports the real condition in the body's `error.type` while the
112
+ * HTTP status stays coarse, so the body is authoritative and the status is the
113
+ * fallback. The one distinction the rest of the line depends on is inside
114
+ * `rate_limit_error`: a 429 that names a SPENT WINDOW OF THIS ACCOUNT is fixed
115
+ * by switching accounts, and a 429 that does not is not — switching accounts on
116
+ * a global limit would burn every account in the pool against a wall none of
117
+ * them can pass. That distinction is exported as a first-class value rather
118
+ * than left for the caller to re-derive from headers.
119
+ */
120
+ import { type ClaudeErrorType } from './types.ts';
121
+ import { type ClaudeModelEntry } from './model-catalog.ts';
122
+ import type { ClaudeCredentials } from './token-store.ts';
123
+ /** A flat, lower-cased header map, ready for `fetch`. */
124
+ export type ClaudeRequestHeaders = Record<string, string>;
125
+ /**
126
+ * Which product string a request reports as.
127
+ *
128
+ * `cli` is the Messages-API identity: `claude-cli/<v> (external, cli)`. The
129
+ * subscription usage endpoint is a different surface and the reference reports
130
+ * a different product there — `claude-code/<v>` — so the choice is a parameter
131
+ * rather than a constant. Reporting the wrong one is not fatal on the usage
132
+ * route, but it is also not free: the route is behind the same gateway that
133
+ * enforces the reported-version floor.
134
+ */
135
+ export type ClaudeUserAgentKind = 'cli' | 'code';
136
+ /** Options for {@link buildClaudeHeaders}. */
137
+ export interface ClaudeHeaderOptions {
138
+ /**
139
+ * Exact model id this request targets.
140
+ *
141
+ * The one header consequence is the `claude-code-20250219` beta: the
142
+ * reference's rule is that a haiku model does not receive it. Omit it for a
143
+ * request that names no model (the usage read), which keeps the beta.
144
+ */
145
+ model?: string;
146
+ /** Whether this request enables thinking; adds the interleaved-thinking beta. */
147
+ thinking?: boolean;
148
+ /** HTTP method. `content-type` is emitted unless this is a GET. */
149
+ method?: 'GET' | 'POST' | string;
150
+ /** Which product identity to report. Defaults to the Messages-API one. */
151
+ userAgent?: ClaudeUserAgentKind;
152
+ /** Extra headers. `x-api-key` is stripped from the result regardless. */
153
+ extra?: Record<string, string>;
154
+ }
155
+ /**
156
+ * The `anthropic-beta` set for one request, in a stable order.
157
+ *
158
+ * Order is not observable — the server parses the header as a set — so the order
159
+ * below is chosen for readability in a log rather than to match a reference.
160
+ */
161
+ export declare function claudeBetas(options?: Pick<ClaudeHeaderOptions, 'model' | 'thinking'>): string[];
162
+ /** The `User-Agent` value this line reports for one surface. */
163
+ export declare function claudeUserAgent(kind?: ClaudeUserAgentKind): string;
164
+ /**
165
+ * Build the headers for one subscription request.
166
+ *
167
+ * The single builder for every surface this module touches — the Messages API,
168
+ * the model listing, the usage read, and the probe. `extra` is how a caller
169
+ * adds a header this module does not own (a streaming `accept`, an
170
+ * `anthropic-beta` the adapter negotiated) without a second builder drifting
171
+ * away from this one.
172
+ *
173
+ * @param accessToken - a CURRENT OAuth access token. This function does not
174
+ * check expiry; the caller owns refresh.
175
+ */
176
+ export declare function buildClaudeHeaders(accessToken: string, options?: ClaudeHeaderOptions): ClaudeRequestHeaders;
177
+ /** Drop the cached listing, forcing the next load to hit the network. */
178
+ export declare function clearCachedCatalog(): void;
179
+ /** The listing currently held, or an empty array before the first load. */
180
+ export declare function getCachedCatalog(): readonly ClaudeModelEntry[];
181
+ /** Whether the held listing is the shipped table standing in for a failed call. */
182
+ export declare function isCatalogFallback(): boolean;
183
+ /** One listing entry, reduced to the two fields this line reads from it. */
184
+ interface ListingEntry {
185
+ id: string;
186
+ /** The window the listing stated, or null when it stated none. */
187
+ contextWindow: number | null;
188
+ }
189
+ /**
190
+ * Fold the live listing onto the frozen capability table.
191
+ *
192
+ * `contextWindow` is the ONLY field the listing may write. Everything else is
193
+ * copied from `resolveClaudeModel`, which returns the catalog entry for a known
194
+ * id and its conservative stub for an unknown one — so an id this line has never
195
+ * heard of keeps the stub's "no images, no thinking form, 200K window" answer
196
+ * instead of inheriting invented capabilities from the listing.
197
+ *
198
+ * The catalog is NOT widened by the listing: the returned array contains exactly
199
+ * the ids the server named, because the listing is the authority on what this
200
+ * account may call. (The fallback path is what keeps a failed call from
201
+ * narrowing it to nothing.)
202
+ */
203
+ export declare function mergeListingIntoCatalog(listing: readonly ListingEntry[]): ClaudeModelEntry[];
204
+ /** Options for {@link loadCatalog}. */
205
+ export interface CatalogLoadOptions {
206
+ fetchFn?: typeof fetch;
207
+ signal?: AbortSignal;
208
+ /** Bypass the cached listing and read the server again. */
209
+ force?: boolean;
210
+ }
211
+ /**
212
+ * Read the models the signed-in account can call.
213
+ *
214
+ * Cached for {@link CATALOG_CACHE_TTL_MS} and single-flighted per credential,
215
+ * because the harness resolves EVERY model of EVERY provider when it builds the
216
+ * picker — without both, one picker build is one network round trip per model.
217
+ *
218
+ * A failure is cached too, deliberately. The fallback table is the full shipped
219
+ * snapshot, so serving it for the cache window costs the user nothing but a
220
+ * slightly wider picker, while NOT caching the failure would put a dead
221
+ * endpoint behind fourteen sequential 20-second timeouts on every picker build.
222
+ * {@link clearCachedCatalog} is the escape hatch: the sign-in path calls it so a
223
+ * user who just authenticated does not sit behind a failure recorded before
224
+ * they did.
225
+ *
226
+ * @param credentials - a current subscription credential; it selects the cache
227
+ * entry, since a different account can see a different listing.
228
+ */
229
+ export declare function loadCatalog(credentials: ClaudeCredentials, options?: CatalogLoadOptions): Promise<readonly ClaudeModelEntry[]>;
230
+ /**
231
+ * One account window, normalized.
232
+ *
233
+ * The three numeric fields are null TOGETHER, and null means "the source did
234
+ * not say" — which is a different statement from 0, "nothing used". Collapsing
235
+ * them would turn a payload that omitted a window into a confident zero.
236
+ */
237
+ export interface ClaudeUsageWindow {
238
+ /** Wire key: `five_hour`, `seven_day`, `seven_day_sonnet`. */
239
+ id: string;
240
+ /** Display label. */
241
+ label: string;
242
+ /** Nominal length of the window in minutes, or null when unknown. */
243
+ windowMinutes: number | null;
244
+ /** Fraction of the window consumed, 0-1. Percent divided by 100 at this boundary. */
245
+ usedFraction: number | null;
246
+ /** Percent of the window consumed, 0-100. */
247
+ usedPercent: number | null;
248
+ /** Percent of the window still available, 0-100. */
249
+ remainingPercent: number | null;
250
+ /** ISO 8601 instant the window resets, or null. */
251
+ resetsAt: string | null;
252
+ /** Which source these numbers came from. */
253
+ source: 'usage' | 'headers';
254
+ }
255
+ /** The account's pay-as-you-go overage pool, as the usage payload states it. */
256
+ export interface ClaudeExtraUsage {
257
+ isEnabled: boolean;
258
+ monthlyLimit: number | null;
259
+ usedCredits: number | null;
260
+ /** Percent of the monthly limit consumed, 0-100 — same unit as the windows. */
261
+ utilization: number | null;
262
+ }
263
+ /** The whole quota snapshot the settings card and the composer badge read. */
264
+ export interface ClaudeAccountQuota {
265
+ windows: ClaudeUsageWindow[];
266
+ extraUsage: ClaudeExtraUsage | null;
267
+ /**
268
+ * Epoch ms of the last successful FULL usage read.
269
+ *
270
+ * Null when only response headers have ever been seen. This is the field that
271
+ * decides whether the next read may be satisfied from headers alone.
272
+ */
273
+ fetchedAt: number | null;
274
+ /** Epoch ms of the most recent update from any source. */
275
+ observedAt: number;
276
+ /** Unified rate-limit status the headers last reported, when they did. */
277
+ status: string | null;
278
+ /** Window the headers last named as representative, when they did. */
279
+ representativeClaim: string | null;
280
+ }
281
+ /** The window with the least headroom, or null when no window states one. */
282
+ export declare function tightestQuotaWindow(windows: readonly ClaudeUsageWindow[]): ClaudeUsageWindow | null;
283
+ /**
284
+ * Read the usage payload's known keys, and only its known keys.
285
+ *
286
+ * The key set varies by account type and the payload also carries churning
287
+ * feature-flag codenames; anything not named below is ignored, because reading
288
+ * an unknown key as a window is how a wrong number gets on the card. A known
289
+ * key that is `null` (the documented state of `seven_day_sonnet`) yields no
290
+ * window at all rather than a zeroed one.
291
+ */
292
+ export declare function parseUsagePayload(payload: unknown): {
293
+ windows: ClaudeUsageWindow[];
294
+ extraUsage: ClaudeExtraUsage | null;
295
+ };
296
+ /** Prefix every unified rate-limit header shares. */
297
+ export declare const QUOTA_HEADER_PREFIX = "anthropic-ratelimit-unified-";
298
+ /**
299
+ * Anything header-shaped.
300
+ *
301
+ * A `Headers` (what `fetch` returns) or a plain map (what a test or a
302
+ * hand-built call site has). Both are read case-insensitively, because HTTP
303
+ * header names are case-insensitive and a plain map from a caller often is not.
304
+ */
305
+ export type ClaudeHeaderSource = Headers | Record<string, string | readonly string[] | undefined>;
306
+ /** Convert the headers' epoch-SECONDS reset instant to the ISO form the card uses. */
307
+ export declare function epochSecondsToIso(value: number | undefined): string | null;
308
+ /** One window as the unified response headers state it. */
309
+ export interface ClaudeHeaderQuotaWindow {
310
+ id: string;
311
+ label: string;
312
+ windowMinutes: number | null;
313
+ /** The header's own unit: a FRACTION, 0-1. */
314
+ usedFraction: number;
315
+ /** The same number as a percent, 0-100. Derived here and nowhere else. */
316
+ usedPercent: number;
317
+ remainingPercent: number;
318
+ /** ISO 8601, converted from the header's epoch SECONDS. */
319
+ resetsAt: string | null;
320
+ /** Always `headers`: what this window is made of, for the merged DTO. */
321
+ source: 'headers';
322
+ }
323
+ /** Everything the unified rate-limit headers said about one response. */
324
+ export interface ClaudeQuotaHeaderReading {
325
+ windows: ClaudeHeaderQuotaWindow[];
326
+ /** `anthropic-ratelimit-unified-status` — e.g. a rejected/allowed verdict. */
327
+ status: string | null;
328
+ /** `anthropic-ratelimit-unified-representative-claim` — the window that speaks for the response. */
329
+ representativeClaim: string | null;
330
+ /** The overage headers, when the response carried any. */
331
+ overage: {
332
+ status: string | null;
333
+ utilization: number | null;
334
+ } | null;
335
+ }
336
+ /**
337
+ * Read the unified rate-limit headers off any response.
338
+ *
339
+ * THIS is the boundary where the header FRACTION becomes a fraction in the
340
+ * normalized DTO — it is never divided or multiplied again downstream. The
341
+ * sibling `resets_at` field in the usage payload is ISO while the header reset
342
+ * is epoch seconds; both land on the DTO as ISO.
343
+ */
344
+ export declare function parseQuotaHeaders(headers: ClaudeHeaderSource): ClaudeQuotaHeaderReading;
345
+ /** Drop the cached snapshot. Called on sign-out and after a credential change. */
346
+ export declare function clearCachedQuota(): void;
347
+ /**
348
+ * The cached snapshot, optionally only when it belongs to this credential.
349
+ *
350
+ * With one stored account the argument is unnecessary; it exists so a later
351
+ * pool chunk cannot render one account's usage under another account's name.
352
+ */
353
+ export declare function getCachedQuota(credentials?: Pick<ClaudeCredentials, 'accessToken'>): ClaudeAccountQuota | null;
354
+ /** Options for {@link fetchAccountQuota}. */
355
+ export interface QuotaFetchOptions {
356
+ fetchFn?: typeof fetch;
357
+ signal?: AbortSignal;
358
+ /** Bypass the cache and read the usage endpoint now. */
359
+ force?: boolean;
360
+ }
361
+ /**
362
+ * Read the account's usage meters.
363
+ *
364
+ * Cached for {@link QUOTA_CACHE_TTL_MS} and single-flighted per credential. The
365
+ * cache is served only while BOTH clocks are inside their windows: a snapshot
366
+ * kept warm by response headers still triggers a full read once the last real
367
+ * read is older than {@link QUOTA_FULL_REFRESH_MS}, which is what stops a busy
368
+ * conversation from pinning the card to numbers that arrived as a side effect.
369
+ *
370
+ * A failure never blanks the card: an existing snapshot for the same credential
371
+ * is returned instead, because a transient outage says nothing about the
372
+ * account's real usage. The exception is a credential failure, which is always
373
+ * surfaced — hiding a revoked credential behind stale numbers is how a user ends
374
+ * up staring at a card that claims they are fine.
375
+ */
376
+ export declare function fetchAccountQuota(credentials: ClaudeCredentials, options?: QuotaFetchOptions): Promise<ClaudeAccountQuota>;
377
+ /**
378
+ * Fold the unified response headers of any response into the cached snapshot.
379
+ *
380
+ * Called on every `/v1/messages` response — including 200s, which is where the
381
+ * numbers usually come from. It advances `observedAt` and NOT `fullReadAt`, so
382
+ * header data can satisfy the composer badge while the card is still required to
383
+ * perform a real read once the last one is older than
384
+ * {@link QUOTA_FULL_REFRESH_MS}.
385
+ *
386
+ * @param headers - response headers of a model call.
387
+ * @param accountKey - the account those headers belong to, as
388
+ * {@link accountKeyFor} derives it. Omitted, the reading is filed under
389
+ * whoever the cached snapshot already belongs to — the correct answer for the
390
+ * single-account case, and a reading whose account cannot be determined is
391
+ * filed under none rather than under the wrong one. When it is given and
392
+ * disagrees with the snapshot's account the reading is DISCARDED rather than
393
+ * merged: mixing two accounts' windows is the one outcome worse than a stale
394
+ * number.
395
+ * @returns the updated snapshot, or null when the headers carried nothing usable.
396
+ */
397
+ export declare function recordQuotaFromHeaders(headers: ClaudeHeaderSource, accountKey?: string): ClaudeAccountQuota | null;
398
+ /** A probe that reached the Messages API and was answered. */
399
+ export interface ClaudeProbeSuccess {
400
+ ok: true;
401
+ model: string;
402
+ status: number;
403
+ latencyMs: number;
404
+ /** `stop_reason` of the one-token answer, when the body carried one. */
405
+ stopReason: string | null;
406
+ }
407
+ /** A probe that did not succeed, with the reason rather than an exception. */
408
+ export interface ClaudeProbeFailure {
409
+ ok: false;
410
+ model: string;
411
+ /** HTTP status, or 0 when no response arrived at all. */
412
+ status: number;
413
+ latencyMs: number;
414
+ failure: ClaudeFailure;
415
+ }
416
+ export type ClaudeProbeResult = ClaudeProbeSuccess | ClaudeProbeFailure;
417
+ /** Options for {@link probeConnection}. */
418
+ export interface ProbeOptions {
419
+ fetchFn?: typeof fetch;
420
+ signal?: AbortSignal;
421
+ /**
422
+ * Model to probe with.
423
+ *
424
+ * Defaults to the cheapest id in the shipped table. A probe is a connectivity
425
+ * and credential check, and the widest-entitlement model answers it on every
426
+ * plan; a caller that wants to prove a SPECIFIC model's entitlement passes it
427
+ * here. Note that the default is a haiku id, so the probe exercises the header
428
+ * set that omits the claude-code beta — pass the user's selected model to
429
+ * probe the exact header set that model's real calls will use.
430
+ */
431
+ model?: string;
432
+ /** Deadline for the probe. Defaults to the discovery ceiling. */
433
+ timeoutMs?: number;
434
+ }
435
+ /** The model a probe uses when the caller names none. */
436
+ export declare const PROBE_DEFAULT_MODEL = "claude-haiku-4-5";
437
+ /**
438
+ * Prove the credential works and measure how long the round trip took.
439
+ *
440
+ * One non-streaming POST with `max_tokens: 1` — the smallest request the
441
+ * Messages API accepts, which keeps a probe from consuming anything meaningful
442
+ * even when it is run from the settings card on every save.
443
+ *
444
+ * An upstream rejection is RETURNED, not thrown: the card's whole job here is to
445
+ * render the reason, and a thrown error would have to be caught and unwrapped at
446
+ * every call site to say the same thing.
447
+ */
448
+ export declare function probeConnection(credentials: ClaudeCredentials, options?: ProbeOptions): Promise<ClaudeProbeResult>;
449
+ /**
450
+ * What a caller should DO about a failure — the taxonomy, not the message.
451
+ *
452
+ * - `credential` — the stored credential is finished (401/403). Final: retrying
453
+ * cannot help, and the user has to sign in again.
454
+ * - `request` — this request was wrong (400/404/413/422). Not retryable, and
455
+ * for this line often a client-version or parameter problem.
456
+ * - `rate_limit_account` — a usage WINDOW OF THIS ACCOUNT is spent. Switching
457
+ * accounts is what fixes it.
458
+ * - `rate_limit_global` — a limit that is not attributable to this account's
459
+ * window. Switching accounts would burn the pool against a wall.
460
+ * - `overloaded` — transient capacity shortage. Retryable, not account-specific.
461
+ * - `server` — a 5xx that is not an overload. Retryable.
462
+ * - `network` — no response arrived at all. Retryable.
463
+ */
464
+ export type ClaudeFailureKind = 'credential' | 'request' | 'rate_limit_account' | 'rate_limit_global' | 'overloaded' | 'server' | 'network';
465
+ /** A classified failure, in the shape the adapter and the settings card consume. */
466
+ export interface ClaudeFailure {
467
+ kind: ClaudeFailureKind;
468
+ /** Wire `error.type`, when the body named one this line recognizes. */
469
+ type: ClaudeErrorType | null;
470
+ /** HTTP status, or 0 for a transport failure. */
471
+ status: number;
472
+ /** Human-readable reason, from the body when it gave one. */
473
+ message: string;
474
+ /** Whether repeating the SAME request could succeed. */
475
+ retryable: boolean;
476
+ /**
477
+ * Whether the failure is attributable to this ACCOUNT's own usage window.
478
+ *
479
+ * This is the field a pool switches accounts on. It is true only for
480
+ * `rate_limit_account`.
481
+ */
482
+ accountScoped: boolean;
483
+ /** Whether the body's code was the reported-client-version floor. */
484
+ clientVersionTooOld: boolean;
485
+ /** ISO 8601 reset instant, when the body or the headers named one. */
486
+ resetsAt: string | null;
487
+ /** `retry-after`, in milliseconds, when the response carried it. */
488
+ retryAfterMs: number | null;
489
+ }
490
+ /** Error carrying a classified failure, for the paths that must signal instead of return. */
491
+ export declare class ClaudeRequestError extends Error {
492
+ readonly failure: ClaudeFailure;
493
+ constructor(failure: ClaudeFailure);
494
+ }
495
+ /** A transport failure: nothing came back, so there is no status and no body. */
496
+ export declare function networkFailure(error: unknown): ClaudeFailure;
497
+ /**
498
+ * Classify one failed response.
499
+ *
500
+ * The body is authoritative and the status is the fallback, because Anthropic's
501
+ * statuses are coarse: several unrelated conditions share a status while
502
+ * `error.type` names the real one. Both paths are exercised by tests with real
503
+ * response fixtures.
504
+ *
505
+ * @param status - HTTP status.
506
+ * @param bodyText - the response body as text. A body this function cannot
507
+ * parse costs a more precise `type`, never a wrong classification.
508
+ * @param headers - response headers, read for the unified rate-limit verdict and
509
+ * for `retry-after`.
510
+ */
511
+ export declare function classifyFailure(status: number, bodyText: string, headers?: ClaudeHeaderSource): ClaudeFailure;
512
+ export {};
513
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/host/claude/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsHG;AAEH,OAAO,EAiBL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAuC,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAC/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAgEzD,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEzD;;;;;;;;;GASG;AACH,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,MAAM,CAAA;AAEhD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mEAAmE;IACnE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAA;IAChC,0EAA0E;IAC1E,SAAS,CAAC,EAAE,mBAAmB,CAAA;IAC/B,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAcD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,UAAU,CAAM,GAAG,MAAM,EAAE,CAQnG;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,IAAI,GAAE,mBAA2B,GAAG,MAAM,CAGzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,oBAAoB,CAc/G;AA0CD,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,IAAI,SAAS,gBAAgB,EAAE,CAE9D;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,4EAA4E;AAC5E,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAA;IACV,kEAAkE;IAClE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAsBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAW5F;AAED,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;IACtB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CA0BtC;AA0CD;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAA;IACV,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,qEAAqE;IACrE,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,qFAAqF;IACrF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,6CAA6C;IAC7C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,mDAAmD;IACnD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,4CAA4C;IAC5C,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;CAC5B;AAED,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,+EAA+E;IAC/E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACnC;;;;;OAKG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,sEAAsE;IACtE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnC;AAuCD,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,iBAAiB,EAAE,GAAG,iBAAiB,GAAG,IAAI,CAOnG;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG;IACnD,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAA;CACpC,CA6CA;AAMD,qDAAqD;AACrD,eAAO,MAAM,mBAAmB,iCAAiC,CAAA;AAEjE;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;AAqCjG,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAI1E;AAED,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAA;IACpB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,yEAAyE;IACzE,MAAM,EAAE,SAAS,CAAA;CAClB;AAED,yEAAyE;AACzE,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,uBAAuB,EAAE,CAAA;IAClC,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,oGAAoG;IACpG,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,0DAA0D;IAC1D,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CACtE;AAQD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,wBAAwB,CA2BvF;AAkBD,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,GAAG,kBAAkB,GAAG,IAAI,CAI9G;AAED,6CAA6C;AAC7C,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;IACtB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAuB7B;AAuDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,kBAAkB,EAC3B,UAAU,CAAC,EAAE,MAAM,GAClB,kBAAkB,GAAG,IAAI,CAiD3B;AAMD,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,IAAI,CAAA;IACR,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,8EAA8E;AAC9E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,KAAK,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,aAAa,CAAA;CACvB;AAED,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,CAAA;AAEvE,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,OAAO,KAAK,CAAA;IACtB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,qBAAqB,CAAA;AAErD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CA+B5B;AAMD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,SAAS,GACT,oBAAoB,GACpB,mBAAmB,GACnB,YAAY,GACZ,QAAQ,GACR,SAAS,CAAA;AAEb,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAA;IACvB,uEAAuE;IACvE,IAAI,EAAE,eAAe,GAAG,IAAI,CAAA;IAC5B,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,aAAa,EAAE,OAAO,CAAA;IACtB,qEAAqE;IACrE,mBAAmB,EAAE,OAAO,CAAA;IAC5B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oEAAoE;IACpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,6FAA6F;AAC7F,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;gBAEnB,OAAO,EAAE,aAAa;CAKnC;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAa5D;AAkDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,kBAAuB,GAC/B,aAAa,CA6Gf"}