dsh-plugin-subscription-usage 0.0.0-stage → 0.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.
Files changed (169) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +50 -2
  3. package/cordis.patch.yml +5 -0
  4. package/icon.svg +9 -0
  5. package/lib/auth/claude-code-creds.d.ts +23 -0
  6. package/lib/auth/claude-code-creds.js +172 -0
  7. package/lib/auth/device-flow.d.ts +55 -0
  8. package/lib/auth/device-flow.js +177 -0
  9. package/lib/auth/jwt.d.ts +10 -0
  10. package/lib/auth/jwt.js +25 -0
  11. package/lib/auth/oauth-flow.d.ts +91 -0
  12. package/lib/auth/oauth-flow.js +227 -0
  13. package/lib/auth/pkce.d.ts +31 -0
  14. package/lib/auth/pkce.js +35 -0
  15. package/lib/auth/rpc.d.ts +233 -0
  16. package/lib/auth/rpc.js +501 -0
  17. package/lib/auth/store.d.ts +198 -0
  18. package/lib/auth/store.js +473 -0
  19. package/lib/client/Footer.d.ts +3 -0
  20. package/lib/client/Footer.js +73 -0
  21. package/lib/client/Gauge.d.ts +28 -0
  22. package/lib/client/Gauge.js +139 -0
  23. package/lib/client/ImageGallery.d.ts +54 -0
  24. package/lib/client/ImageGallery.js +112 -0
  25. package/lib/client/ImageGenerateToolview.d.ts +48 -0
  26. package/lib/client/ImageGenerateToolview.js +130 -0
  27. package/lib/client/Notice.d.ts +15 -0
  28. package/lib/client/Notice.js +93 -0
  29. package/lib/client/ProviderCard.d.ts +35 -0
  30. package/lib/client/ProviderCard.js +240 -0
  31. package/lib/client/SpeedSelect.d.ts +72 -0
  32. package/lib/client/SpeedSelect.js +214 -0
  33. package/lib/client/Usage.d.ts +7 -0
  34. package/lib/client/Usage.js +83 -0
  35. package/lib/client/VideoGenerateToolview.d.ts +42 -0
  36. package/lib/client/VideoGenerateToolview.js +172 -0
  37. package/lib/client/context.d.ts +16 -0
  38. package/lib/client/context.js +11 -0
  39. package/lib/client/fast-command.d.ts +2 -0
  40. package/lib/client/fast-command.js +4 -0
  41. package/lib/client/hooks.d.ts +2 -0
  42. package/lib/client/hooks.js +10 -0
  43. package/lib/client/index.d.ts +3 -0
  44. package/lib/client/index.js +113 -0
  45. package/lib/client/locales.d.ts +139 -0
  46. package/lib/client/locales.js +267 -0
  47. package/lib/client/meter.d.ts +91 -0
  48. package/lib/client/meter.js +196 -0
  49. package/lib/client/prefs.d.ts +51 -0
  50. package/lib/client/prefs.js +71 -0
  51. package/lib/client/rpc.d.ts +161 -0
  52. package/lib/client/rpc.js +31 -0
  53. package/lib/client/store.d.ts +67 -0
  54. package/lib/client/store.js +129 -0
  55. package/lib/client/styles.d.ts +7 -0
  56. package/lib/client/styles.js +176 -0
  57. package/lib/client.js +3933 -0
  58. package/lib/client.js.map +1 -0
  59. package/lib/compat.d.ts +55 -0
  60. package/lib/compat.js +20 -0
  61. package/lib/http.d.ts +134 -0
  62. package/lib/http.js +431 -0
  63. package/lib/index.d.ts +121 -0
  64. package/lib/index.js +13399 -0
  65. package/lib/json-text.d.ts +2 -0
  66. package/lib/json-text.js +4 -0
  67. package/lib/model-defaults.d.ts +23 -0
  68. package/lib/model-defaults.js +238 -0
  69. package/lib/model-speeds.d.ts +10 -0
  70. package/lib/model-speeds.js +48 -0
  71. package/lib/models-directory.d.ts +17 -0
  72. package/lib/models-directory.js +25 -0
  73. package/lib/provider-settings.d.ts +55 -0
  74. package/lib/provider-settings.js +215 -0
  75. package/lib/providers/account-preferences.d.ts +46 -0
  76. package/lib/providers/account-preferences.js +188 -0
  77. package/lib/providers/accounts.d.ts +113 -0
  78. package/lib/providers/accounts.js +142 -0
  79. package/lib/providers/antigravity-oauth-client.d.ts +7 -0
  80. package/lib/providers/antigravity-oauth-client.js +7 -0
  81. package/lib/providers/antigravity.d.ts +119 -0
  82. package/lib/providers/antigravity.js +552 -0
  83. package/lib/providers/catalog-refresh.d.ts +4 -0
  84. package/lib/providers/catalog-refresh.js +30 -0
  85. package/lib/providers/catalog-store.d.ts +47 -0
  86. package/lib/providers/catalog-store.js +283 -0
  87. package/lib/providers/claude-cli-version.d.ts +15 -0
  88. package/lib/providers/claude-cli-version.js +24 -0
  89. package/lib/providers/claude.d.ts +212 -0
  90. package/lib/providers/claude.js +726 -0
  91. package/lib/providers/codex-client-version.d.ts +8 -0
  92. package/lib/providers/codex-client-version.js +17 -0
  93. package/lib/providers/codex-search.d.ts +29 -0
  94. package/lib/providers/codex-search.js +198 -0
  95. package/lib/providers/codex.d.ts +253 -0
  96. package/lib/providers/codex.js +930 -0
  97. package/lib/providers/common.d.ts +415 -0
  98. package/lib/providers/common.js +679 -0
  99. package/lib/providers/copilot.d.ts +365 -0
  100. package/lib/providers/copilot.js +956 -0
  101. package/lib/providers/grok.d.ts +214 -0
  102. package/lib/providers/grok.js +750 -0
  103. package/lib/providers/image-pool.d.ts +30 -0
  104. package/lib/providers/image-pool.js +89 -0
  105. package/lib/providers/npm-cli-version.d.ts +56 -0
  106. package/lib/providers/npm-cli-version.js +98 -0
  107. package/lib/providers/pool-family.d.ts +56 -0
  108. package/lib/providers/pool-family.js +45 -0
  109. package/lib/providers/pool-health.d.ts +91 -0
  110. package/lib/providers/pool-health.js +164 -0
  111. package/lib/providers/pool-usage.d.ts +106 -0
  112. package/lib/providers/pool-usage.js +302 -0
  113. package/lib/providers/pool.d.ts +115 -0
  114. package/lib/providers/pool.js +461 -0
  115. package/lib/providers/rate-limit.d.ts +192 -0
  116. package/lib/providers/rate-limit.js +338 -0
  117. package/lib/providers/replay.d.ts +4 -0
  118. package/lib/providers/replay.js +37 -0
  119. package/lib/providers/reset-redemption.d.ts +20 -0
  120. package/lib/providers/reset-redemption.js +70 -0
  121. package/lib/tools/image-generate.d.ts +128 -0
  122. package/lib/tools/image-generate.js +438 -0
  123. package/lib/tools/registration.d.ts +16 -0
  124. package/lib/tools/registration.js +25 -0
  125. package/lib/tools/usage.d.ts +62 -0
  126. package/lib/tools/usage.js +89 -0
  127. package/lib/tools/video-generate.d.ts +89 -0
  128. package/lib/tools/video-generate.js +257 -0
  129. package/lib/tools/x-search.d.ts +58 -0
  130. package/lib/tools/x-search.js +197 -0
  131. package/lib/translate/anthropic.d.ts +180 -0
  132. package/lib/translate/anthropic.js +534 -0
  133. package/lib/translate/antigravity-images.d.ts +11 -0
  134. package/lib/translate/antigravity-images.js +62 -0
  135. package/lib/translate/antigravity-schema.d.ts +4 -0
  136. package/lib/translate/antigravity-schema.js +145 -0
  137. package/lib/translate/antigravity-thinking.d.ts +5 -0
  138. package/lib/translate/antigravity-thinking.js +44 -0
  139. package/lib/translate/antigravity.d.ts +120 -0
  140. package/lib/translate/antigravity.js +412 -0
  141. package/lib/translate/chat-completions.d.ts +120 -0
  142. package/lib/translate/chat-completions.js +382 -0
  143. package/lib/translate/resolved.d.ts +98 -0
  144. package/lib/translate/resolved.js +186 -0
  145. package/lib/translate/responses.d.ts +192 -0
  146. package/lib/translate/responses.js +443 -0
  147. package/lib/translate/sse.d.ts +21 -0
  148. package/lib/translate/sse.js +62 -0
  149. package/lib/usage/headers.d.ts +21 -0
  150. package/lib/usage/headers.js +64 -0
  151. package/lib/usage/pace.d.ts +53 -0
  152. package/lib/usage/pace.js +48 -0
  153. package/lib/usage/service.d.ts +64 -0
  154. package/lib/usage/service.js +104 -0
  155. package/lib/usage-providers/common.d.ts +23 -0
  156. package/lib/usage-providers/common.js +28 -0
  157. package/lib/usage-providers/index.d.ts +42 -0
  158. package/lib/usage-providers/index.js +71 -0
  159. package/lib/usage-providers/keys.d.ts +11 -0
  160. package/lib/usage-providers/keys.js +52 -0
  161. package/lib/usage-providers/kimi.d.ts +4 -0
  162. package/lib/usage-providers/kimi.js +52 -0
  163. package/lib/usage-providers/ollama-cloud.d.ts +4 -0
  164. package/lib/usage-providers/ollama-cloud.js +42 -0
  165. package/lib/usage-providers/zai.d.ts +5 -0
  166. package/lib/usage-providers/zai.js +53 -0
  167. package/locale/en.json +6 -0
  168. package/locale/zh.json +6 -0
  169. package/package.json +115 -4
