@kenkaiiii/gg-core 5.2.0 → 5.4.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-3EWGFN2V.js +1794 -0
- package/dist/chunk-3EWGFN2V.js.map +1 -0
- package/dist/index.cjs +706 -651
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +41 -1335
- package/dist/index.js.map +1 -1
- package/dist/model-registry.cjs +114 -18
- package/dist/model-registry.cjs.map +1 -1
- package/dist/model-registry.d.cts +28 -1
- package/dist/model-registry.d.ts +28 -1
- package/dist/model-registry.js +6 -1
- package/package.json +2 -2
- package/dist/chunk-7TLD43I3.js +0 -390
- package/dist/chunk-7TLD43I3.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,443 +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-5",
|
|
130
|
-
name: "Claude Sonnet 5",
|
|
131
|
-
provider: "anthropic",
|
|
132
|
-
contextWindow: 1e6,
|
|
133
|
-
maxOutputTokens: 128e3,
|
|
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
|
-
// Omni series: native full-modal understanding (image + audio + video).
|
|
355
|
-
// Video/image ride the OpenAI-compatible transport as base64 data URLs
|
|
356
|
-
// (`video_url`/`image_url`), which the shared transform already emits.
|
|
357
|
-
{
|
|
358
|
-
id: "mimo-v2.5",
|
|
359
|
-
name: "MiMo-V2.5",
|
|
360
|
-
provider: "xiaomi",
|
|
361
|
-
contextWindow: 1e6,
|
|
362
|
-
maxOutputTokens: 131072,
|
|
363
|
-
supportsThinking: true,
|
|
364
|
-
supportsImages: true,
|
|
365
|
-
supportsVideo: true,
|
|
366
|
-
maxVideoBytes: 36 * 1024 * 1024,
|
|
367
|
-
costTier: "medium",
|
|
368
|
-
maxThinkingLevel: "high"
|
|
369
|
-
},
|
|
370
|
-
// ── DeepSeek ───────────────────────────────────────────
|
|
371
|
-
{
|
|
372
|
-
id: "deepseek-v4-pro",
|
|
373
|
-
name: "DeepSeek V4 Pro",
|
|
374
|
-
provider: "deepseek",
|
|
375
|
-
contextWindow: 1048576,
|
|
376
|
-
maxOutputTokens: 384e3,
|
|
377
|
-
supportsThinking: true,
|
|
378
|
-
supportsImages: false,
|
|
379
|
-
supportsVideo: false,
|
|
380
|
-
costTier: "high",
|
|
381
|
-
// DeepSeek V4 maps `xhigh` → its internal `max` tier.
|
|
382
|
-
maxThinkingLevel: "xhigh"
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
id: "deepseek-v4-flash",
|
|
386
|
-
name: "DeepSeek V4 Flash",
|
|
387
|
-
provider: "deepseek",
|
|
388
|
-
contextWindow: 1048576,
|
|
389
|
-
maxOutputTokens: 384e3,
|
|
390
|
-
supportsThinking: true,
|
|
391
|
-
supportsImages: false,
|
|
392
|
-
supportsVideo: false,
|
|
393
|
-
costTier: "low",
|
|
394
|
-
maxThinkingLevel: "xhigh"
|
|
395
|
-
},
|
|
396
|
-
// ── OpenRouter ─────────────────────────────────────────
|
|
397
|
-
{
|
|
398
|
-
id: "qwen/qwen3.6-plus",
|
|
399
|
-
name: "Qwen3.6-Plus",
|
|
400
|
-
provider: "openrouter",
|
|
401
|
-
contextWindow: 1e6,
|
|
402
|
-
maxOutputTokens: 65536,
|
|
403
|
-
supportsThinking: true,
|
|
404
|
-
supportsImages: false,
|
|
405
|
-
supportsVideo: false,
|
|
406
|
-
costTier: "medium",
|
|
407
|
-
maxThinkingLevel: "high"
|
|
408
|
-
}
|
|
409
|
-
];
|
|
410
|
-
function getModel(id) {
|
|
411
|
-
return MODELS.find((m) => m.id === id);
|
|
412
|
-
}
|
|
413
|
-
function getModelsForProvider(provider) {
|
|
414
|
-
return MODELS.filter((m) => m.provider === provider);
|
|
415
|
-
}
|
|
416
|
-
var DEFAULT_MAX_VIDEO_BYTES = 20 * 1024 * 1024;
|
|
417
|
-
function getVideoByteLimit(modelId) {
|
|
418
|
-
const model = getModel(modelId);
|
|
419
|
-
if (!model?.supportsVideo) return void 0;
|
|
420
|
-
return model.maxVideoBytes ?? DEFAULT_MAX_VIDEO_BYTES;
|
|
421
|
-
}
|
|
422
|
-
function getDefaultModel(provider) {
|
|
423
|
-
if (provider === "xiaomi") return MODELS.find((m) => m.id === "mimo-v2.5-pro");
|
|
424
|
-
if (provider === "openai") return MODELS.find((m) => m.id === "gpt-5.5");
|
|
425
|
-
if (provider === "gemini") return MODELS.find((m) => m.id === "gemini-3.1-flash-lite-preview");
|
|
426
|
-
if (provider === "glm") return MODELS.find((m) => m.id === "glm-5.2");
|
|
427
|
-
if (provider === "moonshot") return MODELS.find((m) => m.id === "kimi-k2.7-code");
|
|
428
|
-
if (provider === "minimax") return MODELS.find((m) => m.id === "MiniMax-M3");
|
|
429
|
-
if (provider === "deepseek") return MODELS.find((m) => m.id === "deepseek-v4-pro");
|
|
430
|
-
if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
|
|
431
|
-
if (provider === "sakana") return MODELS.find((m) => m.id === "fugu");
|
|
432
|
-
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
433
|
-
}
|
|
434
|
-
function usesOpenAICodexTransport(options) {
|
|
435
|
-
return options?.provider === "openai" && Boolean(options.accountId);
|
|
436
|
-
}
|
|
437
|
-
function getContextWindow(modelId, options) {
|
|
438
|
-
const model = getModel(modelId);
|
|
439
|
-
if (!model) return 2e5;
|
|
440
|
-
if (usesOpenAICodexTransport(options) && model.codexContextWindow) {
|
|
441
|
-
return model.codexContextWindow;
|
|
442
|
-
}
|
|
443
|
-
return model.contextWindow;
|
|
444
|
-
}
|
|
445
|
-
function getMaxThinkingLevel(modelId) {
|
|
446
|
-
return getModel(modelId)?.maxThinkingLevel ?? "high";
|
|
447
|
-
}
|
|
448
|
-
function getSummaryModel(provider, currentModelId) {
|
|
449
|
-
if (provider === "anthropic") {
|
|
450
|
-
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
451
|
-
}
|
|
452
|
-
if (provider === "openai" || provider === "glm" || provider === "deepseek") {
|
|
453
|
-
const low = getModelsForProvider(provider).find((m) => m.costTier === "low");
|
|
454
|
-
if (low) return low;
|
|
455
|
-
}
|
|
456
|
-
return getModel(currentModelId) ?? getDefaultModel(provider);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// src/thinking-level.ts
|
|
460
|
-
var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
|
|
461
|
-
var SAKANA_THINKING_LEVELS = ["high", "xhigh"];
|
|
462
|
-
var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
|
|
463
|
-
"low",
|
|
464
|
-
"medium",
|
|
465
|
-
"high",
|
|
466
|
-
"xhigh",
|
|
467
|
-
"max"
|
|
468
|
-
];
|
|
469
|
-
var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
|
|
470
|
-
"low",
|
|
471
|
-
"medium",
|
|
472
|
-
"high",
|
|
473
|
-
"max"
|
|
474
|
-
];
|
|
475
|
-
function isOpenAIGptModel(provider, model) {
|
|
476
|
-
return provider === "openai" && model.startsWith("gpt-");
|
|
477
|
-
}
|
|
478
|
-
function isSakanaModel(provider) {
|
|
479
|
-
return provider === "sakana";
|
|
480
|
-
}
|
|
481
|
-
function isAnthropicOpus48Or47Model(provider, model) {
|
|
482
|
-
return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
|
|
483
|
-
}
|
|
484
|
-
function isAnthropicAdaptiveModel(provider, model) {
|
|
485
|
-
return provider === "anthropic" && /opus-4-8|opus-4-7|opus-4-6|sonnet-5|fable-5|mythos-5/.test(model);
|
|
486
|
-
}
|
|
487
|
-
function getSupportedThinkingLevels(provider, model) {
|
|
488
|
-
const maxLevel = getMaxThinkingLevel(model);
|
|
489
|
-
if (isAnthropicAdaptiveModel(provider, model)) {
|
|
490
|
-
const levels = isAnthropicOpus48Or47Model(provider, model) ? ANTHROPIC_OPUS_48_47_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
|
|
491
|
-
const maxIndex2 = levels.indexOf(maxLevel);
|
|
492
|
-
if (maxIndex2 === -1) return ["low", "medium", "high"];
|
|
493
|
-
return levels.slice(0, maxIndex2 + 1);
|
|
494
|
-
}
|
|
495
|
-
if (isSakanaModel(provider)) {
|
|
496
|
-
const maxIndex2 = SAKANA_THINKING_LEVELS.indexOf(maxLevel);
|
|
497
|
-
if (maxIndex2 === -1) return SAKANA_THINKING_LEVELS;
|
|
498
|
-
return SAKANA_THINKING_LEVELS.slice(0, maxIndex2 + 1);
|
|
499
|
-
}
|
|
500
|
-
if (!isOpenAIGptModel(provider, model)) return [maxLevel];
|
|
501
|
-
const maxIndex = OPENAI_GPT_THINKING_LEVELS.indexOf(maxLevel);
|
|
502
|
-
if (maxIndex === -1) return ["medium"];
|
|
503
|
-
return OPENAI_GPT_THINKING_LEVELS.slice(0, maxIndex + 1);
|
|
504
|
-
}
|
|
505
|
-
function isThinkingLevelSupported(provider, model, level) {
|
|
506
|
-
return getSupportedThinkingLevels(provider, model).includes(level);
|
|
507
|
-
}
|
|
508
|
-
function getNextThinkingLevel(provider, model, current) {
|
|
509
|
-
const supportedLevels = getSupportedThinkingLevels(provider, model);
|
|
510
|
-
const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider);
|
|
511
|
-
if (!shouldCycleLevels) {
|
|
512
|
-
return current ? void 0 : supportedLevels[0];
|
|
513
|
-
}
|
|
514
|
-
if (!current) return supportedLevels[0];
|
|
515
|
-
const index = supportedLevels.indexOf(current);
|
|
516
|
-
if (index === -1) return supportedLevels[0];
|
|
517
|
-
return supportedLevels[index + 1];
|
|
518
|
-
}
|
|
85
|
+
// src/auth-storage.ts
|
|
86
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
87
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
519
88
|
|
|
520
89
|
// src/paths.ts
|
|
521
90
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -538,6 +107,31 @@ function getAppPaths() {
|
|
|
538
107
|
};
|
|
539
108
|
}
|
|
540
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
|
+
|
|
541
135
|
// src/logger.ts
|
|
542
136
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
543
137
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
@@ -596,90 +190,26 @@ function log(level, category, message, data) {
|
|
|
596
190
|
}
|
|
597
191
|
line += "\n";
|
|
598
192
|
try {
|
|
599
|
-
import_node_fs.default.writeSync(fd, line);
|
|
600
|
-
} catch {
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
function registerLogCleanup(fn) {
|
|
604
|
-
cleanups.push(fn);
|
|
605
|
-
}
|
|
606
|
-
function closeLogger(opts) {
|
|
607
|
-
if (fd === null) return;
|
|
608
|
-
if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
|
|
609
|
-
try {
|
|
610
|
-
import_node_fs.default.closeSync(fd);
|
|
611
|
-
} catch {
|
|
612
|
-
}
|
|
613
|
-
fd = null;
|
|
614
|
-
for (const unsub of cleanups) unsub();
|
|
615
|
-
cleanups = [];
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
// src/file-lock.ts
|
|
619
|
-
var import_promises = __toESM(require("fs/promises"), 1);
|
|
620
|
-
var import_promises2 = require("timers/promises");
|
|
621
|
-
var STALE_TIMEOUT_MS = 1e4;
|
|
622
|
-
var RETRY_INTERVAL_MS = 50;
|
|
623
|
-
var MAX_WAIT_MS = 5e3;
|
|
624
|
-
async function withFileLock(filePath, fn) {
|
|
625
|
-
const lockPath = filePath + ".lock";
|
|
626
|
-
await acquireLock(lockPath);
|
|
627
|
-
try {
|
|
628
|
-
return await fn();
|
|
629
|
-
} finally {
|
|
630
|
-
await releaseLock(lockPath);
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
async function acquireLock(lockPath) {
|
|
634
|
-
const startTime = Date.now();
|
|
635
|
-
while (true) {
|
|
636
|
-
try {
|
|
637
|
-
const info = { pid: process.pid, timestamp: Date.now() };
|
|
638
|
-
await import_promises.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
|
|
639
|
-
return;
|
|
640
|
-
} catch (err) {
|
|
641
|
-
if (err.code !== "EEXIST") throw err;
|
|
642
|
-
try {
|
|
643
|
-
const content = await import_promises.default.readFile(lockPath, "utf-8");
|
|
644
|
-
const info = JSON.parse(content);
|
|
645
|
-
const isProcessAlive = isAlive(info.pid);
|
|
646
|
-
const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
|
|
647
|
-
if (!isProcessAlive || isStale) {
|
|
648
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
649
|
-
});
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
} catch {
|
|
653
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
654
|
-
});
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
if (Date.now() - startTime > MAX_WAIT_MS) {
|
|
658
|
-
await import_promises.default.unlink(lockPath).catch(() => {
|
|
659
|
-
});
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
await (0, import_promises2.setTimeout)(RETRY_INTERVAL_MS);
|
|
663
|
-
}
|
|
193
|
+
import_node_fs.default.writeSync(fd, line);
|
|
194
|
+
} catch {
|
|
664
195
|
}
|
|
665
196
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
});
|
|
197
|
+
function registerLogCleanup(fn) {
|
|
198
|
+
cleanups.push(fn);
|
|
669
199
|
}
|
|
670
|
-
function
|
|
200
|
+
function closeLogger(opts) {
|
|
201
|
+
if (fd === null) return;
|
|
202
|
+
if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
|
|
671
203
|
try {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
} catch (err) {
|
|
675
|
-
if (err.code === "EPERM") return true;
|
|
676
|
-
return false;
|
|
204
|
+
import_node_fs.default.closeSync(fd);
|
|
205
|
+
} catch {
|
|
677
206
|
}
|
|
207
|
+
fd = null;
|
|
208
|
+
for (const unsub of cleanups) unsub();
|
|
209
|
+
cleanups = [];
|
|
678
210
|
}
|
|
679
211
|
|
|
680
212
|
// src/claude-code-version.ts
|
|
681
|
-
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
682
|
-
var import_node_path3 = __toESM(require("path"), 1);
|
|
683
213
|
var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
|
|
684
214
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
685
215
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
@@ -691,7 +221,7 @@ function cachePath() {
|
|
|
691
221
|
}
|
|
692
222
|
async function readDiskCache() {
|
|
693
223
|
try {
|
|
694
|
-
const raw = await
|
|
224
|
+
const raw = await import_promises.default.readFile(cachePath(), "utf-8");
|
|
695
225
|
const parsed = JSON.parse(raw);
|
|
696
226
|
if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
|
|
697
227
|
return parsed;
|
|
@@ -703,8 +233,8 @@ async function readDiskCache() {
|
|
|
703
233
|
}
|
|
704
234
|
async function writeDiskCache(data) {
|
|
705
235
|
try {
|
|
706
|
-
await
|
|
707
|
-
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 });
|
|
708
238
|
} catch (err) {
|
|
709
239
|
log(
|
|
710
240
|
"WARN",
|
|
@@ -768,31 +298,6 @@ async function getClaudeCliUserAgent() {
|
|
|
768
298
|
return `claude-cli/${version} (external, cli)`;
|
|
769
299
|
}
|
|
770
300
|
|
|
771
|
-
// src/auth-storage.ts
|
|
772
|
-
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
773
|
-
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
774
|
-
|
|
775
|
-
// src/oauth/anthropic.ts
|
|
776
|
-
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
777
|
-
|
|
778
|
-
// src/oauth/pkce.ts
|
|
779
|
-
function base64urlEncode(bytes) {
|
|
780
|
-
let binary = "";
|
|
781
|
-
for (const byte of bytes) {
|
|
782
|
-
binary += String.fromCharCode(byte);
|
|
783
|
-
}
|
|
784
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
785
|
-
}
|
|
786
|
-
async function generatePKCE() {
|
|
787
|
-
const verifierBytes = new Uint8Array(32);
|
|
788
|
-
crypto.getRandomValues(verifierBytes);
|
|
789
|
-
const verifier = base64urlEncode(verifierBytes);
|
|
790
|
-
const data = new TextEncoder().encode(verifier);
|
|
791
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
792
|
-
const challenge = base64urlEncode(new Uint8Array(hashBuffer));
|
|
793
|
-
return { verifier, challenge };
|
|
794
|
-
}
|
|
795
|
-
|
|
796
301
|
// src/oauth/anthropic.ts
|
|
797
302
|
var CLIENT_ID = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
798
303
|
var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
@@ -1643,8 +1148,71 @@ async function refreshKimiToken(refreshToken) {
|
|
|
1643
1148
|
throw new Error(`Kimi token refresh failed (${status}): ${errorCode || errorDetail(data)}`);
|
|
1644
1149
|
}
|
|
1645
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
|
+
|
|
1646
1213
|
// src/auth-storage.ts
|
|
1647
1214
|
var MOONSHOT_OAUTH_KEY = "moonshot-oauth";
|
|
1215
|
+
var XIAOMI_CREDITS_KEY = "xiaomi-credits";
|
|
1648
1216
|
var REFRESH_SKEW_MS = 6e4;
|
|
1649
1217
|
var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
|
|
1650
1218
|
"glm",
|
|
@@ -1678,6 +1246,17 @@ var AuthStorage = class {
|
|
|
1678
1246
|
await this.ensureLoaded();
|
|
1679
1247
|
return Boolean(this.data[provider]);
|
|
1680
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
|
+
}
|
|
1681
1260
|
/**
|
|
1682
1261
|
* True if the user has any usable auth for the logical provider. For
|
|
1683
1262
|
* `moonshot` this is satisfied by either the Kimi OAuth credential or the
|
|
@@ -1688,6 +1267,9 @@ var AuthStorage = class {
|
|
|
1688
1267
|
if (provider === "moonshot") {
|
|
1689
1268
|
return Boolean(this.data[MOONSHOT_OAUTH_KEY] || this.data["moonshot"]);
|
|
1690
1269
|
}
|
|
1270
|
+
if (provider === "xiaomi") {
|
|
1271
|
+
return Boolean(this.data["xiaomi"] || this.data[XIAOMI_CREDITS_KEY]);
|
|
1272
|
+
}
|
|
1691
1273
|
return Boolean(this.data[provider]);
|
|
1692
1274
|
}
|
|
1693
1275
|
/**
|
|
@@ -1744,123 +1326,593 @@ var AuthStorage = class {
|
|
|
1744
1326
|
delete this.data[provider];
|
|
1745
1327
|
await this.save();
|
|
1746
1328
|
}
|
|
1747
|
-
async clearAll() {
|
|
1748
|
-
this.data = {};
|
|
1749
|
-
await this.save();
|
|
1329
|
+
async clearAll() {
|
|
1330
|
+
this.data = {};
|
|
1331
|
+
await this.save();
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Returns valid credentials, auto-refreshing if expired.
|
|
1335
|
+
* If `forceRefresh` is true, refreshes even if the token hasn't expired
|
|
1336
|
+
* (useful when the provider rejects a token with 401 before its stored expiry).
|
|
1337
|
+
* Throws if not logged in.
|
|
1338
|
+
*/
|
|
1339
|
+
async resolveCredentials(provider, opts) {
|
|
1340
|
+
await this.ensureLoaded();
|
|
1341
|
+
if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
|
|
1342
|
+
for (const key of opts.storageKeys) {
|
|
1343
|
+
const creds2 = this.data[key];
|
|
1344
|
+
if (creds2) return creds2;
|
|
1345
|
+
}
|
|
1346
|
+
throw new NotLoggedInError(provider);
|
|
1347
|
+
}
|
|
1348
|
+
if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
|
|
1349
|
+
try {
|
|
1350
|
+
return await this.resolveCredentials(MOONSHOT_OAUTH_KEY, opts);
|
|
1351
|
+
} catch (err) {
|
|
1352
|
+
if (err instanceof NotLoggedInError && this.data["moonshot"]) {
|
|
1353
|
+
log(
|
|
1354
|
+
"WARN",
|
|
1355
|
+
"auth",
|
|
1356
|
+
'Kimi OAuth credential is no longer valid \u2014 falling back to the Moonshot API key. Run "ggcoder login" and choose Kimi OAuth to restore OAuth auth.'
|
|
1357
|
+
);
|
|
1358
|
+
return this.data["moonshot"];
|
|
1359
|
+
}
|
|
1360
|
+
throw err;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
const creds = this.data[provider];
|
|
1364
|
+
if (!creds) {
|
|
1365
|
+
throw new NotLoggedInError(provider);
|
|
1366
|
+
}
|
|
1367
|
+
if (STATIC_API_KEY_PROVIDERS.has(provider)) {
|
|
1368
|
+
return creds;
|
|
1369
|
+
}
|
|
1370
|
+
if (!opts?.forceRefresh && Date.now() < creds.expiresAt - REFRESH_SKEW_MS) {
|
|
1371
|
+
return creds;
|
|
1372
|
+
}
|
|
1373
|
+
const existing = this.refreshLocks.get(provider);
|
|
1374
|
+
if (existing) return existing;
|
|
1375
|
+
const refreshPromise = withFileLock(this.filePath, async () => {
|
|
1376
|
+
try {
|
|
1377
|
+
const content = await import_promises4.default.readFile(this.filePath, "utf-8");
|
|
1378
|
+
const freshData = JSON.parse(content);
|
|
1379
|
+
const freshCreds = freshData[provider];
|
|
1380
|
+
if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt - REFRESH_SKEW_MS) {
|
|
1381
|
+
this.data[provider] = freshCreds;
|
|
1382
|
+
return freshCreds;
|
|
1383
|
+
}
|
|
1384
|
+
} catch {
|
|
1385
|
+
}
|
|
1386
|
+
const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
|
|
1387
|
+
let refreshed;
|
|
1388
|
+
try {
|
|
1389
|
+
refreshed = await refreshFn(creds.refreshToken);
|
|
1390
|
+
} catch (err) {
|
|
1391
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1392
|
+
const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
|
|
1393
|
+
if (isAuthFailure) {
|
|
1394
|
+
delete this.data[provider];
|
|
1395
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1396
|
+
throw new NotLoggedInError(provider);
|
|
1397
|
+
}
|
|
1398
|
+
throw err;
|
|
1399
|
+
}
|
|
1400
|
+
if (!refreshed.accountId && creds.accountId) {
|
|
1401
|
+
refreshed.accountId = creds.accountId;
|
|
1402
|
+
}
|
|
1403
|
+
if (!refreshed.projectId && creds.projectId) {
|
|
1404
|
+
refreshed.projectId = creds.projectId;
|
|
1405
|
+
}
|
|
1406
|
+
if (!refreshed.baseUrl && creds.baseUrl) {
|
|
1407
|
+
refreshed.baseUrl = creds.baseUrl;
|
|
1408
|
+
}
|
|
1409
|
+
this.data[provider] = refreshed;
|
|
1410
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1411
|
+
return refreshed;
|
|
1412
|
+
});
|
|
1413
|
+
this.refreshLocks.set(provider, refreshPromise);
|
|
1414
|
+
try {
|
|
1415
|
+
return await refreshPromise;
|
|
1416
|
+
} finally {
|
|
1417
|
+
this.refreshLocks.delete(provider);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Returns a valid access token, auto-refreshing if expired.
|
|
1422
|
+
* Throws if not logged in.
|
|
1423
|
+
*/
|
|
1424
|
+
async resolveToken(provider) {
|
|
1425
|
+
const creds = await this.resolveCredentials(provider);
|
|
1426
|
+
return creds.accessToken;
|
|
1427
|
+
}
|
|
1428
|
+
async save() {
|
|
1429
|
+
await withFileLock(this.filePath, async () => {
|
|
1430
|
+
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
async function atomicWriteFile(filePath, content) {
|
|
1435
|
+
const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${import_node_crypto6.default.randomUUID().slice(0, 8)}.tmp`;
|
|
1436
|
+
try {
|
|
1437
|
+
await import_promises4.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
|
|
1438
|
+
await import_promises4.default.rename(tmpPath, filePath);
|
|
1439
|
+
} catch (err) {
|
|
1440
|
+
await import_promises4.default.unlink(tmpPath).catch(() => {
|
|
1441
|
+
});
|
|
1442
|
+
throw err;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
var NotLoggedInError = class extends Error {
|
|
1446
|
+
provider;
|
|
1447
|
+
constructor(provider) {
|
|
1448
|
+
super(`Not logged in to ${provider}. Run "ggcoder login" to authenticate.`);
|
|
1449
|
+
this.name = "NotLoggedInError";
|
|
1450
|
+
this.provider = provider;
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
|
|
1454
|
+
// src/model-registry.ts
|
|
1455
|
+
var MODELS = [
|
|
1456
|
+
// ── Anthropic ──────────────────────────────────────────
|
|
1457
|
+
// NOTE: Claude Mythos 5 (`claude-mythos-5`) is kept commented out — it's a
|
|
1458
|
+
// Project Glasswing (limited, invitation-only) model unavailable to most
|
|
1459
|
+
// users. Re-enable once it's generally available.
|
|
1460
|
+
{
|
|
1461
|
+
id: "claude-fable-5",
|
|
1462
|
+
name: "Claude Fable 5",
|
|
1463
|
+
provider: "anthropic",
|
|
1464
|
+
contextWindow: 1e6,
|
|
1465
|
+
maxOutputTokens: 128e3,
|
|
1466
|
+
supportsThinking: true,
|
|
1467
|
+
supportsImages: true,
|
|
1468
|
+
supportsVideo: false,
|
|
1469
|
+
costTier: "high",
|
|
1470
|
+
maxThinkingLevel: "max"
|
|
1471
|
+
},
|
|
1472
|
+
// {
|
|
1473
|
+
// // Mythos-class model offered through Project Glasswing (limited
|
|
1474
|
+
// // availability, invitation-only). Same underlying model as Fable 5 with
|
|
1475
|
+
// // some safeguards lifted; kept here so approved accounts can select it.
|
|
1476
|
+
// id: "claude-mythos-5",
|
|
1477
|
+
// name: "Claude Mythos 5",
|
|
1478
|
+
// provider: "anthropic",
|
|
1479
|
+
// contextWindow: 1_000_000,
|
|
1480
|
+
// maxOutputTokens: 128_000,
|
|
1481
|
+
// supportsThinking: true,
|
|
1482
|
+
// supportsImages: true,
|
|
1483
|
+
// supportsVideo: false,
|
|
1484
|
+
// costTier: "high",
|
|
1485
|
+
// maxThinkingLevel: "max",
|
|
1486
|
+
// },
|
|
1487
|
+
{
|
|
1488
|
+
id: "claude-opus-4-8",
|
|
1489
|
+
name: "Claude Opus 4.8",
|
|
1490
|
+
provider: "anthropic",
|
|
1491
|
+
contextWindow: 1e6,
|
|
1492
|
+
maxOutputTokens: 128e3,
|
|
1493
|
+
supportsThinking: true,
|
|
1494
|
+
supportsImages: true,
|
|
1495
|
+
supportsVideo: false,
|
|
1496
|
+
costTier: "high",
|
|
1497
|
+
maxThinkingLevel: "max"
|
|
1498
|
+
},
|
|
1499
|
+
{
|
|
1500
|
+
id: "claude-sonnet-5",
|
|
1501
|
+
name: "Claude Sonnet 5",
|
|
1502
|
+
provider: "anthropic",
|
|
1503
|
+
contextWindow: 1e6,
|
|
1504
|
+
maxOutputTokens: 128e3,
|
|
1505
|
+
supportsThinking: true,
|
|
1506
|
+
supportsImages: true,
|
|
1507
|
+
supportsVideo: false,
|
|
1508
|
+
costTier: "medium",
|
|
1509
|
+
maxThinkingLevel: "max"
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
id: "claude-haiku-4-5-20251001",
|
|
1513
|
+
name: "Claude Haiku 4.5",
|
|
1514
|
+
provider: "anthropic",
|
|
1515
|
+
contextWindow: 2e5,
|
|
1516
|
+
maxOutputTokens: 64e3,
|
|
1517
|
+
supportsThinking: true,
|
|
1518
|
+
supportsImages: true,
|
|
1519
|
+
supportsVideo: false,
|
|
1520
|
+
costTier: "low",
|
|
1521
|
+
maxThinkingLevel: "high"
|
|
1522
|
+
},
|
|
1523
|
+
// ── OpenAI (Codex) ─────────────────────────────────────
|
|
1524
|
+
{
|
|
1525
|
+
id: "gpt-5.5",
|
|
1526
|
+
name: "GPT-5.5",
|
|
1527
|
+
provider: "openai",
|
|
1528
|
+
contextWindow: 105e4,
|
|
1529
|
+
codexContextWindow: 272e3,
|
|
1530
|
+
maxOutputTokens: 128e3,
|
|
1531
|
+
supportsThinking: true,
|
|
1532
|
+
supportsImages: true,
|
|
1533
|
+
supportsVideo: false,
|
|
1534
|
+
costTier: "high",
|
|
1535
|
+
maxThinkingLevel: "xhigh"
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
id: "gpt-5.4",
|
|
1539
|
+
name: "GPT-5.4",
|
|
1540
|
+
provider: "openai",
|
|
1541
|
+
contextWindow: 105e4,
|
|
1542
|
+
codexContextWindow: 272e3,
|
|
1543
|
+
maxOutputTokens: 128e3,
|
|
1544
|
+
supportsThinking: true,
|
|
1545
|
+
supportsImages: true,
|
|
1546
|
+
supportsVideo: false,
|
|
1547
|
+
costTier: "high",
|
|
1548
|
+
maxThinkingLevel: "xhigh"
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
id: "gpt-5.4-mini",
|
|
1552
|
+
name: "GPT-5.4 Mini",
|
|
1553
|
+
provider: "openai",
|
|
1554
|
+
contextWindow: 4e5,
|
|
1555
|
+
maxOutputTokens: 128e3,
|
|
1556
|
+
supportsThinking: true,
|
|
1557
|
+
supportsImages: true,
|
|
1558
|
+
supportsVideo: false,
|
|
1559
|
+
costTier: "low",
|
|
1560
|
+
maxThinkingLevel: "xhigh"
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
id: "gpt-5.3-codex",
|
|
1564
|
+
name: "GPT-5.3 Codex",
|
|
1565
|
+
provider: "openai",
|
|
1566
|
+
contextWindow: 4e5,
|
|
1567
|
+
maxOutputTokens: 128e3,
|
|
1568
|
+
supportsThinking: true,
|
|
1569
|
+
supportsImages: true,
|
|
1570
|
+
supportsVideo: false,
|
|
1571
|
+
costTier: "high",
|
|
1572
|
+
maxThinkingLevel: "xhigh"
|
|
1573
|
+
},
|
|
1574
|
+
// ── Sakana (Fugu) ──────────────────────────────────────
|
|
1575
|
+
// Sakana Fugu is a multi-agent system surfaced as a standard LLM via the
|
|
1576
|
+
// OpenAI-compatible Sakana API (https://api.sakana.ai/v1). Both models take
|
|
1577
|
+
// text + image input and only accept "high"/"xhigh" reasoning effort, so the
|
|
1578
|
+
// top tier is `xhigh`. `fugu` routes across all providers; `fugu-ultra` is
|
|
1579
|
+
// the heavier tier (may need larger client timeouts on complex tasks).
|
|
1580
|
+
{
|
|
1581
|
+
id: "fugu",
|
|
1582
|
+
name: "Fugu",
|
|
1583
|
+
provider: "sakana",
|
|
1584
|
+
contextWindow: 1e6,
|
|
1585
|
+
maxOutputTokens: 128e3,
|
|
1586
|
+
supportsThinking: true,
|
|
1587
|
+
supportsImages: true,
|
|
1588
|
+
supportsVideo: false,
|
|
1589
|
+
costTier: "medium",
|
|
1590
|
+
maxThinkingLevel: "xhigh"
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
id: "fugu-ultra",
|
|
1594
|
+
name: "Fugu Ultra",
|
|
1595
|
+
provider: "sakana",
|
|
1596
|
+
contextWindow: 1e6,
|
|
1597
|
+
maxOutputTokens: 128e3,
|
|
1598
|
+
supportsThinking: true,
|
|
1599
|
+
supportsImages: true,
|
|
1600
|
+
supportsVideo: false,
|
|
1601
|
+
costTier: "high",
|
|
1602
|
+
maxThinkingLevel: "xhigh"
|
|
1603
|
+
},
|
|
1604
|
+
// ── Gemini ─────────────────────────────────────────────
|
|
1605
|
+
{
|
|
1606
|
+
id: "gemini-3.1-flash-lite-preview",
|
|
1607
|
+
name: "Gemini 3.1 Flash Lite Preview",
|
|
1608
|
+
provider: "gemini",
|
|
1609
|
+
contextWindow: 1048576,
|
|
1610
|
+
maxOutputTokens: 65536,
|
|
1611
|
+
supportsThinking: true,
|
|
1612
|
+
supportsImages: true,
|
|
1613
|
+
supportsVideo: true,
|
|
1614
|
+
maxVideoBytes: 20 * 1024 * 1024,
|
|
1615
|
+
costTier: "low",
|
|
1616
|
+
maxThinkingLevel: "high"
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
id: "gemini-3.5-flash",
|
|
1620
|
+
name: "Gemini 3.5 Flash",
|
|
1621
|
+
provider: "gemini",
|
|
1622
|
+
contextWindow: 1048576,
|
|
1623
|
+
maxOutputTokens: 65536,
|
|
1624
|
+
supportsThinking: true,
|
|
1625
|
+
supportsImages: true,
|
|
1626
|
+
supportsVideo: true,
|
|
1627
|
+
maxVideoBytes: 20 * 1024 * 1024,
|
|
1628
|
+
costTier: "low",
|
|
1629
|
+
maxThinkingLevel: "high"
|
|
1630
|
+
},
|
|
1631
|
+
// ── Moonshot (Kimi) ────────────────────────────────────
|
|
1632
|
+
{
|
|
1633
|
+
id: "kimi-k2.7-code",
|
|
1634
|
+
name: "Kimi K2.7",
|
|
1635
|
+
provider: "moonshot",
|
|
1636
|
+
contextWindow: 262144,
|
|
1637
|
+
maxOutputTokens: 262144,
|
|
1638
|
+
supportsThinking: true,
|
|
1639
|
+
supportsImages: true,
|
|
1640
|
+
supportsVideo: true,
|
|
1641
|
+
maxVideoBytes: 100 * 1024 * 1024,
|
|
1642
|
+
costTier: "medium",
|
|
1643
|
+
maxThinkingLevel: "high"
|
|
1644
|
+
},
|
|
1645
|
+
// ── Z.AI (GLM) ─────────────────────────────────────────
|
|
1646
|
+
// GLM-5.2: coding-first flagship with a usable 1M-token context window
|
|
1647
|
+
// (5x jump over GLM-5.1's ~200K) and 131K max output. Released 2026-06-13.
|
|
1648
|
+
{
|
|
1649
|
+
id: "glm-5.2",
|
|
1650
|
+
name: "GLM-5.2",
|
|
1651
|
+
provider: "glm",
|
|
1652
|
+
contextWindow: 1e6,
|
|
1653
|
+
maxOutputTokens: 131072,
|
|
1654
|
+
supportsThinking: true,
|
|
1655
|
+
supportsImages: false,
|
|
1656
|
+
supportsVideo: false,
|
|
1657
|
+
costTier: "medium",
|
|
1658
|
+
maxThinkingLevel: "high"
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
id: "glm-5.1",
|
|
1662
|
+
name: "GLM-5.1",
|
|
1663
|
+
provider: "glm",
|
|
1664
|
+
contextWindow: 204800,
|
|
1665
|
+
maxOutputTokens: 131072,
|
|
1666
|
+
supportsThinking: true,
|
|
1667
|
+
supportsImages: false,
|
|
1668
|
+
supportsVideo: false,
|
|
1669
|
+
costTier: "medium",
|
|
1670
|
+
maxThinkingLevel: "high"
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
id: "glm-4.7",
|
|
1674
|
+
name: "GLM-4.7",
|
|
1675
|
+
provider: "glm",
|
|
1676
|
+
contextWindow: 2e5,
|
|
1677
|
+
maxOutputTokens: 131072,
|
|
1678
|
+
supportsThinking: true,
|
|
1679
|
+
supportsImages: false,
|
|
1680
|
+
supportsVideo: false,
|
|
1681
|
+
costTier: "low",
|
|
1682
|
+
maxThinkingLevel: "high"
|
|
1683
|
+
},
|
|
1684
|
+
{
|
|
1685
|
+
id: "glm-4.7-flash",
|
|
1686
|
+
name: "GLM-4.7 Flash",
|
|
1687
|
+
provider: "glm",
|
|
1688
|
+
contextWindow: 2e5,
|
|
1689
|
+
maxOutputTokens: 131072,
|
|
1690
|
+
supportsThinking: true,
|
|
1691
|
+
supportsImages: false,
|
|
1692
|
+
supportsVideo: false,
|
|
1693
|
+
costTier: "low",
|
|
1694
|
+
maxThinkingLevel: "high"
|
|
1695
|
+
},
|
|
1696
|
+
// ── MiniMax ────────────────────────────────────────────
|
|
1697
|
+
{
|
|
1698
|
+
id: "MiniMax-M3",
|
|
1699
|
+
name: "MiniMax M3",
|
|
1700
|
+
provider: "minimax",
|
|
1701
|
+
contextWindow: 1e6,
|
|
1702
|
+
maxOutputTokens: 131072,
|
|
1703
|
+
supportsThinking: true,
|
|
1704
|
+
supportsImages: true,
|
|
1705
|
+
supportsVideo: true,
|
|
1706
|
+
maxVideoBytes: 50 * 1024 * 1024,
|
|
1707
|
+
costTier: "medium",
|
|
1708
|
+
maxThinkingLevel: "high"
|
|
1709
|
+
},
|
|
1710
|
+
// ── Xiaomi (MiMo) ──────────────────────────────────────
|
|
1711
|
+
// Pro series: text-only coding/agentic flagship. The legacy mimo-v2-pro
|
|
1712
|
+
// auto-routes to v2.5 on 2026-06-01 and is fully deprecated by 2026-06-30.
|
|
1713
|
+
{
|
|
1714
|
+
id: "mimo-v2.5-pro",
|
|
1715
|
+
name: "MiMo-V2.5-Pro",
|
|
1716
|
+
provider: "xiaomi",
|
|
1717
|
+
contextWindow: 1e6,
|
|
1718
|
+
maxOutputTokens: 131072,
|
|
1719
|
+
supportsThinking: true,
|
|
1720
|
+
supportsImages: false,
|
|
1721
|
+
supportsVideo: false,
|
|
1722
|
+
costTier: "medium",
|
|
1723
|
+
maxThinkingLevel: "high",
|
|
1724
|
+
authStorageKeys: ["xiaomi", XIAOMI_CREDITS_KEY]
|
|
1725
|
+
},
|
|
1726
|
+
// UltraSpeed: lower-latency sibling of the Pro coding flagship, same
|
|
1727
|
+
// text-only capability surface, premium-priced for the throughput gain.
|
|
1728
|
+
// API-only — not served over the Token Plan endpoint, so credentials
|
|
1729
|
+
// resolve from the distinct API Credits key only (see authStorageKeys doc).
|
|
1730
|
+
{
|
|
1731
|
+
id: "mimo-v2.5-pro-ultraspeed",
|
|
1732
|
+
name: "MiMo-V2.5-Pro-UltraSpeed",
|
|
1733
|
+
provider: "xiaomi",
|
|
1734
|
+
contextWindow: 1e6,
|
|
1735
|
+
maxOutputTokens: 131072,
|
|
1736
|
+
supportsThinking: true,
|
|
1737
|
+
supportsImages: false,
|
|
1738
|
+
supportsVideo: false,
|
|
1739
|
+
costTier: "high",
|
|
1740
|
+
maxThinkingLevel: "high",
|
|
1741
|
+
authStorageKeys: [XIAOMI_CREDITS_KEY]
|
|
1742
|
+
},
|
|
1743
|
+
// Omni series: native full-modal understanding (image + audio + video).
|
|
1744
|
+
// Video/image ride the OpenAI-compatible transport as base64 data URLs
|
|
1745
|
+
// (`video_url`/`image_url`), which the shared transform already emits.
|
|
1746
|
+
{
|
|
1747
|
+
id: "mimo-v2.5",
|
|
1748
|
+
name: "MiMo-V2.5",
|
|
1749
|
+
provider: "xiaomi",
|
|
1750
|
+
contextWindow: 1e6,
|
|
1751
|
+
maxOutputTokens: 131072,
|
|
1752
|
+
supportsThinking: true,
|
|
1753
|
+
supportsImages: true,
|
|
1754
|
+
supportsVideo: true,
|
|
1755
|
+
maxVideoBytes: 36 * 1024 * 1024,
|
|
1756
|
+
costTier: "medium",
|
|
1757
|
+
maxThinkingLevel: "high",
|
|
1758
|
+
authStorageKeys: ["xiaomi", XIAOMI_CREDITS_KEY]
|
|
1759
|
+
},
|
|
1760
|
+
// ── DeepSeek ───────────────────────────────────────────
|
|
1761
|
+
{
|
|
1762
|
+
id: "deepseek-v4-pro",
|
|
1763
|
+
name: "DeepSeek V4 Pro",
|
|
1764
|
+
provider: "deepseek",
|
|
1765
|
+
contextWindow: 1048576,
|
|
1766
|
+
maxOutputTokens: 384e3,
|
|
1767
|
+
supportsThinking: true,
|
|
1768
|
+
supportsImages: false,
|
|
1769
|
+
supportsVideo: false,
|
|
1770
|
+
costTier: "high",
|
|
1771
|
+
// DeepSeek V4 maps `xhigh` → its internal `max` tier.
|
|
1772
|
+
maxThinkingLevel: "xhigh"
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
id: "deepseek-v4-flash",
|
|
1776
|
+
name: "DeepSeek V4 Flash",
|
|
1777
|
+
provider: "deepseek",
|
|
1778
|
+
contextWindow: 1048576,
|
|
1779
|
+
maxOutputTokens: 384e3,
|
|
1780
|
+
supportsThinking: true,
|
|
1781
|
+
supportsImages: false,
|
|
1782
|
+
supportsVideo: false,
|
|
1783
|
+
costTier: "low",
|
|
1784
|
+
maxThinkingLevel: "xhigh"
|
|
1785
|
+
},
|
|
1786
|
+
// ── OpenRouter ─────────────────────────────────────────
|
|
1787
|
+
{
|
|
1788
|
+
id: "qwen/qwen3.6-plus",
|
|
1789
|
+
name: "Qwen3.6-Plus",
|
|
1790
|
+
provider: "openrouter",
|
|
1791
|
+
contextWindow: 1e6,
|
|
1792
|
+
maxOutputTokens: 65536,
|
|
1793
|
+
supportsThinking: true,
|
|
1794
|
+
supportsImages: false,
|
|
1795
|
+
supportsVideo: false,
|
|
1796
|
+
costTier: "medium",
|
|
1797
|
+
maxThinkingLevel: "high"
|
|
1798
|
+
}
|
|
1799
|
+
];
|
|
1800
|
+
function getModel(id) {
|
|
1801
|
+
return MODELS.find((m) => m.id === id);
|
|
1802
|
+
}
|
|
1803
|
+
function getModelsForProvider(provider) {
|
|
1804
|
+
return MODELS.filter((m) => m.provider === provider);
|
|
1805
|
+
}
|
|
1806
|
+
function getAuthStorageKeys(provider, modelId) {
|
|
1807
|
+
const model = MODELS.find((m) => m.id === modelId && m.provider === provider);
|
|
1808
|
+
return model?.authStorageKeys ?? [provider];
|
|
1809
|
+
}
|
|
1810
|
+
function getAuthStorageKey(provider, modelId) {
|
|
1811
|
+
return getAuthStorageKeys(provider, modelId)[0];
|
|
1812
|
+
}
|
|
1813
|
+
var DEFAULT_MAX_VIDEO_BYTES = 20 * 1024 * 1024;
|
|
1814
|
+
function getVideoByteLimit(modelId) {
|
|
1815
|
+
const model = getModel(modelId);
|
|
1816
|
+
if (!model?.supportsVideo) return void 0;
|
|
1817
|
+
return model.maxVideoBytes ?? DEFAULT_MAX_VIDEO_BYTES;
|
|
1818
|
+
}
|
|
1819
|
+
function getDefaultModel(provider) {
|
|
1820
|
+
if (provider === "xiaomi") return MODELS.find((m) => m.id === "mimo-v2.5-pro");
|
|
1821
|
+
if (provider === "openai") return MODELS.find((m) => m.id === "gpt-5.5");
|
|
1822
|
+
if (provider === "gemini") return MODELS.find((m) => m.id === "gemini-3.1-flash-lite-preview");
|
|
1823
|
+
if (provider === "glm") return MODELS.find((m) => m.id === "glm-5.2");
|
|
1824
|
+
if (provider === "moonshot") return MODELS.find((m) => m.id === "kimi-k2.7-code");
|
|
1825
|
+
if (provider === "minimax") return MODELS.find((m) => m.id === "MiniMax-M3");
|
|
1826
|
+
if (provider === "deepseek") return MODELS.find((m) => m.id === "deepseek-v4-pro");
|
|
1827
|
+
if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
|
|
1828
|
+
if (provider === "sakana") return MODELS.find((m) => m.id === "fugu");
|
|
1829
|
+
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
1830
|
+
}
|
|
1831
|
+
function usesOpenAICodexTransport(options) {
|
|
1832
|
+
return options?.provider === "openai" && Boolean(options.accountId);
|
|
1833
|
+
}
|
|
1834
|
+
function getContextWindow(modelId, options) {
|
|
1835
|
+
const model = getModel(modelId);
|
|
1836
|
+
if (!model) return 2e5;
|
|
1837
|
+
if (usesOpenAICodexTransport(options) && model.codexContextWindow) {
|
|
1838
|
+
return model.codexContextWindow;
|
|
1750
1839
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
if (provider === "moonshot" && this.data[MOONSHOT_OAUTH_KEY]) {
|
|
1760
|
-
try {
|
|
1761
|
-
return await this.resolveCredentials(MOONSHOT_OAUTH_KEY, opts);
|
|
1762
|
-
} catch (err) {
|
|
1763
|
-
if (err instanceof NotLoggedInError && this.data["moonshot"]) {
|
|
1764
|
-
log(
|
|
1765
|
-
"WARN",
|
|
1766
|
-
"auth",
|
|
1767
|
-
'Kimi OAuth credential is no longer valid \u2014 falling back to the Moonshot API key. Run "ggcoder login" and choose Kimi OAuth to restore OAuth auth.'
|
|
1768
|
-
);
|
|
1769
|
-
return this.data["moonshot"];
|
|
1770
|
-
}
|
|
1771
|
-
throw err;
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
const creds = this.data[provider];
|
|
1775
|
-
if (!creds) {
|
|
1776
|
-
throw new NotLoggedInError(provider);
|
|
1777
|
-
}
|
|
1778
|
-
if (STATIC_API_KEY_PROVIDERS.has(provider)) {
|
|
1779
|
-
return creds;
|
|
1780
|
-
}
|
|
1781
|
-
if (!opts?.forceRefresh && Date.now() < creds.expiresAt - REFRESH_SKEW_MS) {
|
|
1782
|
-
return creds;
|
|
1783
|
-
}
|
|
1784
|
-
const existing = this.refreshLocks.get(provider);
|
|
1785
|
-
if (existing) return existing;
|
|
1786
|
-
const refreshPromise = withFileLock(this.filePath, async () => {
|
|
1787
|
-
try {
|
|
1788
|
-
const content = await import_promises4.default.readFile(this.filePath, "utf-8");
|
|
1789
|
-
const freshData = JSON.parse(content);
|
|
1790
|
-
const freshCreds = freshData[provider];
|
|
1791
|
-
if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt - REFRESH_SKEW_MS) {
|
|
1792
|
-
this.data[provider] = freshCreds;
|
|
1793
|
-
return freshCreds;
|
|
1794
|
-
}
|
|
1795
|
-
} catch {
|
|
1796
|
-
}
|
|
1797
|
-
const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : refreshOpenAIToken;
|
|
1798
|
-
let refreshed;
|
|
1799
|
-
try {
|
|
1800
|
-
refreshed = await refreshFn(creds.refreshToken);
|
|
1801
|
-
} catch (err) {
|
|
1802
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1803
|
-
const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
|
|
1804
|
-
if (isAuthFailure) {
|
|
1805
|
-
delete this.data[provider];
|
|
1806
|
-
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1807
|
-
throw new NotLoggedInError(provider);
|
|
1808
|
-
}
|
|
1809
|
-
throw err;
|
|
1810
|
-
}
|
|
1811
|
-
if (!refreshed.accountId && creds.accountId) {
|
|
1812
|
-
refreshed.accountId = creds.accountId;
|
|
1813
|
-
}
|
|
1814
|
-
if (!refreshed.projectId && creds.projectId) {
|
|
1815
|
-
refreshed.projectId = creds.projectId;
|
|
1816
|
-
}
|
|
1817
|
-
if (!refreshed.baseUrl && creds.baseUrl) {
|
|
1818
|
-
refreshed.baseUrl = creds.baseUrl;
|
|
1819
|
-
}
|
|
1820
|
-
this.data[provider] = refreshed;
|
|
1821
|
-
await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
|
|
1822
|
-
return refreshed;
|
|
1823
|
-
});
|
|
1824
|
-
this.refreshLocks.set(provider, refreshPromise);
|
|
1825
|
-
try {
|
|
1826
|
-
return await refreshPromise;
|
|
1827
|
-
} finally {
|
|
1828
|
-
this.refreshLocks.delete(provider);
|
|
1829
|
-
}
|
|
1840
|
+
return model.contextWindow;
|
|
1841
|
+
}
|
|
1842
|
+
function getMaxThinkingLevel(modelId) {
|
|
1843
|
+
return getModel(modelId)?.maxThinkingLevel ?? "high";
|
|
1844
|
+
}
|
|
1845
|
+
function getSummaryModel(provider, currentModelId) {
|
|
1846
|
+
if (provider === "anthropic") {
|
|
1847
|
+
return MODELS.find((m) => m.id === "claude-sonnet-5");
|
|
1830
1848
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
*/
|
|
1835
|
-
async resolveToken(provider) {
|
|
1836
|
-
const creds = await this.resolveCredentials(provider);
|
|
1837
|
-
return creds.accessToken;
|
|
1849
|
+
if (provider === "openai" || provider === "glm" || provider === "deepseek") {
|
|
1850
|
+
const low = getModelsForProvider(provider).find((m) => m.costTier === "low");
|
|
1851
|
+
if (low) return low;
|
|
1838
1852
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1853
|
+
return getModel(currentModelId) ?? getDefaultModel(provider);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// src/thinking-level.ts
|
|
1857
|
+
var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
|
|
1858
|
+
var SAKANA_THINKING_LEVELS = ["high", "xhigh"];
|
|
1859
|
+
var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
|
|
1860
|
+
"low",
|
|
1861
|
+
"medium",
|
|
1862
|
+
"high",
|
|
1863
|
+
"xhigh",
|
|
1864
|
+
"max"
|
|
1865
|
+
];
|
|
1866
|
+
var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
|
|
1867
|
+
"low",
|
|
1868
|
+
"medium",
|
|
1869
|
+
"high",
|
|
1870
|
+
"max"
|
|
1871
|
+
];
|
|
1872
|
+
function isOpenAIGptModel(provider, model) {
|
|
1873
|
+
return provider === "openai" && model.startsWith("gpt-");
|
|
1874
|
+
}
|
|
1875
|
+
function isSakanaModel(provider) {
|
|
1876
|
+
return provider === "sakana";
|
|
1877
|
+
}
|
|
1878
|
+
function isAnthropicOpus48Or47Model(provider, model) {
|
|
1879
|
+
return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
|
|
1880
|
+
}
|
|
1881
|
+
function isAnthropicAdaptiveModel(provider, model) {
|
|
1882
|
+
return provider === "anthropic" && /opus-4-8|opus-4-7|opus-4-6|sonnet-5|fable-5|mythos-5/.test(model);
|
|
1883
|
+
}
|
|
1884
|
+
function getSupportedThinkingLevels(provider, model) {
|
|
1885
|
+
const maxLevel = getMaxThinkingLevel(model);
|
|
1886
|
+
if (isAnthropicAdaptiveModel(provider, model)) {
|
|
1887
|
+
const levels = isAnthropicOpus48Or47Model(provider, model) ? ANTHROPIC_OPUS_48_47_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
|
|
1888
|
+
const maxIndex2 = levels.indexOf(maxLevel);
|
|
1889
|
+
if (maxIndex2 === -1) return ["low", "medium", "high"];
|
|
1890
|
+
return levels.slice(0, maxIndex2 + 1);
|
|
1843
1891
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
await import_promises4.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
|
|
1849
|
-
await import_promises4.default.rename(tmpPath, filePath);
|
|
1850
|
-
} catch (err) {
|
|
1851
|
-
await import_promises4.default.unlink(tmpPath).catch(() => {
|
|
1852
|
-
});
|
|
1853
|
-
throw err;
|
|
1892
|
+
if (isSakanaModel(provider)) {
|
|
1893
|
+
const maxIndex2 = SAKANA_THINKING_LEVELS.indexOf(maxLevel);
|
|
1894
|
+
if (maxIndex2 === -1) return SAKANA_THINKING_LEVELS;
|
|
1895
|
+
return SAKANA_THINKING_LEVELS.slice(0, maxIndex2 + 1);
|
|
1854
1896
|
}
|
|
1897
|
+
if (!isOpenAIGptModel(provider, model)) return [maxLevel];
|
|
1898
|
+
const maxIndex = OPENAI_GPT_THINKING_LEVELS.indexOf(maxLevel);
|
|
1899
|
+
if (maxIndex === -1) return ["medium"];
|
|
1900
|
+
return OPENAI_GPT_THINKING_LEVELS.slice(0, maxIndex + 1);
|
|
1855
1901
|
}
|
|
1856
|
-
|
|
1857
|
-
provider;
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1902
|
+
function isThinkingLevelSupported(provider, model, level) {
|
|
1903
|
+
return getSupportedThinkingLevels(provider, model).includes(level);
|
|
1904
|
+
}
|
|
1905
|
+
function getNextThinkingLevel(provider, model, current) {
|
|
1906
|
+
const supportedLevels = getSupportedThinkingLevels(provider, model);
|
|
1907
|
+
const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model) || isSakanaModel(provider);
|
|
1908
|
+
if (!shouldCycleLevels) {
|
|
1909
|
+
return current ? void 0 : supportedLevels[0];
|
|
1862
1910
|
}
|
|
1863
|
-
|
|
1911
|
+
if (!current) return supportedLevels[0];
|
|
1912
|
+
const index = supportedLevels.indexOf(current);
|
|
1913
|
+
if (index === -1) return supportedLevels[0];
|
|
1914
|
+
return supportedLevels[index + 1];
|
|
1915
|
+
}
|
|
1864
1916
|
|
|
1865
1917
|
// src/telegram.ts
|
|
1866
1918
|
var TELEGRAM_API = "https://api.telegram.org";
|
|
@@ -2345,12 +2397,15 @@ function createAutoUpdater(config) {
|
|
|
2345
2397
|
MOONSHOT_OAUTH_KEY,
|
|
2346
2398
|
NotLoggedInError,
|
|
2347
2399
|
TelegramBot,
|
|
2400
|
+
XIAOMI_CREDITS_KEY,
|
|
2348
2401
|
closeLogger,
|
|
2349
2402
|
createAutoUpdater,
|
|
2350
2403
|
decodeOggOpus,
|
|
2351
2404
|
downmixToMono,
|
|
2352
2405
|
generatePKCE,
|
|
2353
2406
|
getAppPaths,
|
|
2407
|
+
getAuthStorageKey,
|
|
2408
|
+
getAuthStorageKeys,
|
|
2354
2409
|
getClaudeCliUserAgent,
|
|
2355
2410
|
getClaudeCodeVersion,
|
|
2356
2411
|
getContextWindow,
|