@kenkaiiii/gg-core 4.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/index.cjs ADDED
@@ -0,0 +1,1988 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AuthStorage: () => AuthStorage,
34
+ MODELS: () => MODELS,
35
+ NotLoggedInError: () => NotLoggedInError,
36
+ TelegramBot: () => TelegramBot,
37
+ closeLogger: () => closeLogger,
38
+ createAutoUpdater: () => createAutoUpdater,
39
+ decodeOggOpus: () => decodeOggOpus,
40
+ downmixToMono: () => downmixToMono,
41
+ generatePKCE: () => generatePKCE,
42
+ getAppPaths: () => getAppPaths,
43
+ getClaudeCliUserAgent: () => getClaudeCliUserAgent,
44
+ getClaudeCodeVersion: () => getClaudeCodeVersion,
45
+ getContextWindow: () => getContextWindow,
46
+ getDefaultModel: () => getDefaultModel,
47
+ getMaxThinkingLevel: () => getMaxThinkingLevel,
48
+ getModel: () => getModel,
49
+ getModelsForProvider: () => getModelsForProvider,
50
+ getNextThinkingLevel: () => getNextThinkingLevel,
51
+ getSessionId: () => getSessionId,
52
+ getSummaryModel: () => getSummaryModel,
53
+ getSupportedThinkingLevels: () => getSupportedThinkingLevels,
54
+ isLoggerOpen: () => isLoggerOpen,
55
+ isModelLoaded: () => isModelLoaded,
56
+ isThinkingLevelSupported: () => isThinkingLevelSupported,
57
+ log: () => log,
58
+ loginAnthropic: () => loginAnthropic,
59
+ loginGemini: () => loginGemini,
60
+ loginOpenAI: () => loginOpenAI,
61
+ openLog: () => openLog,
62
+ refreshAnthropicToken: () => refreshAnthropicToken,
63
+ refreshGeminiToken: () => refreshGeminiToken,
64
+ refreshOpenAIToken: () => refreshOpenAIToken,
65
+ registerLogCleanup: () => registerLogCleanup,
66
+ resample: () => resample,
67
+ setProgressCallback: () => setProgressCallback,
68
+ transcribeVoice: () => transcribeVoice,
69
+ usesOpenAICodexTransport: () => usesOpenAICodexTransport,
70
+ withFileLock: () => withFileLock
71
+ });
72
+ module.exports = __toCommonJS(index_exports);
73
+
74
+ // src/model-registry.ts
75
+ var MODELS = [
76
+ // ── Anthropic ──────────────────────────────────────────
77
+ {
78
+ id: "claude-opus-4-8",
79
+ name: "Claude Opus 4.8",
80
+ provider: "anthropic",
81
+ contextWindow: 1e6,
82
+ maxOutputTokens: 128e3,
83
+ supportsThinking: true,
84
+ supportsImages: true,
85
+ supportsVideo: false,
86
+ costTier: "high",
87
+ maxThinkingLevel: "max"
88
+ },
89
+ {
90
+ id: "claude-sonnet-4-6",
91
+ name: "Claude Sonnet 4.6",
92
+ provider: "anthropic",
93
+ contextWindow: 1e6,
94
+ maxOutputTokens: 64e3,
95
+ supportsThinking: true,
96
+ supportsImages: true,
97
+ supportsVideo: false,
98
+ costTier: "medium",
99
+ maxThinkingLevel: "max"
100
+ },
101
+ {
102
+ id: "claude-haiku-4-5-20251001",
103
+ name: "Claude Haiku 4.5",
104
+ provider: "anthropic",
105
+ contextWindow: 2e5,
106
+ maxOutputTokens: 64e3,
107
+ supportsThinking: true,
108
+ supportsImages: true,
109
+ supportsVideo: false,
110
+ costTier: "low",
111
+ maxThinkingLevel: "high"
112
+ },
113
+ // ── OpenAI (Codex) ─────────────────────────────────────
114
+ {
115
+ id: "gpt-5.5",
116
+ name: "GPT-5.5",
117
+ provider: "openai",
118
+ contextWindow: 105e4,
119
+ codexContextWindow: 272e3,
120
+ maxOutputTokens: 128e3,
121
+ supportsThinking: true,
122
+ supportsImages: true,
123
+ supportsVideo: false,
124
+ costTier: "high",
125
+ maxThinkingLevel: "xhigh"
126
+ },
127
+ {
128
+ id: "gpt-5.4",
129
+ name: "GPT-5.4",
130
+ provider: "openai",
131
+ contextWindow: 105e4,
132
+ codexContextWindow: 272e3,
133
+ maxOutputTokens: 128e3,
134
+ supportsThinking: true,
135
+ supportsImages: true,
136
+ supportsVideo: false,
137
+ costTier: "high",
138
+ maxThinkingLevel: "xhigh"
139
+ },
140
+ {
141
+ id: "gpt-5.4-mini",
142
+ name: "GPT-5.4 Mini",
143
+ provider: "openai",
144
+ contextWindow: 4e5,
145
+ maxOutputTokens: 128e3,
146
+ supportsThinking: true,
147
+ supportsImages: true,
148
+ supportsVideo: false,
149
+ costTier: "low",
150
+ maxThinkingLevel: "xhigh"
151
+ },
152
+ {
153
+ id: "gpt-5.3-codex",
154
+ name: "GPT-5.3 Codex",
155
+ provider: "openai",
156
+ contextWindow: 4e5,
157
+ maxOutputTokens: 128e3,
158
+ supportsThinking: true,
159
+ supportsImages: true,
160
+ supportsVideo: false,
161
+ costTier: "high",
162
+ maxThinkingLevel: "xhigh"
163
+ },
164
+ // ── Gemini ─────────────────────────────────────────────
165
+ {
166
+ id: "gemini-3.1-flash-lite-preview",
167
+ name: "Gemini 3.1 Flash Lite Preview",
168
+ provider: "gemini",
169
+ contextWindow: 1048576,
170
+ maxOutputTokens: 65536,
171
+ supportsThinking: true,
172
+ supportsImages: true,
173
+ supportsVideo: true,
174
+ costTier: "low",
175
+ maxThinkingLevel: "high"
176
+ },
177
+ {
178
+ id: "gemini-3.5-flash",
179
+ name: "Gemini 3.5 Flash",
180
+ provider: "gemini",
181
+ contextWindow: 1048576,
182
+ maxOutputTokens: 65536,
183
+ supportsThinking: true,
184
+ supportsImages: true,
185
+ supportsVideo: true,
186
+ costTier: "low",
187
+ maxThinkingLevel: "high"
188
+ },
189
+ // ── Moonshot (Kimi) ────────────────────────────────────
190
+ {
191
+ id: "kimi-k2.6",
192
+ name: "Kimi K2.6",
193
+ provider: "moonshot",
194
+ contextWindow: 262144,
195
+ maxOutputTokens: 262144,
196
+ supportsThinking: true,
197
+ supportsImages: true,
198
+ supportsVideo: true,
199
+ costTier: "medium",
200
+ maxThinkingLevel: "high"
201
+ },
202
+ // ── Z.AI (GLM) ─────────────────────────────────────────
203
+ {
204
+ id: "glm-5.1",
205
+ name: "GLM-5.1",
206
+ provider: "glm",
207
+ contextWindow: 204800,
208
+ maxOutputTokens: 131072,
209
+ supportsThinking: true,
210
+ supportsImages: false,
211
+ supportsVideo: false,
212
+ costTier: "medium",
213
+ maxThinkingLevel: "high"
214
+ },
215
+ {
216
+ id: "glm-4.7",
217
+ name: "GLM-4.7",
218
+ provider: "glm",
219
+ contextWindow: 2e5,
220
+ maxOutputTokens: 131072,
221
+ supportsThinking: true,
222
+ supportsImages: false,
223
+ supportsVideo: false,
224
+ costTier: "low",
225
+ maxThinkingLevel: "high"
226
+ },
227
+ {
228
+ id: "glm-4.7-flash",
229
+ name: "GLM-4.7 Flash",
230
+ provider: "glm",
231
+ contextWindow: 2e5,
232
+ maxOutputTokens: 131072,
233
+ supportsThinking: true,
234
+ supportsImages: false,
235
+ supportsVideo: false,
236
+ costTier: "low",
237
+ maxThinkingLevel: "high"
238
+ },
239
+ // ── MiniMax ────────────────────────────────────────────
240
+ {
241
+ id: "MiniMax-M3",
242
+ name: "MiniMax M3",
243
+ provider: "minimax",
244
+ contextWindow: 1e6,
245
+ maxOutputTokens: 131072,
246
+ supportsThinking: true,
247
+ supportsImages: true,
248
+ supportsVideo: true,
249
+ costTier: "medium",
250
+ maxThinkingLevel: "high"
251
+ },
252
+ // ── Xiaomi (MiMo) ──────────────────────────────────────
253
+ {
254
+ id: "mimo-v2-pro",
255
+ name: "MiMo-V2-Pro",
256
+ provider: "xiaomi",
257
+ contextWindow: 1e6,
258
+ maxOutputTokens: 131072,
259
+ supportsThinking: true,
260
+ supportsImages: false,
261
+ supportsVideo: false,
262
+ costTier: "medium",
263
+ maxThinkingLevel: "high"
264
+ },
265
+ // ── DeepSeek ───────────────────────────────────────────
266
+ {
267
+ id: "deepseek-v4-pro",
268
+ name: "DeepSeek V4 Pro",
269
+ provider: "deepseek",
270
+ contextWindow: 1048576,
271
+ maxOutputTokens: 384e3,
272
+ supportsThinking: true,
273
+ supportsImages: false,
274
+ supportsVideo: false,
275
+ costTier: "high",
276
+ // DeepSeek V4 maps `xhigh` → its internal `max` tier.
277
+ maxThinkingLevel: "xhigh"
278
+ },
279
+ {
280
+ id: "deepseek-v4-flash",
281
+ name: "DeepSeek V4 Flash",
282
+ provider: "deepseek",
283
+ contextWindow: 1048576,
284
+ maxOutputTokens: 384e3,
285
+ supportsThinking: true,
286
+ supportsImages: false,
287
+ supportsVideo: false,
288
+ costTier: "low",
289
+ maxThinkingLevel: "xhigh"
290
+ },
291
+ // ── OpenRouter ─────────────────────────────────────────
292
+ {
293
+ id: "qwen/qwen3.6-plus",
294
+ name: "Qwen3.6-Plus",
295
+ provider: "openrouter",
296
+ contextWindow: 1e6,
297
+ maxOutputTokens: 65536,
298
+ supportsThinking: true,
299
+ supportsImages: false,
300
+ supportsVideo: false,
301
+ costTier: "medium",
302
+ maxThinkingLevel: "high"
303
+ }
304
+ ];
305
+ function getModel(id) {
306
+ return MODELS.find((m) => m.id === id);
307
+ }
308
+ function getModelsForProvider(provider) {
309
+ return MODELS.filter((m) => m.provider === provider);
310
+ }
311
+ function getDefaultModel(provider) {
312
+ if (provider === "xiaomi") return MODELS.find((m) => m.id === "mimo-v2-pro");
313
+ if (provider === "openai") return MODELS.find((m) => m.id === "gpt-5.5");
314
+ if (provider === "gemini") return MODELS.find((m) => m.id === "gemini-3.1-flash-lite-preview");
315
+ if (provider === "glm") return MODELS.find((m) => m.id === "glm-5.1");
316
+ if (provider === "moonshot") return MODELS.find((m) => m.id === "kimi-k2.6");
317
+ if (provider === "minimax") return MODELS.find((m) => m.id === "MiniMax-M3");
318
+ if (provider === "deepseek") return MODELS.find((m) => m.id === "deepseek-v4-pro");
319
+ if (provider === "openrouter") return MODELS.find((m) => m.id === "qwen/qwen3.6-plus");
320
+ return MODELS.find((m) => m.id === "claude-sonnet-4-6");
321
+ }
322
+ function usesOpenAICodexTransport(options) {
323
+ return options?.provider === "openai" && Boolean(options.accountId);
324
+ }
325
+ function getContextWindow(modelId, options) {
326
+ const model = getModel(modelId);
327
+ if (!model) return 2e5;
328
+ if (usesOpenAICodexTransport(options) && model.codexContextWindow) {
329
+ return model.codexContextWindow;
330
+ }
331
+ return model.contextWindow;
332
+ }
333
+ function getMaxThinkingLevel(modelId) {
334
+ return getModel(modelId)?.maxThinkingLevel ?? "high";
335
+ }
336
+ function getSummaryModel(provider, currentModelId) {
337
+ if (provider === "anthropic") {
338
+ return MODELS.find((m) => m.id === "claude-sonnet-4-6");
339
+ }
340
+ if (provider === "openai" || provider === "glm" || provider === "deepseek") {
341
+ const low = getModelsForProvider(provider).find((m) => m.costTier === "low");
342
+ if (low) return low;
343
+ }
344
+ return getModel(currentModelId) ?? getDefaultModel(provider);
345
+ }
346
+
347
+ // src/thinking-level.ts
348
+ var OPENAI_GPT_THINKING_LEVELS = ["medium", "high", "xhigh"];
349
+ var ANTHROPIC_OPUS_48_47_THINKING_LEVELS = [
350
+ "low",
351
+ "medium",
352
+ "high",
353
+ "xhigh",
354
+ "max"
355
+ ];
356
+ var ANTHROPIC_ADAPTIVE_THINKING_LEVELS = [
357
+ "low",
358
+ "medium",
359
+ "high",
360
+ "max"
361
+ ];
362
+ function isOpenAIGptModel(provider, model) {
363
+ return provider === "openai" && model.startsWith("gpt-");
364
+ }
365
+ function isAnthropicOpus48Or47Model(provider, model) {
366
+ return provider === "anthropic" && /opus-4-8|opus-4-7/.test(model);
367
+ }
368
+ function isAnthropicAdaptiveModel(provider, model) {
369
+ return provider === "anthropic" && /opus-4-8|opus-4-7|opus-4-6|sonnet-4-6/.test(model);
370
+ }
371
+ function getSupportedThinkingLevels(provider, model) {
372
+ const maxLevel = getMaxThinkingLevel(model);
373
+ if (isAnthropicAdaptiveModel(provider, model)) {
374
+ const levels = isAnthropicOpus48Or47Model(provider, model) ? ANTHROPIC_OPUS_48_47_THINKING_LEVELS : ANTHROPIC_ADAPTIVE_THINKING_LEVELS;
375
+ const maxIndex2 = levels.indexOf(maxLevel);
376
+ if (maxIndex2 === -1) return ["low", "medium", "high"];
377
+ return levels.slice(0, maxIndex2 + 1);
378
+ }
379
+ if (!isOpenAIGptModel(provider, model)) return [maxLevel];
380
+ const maxIndex = OPENAI_GPT_THINKING_LEVELS.indexOf(maxLevel);
381
+ if (maxIndex === -1) return ["medium"];
382
+ return OPENAI_GPT_THINKING_LEVELS.slice(0, maxIndex + 1);
383
+ }
384
+ function isThinkingLevelSupported(provider, model, level) {
385
+ return getSupportedThinkingLevels(provider, model).includes(level);
386
+ }
387
+ function getNextThinkingLevel(provider, model, current) {
388
+ const supportedLevels = getSupportedThinkingLevels(provider, model);
389
+ const shouldCycleLevels = isOpenAIGptModel(provider, model) || isAnthropicAdaptiveModel(provider, model);
390
+ if (!shouldCycleLevels) {
391
+ return current ? void 0 : supportedLevels[0];
392
+ }
393
+ if (!current) return supportedLevels[0];
394
+ const index = supportedLevels.indexOf(current);
395
+ if (index === -1) return supportedLevels[0];
396
+ return supportedLevels[index + 1];
397
+ }
398
+
399
+ // src/paths.ts
400
+ var import_node_path = __toESM(require("path"), 1);
401
+ var import_node_os = __toESM(require("os"), 1);
402
+ function getAppPaths() {
403
+ const agentDir = import_node_path.default.join(import_node_os.default.homedir(), ".gg");
404
+ return {
405
+ agentDir,
406
+ sessionsDir: import_node_path.default.join(agentDir, "sessions"),
407
+ settingsFile: import_node_path.default.join(agentDir, "settings.json"),
408
+ authFile: import_node_path.default.join(agentDir, "auth.json"),
409
+ telegramFile: import_node_path.default.join(agentDir, "telegram.json"),
410
+ agentHomeFile: import_node_path.default.join(agentDir, "agent-home.json"),
411
+ mcpFile: import_node_path.default.join(agentDir, "mcp.json"),
412
+ logFile: import_node_path.default.join(agentDir, "debug.log"),
413
+ skillsDir: import_node_path.default.join(agentDir, "skills"),
414
+ extensionsDir: import_node_path.default.join(agentDir, "extensions"),
415
+ agentsDir: import_node_path.default.join(agentDir, "agents")
416
+ };
417
+ }
418
+
419
+ // src/logger.ts
420
+ var import_node_fs = __toESM(require("fs"), 1);
421
+ var import_node_path2 = __toESM(require("path"), 1);
422
+ var import_node_crypto = require("crypto");
423
+ var MAX_BYTES = 10 * 1024 * 1024;
424
+ var fd = null;
425
+ var sessionId = "";
426
+ var appName = "app";
427
+ var cleanups = [];
428
+ function rotateIfNeeded(filePath) {
429
+ try {
430
+ const st = import_node_fs.default.statSync(filePath);
431
+ if (st.size < MAX_BYTES) return;
432
+ const rotated = `${filePath}.1`;
433
+ try {
434
+ import_node_fs.default.unlinkSync(rotated);
435
+ } catch {
436
+ }
437
+ import_node_fs.default.renameSync(filePath, rotated);
438
+ } catch {
439
+ }
440
+ }
441
+ function openLog(filePath, name) {
442
+ if (fd !== null) return false;
443
+ appName = name;
444
+ try {
445
+ import_node_fs.default.mkdirSync(import_node_path2.default.dirname(filePath), { recursive: true, mode: 448 });
446
+ } catch {
447
+ }
448
+ rotateIfNeeded(filePath);
449
+ try {
450
+ fd = import_node_fs.default.openSync(filePath, "a");
451
+ } catch {
452
+ return false;
453
+ }
454
+ sessionId = (0, import_node_crypto.randomBytes)(4).toString("hex");
455
+ try {
456
+ import_node_fs.default.writeSync(fd, "\n");
457
+ } catch {
458
+ }
459
+ return true;
460
+ }
461
+ function getSessionId() {
462
+ return sessionId;
463
+ }
464
+ function isLoggerOpen() {
465
+ return fd !== null;
466
+ }
467
+ function log(level, category, message, data) {
468
+ if (fd === null) return;
469
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
470
+ let line = `[${ts}] [sid=${sessionId}] [${level}] [${category}] ${message}`;
471
+ if (data) {
472
+ const pairs = Object.entries(data).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(" ");
473
+ if (pairs) line += ` ${pairs}`;
474
+ }
475
+ line += "\n";
476
+ try {
477
+ import_node_fs.default.writeSync(fd, line);
478
+ } catch {
479
+ }
480
+ }
481
+ function registerLogCleanup(fn) {
482
+ cleanups.push(fn);
483
+ }
484
+ function closeLogger(opts) {
485
+ if (fd === null) return;
486
+ if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
487
+ try {
488
+ import_node_fs.default.closeSync(fd);
489
+ } catch {
490
+ }
491
+ fd = null;
492
+ for (const unsub of cleanups) unsub();
493
+ cleanups = [];
494
+ }
495
+
496
+ // src/file-lock.ts
497
+ var import_promises = __toESM(require("fs/promises"), 1);
498
+ var import_promises2 = require("timers/promises");
499
+ var STALE_TIMEOUT_MS = 1e4;
500
+ var RETRY_INTERVAL_MS = 50;
501
+ var MAX_WAIT_MS = 5e3;
502
+ async function withFileLock(filePath, fn) {
503
+ const lockPath = filePath + ".lock";
504
+ await acquireLock(lockPath);
505
+ try {
506
+ return await fn();
507
+ } finally {
508
+ await releaseLock(lockPath);
509
+ }
510
+ }
511
+ async function acquireLock(lockPath) {
512
+ const startTime = Date.now();
513
+ while (true) {
514
+ try {
515
+ const info = { pid: process.pid, timestamp: Date.now() };
516
+ await import_promises.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
517
+ return;
518
+ } catch (err) {
519
+ if (err.code !== "EEXIST") throw err;
520
+ try {
521
+ const content = await import_promises.default.readFile(lockPath, "utf-8");
522
+ const info = JSON.parse(content);
523
+ const isProcessAlive = isAlive(info.pid);
524
+ const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
525
+ if (!isProcessAlive || isStale) {
526
+ await import_promises.default.unlink(lockPath).catch(() => {
527
+ });
528
+ continue;
529
+ }
530
+ } catch {
531
+ await import_promises.default.unlink(lockPath).catch(() => {
532
+ });
533
+ continue;
534
+ }
535
+ if (Date.now() - startTime > MAX_WAIT_MS) {
536
+ await import_promises.default.unlink(lockPath).catch(() => {
537
+ });
538
+ continue;
539
+ }
540
+ await (0, import_promises2.setTimeout)(RETRY_INTERVAL_MS);
541
+ }
542
+ }
543
+ }
544
+ async function releaseLock(lockPath) {
545
+ await import_promises.default.unlink(lockPath).catch(() => {
546
+ });
547
+ }
548
+ function isAlive(pid) {
549
+ try {
550
+ process.kill(pid, 0);
551
+ return true;
552
+ } catch (err) {
553
+ if (err.code === "EPERM") return true;
554
+ return false;
555
+ }
556
+ }
557
+
558
+ // src/claude-code-version.ts
559
+ var import_promises3 = __toESM(require("fs/promises"), 1);
560
+ var import_node_path3 = __toESM(require("path"), 1);
561
+ var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
562
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
563
+ var FETCH_TIMEOUT_MS = 3e3;
564
+ var FALLBACK_VERSION = "2.1.88";
565
+ var memoryCache = null;
566
+ var inflight = null;
567
+ function cachePath() {
568
+ return import_node_path3.default.join(getAppPaths().agentDir, "claude-code-version.json");
569
+ }
570
+ async function readDiskCache() {
571
+ try {
572
+ const raw = await import_promises3.default.readFile(cachePath(), "utf-8");
573
+ const parsed = JSON.parse(raw);
574
+ if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
575
+ return parsed;
576
+ }
577
+ return null;
578
+ } catch {
579
+ return null;
580
+ }
581
+ }
582
+ async function writeDiskCache(data) {
583
+ try {
584
+ await import_promises3.default.mkdir(getAppPaths().agentDir, { recursive: true, mode: 448 });
585
+ await import_promises3.default.writeFile(cachePath(), JSON.stringify(data), { mode: 384 });
586
+ } catch (err) {
587
+ log(
588
+ "WARN",
589
+ "claude-code-version",
590
+ `Failed to write cache: ${err instanceof Error ? err.message : String(err)}`
591
+ );
592
+ }
593
+ }
594
+ async function fetchLatest() {
595
+ const controller = new AbortController();
596
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
597
+ try {
598
+ const response = await fetch(NPM_LATEST_URL, { signal: controller.signal });
599
+ if (!response.ok) return null;
600
+ const data = await response.json();
601
+ if (typeof data.version === "string" && /^\d/.test(data.version)) {
602
+ return data.version;
603
+ }
604
+ return null;
605
+ } catch {
606
+ return null;
607
+ } finally {
608
+ clearTimeout(timer);
609
+ }
610
+ }
611
+ async function getClaudeCodeVersion() {
612
+ if (memoryCache && Date.now() < memoryCache.expiresAt) {
613
+ return memoryCache.version;
614
+ }
615
+ if (inflight) return inflight;
616
+ inflight = (async () => {
617
+ const disk = await readDiskCache();
618
+ const diskFresh = disk && Date.now() - disk.fetchedAt < CACHE_TTL_MS;
619
+ if (disk && diskFresh) {
620
+ memoryCache = { version: disk.version, expiresAt: Date.now() + CACHE_TTL_MS };
621
+ return disk.version;
622
+ }
623
+ const fetched = await fetchLatest();
624
+ if (fetched) {
625
+ await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
626
+ memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
627
+ return fetched;
628
+ }
629
+ const resolved = disk?.version ?? FALLBACK_VERSION;
630
+ memoryCache = { version: resolved, expiresAt: Date.now() + 5 * 60 * 1e3 };
631
+ log(
632
+ "WARN",
633
+ "claude-code-version",
634
+ `Failed to fetch latest Claude Code version; using ${resolved}`
635
+ );
636
+ return resolved;
637
+ })();
638
+ try {
639
+ return await inflight;
640
+ } finally {
641
+ inflight = null;
642
+ }
643
+ }
644
+ async function getClaudeCliUserAgent() {
645
+ const version = await getClaudeCodeVersion();
646
+ return `claude-cli/${version} (external, cli)`;
647
+ }
648
+
649
+ // src/auth-storage.ts
650
+ var import_promises4 = __toESM(require("fs/promises"), 1);
651
+ var import_node_crypto5 = __toESM(require("crypto"), 1);
652
+
653
+ // src/oauth/anthropic.ts
654
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
655
+
656
+ // src/oauth/pkce.ts
657
+ function base64urlEncode(bytes) {
658
+ let binary = "";
659
+ for (const byte of bytes) {
660
+ binary += String.fromCharCode(byte);
661
+ }
662
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
663
+ }
664
+ async function generatePKCE() {
665
+ const verifierBytes = new Uint8Array(32);
666
+ crypto.getRandomValues(verifierBytes);
667
+ const verifier = base64urlEncode(verifierBytes);
668
+ const data = new TextEncoder().encode(verifier);
669
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
670
+ const challenge = base64urlEncode(new Uint8Array(hashBuffer));
671
+ return { verifier, challenge };
672
+ }
673
+
674
+ // src/oauth/anthropic.ts
675
+ var CLIENT_ID = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
676
+ var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
677
+ var TOKEN_URLS = [
678
+ "https://platform.claude.com/v1/oauth/token",
679
+ "https://console.anthropic.com/v1/oauth/token"
680
+ ];
681
+ var REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
682
+ var SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
683
+ async function postTokenRequest(body, label) {
684
+ const encoded = JSON.stringify(body);
685
+ const headers = {
686
+ "Content-Type": "application/json",
687
+ "User-Agent": await getClaudeCliUserAgent(),
688
+ "anthropic-beta": "oauth-2025-04-20"
689
+ };
690
+ let lastError = null;
691
+ for (const url of TOKEN_URLS) {
692
+ let response;
693
+ try {
694
+ response = await fetch(url, { method: "POST", headers, body: encoded });
695
+ } catch (err) {
696
+ lastError = err instanceof Error ? err : new Error(String(err));
697
+ continue;
698
+ }
699
+ if (response.ok) {
700
+ return await response.json();
701
+ }
702
+ const text = await response.text();
703
+ if (response.status >= 400 && response.status < 500) {
704
+ throw new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
705
+ }
706
+ lastError = new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
707
+ }
708
+ throw lastError ?? new Error(`Anthropic ${label} failed: all endpoints unreachable`);
709
+ }
710
+ function toCredentials(data) {
711
+ return {
712
+ accessToken: data.access_token,
713
+ refreshToken: data.refresh_token,
714
+ expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
715
+ };
716
+ }
717
+ async function loginAnthropic(callbacks) {
718
+ const { verifier, challenge } = await generatePKCE();
719
+ const state = import_node_crypto2.default.randomBytes(16).toString("hex");
720
+ const params = new URLSearchParams({
721
+ code: "true",
722
+ client_id: CLIENT_ID,
723
+ response_type: "code",
724
+ redirect_uri: REDIRECT_URI,
725
+ scope: SCOPES,
726
+ code_challenge: challenge,
727
+ code_challenge_method: "S256",
728
+ state
729
+ });
730
+ const authUrl = `${AUTHORIZE_URL}?${params}`;
731
+ callbacks.onOpenUrl(authUrl);
732
+ const raw = await callbacks.onPromptCode("Paste the code from the browser (format: code#state):");
733
+ const parts = raw.trim().split("#");
734
+ if (parts.length !== 2 || !parts[0] || parts[1] !== state) {
735
+ throw new Error("Invalid code or state mismatch. Please try again.");
736
+ }
737
+ return exchangeAnthropicCode(parts[0], parts[1], verifier);
738
+ }
739
+ async function exchangeAnthropicCode(code, state, verifier) {
740
+ const data = await postTokenRequest(
741
+ {
742
+ grant_type: "authorization_code",
743
+ client_id: CLIENT_ID,
744
+ code,
745
+ state,
746
+ redirect_uri: REDIRECT_URI,
747
+ code_verifier: verifier
748
+ },
749
+ "token exchange"
750
+ );
751
+ return toCredentials(data);
752
+ }
753
+ async function refreshAnthropicToken(refreshToken) {
754
+ const data = await postTokenRequest(
755
+ {
756
+ grant_type: "refresh_token",
757
+ client_id: CLIENT_ID,
758
+ refresh_token: refreshToken
759
+ },
760
+ "token refresh"
761
+ );
762
+ return toCredentials(data);
763
+ }
764
+
765
+ // src/oauth/openai.ts
766
+ var import_node_http = __toESM(require("http"), 1);
767
+ var import_node_crypto3 = __toESM(require("crypto"), 1);
768
+ var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
769
+ var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
770
+ var TOKEN_URL = "https://auth.openai.com/oauth/token";
771
+ var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
772
+ var SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
773
+ var JWT_CLAIM_PATH = "https://api.openai.com/auth";
774
+ async function loginOpenAI(callbacks) {
775
+ const { verifier, challenge } = await generatePKCE();
776
+ const state = import_node_crypto3.default.randomBytes(16).toString("hex");
777
+ const url = new URL(AUTHORIZE_URL2);
778
+ url.searchParams.set("response_type", "code");
779
+ url.searchParams.set("client_id", CLIENT_ID2);
780
+ url.searchParams.set("redirect_uri", REDIRECT_URI2);
781
+ url.searchParams.set("scope", SCOPE);
782
+ url.searchParams.set("code_challenge", challenge);
783
+ url.searchParams.set("code_challenge_method", "S256");
784
+ url.searchParams.set("state", state);
785
+ url.searchParams.set("id_token_add_organizations", "true");
786
+ url.searchParams.set("codex_cli_simplified_flow", "true");
787
+ url.searchParams.set("originator", "ggcoder");
788
+ let code;
789
+ try {
790
+ code = await loginWithServer(url.toString(), state, callbacks);
791
+ } catch {
792
+ callbacks.onOpenUrl(url.toString());
793
+ const raw = await callbacks.onPromptCode(
794
+ "Could not start local server. Paste the callback URL or code from the browser:"
795
+ );
796
+ const parsed = parseAuthorizationInput(raw);
797
+ if (!parsed.code) {
798
+ throw new Error("No authorization code found in input.");
799
+ }
800
+ code = parsed.code;
801
+ }
802
+ const creds = await exchangeOpenAICode(code, verifier);
803
+ const accountId = getAccountId(creds.accessToken);
804
+ if (!accountId) {
805
+ throw new Error("Failed to extract accountId from OpenAI token.");
806
+ }
807
+ creds.accountId = accountId;
808
+ return creds;
809
+ }
810
+ function parseAuthorizationInput(input) {
811
+ const value = input.trim();
812
+ if (!value) return {};
813
+ try {
814
+ const url = new URL(value);
815
+ return {
816
+ code: url.searchParams.get("code") ?? void 0,
817
+ state: url.searchParams.get("state") ?? void 0
818
+ };
819
+ } catch {
820
+ }
821
+ if (value.includes("#")) {
822
+ const [code, state] = value.split("#", 2);
823
+ return { code, state };
824
+ }
825
+ if (value.includes("code=")) {
826
+ const params = new URLSearchParams(value);
827
+ return {
828
+ code: params.get("code") ?? void 0,
829
+ state: params.get("state") ?? void 0
830
+ };
831
+ }
832
+ return { code: value };
833
+ }
834
+ function decodeJwt(token) {
835
+ try {
836
+ const parts = token.split(".");
837
+ if (parts.length !== 3) return null;
838
+ const decoded = atob(parts[1]);
839
+ return JSON.parse(decoded);
840
+ } catch {
841
+ return null;
842
+ }
843
+ }
844
+ function getAccountId(accessToken) {
845
+ const payload = decodeJwt(accessToken);
846
+ const auth = payload?.[JWT_CLAIM_PATH];
847
+ const accountId = auth?.chatgpt_account_id;
848
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
849
+ }
850
+ async function loginWithServer(authUrl, expectedState, callbacks) {
851
+ return new Promise((resolve, reject) => {
852
+ let receivedCode = null;
853
+ const server = import_node_http.default.createServer((req, res) => {
854
+ const url = new URL(req.url || "", "http://localhost");
855
+ if (url.pathname !== "/auth/callback") {
856
+ res.statusCode = 404;
857
+ res.end("Not found");
858
+ return;
859
+ }
860
+ if (url.searchParams.get("state") !== expectedState) {
861
+ res.statusCode = 400;
862
+ res.end("State mismatch");
863
+ return;
864
+ }
865
+ receivedCode = url.searchParams.get("code");
866
+ res.writeHead(200, { "Content-Type": "text/html" });
867
+ res.end("<html><body><h1>Login successful!</h1><p>You can close this tab.</p></body></html>");
868
+ server.close();
869
+ });
870
+ server.on("error", (err) => {
871
+ reject(err);
872
+ });
873
+ server.listen(1455, "127.0.0.1", () => {
874
+ callbacks.onOpenUrl(authUrl);
875
+ callbacks.onStatus("Waiting for browser callback...");
876
+ });
877
+ const timeout = setTimeout(() => {
878
+ if (!receivedCode) {
879
+ server.close();
880
+ }
881
+ }, 12e4);
882
+ timeout.unref();
883
+ server.on("close", () => {
884
+ clearTimeout(timeout);
885
+ if (receivedCode) {
886
+ resolve(receivedCode);
887
+ } else {
888
+ reject(new Error("Server closed without receiving code"));
889
+ }
890
+ });
891
+ });
892
+ }
893
+ async function exchangeOpenAICode(code, verifier) {
894
+ const response = await fetch(TOKEN_URL, {
895
+ method: "POST",
896
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
897
+ body: new URLSearchParams({
898
+ grant_type: "authorization_code",
899
+ client_id: CLIENT_ID2,
900
+ code,
901
+ redirect_uri: REDIRECT_URI2,
902
+ code_verifier: verifier
903
+ })
904
+ });
905
+ if (!response.ok) {
906
+ const text = await response.text();
907
+ throw new Error(`OpenAI token exchange failed (${response.status}): ${text}`);
908
+ }
909
+ const data = await response.json();
910
+ return {
911
+ accessToken: data.access_token,
912
+ refreshToken: data.refresh_token,
913
+ expiresAt: Date.now() + data.expires_in * 1e3
914
+ };
915
+ }
916
+ async function refreshOpenAIToken(refreshToken) {
917
+ const response = await fetch(TOKEN_URL, {
918
+ method: "POST",
919
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
920
+ body: new URLSearchParams({
921
+ grant_type: "refresh_token",
922
+ refresh_token: refreshToken,
923
+ client_id: CLIENT_ID2
924
+ })
925
+ });
926
+ if (!response.ok) {
927
+ const text = await response.text();
928
+ throw new Error(`OpenAI token refresh failed (${response.status}): ${text}`);
929
+ }
930
+ const data = await response.json();
931
+ const creds = {
932
+ accessToken: data.access_token,
933
+ refreshToken: data.refresh_token,
934
+ expiresAt: Date.now() + data.expires_in * 1e3
935
+ };
936
+ const accountId = getAccountId(creds.accessToken);
937
+ if (accountId) {
938
+ creds.accountId = accountId;
939
+ }
940
+ return creds;
941
+ }
942
+
943
+ // src/oauth/gemini.ts
944
+ var import_node_http2 = __toESM(require("http"), 1);
945
+ var import_node_crypto4 = __toESM(require("crypto"), 1);
946
+ var CLIENT_ID_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_ID";
947
+ var CLIENT_SECRET_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_SECRET";
948
+ var DEFAULT_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
949
+ var DEFAULT_CLIENT_SECRET = "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";
950
+ var AUTHORIZE_URL3 = "https://accounts.google.com/o/oauth2/v2/auth";
951
+ var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
952
+ var CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
953
+ var CODE_ASSIST_API_VERSION = "v1internal";
954
+ var CODE_ASSIST_POST_RETRIES = 3;
955
+ var CODE_ASSIST_POST_RETRY_DELAY_MS = 100;
956
+ var SCOPE2 = [
957
+ "https://www.googleapis.com/auth/cloud-platform",
958
+ "https://www.googleapis.com/auth/userinfo.email",
959
+ "https://www.googleapis.com/auth/userinfo.profile"
960
+ ].join(" ");
961
+ var USER_TIER_FREE = "free-tier";
962
+ var USER_TIER_LEGACY = "legacy-tier";
963
+ var USER_TIER_STANDARD = "standard-tier";
964
+ var VALIDATION_REQUIRED_REASON = "VALIDATION_REQUIRED";
965
+ var VPC_SC_REASON = "SECURITY_POLICY_VIOLATED";
966
+ var CodeAssistHttpError = class extends Error {
967
+ status;
968
+ body;
969
+ constructor(label, status, body) {
970
+ super(`Gemini Code Assist ${label} failed (${status}): ${body}`);
971
+ this.name = "CodeAssistHttpError";
972
+ this.status = status;
973
+ this.body = body;
974
+ }
975
+ };
976
+ async function loginGemini(callbacks) {
977
+ const { clientId, clientSecret } = getGeminiOAuthClientCredentials();
978
+ const { verifier, challenge } = await generatePKCE();
979
+ const state = import_node_crypto4.default.randomBytes(32).toString("hex");
980
+ const redirectUri = await getLoopbackRedirectUri();
981
+ const url = new URL(AUTHORIZE_URL3);
982
+ url.searchParams.set("response_type", "code");
983
+ url.searchParams.set("client_id", clientId);
984
+ url.searchParams.set("redirect_uri", redirectUri);
985
+ url.searchParams.set("scope", SCOPE2);
986
+ url.searchParams.set("access_type", "offline");
987
+ url.searchParams.set("prompt", "consent");
988
+ url.searchParams.set("code_challenge", challenge);
989
+ url.searchParams.set("code_challenge_method", "S256");
990
+ url.searchParams.set("state", state);
991
+ let code;
992
+ try {
993
+ code = await loginWithServer2(url.toString(), redirectUri, state, callbacks);
994
+ } catch {
995
+ callbacks.onOpenUrl(url.toString());
996
+ const raw = await callbacks.onPromptCode(
997
+ "Could not start local server. Paste the callback URL or code from the browser:"
998
+ );
999
+ const parsed = parseAuthorizationInput2(raw);
1000
+ if (!parsed.code) {
1001
+ throw new Error("No authorization code found in input.");
1002
+ }
1003
+ if (parsed.state && parsed.state !== state) {
1004
+ throw new Error("Invalid state. Please try again.");
1005
+ }
1006
+ code = parsed.code;
1007
+ }
1008
+ const creds = await exchangeGeminiCode(code, verifier, redirectUri, clientId, clientSecret);
1009
+ callbacks.onStatus("Setting up Gemini Code Assist access...");
1010
+ const projectId = await setupCodeAssistProject(creds.accessToken, callbacks);
1011
+ return {
1012
+ ...creds,
1013
+ projectId
1014
+ };
1015
+ }
1016
+ async function refreshGeminiToken(refreshToken) {
1017
+ const { clientId, clientSecret } = getGeminiOAuthClientCredentials();
1018
+ const data = await postTokenRequest2({
1019
+ grant_type: "refresh_token",
1020
+ refresh_token: refreshToken,
1021
+ client_id: clientId,
1022
+ client_secret: clientSecret
1023
+ });
1024
+ return {
1025
+ accessToken: data.access_token,
1026
+ refreshToken: data.refresh_token ?? refreshToken,
1027
+ expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
1028
+ };
1029
+ }
1030
+ function getGeminiOAuthClientCredentials() {
1031
+ const clientId = process.env[CLIENT_ID_ENV]?.trim() || DEFAULT_CLIENT_ID;
1032
+ const clientSecret = process.env[CLIENT_SECRET_ENV]?.trim() || DEFAULT_CLIENT_SECRET;
1033
+ return { clientId, clientSecret };
1034
+ }
1035
+ async function getLoopbackRedirectUri() {
1036
+ return new Promise((resolve, reject) => {
1037
+ const server = import_node_http2.default.createServer();
1038
+ server.listen(0, "127.0.0.1", () => {
1039
+ const addr = server.address();
1040
+ server.close(() => {
1041
+ if (addr && typeof addr === "object") {
1042
+ resolve(`http://127.0.0.1:${addr.port}/oauth2callback`);
1043
+ } else {
1044
+ reject(new Error("Failed to allocate OAuth callback port."));
1045
+ }
1046
+ });
1047
+ });
1048
+ server.on("error", reject);
1049
+ });
1050
+ }
1051
+ function parseAuthorizationInput2(input) {
1052
+ const value = input.trim();
1053
+ if (!value) return {};
1054
+ try {
1055
+ const url = new URL(value);
1056
+ return {
1057
+ code: url.searchParams.get("code") ?? void 0,
1058
+ state: url.searchParams.get("state") ?? void 0
1059
+ };
1060
+ } catch {
1061
+ }
1062
+ if (value.includes("code=")) {
1063
+ const params = new URLSearchParams(value);
1064
+ return {
1065
+ code: params.get("code") ?? void 0,
1066
+ state: params.get("state") ?? void 0
1067
+ };
1068
+ }
1069
+ return { code: value };
1070
+ }
1071
+ async function loginWithServer2(authUrl, redirectUri, expectedState, callbacks) {
1072
+ const redirect = new URL(redirectUri);
1073
+ const port = Number(redirect.port);
1074
+ return new Promise((resolve, reject) => {
1075
+ let receivedCode = null;
1076
+ const server = import_node_http2.default.createServer((req, res) => {
1077
+ const url = new URL(req.url || "", redirect.origin);
1078
+ if (url.pathname !== redirect.pathname) {
1079
+ res.statusCode = 404;
1080
+ res.end("Not found");
1081
+ return;
1082
+ }
1083
+ if (url.searchParams.get("state") !== expectedState) {
1084
+ res.statusCode = 400;
1085
+ res.end("State mismatch");
1086
+ return;
1087
+ }
1088
+ receivedCode = url.searchParams.get("code");
1089
+ res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
1090
+ res.end("<html><body><h1>Login successful!</h1><p>You can close this tab.</p></body></html>");
1091
+ server.close();
1092
+ });
1093
+ server.on("error", (err) => reject(err));
1094
+ server.listen(port, "127.0.0.1", () => {
1095
+ callbacks.onOpenUrl(authUrl);
1096
+ callbacks.onStatus("Waiting for browser callback...");
1097
+ });
1098
+ const timeout = setTimeout(() => {
1099
+ if (!receivedCode) server.close();
1100
+ }, 12e4);
1101
+ timeout.unref();
1102
+ server.on("close", () => {
1103
+ clearTimeout(timeout);
1104
+ if (receivedCode) {
1105
+ resolve(receivedCode);
1106
+ } else {
1107
+ reject(new Error("Server closed without receiving code."));
1108
+ }
1109
+ });
1110
+ });
1111
+ }
1112
+ async function exchangeGeminiCode(code, verifier, redirectUri, clientId, clientSecret) {
1113
+ const data = await postTokenRequest2({
1114
+ grant_type: "authorization_code",
1115
+ client_id: clientId,
1116
+ client_secret: clientSecret,
1117
+ code,
1118
+ redirect_uri: redirectUri,
1119
+ code_verifier: verifier
1120
+ });
1121
+ if (!data.refresh_token) {
1122
+ throw new Error("Gemini OAuth did not return a refresh token. Please try login again.");
1123
+ }
1124
+ return {
1125
+ accessToken: data.access_token,
1126
+ refreshToken: data.refresh_token,
1127
+ expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
1128
+ };
1129
+ }
1130
+ async function postTokenRequest2(body) {
1131
+ const response = await fetch(TOKEN_URL2, {
1132
+ method: "POST",
1133
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1134
+ body: new URLSearchParams(body)
1135
+ });
1136
+ if (!response.ok) {
1137
+ const text = await response.text();
1138
+ throw new Error(`Gemini token request failed (${response.status}): ${text}`);
1139
+ }
1140
+ return await response.json();
1141
+ }
1142
+ async function setupCodeAssistProject(accessToken, callbacks) {
1143
+ const envProject = process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GOOGLE_CLOUD_PROJECT_ID;
1144
+ if (envProject && /^\d+$/.test(envProject)) {
1145
+ throw new Error("GOOGLE_CLOUD_PROJECT must be a project ID, not a numeric project number.");
1146
+ }
1147
+ const coreMetadata = {
1148
+ ideType: "IDE_UNSPECIFIED",
1149
+ platform: "PLATFORM_UNSPECIFIED",
1150
+ pluginType: "GEMINI"
1151
+ };
1152
+ const projectMetadata = {
1153
+ ...coreMetadata,
1154
+ ...envProject ? { duetProject: envProject } : {}
1155
+ };
1156
+ let loadRes;
1157
+ while (true) {
1158
+ loadRes = await loadCodeAssist(accessToken, envProject, projectMetadata);
1159
+ const validation = getValidationRequiredTier(loadRes);
1160
+ if (!validation) break;
1161
+ callbacks.onStatus(
1162
+ `Gemini Code Assist requires account validation${validation.reasonMessage ? `: ${validation.reasonMessage}` : ""}`
1163
+ );
1164
+ callbacks.onOpenUrl(validation.validationUrl);
1165
+ const answer = await callbacks.onPromptCode(
1166
+ "Complete validation in the browser, then press Enter to retry (or type cancel):"
1167
+ );
1168
+ if (answer.trim().toLowerCase() === "cancel") {
1169
+ throw new Error("Gemini Code Assist account validation was cancelled.");
1170
+ }
1171
+ }
1172
+ if (loadRes.currentTier) {
1173
+ const project2 = loadRes.cloudaicompanionProject ?? envProject;
1174
+ if (!project2) throwProjectError(loadRes);
1175
+ return project2;
1176
+ }
1177
+ const tier = getOnboardTier(loadRes);
1178
+ const onboardReq = tier.id === USER_TIER_FREE ? {
1179
+ tierId: tier.id,
1180
+ cloudaicompanionProject: void 0,
1181
+ metadata: coreMetadata
1182
+ } : {
1183
+ tierId: tier.id,
1184
+ cloudaicompanionProject: envProject,
1185
+ metadata: projectMetadata
1186
+ };
1187
+ let operation = await codeAssistPost(
1188
+ accessToken,
1189
+ "onboardUser",
1190
+ onboardReq
1191
+ );
1192
+ while (!operation.done && operation.name) {
1193
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
1194
+ operation = await codeAssistGet(accessToken, operation.name);
1195
+ }
1196
+ const project = operation.response?.cloudaicompanionProject?.id ?? envProject;
1197
+ if (!project) throwProjectError(loadRes);
1198
+ return project;
1199
+ }
1200
+ async function loadCodeAssist(accessToken, envProject, metadata) {
1201
+ try {
1202
+ return await codeAssistPost(accessToken, "loadCodeAssist", {
1203
+ ...envProject ? { cloudaicompanionProject: envProject } : {},
1204
+ metadata
1205
+ });
1206
+ } catch (err) {
1207
+ if (err instanceof CodeAssistHttpError && isVpcScAffectedError(err)) {
1208
+ return { currentTier: { id: USER_TIER_STANDARD } };
1209
+ }
1210
+ if (err instanceof CodeAssistHttpError && err.status === 403 && envProject === "cloudshell-gca") {
1211
+ throw new Error(
1212
+ "Access to the default Cloud Shell Gemini project was denied.\nPlease set your own Google Cloud project by running:\ngcloud config set project [PROJECT_ID]\nor setting export GOOGLE_CLOUD_PROJECT=...",
1213
+ { cause: err }
1214
+ );
1215
+ }
1216
+ throw err;
1217
+ }
1218
+ }
1219
+ function getValidationRequiredTier(response) {
1220
+ return response.ineligibleTiers?.find(
1221
+ (tier) => tier.reasonCode === VALIDATION_REQUIRED_REASON && typeof tier.validationUrl === "string"
1222
+ );
1223
+ }
1224
+ function getOnboardTier(response) {
1225
+ const defaultTier = response.allowedTiers?.find((tier) => tier.isDefault);
1226
+ return defaultTier ?? { id: USER_TIER_LEGACY, name: "" };
1227
+ }
1228
+ function throwProjectError(response) {
1229
+ const reasons = response.ineligibleTiers?.map((tier) => tier.reasonMessage ?? tier.tierName).filter((reason) => Boolean(reason));
1230
+ if (reasons && reasons.length > 0) {
1231
+ throw new Error(`Gemini Code Assist setup failed: ${reasons.join(", ")}`);
1232
+ }
1233
+ throw new Error(
1234
+ "Gemini requires a Google Cloud project for this account. Set GOOGLE_CLOUD_PROJECT and try again."
1235
+ );
1236
+ }
1237
+ async function codeAssistPost(accessToken, method, body) {
1238
+ let lastError;
1239
+ for (let attempt = 0; attempt <= CODE_ASSIST_POST_RETRIES; attempt++) {
1240
+ try {
1241
+ return await codeAssistRequest(getCodeAssistMethodUrl(method), accessToken, method, {
1242
+ method: "POST",
1243
+ body: JSON.stringify(body)
1244
+ });
1245
+ } catch (err) {
1246
+ if (!(err instanceof CodeAssistHttpError) || attempt === CODE_ASSIST_POST_RETRIES || !shouldRetryCodeAssistStatus(err.status)) {
1247
+ throw err;
1248
+ }
1249
+ lastError = err;
1250
+ }
1251
+ await new Promise((resolve) => setTimeout(resolve, CODE_ASSIST_POST_RETRY_DELAY_MS));
1252
+ }
1253
+ throw lastError ?? new Error(`Gemini Code Assist ${method} failed.`);
1254
+ }
1255
+ async function codeAssistGet(accessToken, operationName) {
1256
+ return codeAssistRequest(getCodeAssistOperationUrl(operationName), accessToken, "operation", {
1257
+ method: "GET"
1258
+ });
1259
+ }
1260
+ async function codeAssistRequest(url, accessToken, label, init) {
1261
+ const response = await fetch(url, {
1262
+ ...init,
1263
+ headers: codeAssistHeaders(accessToken)
1264
+ });
1265
+ if (!response.ok) {
1266
+ const text = await response.text();
1267
+ throw new CodeAssistHttpError(label, response.status, text);
1268
+ }
1269
+ return await response.json();
1270
+ }
1271
+ function getCodeAssistBaseUrl() {
1272
+ const endpoint = process.env.CODE_ASSIST_ENDPOINT ?? CODE_ASSIST_BASE_URL;
1273
+ const version = process.env.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
1274
+ return `${endpoint}/${version}`;
1275
+ }
1276
+ function getCodeAssistMethodUrl(method) {
1277
+ return `${getCodeAssistBaseUrl()}:${method}`;
1278
+ }
1279
+ function getCodeAssistOperationUrl(operationName) {
1280
+ return `${getCodeAssistBaseUrl()}/${operationName}`;
1281
+ }
1282
+ function shouldRetryCodeAssistStatus(status) {
1283
+ return status === 429 || status === 499 || status >= 500 && status <= 599;
1284
+ }
1285
+ function isVpcScAffectedError(error) {
1286
+ try {
1287
+ const parsed = JSON.parse(error.body);
1288
+ if (!parsed || typeof parsed !== "object" || !("error" in parsed)) return false;
1289
+ const details = parsed.error?.details;
1290
+ return Array.isArray(details) ? details.some(
1291
+ (detail) => detail != null && typeof detail === "object" && "reason" in detail && detail.reason === VPC_SC_REASON
1292
+ ) : false;
1293
+ } catch {
1294
+ return false;
1295
+ }
1296
+ }
1297
+ function codeAssistHeaders(accessToken) {
1298
+ return {
1299
+ Authorization: `Bearer ${accessToken}`,
1300
+ "Content-Type": "application/json",
1301
+ "User-Agent": "google-gemini-cli",
1302
+ "X-Goog-Api-Client": "gemini-cli/0.0.0"
1303
+ };
1304
+ }
1305
+
1306
+ // src/auth-storage.ts
1307
+ var AuthStorage = class {
1308
+ data = {};
1309
+ filePath;
1310
+ loaded = false;
1311
+ /** Per-provider lock to serialize concurrent refresh calls. */
1312
+ refreshLocks = /* @__PURE__ */ new Map();
1313
+ constructor(filePath) {
1314
+ this.filePath = filePath ?? getAppPaths().authFile;
1315
+ }
1316
+ /** Path to the on-disk auth file. Useful for status output. */
1317
+ get path() {
1318
+ return this.filePath;
1319
+ }
1320
+ /** List provider keys with stored credentials. */
1321
+ async listProviders() {
1322
+ await this.ensureLoaded();
1323
+ return Object.keys(this.data);
1324
+ }
1325
+ /** True if credentials exist for `provider`. */
1326
+ async hasCredentials(provider) {
1327
+ await this.ensureLoaded();
1328
+ return Boolean(this.data[provider]);
1329
+ }
1330
+ async load() {
1331
+ await withFileLock(this.filePath, async () => {
1332
+ try {
1333
+ const content = await import_promises4.default.readFile(this.filePath, "utf-8");
1334
+ this.data = JSON.parse(content);
1335
+ log("INFO", "auth", `Loaded credentials from ${this.filePath}`, {
1336
+ providers: Object.keys(this.data).join(",") || "(none)"
1337
+ });
1338
+ } catch (err) {
1339
+ this.data = {};
1340
+ const code = err.code;
1341
+ if (code === "ENOENT") {
1342
+ log("INFO", "auth", `No auth file found at ${this.filePath} (first run)`);
1343
+ } else {
1344
+ log(
1345
+ "ERROR",
1346
+ "auth",
1347
+ `Failed to load auth file: ${err instanceof Error ? err.message : String(err)}`,
1348
+ { path: this.filePath, code: code ?? "unknown" }
1349
+ );
1350
+ }
1351
+ }
1352
+ });
1353
+ this.loaded = true;
1354
+ }
1355
+ async ensureLoaded() {
1356
+ if (!this.loaded) await this.load();
1357
+ }
1358
+ async getCredentials(provider) {
1359
+ await this.ensureLoaded();
1360
+ return this.data[provider];
1361
+ }
1362
+ async setCredentials(provider, creds) {
1363
+ await this.ensureLoaded();
1364
+ this.data[provider] = creds;
1365
+ await this.save();
1366
+ }
1367
+ async clearCredentials(provider) {
1368
+ await this.ensureLoaded();
1369
+ delete this.data[provider];
1370
+ await this.save();
1371
+ }
1372
+ async clearAll() {
1373
+ this.data = {};
1374
+ await this.save();
1375
+ }
1376
+ /**
1377
+ * Returns valid credentials, auto-refreshing if expired.
1378
+ * If `forceRefresh` is true, refreshes even if the token hasn't expired
1379
+ * (useful when the provider rejects a token with 401 before its stored expiry).
1380
+ * Throws if not logged in.
1381
+ */
1382
+ async resolveCredentials(provider, opts) {
1383
+ await this.ensureLoaded();
1384
+ const creds = this.data[provider];
1385
+ if (!creds) {
1386
+ throw new NotLoggedInError(provider);
1387
+ }
1388
+ if (provider === "glm" || provider === "moonshot" || provider === "xiaomi" || provider === "minimax" || provider === "deepseek" || provider === "openrouter") {
1389
+ return creds;
1390
+ }
1391
+ if (!opts?.forceRefresh && Date.now() < creds.expiresAt) {
1392
+ return creds;
1393
+ }
1394
+ const existing = this.refreshLocks.get(provider);
1395
+ if (existing) return existing;
1396
+ const refreshPromise = withFileLock(this.filePath, async () => {
1397
+ try {
1398
+ const content = await import_promises4.default.readFile(this.filePath, "utf-8");
1399
+ const freshData = JSON.parse(content);
1400
+ const freshCreds = freshData[provider];
1401
+ if (freshCreds && !opts?.forceRefresh && Date.now() < freshCreds.expiresAt) {
1402
+ this.data[provider] = freshCreds;
1403
+ return freshCreds;
1404
+ }
1405
+ } catch {
1406
+ }
1407
+ const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : refreshOpenAIToken;
1408
+ let refreshed;
1409
+ try {
1410
+ refreshed = await refreshFn(creds.refreshToken);
1411
+ } catch (err) {
1412
+ const msg = err instanceof Error ? err.message : String(err);
1413
+ const isAuthFailure = /\((401|400)\)/.test(msg) || /invalid_grant|invalid_token|invalid.*refresh/i.test(msg) || /unauthorized/i.test(msg);
1414
+ if (isAuthFailure) {
1415
+ delete this.data[provider];
1416
+ await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1417
+ throw new NotLoggedInError(provider);
1418
+ }
1419
+ throw err;
1420
+ }
1421
+ if (!refreshed.accountId && creds.accountId) {
1422
+ refreshed.accountId = creds.accountId;
1423
+ }
1424
+ if (!refreshed.projectId && creds.projectId) {
1425
+ refreshed.projectId = creds.projectId;
1426
+ }
1427
+ this.data[provider] = refreshed;
1428
+ await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1429
+ return refreshed;
1430
+ });
1431
+ this.refreshLocks.set(provider, refreshPromise);
1432
+ try {
1433
+ return await refreshPromise;
1434
+ } finally {
1435
+ this.refreshLocks.delete(provider);
1436
+ }
1437
+ }
1438
+ /**
1439
+ * Returns a valid access token, auto-refreshing if expired.
1440
+ * Throws if not logged in.
1441
+ */
1442
+ async resolveToken(provider) {
1443
+ const creds = await this.resolveCredentials(provider);
1444
+ return creds.accessToken;
1445
+ }
1446
+ async save() {
1447
+ await withFileLock(this.filePath, async () => {
1448
+ await atomicWriteFile(this.filePath, JSON.stringify(this.data, null, 2));
1449
+ });
1450
+ }
1451
+ };
1452
+ async function atomicWriteFile(filePath, content) {
1453
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${import_node_crypto5.default.randomUUID().slice(0, 8)}.tmp`;
1454
+ try {
1455
+ await import_promises4.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
1456
+ await import_promises4.default.rename(tmpPath, filePath);
1457
+ } catch (err) {
1458
+ await import_promises4.default.unlink(tmpPath).catch(() => {
1459
+ });
1460
+ throw err;
1461
+ }
1462
+ }
1463
+ var NotLoggedInError = class extends Error {
1464
+ provider;
1465
+ constructor(provider) {
1466
+ super(`Not logged in to ${provider}. Run "ggcoder login" to authenticate.`);
1467
+ this.name = "NotLoggedInError";
1468
+ this.provider = provider;
1469
+ }
1470
+ };
1471
+
1472
+ // src/telegram.ts
1473
+ var TELEGRAM_API = "https://api.telegram.org";
1474
+ var MAX_MESSAGE_LENGTH = 4096;
1475
+ var TelegramBot = class {
1476
+ token;
1477
+ allowedUserId;
1478
+ offset = 0;
1479
+ running = false;
1480
+ onMessage = null;
1481
+ onVoiceMessage = null;
1482
+ onCallback = null;
1483
+ onBotAdded = null;
1484
+ onBotRemoved = null;
1485
+ constructor(config) {
1486
+ this.token = config.botToken;
1487
+ this.allowedUserId = config.allowedUserId;
1488
+ }
1489
+ /** Register handler for incoming text messages. */
1490
+ onText(handler) {
1491
+ this.onMessage = handler;
1492
+ }
1493
+ /** Register handler for incoming voice notes. */
1494
+ onVoice(handler) {
1495
+ this.onVoiceMessage = handler;
1496
+ }
1497
+ /** Register handler for inline keyboard button presses. */
1498
+ onCallbackQuery(handler) {
1499
+ this.onCallback = handler;
1500
+ }
1501
+ /** Register handler for when the bot is added to a group. */
1502
+ onAddedToGroup(handler) {
1503
+ this.onBotAdded = handler;
1504
+ }
1505
+ /** Register handler for when the bot is removed from a group. */
1506
+ onRemovedFromGroup(handler) {
1507
+ this.onBotRemoved = handler;
1508
+ }
1509
+ /** Start long polling. Blocks until stop() is called. */
1510
+ async start() {
1511
+ this.running = true;
1512
+ const me = await this.apiCall("getMe");
1513
+ if (!me.ok) {
1514
+ throw new Error(`Invalid bot token: ${JSON.stringify(me)}`);
1515
+ }
1516
+ while (this.running) {
1517
+ try {
1518
+ const updates = await this.getUpdates();
1519
+ for (const update of updates) {
1520
+ await this.handleUpdate(update);
1521
+ }
1522
+ } catch (err) {
1523
+ if (!this.running) break;
1524
+ console.error(`[telegram] Poll error: ${err instanceof Error ? err.message : err}`);
1525
+ await sleep(3e3);
1526
+ }
1527
+ }
1528
+ }
1529
+ /** Stop long polling. */
1530
+ stop() {
1531
+ this.running = false;
1532
+ }
1533
+ /** Send a text message to a specific chat. Converts markdown and splits long messages. */
1534
+ async send(chatId, text, buttons) {
1535
+ const converted = toTelegramMarkdown(text);
1536
+ const chunks = splitMessage(converted);
1537
+ for (let i = 0; i < chunks.length; i++) {
1538
+ const isLast = i === chunks.length - 1;
1539
+ const replyMarkup = isLast && buttons ? {
1540
+ inline_keyboard: buttons.map(
1541
+ (row) => row.map((b) => ({ text: b.text, callback_data: b.callback_data }))
1542
+ )
1543
+ } : void 0;
1544
+ await this.apiCall("sendMessage", {
1545
+ chat_id: chatId,
1546
+ text: chunks[i],
1547
+ parse_mode: "Markdown",
1548
+ ...replyMarkup ? { reply_markup: replyMarkup } : {}
1549
+ });
1550
+ }
1551
+ }
1552
+ /** Send a plain text message (no markdown parsing) to a specific chat. */
1553
+ async sendPlain(chatId, text) {
1554
+ const chunks = splitMessage(text);
1555
+ for (const chunk of chunks) {
1556
+ await this.apiCall("sendMessage", {
1557
+ chat_id: chatId,
1558
+ text: chunk
1559
+ });
1560
+ }
1561
+ }
1562
+ /** Send a typing indicator to a specific chat. */
1563
+ async sendTyping(chatId) {
1564
+ await this.apiCall("sendChatAction", {
1565
+ chat_id: chatId,
1566
+ action: "typing"
1567
+ });
1568
+ }
1569
+ /** Get a direct download URL for a Telegram file. */
1570
+ async getFileUrl(fileId) {
1571
+ const result = await this.apiCall("getFile", { file_id: fileId });
1572
+ if (!result.ok) throw new Error(`Failed to get file: ${JSON.stringify(result)}`);
1573
+ const filePath = result.result.file_path;
1574
+ return `${TELEGRAM_API}/file/bot${this.token}/${filePath}`;
1575
+ }
1576
+ // ── Private ───────────────────────────────────────────
1577
+ async getUpdates() {
1578
+ const result = await this.apiCall("getUpdates", {
1579
+ offset: this.offset,
1580
+ timeout: 30,
1581
+ allowed_updates: ["message", "callback_query", "my_chat_member"]
1582
+ });
1583
+ if (!result.ok || !Array.isArray(result.result)) return [];
1584
+ const updates = result.result;
1585
+ if (updates.length > 0) {
1586
+ this.offset = updates[updates.length - 1].update_id + 1;
1587
+ }
1588
+ return updates;
1589
+ }
1590
+ async handleUpdate(update) {
1591
+ if (update.message) {
1592
+ const msg = update.message;
1593
+ if (msg.from.id !== this.allowedUserId) {
1594
+ return;
1595
+ }
1596
+ if (msg.text && this.onMessage) {
1597
+ this.onMessage({
1598
+ text: msg.text,
1599
+ chatId: msg.chat.id,
1600
+ chatType: msg.chat.type,
1601
+ chatTitle: msg.chat.title
1602
+ });
1603
+ } else if (msg.voice && this.onVoiceMessage) {
1604
+ this.onVoiceMessage({
1605
+ fileId: msg.voice.file_id,
1606
+ duration: msg.voice.duration,
1607
+ chatId: msg.chat.id,
1608
+ chatType: msg.chat.type,
1609
+ chatTitle: msg.chat.title
1610
+ });
1611
+ }
1612
+ }
1613
+ if (update.my_chat_member) {
1614
+ const member = update.my_chat_member;
1615
+ const status = member.new_chat_member.status;
1616
+ if ((status === "member" || status === "administrator") && this.onBotAdded) {
1617
+ this.onBotAdded(member.chat.id, member.chat.title);
1618
+ } else if ((status === "left" || status === "kicked") && this.onBotRemoved) {
1619
+ this.onBotRemoved(member.chat.id);
1620
+ }
1621
+ }
1622
+ if (update.callback_query) {
1623
+ const cb = update.callback_query;
1624
+ if (cb.from.id !== this.allowedUserId) return;
1625
+ await this.apiCall("answerCallbackQuery", { callback_query_id: cb.id });
1626
+ if (cb.data && this.onCallback) {
1627
+ this.onCallback(cb.data, cb.message.chat.id);
1628
+ }
1629
+ }
1630
+ }
1631
+ async apiCall(method, body) {
1632
+ const url = `${TELEGRAM_API}/bot${this.token}/${method}`;
1633
+ const response = await fetch(url, {
1634
+ method: "POST",
1635
+ headers: { "Content-Type": "application/json" },
1636
+ body: body ? JSON.stringify(body) : void 0
1637
+ });
1638
+ if (!response.ok) {
1639
+ return { ok: false };
1640
+ }
1641
+ return response.json();
1642
+ }
1643
+ };
1644
+ function toTelegramMarkdown(text) {
1645
+ const lines = text.split("\n");
1646
+ const result = [];
1647
+ let inCodeBlock = false;
1648
+ for (const line of lines) {
1649
+ if (line.trimStart().startsWith("```")) {
1650
+ inCodeBlock = !inCodeBlock;
1651
+ result.push(line);
1652
+ continue;
1653
+ }
1654
+ if (inCodeBlock) {
1655
+ result.push(line);
1656
+ continue;
1657
+ }
1658
+ let transformed = line;
1659
+ const headingMatch = transformed.match(/^(#{1,6})\s+(.+)$/);
1660
+ if (headingMatch) {
1661
+ transformed = `*${headingMatch[2]}*`;
1662
+ result.push(transformed);
1663
+ continue;
1664
+ }
1665
+ if (/^(-{3,}|_{3,}|\*{3,})$/.test(transformed.trim())) {
1666
+ result.push("");
1667
+ continue;
1668
+ }
1669
+ transformed = transformed.replace(/\*\*(.+?)\*\*/g, "*$1*");
1670
+ result.push(transformed);
1671
+ }
1672
+ return result.join("\n");
1673
+ }
1674
+ function splitMessage(text) {
1675
+ if (text.length <= MAX_MESSAGE_LENGTH) return [text];
1676
+ const chunks = [];
1677
+ let remaining = text;
1678
+ while (remaining.length > 0) {
1679
+ if (remaining.length <= MAX_MESSAGE_LENGTH) {
1680
+ chunks.push(remaining);
1681
+ break;
1682
+ }
1683
+ let splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH);
1684
+ if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
1685
+ splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH);
1686
+ }
1687
+ if (splitAt === -1 || splitAt < MAX_MESSAGE_LENGTH * 0.5) {
1688
+ splitAt = MAX_MESSAGE_LENGTH;
1689
+ }
1690
+ chunks.push(remaining.slice(0, splitAt));
1691
+ remaining = remaining.slice(splitAt).trimStart();
1692
+ }
1693
+ return chunks;
1694
+ }
1695
+ function sleep(ms) {
1696
+ return new Promise((r) => setTimeout(r, ms));
1697
+ }
1698
+
1699
+ // src/voice-transcriber.ts
1700
+ var TARGET_SAMPLE_RATE = 16e3;
1701
+ var MODEL_ID = "Xenova/whisper-tiny.en";
1702
+ var transcriber = null;
1703
+ var loadPromise = null;
1704
+ var onProgress = null;
1705
+ function setProgressCallback(cb) {
1706
+ onProgress = cb;
1707
+ }
1708
+ function resample(audio, fromRate, toRate) {
1709
+ if (fromRate === toRate) return audio;
1710
+ const ratio = fromRate / toRate;
1711
+ const newLength = Math.round(audio.length / ratio);
1712
+ const result = new Float32Array(newLength);
1713
+ for (let i = 0; i < newLength; i++) {
1714
+ const srcIndex = i * ratio;
1715
+ const low = Math.floor(srcIndex);
1716
+ const high = Math.min(low + 1, audio.length - 1);
1717
+ const frac = srcIndex - low;
1718
+ result[i] = audio[low] * (1 - frac) + audio[high] * frac;
1719
+ }
1720
+ return result;
1721
+ }
1722
+ function downmixToMono(channelData) {
1723
+ if (channelData.length === 0) return new Float32Array();
1724
+ if (channelData.length === 1) return channelData[0];
1725
+ const samples = channelData[0].length;
1726
+ const out = new Float32Array(samples);
1727
+ const scale = 1 / channelData.length;
1728
+ for (let i = 0; i < samples; i++) {
1729
+ let mixed = 0;
1730
+ for (const channel of channelData) mixed += channel[i] ?? 0;
1731
+ out[i] = mixed * scale;
1732
+ }
1733
+ return out;
1734
+ }
1735
+ async function decodeOggOpus(buffer) {
1736
+ const { OggOpusDecoder } = await import("ogg-opus-decoder");
1737
+ const decoder = new OggOpusDecoder();
1738
+ await decoder.ready;
1739
+ try {
1740
+ const decoded = await decoder.decodeFile(buffer);
1741
+ if (!decoded.channelData?.length || !decoded.channelData[0]?.length) {
1742
+ throw new Error("Decoded audio is empty");
1743
+ }
1744
+ const mono = downmixToMono(decoded.channelData);
1745
+ return resample(mono, decoded.sampleRate, TARGET_SAMPLE_RATE);
1746
+ } finally {
1747
+ decoder.free();
1748
+ }
1749
+ }
1750
+ async function getTranscriber() {
1751
+ if (transcriber) return transcriber;
1752
+ if (!loadPromise) {
1753
+ loadPromise = (async () => {
1754
+ const { pipeline } = await import("@huggingface/transformers");
1755
+ const instance = await pipeline("automatic-speech-recognition", MODEL_ID, {
1756
+ dtype: "fp32",
1757
+ progress_callback: onProgress ?? void 0
1758
+ });
1759
+ transcriber = instance;
1760
+ return instance;
1761
+ })();
1762
+ }
1763
+ return loadPromise;
1764
+ }
1765
+ function isModelLoaded() {
1766
+ return transcriber !== null;
1767
+ }
1768
+ async function transcribeVoice(fileUrl) {
1769
+ const response = await fetch(fileUrl);
1770
+ if (!response.ok) throw new Error(`Failed to download voice file: ${response.status}`);
1771
+ const buffer = new Uint8Array(await response.arrayBuffer());
1772
+ const pcm = await decodeOggOpus(buffer);
1773
+ const asr = await getTranscriber();
1774
+ const result = await asr(pcm);
1775
+ const text = Array.isArray(result) ? result[0]?.text : result.text;
1776
+ return (text ?? "").trim();
1777
+ }
1778
+
1779
+ // src/auto-update.ts
1780
+ var import_node_child_process = require("child_process");
1781
+ var import_node_fs2 = __toESM(require("fs"), 1);
1782
+ var import_node_path4 = __toESM(require("path"), 1);
1783
+ var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
1784
+ var FETCH_TIMEOUT_MS2 = 1e4;
1785
+ function compareVersions(a, b) {
1786
+ const pa = a.split(".").map(Number);
1787
+ const pb = b.split(".").map(Number);
1788
+ for (let i = 0; i < 3; i++) {
1789
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
1790
+ if (diff !== 0) return diff;
1791
+ }
1792
+ return 0;
1793
+ }
1794
+ function performUpdateInBackground(command) {
1795
+ try {
1796
+ const parts = command.split(" ");
1797
+ const child = (0, import_node_child_process.spawn)(parts[0], parts.slice(1), {
1798
+ detached: true,
1799
+ stdio: "ignore",
1800
+ env: { ...process.env, npm_config_loglevel: "silent" }
1801
+ });
1802
+ child.unref();
1803
+ } catch {
1804
+ }
1805
+ }
1806
+ function createAutoUpdater(config) {
1807
+ const REGISTRY_URL = `https://registry.npmjs.org/${config.packageName}/latest`;
1808
+ const periodicMessage = config.periodicMessage ?? (({ currentVersion, latestVersion, updateCommand }) => `Ken just pushed a fresh update \u2014 ${currentVersion} \u2192 ${latestVersion}! I'll grab it on next launch (or run ${updateCommand} if you can't wait).`);
1809
+ let periodicTimer = null;
1810
+ function stateFilePath() {
1811
+ return typeof config.stateFilePath === "function" ? config.stateFilePath() : config.stateFilePath;
1812
+ }
1813
+ function readState() {
1814
+ try {
1815
+ const raw = import_node_fs2.default.readFileSync(stateFilePath(), "utf-8");
1816
+ return JSON.parse(raw);
1817
+ } catch {
1818
+ return null;
1819
+ }
1820
+ }
1821
+ function writeState(state) {
1822
+ try {
1823
+ const filePath = stateFilePath();
1824
+ import_node_fs2.default.mkdirSync(import_node_path4.default.dirname(filePath), { recursive: true, mode: 448 });
1825
+ import_node_fs2.default.writeFileSync(filePath, JSON.stringify(state));
1826
+ } catch {
1827
+ }
1828
+ }
1829
+ function detectInstallInfo() {
1830
+ const scriptPath = (process.argv[1] ?? "").replace(/\\/g, "/");
1831
+ if (scriptPath.includes("/_npx/")) {
1832
+ return { packageManager: "unknown" /* UNKNOWN */, updateCommand: null };
1833
+ }
1834
+ if (scriptPath.includes("/.pnpm") || scriptPath.includes("/pnpm/global")) {
1835
+ return {
1836
+ packageManager: "pnpm" /* PNPM */,
1837
+ updateCommand: `pnpm add -g ${config.packageName}@latest`
1838
+ };
1839
+ }
1840
+ if (scriptPath.includes("/.yarn/") || scriptPath.includes("/yarn/global")) {
1841
+ return {
1842
+ packageManager: "yarn" /* YARN */,
1843
+ updateCommand: `yarn global add ${config.packageName}@latest`
1844
+ };
1845
+ }
1846
+ return {
1847
+ packageManager: "npm" /* NPM */,
1848
+ updateCommand: `npm install -g ${config.packageName}@latest`
1849
+ };
1850
+ }
1851
+ async function fetchLatestVersion() {
1852
+ try {
1853
+ const controller = new AbortController();
1854
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
1855
+ const response = await fetch(REGISTRY_URL, { signal: controller.signal });
1856
+ clearTimeout(timeout);
1857
+ const data = await response.json();
1858
+ const version = data.version?.trim();
1859
+ return version && /^\d+\.\d+\.\d+/.test(version) ? version : null;
1860
+ } catch {
1861
+ return null;
1862
+ }
1863
+ }
1864
+ function scheduleBackgroundCheck(currentVersion) {
1865
+ fetchLatestVersion().then((latestVersion) => {
1866
+ const newState = {
1867
+ lastCheckedAt: Date.now(),
1868
+ latestVersion: latestVersion ?? void 0,
1869
+ updatePending: false
1870
+ };
1871
+ if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) {
1872
+ newState.updatePending = true;
1873
+ }
1874
+ writeState(newState);
1875
+ }).catch(() => {
1876
+ });
1877
+ }
1878
+ function checkAndAutoUpdate(currentVersion) {
1879
+ try {
1880
+ const state = readState();
1881
+ let message = null;
1882
+ if (state?.updatePending && state.latestVersion) {
1883
+ if (compareVersions(state.latestVersion, currentVersion) > 0) {
1884
+ const info = detectInstallInfo();
1885
+ if (info.updateCommand) {
1886
+ performUpdateInBackground(info.updateCommand);
1887
+ message = `Ken just shipped ${state.latestVersion}! Installing in the background \u2014 takes effect next launch.`;
1888
+ writeState({
1889
+ ...state,
1890
+ lastCheckedAt: Date.now(),
1891
+ updatePending: false,
1892
+ lastUpdateAttempt: Date.now()
1893
+ });
1894
+ }
1895
+ } else {
1896
+ writeState({ ...state, updatePending: false });
1897
+ }
1898
+ }
1899
+ const shouldCheck = !state || Date.now() - state.lastCheckedAt > CHECK_INTERVAL_MS;
1900
+ if (shouldCheck) scheduleBackgroundCheck(currentVersion);
1901
+ return message;
1902
+ } catch {
1903
+ return null;
1904
+ }
1905
+ }
1906
+ function getPendingUpdate(currentVersion) {
1907
+ try {
1908
+ const state = readState();
1909
+ if (!state?.latestVersion) return null;
1910
+ if (compareVersions(state.latestVersion, currentVersion) <= 0) return null;
1911
+ return { latestVersion: state.latestVersion };
1912
+ } catch {
1913
+ return null;
1914
+ }
1915
+ }
1916
+ function startPeriodicUpdateCheck(currentVersion, onUpdate) {
1917
+ if (periodicTimer) return;
1918
+ periodicTimer = setInterval(() => {
1919
+ fetchLatestVersion().then((latestVersion) => {
1920
+ if (!latestVersion) return;
1921
+ if (compareVersions(latestVersion, currentVersion) <= 0) return;
1922
+ const info = detectInstallInfo();
1923
+ if (!info.updateCommand) return;
1924
+ writeState({ lastCheckedAt: Date.now(), latestVersion, updatePending: true });
1925
+ onUpdate(
1926
+ periodicMessage({ currentVersion, latestVersion, updateCommand: info.updateCommand })
1927
+ );
1928
+ stopPeriodicUpdateCheck();
1929
+ }).catch(() => {
1930
+ });
1931
+ }, CHECK_INTERVAL_MS);
1932
+ periodicTimer.unref();
1933
+ }
1934
+ function stopPeriodicUpdateCheck() {
1935
+ if (periodicTimer) {
1936
+ clearInterval(periodicTimer);
1937
+ periodicTimer = null;
1938
+ }
1939
+ }
1940
+ return {
1941
+ checkAndAutoUpdate,
1942
+ getPendingUpdate,
1943
+ startPeriodicUpdateCheck,
1944
+ stopPeriodicUpdateCheck
1945
+ };
1946
+ }
1947
+ // Annotate the CommonJS export names for ESM import in node:
1948
+ 0 && (module.exports = {
1949
+ AuthStorage,
1950
+ MODELS,
1951
+ NotLoggedInError,
1952
+ TelegramBot,
1953
+ closeLogger,
1954
+ createAutoUpdater,
1955
+ decodeOggOpus,
1956
+ downmixToMono,
1957
+ generatePKCE,
1958
+ getAppPaths,
1959
+ getClaudeCliUserAgent,
1960
+ getClaudeCodeVersion,
1961
+ getContextWindow,
1962
+ getDefaultModel,
1963
+ getMaxThinkingLevel,
1964
+ getModel,
1965
+ getModelsForProvider,
1966
+ getNextThinkingLevel,
1967
+ getSessionId,
1968
+ getSummaryModel,
1969
+ getSupportedThinkingLevels,
1970
+ isLoggerOpen,
1971
+ isModelLoaded,
1972
+ isThinkingLevelSupported,
1973
+ log,
1974
+ loginAnthropic,
1975
+ loginGemini,
1976
+ loginOpenAI,
1977
+ openLog,
1978
+ refreshAnthropicToken,
1979
+ refreshGeminiToken,
1980
+ refreshOpenAIToken,
1981
+ registerLogCleanup,
1982
+ resample,
1983
+ setProgressCallback,
1984
+ transcribeVoice,
1985
+ usesOpenAICodexTransport,
1986
+ withFileLock
1987
+ });
1988
+ //# sourceMappingURL=index.cjs.map