@@ -0,0 +1,283 @@
1
+ /**
2
+ * On-disk discovered-model-catalog cache at
3
+ * `~/.dsh/plugins/subscriptions/models.json` — the durable half of each
4
+ * provider's {@link ModelCatalogCache}. One entry per provider: the last
5
+ * successfully discovered catalog with its fetch time, so capability metadata
6
+ * (reasoning efforts) survives restarts and network failures.
7
+ *
8
+ * Unlike the auth store, this file is a cache: a missing, corrupt, or
9
+ * malformed file silently reads as absent, because the next successful
10
+ * discovery rewrites it. Loads are strictly validated — a malformed entry
11
+ * passed through `resolveModel` would make the harness's metadata validation
12
+ * throw on every call, which is worse than having no fallback at all.
13
+ */
14
+ import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
15
+ import { dirname } from 'node:path';
16
+ import { dshHomePath } from '@deepseek-ai/dsh-home-paths';
17
+ import { ReasoningEffortId } from '@deepseek-ai/dsh-llm';
18
+ import { parseJsonText } from '../json-text.js';
19
+ /**
20
+ * Absolute path of the catalog store file.
21
+ * @returns `dshHomePath('plugins', 'subscriptions', 'models.json')`.
22
+ */
23
+ export function modelsFilePath() {
24
+ return dshHomePath('plugins', 'subscriptions', 'models.json');
25
+ }
26
+ /** The per-account section of the file, or undefined when absent or malformed. */
27
+ function accountSection(store, provider) {
28
+ const accounts = store.accounts;
29
+ if (typeof accounts !== 'object' || accounts === null || Array.isArray(accounts))
30
+ return undefined;
31
+ const section = accounts[provider];
32
+ if (typeof section !== 'object' || section === null || Array.isArray(section))
33
+ return undefined;
34
+ return section;
35
+ }
36
+ /** Validate one persisted reasoning block, or undefined when malformed. */
37
+ function sanitizeReasoning(value) {
38
+ if (typeof value !== 'object' || value === null)
39
+ return undefined;
40
+ const raw = value;
41
+ if (!Array.isArray(raw.efforts) || raw.efforts.length === 0)
42
+ return undefined;
43
+ const seen = new Set();
44
+ const efforts = [];
45
+ for (const entry of raw.efforts) {
46
+ if (typeof entry !== 'object' || entry === null)
47
+ return undefined;
48
+ const effort = entry;
49
+ if (typeof effort.id !== 'string' || effort.id.length === 0
50
+ || typeof effort.name !== 'string' || effort.name.length === 0
51
+ || (effort.description !== undefined && typeof effort.description !== 'string')
52
+ || seen.has(effort.id))
53
+ return undefined;
54
+ seen.add(effort.id);
55
+ efforts.push({
56
+ id: ReasoningEffortId(effort.id),
57
+ name: effort.name,
58
+ ...effort.description === undefined ? {} : { description: effort.description },
59
+ });
60
+ }
61
+ if (raw.defaultEffort !== undefined
62
+ && (typeof raw.defaultEffort !== 'string' || !seen.has(raw.defaultEffort)))
63
+ return undefined;
64
+ return {
65
+ efforts,
66
+ ...raw.defaultEffort === undefined ? {} : { defaultEffort: ReasoningEffortId(raw.defaultEffort) },
67
+ };
68
+ }
69
+ /** Validate one persisted model, or undefined when malformed. */
70
+ function sanitizeModel(value) {
71
+ if (typeof value !== 'object' || value === null)
72
+ return undefined;
73
+ const raw = value;
74
+ if (typeof raw.id !== 'string' || raw.id.length === 0
75
+ || typeof raw.name !== 'string' || raw.name.length === 0
76
+ || (raw.description !== undefined && typeof raw.description !== 'string')
77
+ || (raw.contextWindow !== undefined
78
+ && (typeof raw.contextWindow !== 'number' || !Number.isInteger(raw.contextWindow) || raw.contextWindow <= 0))
79
+ || (raw.maxContextWindow !== undefined
80
+ && (typeof raw.maxContextWindow !== 'number' || !Number.isSafeInteger(raw.maxContextWindow) || raw.maxContextWindow <= 0))
81
+ || (raw.maxOutputTokens !== undefined
82
+ && (typeof raw.maxOutputTokens !== 'number' || !Number.isSafeInteger(raw.maxOutputTokens) || raw.maxOutputTokens <= 0))
83
+ || (raw.priority !== undefined
84
+ && (typeof raw.priority !== 'number' || !Number.isFinite(raw.priority))))
85
+ return undefined;
86
+ const reasoning = raw.reasoning === undefined ? undefined : sanitizeReasoning(raw.reasoning);
87
+ if (raw.reasoning !== undefined && reasoning === undefined)
88
+ return undefined;
89
+ const thinkingType = raw.thinkingType;
90
+ if (thinkingType !== undefined && thinkingType !== 'enabled' && thinkingType !== 'adaptive')
91
+ return undefined;
92
+ const fastTier = raw.fastTier;
93
+ if (fastTier !== undefined && typeof fastTier !== 'boolean')
94
+ return undefined;
95
+ const copilotWire = raw.copilotWire;
96
+ if (copilotWire !== undefined && copilotWire !== 'chat-completions' && copilotWire !== 'responses') {
97
+ return undefined;
98
+ }
99
+ const copilotResponses = raw.copilotResponses;
100
+ if (copilotResponses !== undefined && typeof copilotResponses !== 'boolean')
101
+ return undefined;
102
+ const inputModalities = raw.inputModalities;
103
+ if (inputModalities !== undefined
104
+ && (!Array.isArray(inputModalities) || inputModalities.length === 0
105
+ || inputModalities.some(modality => modality !== 'text' && modality !== 'image')))
106
+ return undefined;
107
+ return {
108
+ id: raw.id,
109
+ name: raw.name,
110
+ ...raw.description === undefined ? {} : { description: raw.description },
111
+ ...raw.contextWindow === undefined ? {} : { contextWindow: raw.contextWindow },
112
+ ...raw.maxContextWindow === undefined ? {} : { maxContextWindow: raw.maxContextWindow },
113
+ ...raw.maxOutputTokens === undefined ? {} : { maxOutputTokens: raw.maxOutputTokens },
114
+ ...raw.priority === undefined ? {} : { priority: raw.priority },
115
+ ...reasoning === undefined ? {} : { reasoning },
116
+ ...thinkingType === undefined ? {} : { thinkingType: thinkingType },
117
+ ...fastTier === undefined ? {} : { fastTier },
118
+ ...copilotWire === undefined ? {} : { copilotWire: copilotWire },
119
+ ...copilotResponses === undefined ? {} : { copilotResponses },
120
+ ...inputModalities === undefined ? {} : { inputModalities: [...inputModalities] },
121
+ };
122
+ }
123
+ /**
124
+ * Validate one persisted snapshot. Strict: any malformed field drops the
125
+ * whole snapshot rather than repairing it — the next successful discovery
126
+ * rewrites the entry anyway.
127
+ * @param value - the raw per-provider file entry.
128
+ * @returns the validated snapshot, or undefined when unusable.
129
+ */
130
+ export function sanitizeSnapshot(value) {
131
+ if (typeof value !== 'object' || value === null)
132
+ return undefined;
133
+ const raw = value;
134
+ if (typeof raw.at !== 'number' || !Number.isFinite(raw.at))
135
+ return undefined;
136
+ if (!Array.isArray(raw.models) || raw.models.length === 0)
137
+ return undefined;
138
+ const seen = new Set();
139
+ const models = [];
140
+ for (const entry of raw.models) {
141
+ const model = sanitizeModel(entry);
142
+ if (model === undefined || seen.has(model.id))
143
+ return undefined;
144
+ seen.add(model.id);
145
+ models.push(model);
146
+ }
147
+ return { at: raw.at, models };
148
+ }
149
+ /** Read the whole file; missing or unparsable reads as an empty cache. */
150
+ async function readCatalogFile(path) {
151
+ let text;
152
+ try {
153
+ text = await readFile(path, 'utf8');
154
+ }
155
+ catch {
156
+ return {};
157
+ }
158
+ try {
159
+ // Tolerate a leading BOM, or a hand-edited cache would silently read as
160
+ // empty and the model dropdown would fall back to the static list.
161
+ const parsed = parseJsonText(text);
162
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
163
+ return {};
164
+ return parsed;
165
+ }
166
+ catch {
167
+ return {};
168
+ }
169
+ }
170
+ /**
171
+ * Windows refuses to replace a file another handle holds open without
172
+ * `FILE_SHARE_DELETE` (an indexer, antivirus scan, or a second dsh profile
173
+ * reading the cache), surfacing as a transient `EPERM`/`EBUSY`/`EACCES` on
174
+ * `rename` (issue #129). Retry briefly before giving the write up.
175
+ */
176
+ const RENAME_RETRY_CODES = new Set(['EPERM', 'EBUSY', 'EACCES']);
177
+ const RENAME_RETRIES = 5;
178
+ const RENAME_RETRY_DELAY_MS = 20;
179
+ /** `rename` with a short bounded retry on share-mode conflicts. */
180
+ async function renameWithRetry(from, to) {
181
+ for (let attempt = 0;; attempt++) {
182
+ try {
183
+ await rename(from, to);
184
+ return;
185
+ }
186
+ catch (error) {
187
+ const code = error.code;
188
+ if (attempt >= RENAME_RETRIES || code === undefined || !RENAME_RETRY_CODES.has(code))
189
+ throw error;
190
+ await new Promise(resolve => setTimeout(resolve, RENAME_RETRY_DELAY_MS * (attempt + 1)));
191
+ }
192
+ }
193
+ }
194
+ /** Persist the whole file atomically (tmp file + rename). */
195
+ async function writeCatalogFile(store, path) {
196
+ await mkdir(dirname(path), { recursive: true });
197
+ const tmp = `${path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
198
+ try {
199
+ await writeFile(tmp, JSON.stringify(store, null, 2));
200
+ await renameWithRetry(tmp, path);
201
+ }
202
+ catch (error) {
203
+ await rm(tmp, { force: true });
204
+ throw error;
205
+ }
206
+ }
207
+ /** Serialize snapshot updates so parallel account discoveries cannot erase siblings. */
208
+ const catalogWrites = new Map();
209
+ function updateCatalogFile(path, update) {
210
+ const pending = (catalogWrites.get(path) ?? Promise.resolve()).catch(() => undefined).then(async () => {
211
+ const store = await readCatalogFile(path);
212
+ if (update(store))
213
+ await writeCatalogFile(store, path);
214
+ });
215
+ catalogWrites.set(path, pending);
216
+ void pending.finally(() => { if (catalogWrites.get(path) === pending)
217
+ catalogWrites.delete(path); }).catch(() => undefined);
218
+ return pending;
219
+ }
220
+ /**
221
+ * Build the durable half of one provider's catalog cache over the shared
222
+ * models.json file. Snapshot mutations are serialized across providers/accounts.
223
+ * @param provider - the provider route keying the file entry.
224
+ * @param path - store file path; defaults to {@link modelsFilePath}.
225
+ * @returns the persistence hooks for {@link ModelCatalogCache}.
226
+ */
227
+ export function catalogStore(provider, path = modelsFilePath()) {
228
+ return {
229
+ async load() {
230
+ return sanitizeSnapshot((await readCatalogFile(path))[provider]);
231
+ },
232
+ async save(snapshot) {
233
+ await updateCatalogFile(path, store => { store[provider] = snapshot; return true; });
234
+ },
235
+ async clear() {
236
+ await updateCatalogFile(path, store => {
237
+ if (store[provider] === undefined)
238
+ return false;
239
+ delete store[provider];
240
+ return true;
241
+ });
242
+ },
243
+ };
244
+ }
245
+ /**
246
+ * The durable half of one NON-default account's catalog cache. The provider
247
+ * entry belongs to the default account; every other account keeps its own
248
+ * snapshot under `accounts[provider][account]`, so a working secondary login
249
+ * survives restarts and network failures even when the default login is dead.
250
+ * @param provider - the provider route.
251
+ * @param account - the canonical account key.
252
+ * @param path - store file path; defaults to {@link modelsFilePath}.
253
+ * @returns the persistence hooks for {@link ModelCatalogCache}.
254
+ */
255
+ export function accountCatalogStore(provider, account, path = modelsFilePath()) {
256
+ return {
257
+ async load() {
258
+ const section = accountSection(await readCatalogFile(path), provider);
259
+ return section !== undefined && Object.hasOwn(section, account) ? sanitizeSnapshot(section[account]) : undefined;
260
+ },
261
+ async save(snapshot) {
262
+ await updateCatalogFile(path, store => {
263
+ const accounts = typeof store.accounts === 'object' && store.accounts !== null && !Array.isArray(store.accounts)
264
+ ? store.accounts
265
+ : {};
266
+ const section = { ...accountSection(store, provider) ?? {}, [account]: snapshot };
267
+ store.accounts = { ...accounts, [provider]: section };
268
+ return true;
269
+ });
270
+ },
271
+ async clear() {
272
+ await updateCatalogFile(path, store => {
273
+ const section = accountSection(store, provider);
274
+ if (section === undefined || !Object.hasOwn(section, account))
275
+ return false;
276
+ const rest = { ...section };
277
+ delete rest[account];
278
+ store.accounts = { ...store.accounts, [provider]: rest };
279
+ return true;
280
+ });
281
+ },
282
+ };
283
+ }
@@ -0,0 +1,15 @@
1
+ import type { FetchFn } from './common.js';
2
+ import { NpmCliVersionCache } from './npm-cli-version.js';
3
+ /**
4
+ * Public metadata only: never send subscription credentials to this endpoint.
5
+ * `latest`, not `stable`: the endpoint gates new models on versions that
6
+ * reach `stable` about a week later.
7
+ */
8
+ export declare const CLAUDE_CLI_VERSION_URL = "https://registry.npmjs.org/@anthropic-ai%2fclaude-code/latest";
9
+ /**
10
+ * Lazy, shared-per-plugin lookup of Claude Code's newest release, never
11
+ * below the local CLI or the bundled fallback version.
12
+ */
13
+ export declare class ClaudeCliVersionCache extends NpmCliVersionCache {
14
+ constructor(fetchFn?: FetchFn, now?: () => number, timeoutMs?: number, detect?: () => string);
15
+ }
@@ -0,0 +1,24 @@
1
+ import { claudeCliVersionFloor } from './claude.js';
2
+ import { NpmCliVersionCache } from './npm-cli-version.js';
3
+ /**
4
+ * Public metadata only: never send subscription credentials to this endpoint.
5
+ * `latest`, not `stable`: the endpoint gates new models on versions that
6
+ * reach `stable` about a week later.
7
+ */
8
+ export const CLAUDE_CLI_VERSION_URL = 'https://registry.npmjs.org/@anthropic-ai%2fclaude-code/latest';
9
+ /**
10
+ * Lazy, shared-per-plugin lookup of Claude Code's newest release, never
11
+ * below the local CLI or the bundled fallback version.
12
+ */
13
+ export class ClaudeCliVersionCache extends NpmCliVersionCache {
14
+ constructor(fetchFn, now, timeoutMs, detect) {
15
+ super({
16
+ url: CLAUDE_CLI_VERSION_URL,
17
+ label: 'Claude Code',
18
+ floor: () => claudeCliVersionFloor(detect),
19
+ ...fetchFn === undefined ? {} : { fetchFn },
20
+ ...now === undefined ? {} : { now },
21
+ ...timeoutMs === undefined ? {} : { timeoutMs },
22
+ });
23
+ }
24
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Claude Pro/Max subscription provider: OAuth against claude.ai /
3
+ * platform.claude.com with the Claude Code client id, and streaming against
4
+ * the Anthropic Messages API with the Claude Code identity headers.
5
+ */
6
+ import { LlmAdapter } from '@deepseek-ai/dsh-llm';
7
+ import type { GenerateOptions, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
8
+ import type { FlowSpec } from '../auth/oauth-flow.js';
9
+ import type { ClaudeSession } from '../auth/store.js';
10
+ import type { PoolAdapter } from './pool.js';
11
+ import type { AttachmentStore } from '@deepseek-ai/dsh-attachment';
12
+ import type { TranslatableMessage } from '../translate/resolved.js';
13
+ import type { PromptCacheTtl } from '../translate/anthropic.js';
14
+ import { AccountTokenManager } from './accounts.js';
15
+ import type { CatalogPersistence, DiscoveredModel, FetchFn, ModelEntry, ProviderUsage } from './common.js';
16
+ import type { CliVersion } from './npm-cli-version.js';
17
+ import type { RateLimitResetReader, RateLimitWait } from './rate-limit.js';
18
+ export declare const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
19
+ export declare const CLAUDE_AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
20
+ export declare const CLAUDE_TOKEN_URL = "https://claude.ai/v1/oauth/token";
21
+ export declare const CLAUDE_API_URL = "https://api.anthropic.com/v1/messages?beta=true";
22
+ export declare const CLAUDE_PROFILE_URL = "https://api.anthropic.com/api/oauth/profile";
23
+ export declare const CLAUDE_MODELS_URL = "https://api.anthropic.com/v1/models?beta=true";
24
+ export declare const CLAUDE_SCOPE = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
25
+ export declare const CLAUDE_CALLBACK_PATH = "/callback";
26
+ /** Refresh when the access token has less than this much life left. */
27
+ export declare const CLAUDE_PREEMPT_MS: number;
28
+ /**
29
+ * Reads the reset instant of the Anthropic window that rejected a request.
30
+ *
31
+ * `anthropic-ratelimit-unified-*` is the subscription-plan family — the one
32
+ * Claude Code renders as "resets 3pm" — and is the only header that names the
33
+ * window which actually rejected this request. The per-bucket
34
+ * `anthropic-ratelimit-{requests,tokens,input-tokens,output-tokens}-reset`
35
+ * headers are deliberately not read: they are rollover snapshots attached to
36
+ * every response, so on a 429 they cannot say which bucket refused, and the
37
+ * earliest of them is typically the bucket that still had room — a wait that
38
+ * lands straight back in the closed window. They reach the operator through
39
+ * `rateLimitDiagnostics` instead.
40
+ */
41
+ export declare const claudeRateLimitReset: RateLimitResetReader;
42
+ /**
43
+ * The subscription endpoint only serves requests presenting as Claude Code,
44
+ * so these headers impersonate the CLI; the harness attribution user-agent
45
+ * cannot be sent here (one user-agent slot, and the CLI's wins). The endpoint
46
+ * also gates new models on the CLI version, so the version normally comes
47
+ * from npm (`ClaudeCliVersionCache`); this is only the offline floor.
48
+ */
49
+ export declare const CLAUDE_CLI_FALLBACK_VERSION = "2.1.283";
50
+ export declare function detectClaudeVersion(): string;
51
+ /**
52
+ * The lowest version to present: the local CLI or the bundled fallback,
53
+ * whichever is newer. An outdated local install must not pin requests to a
54
+ * version the endpoint already rejects for new models.
55
+ * @param detect - local CLI probe (injectable for tests).
56
+ */
57
+ export declare function claudeCliVersionFloor(detect?: () => string): CliVersion;
58
+ /** The User-Agent Claude Code sends at `version`. */
59
+ export declare function claudeCliUserAgent(version: string): string;
60
+ export declare const CLAUDE_BETA_FALLBACK: string;
61
+ /** Static claude flow facts for the OAuth flow engine. */
62
+ export declare const claudeFlow: FlowSpec;
63
+ /** Best-effort account profile; login must not fail when this does. */
64
+ export declare function fetchClaudeProfile(accessToken: string): Promise<Pick<ClaudeSession, 'emailAddress' | 'subscriptionType'>>;
65
+ /**
66
+ * Exchange an authorization code for a claude session (JSON grant).
67
+ * @param code - the authorization code from the callback.
68
+ * @param verifier - the PKCE verifier minted for the attempt.
69
+ * @param redirectUri - the attempt's redirect URI.
70
+ * @param state - the attempt's state (echoed to the token endpoint).
71
+ * @returns the session to store.
72
+ */
73
+ export declare function exchangeClaudeCode(code: string, verifier: string, redirectUri: string, state: string): Promise<ClaudeSession>;
74
+ /**
75
+ * Refresh a claude session (JSON grant echoing the issued scope).
76
+ * @param session - the stored session.
77
+ * @returns the fresh session to store.
78
+ */
79
+ export declare function refreshClaude(session: ClaudeSession): Promise<ClaudeSession>;
80
+ /**
81
+ * Whether a claude refresh failure means the login is permanently gone.
82
+ * @param error - the thrown refresh error.
83
+ * @returns true when re-login is the only fix.
84
+ */
85
+ export declare function isClaudePermanentRefreshError(error: unknown): boolean;
86
+ export declare const CLAUDE_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
87
+ /**
88
+ * Fetch the claude subscription usage from the OAuth usage endpoint (the
89
+ * source of Claude Code's `/usage` screen). Newer responses carry a
90
+ * structured `limits` array; older ones the flat `five_hour`/`seven_day*`
91
+ * buckets — both shapes are read, the array winning when it has entries.
92
+ * @param session - the stored session (used as-is; never refreshed here).
93
+ * @param fetchFn - fetch implementation (injectable for tests).
94
+ * @param signal - caller cancellation from the RPC transport.
95
+ * @param cliVersion - resolves the Claude Code version to present; defaults to the local floor.
96
+ * @returns the mapped usage snapshot.
97
+ */
98
+ export declare function fetchClaudeUsage(session: ClaudeSession, fetchFn?: FetchFn, signal?: AbortSignal, cliVersion?: () => Promise<string>): Promise<ProviderUsage>;
99
+ /**
100
+ * Fetch the live model catalog from the subscription endpoint. `signal`
101
+ * cancels the request; `cliVersion` resolves the Claude Code version to
102
+ * present (defaults to the local floor).
103
+ */
104
+ export declare function fetchClaudeModels(session: ClaudeSession, fetchFn?: FetchFn, signal?: AbortSignal, cliVersion?: () => Promise<string>): Promise<DiscoveredModel[]>;
105
+ /** Constructor dependencies for {@link ClaudeAdapter}. */
106
+ export interface ClaudeAdapterOptions {
107
+ /**
108
+ * Observes every upstream reply before its status is checked, with the
109
+ * resolved account key. Usage headers ride both 2xx and 429 replies.
110
+ */
111
+ onResponse?: (response: Response, account: string, sessionId: string | undefined) => void;
112
+ models: readonly ModelEntry[];
113
+ streamIdleTimeoutMs: number;
114
+ tokens: AccountTokenManager<ClaudeSession>;
115
+ /** Late-bound pool facade (wired after adapter construction); pools list under their first member's provider. */
116
+ pool?: () => PoolAdapter | undefined;
117
+ /** Whether to fetch the live catalog when logged in (false when config `models` overrides). */
118
+ discovery: boolean;
119
+ fetchFn?: FetchFn;
120
+ onWarn?: (message: string) => void;
121
+ /** How long this route may hold a turn open waiting for a rate-limit window; defaults to waiting on, six-hour ceiling. */
122
+ rateLimit?: RateLimitWait;
123
+ /** Resolve the attachment service per request; absent means image requests fail loudly. */
124
+ resolveAttachments?: () => AttachmentStore | undefined;
125
+ /** Durable catalog store seeding capability metadata across restarts. */
126
+ catalogStore?: CatalogPersistence;
127
+ onCatalogChanged?: () => void;
128
+ accountCatalogStore?: (account: string) => CatalogPersistence;
129
+ /**
130
+ * Per-model default reasoning effort override (the Settings page's picker).
131
+ * Returns the user-configured default for one model, or undefined to follow
132
+ * the provider's own default.
133
+ */
134
+ defaultEffortOf?: (model: string) => string | undefined;
135
+ /** Resolves the Claude Code version to present (npm-backed); absent means the local floor. */
136
+ resolveCliVersion?: () => Promise<string>;
137
+ /** Lifetime of the request's cache breakpoints; absent means Anthropic's five-minute default. */
138
+ promptCacheTtl?: PromptCacheTtl;
139
+ /**
140
+ * The user's Settings-page choice, read per request so a save applies to
141
+ * the next turn without a restart; undefined falls back to `promptCacheTtl`.
142
+ */
143
+ promptCacheTtlOverride?: () => PromptCacheTtl | undefined;
144
+ }
145
+ /**
146
+ * Inline image data a Claude request may carry, in base64 bytes. Every turn
147
+ * resends the history, so screenshots accumulate until the request passes the
148
+ * API's 32MB body limit and every later turn and compaction fails with HTTP
149
+ * 413 `request_too_large`. Past this budget the host offloads the oldest
150
+ * images to text and retries; the remaining 12MB holds prompts, tools, and text.
151
+ */
152
+ export declare const CLAUDE_REQUEST_IMAGE_BUDGET: number;
153
+ /**
154
+ * Assemble the Anthropic request body.
155
+ *
156
+ * Extracted from the adapter so the wire shape — cache breakpoints above all —
157
+ * is testable without a network round trip. The message array is marked before
158
+ * it is placed so the breakpoints land on the blocks the body ships: one on the
159
+ * last `system` block (covering `tools` + `system`, which render ahead of it)
160
+ * and up to three across the history, Anthropic's four-slot maximum.
161
+ * @param options - the generate request.
162
+ * @param messages - conversation messages with images already resolved.
163
+ * @param maxTokens - the resolved output cap.
164
+ * @param thinking - the thinking parameter, when the model takes one.
165
+ * @param effort - the reasoning effort, when the model advertises efforts.
166
+ * @param cacheTtl - lifetime of every cache breakpoint; defaults to five minutes.
167
+ * Requests that carry a `purpose` (compaction, session titles) always use five
168
+ * minutes to preserve their existing auxiliary-request behavior. A five-minute
169
+ * mark can still read an entry a one-hour mark wrote.
170
+ * @returns the JSON body to POST.
171
+ */
172
+ export declare function claudeRequestBody(options: GenerateOptions, messages: readonly TranslatableMessage[], maxTokens: number, thinking?: Record<string, unknown>, effort?: string, cacheTtl?: PromptCacheTtl): Record<string, unknown>;
173
+ /** Claude wire adapter: one instance serves the `claude` provider route. */
174
+ export declare class ClaudeAdapter extends LlmAdapter {
175
+ private readonly options;
176
+ private readonly catalog;
177
+ /** In-memory catalogs for non-default accounts (the persisted cache is the default's). */
178
+ private readonly accountCatalogs;
179
+ private accountCatalogsInvalidated;
180
+ private readonly invalidatedAccounts;
181
+ /** Account whose snapshot currently lives in {@link catalog}; cleared on default change. */
182
+ private catalogOwner;
183
+ constructor(options: ClaudeAdapterOptions);
184
+ private fetchCatalog;
185
+ /** Drop cached catalogs after login/logout so the next list does not reuse a stale plan. */
186
+ clearAccountCatalog(account?: string): void;
187
+ /** Default and secondary accounts each seed their own persisted catalog. */
188
+ private catalogFor;
189
+ private discovered;
190
+ private staticModels;
191
+ providerInfo(provider: string): LlmProviderInfo;
192
+ providerRetryPolicy(provider: string): import("@deepseek-ai/dsh-llm").ResolvedRetryPolicy;
193
+ listModels(provider: string): Promise<readonly LlmModelInfo[]>;
194
+ /** The provider's own catalog: union of every account, or one account when named. */
195
+ listOwnModels(provider: string, account?: string, signal?: AbortSignal, preferCached?: boolean): Promise<readonly LlmModelInfo[]>;
196
+ resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
197
+ /** Capability resolution of the provider's own models (the pool resolves members here). */
198
+ resolveOwnModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
199
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
200
+ /** Pool seam: stream through one specific account instead of the default. */
201
+ streamAccount(options: GenerateOptions, account: string): AsyncIterable<StreamChunk>;
202
+ private streamCore;
203
+ /**
204
+ * `display: 'summarized'` is set explicitly on both shapes: `adaptive`-type
205
+ * models default to `display: 'omitted'`, which returns thinking blocks with
206
+ * an empty `thinking` field — without this override the "Think" panel would
207
+ * always render empty even though real reasoning (and billed thinking_tokens)
208
+ * ran.
209
+ */
210
+ private thinkingParam;
211
+ private request;
212
+ }