@prestyj/core 5.1.0 → 5.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.
- package/dist/chunk-5GGCATYH.js +1805 -0
- package/dist/chunk-5GGCATYH.js.map +1 -0
- package/dist/index.cjs +718 -666
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -2
- package/dist/index.d.ts +28 -2
- package/dist/index.js +42 -1336
- package/dist/index.js.map +1 -1
- package/dist/model-registry.cjs +110 -28
- package/dist/model-registry.cjs.map +1 -1
- package/dist/model-registry.d.cts +30 -3
- package/dist/model-registry.d.ts +30 -3
- package/dist/model-registry.js +6 -1
- package/package.json +2 -2
- package/dist/chunk-GRBEFHTI.js +0 -404
- package/dist/chunk-GRBEFHTI.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -36,12 +36,15 @@ __export(index_exports, {
|
|
|
36
36
|
MOONSHOT_OAUTH_KEY: () => MOONSHOT_OAUTH_KEY,
|
|
37
37
|
NotLoggedInError: () => NotLoggedInError,
|
|
38
38
|
TelegramBot: () => TelegramBot,
|
|
39
|
+
XIAOMI_CREDITS_KEY: () => XIAOMI_CREDITS_KEY,
|
|
39
40
|
closeLogger: () => closeLogger,
|
|
40
41
|
createAutoUpdater: () => createAutoUpdater,
|
|
41
42
|
decodeOggOpus: () => decodeOggOpus,
|
|
42
43
|
downmixToMono: () => downmixToMono,
|
|
43
44
|
generatePKCE: () => generatePKCE,
|
|
44
45
|
getAppPaths: () => getAppPaths,
|
|
46
|
+
getAuthStorageKey: () => getAuthStorageKey,
|
|
47
|
+
getAuthStorageKeys: () => getAuthStorageKeys,
|
|
45
48
|
getClaudeCliUserAgent: () => getClaudeCliUserAgent,
|
|
46
49
|
getClaudeCodeVersion: () => getClaudeCodeVersion,
|
|
47
50
|
getContextWindow: () => getContextWindow,
|
|
@@ -79,457 +82,9 @@ __export(index_exports, {
|
|
|
79
82
|
});
|
|
80
83
|
module.exports = __toCommonJS(index_exports);
|
|
81
84
|
|
|
82
|
-
// src/
|
|
83
|
-
var
|
|
84
|
-
|
|
85
|
-
// NOTE: Claude Fable 5 (`claude-fable-5`) and Claude Mythos 5
|
|
86
|
-
// (`claude-mythos-5`) are temporarily unavailable, so they're commented out
|
|
87
|
-
// here to keep them out of the /model selector and avoid user confusion.
|
|
88
|
-
// Re-enable once they're generally available again.
|
|
89
|
-
// {
|
|
90
|
-
// id: "claude-fable-5",
|
|
91
|
-
// name: "Claude Fable 5",
|
|
92
|
-
// provider: "anthropic",
|
|
93
|
-
// contextWindow: 1_000_000,
|
|
94
|
-
// maxOutputTokens: 128_000,
|
|
95
|
-
// supportsThinking: true,
|
|
96
|
-
// supportsImages: true,
|
|
97
|
-
// supportsVideo: false,
|
|
98
|
-
// costTier: "high",
|
|
99
|
-
// maxThinkingLevel: "max",
|
|
100
|
-
// },
|
|
101
|
-
// {
|
|
102
|
-
// // Mythos-class model offered through Project Glasswing (limited
|
|
103
|
-
// // availability, invitation-only). Same underlying model as Fable 5 with
|
|
104
|
-
// // some safeguards lifted; kept here so approved accounts can select it.
|
|
105
|
-
// id: "claude-mythos-5",
|
|
106
|
-
// name: "Claude Mythos 5",
|
|
107
|
-
// provider: "anthropic",
|
|
108
|
-
// contextWindow: 1_000_000,
|
|
109
|
-
// maxOutputTokens: 128_000,
|
|
110
|
-
// supportsThinking: true,
|
|
111
|
-
// supportsImages: true,
|
|
112
|
-
// supportsVideo: false,
|
|
113
|
-
// costTier: "high",
|
|
114
|
-
// maxThinkingLevel: "max",
|
|
115
|
-
// },
|
|
116
|
-
{
|
|
117
|
-
id: "claude-opus-4-8",
|
|
118
|
-
name: "Claude Opus 4.8",
|
|
119
|
-
provider: "anthropic",
|
|
120
|
-
contextWindow: 1e6,
|
|
121
|
-
maxOutputTokens: 128e3,
|
|
122
|
-
supportsThinking: true,
|
|
123
|
-
supportsImages: true,
|
|
124
|
-
supportsVideo: false,
|
|
125
|
-
costTier: "high",
|
|
126
|
-
maxThinkingLevel: "max"
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
id: "claude-sonnet-4-6",
|
|
130
|
-
name: "Claude Sonnet 4.6",
|
|
131
|
-
provider: "anthropic",
|
|
132
|
-
contextWindow: 1e6,
|
|
133
|
-
maxOutputTokens: 64e3,
|
|
134
|
-
supportsThinking: true,
|
|
135
|
-
supportsImages: true,
|
|
136
|
-
supportsVideo: false,
|
|
137
|
-
costTier: "medium",
|
|
138
|
-
maxThinkingLevel: "max"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: "claude-haiku-4-5-20251001",
|
|
142
|
-
name: "Claude Haiku 4.5",
|
|
143
|
-
provider: "anthropic",
|
|
144
|
-
contextWindow: 2e5,
|
|
145
|
-
maxOutputTokens: 64e3,
|
|
146
|
-
supportsThinking: true,
|
|
147
|
-
supportsImages: true,
|
|
148
|
-
supportsVideo: false,
|
|
149
|
-
costTier: "low",
|
|
150
|
-
maxThinkingLevel: "high"
|
|
151
|
-
},
|
|
152
|
-
// ── OpenAI (Codex) ─────────────────────────────────────
|
|
153
|
-
{
|
|
154
|
-
id: "gpt-5.5",
|
|
155
|
-
name: "GPT-5.5",
|
|
156
|
-
provider: "openai",
|
|
157
|
-
contextWindow: 105e4,
|
|
158
|
-
codexContextWindow: 272e3,
|
|
159
|
-
maxOutputTokens: 128e3,
|
|
160
|
-
supportsThinking: true,
|
|
161
|
-
supportsImages: true,
|
|
162
|
-
supportsVideo: false,
|
|
163
|
-
costTier: "high",
|
|
164
|
-
maxThinkingLevel: "xhigh"
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
id: "gpt-5.4",
|
|
168
|
-
name: "GPT-5.4",
|
|
169
|
-
provider: "openai",
|
|
170
|
-
contextWindow: 105e4,
|
|
171
|
-
codexContextWindow: 272e3,
|
|
172
|
-
maxOutputTokens: 128e3,
|
|
173
|
-
supportsThinking: true,
|
|
174
|
-
supportsImages: true,
|
|
175
|
-
supportsVideo: false,
|
|
176
|
-
costTier: "high",
|
|
177
|
-
maxThinkingLevel: "xhigh"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
id: "gpt-5.4-mini",
|
|
181
|
-
name: "GPT-5.4 Mini",
|
|
182
|
-
provider: "openai",
|
|
183
|
-
contextWindow: 4e5,
|
|
184
|
-
maxOutputTokens: 128e3,
|
|
185
|
-
supportsThinking: true,
|
|
186
|
-
supportsImages: true,
|
|
187
|
-
supportsVideo: false,
|
|
188
|
-
costTier: "low",
|
|
189
|
-
maxThinkingLevel: "xhigh"
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
id: "gpt-5.3-codex",
|
|
193
|
-
name: "GPT-5.3 Codex",
|
|
194
|
-
provider: "openai",
|
|
195
|
-
contextWindow: 4e5,
|
|
196
|
-
maxOutputTokens: 128e3,
|
|
197
|
-
supportsThinking: true,
|
|
198
|
-
supportsImages: true,
|
|
199
|
-
supportsVideo: false,
|
|
200
|
-
costTier: "high",
|
|
201
|
-
maxThinkingLevel: "xhigh"
|
|
202
|
-
},
|
|
203
|
-
// ── Sakana (Fugu) ──────────────────────────────────────
|
|
204
|
-
// Sakana Fugu is a multi-agent system surfaced as a standard LLM via the
|
|
205
|
-
// OpenAI-compatible Sakana API (https://api.sakana.ai/v1). Both models take
|
|
206
|
-
// text + image input and only accept "high"/"xhigh" reasoning effort, so the
|
|
207
|
-
// top tier is `xhigh`. `fugu` routes across all providers; `fugu-ultra` is
|
|
208
|
-
// the heavier tier (may need larger client timeouts on complex tasks).
|
|
209
|
-
{
|
|
210
|
-
id: "fugu",
|
|
211
|
-
name: "Fugu",
|
|
212
|
-
provider: "sakana",
|
|
213
|
-
contextWindow: 1e6,
|
|
214
|
-
maxOutputTokens: 128e3,
|
|
215
|
-
supportsThinking: true,
|
|
216
|
-
supportsImages: true,
|
|
217
|
-
supportsVideo: false,
|
|
218
|
-
costTier: "medium",
|
|
219
|
-
maxThinkingLevel: "xhigh"
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
id: "fugu-ultra",
|
|
223
|
-
name: "Fugu Ultra",
|
|
224
|
-
provider: "sakana",
|
|
225
|
-
contextWindow: 1e6,
|
|
226
|
-
maxOutputTokens: 128e3,
|
|
227
|
-
supportsThinking: true,
|
|
228
|
-
supportsImages: true,
|
|
229
|
-
supportsVideo: false,
|
|
230
|
-
costTier: "high",
|
|
231
|
-
maxThinkingLevel: "xhigh"
|
|
232
|
-
},
|
|
233
|
-
// ── Gemini ─────────────────────────────────────────────
|
|
234
|
-
{
|
|
235
|
-
id: "gemini-3.1-flash-lite-preview",
|
|
236
|
-
name: "Gemini 3.1 Flash Lite Preview",
|
|
237
|
-
provider: "gemini",
|
|
238
|
-
contextWindow: 1048576,
|
|
239
|
-
maxOutputTokens: 65536,
|
|
240
|
-
supportsThinking: true,
|
|
241
|
-
supportsImages: true,
|
|
242
|
-
supportsVideo: true,
|
|
243
|
-
maxVideoBytes: 20 * 1024 * 1024,
|
|
244
|
-
costTier: "low",
|
|
245
|
-
maxThinkingLevel: "high"
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
id: "gemini-3.5-flash",
|
|
249
|
-
name: "Gemini 3.5 Flash",
|
|
250
|
-
provider: "gemini",
|
|
251
|
-
contextWindow: 1048576,
|
|
252
|
-
maxOutputTokens: 65536,
|
|
253
|
-
supportsThinking: true,
|
|
254
|
-
supportsImages: true,
|
|
255
|
-
supportsVideo: true,
|
|
256
|
-
maxVideoBytes: 20 * 1024 * 1024,
|
|
257
|
-
costTier: "low",
|
|
258
|
-
maxThinkingLevel: "high"
|
|
259
|
-
},
|
|
260
|
-
// ── Moonshot (Kimi) ────────────────────────────────────
|
|
261
|
-
{
|
|
262
|
-
id: "kimi-k2.7-code",
|
|
263
|
-
name: "Kimi K2.7",
|
|
264
|
-
provider: "moonshot",
|
|
265
|
-
contextWindow: 262144,
|
|
266
|
-
maxOutputTokens: 262144,
|
|
267
|
-
supportsThinking: true,
|
|
268
|
-
supportsImages: true,
|
|
269
|
-
supportsVideo: true,
|
|
270
|
-
maxVideoBytes: 100 * 1024 * 1024,
|
|
271
|
-
costTier: "medium",
|
|
272
|
-
maxThinkingLevel: "high"
|
|
273
|
-
},
|
|
274
|
-
// ── Z.AI (GLM) ─────────────────────────────────────────
|
|
275
|
-
// GLM-5.2: coding-first flagship with a usable 1M-token context window
|
|
276
|
-
// (5x jump over GLM-5.1's ~200K) and 131K max output. Released 2026-06-13.
|
|
277
|
-
{
|
|
278
|
-
id: "glm-5.2",
|
|
279
|
-
name: "GLM-5.2",
|
|
280
|
-
provider: "glm",
|
|
281
|
-
contextWindow: 1e6,
|
|
282
|
-
maxOutputTokens: 131072,
|
|
283
|
-
supportsThinking: true,
|
|
284
|
-
supportsImages: false,
|
|
285
|
-
supportsVideo: false,
|
|
286
|
-
costTier: "medium",
|
|
287
|
-
maxThinkingLevel: "high"
|
|
288
|
-
},
|
|
289
|
-
{
|
|
290
|
-
id: "glm-5.1",
|
|
291
|
-
name: "GLM-5.1",
|
|
292
|
-
provider: "glm",
|
|
293
|
-
contextWindow: 204800,
|
|
294
|
-
maxOutputTokens: 131072,
|
|
295
|
-
supportsThinking: true,
|
|
296
|
-
supportsImages: false,
|
|
297
|
-
supportsVideo: false,
|
|
298
|
-
costTier: "medium",
|
|
299
|
-
maxThinkingLevel: "high"
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
id: "glm-4.7",
|
|
303
|
-
name: "GLM-4.7",
|
|
304
|
-
provider: "glm",
|
|
305
|
-
contextWindow: 2e5,
|
|
306
|
-
maxOutputTokens: 131072,
|
|
307
|
-
supportsThinking: true,
|
|
308
|
-
supportsImages: false,
|
|
309
|
-
supportsVideo: false,
|
|
310
|
-
costTier: "low",
|
|
311
|
-
maxThinkingLevel: "high"
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
id: "glm-4.7-flash",
|
|
315
|
-
name: "GLM-4.7 Flash",
|
|
316
|
-
provider: "glm",
|
|
317
|
-
contextWindow: 2e5,
|
|
318
|
-
maxOutputTokens: 131072,
|
|
319
|
-
supportsThinking: true,
|
|
320
|
-
supportsImages: false,
|
|
321
|
-
supportsVideo: false,
|
|
322
|
-
costTier: "low",
|
|
323
|
-
maxThinkingLevel: "high"
|
|
324
|
-
},
|
|
325
|
-
// ── MiniMax ────────────────────────────────────────────
|
|
326
|
-
{
|
|
327
|
-
id: "MiniMax-M3",
|
|
328
|
-
name: "MiniMax M3",
|
|
329
|
-
provider: "minimax",
|
|
330
|
-
contextWindow: 1e6,
|
|
331
|
-
maxOutputTokens: 131072,
|
|
332
|
-
supportsThinking: true,
|
|
333
|
-
supportsImages: true,
|
|
334
|
-
supportsVideo: true,
|
|
335
|
-
maxVideoBytes: 50 * 1024 * 1024,
|
|
336
|
-
costTier: "medium",
|
|
337
|
-
maxThinkingLevel: "high"
|
|
338
|
-
},
|
|
339
|
-
// ── Xiaomi (MiMo) ──────────────────────────────────────
|
|
340
|
-
// Pro series: text-only coding/agentic flagship. The legacy mimo-v2-pro
|
|
341
|
-
// auto-routes to v2.5 on 2026-06-01 and is fully deprecated by 2026-06-30.
|
|
342
|
-
{
|
|
343
|
-
id: "mimo-v2.5-pro",
|
|
344
|
-
name: "MiMo-V2.5-Pro",
|
|
345
|
-
provider: "xiaomi",
|
|
346
|
-
contextWindow: 1e6,
|
|
347
|
-
maxOutputTokens: 131072,
|
|
348
|
-
supportsThinking: true,
|
|
349
|
-
supportsImages: false,
|
|
350
|
-
supportsVideo: false,
|
|
351
|
-
costTier: "medium",
|
|
352
|
-
maxThinkingLevel: "high"
|
|
353
|
-
},
|
|
354
|
-
// UltraSpeed: latency-optimized Pro variant (beta). Same text-only coding
|
|
355
|
-
// profile as mimo-v2.5-pro, tuned for faster time-to-first-token.
|
|
356
|
-
{
|
|
357
|
-
id: "mimo-v2.5-pro-ultraspeed",
|
|
358
|
-
name: "MiMo-V2.5-Pro UltraSpeed",
|
|
359
|
-
provider: "xiaomi",
|
|
360
|
-
contextWindow: 1e6,
|
|
361
|
-
maxOutputTokens: 131072,
|
|
362
|
-
supportsThinking: true,
|
|
363
|
-
supportsImages: false,
|
|
364
|
-
supportsVideo: false,
|
|
365
|
-
costTier: "medium",
|
|
366
|
-
maxThinkingLevel: "high"
|
|
367
|
-
},
|
|
368
|
-
// Omni series: native full-modal understanding (image + audio + video).
|
|
369
|
-
// Video/image ride the OpenAI-compatible transport as base64 data URLs
|
|
370
|
-
// (`video_url`/`image_url`), which the shared transform already emits.
|
|
371
|
-
{
|
|
372
|
-
id: "mimo-v2.5",
|
|
373
|
-
name: "MiMo-V2.5",
|
|
374
|
-
provider: "xiaomi",
|
|
375
|
-
contextWindow: 1e6,
|
|
376
|
-
maxOutputTokens: 131072,
|
|
377
|
-
supportsThinking: true,
|
|
378
|
-
supportsImages: true,
|
|
379
|
-
supportsVideo: true,
|
|
380
|
-
maxVideoBytes: 36 * 1024 * 1024,
|
|
381
|
-
costTier: "medium",
|
|
382
|
-
maxThinkingLevel: "high"
|
|
383
|
-
},
|
|
384
|
-
// ── DeepSeek ───────────────────────────────────────────
|
|
385
|
-
{
|
|
386
|
-
id: "deepseek-v4-pro",
|
|
387
|
-
name: "DeepSeek V4 Pro",
|
|
388
|
-
provider: "deepseek",
|
|
389
|
-
contextWindow: 1048576,
|
|
390
|
-
maxOutputTokens: 384e3,
|
|
391
|
-
supportsThinking: true,
|
|
392
|
-
supportsImages: false,
|
|
393
|
-
supportsVideo: false,
|
|
394
|
-
costTier: "high",
|
|
395
|
-
// DeepSeek V4 maps `xhigh` → its internal `max` tier.
|
|
396
|
-
maxThinkingLevel: "xhigh"
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
id: "deepseek-v4-flash",
|
|
400
|
-
name: "DeepSeek V4 Flash",
|
|
401
|
-
provider: "deepseek",
|
|
402
|
-
contextWindow: 1048576,
|
|
403
|
-
maxOutputTokens: 384e3,
|
|
404
|
-
supportsThinking: true,
|
|
405
|
-
supportsImages: false,
|
|
406
|
-
supportsVideo: false,
|
|
407
|
-
costTier: "low",
|
|
408
|
-
maxThinkingLevel: "xhigh"
|
|
409
|
-
},
|
|
410
|
-
// ── OpenRouter ─────────────────────────────────────────
|
|
411
|
-
{
|
|
412
|
-
id: "qwen/qwen3.6-plus",
|
|
413
|
-
name: "Qwen3.6-Plus",
|
|
414
|
-
provider: "openrouter",
|
|
415
|
-
contextWindow: 1e6,
|
|
416
|
-
maxOutputTokens: 65536,
|
|
417
|
-
supportsThinking: true,
|
|
418
|
-
supportsImages: false,
|
|
419
|
-
supportsVideo: false,
|
|
420
|
-
costTier: "medium",
|
|
421
|
-
maxThinkingLevel: "high"
|
|
422
|
-
}
|
|
423
|
-
];
|
|
424
|
-
function getModel(id) {
|
|
425
|
-
return MODELS.find((m) => m.id === id);
|
|
426
|
-
}
|
|
427
|
-
function getModelsForProvider(provider) {
|
|
428
|
-
return MODELS.filter((m) => m.provider === provider);
|
|
429
|
-
}
|
|
430
|
-
var DEFAULT_MAX_VIDEO_BYTES = 20 * 1024 * 1024;
|
|
431
|
-
function getVideoByteLimit(modelId) {
|
|
432
|
-
const model = getModel(modelId);
|
|
433
|
-
if (!model?.supportsVideo) return void 0;
|
|
434
|
-
return model.maxVideoBytes ?? DEFAULT_MAX_VIDEO_BYTES;
|
|
435
|
-
}
|
|
436
|
-
function getDefaultModel(provider) {
|
|
437
|
-
if (provider === "xiaomi") return MODELS.find((m) => m.id === "mimo-v2.5-pro");
|
|
438
|
-
if (provider === "openai") return MODELS.find((m) => m.id === "gpt-5.5");
|
|
439
|
-
if (provider === "gemini") return MODELS.find((m) => m.id === "gemini-3.1-flash-lite-preview");
|
|
440
|
-
if (provider === "glm") return MODELS.find((m) => m.id === "glm-5.2");
|
|
441
|
-
if (provider === "moonshot") return MODELS.find((m) => m.id === "kimi-k2.7-code");
|
|
442
|
-
if (provider === "minimax") return MODELS.find((m) => m.id === "MiniMax-M3");
|
|
443
|
-
if (provider === "deepseek") return MODELS.find((m) => m.id === "deepseek-v4-pro");
|
|
444
|
-
if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
|
|
445
|
-
if (provider === "sakana") return MODELS.find((m) => m.id === "fugu");
|
|
446
|
-
return MODELS.find((m) => m.id === "claude-sonnet-4-6");
|
|
447
|
-
}
|
|
448
|
-
function usesOpenAICodexTransport(options) {
|
|
449
|
-
return options?.provider === "openai" && Boolean(options.accountId);
|
|
450
|
-
}
|
|
451
|
-
function getContextWindow(modelId, options) {
|
|
452
|
-
const model = getModel(modelId);
|
|
453
|
-
if (!model) return 2e5;
|
|
454
|
-
if (usesOpenAICodexTransport(options) && model.codexContextWindow) {
|
|
455
|
-
return model.codexContextWindow;
|
|
456
|
-
}
|
|
457
|
-
return model.contextWindow;
|
|
458
|
-
}
|
|
459
|
-
function getMaxThinkingLevel(modelId) {
|
|
460
|
-
return getModel(modelId)?.maxThinkingLevel ?? "high";
|
|
461
|
-
}
|
|
462
|
-
function getSummaryModel(provider, currentModelId) {
|
|
463
|
-
if (provider === "anthropic") {
|
|
464
|
-
return MODELS.find((m) => m.id === "claude-sonnet-4-6");
|
|
465
|
-
}
|
|
466
|
-
if (provider === "openai" || provider === "glm" || provider === "deepseek") {
|
|
467
|
-
const low = getModelsForProvider(provider).find((m) => m.costTier === "low");
|
|
468
|
-
if (low) return low;
|
|
469
|
-
}
|
|
470
|
-
return getModel(currentModelId) ?? getDefaultModel(provider);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// src/thinking-level.ts
|
|
474
|
-
var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
|
|
475
|
-
var SAKANA_THINKING_LEVELS = ["high", "xhigh"];
|
|
476
|
-
var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
|
|
477
|
-
"low",
|
|
478
|
-
"medium",
|
|
479
|
-
"high",
|
|
480
|
-
"xhigh",
|
|
481
|
-
"max"
|
|
482
|
-
];
|
|
483
|
-
var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
|
|
484
|
-
"low",
|
|
485
|
-
"medium",
|
|
486
|
-
"high",
|
|
487
|
-
"max"
|
|
488
|
-
];
|
|
489
|
-
function isOpenAIGptModel(provider, model) {
|
|
490
|
-
return provider === "openai" && model.startsWith("gpt-");
|
|
491
|
-
}
|
|
492
|
-
function isSakanaModel(provider) {
|
|
493
|
-
return provider === "sakana";
|
|
494
|
-
}
|
|
495
|
-
function isAnthropicOpus48Or47Model(provider, model) {
|
|
496
|
-
return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
|
|
497
|
-
}
|
|
498
|
-
function isAnthropicAdaptiveModel(provider, model) {
|
|
499
|
-
return provider === "anthropic" && /opus-4-8|opus-4-7|opus-4-6|sonnet-4-6|fable-5|mythos-5/.test(model);
|
|
500
|
-
}
|
|
501
|
-
function getSupportedThinkingLevels(provider, model) {
|
|
502
|
-
const maxLevel = getMaxThinkingLevel(model);
|
|
503
|
-
if (isAnthropicAdaptiveModel(provider, model)) {
|
|
504
|
-
const levels = isAnthropicOpus48Or47Model(provider, model) ? ANTHROPIC_OPUS_48_47_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
|
|
505
|
-
const maxIndex2 = levels.indexOf(maxLevel);
|
|
506
|
-
if (maxIndex2 === -1) return ["low", "medium", "high"];
|
|
507
|
-
return levels.slice(0, maxIndex2 + 1);
|
|
508
|
-
}
|
|
509
|
-
if (isSakanaModel(provider)) {
|
|
510
|
-
const maxIndex2 = SAKANA_THINKING_LEVELS.indexOf(maxLevel);
|
|
511
|
-
if (maxIndex2 === -1) return SAKANA_THINKING_LEVELS;
|
|
512
|
-
return SAKANA_THINKING_LEVELS.slice(0, maxIndex2 + 1);
|
|
513
|
-
}
|
|
514
|
-
if (!isOpenAIGptModel(provider, model)) return [maxLevel];
|
|
515
|
-
const maxIndex = OPENAI_GPT_THINKING_LEVELS.indexOf(maxLevel);
|
|
516
|
-
if (maxIndex === -1) return ["medium"];
|
|
517
|
-
return OPENAI_GPT_THINKING_LEVELS.slice(0, maxIndex + 1);
|
|
518
|
-
}
|
|
519
|
-
function isThinkingLevelSupported(provider, model, level) {
|
|
520
|
-
return getSupportedThinkingLevels(provider, model).includes(level);
|
|
521
|
-
}
|
|
522
|
-
function getNextThinkingLevel(provider, model, current) {
|
|
523
|
-
const supportedLevels = getSupportedThinkingLevels(provider, model);
|
|
524
|
-
const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider);
|
|
525
|
-
if (!shouldCycleLevels) {
|
|
526
|
-
return current ? void 0 : supportedLevels[0];
|
|
527
|
-
}
|
|
528
|
-
if (!current) return supportedLevels[0];
|
|
529
|
-
const index = supportedLevels.indexOf(current);
|
|
530
|
-
if (index === -1) return supportedLevels[0];
|
|
531
|
-
return supportedLevels[index + 1];
|
|
532
|
-
}
|
|
85
|
+
// src/auth-storage.ts
|
|
86
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
87
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
533
88
|
|
|
534
89
|
// src/paths.ts
|
|
535
90
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -552,6 +107,31 @@ function getAppPaths() {
|
|
|
552
107
|
};
|
|
553
108
|
}
|
|
554
109
|
|
|
110
|
+
// src/oauth/anthropic.ts
|
|
111
|
+
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
112
|
+
|
|
113
|
+
// src/oauth/pkce.ts
|
|
114
|
+
function base64urlEncode(bytes) {
|
|
115
|
+
let binary = "";
|
|
116
|
+
for (const byte of bytes) {
|
|
117
|
+
binary += String.fromCharCode(byte);
|
|
118
|
+
}
|
|
119
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
120
|
+
}
|
|
121
|
+
async function generatePKCE() {
|
|
122
|
+
const verifierBytes = new Uint8Array(32);
|
|
123
|
+
crypto.getRandomValues(verifierBytes);
|
|
124
|
+
const verifier = base64urlEncode(verifierBytes);
|
|
125
|
+
const data = new TextEncoder().encode(verifier);
|
|
126
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
127
|
+
const challenge = base64urlEncode(new Uint8Array(hashBuffer));
|
|
128
|
+
return { verifier, challenge };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/claude-code-version.ts
|
|
132
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
133
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
134
|
+
|
|
555
135
|
// src/logger.ts
|
|
556
136
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
557
137
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
@@ -609,91 +189,27 @@ function log(level, category, message, data) {
|
|
|
609
189
|
if (pairs) line += ` ${pairs}`;
|
|
610
190
|
}
|
|
611
191
|
line += "\n";
|
|
612
|
-
try {
|
|
613
|
-
import_node_fs.default.writeSync(fd, line);
|
|
614
|
-
} catch {
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
function registerLogCleanup(fn) {
|
|
618
|
-
cleanups.push(fn);
|
|
619
|
-
}
|
|
620
|
-
function closeLogger(opts) {
|
|
621
|
-
if (fd === null) return;
|
|
622
|
-
if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
|
|
623
|
-
try {
|
|
624
|
-
import_node_fs.default.closeSync(fd);
|
|
625
|
-
} catch {
|
|
626
|
-
}
|
|
627
|
-
fd = null;
|
|
628
|
-
for (const unsub of cleanups) unsub();
|
|
629
|
-
cleanups = [];
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// src/file-lock.ts
|
|
633
|
-
var import_promises = __toESM(require("fs/promises"), 1);
|
|
634
|
-
var import_promises2 = require("timers/promises");
|
|
635
|
-
var STALE_TIMEOUT_MS = 1e4;
|
|
636
|
-
var RETRY_INTERVAL_MS = 50;
|
|
637
|
-
var MAX_WAIT_MS = 5e3;
|
|
638
|
-
async function withFileLock(filePath, fn) {
|
|
639
|
-
const lockPath = filePath + ".lock";
|
|
640
|
-
await acquireLock(lockPath);
|
|
641
|
-
try {
|
|
642
|
-
return await fn();
|
|
643
|
-
} finally {
|
|
644
|
-
await releaseLock(lockPath);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
async function acquireLock(lockPath) {
|
|
648
|
-
const startTime = Date.now();
|
|
649
|
-
while (true) {
|
|
650
|
-
try {
|
|
651
|
-
const info = { pid: process.pid, timestamp: Date.now() };
|
|
652
|
-
await import_promises.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
|
|
653
|
-
return;
|
|
654
|
-
} catch (err) {
|
|
655
|
-
if (err.code !== "EEXIST") throw err;
|
|
656
|
-
try {
|
|
657
|
-
const content = await import_promises.default.readFile(lockPath, "utf-8");
|
|
658
|
-
const info = JSON.parse(content);
|
|
659
|
-
const isProcessAlive = isAlive(info.pid);
|
|
660
|
-
const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
|
|
661
|
-
if (!isProcessAlive || isStale) {
|
|
662
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
663
|
-
});
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
} catch {
|
|
667
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
668
|
-
});
|
|
669
|
-
continue;
|
|
670
|
-
}
|
|
671
|
-
if (Date.now() - startTime > MAX_WAIT_MS) {
|
|
672
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
673
|
-
});
|
|
674
|
-
continue;
|
|
675
|
-
}
|
|
676
|
-
await (0, import_promises2.setTimeout)(RETRY_INTERVAL_MS);
|
|
677
|
-
}
|
|
192
|
+
try {
|
|
193
|
+
import_node_fs.default.writeSync(fd, line);
|
|
194
|
+
} catch {
|
|
678
195
|
}
|
|
679
196
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
});
|
|
197
|
+
function registerLogCleanup(fn) {
|
|
198
|
+
cleanups.push(fn);
|
|
683
199
|
}
|
|
684
|
-
function
|
|
200
|
+
function closeLogger(opts) {
|
|
201
|
+
if (fd === null) return;
|
|
202
|
+
if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
|
|
685
203
|
try {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
} catch (err) {
|
|
689
|
-
if (err.code === "EPERM") return true;
|
|
690
|
-
return false;
|
|
204
|
+
import_node_fs.default.closeSync(fd);
|
|
205
|
+
} catch {
|
|
691
206
|
}
|
|
207
|
+
fd = null;
|
|
208
|
+
for (const unsub of cleanups) unsub();
|
|
209
|
+
cleanups = [];
|
|
692
210
|
}
|
|
693
211
|
|
|
694
212
|
// src/claude-code-version.ts
|
|
695
|
-
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
696
|
-
var import_node_path3 = __toESM(require("path"), 1);
|
|
697
213
|
var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
|
|
698
214
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
699
215
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
@@ -705,7 +221,7 @@ function cachePath() {
|
|
|
705
221
|
}
|
|
706
222
|
async function readDiskCache() {
|
|
707
223
|
try {
|
|
708
|
-
const raw = await
|
|
224
|
+
const raw = await import_promises.default.readFile(cachePath(), "utf-8");
|
|
709
225
|
const parsed = JSON.parse(raw);
|
|
710
226
|
if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
|
|
711
227
|
return parsed;
|
|
@@ -717,8 +233,8 @@ async function readDiskCache() {
|
|
|
717
233
|
}
|
|
718
234
|
async function writeDiskCache(data) {
|
|
719
235
|
try {
|
|
720
|
-
await
|
|
721
|
-
await
|
|
236
|
+
await import_promises.default.mkdir(getAppPaths().agentDir, { recursive: true, mode: 448 });
|
|
237
|
+
await import_promises.default.writeFile(cachePath(), JSON.stringify(data), { mode: 384 });
|
|
722
238
|
} catch (err) {
|
|
723
239
|
log(
|
|
724
240
|
"WARN",
|
|
@@ -782,31 +298,6 @@ async function getClaudeCliUserAgent() {
|
|
|
782
298
|
return `claude-cli/${version} (external, cli)`;
|
|
783
299
|
}
|
|
784
300
|
|
|
785
|
-
// src/auth-storage.ts
|
|
786
|
-
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
787
|
-
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
788
|
-
|
|
789
|
-
// src/oauth/anthropic.ts
|
|
790
|
-
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
791
|
-
|
|
792
|
-
// src/oauth/pkce.ts
|
|
793
|
-
function base64urlEncode(bytes) {
|
|
794
|
-
let binary = "";
|
|
795
|
-
for (const byte of bytes) {
|
|
796
|
-
binary += String.fromCharCode(byte);
|
|
797
|
-
}
|
|
798
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
799
|
-
}
|
|
800
|
-
async function generatePKCE() {
|
|
801
|
-
const verifierBytes = new Uint8Array(32);
|
|
802
|
-
crypto.getRandomValues(verifierBytes);
|
|
803
|
-
const verifier = base64urlEncode(verifierBytes);
|
|
804
|
-
const data = new TextEncoder().encode(verifier);
|
|
805
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
806
|
-
const challenge = base64urlEncode(new Uint8Array(hashBuffer));
|
|
807
|
-
return { verifier, challenge };
|
|
808
|
-
}
|
|
809
|
-
|
|
810
301
|
// src/oauth/anthropic.ts
|
|
811
302
|
var CLIENT_ID = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
812
303
|
var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
@@ -1657,8 +1148,71 @@ async function refreshKimiToken(refreshToken) {
|
|
|
1657
1148
|
throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
|
|
1658
1149
|
}
|
|
1659
1150
|
|
|
1151
|
+
// src/file-lock.ts
|
|
1152
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
1153
|
+
var import_promises3 = require("timers/promises");
|
|
1154
|
+
var STALE_TIMEOUT_MS = 1e4;
|
|
1155
|
+
var RETRY_INTERVAL_MS = 50;
|
|
1156
|
+
var MAX_WAIT_MS = 5e3;
|
|
1157
|
+
async function withFileLock(filePath, fn) {
|
|
1158
|
+
const lockPath = filePath + ".lock";
|
|
1159
|
+
await acquireLock(lockPath);
|
|
1160
|
+
try {
|
|
1161
|
+
return await fn();
|
|
1162
|
+
} finally {
|
|
1163
|
+
await releaseLock(lockPath);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
async function acquireLock(lockPath) {
|
|
1167
|
+
const startTime = Date.now();
|
|
1168
|
+
while (true) {
|
|
1169
|
+
try {
|
|
1170
|
+
const info = { pid: process.pid, timestamp: Date.now() };
|
|
1171
|
+
await import_promises2.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
|
|
1172
|
+
return;
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
if (err.code !== "EEXIST") throw err;
|
|
1175
|
+
try {
|
|
1176
|
+
const content = await import_promises2.default.readFile(lockPath, "utf-8");
|
|
1177
|
+
const info = JSON.parse(content);
|
|
1178
|
+
const isProcessAlive = isAlive(info.pid);
|
|
1179
|
+
const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
|
|
1180
|
+
if (!isProcessAlive || isStale) {
|
|
1181
|
+
await import_promises2.default.unlink(lockPath).catch(() => {
|
|
1182
|
+
});
|
|
1183
|
+
continue;
|
|
1184
|
+
}
|
|
1185
|
+
} catch {
|
|
1186
|
+
await import_promises2.default.unlink(lockPath).catch(() => {
|
|
1187
|
+
});
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
if (Date.now() - startTime > MAX_WAIT_MS) {
|
|
1191
|
+
await import_promises2.default.unlink(lockPath).catch(() => {
|
|
1192
|
+
});
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
await (0, import_promises3.setTimeout)(RETRY_INTERVAL_MS);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
async function releaseLock(lockPath) {
|
|
1200
|
+
await import_promises2.default.unlink(lockPath).catch(() => {
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
function isAlive(pid) {
|
|
1204
|
+
try {
|
|
1205
|
+
process.kill(pid, 0);
|
|
1206
|
+
return true;
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
if (err.code === "EPERM") return true;
|
|
1209
|
+
return false;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1660
1213
|
// src/auth-storage.ts
|
|
1661
1214
|
var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
|
|
1215
|
+
var XIAOMI_CREDITS_KEY = "xiaomi-credits";
|
|
1662
1216
|
var REFRESH_SKEW_MS = 6e4;
|
|
1663
1217
|
var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
|
|
1664
1218
|
"glm",
|
|
@@ -1692,6 +1246,17 @@ var AuthStorage = class {
|
|
|
1692
1246
|
await this.ensureLoaded();
|
|
1693
1247
|
return Boolean(this.data[provider]);
|
|
1694
1248
|
}
|
|
1249
|
+
/**
|
|
1250
|
+
* First key in `keys` (in order) that has stored credentials, or `undefined`
|
|
1251
|
+
* if none do. Mirrors the first-match logic `resolveCredentials({ storageKeys })`
|
|
1252
|
+
* uses internally — callers that need to know WHICH credential will actually
|
|
1253
|
+
* be used (e.g. to clear the right one after a 401) call this directly
|
|
1254
|
+
* instead of re-deriving the same order.
|
|
1255
|
+
*/
|
|
1256
|
+
async pickStorageKey(keys) {
|
|
1257
|
+
await this.ensureLoaded();
|
|
1258
|
+
return keys.find((key) => Boolean(this.data[key]));
|
|
1259
|
+
}
|
|
1695
1260
|
/**
|
|
1696
1261
|
* True if the user has any usable auth for the logical provider. For
|
|
1697
1262
|
* `moonshot` this is satisfied by either the Kimi OAuth credential or the
|
|
@@ -1702,6 +1267,9 @@ var AuthStorage = class {
|
|
|
1702
1267
|
if (provider === "moonshot") {
|
|
1703
1268
|
return Boolean(this.data[MOONSHOT_OAUTH_KEY] || this.data["moonshot"]);
|
|
1704
1269
|
}
|
|
1270
|
+
if (provider === "xiaomi") {
|
|
1271
|
+
return Boolean(this.data["xiaomi"] || this.data[XIAOMI_CREDITS_KEY]);
|
|
1272
|
+
}
|
|
1705
1273
|
return Boolean(this.data[provider]);
|
|
1706
1274
|
}
|
|
1707
1275
|
/**
|
|
@@ -1744,6 +1312,17 @@ var AuthStorage = class {
|
|
|
1744
1312
|
async ensureLoaded() {
|
|
1745
1313
|
if (!this.loaded) await this.load();
|
|
1746
1314
|
}
|
|
1315
|
+
/**
|
|
1316
|
+
* Force a re-read from disk, discarding the in-memory cache. Needed when
|
|
1317
|
+
* another process mutates the auth file out-of-band — e.g. the desktop app
|
|
1318
|
+
* writes API keys natively (Rust → ~/.ezcoder/auth.json) without going through
|
|
1319
|
+
* this instance, so a long-lived daemon's cache would otherwise stay stale and
|
|
1320
|
+
* never see a newly added provider key.
|
|
1321
|
+
*/
|
|
1322
|
+
async reload() {
|
|
1323
|
+
this.loaded = false;
|
|
1324
|
+
await this.load();
|
|
1325
|
+
}
|
|
1747
1326
|
async getCredentials(provider) {
|
|
1748
1327
|
await this.ensureLoaded();
|
|
1749
1328
|
return this.data[provider];
|
|
@@ -1758,123 +1337,593 @@ var AuthStorage = class {
|
|
|
1758
1337
|
delete this.data[provider];
|
|
1759
1338
|
await this.save();
|
|
1760
1339
|
}
|
|
1761
|
-
async clearAll() {
|
|
1762
|
-
this.data = {};
|
|
1763
|
-
await this.save();
|
|
1340
|
+
async clearAll() {
|
|
1341
|
+
this.data = {};
|
|
1342
|
+
await this.save();
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Returns valid credentials, auto-refreshing if expired.
|
|
1346
|
+
* If `forceRefresh` is true, refreshes even if the token hasn't expired
|
|
1347
|
+
* (useful when the provider rejects a token with 401 before its stored expiry).
|
|
1348
|
+
* Throws if not logged in.
|
|
1349
|
+
*/
|
|
1350
|
+
async resolveCredentials(provider, opts) {
|
|
1351
|
+
await this.ensureLoaded();
|
|
1352
|
+
if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
|
|
1353
|
+
for (const key of opts.storageKeys) {
|
|
1354
|
+
const creds2 = this.data[key];
|
|
1355
|
+
if (creds2) return creds2;
|
|
1356
|
+
}
|
|
1357
|
+
throw new NotLoggedInError(provider);
|
|
1358
|
+
}
|
|
1359
|
+
if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
|
|
1360
|
+
try {
|
|
1361
|
+
return await this.resolveCredentials(MOONSHOT_OAUTH_KEY, opts);
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
if (err instanceof NotLoggedInError && this.data["moonshot"]) {
|
|
1364
|
+
log(
|
|
1365
|
+
"WARN",
|
|
1366
|
+
"auth",
|
|
1367
|
+
'Kimi OAuth credential is no longer valid \u2014 falling back to the Moonshot API key. Run "ezcoder login" and choose Kimi OAuth to restore OAuth auth.'
|
|
1368
|
+
);
|
|
1369
|
+
return this.data["moonshot"];
|
|
1370
|
+
}
|
|
1371
|
+
throw err;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
const creds = this.data[provider];
|
|
1375
|
+
if (!creds) {
|
|
1376
|
+
throw new NotLoggedInError(provider);
|
|
1377
|
+
}
|
|
1378
|
+
if (STATIC_API_KEY_PROVIDERS.has(provider)) {
|
|
1379
|
+
return creds;
|
|
1380
|
+
}
|
|
1381
|
+
if (!opts?.forceRefresh && Date.now() < creds.expiresAt - REFRESH_SKEW_MS) {
|
|
1382
|
+
return creds;
|
|
1383
|
+
}
|
|
1384
|
+
const existing = this.refreshLocks.get(provider);
|
|
1385
|
+
if (existing) return existing;
|
|
1386
|
+
const refreshPromise = withFileLock(this.filePath, async () => {
|
|
1387
|
+
try {
|
|
1388
|
+
const content = await import_promises4.default.readFile(this.filePath, "utf-8");
|
|
1389
|
+
const freshData = JSON.parse(content);
|
|
1390
|
+
const freshCreds = freshData[provider];
|
|
1391
|
+
if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt - REFRESH_SKEW_MS) {
|
|
1392
|
+
this.data[provider] = freshCreds;
|
|
1393
|
+
return freshCreds;
|
|
1394
|
+
}
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1397
|
+
const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
|
|
1398
|
+
let refreshed;
|
|
1399
|
+
try {
|
|
1400
|
+
refreshed = await refreshFn(creds.refreshToken);
|
|
1401
|
+
} catch (err) {
|
|
1402
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1403
|
+
const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
|
|
1404
|
+
if (isAuthFailure) {
|
|
1405
|
+
delete this.data[provider];
|
|
1406
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1407
|
+
throw new NotLoggedInError(provider);
|
|
1408
|
+
}
|
|
1409
|
+
throw err;
|
|
1410
|
+
}
|
|
1411
|
+
if (!refreshed.accountId && creds.accountId) {
|
|
1412
|
+
refreshed.accountId = creds.accountId;
|
|
1413
|
+
}
|
|
1414
|
+
if (!refreshed.projectId && creds.projectId) {
|
|
1415
|
+
refreshed.projectId = creds.projectId;
|
|
1416
|
+
}
|
|
1417
|
+
if (!refreshed.baseUrl && creds.baseUrl) {
|
|
1418
|
+
refreshed.baseUrl = creds.baseUrl;
|
|
1419
|
+
}
|
|
1420
|
+
this.data[provider] = refreshed;
|
|
1421
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1422
|
+
return refreshed;
|
|
1423
|
+
});
|
|
1424
|
+
this.refreshLocks.set(provider, refreshPromise);
|
|
1425
|
+
try {
|
|
1426
|
+
return await refreshPromise;
|
|
1427
|
+
} finally {
|
|
1428
|
+
this.refreshLocks.delete(provider);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Returns a valid access token, auto-refreshing if expired.
|
|
1433
|
+
* Throws if not logged in.
|
|
1434
|
+
*/
|
|
1435
|
+
async resolveToken(provider) {
|
|
1436
|
+
const creds = await this.resolveCredentials(provider);
|
|
1437
|
+
return creds.accessToken;
|
|
1438
|
+
}
|
|
1439
|
+
async save() {
|
|
1440
|
+
await withFileLock(this.filePath, async () => {
|
|
1441
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
async function atomicWriteFile(filePath, content) {
|
|
1446
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${import_node_crypto6.default.randomUUID().slice(0, 8)}.tmp`;
|
|
1447
|
+
try {
|
|
1448
|
+
await import_promises4.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
|
|
1449
|
+
await import_promises4.default.rename(tmpPath, filePath);
|
|
1450
|
+
} catch (err) {
|
|
1451
|
+
await import_promises4.default.unlink(tmpPath).catch(() => {
|
|
1452
|
+
});
|
|
1453
|
+
throw err;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
var NotLoggedInError = class extends Error {
|
|
1457
|
+
provider;
|
|
1458
|
+
constructor(provider) {
|
|
1459
|
+
super(`Not logged in to ${provider}. Run "ezcoder login" to authenticate.`);
|
|
1460
|
+
this.name = "NotLoggedInError";
|
|
1461
|
+
this.provider = provider;
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
// src/model-registry.ts
|
|
1466
|
+
var MODELS = [
|
|
1467
|
+
// ── Anthropic ──────────────────────────────────────────
|
|
1468
|
+
// NOTE: Claude Mythos 5 (`claude-mythos-5`) is kept commented out — it's a
|
|
1469
|
+
// Project Glasswing (limited, invitation-only) model unavailable to most
|
|
1470
|
+
// users. Re-enable once it's generally available.
|
|
1471
|
+
{
|
|
1472
|
+
id: "claude-fable-5",
|
|
1473
|
+
name: "Claude Fable 5",
|
|
1474
|
+
provider: "anthropic",
|
|
1475
|
+
contextWindow: 1e6,
|
|
1476
|
+
maxOutputTokens: 128e3,
|
|
1477
|
+
supportsThinking: true,
|
|
1478
|
+
supportsImages: true,
|
|
1479
|
+
supportsVideo: false,
|
|
1480
|
+
costTier: "high",
|
|
1481
|
+
maxThinkingLevel: "max"
|
|
1482
|
+
},
|
|
1483
|
+
// {
|
|
1484
|
+
// // Mythos-class model offered through Project Glasswing (limited
|
|
1485
|
+
// // availability, invitation-only). Same underlying model as Fable 5 with
|
|
1486
|
+
// // some safeguards lifted; kept here so approved accounts can select it.
|
|
1487
|
+
// id: "claude-mythos-5",
|
|
1488
|
+
// name: "Claude Mythos 5",
|
|
1489
|
+
// provider: "anthropic",
|
|
1490
|
+
// contextWindow: 1_000_000,
|
|
1491
|
+
// maxOutputTokens: 128_000,
|
|
1492
|
+
// supportsThinking: true,
|
|
1493
|
+
// supportsImages: true,
|
|
1494
|
+
// supportsVideo: false,
|
|
1495
|
+
// costTier: "high",
|
|
1496
|
+
// maxThinkingLevel: "max",
|
|
1497
|
+
// },
|
|
1498
|
+
{
|
|
1499
|
+
id: "claude-opus-4-8",
|
|
1500
|
+
name: "Claude Opus 4.8",
|
|
1501
|
+
provider: "anthropic",
|
|
1502
|
+
contextWindow: 1e6,
|
|
1503
|
+
maxOutputTokens: 128e3,
|
|
1504
|
+
supportsThinking: true,
|
|
1505
|
+
supportsImages: true,
|
|
1506
|
+
supportsVideo: false,
|
|
1507
|
+
costTier: "high",
|
|
1508
|
+
maxThinkingLevel: "max"
|
|
1509
|
+
},
|
|
1510
|
+
{
|
|
1511
|
+
id: "claude-sonnet-5",
|
|
1512
|
+
name: "Claude Sonnet 5",
|
|
1513
|
+
provider: "anthropic",
|
|
1514
|
+
contextWindow: 1e6,
|
|
1515
|
+
maxOutputTokens: 128e3,
|
|
1516
|
+
supportsThinking: true,
|
|
1517
|
+
supportsImages: true,
|
|
1518
|
+
supportsVideo: false,
|
|
1519
|
+
costTier: "medium",
|
|
1520
|
+
maxThinkingLevel: "max"
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
id: "claude-haiku-4-5-20251001",
|
|
1524
|
+
name: "Claude Haiku 4.5",
|
|
1525
|
+
provider: "anthropic",
|
|
1526
|
+
contextWindow: 2e5,
|
|
1527
|
+
maxOutputTokens: 64e3,
|
|
1528
|
+
supportsThinking: true,
|
|
1529
|
+
supportsImages: true,
|
|
1530
|
+
supportsVideo: false,
|
|
1531
|
+
costTier: "low",
|
|
1532
|
+
maxThinkingLevel: "high"
|
|
1533
|
+
},
|
|
1534
|
+
// ── OpenAI (Codex) ─────────────────────────────────────
|
|
1535
|
+
{
|
|
1536
|
+
id: "gpt-5.5",
|
|
1537
|
+
name: "GPT-5.5",
|
|
1538
|
+
provider: "openai",
|
|
1539
|
+
contextWindow: 105e4,
|
|
1540
|
+
codexContextWindow: 272e3,
|
|
1541
|
+
maxOutputTokens: 128e3,
|
|
1542
|
+
supportsThinking: true,
|
|
1543
|
+
supportsImages: true,
|
|
1544
|
+
supportsVideo: false,
|
|
1545
|
+
costTier: "high",
|
|
1546
|
+
maxThinkingLevel: "xhigh"
|
|
1547
|
+
},
|
|
1548
|
+
{
|
|
1549
|
+
id: "gpt-5.4",
|
|
1550
|
+
name: "GPT-5.4",
|
|
1551
|
+
provider: "openai",
|
|
1552
|
+
contextWindow: 105e4,
|
|
1553
|
+
codexContextWindow: 272e3,
|
|
1554
|
+
maxOutputTokens: 128e3,
|
|
1555
|
+
supportsThinking: true,
|
|
1556
|
+
supportsImages: true,
|
|
1557
|
+
supportsVideo: false,
|
|
1558
|
+
costTier: "high",
|
|
1559
|
+
maxThinkingLevel: "xhigh"
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
id: "gpt-5.4-mini",
|
|
1563
|
+
name: "GPT-5.4 Mini",
|
|
1564
|
+
provider: "openai",
|
|
1565
|
+
contextWindow: 4e5,
|
|
1566
|
+
maxOutputTokens: 128e3,
|
|
1567
|
+
supportsThinking: true,
|
|
1568
|
+
supportsImages: true,
|
|
1569
|
+
supportsVideo: false,
|
|
1570
|
+
costTier: "low",
|
|
1571
|
+
maxThinkingLevel: "xhigh"
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
id: "gpt-5.3-codex",
|
|
1575
|
+
name: "GPT-5.3 Codex",
|
|
1576
|
+
provider: "openai",
|
|
1577
|
+
contextWindow: 4e5,
|
|
1578
|
+
maxOutputTokens: 128e3,
|
|
1579
|
+
supportsThinking: true,
|
|
1580
|
+
supportsImages: true,
|
|
1581
|
+
supportsVideo: false,
|
|
1582
|
+
costTier: "high",
|
|
1583
|
+
maxThinkingLevel: "xhigh"
|
|
1584
|
+
},
|
|
1585
|
+
// ── Sakana (Fugu) ──────────────────────────────────────
|
|
1586
|
+
// Sakana Fugu is a multi-agent system surfaced as a standard LLM via the
|
|
1587
|
+
// OpenAI-compatible Sakana API (https://api.sakana.ai/v1). Both models take
|
|
1588
|
+
// text + image input and only accept "high"/"xhigh" reasoning effort, so the
|
|
1589
|
+
// top tier is `xhigh`. `fugu` routes across all providers; `fugu-ultra` is
|
|
1590
|
+
// the heavier tier (may need larger client timeouts on complex tasks).
|
|
1591
|
+
{
|
|
1592
|
+
id: "fugu",
|
|
1593
|
+
name: "Fugu",
|
|
1594
|
+
provider: "sakana",
|
|
1595
|
+
contextWindow: 1e6,
|
|
1596
|
+
maxOutputTokens: 128e3,
|
|
1597
|
+
supportsThinking: true,
|
|
1598
|
+
supportsImages: true,
|
|
1599
|
+
supportsVideo: false,
|
|
1600
|
+
costTier: "medium",
|
|
1601
|
+
maxThinkingLevel: "xhigh"
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
id: "fugu-ultra",
|
|
1605
|
+
name: "Fugu Ultra",
|
|
1606
|
+
provider: "sakana",
|
|
1607
|
+
contextWindow: 1e6,
|
|
1608
|
+
maxOutputTokens: 128e3,
|
|
1609
|
+
supportsThinking: true,
|
|
1610
|
+
supportsImages: true,
|
|
1611
|
+
supportsVideo: false,
|
|
1612
|
+
costTier: "high",
|
|
1613
|
+
maxThinkingLevel: "xhigh"
|
|
1614
|
+
},
|
|
1615
|
+
// ── Gemini ─────────────────────────────────────────────
|
|
1616
|
+
{
|
|
1617
|
+
id: "gemini-3.1-flash-lite-preview",
|
|
1618
|
+
name: "Gemini 3.1 Flash Lite Preview",
|
|
1619
|
+
provider: "gemini",
|
|
1620
|
+
contextWindow: 1048576,
|
|
1621
|
+
maxOutputTokens: 65536,
|
|
1622
|
+
supportsThinking: true,
|
|
1623
|
+
supportsImages: true,
|
|
1624
|
+
supportsVideo: true,
|
|
1625
|
+
maxVideoBytes: 20 * 1024 * 1024,
|
|
1626
|
+
costTier: "low",
|
|
1627
|
+
maxThinkingLevel: "high"
|
|
1628
|
+
},
|
|
1629
|
+
{
|
|
1630
|
+
id: "gemini-3.5-flash",
|
|
1631
|
+
name: "Gemini 3.5 Flash",
|
|
1632
|
+
provider: "gemini",
|
|
1633
|
+
contextWindow: 1048576,
|
|
1634
|
+
maxOutputTokens: 65536,
|
|
1635
|
+
supportsThinking: true,
|
|
1636
|
+
supportsImages: true,
|
|
1637
|
+
supportsVideo: true,
|
|
1638
|
+
maxVideoBytes: 20 * 1024 * 1024,
|
|
1639
|
+
costTier: "low",
|
|
1640
|
+
maxThinkingLevel: "high"
|
|
1641
|
+
},
|
|
1642
|
+
// ── Moonshot (Kimi) ────────────────────────────────────
|
|
1643
|
+
{
|
|
1644
|
+
id: "kimi-k2.7-code",
|
|
1645
|
+
name: "Kimi K2.7",
|
|
1646
|
+
provider: "moonshot",
|
|
1647
|
+
contextWindow: 262144,
|
|
1648
|
+
maxOutputTokens: 262144,
|
|
1649
|
+
supportsThinking: true,
|
|
1650
|
+
supportsImages: true,
|
|
1651
|
+
supportsVideo: true,
|
|
1652
|
+
maxVideoBytes: 100 * 1024 * 1024,
|
|
1653
|
+
costTier: "medium",
|
|
1654
|
+
maxThinkingLevel: "high"
|
|
1655
|
+
},
|
|
1656
|
+
// ── Z.AI (GLM) ─────────────────────────────────────────
|
|
1657
|
+
// GLM-5.2: coding-first flagship with a usable 1M-token context window
|
|
1658
|
+
// (5x jump over GLM-5.1's ~200K) and 131K max output. Released 2026-06-13.
|
|
1659
|
+
{
|
|
1660
|
+
id: "glm-5.2",
|
|
1661
|
+
name: "GLM-5.2",
|
|
1662
|
+
provider: "glm",
|
|
1663
|
+
contextWindow: 1e6,
|
|
1664
|
+
maxOutputTokens: 131072,
|
|
1665
|
+
supportsThinking: true,
|
|
1666
|
+
supportsImages: false,
|
|
1667
|
+
supportsVideo: false,
|
|
1668
|
+
costTier: "medium",
|
|
1669
|
+
maxThinkingLevel: "high"
|
|
1670
|
+
},
|
|
1671
|
+
{
|
|
1672
|
+
id: "glm-5.1",
|
|
1673
|
+
name: "GLM-5.1",
|
|
1674
|
+
provider: "glm",
|
|
1675
|
+
contextWindow: 204800,
|
|
1676
|
+
maxOutputTokens: 131072,
|
|
1677
|
+
supportsThinking: true,
|
|
1678
|
+
supportsImages: false,
|
|
1679
|
+
supportsVideo: false,
|
|
1680
|
+
costTier: "medium",
|
|
1681
|
+
maxThinkingLevel: "high"
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
id: "glm-4.7",
|
|
1685
|
+
name: "GLM-4.7",
|
|
1686
|
+
provider: "glm",
|
|
1687
|
+
contextWindow: 2e5,
|
|
1688
|
+
maxOutputTokens: 131072,
|
|
1689
|
+
supportsThinking: true,
|
|
1690
|
+
supportsImages: false,
|
|
1691
|
+
supportsVideo: false,
|
|
1692
|
+
costTier: "low",
|
|
1693
|
+
maxThinkingLevel: "high"
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
id: "glm-4.7-flash",
|
|
1697
|
+
name: "GLM-4.7 Flash",
|
|
1698
|
+
provider: "glm",
|
|
1699
|
+
contextWindow: 2e5,
|
|
1700
|
+
maxOutputTokens: 131072,
|
|
1701
|
+
supportsThinking: true,
|
|
1702
|
+
supportsImages: false,
|
|
1703
|
+
supportsVideo: false,
|
|
1704
|
+
costTier: "low",
|
|
1705
|
+
maxThinkingLevel: "high"
|
|
1706
|
+
},
|
|
1707
|
+
// ── MiniMax ────────────────────────────────────────────
|
|
1708
|
+
{
|
|
1709
|
+
id: "MiniMax-M3",
|
|
1710
|
+
name: "MiniMax M3",
|
|
1711
|
+
provider: "minimax",
|
|
1712
|
+
contextWindow: 1e6,
|
|
1713
|
+
maxOutputTokens: 131072,
|
|
1714
|
+
supportsThinking: true,
|
|
1715
|
+
supportsImages: true,
|
|
1716
|
+
supportsVideo: true,
|
|
1717
|
+
maxVideoBytes: 50 * 1024 * 1024,
|
|
1718
|
+
costTier: "medium",
|
|
1719
|
+
maxThinkingLevel: "high"
|
|
1720
|
+
},
|
|
1721
|
+
// ── Xiaomi (MiMo) ──────────────────────────────────────
|
|
1722
|
+
// Pro series: text-only coding/agentic flagship. The legacy mimo-v2-pro
|
|
1723
|
+
// auto-routes to v2.5 on 2026-06-01 and is fully deprecated by 2026-06-30.
|
|
1724
|
+
{
|
|
1725
|
+
id: "mimo-v2.5-pro",
|
|
1726
|
+
name: "MiMo-V2.5-Pro",
|
|
1727
|
+
provider: "xiaomi",
|
|
1728
|
+
contextWindow: 1e6,
|
|
1729
|
+
maxOutputTokens: 131072,
|
|
1730
|
+
supportsThinking: true,
|
|
1731
|
+
supportsImages: false,
|
|
1732
|
+
supportsVideo: false,
|
|
1733
|
+
costTier: "medium",
|
|
1734
|
+
maxThinkingLevel: "high",
|
|
1735
|
+
authStorageKeys: ["xiaomi", XIAOMI_CREDITS_KEY]
|
|
1736
|
+
},
|
|
1737
|
+
// UltraSpeed: lower-latency sibling of the Pro coding flagship, same
|
|
1738
|
+
// text-only capability surface, premium-priced for the throughput gain.
|
|
1739
|
+
// API-only — not served over the Token Plan endpoint, so credentials
|
|
1740
|
+
// resolve from the distinct API Credits key only (see authStorageKeys doc).
|
|
1741
|
+
{
|
|
1742
|
+
id: "mimo-v2.5-pro-ultraspeed",
|
|
1743
|
+
name: "MiMo-V2.5-Pro-UltraSpeed",
|
|
1744
|
+
provider: "xiaomi",
|
|
1745
|
+
contextWindow: 1e6,
|
|
1746
|
+
maxOutputTokens: 131072,
|
|
1747
|
+
supportsThinking: true,
|
|
1748
|
+
supportsImages: false,
|
|
1749
|
+
supportsVideo: false,
|
|
1750
|
+
costTier: "high",
|
|
1751
|
+
maxThinkingLevel: "high",
|
|
1752
|
+
authStorageKeys: [XIAOMI_CREDITS_KEY]
|
|
1753
|
+
},
|
|
1754
|
+
// Omni series: native full-modal understanding (image + audio + video).
|
|
1755
|
+
// Video/image ride the OpenAI-compatible transport as base64 data URLs
|
|
1756
|
+
// (`video_url`/`image_url`), which the shared transform already emits.
|
|
1757
|
+
{
|
|
1758
|
+
id: "mimo-v2.5",
|
|
1759
|
+
name: "MiMo-V2.5",
|
|
1760
|
+
provider: "xiaomi",
|
|
1761
|
+
contextWindow: 1e6,
|
|
1762
|
+
maxOutputTokens: 131072,
|
|
1763
|
+
supportsThinking: true,
|
|
1764
|
+
supportsImages: true,
|
|
1765
|
+
supportsVideo: true,
|
|
1766
|
+
maxVideoBytes: 36 * 1024 * 1024,
|
|
1767
|
+
costTier: "medium",
|
|
1768
|
+
maxThinkingLevel: "high",
|
|
1769
|
+
authStorageKeys: ["xiaomi", XIAOMI_CREDITS_KEY]
|
|
1770
|
+
},
|
|
1771
|
+
// ── DeepSeek ───────────────────────────────────────────
|
|
1772
|
+
{
|
|
1773
|
+
id: "deepseek-v4-pro",
|
|
1774
|
+
name: "DeepSeek V4 Pro",
|
|
1775
|
+
provider: "deepseek",
|
|
1776
|
+
contextWindow: 1048576,
|
|
1777
|
+
maxOutputTokens: 384e3,
|
|
1778
|
+
supportsThinking: true,
|
|
1779
|
+
supportsImages: false,
|
|
1780
|
+
supportsVideo: false,
|
|
1781
|
+
costTier: "high",
|
|
1782
|
+
// DeepSeek V4 maps `xhigh` → its internal `max` tier.
|
|
1783
|
+
maxThinkingLevel: "xhigh"
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
id: "deepseek-v4-flash",
|
|
1787
|
+
name: "DeepSeek V4 Flash",
|
|
1788
|
+
provider: "deepseek",
|
|
1789
|
+
contextWindow: 1048576,
|
|
1790
|
+
maxOutputTokens: 384e3,
|
|
1791
|
+
supportsThinking: true,
|
|
1792
|
+
supportsImages: false,
|
|
1793
|
+
supportsVideo: false,
|
|
1794
|
+
costTier: "low",
|
|
1795
|
+
maxThinkingLevel: "xhigh"
|
|
1796
|
+
},
|
|
1797
|
+
// ── OpenRouter ─────────────────────────────────────────
|
|
1798
|
+
{
|
|
1799
|
+
id: "qwen/qwen3.6-plus",
|
|
1800
|
+
name: "Qwen3.6-Plus",
|
|
1801
|
+
provider: "openrouter",
|
|
1802
|
+
contextWindow: 1e6,
|
|
1803
|
+
maxOutputTokens: 65536,
|
|
1804
|
+
supportsThinking: true,
|
|
1805
|
+
supportsImages: false,
|
|
1806
|
+
supportsVideo: false,
|
|
1807
|
+
costTier: "medium",
|
|
1808
|
+
maxThinkingLevel: "high"
|
|
1809
|
+
}
|
|
1810
|
+
];
|
|
1811
|
+
function getModel(id) {
|
|
1812
|
+
return MODELS.find((m) => m.id === id);
|
|
1813
|
+
}
|
|
1814
|
+
function getModelsForProvider(provider) {
|
|
1815
|
+
return MODELS.filter((m) => m.provider === provider);
|
|
1816
|
+
}
|
|
1817
|
+
function getAuthStorageKeys(provider, modelId) {
|
|
1818
|
+
const model = MODELS.find((m) => m.id === modelId && m.provider === provider);
|
|
1819
|
+
return model?.authStorageKeys ?? [provider];
|
|
1820
|
+
}
|
|
1821
|
+
function getAuthStorageKey(provider, modelId) {
|
|
1822
|
+
return getAuthStorageKeys(provider, modelId)[0];
|
|
1823
|
+
}
|
|
1824
|
+
var DEFAULT_MAX_VIDEO_BYTES = 20 * 1024 * 1024;
|
|
1825
|
+
function getVideoByteLimit(modelId) {
|
|
1826
|
+
const model = getModel(modelId);
|
|
1827
|
+
if (!model?.supportsVideo) return void 0;
|
|
1828
|
+
return model.maxVideoBytes ?? DEFAULT_MAX_VIDEO_BYTES;
|
|
1829
|
+
}
|
|
1830
|
+
function getDefaultModel(provider) {
|
|
1831
|
+
if (provider === "xiaomi") return MODELS.find((m) => m.id === "mimo-v2.5-pro");
|
|
1832
|
+
if (provider === "openai") return MODELS.find((m) => m.id === "gpt-5.5");
|
|
1833
|
+
if (provider === "gemini") return MODELS.find((m) => m.id === "gemini-3.1-flash-lite-preview");
|
|
1834
|
+
if (provider === "glm") return MODELS.find((m) => m.id === "glm-5.2");
|
|
1835
|
+
if (provider === "moonshot") return MODELS.find((m) => m.id === "kimi-k2.7-code");
|
|
1836
|
+
if (provider === "minimax") return MODELS.find((m) => m.id === "MiniMax-M3");
|
|
1837
|
+
if (provider === "deepseek") return MODELS.find((m) => m.id === "deepseek-v4-pro");
|
|
1838
|
+
if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
|
|
1839
|
+
if (provider === "sakana") return MODELS.find((m) => m.id === "fugu");
|
|
1840
|
+
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
1841
|
+
}
|
|
1842
|
+
function usesOpenAICodexTransport(options) {
|
|
1843
|
+
return options?.provider === "openai" && Boolean(options.accountId);
|
|
1844
|
+
}
|
|
1845
|
+
function getContextWindow(modelId, options) {
|
|
1846
|
+
const model = getModel(modelId);
|
|
1847
|
+
if (!model) return 2e5;
|
|
1848
|
+
if (usesOpenAICodexTransport(options) && model.codexContextWindow) {
|
|
1849
|
+
return model.codexContextWindow;
|
|
1764
1850
|
}
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
|
|
1774
|
-
try {
|
|
1775
|
-
return await this.resolveCredentials(MOONSHOT_OAUTH_KEY, opts);
|
|
1776
|
-
} catch (err) {
|
|
1777
|
-
if (err instanceof NotLoggedInError && this.data["moonshot"]) {
|
|
1778
|
-
log(
|
|
1779
|
-
"WARN",
|
|
1780
|
-
"auth",
|
|
1781
|
-
'Kimi OAuth credential is no longer valid \u2014 falling back to the Moonshot API key. Run "ezcoder login" and choose Kimi OAuth to restore OAuth auth.'
|
|
1782
|
-
);
|
|
1783
|
-
return this.data["moonshot"];
|
|
1784
|
-
}
|
|
1785
|
-
throw err;
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
const creds = this.data[provider];
|
|
1789
|
-
if (!creds) {
|
|
1790
|
-
throw new NotLoggedInError(provider);
|
|
1791
|
-
}
|
|
1792
|
-
if (STATIC_API_KEY_PROVIDERS.has(provider)) {
|
|
1793
|
-
return creds;
|
|
1794
|
-
}
|
|
1795
|
-
if (!opts?.forceRefresh && Date.now() < creds.expiresAt - REFRESH_SKEW_MS) {
|
|
1796
|
-
return creds;
|
|
1797
|
-
}
|
|
1798
|
-
const existing = this.refreshLocks.get(provider);
|
|
1799
|
-
if (existing) return existing;
|
|
1800
|
-
const refreshPromise = withFileLock(this.filePath, async () => {
|
|
1801
|
-
try {
|
|
1802
|
-
const content = await import_promises4.default.readFile(this.filePath, "utf-8");
|
|
1803
|
-
const freshData = JSON.parse(content);
|
|
1804
|
-
const freshCreds = freshData[provider];
|
|
1805
|
-
if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt - REFRESH_SKEW_MS) {
|
|
1806
|
-
this.data[provider] = freshCreds;
|
|
1807
|
-
return freshCreds;
|
|
1808
|
-
}
|
|
1809
|
-
} catch {
|
|
1810
|
-
}
|
|
1811
|
-
const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
|
|
1812
|
-
let refreshed;
|
|
1813
|
-
try {
|
|
1814
|
-
refreshed = await refreshFn(creds.refreshToken);
|
|
1815
|
-
} catch (err) {
|
|
1816
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1817
|
-
const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
|
|
1818
|
-
if (isAuthFailure) {
|
|
1819
|
-
delete this.data[provider];
|
|
1820
|
-
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1821
|
-
throw new NotLoggedInError(provider);
|
|
1822
|
-
}
|
|
1823
|
-
throw err;
|
|
1824
|
-
}
|
|
1825
|
-
if (!refreshed.accountId && creds.accountId) {
|
|
1826
|
-
refreshed.accountId = creds.accountId;
|
|
1827
|
-
}
|
|
1828
|
-
if (!refreshed.projectId && creds.projectId) {
|
|
1829
|
-
refreshed.projectId = creds.projectId;
|
|
1830
|
-
}
|
|
1831
|
-
if (!refreshed.baseUrl && creds.baseUrl) {
|
|
1832
|
-
refreshed.baseUrl = creds.baseUrl;
|
|
1833
|
-
}
|
|
1834
|
-
this.data[provider] = refreshed;
|
|
1835
|
-
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1836
|
-
return refreshed;
|
|
1837
|
-
});
|
|
1838
|
-
this.refreshLocks.set(provider, refreshPromise);
|
|
1839
|
-
try {
|
|
1840
|
-
return await refreshPromise;
|
|
1841
|
-
} finally {
|
|
1842
|
-
this.refreshLocks.delete(provider);
|
|
1843
|
-
}
|
|
1851
|
+
return model.contextWindow;
|
|
1852
|
+
}
|
|
1853
|
+
function getMaxThinkingLevel(modelId) {
|
|
1854
|
+
return getModel(modelId)?.maxThinkingLevel ?? "high";
|
|
1855
|
+
}
|
|
1856
|
+
function getSummaryModel(provider, currentModelId) {
|
|
1857
|
+
if (provider === "anthropic") {
|
|
1858
|
+
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
1844
1859
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
*/
|
|
1849
|
-
async resolveToken(provider) {
|
|
1850
|
-
const creds = await this.resolveCredentials(provider);
|
|
1851
|
-
return creds.accessToken;
|
|
1860
|
+
if (provider === "openai" || provider === "glm" || provider === "deepseek") {
|
|
1861
|
+
const low = getModelsForProvider(provider).find((m) => m.costTier === "low");
|
|
1862
|
+
if (low) return low;
|
|
1852
1863
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1864
|
+
return getModel(currentModelId) ?? getDefaultModel(provider);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// src/thinking-level.ts
|
|
1868
|
+
var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
|
|
1869
|
+
var SAKANA_THINKING_LEVELS = ["high", "xhigh"];
|
|
1870
|
+
var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
|
|
1871
|
+
"low",
|
|
1872
|
+
"medium",
|
|
1873
|
+
"high",
|
|
1874
|
+
"xhigh",
|
|
1875
|
+
"max"
|
|
1876
|
+
];
|
|
1877
|
+
var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
|
|
1878
|
+
"low",
|
|
1879
|
+
"medium",
|
|
1880
|
+
"high",
|
|
1881
|
+
"max"
|
|
1882
|
+
];
|
|
1883
|
+
function isOpenAIGptModel(provider, model) {
|
|
1884
|
+
return provider === "openai" && model.startsWith("gpt-");
|
|
1885
|
+
}
|
|
1886
|
+
function isSakanaModel(provider) {
|
|
1887
|
+
return provider === "sakana";
|
|
1888
|
+
}
|
|
1889
|
+
function isAnthropicOpus48Or47Model(provider, model) {
|
|
1890
|
+
return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
|
|
1891
|
+
}
|
|
1892
|
+
function isAnthropicAdaptiveModel(provider, model) {
|
|
1893
|
+
return provider === "anthropic" && /opus-4-8|opus-4-7|opus-4-6|sonnet-5|fable-5|mythos-5/.test(model);
|
|
1894
|
+
}
|
|
1895
|
+
function getSupportedThinkingLevels(provider, model) {
|
|
1896
|
+
const maxLevel = getMaxThinkingLevel(model);
|
|
1897
|
+
if (isAnthropicAdaptiveModel(provider, model)) {
|
|
1898
|
+
const levels = isAnthropicOpus48Or47Model(provider, model) ? ANTHROPIC_OPUS_48_47_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
|
|
1899
|
+
const maxIndex2 = levels.indexOf(maxLevel);
|
|
1900
|
+
if (maxIndex2 === -1) return ["low", "medium", "high"];
|
|
1901
|
+
return levels.slice(0, maxIndex2 + 1);
|
|
1857
1902
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
await import_promises4.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
|
|
1863
|
-
await import_promises4.default.rename(tmpPath, filePath);
|
|
1864
|
-
} catch (err) {
|
|
1865
|
-
await import_promises4.default.unlink(tmpPath).catch(() => {
|
|
1866
|
-
});
|
|
1867
|
-
throw err;
|
|
1903
|
+
if (isSakanaModel(provider)) {
|
|
1904
|
+
const maxIndex2 = SAKANA_THINKING_LEVELS.indexOf(maxLevel);
|
|
1905
|
+
if (maxIndex2 === -1) return SAKANA_THINKING_LEVELS;
|
|
1906
|
+
return SAKANA_THINKING_LEVELS.slice(0, maxIndex2 + 1);
|
|
1868
1907
|
}
|
|
1908
|
+
if (!isOpenAIGptModel(provider, model)) return [maxLevel];
|
|
1909
|
+
const maxIndex = OPENAI_GPT_THINKING_LEVELS.indexOf(maxLevel);
|
|
1910
|
+
if (maxIndex === -1) return ["medium"];
|
|
1911
|
+
return OPENAI_GPT_THINKING_LEVELS.slice(0, maxIndex + 1);
|
|
1869
1912
|
}
|
|
1870
|
-
|
|
1871
|
-
provider;
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1913
|
+
function isThinkingLevelSupported(provider, model, level) {
|
|
1914
|
+
return getSupportedThinkingLevels(provider, model).includes(level);
|
|
1915
|
+
}
|
|
1916
|
+
function getNextThinkingLevel(provider, model, current) {
|
|
1917
|
+
const supportedLevels = getSupportedThinkingLevels(provider, model);
|
|
1918
|
+
const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider);
|
|
1919
|
+
if (!shouldCycleLevels) {
|
|
1920
|
+
return current ? void 0 : supportedLevels[0];
|
|
1876
1921
|
}
|
|
1877
|
-
|
|
1922
|
+
if (!current) return supportedLevels[0];
|
|
1923
|
+
const index = supportedLevels.indexOf(current);
|
|
1924
|
+
if (index === -1) return supportedLevels[0];
|
|
1925
|
+
return supportedLevels[index + 1];
|
|
1926
|
+
}
|
|
1878
1927
|
|
|
1879
1928
|
// src/telegram.ts
|
|
1880
1929
|
var TELEGRAM_API = "https://api.telegram.org";
|
|
@@ -2359,12 +2408,15 @@ function createAutoUpdater(config) {
|
|
|
2359
2408
|
MOONSHOT_OAUTH_KEY,
|
|
2360
2409
|
NotLoggedInError,
|
|
2361
2410
|
TelegramBot,
|
|
2411
|
+
XIAOMI_CREDITS_KEY,
|
|
2362
2412
|
closeLogger,
|
|
2363
2413
|
createAutoUpdater,
|
|
2364
2414
|
decodeOggOpus,
|
|
2365
2415
|
downmixToMono,
|
|
2366
2416
|
generatePKCE,
|
|
2367
2417
|
getAppPaths,
|
|
2418
|
+
getAuthStorageKey,
|
|
2419
|
+
getAuthStorageKeys,
|
|
2368
2420
|
getClaudeCliUserAgent,
|
|
2369
2421
|
getClaudeCodeVersion,
|
|
2370
2422
|
getContextWindow,
|