@kenkaiiii/gg-core 5.93.0 → 5.94.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 CHANGED
@@ -32,6 +32,9 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AUTH_PROVIDERS: () => AUTH_PROVIDERS,
34
34
  AuthStorage: () => AuthStorage,
35
+ CLAUDE_CODE_AUTH_KEY: () => CLAUDE_CODE_AUTH_KEY,
36
+ CLAUDE_CODE_AUTH_MARKER: () => CLAUDE_CODE_AUTH_MARKER,
37
+ CLAUDE_CODE_MANIFEST: () => CLAUDE_CODE_MANIFEST,
35
38
  DEFAULT_LOCAL_ENDPOINTS: () => DEFAULT_LOCAL_ENDPOINTS,
36
39
  DEFAULT_MAX_VIDEO_BYTES: () => DEFAULT_MAX_VIDEO_BYTES,
37
40
  FALLBACK_CONTEXT_WINDOW: () => FALLBACK_CONTEXT_WINDOW,
@@ -54,6 +57,14 @@ __export(index_exports, {
54
57
  XIAOMI_TOKEN_PLAN_BASE_URLS: () => XIAOMI_TOKEN_PLAN_BASE_URLS,
55
58
  authPriorityNote: () => authPriorityNote,
56
59
  clampThinkingForPlanMode: () => clampThinkingForPlanMode,
60
+ claudeCodeAuthStatus: () => claudeCodeAuthStatus,
61
+ claudeCodeBinaryPath: () => claudeCodeBinaryPath,
62
+ claudeCodeCallbackUrl: () => claudeCodeCallbackUrl,
63
+ claudeCodeConfigDir: () => claudeCodeConfigDir,
64
+ claudeCodeEnv: () => claudeCodeEnv,
65
+ claudeCodeHome: () => claudeCodeHome,
66
+ claudeCodePlatformKey: () => claudeCodePlatformKey,
67
+ clearClaudeCodeUsage: () => clearClaudeCodeUsage,
57
68
  clearLocalDiscoveryCache: () => clearLocalDiscoveryCache,
58
69
  clearRuntimeModels: () => clearRuntimeModels,
59
70
  closeLogger: () => closeLogger,
@@ -67,8 +78,10 @@ __export(index_exports, {
67
78
  dualAuthProvider: () => dualAuthProvider,
68
79
  dualAuthProviderByOAuthKey: () => dualAuthProviderByOAuthKey,
69
80
  endpointRoot: () => endpointRoot,
81
+ ensureClaudeCodeBinary: () => ensureClaudeCodeBinary,
70
82
  fetchSubscriptionUsage: () => fetchSubscriptionUsage,
71
83
  findProbedModel: () => findProbedModel,
84
+ formatClaudeCodeInstallProgress: () => formatClaudeCodeInstallProgress,
72
85
  formatLocalModelId: () => formatLocalModelId,
73
86
  generatePKCE: () => generatePKCE,
74
87
  getAllModels: () => getAllModels,
@@ -76,8 +89,6 @@ __export(index_exports, {
76
89
  getAuthProvider: () => getAuthProvider,
77
90
  getAuthStorageKey: () => getAuthStorageKey,
78
91
  getAuthStorageKeys: () => getAuthStorageKeys,
79
- getClaudeCliUserAgent: () => import_gg_ai.getClaudeCliUserAgent,
80
- getClaudeCodeVersion: () => import_gg_ai.getClaudeCodeVersion,
81
92
  getContextWindow: () => getContextWindow,
82
93
  getDefaultModel: () => getDefaultModel,
83
94
  getDefaultThinkingLevel: () => getDefaultThinkingLevel,
@@ -95,9 +106,11 @@ __export(index_exports, {
95
106
  getVideoByteLimit: () => getVideoByteLimit,
96
107
  grokCliBaseUrl: () => grokCliBaseUrl,
97
108
  grokCliHeaders: () => grokCliHeaders,
109
+ isClaudeCodeAuthMarker: () => isClaudeCodeAuthMarker,
98
110
  isGrokCliEndpoint: () => isGrokCliEndpoint,
99
111
  isHiddenProvider: () => isHiddenProvider,
100
112
  isKimiCodingEndpoint: () => isKimiCodingEndpoint,
113
+ isLegacyClaudeOAuth: () => isLegacyClaudeOAuth,
101
114
  isLocalModelId: () => isLocalModelId,
102
115
  isLoggerOpen: () => isLoggerOpen,
103
116
  isModelLoaded: () => isModelLoaded,
@@ -108,21 +121,23 @@ __export(index_exports, {
108
121
  localAuthStorageKey: () => localAuthStorageKey,
109
122
  localModelShortName: () => localModelShortName,
110
123
  log: () => log,
111
- loginAnthropic: () => loginAnthropic,
124
+ loginClaudeCode: () => loginClaudeCode,
112
125
  loginGemini: () => loginGemini,
113
126
  loginKimi: () => loginKimi,
114
127
  loginOpenAI: () => loginOpenAI,
115
128
  loginXai: () => loginXai,
129
+ logoutClaudeCode: () => logoutClaudeCode,
130
+ mergeClaudeCodeRateLimit: () => mergeClaudeCodeRateLimit,
116
131
  oauthStorageKey: () => oauthStorageKey,
117
132
  ollamaServedContext: () => ollamaServedContext,
118
133
  openLog: () => openLog,
119
134
  parseLocalModelId: () => parseLocalModelId,
120
135
  parseOpenRouterCatalog: () => parseOpenRouterCatalog,
121
- pendingClaudeCodeVersionRefresh: () => import_gg_ai.pendingClaudeCodeVersionRefresh,
122
136
  probeEndpoint: () => probeEndpoint,
123
137
  providerStorageKeys: () => providerStorageKeys,
138
+ readClaudeCodeUsage: () => readClaudeCodeUsage,
124
139
  readStoredBaseUrlSync: () => readStoredBaseUrlSync,
125
- refreshAnthropicToken: () => refreshAnthropicToken,
140
+ recordClaudeCodeRateLimit: () => recordClaudeCodeRateLimit,
126
141
  refreshGeminiToken: () => refreshGeminiToken,
127
142
  refreshKimiToken: () => refreshKimiToken,
128
143
  refreshOpenAIToken: () => refreshOpenAIToken,
@@ -130,8 +145,11 @@ __export(index_exports, {
130
145
  registerLogCleanup: () => registerLogCleanup,
131
146
  registerRuntimeModels: () => registerRuntimeModels,
132
147
  resample: () => resample,
148
+ resolveClaudeCodeBinary: () => resolveClaudeCodeBinary,
133
149
  setProgressCallback: () => setProgressCallback,
134
150
  setProviderCatalog: () => setProviderCatalog,
151
+ signInWithClaudeCode: () => signInWithClaudeCode,
152
+ signOutOfClaudeCode: () => signOutOfClaudeCode,
135
153
  toModelInfo: () => toModelInfo,
136
154
  transcribeVoice: () => transcribeVoice,
137
155
  usesOpenAICodexTransport: () => usesOpenAICodexTransport,
@@ -170,7 +188,9 @@ function getAppPaths() {
170
188
  agentsDir: import_node_path.default.join(agentDir, "agents"),
171
189
  progressFile: import_node_path.default.join(agentDir, "progress.json"),
172
190
  progressBackupFile: import_node_path.default.join(agentDir, "progress.backup.json"),
173
- trophiesFile: import_node_path.default.join(agentDir, "trophies.json")
191
+ trophiesFile: import_node_path.default.join(agentDir, "trophies.json"),
192
+ claudeCodeDir: import_node_path.default.join(agentDir, "claude-code"),
193
+ claudeCodeConfigDir: import_node_path.default.join(agentDir, "claude-code", "config")
174
194
  };
175
195
  }
176
196
 
@@ -395,137 +415,539 @@ async function refreshKimiToken(refreshToken) {
395
415
  }
396
416
 
397
417
  // src/auth-storage.ts
398
- var import_promises3 = __toESM(require("fs/promises"), 1);
399
- var import_node_fs3 = require("fs");
418
+ var import_promises5 = __toESM(require("fs/promises"), 1);
419
+ var import_node_fs5 = require("fs");
400
420
  var import_node_crypto6 = __toESM(require("crypto"), 1);
401
421
 
402
- // src/oauth/anthropic.ts
422
+ // src/claude-code/runtime.ts
423
+ var import_node_child_process2 = require("child_process");
403
424
  var import_node_crypto2 = __toESM(require("crypto"), 1);
425
+ var import_node_fs3 = __toESM(require("fs"), 1);
426
+ var import_promises2 = __toESM(require("fs/promises"), 1);
427
+ var import_node_path3 = __toESM(require("path"), 1);
404
428
 
405
- // src/oauth/pkce.ts
406
- function base64urlEncode(bytes) {
407
- let binary = "";
408
- for (const byte of bytes) {
409
- binary += String.fromCharCode(byte);
429
+ // src/claude-code/tar.ts
430
+ var import_node_fs2 = __toESM(require("fs"), 1);
431
+ var import_promises = __toESM(require("fs/promises"), 1);
432
+ var import_node_zlib = __toESM(require("zlib"), 1);
433
+ var BLOCK = 512;
434
+ function readString(buf, offset, length) {
435
+ const end = buf.indexOf(0, offset);
436
+ return buf.toString("utf8", offset, end === -1 || end > offset + length ? offset + length : end);
437
+ }
438
+ function readSize(buf, offset) {
439
+ if (buf[offset] & 128) {
440
+ let value = buf[offset] & 127;
441
+ for (let i = 1; i < 12; i++) value = value * 256 + buf[offset + i];
442
+ return value;
443
+ }
444
+ const text = readString(buf, offset, 12).trim();
445
+ return text ? parseInt(text, 8) : 0;
446
+ }
447
+ function paxPath(data) {
448
+ let pos = 0;
449
+ while (pos < data.length) {
450
+ const space = data.indexOf(32, pos);
451
+ if (space === -1) break;
452
+ const len = parseInt(data.toString("utf8", pos, space), 10);
453
+ if (!Number.isFinite(len) || len <= 0) break;
454
+ const record = data.toString("utf8", space + 1, pos + len - 1);
455
+ const eq = record.indexOf("=");
456
+ if (eq !== -1 && record.slice(0, eq) === "path") return record.slice(eq + 1);
457
+ pos += len;
410
458
  }
411
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
459
+ return void 0;
412
460
  }
413
- async function generatePKCE() {
414
- const verifierBytes = new Uint8Array(32);
415
- crypto.getRandomValues(verifierBytes);
416
- const verifier = base64urlEncode(verifierBytes);
417
- const data = new TextEncoder().encode(verifier);
418
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
419
- const challenge = base64urlEncode(new Uint8Array(hashBuffer));
420
- return { verifier, challenge };
461
+ async function extractFileFromTarGz(tgzPath, entryName, destPath) {
462
+ const input = import_node_fs2.default.createReadStream(tgzPath).pipe(import_node_zlib.default.createGunzip());
463
+ let pending = Buffer.alloc(0);
464
+ let remaining = 0;
465
+ let padding = 0;
466
+ let kind = "skip";
467
+ let meta = [];
468
+ let overridePath;
469
+ let out;
470
+ let found = false;
471
+ try {
472
+ for await (const chunk of input) {
473
+ pending = pending.length ? Buffer.concat([pending, chunk]) : chunk;
474
+ let pos = 0;
475
+ while (pos < pending.length) {
476
+ if (remaining > 0) {
477
+ const take = Math.min(remaining, pending.length - pos);
478
+ const slice = pending.subarray(pos, pos + take);
479
+ if (kind === "target") await out.write(slice);
480
+ else if (kind === "pax" || kind === "longname") meta.push(Buffer.from(slice));
481
+ pos += take;
482
+ remaining -= take;
483
+ if (remaining === 0) {
484
+ if (kind === "target") {
485
+ await out.close();
486
+ out = void 0;
487
+ found = true;
488
+ } else if (kind === "pax") {
489
+ overridePath = paxPath(Buffer.concat(meta)) ?? overridePath;
490
+ } else if (kind === "longname") {
491
+ overridePath = readString(Buffer.concat(meta), 0, Number.MAX_SAFE_INTEGER);
492
+ }
493
+ }
494
+ continue;
495
+ }
496
+ if (padding > 0) {
497
+ const skip = Math.min(padding, pending.length - pos);
498
+ pos += skip;
499
+ padding -= skip;
500
+ continue;
501
+ }
502
+ if (found) break;
503
+ if (pending.length - pos < BLOCK) break;
504
+ const header = pending.subarray(pos, pos + BLOCK);
505
+ pos += BLOCK;
506
+ if (header.every((b) => b === 0)) continue;
507
+ const size = readSize(header, 124);
508
+ const type2 = String.fromCharCode(header[156] || 48);
509
+ let name = readString(header, 0, 100);
510
+ if (readString(header, 257, 6) === "ustar") {
511
+ const prefix = readString(header, 345, 155);
512
+ if (prefix) name = `${prefix}/${name}`;
513
+ }
514
+ remaining = size;
515
+ padding = (BLOCK - size % BLOCK) % BLOCK;
516
+ meta = [];
517
+ if (type2 === "x") kind = "pax";
518
+ else if (type2 === "L") kind = "longname";
519
+ else {
520
+ const path7 = overridePath ?? name;
521
+ overridePath = void 0;
522
+ if ((type2 === "0" || type2 === "\0" || type2 === "7") && path7 === entryName) {
523
+ kind = "target";
524
+ out = await import_promises.default.open(destPath, "w", 493);
525
+ if (size === 0) {
526
+ await out.close();
527
+ out = void 0;
528
+ found = true;
529
+ }
530
+ } else {
531
+ kind = "skip";
532
+ }
533
+ }
534
+ }
535
+ pending = pos < pending.length ? Buffer.from(pending.subarray(pos)) : Buffer.alloc(0);
536
+ if (found) break;
537
+ }
538
+ if (!found && out) throw new Error(`Archive ended inside ${entryName}`);
539
+ return found;
540
+ } finally {
541
+ if (out) await out.close().catch(() => {
542
+ });
543
+ input.destroy();
544
+ }
421
545
  }
422
546
 
423
- // src/claude-code-version.ts
424
- var import_gg_ai = require("@kenkaiiii/gg-ai");
547
+ // src/claude-code/manifest.json
548
+ var manifest_default = {
549
+ sdkVersion: "0.3.318",
550
+ claudeCodeVersion: "2.1.318",
551
+ platforms: {
552
+ "darwin-arm64": {
553
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.318.tgz",
554
+ integrity: "sha512-AWoXeDXzAMD3RaA21lWuHvCrKd8xmbTTmKjovninn4j07jheoVSMzFbCFjpp4kk5TdhT36JtWPkAxvjNMbcO8w==",
555
+ bin: "claude"
556
+ },
557
+ "darwin-x64": {
558
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.318.tgz",
559
+ integrity: "sha512-6CkZ5PCdebSY/LePp16sf/HYRtM6zVuxtn8qYICFS5KUZFsjccJJ9swVsOdHXNHi4SWZcSmIFTwoUbfFCzisRw==",
560
+ bin: "claude"
561
+ },
562
+ "linux-arm64": {
563
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.318.tgz",
564
+ integrity: "sha512-mBm4su17aMitLBBCQj3bHdejyUDY6z/ormGih+HG24N689RSbdKECjGjkT87tDBIR/4jyoqmuSeHqM1DI8TJlg==",
565
+ bin: "claude"
566
+ },
567
+ "linux-arm64-musl": {
568
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.318.tgz",
569
+ integrity: "sha512-l4LSYFOrvlYCrUImGfxMTQ0wg+T3s9CBhrBvPF/0oJGKZg+QnxHHU8jUVm9e9/xYmDRTtqFIrr4id/5AfH4XGw==",
570
+ bin: "claude"
571
+ },
572
+ "linux-x64": {
573
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.318.tgz",
574
+ integrity: "sha512-+mE+ueBD3oRFdaG16rd0h+LMjdeivGxL0pr/+FZK7JeLWVu+yPSw/f/X02b9oK36/DWykiFTu2G6HnJ9/+YO6w==",
575
+ bin: "claude"
576
+ },
577
+ "linux-x64-musl": {
578
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.318.tgz",
579
+ integrity: "sha512-SAF3lj/2OzTU84p+OOA13QEKA9+5fYAw0M9E7AJjdnRmVFGqQfbV7Do1i1f9Sj0mIubB3rLTRphGRw8O4HZjgA==",
580
+ bin: "claude"
581
+ },
582
+ "win32-arm64": {
583
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.318.tgz",
584
+ integrity: "sha512-19iVSZ4379QbZU1XnGUtfFvhKS1WzS8H1sbU/DbrzqbJ5Kq1eB1drj+obB1fCxTh6m26tRPj0zMJmdeu/jk3Gw==",
585
+ bin: "claude.exe"
586
+ },
587
+ "win32-x64": {
588
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.318.tgz",
589
+ integrity: "sha512-ZdKVWpyVNVt0cXrOf8sUBaxcxLEAP+oGMdQDLBWr/BqOxTWLy7TBWOj1kzw+wVg/9WIkjkgQWeJzGXPWulR1sA==",
590
+ bin: "claude.exe"
591
+ }
592
+ }
593
+ };
425
594
 
426
- // src/oauth/anthropic.ts
427
- var CLIENT_ID2 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
428
- var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
429
- var TOKEN_URLS = [
430
- "https://platform.claude.com/v1/oauth/token",
431
- "https://console.anthropic.com/v1/oauth/token"
432
- ];
433
- var REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
434
- var SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
435
- async function postTokenRequest(body, label) {
436
- const encoded = JSON.stringify(body);
437
- const headers = {
438
- "Content-Type": "application/json",
439
- "User-Agent": await (0, import_gg_ai.getClaudeCliUserAgent)(),
440
- "anthropic-beta": "oauth-2025-04-20"
441
- };
442
- let lastError = null;
443
- for (const url of TOKEN_URLS) {
444
- let response;
595
+ // src/claude-code/runtime.ts
596
+ var CLAUDE_CODE_MANIFEST = manifest_default;
597
+ var CLAUDE_CODE_AUTH_KEY = "anthropic-claude-code";
598
+ var CLAUDE_CODE_AUTH_MARKER = { via: "claude-code" };
599
+ function isClaudeCodeAuthMarker(value) {
600
+ return value?.via === CLAUDE_CODE_AUTH_MARKER.via;
601
+ }
602
+ function isLegacyClaudeOAuth(value) {
603
+ const token = value?.accessToken;
604
+ return typeof token === "string" && token.startsWith("sk-ant-oat");
605
+ }
606
+ function isMusl() {
607
+ if (process.platform !== "linux") return false;
608
+ const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
609
+ return report != null && report.header?.glibcVersionRuntime === void 0;
610
+ }
611
+ function claudeCodePlatformKey(info = { platform: process.platform, arch: process.arch, musl: isMusl() }) {
612
+ const arch2 = info.arch === "arm64" || info.arch === "x64" ? info.arch : null;
613
+ if (!arch2) return null;
614
+ if (info.platform === "darwin" || info.platform === "win32") return `${info.platform}-${arch2}`;
615
+ if (info.platform === "linux") return `linux-${arch2}${info.musl ? "-musl" : ""}`;
616
+ return null;
617
+ }
618
+ function resolveAsset(opts) {
619
+ const manifest = opts.manifest ?? CLAUDE_CODE_MANIFEST;
620
+ const key = opts.platformKey ?? claudeCodePlatformKey();
621
+ const asset = key ? manifest.platforms[key] : void 0;
622
+ if (!asset) {
623
+ throw new Error(
624
+ `Claude Code isn't available for ${process.platform}-${process.arch}. Use an Anthropic API key instead.`
625
+ );
626
+ }
627
+ return { manifest, asset };
628
+ }
629
+ function claudeCodeHome(opts = {}) {
630
+ return opts.home ?? getAppPaths().claudeCodeDir;
631
+ }
632
+ function claudeCodeConfigDir(opts = {}) {
633
+ return opts.home ? import_node_path3.default.join(opts.home, "config") : getAppPaths().claudeCodeConfigDir;
634
+ }
635
+ function claudeCodeBinaryPath(opts = {}) {
636
+ const { manifest, asset } = resolveAsset(opts);
637
+ return import_node_path3.default.join(claudeCodeHome(opts), manifest.claudeCodeVersion, asset.bin);
638
+ }
639
+ function resolveClaudeCodeBinary(opts = {}) {
640
+ let binary;
641
+ try {
642
+ binary = claudeCodeBinaryPath(opts);
643
+ } catch {
644
+ return null;
645
+ }
646
+ try {
647
+ return import_node_fs3.default.statSync(binary).isFile() ? binary : null;
648
+ } catch {
649
+ return null;
650
+ }
651
+ }
652
+ var installs = /* @__PURE__ */ new Map();
653
+ async function ensureClaudeCodeBinary(opts = {}) {
654
+ const existing = resolveClaudeCodeBinary(opts);
655
+ if (existing) return existing;
656
+ const target = claudeCodeBinaryPath(opts);
657
+ let install = installs.get(target);
658
+ if (!install) {
659
+ install = installClaudeCode(opts).finally(() => installs.delete(target));
660
+ installs.set(target, install);
661
+ }
662
+ return install;
663
+ }
664
+ async function installClaudeCode(opts) {
665
+ const { manifest, asset } = resolveAsset(opts);
666
+ const home = claudeCodeHome(opts);
667
+ const versionDir = import_node_path3.default.join(home, manifest.claudeCodeVersion);
668
+ const binary = import_node_path3.default.join(versionDir, asset.bin);
669
+ await import_promises2.default.mkdir(home, { recursive: true });
670
+ const tag = `${process.pid}-${import_node_crypto2.default.randomBytes(4).toString("hex")}`;
671
+ const tarball = import_node_path3.default.join(home, `.download-${tag}.tgz`);
672
+ const staging = import_node_path3.default.join(home, `.staging-${tag}`);
673
+ try {
674
+ await download(asset, tarball, opts);
675
+ opts.onProgress?.({ phase: "extract" });
676
+ await import_promises2.default.mkdir(staging, { recursive: true });
677
+ const found = await extractFileFromTarGz(
678
+ tarball,
679
+ `package/${asset.bin}`,
680
+ import_node_path3.default.join(staging, asset.bin)
681
+ );
682
+ if (!found) throw new Error(`The Claude Code download has no ${asset.bin} binary.`);
683
+ await import_promises2.default.chmod(import_node_path3.default.join(staging, asset.bin), 493);
445
684
  try {
446
- response = await fetch(url, {
447
- method: "POST",
448
- headers,
449
- body: encoded,
450
- signal: oauthRequestSignal()
451
- });
685
+ await import_promises2.default.rename(staging, versionDir);
452
686
  } catch (err) {
453
- lastError = err instanceof Error ? err : new Error(String(err));
454
- continue;
687
+ if (!import_node_fs3.default.existsSync(binary)) throw err;
455
688
  }
456
- if (response.ok) {
457
- return await response.json();
458
- }
459
- const text = await response.text();
460
- if (response.status >= 400 && response.status < 500) {
461
- throw new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
689
+ await removeOldVersions(home, manifest.claudeCodeVersion);
690
+ return binary;
691
+ } finally {
692
+ await import_promises2.default.rm(tarball, { force: true }).catch(() => {
693
+ });
694
+ await import_promises2.default.rm(staging, { recursive: true, force: true }).catch(() => {
695
+ });
696
+ }
697
+ }
698
+ async function download(asset, dest, opts) {
699
+ const doFetch = opts.fetch ?? fetch;
700
+ const res = await doFetch(asset.url, { signal: opts.signal });
701
+ if (!res.ok || !res.body) {
702
+ throw new Error(`Claude Code download failed (HTTP ${res.status}).`);
703
+ }
704
+ const totalBytes = Number(res.headers.get("content-length")) || void 0;
705
+ const hash = import_node_crypto2.default.createHash("sha512");
706
+ const out = await import_promises2.default.open(dest, "w");
707
+ let receivedBytes = 0;
708
+ let lastReported = -1;
709
+ try {
710
+ opts.onProgress?.({ phase: "download", receivedBytes, totalBytes });
711
+ for await (const chunk of res.body) {
712
+ if (opts.signal?.aborted) throw opts.signal.reason ?? new Error("Download cancelled.");
713
+ hash.update(chunk);
714
+ await out.write(chunk);
715
+ receivedBytes += chunk.byteLength;
716
+ const step = totalBytes ? Math.floor(receivedBytes / totalBytes * 100) : Math.floor(receivedBytes / 4194304);
717
+ if (step !== lastReported) {
718
+ lastReported = step;
719
+ opts.onProgress?.({ phase: "download", receivedBytes, totalBytes });
720
+ }
462
721
  }
463
- lastError = new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
722
+ } finally {
723
+ await out.close();
724
+ }
725
+ opts.onProgress?.({ phase: "verify" });
726
+ const expected = asset.integrity.replace(/^sha512-/, "");
727
+ if (hash.digest("base64") !== expected) {
728
+ throw new Error(
729
+ "The Claude Code download didn't match its pinned checksum, so it was discarded."
730
+ );
464
731
  }
465
- throw lastError ?? new Error(`Anthropic ${label} failed: all endpoints unreachable`);
466
732
  }
467
- function toCredentials(data) {
468
- return {
469
- accessToken: data.access_token,
470
- refreshToken: data.refresh_token,
471
- expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
472
- };
733
+ async function removeOldVersions(home, keep) {
734
+ let entries;
735
+ try {
736
+ entries = await import_promises2.default.readdir(home, { withFileTypes: true });
737
+ } catch {
738
+ return;
739
+ }
740
+ for (const entry of entries) {
741
+ if (!entry.isDirectory() || entry.name === keep || entry.name === "config") continue;
742
+ if (!/^\d+\.\d+\.\d+/.test(entry.name)) continue;
743
+ await import_promises2.default.rm(import_node_path3.default.join(home, entry.name), { recursive: true, force: true }).catch(() => {
744
+ });
745
+ }
473
746
  }
474
- async function loginAnthropic(callbacks) {
475
- const { verifier, challenge } = await generatePKCE();
476
- const state = import_node_crypto2.default.randomBytes(16).toString("hex");
477
- const params = new URLSearchParams({
478
- code: "true",
479
- client_id: CLIENT_ID2,
480
- response_type: "code",
481
- redirect_uri: REDIRECT_URI,
482
- scope: SCOPES,
483
- code_challenge: challenge,
484
- code_challenge_method: "S256",
485
- state
747
+ function formatClaudeCodeInstallProgress(progress) {
748
+ if (progress.phase === "verify") return "Checking the Claude Code download\u2026";
749
+ if (progress.phase === "extract") return "Unpacking Claude Code\u2026";
750
+ const mb = (bytes) => Math.round(bytes / 1048576);
751
+ if (!progress.totalBytes) return `Downloading Claude Code\u2026 ${mb(progress.receivedBytes)} MB`;
752
+ const pct = Math.floor(progress.receivedBytes / progress.totalBytes * 100);
753
+ return `Downloading Claude Code\u2026 ${pct}% (${mb(progress.receivedBytes)} of ${mb(progress.totalBytes)} MB)`;
754
+ }
755
+ var STRIPPED_ENV = [
756
+ "ANTHROPIC_API_KEY",
757
+ "ANTHROPIC_AUTH_TOKEN",
758
+ "ANTHROPIC_BASE_URL",
759
+ "CLAUDE_CODE_OAUTH_TOKEN",
760
+ "CLAUDE_CODE_OAUTH_REFRESH_TOKEN",
761
+ "CLAUDE_CODE_USE_BEDROCK",
762
+ "CLAUDE_CODE_USE_VERTEX",
763
+ "CLAUDE_CODE_USE_FOUNDRY",
764
+ "NODE_OPTIONS"
765
+ ];
766
+ function claudeCodeEnv(opts = {}, base = process.env) {
767
+ const env = {};
768
+ for (const [key, value] of Object.entries(base)) {
769
+ if (value !== void 0 && !STRIPPED_ENV.includes(key)) env[key] = value;
770
+ }
771
+ env.CLAUDE_CONFIG_DIR = claudeCodeConfigDir(opts);
772
+ env.DISABLE_AUTOUPDATER = "1";
773
+ return env;
774
+ }
775
+ function runCommand(opts) {
776
+ if (opts.binary) return { command: opts.binary, prefix: opts.binaryArgs ?? [] };
777
+ const binary = resolveClaudeCodeBinary(opts);
778
+ if (!binary) throw new Error("Claude Code isn't installed yet. Sign in with Claude first.");
779
+ return { command: binary, prefix: [] };
780
+ }
781
+ function runClaude(args, opts) {
782
+ const { command, prefix } = runCommand(opts);
783
+ return new Promise((resolve, reject) => {
784
+ const child = (0, import_node_child_process2.spawn)(command, [...prefix, ...args], {
785
+ env: claudeCodeEnv(opts),
786
+ stdio: ["ignore", "pipe", "pipe"],
787
+ windowsHide: true,
788
+ signal: opts.signal
789
+ });
790
+ let stdout = "";
791
+ let stderr = "";
792
+ child.stdout.on("data", (d) => stdout += d.toString());
793
+ child.stderr.on("data", (d) => stderr += d.toString());
794
+ child.on("error", reject);
795
+ child.on("close", (code) => resolve({ code, stdout, stderr }));
486
796
  });
487
- const authUrl = `${AUTHORIZE_URL}?${params}`;
488
- callbacks.onOpenUrl(authUrl);
489
- const raw = await callbacks.onPromptCode("Paste the code from the browser (format: code#state):");
490
- const parts = raw.trim().split("#");
491
- if (parts.length !== 2 || !parts[0] || parts[1] !== state) {
492
- throw new Error("Invalid code or state mismatch. Please try again.");
797
+ }
798
+ async function claudeCodeAuthStatus(opts = {}) {
799
+ if (!opts.binary && !resolveClaudeCodeBinary(opts)) return { loggedIn: false };
800
+ const { stdout } = await runClaude(["auth", "status", "--json"], opts);
801
+ try {
802
+ const parsed = JSON.parse(stdout);
803
+ return {
804
+ loggedIn: parsed.loggedIn === true,
805
+ ...typeof parsed.authMethod === "string" ? { authMethod: parsed.authMethod } : {},
806
+ ...typeof parsed.email === "string" ? { email: parsed.email } : {},
807
+ ...typeof parsed.subscriptionType === "string" ? { subscriptionType: parsed.subscriptionType } : {}
808
+ };
809
+ } catch {
810
+ return { loggedIn: false };
493
811
  }
494
- return exchangeAnthropicCode(parts[0], parts[1], verifier);
495
812
  }
496
- async function exchangeAnthropicCode(code, state, verifier) {
497
- const data = await postTokenRequest(
498
- {
499
- grant_type: "authorization_code",
500
- client_id: CLIENT_ID2,
501
- code,
502
- state,
503
- redirect_uri: REDIRECT_URI,
504
- code_verifier: verifier
505
- },
506
- "token exchange"
507
- );
508
- return toCredentials(data);
813
+ async function logoutClaudeCode(opts = {}) {
814
+ if (!opts.binary && !resolveClaudeCodeBinary(opts)) return;
815
+ const { code, stderr } = await runClaude(["auth", "logout"], opts);
816
+ if (code !== 0) throw new Error(stderr.trim() || `claude auth logout exited with ${code}`);
509
817
  }
510
- async function refreshAnthropicToken(refreshToken) {
511
- const data = await postTokenRequest(
512
- {
513
- grant_type: "refresh_token",
514
- client_id: CLIENT_ID2,
515
- refresh_token: refreshToken
516
- },
517
- "token refresh"
518
- );
519
- return toCredentials(data);
818
+ var OPEN_URL_HOOK = `const arg = String(process.argv[1] || "");
819
+ const file = process.env.GG_CLAUDE_CODE_URL_FILE;
820
+ if (file && /https?:/.test(arg)) {
821
+ require("node:fs").appendFileSync(file, arg + "\\n");
822
+ process.exit(0);
823
+ }
824
+ `;
825
+ var CAPTURE_WAIT_MS = 3e3;
826
+ function stripAnsi(text) {
827
+ return text.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[A-Za-z]/g, "");
828
+ }
829
+ function claudeCodeCallbackUrl(manualUrl, captured) {
830
+ const port = /localhost(?::|%3A)(\d{2,5})(?:\/|%2F)callback/i.exec(captured)?.[1];
831
+ if (!port) return null;
832
+ try {
833
+ const url = new URL(manualUrl);
834
+ url.searchParams.set("redirect_uri", `http://localhost:${port}/callback`);
835
+ return url.toString();
836
+ } catch {
837
+ return null;
838
+ }
839
+ }
840
+ async function loginClaudeCode(callbacks, opts = {}) {
841
+ const { command, prefix } = runCommand(opts);
842
+ const home = claudeCodeHome(opts);
843
+ await import_promises2.default.mkdir(claudeCodeConfigDir(opts), { recursive: true });
844
+ const tag = `${process.pid}-${import_node_crypto2.default.randomBytes(4).toString("hex")}`;
845
+ const hookFile = import_node_path3.default.join(home, `.open-url-${tag}.cjs`);
846
+ const urlFile = import_node_path3.default.join(home, `.open-url-${tag}.txt`);
847
+ await import_promises2.default.writeFile(hookFile, OPEN_URL_HOOK);
848
+ const env = {
849
+ ...claudeCodeEnv(opts),
850
+ BROWSER: process.execPath,
851
+ NODE_OPTIONS: `--require ${JSON.stringify(hookFile)}`,
852
+ GG_CLAUDE_CODE_URL_FILE: urlFile
853
+ };
854
+ const promptAbort = new AbortController();
855
+ let timer;
856
+ try {
857
+ await new Promise((resolve, reject) => {
858
+ const child = (0, import_node_child_process2.spawn)(command, [...prefix, "auth", "login", "--claudeai"], {
859
+ env,
860
+ stdio: ["pipe", "pipe", "pipe"],
861
+ windowsHide: true
862
+ });
863
+ let stdout = "";
864
+ let stderr = "";
865
+ let manualUrl = null;
866
+ let opened = false;
867
+ const onAbort = () => child.kill();
868
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
869
+ const open = (url, needsCode) => {
870
+ if (opened) return;
871
+ opened = true;
872
+ clearInterval(timer);
873
+ callbacks.onOpenUrl(url);
874
+ callbacks.onStatus("Finish signing in to Claude in your browser\u2026");
875
+ if (!needsCode) return;
876
+ void callbacks.onPromptCode("Paste the code shown after signing in to Claude:", promptAbort.signal).then((code) => {
877
+ const trimmed = code.trim();
878
+ if (trimmed && !child.stdin.destroyed) child.stdin.write(`${trimmed}
879
+ `);
880
+ }).catch(() => {
881
+ });
882
+ };
883
+ child.stdout.on("data", (d) => {
884
+ stdout += d.toString();
885
+ if (manualUrl) return;
886
+ const match = /visit:\s*(https:\/\/\S+)/.exec(stripAnsi(stdout));
887
+ if (!match) return;
888
+ manualUrl = match[1];
889
+ const started = Date.now();
890
+ timer = setInterval(() => {
891
+ let captured = "";
892
+ try {
893
+ captured = import_node_fs3.default.readFileSync(urlFile, "utf8");
894
+ } catch {
895
+ }
896
+ const callbackUrl = captured ? claudeCodeCallbackUrl(manualUrl, captured) : null;
897
+ if (callbackUrl) open(callbackUrl, false);
898
+ else if (Date.now() - started > (opts.browserWaitMs ?? CAPTURE_WAIT_MS))
899
+ open(manualUrl, true);
900
+ }, 100);
901
+ });
902
+ child.stderr.on("data", (d) => stderr += d.toString());
903
+ child.on("error", reject);
904
+ child.on("close", (code) => {
905
+ opts.signal?.removeEventListener("abort", onAbort);
906
+ if (opts.signal?.aborted) reject(new Error("Sign-in cancelled."));
907
+ else if (code === 0) resolve();
908
+ else {
909
+ const detail = stripAnsi(stderr).trim().split("\n").pop()?.trim();
910
+ reject(new Error(detail || `Claude sign-in failed (exit ${code}).`));
911
+ }
912
+ });
913
+ });
914
+ } finally {
915
+ clearInterval(timer);
916
+ promptAbort.abort();
917
+ await import_promises2.default.rm(hookFile, { force: true }).catch(() => {
918
+ });
919
+ await import_promises2.default.rm(urlFile, { force: true }).catch(() => {
920
+ });
921
+ }
520
922
  }
521
923
 
522
924
  // src/oauth/openai.ts
523
925
  var import_node_http = __toESM(require("http"), 1);
524
926
  var import_node_crypto3 = __toESM(require("crypto"), 1);
525
- var CLIENT_ID3 = "app_EMoamEEZ73f0CkXaXp7hrann";
526
- var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
927
+
928
+ // src/oauth/pkce.ts
929
+ function base64urlEncode(bytes) {
930
+ let binary = "";
931
+ for (const byte of bytes) {
932
+ binary += String.fromCharCode(byte);
933
+ }
934
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
935
+ }
936
+ async function generatePKCE() {
937
+ const verifierBytes = new Uint8Array(32);
938
+ crypto.getRandomValues(verifierBytes);
939
+ const verifier = base64urlEncode(verifierBytes);
940
+ const data = new TextEncoder().encode(verifier);
941
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
942
+ const challenge = base64urlEncode(new Uint8Array(hashBuffer));
943
+ return { verifier, challenge };
944
+ }
945
+
946
+ // src/oauth/openai.ts
947
+ var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
948
+ var AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
527
949
  var TOKEN_URL = "https://auth.openai.com/oauth/token";
528
- var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
950
+ var REDIRECT_URI = "http://localhost:1455/auth/callback";
529
951
  var SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
530
952
  var JWT_CLAIM_PATH = "https://api.openai.com/auth";
531
953
  var CALLBACK_TIMEOUT_MS = 12e4;
@@ -534,10 +956,10 @@ var MIN_RAW_CODE_LENGTH = 10;
534
956
  async function loginOpenAI(callbacks) {
535
957
  const { verifier, challenge } = await generatePKCE();
536
958
  const state = import_node_crypto3.default.randomBytes(16).toString("hex");
537
- const url = new URL(AUTHORIZE_URL2);
959
+ const url = new URL(AUTHORIZE_URL);
538
960
  url.searchParams.set("response_type", "code");
539
- url.searchParams.set("client_id", CLIENT_ID3);
540
- url.searchParams.set("redirect_uri", REDIRECT_URI2);
961
+ url.searchParams.set("client_id", CLIENT_ID2);
962
+ url.searchParams.set("redirect_uri", REDIRECT_URI);
541
963
  url.searchParams.set("scope", SCOPE);
542
964
  url.searchParams.set("code_challenge", challenge);
543
965
  url.searchParams.set("code_challenge_method", "S256");
@@ -693,9 +1115,9 @@ async function exchangeOpenAICode(code, verifier) {
693
1115
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
694
1116
  body: new URLSearchParams({
695
1117
  grant_type: "authorization_code",
696
- client_id: CLIENT_ID3,
1118
+ client_id: CLIENT_ID2,
697
1119
  code,
698
- redirect_uri: REDIRECT_URI2,
1120
+ redirect_uri: REDIRECT_URI,
699
1121
  code_verifier: verifier
700
1122
  }),
701
1123
  signal: oauthRequestSignal()
@@ -718,7 +1140,7 @@ async function refreshOpenAIToken(refreshToken) {
718
1140
  body: new URLSearchParams({
719
1141
  grant_type: "refresh_token",
720
1142
  refresh_token: refreshToken,
721
- client_id: CLIENT_ID3
1143
+ client_id: CLIENT_ID2
722
1144
  }),
723
1145
  signal: oauthRequestSignal()
724
1146
  });
@@ -746,7 +1168,7 @@ var CLIENT_ID_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_ID";
746
1168
  var CLIENT_SECRET_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_SECRET";
747
1169
  var DEFAULT_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
748
1170
  var DEFAULT_CLIENT_SECRET = "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";
749
- var AUTHORIZE_URL3 = "https://accounts.google.com/o/oauth2/v2/auth";
1171
+ var AUTHORIZE_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
750
1172
  var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
751
1173
  var CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
752
1174
  var CODE_ASSIST_API_VERSION = "v1internal";
@@ -777,7 +1199,7 @@ async function loginGemini(callbacks) {
777
1199
  const { verifier, challenge } = await generatePKCE();
778
1200
  const state = import_node_crypto4.default.randomBytes(32).toString("hex");
779
1201
  const redirectUri = await getLoopbackRedirectUri();
780
- const url = new URL(AUTHORIZE_URL3);
1202
+ const url = new URL(AUTHORIZE_URL2);
781
1203
  url.searchParams.set("response_type", "code");
782
1204
  url.searchParams.set("client_id", clientId2);
783
1205
  url.searchParams.set("redirect_uri", redirectUri);
@@ -814,7 +1236,7 @@ async function loginGemini(callbacks) {
814
1236
  }
815
1237
  async function refreshGeminiToken(refreshToken) {
816
1238
  const { clientId: clientId2, clientSecret } = getGeminiOAuthClientCredentials();
817
- const data = await postTokenRequest2({
1239
+ const data = await postTokenRequest({
818
1240
  grant_type: "refresh_token",
819
1241
  refresh_token: refreshToken,
820
1242
  client_id: clientId2,
@@ -909,7 +1331,7 @@ async function loginWithServer(authUrl, redirectUri, expectedState, callbacks) {
909
1331
  });
910
1332
  }
911
1333
  async function exchangeGeminiCode(code, verifier, redirectUri, clientId2, clientSecret) {
912
- const data = await postTokenRequest2({
1334
+ const data = await postTokenRequest({
913
1335
  grant_type: "authorization_code",
914
1336
  client_id: clientId2,
915
1337
  client_secret: clientSecret,
@@ -926,7 +1348,7 @@ async function exchangeGeminiCode(code, verifier, redirectUri, clientId2, client
926
1348
  expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
927
1349
  };
928
1350
  }
929
- async function postTokenRequest2(body) {
1351
+ async function postTokenRequest(body) {
930
1352
  const response = await fetch(TOKEN_URL2, {
931
1353
  method: "POST",
932
1354
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -1304,8 +1726,8 @@ async function refreshXaiToken(refreshToken) {
1304
1726
  }
1305
1727
 
1306
1728
  // src/file-lock.ts
1307
- var import_promises = __toESM(require("fs/promises"), 1);
1308
- var import_promises2 = require("timers/promises");
1729
+ var import_promises3 = __toESM(require("fs/promises"), 1);
1730
+ var import_promises4 = require("timers/promises");
1309
1731
  var STALE_TIMEOUT_MS = 1e4;
1310
1732
  var RETRY_INTERVAL_MS = 50;
1311
1733
  var MAX_WAIT_MS = 5e3;
@@ -1325,7 +1747,7 @@ async function acquireLock(lockPath) {
1325
1747
  while (true) {
1326
1748
  try {
1327
1749
  const info = { pid: process.pid, timestamp: Date.now() };
1328
- await import_promises.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
1750
+ await import_promises3.default.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
1329
1751
  return;
1330
1752
  } catch (err) {
1331
1753
  const code = err.code;
@@ -1334,47 +1756,47 @@ async function acquireLock(lockPath) {
1334
1756
  const lockPresent = await lockFileExists(lockPath);
1335
1757
  if (!lockPresent && ++refusedWithoutLock > 3) throw err;
1336
1758
  if (Date.now() - startTime > MAX_WAIT_MS) throw err;
1337
- await (0, import_promises2.setTimeout)(RETRY_INTERVAL_MS);
1759
+ await (0, import_promises4.setTimeout)(RETRY_INTERVAL_MS);
1338
1760
  continue;
1339
1761
  }
1340
1762
  try {
1341
- const content = await import_promises.default.readFile(lockPath, "utf-8");
1763
+ const content = await import_promises3.default.readFile(lockPath, "utf-8");
1342
1764
  const info = JSON.parse(content);
1343
1765
  const isProcessAlive = isAlive(info.pid);
1344
1766
  const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
1345
1767
  if (!isProcessAlive || isStale) {
1346
- await import_promises.default.unlink(lockPath).catch(() => {
1768
+ await import_promises3.default.unlink(lockPath).catch(() => {
1347
1769
  });
1348
1770
  continue;
1349
1771
  }
1350
1772
  } catch {
1351
- const stat = await import_promises.default.stat(lockPath).catch(() => null);
1773
+ const stat = await import_promises3.default.stat(lockPath).catch(() => null);
1352
1774
  if (!stat) continue;
1353
1775
  if (Date.now() - stat.mtimeMs > STALE_TIMEOUT_MS) {
1354
- await import_promises.default.unlink(lockPath).catch(() => {
1776
+ await import_promises3.default.unlink(lockPath).catch(() => {
1355
1777
  });
1356
1778
  continue;
1357
1779
  }
1358
1780
  }
1359
1781
  if (Date.now() - startTime > MAX_WAIT_MS) {
1360
- await import_promises.default.unlink(lockPath).catch(() => {
1782
+ await import_promises3.default.unlink(lockPath).catch(() => {
1361
1783
  });
1362
1784
  continue;
1363
1785
  }
1364
- await (0, import_promises2.setTimeout)(RETRY_INTERVAL_MS);
1786
+ await (0, import_promises4.setTimeout)(RETRY_INTERVAL_MS);
1365
1787
  }
1366
1788
  }
1367
1789
  }
1368
1790
  async function lockFileExists(lockPath) {
1369
1791
  try {
1370
- await import_promises.default.stat(lockPath);
1792
+ await import_promises3.default.stat(lockPath);
1371
1793
  return true;
1372
1794
  } catch (err) {
1373
1795
  return err.code !== "ENOENT";
1374
1796
  }
1375
1797
  }
1376
1798
  async function releaseLock(lockPath) {
1377
- await import_promises.default.unlink(lockPath).catch(() => {
1799
+ await import_promises3.default.unlink(lockPath).catch(() => {
1378
1800
  });
1379
1801
  }
1380
1802
  function isAlive(pid) {
@@ -1388,10 +1810,10 @@ function isAlive(pid) {
1388
1810
  }
1389
1811
 
1390
1812
  // src/logger.ts
1391
- var import_node_fs2 = __toESM(require("fs"), 1);
1392
- var import_node_path3 = __toESM(require("path"), 1);
1813
+ var import_node_fs4 = __toESM(require("fs"), 1);
1814
+ var import_node_path4 = __toESM(require("path"), 1);
1393
1815
  var import_node_crypto5 = require("crypto");
1394
- var import_gg_ai2 = require("@kenkaiiii/gg-ai");
1816
+ var import_gg_ai = require("@kenkaiiii/gg-ai");
1395
1817
  var MAX_BYTES = 10 * 1024 * 1024;
1396
1818
  var MIN_HEADROOM_BYTES = 64 * 1024;
1397
1819
  var fd = null;
@@ -1406,14 +1828,14 @@ var cleanups = [];
1406
1828
  var exactSecrets = [];
1407
1829
  function rotateIfNeeded(filePath, force = false) {
1408
1830
  try {
1409
- const st = import_node_fs2.default.statSync(filePath);
1831
+ const st = import_node_fs4.default.statSync(filePath);
1410
1832
  if (!force && st.size <= MAX_BYTES - MIN_HEADROOM_BYTES) return;
1411
1833
  const rotated = `${filePath}.1`;
1412
1834
  try {
1413
- import_node_fs2.default.unlinkSync(rotated);
1835
+ import_node_fs4.default.unlinkSync(rotated);
1414
1836
  } catch {
1415
1837
  }
1416
- import_node_fs2.default.renameSync(filePath, rotated);
1838
+ import_node_fs4.default.renameSync(filePath, rotated);
1417
1839
  } catch {
1418
1840
  }
1419
1841
  }
@@ -1421,18 +1843,18 @@ function rotateOpenLog() {
1421
1843
  if (fd === null || logPath === null) return false;
1422
1844
  const sizeBefore = fileBytes;
1423
1845
  try {
1424
- import_node_fs2.default.closeSync(fd);
1846
+ import_node_fs4.default.closeSync(fd);
1425
1847
  } catch {
1426
1848
  }
1427
1849
  fd = null;
1428
1850
  rotateIfNeeded(logPath, true);
1429
1851
  try {
1430
- fd = import_node_fs2.default.openSync(logPath, "a");
1431
- fileBytes = import_node_fs2.default.fstatSync(fd).size;
1852
+ fd = import_node_fs4.default.openSync(logPath, "a");
1853
+ fileBytes = import_node_fs4.default.fstatSync(fd).size;
1432
1854
  } catch {
1433
1855
  if (fd !== null) {
1434
1856
  try {
1435
- import_node_fs2.default.closeSync(fd);
1857
+ import_node_fs4.default.closeSync(fd);
1436
1858
  } catch {
1437
1859
  }
1438
1860
  }
@@ -1446,24 +1868,24 @@ function rotateOpenLog() {
1446
1868
  function openLog(filePath, name) {
1447
1869
  if (fd !== null || capped) return false;
1448
1870
  appName = name;
1449
- exactSecrets = (0, import_gg_ai2.environmentSecrets)(process.env);
1871
+ exactSecrets = (0, import_gg_ai.environmentSecrets)(process.env);
1450
1872
  try {
1451
- import_node_fs2.default.mkdirSync(import_node_path3.default.dirname(filePath), { recursive: true, mode: 448 });
1873
+ import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(filePath), { recursive: true, mode: 448 });
1452
1874
  } catch {
1453
1875
  }
1454
1876
  rotateIfNeeded(filePath);
1455
1877
  try {
1456
- fd = import_node_fs2.default.openSync(filePath, "a");
1878
+ fd = import_node_fs4.default.openSync(filePath, "a");
1457
1879
  } catch {
1458
1880
  fd = null;
1459
1881
  fileBytes = 0;
1460
1882
  return false;
1461
1883
  }
1462
1884
  try {
1463
- fileBytes = import_node_fs2.default.fstatSync(fd).size;
1885
+ fileBytes = import_node_fs4.default.fstatSync(fd).size;
1464
1886
  } catch {
1465
1887
  try {
1466
- import_node_fs2.default.closeSync(fd);
1888
+ import_node_fs4.default.closeSync(fd);
1467
1889
  } catch {
1468
1890
  }
1469
1891
  fd = null;
@@ -1476,7 +1898,7 @@ function openLog(filePath, name) {
1476
1898
  sessionId = (0, import_node_crypto5.randomBytes)(4).toString("hex");
1477
1899
  try {
1478
1900
  if (fileBytes < MAX_BYTES) {
1479
- const written = import_node_fs2.default.writeSync(fd, "\n");
1901
+ const written = import_node_fs4.default.writeSync(fd, "\n");
1480
1902
  fileBytes += written;
1481
1903
  processBytes += written;
1482
1904
  }
@@ -1493,10 +1915,10 @@ function isLoggerOpen() {
1493
1915
  function log(level, category, message, data) {
1494
1916
  if (fd === null) return;
1495
1917
  const ts = (/* @__PURE__ */ new Date()).toISOString();
1496
- const safeMessage = (0, import_gg_ai2.redactText)(message, { secrets: exactSecrets });
1918
+ const safeMessage = (0, import_gg_ai.redactText)(message, { secrets: exactSecrets });
1497
1919
  let line = `[${ts}] [sid=${sessionId}] [${level}] [${category}] ${safeMessage}`;
1498
1920
  if (data) {
1499
- const safeData = (0, import_gg_ai2.redactValue)(data, { secrets: exactSecrets });
1921
+ const safeData = (0, import_gg_ai.redactValue)(data, { secrets: exactSecrets });
1500
1922
  const pairs = Object.entries(safeData).map(([k, v]) => {
1501
1923
  if (typeof v === "string") return `${k}=${v}`;
1502
1924
  if (typeof v === "bigint") return `${k}=${String(v)}`;
@@ -1511,9 +1933,9 @@ function log(level, category, message, data) {
1511
1933
  `;
1512
1934
  try {
1513
1935
  if (fileBytes + Buffer.byteLength(capLine) <= MAX_BYTES) {
1514
- fileBytes += import_node_fs2.default.writeSync(fd, capLine);
1936
+ fileBytes += import_node_fs4.default.writeSync(fd, capLine);
1515
1937
  }
1516
- import_node_fs2.default.closeSync(fd);
1938
+ import_node_fs4.default.closeSync(fd);
1517
1939
  } catch {
1518
1940
  }
1519
1941
  fd = null;
@@ -1523,7 +1945,7 @@ function log(level, category, message, data) {
1523
1945
  if (fileBytes + lineBytes > MAX_BYTES && !rotationFailed && !rotateOpenLog()) return;
1524
1946
  if (fd === null) return;
1525
1947
  try {
1526
- const written = import_node_fs2.default.writeSync(fd, line);
1948
+ const written = import_node_fs4.default.writeSync(fd, line);
1527
1949
  fileBytes += written;
1528
1950
  processBytes += written;
1529
1951
  } catch {
@@ -1536,7 +1958,7 @@ function closeLogger(opts) {
1536
1958
  if (fd !== null) {
1537
1959
  if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
1538
1960
  try {
1539
- if (fd !== null) import_node_fs2.default.closeSync(fd);
1961
+ if (fd !== null) import_node_fs4.default.closeSync(fd);
1540
1962
  } catch {
1541
1963
  }
1542
1964
  }
@@ -1558,7 +1980,7 @@ function isHiddenProvider(provider) {
1558
1980
  }
1559
1981
 
1560
1982
  // src/auth-storage.ts
1561
- var import_gg_ai3 = require("@kenkaiiii/gg-ai");
1983
+ var import_gg_ai2 = require("@kenkaiiii/gg-ai");
1562
1984
 
1563
1985
  // src/minimax-region.ts
1564
1986
  var MINIMAX_GLOBAL_BASE_URL = "https://api.minimax.io/anthropic";
@@ -1703,7 +2125,7 @@ function cachedMiniMaxRegion(apiKey) {
1703
2125
  }
1704
2126
  function readStoredBaseUrlSync(authFile, provider) {
1705
2127
  try {
1706
- const data = JSON.parse((0, import_node_fs3.readFileSync)(authFile, "utf-8"));
2128
+ const data = JSON.parse((0, import_node_fs5.readFileSync)(authFile, "utf-8"));
1707
2129
  return activeBaseUrlEntry(data, provider)?.baseUrl;
1708
2130
  } catch {
1709
2131
  return void 0;
@@ -1717,6 +2139,8 @@ function refreshThresholdMs(creds) {
1717
2139
  }
1718
2140
  var USAGE_EXHAUSTED_DEFAULT_MS = 15 * 60 * 1e3;
1719
2141
  var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
2142
+ // Anthropic is API-key only; Claude subscriptions run through Claude Code.
2143
+ "anthropic",
1720
2144
  "glm",
1721
2145
  "moonshot",
1722
2146
  "xiaomi",
@@ -1797,11 +2221,44 @@ var AuthStorage = class {
1797
2221
  if (provider === "gemini") {
1798
2222
  return Boolean(this.data["gemini"] || this.data[GEMINI_API_KEY]);
1799
2223
  }
2224
+ if (provider === "anthropic") {
2225
+ return Boolean(this.data.anthropic) && !isLegacyClaudeOAuth(this.data.anthropic) || isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY]);
2226
+ }
1800
2227
  if (provider === "local") {
1801
2228
  return Object.keys(this.data).some((key) => key.startsWith(LOCAL_AUTH_KEY_PREFIX));
1802
2229
  }
1803
2230
  return Boolean(this.data[provider]);
1804
2231
  }
2232
+ /** True when the user signed in with Claude through Claude Code (marker only, no token). */
2233
+ async hasClaudeCodeLogin() {
2234
+ await this.ensureFresh();
2235
+ return isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY]);
2236
+ }
2237
+ /** Record a Claude Code sign-in. Claude Code keeps the token; auth.json gets only the marker. */
2238
+ async setClaudeCodeLogin() {
2239
+ await this.mutateLatest((latest) => {
2240
+ latest[CLAUDE_CODE_AUTH_KEY] = { ...CLAUDE_CODE_AUTH_MARKER };
2241
+ if (isLegacyClaudeOAuth(latest.anthropic)) delete latest.anthropic;
2242
+ });
2243
+ }
2244
+ /**
2245
+ * Remove an old GG Claude sign-in token (`sk-ant-oat…`), if any. True when one
2246
+ * was removed, so the caller can tell the user once to sign in with Claude again.
2247
+ */
2248
+ async takeLegacyClaudeSignIn() {
2249
+ await this.ensureFresh();
2250
+ if (!isLegacyClaudeOAuth(this.data.anthropic)) return false;
2251
+ let removed = false;
2252
+ await this.mutateLatest((latest) => {
2253
+ if (!isLegacyClaudeOAuth(latest.anthropic)) return;
2254
+ delete latest.anthropic;
2255
+ removed = true;
2256
+ });
2257
+ return removed;
2258
+ }
2259
+ async clearClaudeCodeLogin() {
2260
+ await this.clearCredentials(CLAUDE_CODE_AUTH_KEY);
2261
+ }
1805
2262
  /** Endpoint ids that currently have a `local:<id>` credential stored. */
1806
2263
  async listLocalEndpointIds() {
1807
2264
  await this.ensureLoaded();
@@ -1814,7 +2271,7 @@ var AuthStorage = class {
1814
2271
  */
1815
2272
  async setLocalEndpoint(endpointId, baseUrl, apiKey) {
1816
2273
  await this.setCredentials(`${LOCAL_AUTH_KEY_PREFIX}${endpointId}`, {
1817
- accessToken: apiKey && apiKey.length > 0 ? apiKey : import_gg_ai3.LOCAL_NO_API_KEY,
2274
+ accessToken: apiKey && apiKey.length > 0 ? apiKey : import_gg_ai2.LOCAL_NO_API_KEY,
1818
2275
  refreshToken: "",
1819
2276
  expiresAt: Date.now() + LOCAL_CREDENTIAL_LIFETIME_MS,
1820
2277
  baseUrl
@@ -1855,7 +2312,7 @@ var AuthStorage = class {
1855
2312
  const first = !this.loaded;
1856
2313
  await withFileLock(this.filePath, async () => {
1857
2314
  try {
1858
- const content = await import_promises3.default.readFile(this.filePath, "utf-8");
2315
+ const content = await import_promises5.default.readFile(this.filePath, "utf-8");
1859
2316
  this.data = JSON.parse(content);
1860
2317
  if (first) {
1861
2318
  log("INFO", "auth", `Loaded credentials from ${this.filePath}`, {
@@ -1901,7 +2358,7 @@ var AuthStorage = class {
1901
2358
  }
1902
2359
  let changed;
1903
2360
  try {
1904
- const stat = await import_promises3.default.stat(this.filePath);
2361
+ const stat = await import_promises5.default.stat(this.filePath);
1905
2362
  changed = stat.ino !== this.snapshotIno || stat.mtimeMs !== this.snapshotMtimeMs || stat.size !== this.snapshotSize;
1906
2363
  } catch {
1907
2364
  changed = this.snapshotSize !== -1;
@@ -1914,7 +2371,7 @@ var AuthStorage = class {
1914
2371
  */
1915
2372
  async rememberSnapshot() {
1916
2373
  try {
1917
- const stat = await import_promises3.default.stat(this.filePath);
2374
+ const stat = await import_promises5.default.stat(this.filePath);
1918
2375
  this.snapshotMtimeMs = stat.mtimeMs;
1919
2376
  this.snapshotSize = stat.size;
1920
2377
  this.snapshotIno = stat.ino;
@@ -2055,7 +2512,7 @@ var AuthStorage = class {
2055
2512
  await this.ensureFresh();
2056
2513
  }
2057
2514
  const directStorageKeys = opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider) ? opts.storageKeys : providerStorageKeys(provider);
2058
- if (!directStorageKeys.some((key) => Boolean(this.data[key]))) {
2515
+ if (!directStorageKeys.some((key) => Boolean(this.data[key])) && !(provider === "anthropic" && this.data[CLAUDE_CODE_AUTH_KEY])) {
2059
2516
  await this.reloadLatest();
2060
2517
  }
2061
2518
  if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
@@ -2078,6 +2535,12 @@ var AuthStorage = class {
2078
2535
  }
2079
2536
  throw new NotLoggedInError(provider);
2080
2537
  }
2538
+ if (provider === "anthropic" && (!this.data.anthropic || isLegacyClaudeOAuth(this.data.anthropic))) {
2539
+ if (isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY])) {
2540
+ return { accessToken: "", refreshToken: "", expiresAt: Number.MAX_SAFE_INTEGER };
2541
+ }
2542
+ if (this.data.anthropic) throw new NotLoggedInError(provider);
2543
+ }
2081
2544
  const dual = dualAuthProvider(provider);
2082
2545
  const dualOAuthCreds = dual ? this.data[dual.oauthKey] : void 0;
2083
2546
  if (dual && dualOAuthCreds) {
@@ -2140,7 +2603,7 @@ var AuthStorage = class {
2140
2603
  this.data = latest;
2141
2604
  return latestCreds;
2142
2605
  }
2143
- const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : provider === XAI_OAUTH_KEY ? refreshXaiToken : refreshOpenAIToken;
2606
+ const refreshFn = provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : provider === XAI_OAUTH_KEY ? refreshXaiToken : refreshOpenAIToken;
2144
2607
  let refreshed;
2145
2608
  try {
2146
2609
  refreshed = await refreshFn(latestCreds.refreshToken);
@@ -2199,7 +2662,7 @@ var AuthStorage = class {
2199
2662
  };
2200
2663
  async function readAuthData(filePath) {
2201
2664
  try {
2202
- const content = await import_promises3.default.readFile(filePath, "utf-8");
2665
+ const content = await import_promises5.default.readFile(filePath, "utf-8");
2203
2666
  return JSON.parse(content);
2204
2667
  } catch (error) {
2205
2668
  if (error.code === "ENOENT") return {};
@@ -2209,10 +2672,10 @@ async function readAuthData(filePath) {
2209
2672
  async function atomicWriteFile(filePath, content) {
2210
2673
  const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${import_node_crypto6.default.randomUUID().slice(0, 8)}.tmp`;
2211
2674
  try {
2212
- await import_promises3.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
2213
- await import_promises3.default.rename(tmpPath, filePath);
2675
+ await import_promises5.default.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
2676
+ await import_promises5.default.rename(tmpPath, filePath);
2214
2677
  } catch (err) {
2215
- await import_promises3.default.unlink(tmpPath).catch(() => {
2678
+ await import_promises5.default.unlink(tmpPath).catch(() => {
2216
2679
  });
2217
2680
  throw err;
2218
2681
  }
@@ -2415,9 +2878,9 @@ var MODELS = [
2415
2878
  billing: CODEX_FAST_BILLING
2416
2879
  },
2417
2880
  // GPT-6 Sol + Luna — released 2026-09-22 below Astra, replacing the whole
2418
- // GPT-5.6 family (Sol/Terra/Luna; there is no GPT-6 Terra — OpenAI's Codex
2881
+ // GPT-5.6 family (Sol/Terra/Luna; there was no GPT-6 Terra — OpenAI's Codex
2419
2882
  // catalog upgrades 5.6 Terra to 6 Sol). GPT-6.1 Sol (2026-09-29) then replaced
2420
- // GPT-6 Sol. Same window split as Astra: 1.05M on the public Responses API,
2883
+ // GPT-6 Sol, and GPT-6.1 Terra (2026-10-26) sits between Sol and Luna. Same window split as Astra: 1.05M on the public Responses API,
2421
2884
  // 272K on the Codex route; 128K output, text+image input, freeform
2422
2885
  // apply_patch, responses-lite transport. The GPT-5.6 ids and `gpt-6-sol` are
2423
2886
  // retired — a saved session on one falls back to the provider default on
@@ -2459,6 +2922,40 @@ var MODELS = [
2459
2922
  maxThinkingLevel: "ultra",
2460
2923
  billing: CODEX_FAST_BILLING
2461
2924
  },
2925
+ {
2926
+ // GPT-6.1 Terra (Codex priority 7, default low, client >= 0.153.0). $1/$5
2927
+ // MTok, cached input $0.05. Unlike Sol/Astra the Codex route gets a 500K
2928
+ // window (catalog `context_window`). Same ladder and Ultra preset as Sol.
2929
+ id: "gpt-6.1-terra",
2930
+ name: "GPT-6.1 Terra",
2931
+ provider: "openai",
2932
+ contextWindow: 105e4,
2933
+ codexContextWindow: 5e5,
2934
+ maxOutputTokens: 128e3,
2935
+ supportsThinking: true,
2936
+ defaultThinkingLevel: "low",
2937
+ supportsImages: true,
2938
+ supportsVideo: false,
2939
+ costTier: "medium",
2940
+ maxThinkingLevel: "ultra"
2941
+ },
2942
+ {
2943
+ // Codex Fast mode for 6.1 Terra ("1.5x speed") — see `gpt-6-astra-fast`
2944
+ // for the wire mapping and billing.
2945
+ id: "gpt-6.1-terra-fast",
2946
+ name: "GPT-6.1 Terra Fast",
2947
+ provider: "openai",
2948
+ contextWindow: 105e4,
2949
+ codexContextWindow: 5e5,
2950
+ maxOutputTokens: 128e3,
2951
+ supportsThinking: true,
2952
+ defaultThinkingLevel: "low",
2953
+ supportsImages: true,
2954
+ supportsVideo: false,
2955
+ costTier: "medium",
2956
+ maxThinkingLevel: "ultra",
2957
+ billing: CODEX_FAST_BILLING
2958
+ },
2462
2959
  {
2463
2960
  // Luna — "Fast and affordable model for easier tasks." (Codex priority 4,
2464
2961
  // default medium, needs a Codex client >= 0.155.0). $0.10/$0.50 MTok.
@@ -3319,12 +3816,12 @@ function clampThinkingForPlanMode(level) {
3319
3816
  }
3320
3817
 
3321
3818
  // src/local-models.ts
3322
- var import_gg_ai4 = require("@kenkaiiii/gg-ai");
3819
+ var import_gg_ai3 = require("@kenkaiiii/gg-ai");
3323
3820
  var DEFAULT_LOCAL_ENDPOINTS = [
3324
3821
  { id: "ollama", label: "Ollama", baseUrl: "http://127.0.0.1:11434/v1", kind: "ollama" }
3325
3822
  ];
3326
3823
  var FALLBACK_CONTEXT_WINDOW = 4096;
3327
- var LOCAL_API_KEY_PLACEHOLDER = import_gg_ai4.LOCAL_NO_API_KEY;
3824
+ var LOCAL_API_KEY_PLACEHOLDER = import_gg_ai3.LOCAL_NO_API_KEY;
3328
3825
  var DEFAULT_PROBE_TIMEOUT_MS = 1200;
3329
3826
  var ENRICH_CONCURRENCY = 6;
3330
3827
  var CACHE_TTL_MS = 3e4;
@@ -3353,7 +3850,7 @@ function endpointRoot(baseUrl) {
3353
3850
  return baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "");
3354
3851
  }
3355
3852
  function authHeaders(endpoint) {
3356
- const key = (0, import_gg_ai4.localBearerKey)(endpoint.apiKey);
3853
+ const key = (0, import_gg_ai3.localBearerKey)(endpoint.apiKey);
3357
3854
  return {
3358
3855
  ...key ? { Authorization: `Bearer ${key}` } : {},
3359
3856
  Accept: "application/json"
@@ -3643,18 +4140,138 @@ function findProbedModel(probes, modelId) {
3643
4140
  return void 0;
3644
4141
  }
3645
4142
 
4143
+ // src/claude-code/usage.ts
4144
+ var import_promises6 = __toESM(require("fs/promises"), 1);
4145
+ var import_node_path5 = __toESM(require("path"), 1);
4146
+ var USAGE_FILE = "usage.json";
4147
+ var WINDOWS = {
4148
+ five_hour: { kind: "current", label: "5-hour" },
4149
+ seven_day: { kind: "weekly", label: "Weekly" }
4150
+ };
4151
+ function finite(value) {
4152
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
4153
+ }
4154
+ function toWindow(key, raw) {
4155
+ const shape = WINDOWS[key];
4156
+ const fraction = finite(raw?.utilization);
4157
+ if (!shape || fraction === void 0) return null;
4158
+ const resetsAt = finite(raw?.resetsAt);
4159
+ return {
4160
+ ...shape,
4161
+ usedPercent: Math.min(100, Math.max(0, fraction * 100)),
4162
+ ...resetsAt !== void 0 ? { resetsAt: resetsAt * 1e3 } : {}
4163
+ };
4164
+ }
4165
+ function mergeClaudeCodeRateLimit(info, previous, now = Date.now()) {
4166
+ const reported = info.unifiedWindows ? Object.entries(info.unifiedWindows).map(([key, raw]) => toWindow(key, raw)) : info.rateLimitType ? [toWindow(info.rateLimitType, info)] : [];
4167
+ const byKind = new Map(previous?.windows.map((w) => [w.kind, w]) ?? []);
4168
+ for (const window of reported) if (window) byKind.set(window.kind, window);
4169
+ const windows = [...byKind.values()].sort(
4170
+ (a, b) => a.kind === b.kind ? 0 : a.kind === "current" ? -1 : 1
4171
+ );
4172
+ return { provider: "anthropic", displayName: "Anthropic", windows, fetchedAt: now };
4173
+ }
4174
+ function usagePath(opts) {
4175
+ return import_node_path5.default.join(claudeCodeHome(opts), USAGE_FILE);
4176
+ }
4177
+ async function readClaudeCodeUsage(opts = {}) {
4178
+ let parsed;
4179
+ try {
4180
+ parsed = JSON.parse(await import_promises6.default.readFile(usagePath(opts), "utf8"));
4181
+ } catch {
4182
+ return null;
4183
+ }
4184
+ if (!Array.isArray(parsed?.windows) || typeof parsed.fetchedAt !== "number") return null;
4185
+ const now = opts.now ?? Date.now();
4186
+ const windows = parsed.windows.filter(
4187
+ (w) => (w.kind === "current" || w.kind === "weekly") && typeof w.usedPercent === "number" && (w.resetsAt === void 0 || w.resetsAt > now)
4188
+ );
4189
+ return { provider: "anthropic", displayName: "Anthropic", windows, fetchedAt: parsed.fetchedAt };
4190
+ }
4191
+ var writeQueue = Promise.resolve();
4192
+ function recordClaudeCodeRateLimit(info, opts = {}) {
4193
+ const write = async () => {
4194
+ const now = opts.now ?? Date.now();
4195
+ const next = mergeClaudeCodeRateLimit(info, await readClaudeCodeUsage({ ...opts, now }), now);
4196
+ if (next.windows.length === 0) return;
4197
+ const file = usagePath(opts);
4198
+ await import_promises6.default.mkdir(import_node_path5.default.dirname(file), { recursive: true });
4199
+ await import_promises6.default.writeFile(file, JSON.stringify(next), "utf8");
4200
+ };
4201
+ writeQueue = writeQueue.then(write).catch(() => {
4202
+ });
4203
+ return writeQueue;
4204
+ }
4205
+ async function clearClaudeCodeUsage(opts = {}) {
4206
+ await writeQueue;
4207
+ await import_promises6.default.rm(usagePath(opts), { force: true }).catch(() => {
4208
+ });
4209
+ }
4210
+
4211
+ // src/claude-code/sign-in.ts
4212
+ async function signInWithClaudeCode(auth, callbacks, opts = {}) {
4213
+ if (!opts.binary && !resolveClaudeCodeBinary(opts)) {
4214
+ callbacks.onStatus("Downloading Claude Code (one time)\u2026");
4215
+ await ensureClaudeCodeBinary({
4216
+ ...opts,
4217
+ onProgress: (progress) => {
4218
+ callbacks.onStatus(formatClaudeCodeInstallProgress(progress));
4219
+ opts.onProgress?.(progress);
4220
+ }
4221
+ });
4222
+ }
4223
+ let status = await claudeCodeAuthStatus(opts);
4224
+ if (!status.loggedIn) {
4225
+ callbacks.onStatus("Opening Claude sign-in\u2026");
4226
+ await loginClaudeCode(callbacks, opts);
4227
+ status = await claudeCodeAuthStatus(opts);
4228
+ }
4229
+ if (!status.loggedIn) throw new Error("Claude sign-in didn't finish. Try again.");
4230
+ await auth.setClaudeCodeLogin();
4231
+ return status;
4232
+ }
4233
+ async function signOutOfClaudeCode(auth, opts = {}) {
4234
+ await auth.clearClaudeCodeLogin();
4235
+ await clearClaudeCodeUsage(opts);
4236
+ try {
4237
+ await logoutClaudeCode(opts);
4238
+ } catch (err) {
4239
+ log(
4240
+ "WARN",
4241
+ "auth",
4242
+ `claude auth logout failed: ${err instanceof Error ? err.message : String(err)}`
4243
+ );
4244
+ }
4245
+ }
4246
+
3646
4247
  // src/auth-providers.ts
3647
4248
  var ALL_AUTH_PROVIDERS = [
3648
4249
  {
3649
4250
  value: "anthropic",
3650
4251
  label: "Anthropic",
3651
4252
  description: "Claude Fable 5.1, Opus 5.5, Opus 5.5 Fast, Sonnet 5.5, Haiku 5.5",
3652
- methods: ["oauth"]
4253
+ methods: ["oauth", "apikey"],
4254
+ apiKeyLabel: "Anthropic",
4255
+ methodDetails: {
4256
+ oauth: {
4257
+ method: "oauth",
4258
+ label: "Sign in with Claude",
4259
+ billing: "Uses your Claude Pro or Max plan.",
4260
+ when: "Runs through Anthropic's Claude Code, which GG Coder downloads once (about 140 MB) the first time you pick this.",
4261
+ requires: "A Claude Pro or Max subscription."
4262
+ },
4263
+ apikey: {
4264
+ method: "apikey",
4265
+ label: "Anthropic API key",
4266
+ billing: "Pay-per-token from an Anthropic Console key.",
4267
+ when: "No subscription needed. Used only while you are not signed in with Claude."
4268
+ }
4269
+ }
3653
4270
  },
3654
4271
  {
3655
4272
  value: "openai",
3656
4273
  label: "OpenAI",
3657
- description: "GPT-6 Astra, GPT-6 Astra Fast, GPT-6.1 Sol, GPT-6.1 Sol Fast, GPT-6 Luna, GPT-6 Luna Fast",
4274
+ description: "GPT-6 Astra, GPT-6 Astra Fast, GPT-6.1 Sol, GPT-6.1 Sol Fast, GPT-6.1 Terra, GPT-6.1 Terra Fast, GPT-6 Luna, GPT-6 Luna Fast",
3658
4275
  methods: ["oauth"]
3659
4276
  },
3660
4277
  {
@@ -3817,6 +4434,9 @@ function authPriorityNote(provider) {
3817
4434
  if (provider === "gemini") {
3818
4435
  return "Uses the Gemini API key first when one is set; otherwise the Google sign-in.";
3819
4436
  }
4437
+ if (provider === "anthropic") {
4438
+ return "Uses Sign in with Claude while you are signed in; otherwise the Anthropic API key.";
4439
+ }
3820
4440
  const dual = dualAuthProvider(provider);
3821
4441
  if (!dual) return void 0;
3822
4442
  return `Uses ${dual.oauthLabel} first; the ${dual.apiKeyLabel} takes over when it runs out, then switches back.`;
@@ -3831,7 +4451,6 @@ var SubscriptionUsageError = class extends Error {
3831
4451
  this.name = "SubscriptionUsageError";
3832
4452
  }
3833
4453
  };
3834
- var ANTHROPIC_USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
3835
4454
  var CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
3836
4455
  function finiteNumber(value) {
3837
4456
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
@@ -3899,45 +4518,6 @@ async function readUsageResponse(response, now) {
3899
4518
  throw new SubscriptionUsageError("Subscription usage response was not valid JSON");
3900
4519
  }
3901
4520
  }
3902
- async function fetchAnthropicUsage(credentials, fetchFn, signal, now) {
3903
- const response = await fetchFn(ANTHROPIC_USAGE_URL, {
3904
- method: "GET",
3905
- signal,
3906
- headers: {
3907
- Authorization: `Bearer ${credentials.accessToken}`,
3908
- Accept: "application/json",
3909
- "anthropic-version": "2023-06-01",
3910
- "anthropic-beta": "oauth-2025-04-20",
3911
- "User-Agent": "ggcoder"
3912
- }
3913
- });
3914
- const data = await readUsageResponse(response, now());
3915
- const windows = [];
3916
- const currentPercent = clampPercent(data.five_hour?.utilization);
3917
- if (currentPercent !== void 0) {
3918
- windows.push({
3919
- kind: "current",
3920
- label: "5-hour",
3921
- usedPercent: currentPercent,
3922
- resetsAt: isoTimestamp(data.five_hour?.resets_at)
3923
- });
3924
- }
3925
- const weeklyPercent = clampPercent(data.seven_day?.utilization);
3926
- if (weeklyPercent !== void 0) {
3927
- windows.push({
3928
- kind: "weekly",
3929
- label: "Weekly",
3930
- usedPercent: weeklyPercent,
3931
- resetsAt: isoTimestamp(data.seven_day?.resets_at)
3932
- });
3933
- }
3934
- return {
3935
- provider: "anthropic",
3936
- displayName: "Anthropic",
3937
- windows,
3938
- fetchedAt: now()
3939
- };
3940
- }
3941
4521
  async function fetchCodexUsage(credentials, fetchFn, signal, now) {
3942
4522
  const headers = {
3943
4523
  Authorization: `Bearer ${credentials.accessToken}`,
@@ -4053,7 +4633,7 @@ async function fetchSubscriptionUsage(provider, credentials, options = {}) {
4053
4633
  const now = options.now ?? Date.now;
4054
4634
  const signal = AbortSignal.timeout(options.timeoutMs ?? 8e3);
4055
4635
  try {
4056
- return provider === "anthropic" ? await fetchAnthropicUsage(credentials, fetchFn, signal, now) : provider === "openai" ? await fetchCodexUsage(credentials, fetchFn, signal, now) : await fetchKimiUsage(credentials, fetchFn, signal, now);
4636
+ return provider === "openai" ? await fetchCodexUsage(credentials, fetchFn, signal, now) : await fetchKimiUsage(credentials, fetchFn, signal, now);
4057
4637
  } catch (error) {
4058
4638
  if (error instanceof SubscriptionUsageError) throw error;
4059
4639
  const message = error instanceof Error ? error.message : String(error);
@@ -4389,9 +4969,9 @@ async function transcribeVoice(fileUrl) {
4389
4969
  }
4390
4970
 
4391
4971
  // src/auto-update.ts
4392
- var import_node_child_process2 = require("child_process");
4393
- var import_node_fs4 = __toESM(require("fs"), 1);
4394
- var import_node_path4 = __toESM(require("path"), 1);
4972
+ var import_node_child_process3 = require("child_process");
4973
+ var import_node_fs6 = __toESM(require("fs"), 1);
4974
+ var import_node_path6 = __toESM(require("path"), 1);
4395
4975
  var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
4396
4976
  var FETCH_TIMEOUT_MS = 1e4;
4397
4977
  function compareVersions(a, b) {
@@ -4406,7 +4986,7 @@ function compareVersions(a, b) {
4406
4986
  function performUpdateInBackground(command) {
4407
4987
  try {
4408
4988
  const parts = command.split(" ");
4409
- const child = (0, import_node_child_process2.spawn)(parts[0], parts.slice(1), {
4989
+ const child = (0, import_node_child_process3.spawn)(parts[0], parts.slice(1), {
4410
4990
  detached: true,
4411
4991
  stdio: "ignore",
4412
4992
  env: { ...process.env, npm_config_loglevel: "silent" }
@@ -4424,7 +5004,7 @@ function createAutoUpdater(config) {
4424
5004
  }
4425
5005
  function readState() {
4426
5006
  try {
4427
- const raw = import_node_fs4.default.readFileSync(stateFilePath(), "utf-8");
5007
+ const raw = import_node_fs6.default.readFileSync(stateFilePath(), "utf-8");
4428
5008
  return JSON.parse(raw);
4429
5009
  } catch {
4430
5010
  return null;
@@ -4433,8 +5013,8 @@ function createAutoUpdater(config) {
4433
5013
  function writeState(state) {
4434
5014
  try {
4435
5015
  const filePath = stateFilePath();
4436
- import_node_fs4.default.mkdirSync(import_node_path4.default.dirname(filePath), { recursive: true, mode: 448 });
4437
- import_node_fs4.default.writeFileSync(filePath, JSON.stringify(state));
5016
+ import_node_fs6.default.mkdirSync(import_node_path6.default.dirname(filePath), { recursive: true, mode: 448 });
5017
+ import_node_fs6.default.writeFileSync(filePath, JSON.stringify(state));
4438
5018
  } catch {
4439
5019
  }
4440
5020
  }
@@ -4560,6 +5140,9 @@ function createAutoUpdater(config) {
4560
5140
  0 && (module.exports = {
4561
5141
  AUTH_PROVIDERS,
4562
5142
  AuthStorage,
5143
+ CLAUDE_CODE_AUTH_KEY,
5144
+ CLAUDE_CODE_AUTH_MARKER,
5145
+ CLAUDE_CODE_MANIFEST,
4563
5146
  DEFAULT_LOCAL_ENDPOINTS,
4564
5147
  DEFAULT_MAX_VIDEO_BYTES,
4565
5148
  FALLBACK_CONTEXT_WINDOW,
@@ -4582,6 +5165,14 @@ function createAutoUpdater(config) {
4582
5165
  XIAOMI_TOKEN_PLAN_BASE_URLS,
4583
5166
  authPriorityNote,
4584
5167
  clampThinkingForPlanMode,
5168
+ claudeCodeAuthStatus,
5169
+ claudeCodeBinaryPath,
5170
+ claudeCodeCallbackUrl,
5171
+ claudeCodeConfigDir,
5172
+ claudeCodeEnv,
5173
+ claudeCodeHome,
5174
+ claudeCodePlatformKey,
5175
+ clearClaudeCodeUsage,
4585
5176
  clearLocalDiscoveryCache,
4586
5177
  clearRuntimeModels,
4587
5178
  closeLogger,
@@ -4595,8 +5186,10 @@ function createAutoUpdater(config) {
4595
5186
  dualAuthProvider,
4596
5187
  dualAuthProviderByOAuthKey,
4597
5188
  endpointRoot,
5189
+ ensureClaudeCodeBinary,
4598
5190
  fetchSubscriptionUsage,
4599
5191
  findProbedModel,
5192
+ formatClaudeCodeInstallProgress,
4600
5193
  formatLocalModelId,
4601
5194
  generatePKCE,
4602
5195
  getAllModels,
@@ -4604,8 +5197,6 @@ function createAutoUpdater(config) {
4604
5197
  getAuthProvider,
4605
5198
  getAuthStorageKey,
4606
5199
  getAuthStorageKeys,
4607
- getClaudeCliUserAgent,
4608
- getClaudeCodeVersion,
4609
5200
  getContextWindow,
4610
5201
  getDefaultModel,
4611
5202
  getDefaultThinkingLevel,
@@ -4623,9 +5214,11 @@ function createAutoUpdater(config) {
4623
5214
  getVideoByteLimit,
4624
5215
  grokCliBaseUrl,
4625
5216
  grokCliHeaders,
5217
+ isClaudeCodeAuthMarker,
4626
5218
  isGrokCliEndpoint,
4627
5219
  isHiddenProvider,
4628
5220
  isKimiCodingEndpoint,
5221
+ isLegacyClaudeOAuth,
4629
5222
  isLocalModelId,
4630
5223
  isLoggerOpen,
4631
5224
  isModelLoaded,
@@ -4636,21 +5229,23 @@ function createAutoUpdater(config) {
4636
5229
  localAuthStorageKey,
4637
5230
  localModelShortName,
4638
5231
  log,
4639
- loginAnthropic,
5232
+ loginClaudeCode,
4640
5233
  loginGemini,
4641
5234
  loginKimi,
4642
5235
  loginOpenAI,
4643
5236
  loginXai,
5237
+ logoutClaudeCode,
5238
+ mergeClaudeCodeRateLimit,
4644
5239
  oauthStorageKey,
4645
5240
  ollamaServedContext,
4646
5241
  openLog,
4647
5242
  parseLocalModelId,
4648
5243
  parseOpenRouterCatalog,
4649
- pendingClaudeCodeVersionRefresh,
4650
5244
  probeEndpoint,
4651
5245
  providerStorageKeys,
5246
+ readClaudeCodeUsage,
4652
5247
  readStoredBaseUrlSync,
4653
- refreshAnthropicToken,
5248
+ recordClaudeCodeRateLimit,
4654
5249
  refreshGeminiToken,
4655
5250
  refreshKimiToken,
4656
5251
  refreshOpenAIToken,
@@ -4658,8 +5253,11 @@ function createAutoUpdater(config) {
4658
5253
  registerLogCleanup,
4659
5254
  registerRuntimeModels,
4660
5255
  resample,
5256
+ resolveClaudeCodeBinary,
4661
5257
  setProgressCallback,
4662
5258
  setProviderCatalog,
5259
+ signInWithClaudeCode,
5260
+ signOutOfClaudeCode,
4663
5261
  toModelInfo,
4664
5262
  transcribeVoice,
4665
5263
  usesOpenAICodexTransport,