@kenkaiiii/gg-core 5.92.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getAppPaths
3
- } from "./chunk-2X5L6U4Z.js";
3
+ } from "./chunk-GXOFTHM5.js";
4
4
 
5
5
  // src/oauth/kimi.ts
6
6
  import { execFileSync } from "child_process";
@@ -230,141 +230,539 @@ async function refreshKimiToken(refreshToken) {
230
230
  }
231
231
 
232
232
  // src/auth-storage.ts
233
- import fs3 from "fs/promises";
233
+ import fs5 from "fs/promises";
234
234
  import { readFileSync as readFileSync2 } from "fs";
235
235
  import crypto5 from "crypto";
236
236
 
237
- // src/oauth/anthropic.ts
237
+ // src/claude-code/runtime.ts
238
+ import { spawn } from "child_process";
238
239
  import crypto2 from "crypto";
240
+ import fs2 from "fs";
241
+ import fsp2 from "fs/promises";
242
+ import path2 from "path";
239
243
 
240
- // src/oauth/pkce.ts
241
- function base64urlEncode(bytes) {
242
- let binary = "";
243
- for (const byte of bytes) {
244
- binary += String.fromCharCode(byte);
244
+ // src/claude-code/tar.ts
245
+ import fs from "fs";
246
+ import fsp from "fs/promises";
247
+ import zlib from "zlib";
248
+ var BLOCK = 512;
249
+ function readString(buf, offset, length) {
250
+ const end = buf.indexOf(0, offset);
251
+ return buf.toString("utf8", offset, end === -1 || end > offset + length ? offset + length : end);
252
+ }
253
+ function readSize(buf, offset) {
254
+ if (buf[offset] & 128) {
255
+ let value = buf[offset] & 127;
256
+ for (let i = 1; i < 12; i++) value = value * 256 + buf[offset + i];
257
+ return value;
258
+ }
259
+ const text = readString(buf, offset, 12).trim();
260
+ return text ? parseInt(text, 8) : 0;
261
+ }
262
+ function paxPath(data) {
263
+ let pos = 0;
264
+ while (pos < data.length) {
265
+ const space = data.indexOf(32, pos);
266
+ if (space === -1) break;
267
+ const len = parseInt(data.toString("utf8", pos, space), 10);
268
+ if (!Number.isFinite(len) || len <= 0) break;
269
+ const record = data.toString("utf8", space + 1, pos + len - 1);
270
+ const eq = record.indexOf("=");
271
+ if (eq !== -1 && record.slice(0, eq) === "path") return record.slice(eq + 1);
272
+ pos += len;
245
273
  }
246
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
274
+ return void 0;
247
275
  }
248
- async function generatePKCE() {
249
- const verifierBytes = new Uint8Array(32);
250
- crypto.getRandomValues(verifierBytes);
251
- const verifier = base64urlEncode(verifierBytes);
252
- const data = new TextEncoder().encode(verifier);
253
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
254
- const challenge = base64urlEncode(new Uint8Array(hashBuffer));
255
- return { verifier, challenge };
276
+ async function extractFileFromTarGz(tgzPath, entryName, destPath) {
277
+ const input = fs.createReadStream(tgzPath).pipe(zlib.createGunzip());
278
+ let pending = Buffer.alloc(0);
279
+ let remaining = 0;
280
+ let padding = 0;
281
+ let kind = "skip";
282
+ let meta = [];
283
+ let overridePath;
284
+ let out;
285
+ let found = false;
286
+ try {
287
+ for await (const chunk of input) {
288
+ pending = pending.length ? Buffer.concat([pending, chunk]) : chunk;
289
+ let pos = 0;
290
+ while (pos < pending.length) {
291
+ if (remaining > 0) {
292
+ const take = Math.min(remaining, pending.length - pos);
293
+ const slice = pending.subarray(pos, pos + take);
294
+ if (kind === "target") await out.write(slice);
295
+ else if (kind === "pax" || kind === "longname") meta.push(Buffer.from(slice));
296
+ pos += take;
297
+ remaining -= take;
298
+ if (remaining === 0) {
299
+ if (kind === "target") {
300
+ await out.close();
301
+ out = void 0;
302
+ found = true;
303
+ } else if (kind === "pax") {
304
+ overridePath = paxPath(Buffer.concat(meta)) ?? overridePath;
305
+ } else if (kind === "longname") {
306
+ overridePath = readString(Buffer.concat(meta), 0, Number.MAX_SAFE_INTEGER);
307
+ }
308
+ }
309
+ continue;
310
+ }
311
+ if (padding > 0) {
312
+ const skip = Math.min(padding, pending.length - pos);
313
+ pos += skip;
314
+ padding -= skip;
315
+ continue;
316
+ }
317
+ if (found) break;
318
+ if (pending.length - pos < BLOCK) break;
319
+ const header = pending.subarray(pos, pos + BLOCK);
320
+ pos += BLOCK;
321
+ if (header.every((b) => b === 0)) continue;
322
+ const size = readSize(header, 124);
323
+ const type2 = String.fromCharCode(header[156] || 48);
324
+ let name = readString(header, 0, 100);
325
+ if (readString(header, 257, 6) === "ustar") {
326
+ const prefix = readString(header, 345, 155);
327
+ if (prefix) name = `${prefix}/${name}`;
328
+ }
329
+ remaining = size;
330
+ padding = (BLOCK - size % BLOCK) % BLOCK;
331
+ meta = [];
332
+ if (type2 === "x") kind = "pax";
333
+ else if (type2 === "L") kind = "longname";
334
+ else {
335
+ const path4 = overridePath ?? name;
336
+ overridePath = void 0;
337
+ if ((type2 === "0" || type2 === "\0" || type2 === "7") && path4 === entryName) {
338
+ kind = "target";
339
+ out = await fsp.open(destPath, "w", 493);
340
+ if (size === 0) {
341
+ await out.close();
342
+ out = void 0;
343
+ found = true;
344
+ }
345
+ } else {
346
+ kind = "skip";
347
+ }
348
+ }
349
+ }
350
+ pending = pos < pending.length ? Buffer.from(pending.subarray(pos)) : Buffer.alloc(0);
351
+ if (found) break;
352
+ }
353
+ if (!found && out) throw new Error(`Archive ended inside ${entryName}`);
354
+ return found;
355
+ } finally {
356
+ if (out) await out.close().catch(() => {
357
+ });
358
+ input.destroy();
359
+ }
256
360
  }
257
361
 
258
- // src/claude-code-version.ts
259
- import {
260
- getClaudeCodeVersion,
261
- getClaudeCliUserAgent,
262
- pendingClaudeCodeVersionRefresh
263
- } from "@kenkaiiii/gg-ai";
362
+ // src/claude-code/manifest.json
363
+ var manifest_default = {
364
+ sdkVersion: "0.3.318",
365
+ claudeCodeVersion: "2.1.318",
366
+ platforms: {
367
+ "darwin-arm64": {
368
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.318.tgz",
369
+ integrity: "sha512-AWoXeDXzAMD3RaA21lWuHvCrKd8xmbTTmKjovninn4j07jheoVSMzFbCFjpp4kk5TdhT36JtWPkAxvjNMbcO8w==",
370
+ bin: "claude"
371
+ },
372
+ "darwin-x64": {
373
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.318.tgz",
374
+ integrity: "sha512-6CkZ5PCdebSY/LePp16sf/HYRtM6zVuxtn8qYICFS5KUZFsjccJJ9swVsOdHXNHi4SWZcSmIFTwoUbfFCzisRw==",
375
+ bin: "claude"
376
+ },
377
+ "linux-arm64": {
378
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.318.tgz",
379
+ integrity: "sha512-mBm4su17aMitLBBCQj3bHdejyUDY6z/ormGih+HG24N689RSbdKECjGjkT87tDBIR/4jyoqmuSeHqM1DI8TJlg==",
380
+ bin: "claude"
381
+ },
382
+ "linux-arm64-musl": {
383
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.318.tgz",
384
+ integrity: "sha512-l4LSYFOrvlYCrUImGfxMTQ0wg+T3s9CBhrBvPF/0oJGKZg+QnxHHU8jUVm9e9/xYmDRTtqFIrr4id/5AfH4XGw==",
385
+ bin: "claude"
386
+ },
387
+ "linux-x64": {
388
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.318.tgz",
389
+ integrity: "sha512-+mE+ueBD3oRFdaG16rd0h+LMjdeivGxL0pr/+FZK7JeLWVu+yPSw/f/X02b9oK36/DWykiFTu2G6HnJ9/+YO6w==",
390
+ bin: "claude"
391
+ },
392
+ "linux-x64-musl": {
393
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.318.tgz",
394
+ integrity: "sha512-SAF3lj/2OzTU84p+OOA13QEKA9+5fYAw0M9E7AJjdnRmVFGqQfbV7Do1i1f9Sj0mIubB3rLTRphGRw8O4HZjgA==",
395
+ bin: "claude"
396
+ },
397
+ "win32-arm64": {
398
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.318.tgz",
399
+ integrity: "sha512-19iVSZ4379QbZU1XnGUtfFvhKS1WzS8H1sbU/DbrzqbJ5Kq1eB1drj+obB1fCxTh6m26tRPj0zMJmdeu/jk3Gw==",
400
+ bin: "claude.exe"
401
+ },
402
+ "win32-x64": {
403
+ url: "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.318.tgz",
404
+ integrity: "sha512-ZdKVWpyVNVt0cXrOf8sUBaxcxLEAP+oGMdQDLBWr/BqOxTWLy7TBWOj1kzw+wVg/9WIkjkgQWeJzGXPWulR1sA==",
405
+ bin: "claude.exe"
406
+ }
407
+ }
408
+ };
264
409
 
265
- // src/oauth/anthropic.ts
266
- var CLIENT_ID2 = atob("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
267
- var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
268
- var TOKEN_URLS = [
269
- "https://platform.claude.com/v1/oauth/token",
270
- "https://console.anthropic.com/v1/oauth/token"
271
- ];
272
- var REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
273
- var SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
274
- async function postTokenRequest(body, label) {
275
- const encoded = JSON.stringify(body);
276
- const headers = {
277
- "Content-Type": "application/json",
278
- "User-Agent": await getClaudeCliUserAgent(),
279
- "anthropic-beta": "oauth-2025-04-20"
280
- };
281
- let lastError = null;
282
- for (const url of TOKEN_URLS) {
283
- let response;
410
+ // src/claude-code/runtime.ts
411
+ var CLAUDE_CODE_MANIFEST = manifest_default;
412
+ var CLAUDE_CODE_AUTH_KEY = "anthropic-claude-code";
413
+ var CLAUDE_CODE_AUTH_MARKER = { via: "claude-code" };
414
+ function isClaudeCodeAuthMarker(value) {
415
+ return value?.via === CLAUDE_CODE_AUTH_MARKER.via;
416
+ }
417
+ function isLegacyClaudeOAuth(value) {
418
+ const token = value?.accessToken;
419
+ return typeof token === "string" && token.startsWith("sk-ant-oat");
420
+ }
421
+ function isMusl() {
422
+ if (process.platform !== "linux") return false;
423
+ const report = typeof process.report?.getReport === "function" ? process.report.getReport() : null;
424
+ return report != null && report.header?.glibcVersionRuntime === void 0;
425
+ }
426
+ function claudeCodePlatformKey(info = { platform: process.platform, arch: process.arch, musl: isMusl() }) {
427
+ const arch2 = info.arch === "arm64" || info.arch === "x64" ? info.arch : null;
428
+ if (!arch2) return null;
429
+ if (info.platform === "darwin" || info.platform === "win32") return `${info.platform}-${arch2}`;
430
+ if (info.platform === "linux") return `linux-${arch2}${info.musl ? "-musl" : ""}`;
431
+ return null;
432
+ }
433
+ function resolveAsset(opts) {
434
+ const manifest = opts.manifest ?? CLAUDE_CODE_MANIFEST;
435
+ const key = opts.platformKey ?? claudeCodePlatformKey();
436
+ const asset = key ? manifest.platforms[key] : void 0;
437
+ if (!asset) {
438
+ throw new Error(
439
+ `Claude Code isn't available for ${process.platform}-${process.arch}. Use an Anthropic API key instead.`
440
+ );
441
+ }
442
+ return { manifest, asset };
443
+ }
444
+ function claudeCodeHome(opts = {}) {
445
+ return opts.home ?? getAppPaths().claudeCodeDir;
446
+ }
447
+ function claudeCodeConfigDir(opts = {}) {
448
+ return opts.home ? path2.join(opts.home, "config") : getAppPaths().claudeCodeConfigDir;
449
+ }
450
+ function claudeCodeBinaryPath(opts = {}) {
451
+ const { manifest, asset } = resolveAsset(opts);
452
+ return path2.join(claudeCodeHome(opts), manifest.claudeCodeVersion, asset.bin);
453
+ }
454
+ function resolveClaudeCodeBinary(opts = {}) {
455
+ let binary;
456
+ try {
457
+ binary = claudeCodeBinaryPath(opts);
458
+ } catch {
459
+ return null;
460
+ }
461
+ try {
462
+ return fs2.statSync(binary).isFile() ? binary : null;
463
+ } catch {
464
+ return null;
465
+ }
466
+ }
467
+ var installs = /* @__PURE__ */ new Map();
468
+ async function ensureClaudeCodeBinary(opts = {}) {
469
+ const existing = resolveClaudeCodeBinary(opts);
470
+ if (existing) return existing;
471
+ const target = claudeCodeBinaryPath(opts);
472
+ let install = installs.get(target);
473
+ if (!install) {
474
+ install = installClaudeCode(opts).finally(() => installs.delete(target));
475
+ installs.set(target, install);
476
+ }
477
+ return install;
478
+ }
479
+ async function installClaudeCode(opts) {
480
+ const { manifest, asset } = resolveAsset(opts);
481
+ const home = claudeCodeHome(opts);
482
+ const versionDir = path2.join(home, manifest.claudeCodeVersion);
483
+ const binary = path2.join(versionDir, asset.bin);
484
+ await fsp2.mkdir(home, { recursive: true });
485
+ const tag = `${process.pid}-${crypto2.randomBytes(4).toString("hex")}`;
486
+ const tarball = path2.join(home, `.download-${tag}.tgz`);
487
+ const staging = path2.join(home, `.staging-${tag}`);
488
+ try {
489
+ await download(asset, tarball, opts);
490
+ opts.onProgress?.({ phase: "extract" });
491
+ await fsp2.mkdir(staging, { recursive: true });
492
+ const found = await extractFileFromTarGz(
493
+ tarball,
494
+ `package/${asset.bin}`,
495
+ path2.join(staging, asset.bin)
496
+ );
497
+ if (!found) throw new Error(`The Claude Code download has no ${asset.bin} binary.`);
498
+ await fsp2.chmod(path2.join(staging, asset.bin), 493);
284
499
  try {
285
- response = await fetch(url, {
286
- method: "POST",
287
- headers,
288
- body: encoded,
289
- signal: oauthRequestSignal()
290
- });
500
+ await fsp2.rename(staging, versionDir);
291
501
  } catch (err) {
292
- lastError = err instanceof Error ? err : new Error(String(err));
293
- continue;
294
- }
295
- if (response.ok) {
296
- return await response.json();
502
+ if (!fs2.existsSync(binary)) throw err;
297
503
  }
298
- const text = await response.text();
299
- if (response.status >= 400 && response.status < 500) {
300
- throw new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
504
+ await removeOldVersions(home, manifest.claudeCodeVersion);
505
+ return binary;
506
+ } finally {
507
+ await fsp2.rm(tarball, { force: true }).catch(() => {
508
+ });
509
+ await fsp2.rm(staging, { recursive: true, force: true }).catch(() => {
510
+ });
511
+ }
512
+ }
513
+ async function download(asset, dest, opts) {
514
+ const doFetch = opts.fetch ?? fetch;
515
+ const res = await doFetch(asset.url, { signal: opts.signal });
516
+ if (!res.ok || !res.body) {
517
+ throw new Error(`Claude Code download failed (HTTP ${res.status}).`);
518
+ }
519
+ const totalBytes = Number(res.headers.get("content-length")) || void 0;
520
+ const hash = crypto2.createHash("sha512");
521
+ const out = await fsp2.open(dest, "w");
522
+ let receivedBytes = 0;
523
+ let lastReported = -1;
524
+ try {
525
+ opts.onProgress?.({ phase: "download", receivedBytes, totalBytes });
526
+ for await (const chunk of res.body) {
527
+ if (opts.signal?.aborted) throw opts.signal.reason ?? new Error("Download cancelled.");
528
+ hash.update(chunk);
529
+ await out.write(chunk);
530
+ receivedBytes += chunk.byteLength;
531
+ const step = totalBytes ? Math.floor(receivedBytes / totalBytes * 100) : Math.floor(receivedBytes / 4194304);
532
+ if (step !== lastReported) {
533
+ lastReported = step;
534
+ opts.onProgress?.({ phase: "download", receivedBytes, totalBytes });
535
+ }
301
536
  }
302
- lastError = new Error(`Anthropic ${label} failed (${response.status}): ${text}`);
537
+ } finally {
538
+ await out.close();
539
+ }
540
+ opts.onProgress?.({ phase: "verify" });
541
+ const expected = asset.integrity.replace(/^sha512-/, "");
542
+ if (hash.digest("base64") !== expected) {
543
+ throw new Error(
544
+ "The Claude Code download didn't match its pinned checksum, so it was discarded."
545
+ );
303
546
  }
304
- throw lastError ?? new Error(`Anthropic ${label} failed: all endpoints unreachable`);
305
547
  }
306
- function toCredentials(data) {
307
- return {
308
- accessToken: data.access_token,
309
- refreshToken: data.refresh_token,
310
- expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
311
- };
548
+ async function removeOldVersions(home, keep) {
549
+ let entries;
550
+ try {
551
+ entries = await fsp2.readdir(home, { withFileTypes: true });
552
+ } catch {
553
+ return;
554
+ }
555
+ for (const entry of entries) {
556
+ if (!entry.isDirectory() || entry.name === keep || entry.name === "config") continue;
557
+ if (!/^\d+\.\d+\.\d+/.test(entry.name)) continue;
558
+ await fsp2.rm(path2.join(home, entry.name), { recursive: true, force: true }).catch(() => {
559
+ });
560
+ }
312
561
  }
313
- async function loginAnthropic(callbacks) {
314
- const { verifier, challenge } = await generatePKCE();
315
- const state = crypto2.randomBytes(16).toString("hex");
316
- const params = new URLSearchParams({
317
- code: "true",
318
- client_id: CLIENT_ID2,
319
- response_type: "code",
320
- redirect_uri: REDIRECT_URI,
321
- scope: SCOPES,
322
- code_challenge: challenge,
323
- code_challenge_method: "S256",
324
- state
562
+ function formatClaudeCodeInstallProgress(progress) {
563
+ if (progress.phase === "verify") return "Checking the Claude Code download\u2026";
564
+ if (progress.phase === "extract") return "Unpacking Claude Code\u2026";
565
+ const mb = (bytes) => Math.round(bytes / 1048576);
566
+ if (!progress.totalBytes) return `Downloading Claude Code\u2026 ${mb(progress.receivedBytes)} MB`;
567
+ const pct = Math.floor(progress.receivedBytes / progress.totalBytes * 100);
568
+ return `Downloading Claude Code\u2026 ${pct}% (${mb(progress.receivedBytes)} of ${mb(progress.totalBytes)} MB)`;
569
+ }
570
+ var STRIPPED_ENV = [
571
+ "ANTHROPIC_API_KEY",
572
+ "ANTHROPIC_AUTH_TOKEN",
573
+ "ANTHROPIC_BASE_URL",
574
+ "CLAUDE_CODE_OAUTH_TOKEN",
575
+ "CLAUDE_CODE_OAUTH_REFRESH_TOKEN",
576
+ "CLAUDE_CODE_USE_BEDROCK",
577
+ "CLAUDE_CODE_USE_VERTEX",
578
+ "CLAUDE_CODE_USE_FOUNDRY",
579
+ "NODE_OPTIONS"
580
+ ];
581
+ function claudeCodeEnv(opts = {}, base = process.env) {
582
+ const env = {};
583
+ for (const [key, value] of Object.entries(base)) {
584
+ if (value !== void 0 && !STRIPPED_ENV.includes(key)) env[key] = value;
585
+ }
586
+ env.CLAUDE_CONFIG_DIR = claudeCodeConfigDir(opts);
587
+ env.DISABLE_AUTOUPDATER = "1";
588
+ return env;
589
+ }
590
+ function runCommand(opts) {
591
+ if (opts.binary) return { command: opts.binary, prefix: opts.binaryArgs ?? [] };
592
+ const binary = resolveClaudeCodeBinary(opts);
593
+ if (!binary) throw new Error("Claude Code isn't installed yet. Sign in with Claude first.");
594
+ return { command: binary, prefix: [] };
595
+ }
596
+ function runClaude(args, opts) {
597
+ const { command, prefix } = runCommand(opts);
598
+ return new Promise((resolve, reject) => {
599
+ const child = spawn(command, [...prefix, ...args], {
600
+ env: claudeCodeEnv(opts),
601
+ stdio: ["ignore", "pipe", "pipe"],
602
+ windowsHide: true,
603
+ signal: opts.signal
604
+ });
605
+ let stdout = "";
606
+ let stderr = "";
607
+ child.stdout.on("data", (d) => stdout += d.toString());
608
+ child.stderr.on("data", (d) => stderr += d.toString());
609
+ child.on("error", reject);
610
+ child.on("close", (code) => resolve({ code, stdout, stderr }));
325
611
  });
326
- const authUrl = `${AUTHORIZE_URL}?${params}`;
327
- callbacks.onOpenUrl(authUrl);
328
- const raw = await callbacks.onPromptCode("Paste the code from the browser (format: code#state):");
329
- const parts = raw.trim().split("#");
330
- if (parts.length !== 2 || !parts[0] || parts[1] !== state) {
331
- throw new Error("Invalid code or state mismatch. Please try again.");
612
+ }
613
+ async function claudeCodeAuthStatus(opts = {}) {
614
+ if (!opts.binary && !resolveClaudeCodeBinary(opts)) return { loggedIn: false };
615
+ const { stdout } = await runClaude(["auth", "status", "--json"], opts);
616
+ try {
617
+ const parsed = JSON.parse(stdout);
618
+ return {
619
+ loggedIn: parsed.loggedIn === true,
620
+ ...typeof parsed.authMethod === "string" ? { authMethod: parsed.authMethod } : {},
621
+ ...typeof parsed.email === "string" ? { email: parsed.email } : {},
622
+ ...typeof parsed.subscriptionType === "string" ? { subscriptionType: parsed.subscriptionType } : {}
623
+ };
624
+ } catch {
625
+ return { loggedIn: false };
332
626
  }
333
- return exchangeAnthropicCode(parts[0], parts[1], verifier);
334
627
  }
335
- async function exchangeAnthropicCode(code, state, verifier) {
336
- const data = await postTokenRequest(
337
- {
338
- grant_type: "authorization_code",
339
- client_id: CLIENT_ID2,
340
- code,
341
- state,
342
- redirect_uri: REDIRECT_URI,
343
- code_verifier: verifier
344
- },
345
- "token exchange"
346
- );
347
- return toCredentials(data);
628
+ async function logoutClaudeCode(opts = {}) {
629
+ if (!opts.binary && !resolveClaudeCodeBinary(opts)) return;
630
+ const { code, stderr } = await runClaude(["auth", "logout"], opts);
631
+ if (code !== 0) throw new Error(stderr.trim() || `claude auth logout exited with ${code}`);
348
632
  }
349
- async function refreshAnthropicToken(refreshToken) {
350
- const data = await postTokenRequest(
351
- {
352
- grant_type: "refresh_token",
353
- client_id: CLIENT_ID2,
354
- refresh_token: refreshToken
355
- },
356
- "token refresh"
357
- );
358
- return toCredentials(data);
633
+ var OPEN_URL_HOOK = `const arg = String(process.argv[1] || "");
634
+ const file = process.env.GG_CLAUDE_CODE_URL_FILE;
635
+ if (file && /https?:/.test(arg)) {
636
+ require("node:fs").appendFileSync(file, arg + "\\n");
637
+ process.exit(0);
638
+ }
639
+ `;
640
+ var CAPTURE_WAIT_MS = 3e3;
641
+ function stripAnsi(text) {
642
+ return text.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[A-Za-z]/g, "");
643
+ }
644
+ function claudeCodeCallbackUrl(manualUrl, captured) {
645
+ const port = /localhost(?::|%3A)(\d{2,5})(?:\/|%2F)callback/i.exec(captured)?.[1];
646
+ if (!port) return null;
647
+ try {
648
+ const url = new URL(manualUrl);
649
+ url.searchParams.set("redirect_uri", `http://localhost:${port}/callback`);
650
+ return url.toString();
651
+ } catch {
652
+ return null;
653
+ }
654
+ }
655
+ async function loginClaudeCode(callbacks, opts = {}) {
656
+ const { command, prefix } = runCommand(opts);
657
+ const home = claudeCodeHome(opts);
658
+ await fsp2.mkdir(claudeCodeConfigDir(opts), { recursive: true });
659
+ const tag = `${process.pid}-${crypto2.randomBytes(4).toString("hex")}`;
660
+ const hookFile = path2.join(home, `.open-url-${tag}.cjs`);
661
+ const urlFile = path2.join(home, `.open-url-${tag}.txt`);
662
+ await fsp2.writeFile(hookFile, OPEN_URL_HOOK);
663
+ const env = {
664
+ ...claudeCodeEnv(opts),
665
+ BROWSER: process.execPath,
666
+ NODE_OPTIONS: `--require ${JSON.stringify(hookFile)}`,
667
+ GG_CLAUDE_CODE_URL_FILE: urlFile
668
+ };
669
+ const promptAbort = new AbortController();
670
+ let timer;
671
+ try {
672
+ await new Promise((resolve, reject) => {
673
+ const child = spawn(command, [...prefix, "auth", "login", "--claudeai"], {
674
+ env,
675
+ stdio: ["pipe", "pipe", "pipe"],
676
+ windowsHide: true
677
+ });
678
+ let stdout = "";
679
+ let stderr = "";
680
+ let manualUrl = null;
681
+ let opened = false;
682
+ const onAbort = () => child.kill();
683
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
684
+ const open = (url, needsCode) => {
685
+ if (opened) return;
686
+ opened = true;
687
+ clearInterval(timer);
688
+ callbacks.onOpenUrl(url);
689
+ callbacks.onStatus("Finish signing in to Claude in your browser\u2026");
690
+ if (!needsCode) return;
691
+ void callbacks.onPromptCode("Paste the code shown after signing in to Claude:", promptAbort.signal).then((code) => {
692
+ const trimmed = code.trim();
693
+ if (trimmed && !child.stdin.destroyed) child.stdin.write(`${trimmed}
694
+ `);
695
+ }).catch(() => {
696
+ });
697
+ };
698
+ child.stdout.on("data", (d) => {
699
+ stdout += d.toString();
700
+ if (manualUrl) return;
701
+ const match = /visit:\s*(https:\/\/\S+)/.exec(stripAnsi(stdout));
702
+ if (!match) return;
703
+ manualUrl = match[1];
704
+ const started = Date.now();
705
+ timer = setInterval(() => {
706
+ let captured = "";
707
+ try {
708
+ captured = fs2.readFileSync(urlFile, "utf8");
709
+ } catch {
710
+ }
711
+ const callbackUrl = captured ? claudeCodeCallbackUrl(manualUrl, captured) : null;
712
+ if (callbackUrl) open(callbackUrl, false);
713
+ else if (Date.now() - started > (opts.browserWaitMs ?? CAPTURE_WAIT_MS))
714
+ open(manualUrl, true);
715
+ }, 100);
716
+ });
717
+ child.stderr.on("data", (d) => stderr += d.toString());
718
+ child.on("error", reject);
719
+ child.on("close", (code) => {
720
+ opts.signal?.removeEventListener("abort", onAbort);
721
+ if (opts.signal?.aborted) reject(new Error("Sign-in cancelled."));
722
+ else if (code === 0) resolve();
723
+ else {
724
+ const detail = stripAnsi(stderr).trim().split("\n").pop()?.trim();
725
+ reject(new Error(detail || `Claude sign-in failed (exit ${code}).`));
726
+ }
727
+ });
728
+ });
729
+ } finally {
730
+ clearInterval(timer);
731
+ promptAbort.abort();
732
+ await fsp2.rm(hookFile, { force: true }).catch(() => {
733
+ });
734
+ await fsp2.rm(urlFile, { force: true }).catch(() => {
735
+ });
736
+ }
359
737
  }
360
738
 
361
739
  // src/oauth/openai.ts
362
740
  import http from "http";
363
741
  import crypto3 from "crypto";
364
- var CLIENT_ID3 = "app_EMoamEEZ73f0CkXaXp7hrann";
365
- var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
742
+
743
+ // src/oauth/pkce.ts
744
+ function base64urlEncode(bytes) {
745
+ let binary = "";
746
+ for (const byte of bytes) {
747
+ binary += String.fromCharCode(byte);
748
+ }
749
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
750
+ }
751
+ async function generatePKCE() {
752
+ const verifierBytes = new Uint8Array(32);
753
+ crypto.getRandomValues(verifierBytes);
754
+ const verifier = base64urlEncode(verifierBytes);
755
+ const data = new TextEncoder().encode(verifier);
756
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
757
+ const challenge = base64urlEncode(new Uint8Array(hashBuffer));
758
+ return { verifier, challenge };
759
+ }
760
+
761
+ // src/oauth/openai.ts
762
+ var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
763
+ var AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
366
764
  var TOKEN_URL = "https://auth.openai.com/oauth/token";
367
- var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
765
+ var REDIRECT_URI = "http://localhost:1455/auth/callback";
368
766
  var SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
369
767
  var JWT_CLAIM_PATH = "https://api.openai.com/auth";
370
768
  var CALLBACK_TIMEOUT_MS = 12e4;
@@ -373,10 +771,10 @@ var MIN_RAW_CODE_LENGTH = 10;
373
771
  async function loginOpenAI(callbacks) {
374
772
  const { verifier, challenge } = await generatePKCE();
375
773
  const state = crypto3.randomBytes(16).toString("hex");
376
- const url = new URL(AUTHORIZE_URL2);
774
+ const url = new URL(AUTHORIZE_URL);
377
775
  url.searchParams.set("response_type", "code");
378
- url.searchParams.set("client_id", CLIENT_ID3);
379
- url.searchParams.set("redirect_uri", REDIRECT_URI2);
776
+ url.searchParams.set("client_id", CLIENT_ID2);
777
+ url.searchParams.set("redirect_uri", REDIRECT_URI);
380
778
  url.searchParams.set("scope", SCOPE);
381
779
  url.searchParams.set("code_challenge", challenge);
382
780
  url.searchParams.set("code_challenge_method", "S256");
@@ -532,9 +930,9 @@ async function exchangeOpenAICode(code, verifier) {
532
930
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
533
931
  body: new URLSearchParams({
534
932
  grant_type: "authorization_code",
535
- client_id: CLIENT_ID3,
933
+ client_id: CLIENT_ID2,
536
934
  code,
537
- redirect_uri: REDIRECT_URI2,
935
+ redirect_uri: REDIRECT_URI,
538
936
  code_verifier: verifier
539
937
  }),
540
938
  signal: oauthRequestSignal()
@@ -557,7 +955,7 @@ async function refreshOpenAIToken(refreshToken) {
557
955
  body: new URLSearchParams({
558
956
  grant_type: "refresh_token",
559
957
  refresh_token: refreshToken,
560
- client_id: CLIENT_ID3
958
+ client_id: CLIENT_ID2
561
959
  }),
562
960
  signal: oauthRequestSignal()
563
961
  });
@@ -585,7 +983,7 @@ var CLIENT_ID_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_ID";
585
983
  var CLIENT_SECRET_ENV = "GGCODER_GEMINI_OAUTH_CLIENT_SECRET";
586
984
  var DEFAULT_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
587
985
  var DEFAULT_CLIENT_SECRET = "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";
588
- var AUTHORIZE_URL3 = "https://accounts.google.com/o/oauth2/v2/auth";
986
+ var AUTHORIZE_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
589
987
  var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
590
988
  var CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
591
989
  var CODE_ASSIST_API_VERSION = "v1internal";
@@ -616,7 +1014,7 @@ async function loginGemini(callbacks) {
616
1014
  const { verifier, challenge } = await generatePKCE();
617
1015
  const state = crypto4.randomBytes(32).toString("hex");
618
1016
  const redirectUri = await getLoopbackRedirectUri();
619
- const url = new URL(AUTHORIZE_URL3);
1017
+ const url = new URL(AUTHORIZE_URL2);
620
1018
  url.searchParams.set("response_type", "code");
621
1019
  url.searchParams.set("client_id", clientId2);
622
1020
  url.searchParams.set("redirect_uri", redirectUri);
@@ -653,7 +1051,7 @@ async function loginGemini(callbacks) {
653
1051
  }
654
1052
  async function refreshGeminiToken(refreshToken) {
655
1053
  const { clientId: clientId2, clientSecret } = getGeminiOAuthClientCredentials();
656
- const data = await postTokenRequest2({
1054
+ const data = await postTokenRequest({
657
1055
  grant_type: "refresh_token",
658
1056
  refresh_token: refreshToken,
659
1057
  client_id: clientId2,
@@ -748,7 +1146,7 @@ async function loginWithServer(authUrl, redirectUri, expectedState, callbacks) {
748
1146
  });
749
1147
  }
750
1148
  async function exchangeGeminiCode(code, verifier, redirectUri, clientId2, clientSecret) {
751
- const data = await postTokenRequest2({
1149
+ const data = await postTokenRequest({
752
1150
  grant_type: "authorization_code",
753
1151
  client_id: clientId2,
754
1152
  client_secret: clientSecret,
@@ -765,7 +1163,7 @@ async function exchangeGeminiCode(code, verifier, redirectUri, clientId2, client
765
1163
  expiresAt: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
766
1164
  };
767
1165
  }
768
- async function postTokenRequest2(body) {
1166
+ async function postTokenRequest(body) {
769
1167
  const response = await fetch(TOKEN_URL2, {
770
1168
  method: "POST",
771
1169
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
@@ -1143,7 +1541,7 @@ async function refreshXaiToken(refreshToken) {
1143
1541
  }
1144
1542
 
1145
1543
  // src/file-lock.ts
1146
- import fs from "fs/promises";
1544
+ import fs3 from "fs/promises";
1147
1545
  import { setTimeout as setTimeout2 } from "timers/promises";
1148
1546
  var STALE_TIMEOUT_MS = 1e4;
1149
1547
  var RETRY_INTERVAL_MS = 50;
@@ -1164,7 +1562,7 @@ async function acquireLock(lockPath) {
1164
1562
  while (true) {
1165
1563
  try {
1166
1564
  const info = { pid: process.pid, timestamp: Date.now() };
1167
- await fs.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
1565
+ await fs3.writeFile(lockPath, JSON.stringify(info), { flag: "wx" });
1168
1566
  return;
1169
1567
  } catch (err) {
1170
1568
  const code = err.code;
@@ -1177,26 +1575,26 @@ async function acquireLock(lockPath) {
1177
1575
  continue;
1178
1576
  }
1179
1577
  try {
1180
- const content = await fs.readFile(lockPath, "utf-8");
1578
+ const content = await fs3.readFile(lockPath, "utf-8");
1181
1579
  const info = JSON.parse(content);
1182
1580
  const isProcessAlive = isAlive(info.pid);
1183
1581
  const isStale = Date.now() - info.timestamp > STALE_TIMEOUT_MS;
1184
1582
  if (!isProcessAlive || isStale) {
1185
- await fs.unlink(lockPath).catch(() => {
1583
+ await fs3.unlink(lockPath).catch(() => {
1186
1584
  });
1187
1585
  continue;
1188
1586
  }
1189
1587
  } catch {
1190
- const stat = await fs.stat(lockPath).catch(() => null);
1588
+ const stat = await fs3.stat(lockPath).catch(() => null);
1191
1589
  if (!stat) continue;
1192
1590
  if (Date.now() - stat.mtimeMs > STALE_TIMEOUT_MS) {
1193
- await fs.unlink(lockPath).catch(() => {
1591
+ await fs3.unlink(lockPath).catch(() => {
1194
1592
  });
1195
1593
  continue;
1196
1594
  }
1197
1595
  }
1198
1596
  if (Date.now() - startTime > MAX_WAIT_MS) {
1199
- await fs.unlink(lockPath).catch(() => {
1597
+ await fs3.unlink(lockPath).catch(() => {
1200
1598
  });
1201
1599
  continue;
1202
1600
  }
@@ -1206,14 +1604,14 @@ async function acquireLock(lockPath) {
1206
1604
  }
1207
1605
  async function lockFileExists(lockPath) {
1208
1606
  try {
1209
- await fs.stat(lockPath);
1607
+ await fs3.stat(lockPath);
1210
1608
  return true;
1211
1609
  } catch (err) {
1212
1610
  return err.code !== "ENOENT";
1213
1611
  }
1214
1612
  }
1215
1613
  async function releaseLock(lockPath) {
1216
- await fs.unlink(lockPath).catch(() => {
1614
+ await fs3.unlink(lockPath).catch(() => {
1217
1615
  });
1218
1616
  }
1219
1617
  function isAlive(pid) {
@@ -1227,8 +1625,8 @@ function isAlive(pid) {
1227
1625
  }
1228
1626
 
1229
1627
  // src/logger.ts
1230
- import fs2 from "fs";
1231
- import path2 from "path";
1628
+ import fs4 from "fs";
1629
+ import path3 from "path";
1232
1630
  import { randomBytes } from "crypto";
1233
1631
  import { environmentSecrets, redactText, redactValue } from "@kenkaiiii/gg-ai";
1234
1632
  var MAX_BYTES = 10 * 1024 * 1024;
@@ -1245,14 +1643,14 @@ var cleanups = [];
1245
1643
  var exactSecrets = [];
1246
1644
  function rotateIfNeeded(filePath, force = false) {
1247
1645
  try {
1248
- const st = fs2.statSync(filePath);
1646
+ const st = fs4.statSync(filePath);
1249
1647
  if (!force && st.size <= MAX_BYTES - MIN_HEADROOM_BYTES) return;
1250
1648
  const rotated = `${filePath}.1`;
1251
1649
  try {
1252
- fs2.unlinkSync(rotated);
1650
+ fs4.unlinkSync(rotated);
1253
1651
  } catch {
1254
1652
  }
1255
- fs2.renameSync(filePath, rotated);
1653
+ fs4.renameSync(filePath, rotated);
1256
1654
  } catch {
1257
1655
  }
1258
1656
  }
@@ -1260,18 +1658,18 @@ function rotateOpenLog() {
1260
1658
  if (fd === null || logPath === null) return false;
1261
1659
  const sizeBefore = fileBytes;
1262
1660
  try {
1263
- fs2.closeSync(fd);
1661
+ fs4.closeSync(fd);
1264
1662
  } catch {
1265
1663
  }
1266
1664
  fd = null;
1267
1665
  rotateIfNeeded(logPath, true);
1268
1666
  try {
1269
- fd = fs2.openSync(logPath, "a");
1270
- fileBytes = fs2.fstatSync(fd).size;
1667
+ fd = fs4.openSync(logPath, "a");
1668
+ fileBytes = fs4.fstatSync(fd).size;
1271
1669
  } catch {
1272
1670
  if (fd !== null) {
1273
1671
  try {
1274
- fs2.closeSync(fd);
1672
+ fs4.closeSync(fd);
1275
1673
  } catch {
1276
1674
  }
1277
1675
  }
@@ -1287,22 +1685,22 @@ function openLog(filePath, name) {
1287
1685
  appName = name;
1288
1686
  exactSecrets = environmentSecrets(process.env);
1289
1687
  try {
1290
- fs2.mkdirSync(path2.dirname(filePath), { recursive: true, mode: 448 });
1688
+ fs4.mkdirSync(path3.dirname(filePath), { recursive: true, mode: 448 });
1291
1689
  } catch {
1292
1690
  }
1293
1691
  rotateIfNeeded(filePath);
1294
1692
  try {
1295
- fd = fs2.openSync(filePath, "a");
1693
+ fd = fs4.openSync(filePath, "a");
1296
1694
  } catch {
1297
1695
  fd = null;
1298
1696
  fileBytes = 0;
1299
1697
  return false;
1300
1698
  }
1301
1699
  try {
1302
- fileBytes = fs2.fstatSync(fd).size;
1700
+ fileBytes = fs4.fstatSync(fd).size;
1303
1701
  } catch {
1304
1702
  try {
1305
- fs2.closeSync(fd);
1703
+ fs4.closeSync(fd);
1306
1704
  } catch {
1307
1705
  }
1308
1706
  fd = null;
@@ -1315,7 +1713,7 @@ function openLog(filePath, name) {
1315
1713
  sessionId = randomBytes(4).toString("hex");
1316
1714
  try {
1317
1715
  if (fileBytes < MAX_BYTES) {
1318
- const written = fs2.writeSync(fd, "\n");
1716
+ const written = fs4.writeSync(fd, "\n");
1319
1717
  fileBytes += written;
1320
1718
  processBytes += written;
1321
1719
  }
@@ -1350,9 +1748,9 @@ function log(level, category, message, data) {
1350
1748
  `;
1351
1749
  try {
1352
1750
  if (fileBytes + Buffer.byteLength(capLine) <= MAX_BYTES) {
1353
- fileBytes += fs2.writeSync(fd, capLine);
1751
+ fileBytes += fs4.writeSync(fd, capLine);
1354
1752
  }
1355
- fs2.closeSync(fd);
1753
+ fs4.closeSync(fd);
1356
1754
  } catch {
1357
1755
  }
1358
1756
  fd = null;
@@ -1362,7 +1760,7 @@ function log(level, category, message, data) {
1362
1760
  if (fileBytes + lineBytes > MAX_BYTES && !rotationFailed && !rotateOpenLog()) return;
1363
1761
  if (fd === null) return;
1364
1762
  try {
1365
- const written = fs2.writeSync(fd, line);
1763
+ const written = fs4.writeSync(fd, line);
1366
1764
  fileBytes += written;
1367
1765
  processBytes += written;
1368
1766
  } catch {
@@ -1375,7 +1773,7 @@ function closeLogger(opts) {
1375
1773
  if (fd !== null) {
1376
1774
  if (opts?.shutdownLine !== false) log("INFO", "shutdown", `${appName} shutting down`);
1377
1775
  try {
1378
- if (fd !== null) fs2.closeSync(fd);
1776
+ if (fd !== null) fs4.closeSync(fd);
1379
1777
  } catch {
1380
1778
  }
1381
1779
  }
@@ -1556,6 +1954,8 @@ function refreshThresholdMs(creds) {
1556
1954
  }
1557
1955
  var USAGE_EXHAUSTED_DEFAULT_MS = 15 * 60 * 1e3;
1558
1956
  var STATIC_API_KEY_PROVIDERS = /* @__PURE__ */ new Set([
1957
+ // Anthropic is API-key only; Claude subscriptions run through Claude Code.
1958
+ "anthropic",
1559
1959
  "glm",
1560
1960
  "moonshot",
1561
1961
  "xiaomi",
@@ -1636,11 +2036,44 @@ var AuthStorage = class {
1636
2036
  if (provider === "gemini") {
1637
2037
  return Boolean(this.data["gemini"] || this.data[GEMINI_API_KEY]);
1638
2038
  }
2039
+ if (provider === "anthropic") {
2040
+ return Boolean(this.data.anthropic) && !isLegacyClaudeOAuth(this.data.anthropic) || isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY]);
2041
+ }
1639
2042
  if (provider === "local") {
1640
2043
  return Object.keys(this.data).some((key) => key.startsWith(LOCAL_AUTH_KEY_PREFIX));
1641
2044
  }
1642
2045
  return Boolean(this.data[provider]);
1643
2046
  }
2047
+ /** True when the user signed in with Claude through Claude Code (marker only, no token). */
2048
+ async hasClaudeCodeLogin() {
2049
+ await this.ensureFresh();
2050
+ return isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY]);
2051
+ }
2052
+ /** Record a Claude Code sign-in. Claude Code keeps the token; auth.json gets only the marker. */
2053
+ async setClaudeCodeLogin() {
2054
+ await this.mutateLatest((latest) => {
2055
+ latest[CLAUDE_CODE_AUTH_KEY] = { ...CLAUDE_CODE_AUTH_MARKER };
2056
+ if (isLegacyClaudeOAuth(latest.anthropic)) delete latest.anthropic;
2057
+ });
2058
+ }
2059
+ /**
2060
+ * Remove an old GG Claude sign-in token (`sk-ant-oat…`), if any. True when one
2061
+ * was removed, so the caller can tell the user once to sign in with Claude again.
2062
+ */
2063
+ async takeLegacyClaudeSignIn() {
2064
+ await this.ensureFresh();
2065
+ if (!isLegacyClaudeOAuth(this.data.anthropic)) return false;
2066
+ let removed = false;
2067
+ await this.mutateLatest((latest) => {
2068
+ if (!isLegacyClaudeOAuth(latest.anthropic)) return;
2069
+ delete latest.anthropic;
2070
+ removed = true;
2071
+ });
2072
+ return removed;
2073
+ }
2074
+ async clearClaudeCodeLogin() {
2075
+ await this.clearCredentials(CLAUDE_CODE_AUTH_KEY);
2076
+ }
1644
2077
  /** Endpoint ids that currently have a `local:<id>` credential stored. */
1645
2078
  async listLocalEndpointIds() {
1646
2079
  await this.ensureLoaded();
@@ -1694,7 +2127,7 @@ var AuthStorage = class {
1694
2127
  const first = !this.loaded;
1695
2128
  await withFileLock(this.filePath, async () => {
1696
2129
  try {
1697
- const content = await fs3.readFile(this.filePath, "utf-8");
2130
+ const content = await fs5.readFile(this.filePath, "utf-8");
1698
2131
  this.data = JSON.parse(content);
1699
2132
  if (first) {
1700
2133
  log("INFO", "auth", `Loaded credentials from ${this.filePath}`, {
@@ -1740,7 +2173,7 @@ var AuthStorage = class {
1740
2173
  }
1741
2174
  let changed;
1742
2175
  try {
1743
- const stat = await fs3.stat(this.filePath);
2176
+ const stat = await fs5.stat(this.filePath);
1744
2177
  changed = stat.ino !== this.snapshotIno || stat.mtimeMs !== this.snapshotMtimeMs || stat.size !== this.snapshotSize;
1745
2178
  } catch {
1746
2179
  changed = this.snapshotSize !== -1;
@@ -1753,7 +2186,7 @@ var AuthStorage = class {
1753
2186
  */
1754
2187
  async rememberSnapshot() {
1755
2188
  try {
1756
- const stat = await fs3.stat(this.filePath);
2189
+ const stat = await fs5.stat(this.filePath);
1757
2190
  this.snapshotMtimeMs = stat.mtimeMs;
1758
2191
  this.snapshotSize = stat.size;
1759
2192
  this.snapshotIno = stat.ino;
@@ -1894,7 +2327,7 @@ var AuthStorage = class {
1894
2327
  await this.ensureFresh();
1895
2328
  }
1896
2329
  const directStorageKeys = opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider) ? opts.storageKeys : providerStorageKeys(provider);
1897
- if (!directStorageKeys.some((key) => Boolean(this.data[key]))) {
2330
+ if (!directStorageKeys.some((key) => Boolean(this.data[key])) && !(provider === "anthropic" && this.data[CLAUDE_CODE_AUTH_KEY])) {
1898
2331
  await this.reloadLatest();
1899
2332
  }
1900
2333
  if (opts?.storageKeys && !(opts.storageKeys.length === 1 && opts.storageKeys[0] === provider)) {
@@ -1917,6 +2350,12 @@ var AuthStorage = class {
1917
2350
  }
1918
2351
  throw new NotLoggedInError(provider);
1919
2352
  }
2353
+ if (provider === "anthropic" && (!this.data.anthropic || isLegacyClaudeOAuth(this.data.anthropic))) {
2354
+ if (isClaudeCodeAuthMarker(this.data[CLAUDE_CODE_AUTH_KEY])) {
2355
+ return { accessToken: "", refreshToken: "", expiresAt: Number.MAX_SAFE_INTEGER };
2356
+ }
2357
+ if (this.data.anthropic) throw new NotLoggedInError(provider);
2358
+ }
1920
2359
  const dual = dualAuthProvider(provider);
1921
2360
  const dualOAuthCreds = dual ? this.data[dual.oauthKey] : void 0;
1922
2361
  if (dual && dualOAuthCreds) {
@@ -1979,7 +2418,7 @@ var AuthStorage = class {
1979
2418
  this.data = latest;
1980
2419
  return latestCreds;
1981
2420
  }
1982
- const refreshFn = provider === "anthropic" ? refreshAnthropicToken : provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : provider === XAI_OAUTH_KEY ? refreshXaiToken : refreshOpenAIToken;
2421
+ const refreshFn = provider === "gemini" ? refreshGeminiToken : provider === MOONSHOT_OAUTH_KEY ? refreshKimiToken : provider === XAI_OAUTH_KEY ? refreshXaiToken : refreshOpenAIToken;
1983
2422
  let refreshed;
1984
2423
  try {
1985
2424
  refreshed = await refreshFn(latestCreds.refreshToken);
@@ -2038,7 +2477,7 @@ var AuthStorage = class {
2038
2477
  };
2039
2478
  async function readAuthData(filePath) {
2040
2479
  try {
2041
- const content = await fs3.readFile(filePath, "utf-8");
2480
+ const content = await fs5.readFile(filePath, "utf-8");
2042
2481
  return JSON.parse(content);
2043
2482
  } catch (error) {
2044
2483
  if (error.code === "ENOENT") return {};
@@ -2048,10 +2487,10 @@ async function readAuthData(filePath) {
2048
2487
  async function atomicWriteFile(filePath, content) {
2049
2488
  const tmpPath = `${filePath}.${process.pid}.${Date.now()}.${crypto5.randomUUID().slice(0, 8)}.tmp`;
2050
2489
  try {
2051
- await fs3.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
2052
- await fs3.rename(tmpPath, filePath);
2490
+ await fs5.writeFile(tmpPath, content, { encoding: "utf-8", mode: 384 });
2491
+ await fs5.rename(tmpPath, filePath);
2053
2492
  } catch (err) {
2054
- await fs3.unlink(tmpPath).catch(() => {
2493
+ await fs5.unlink(tmpPath).catch(() => {
2055
2494
  });
2056
2495
  throw err;
2057
2496
  }
@@ -2254,9 +2693,9 @@ var MODELS = [
2254
2693
  billing: CODEX_FAST_BILLING
2255
2694
  },
2256
2695
  // GPT-6 Sol + Luna — released 2026-09-22 below Astra, replacing the whole
2257
- // GPT-5.6 family (Sol/Terra/Luna; there is no GPT-6 Terra — OpenAI's Codex
2696
+ // GPT-5.6 family (Sol/Terra/Luna; there was no GPT-6 Terra — OpenAI's Codex
2258
2697
  // catalog upgrades 5.6 Terra to 6 Sol). GPT-6.1 Sol (2026-09-29) then replaced
2259
- // GPT-6 Sol. Same window split as Astra: 1.05M on the public Responses API,
2698
+ // 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,
2260
2699
  // 272K on the Codex route; 128K output, text+image input, freeform
2261
2700
  // apply_patch, responses-lite transport. The GPT-5.6 ids and `gpt-6-sol` are
2262
2701
  // retired — a saved session on one falls back to the provider default on
@@ -2298,6 +2737,40 @@ var MODELS = [
2298
2737
  maxThinkingLevel: "ultra",
2299
2738
  billing: CODEX_FAST_BILLING
2300
2739
  },
2740
+ {
2741
+ // GPT-6.1 Terra (Codex priority 7, default low, client >= 0.153.0). $1/$5
2742
+ // MTok, cached input $0.05. Unlike Sol/Astra the Codex route gets a 500K
2743
+ // window (catalog `context_window`). Same ladder and Ultra preset as Sol.
2744
+ id: "gpt-6.1-terra",
2745
+ name: "GPT-6.1 Terra",
2746
+ provider: "openai",
2747
+ contextWindow: 105e4,
2748
+ codexContextWindow: 5e5,
2749
+ maxOutputTokens: 128e3,
2750
+ supportsThinking: true,
2751
+ defaultThinkingLevel: "low",
2752
+ supportsImages: true,
2753
+ supportsVideo: false,
2754
+ costTier: "medium",
2755
+ maxThinkingLevel: "ultra"
2756
+ },
2757
+ {
2758
+ // Codex Fast mode for 6.1 Terra ("1.5x speed") — see `gpt-6-astra-fast`
2759
+ // for the wire mapping and billing.
2760
+ id: "gpt-6.1-terra-fast",
2761
+ name: "GPT-6.1 Terra Fast",
2762
+ provider: "openai",
2763
+ contextWindow: 105e4,
2764
+ codexContextWindow: 5e5,
2765
+ maxOutputTokens: 128e3,
2766
+ supportsThinking: true,
2767
+ defaultThinkingLevel: "low",
2768
+ supportsImages: true,
2769
+ supportsVideo: false,
2770
+ costTier: "medium",
2771
+ maxThinkingLevel: "ultra",
2772
+ billing: CODEX_FAST_BILLING
2773
+ },
2301
2774
  {
2302
2775
  // Luna — "Fast and affordable model for easier tasks." (Codex priority 4,
2303
2776
  // default medium, needs a Codex client >= 0.155.0). $0.10/$0.50 MTok.
@@ -2974,12 +3447,24 @@ export {
2974
3447
  isKimiCodingEndpoint,
2975
3448
  loginKimi,
2976
3449
  refreshKimiToken,
3450
+ CLAUDE_CODE_MANIFEST,
3451
+ CLAUDE_CODE_AUTH_KEY,
3452
+ CLAUDE_CODE_AUTH_MARKER,
3453
+ isClaudeCodeAuthMarker,
3454
+ isLegacyClaudeOAuth,
3455
+ claudeCodePlatformKey,
3456
+ claudeCodeHome,
3457
+ claudeCodeConfigDir,
3458
+ claudeCodeBinaryPath,
3459
+ resolveClaudeCodeBinary,
3460
+ ensureClaudeCodeBinary,
3461
+ formatClaudeCodeInstallProgress,
3462
+ claudeCodeEnv,
3463
+ claudeCodeAuthStatus,
3464
+ logoutClaudeCode,
3465
+ claudeCodeCallbackUrl,
3466
+ loginClaudeCode,
2977
3467
  generatePKCE,
2978
- getClaudeCodeVersion,
2979
- getClaudeCliUserAgent,
2980
- pendingClaudeCodeVersionRefresh,
2981
- loginAnthropic,
2982
- refreshAnthropicToken,
2983
3468
  loginOpenAI,
2984
3469
  refreshOpenAIToken,
2985
3470
  loginGemini,
@@ -3039,4 +3524,4 @@ export {
3039
3524
  getDefaultThinkingLevel,
3040
3525
  getSummaryModel
3041
3526
  };
3042
- //# sourceMappingURL=chunk-SDUIKPGO.js.map
3527
+ //# sourceMappingURL=chunk-JLEBGMG4.js.map