@kenkaiiii/gg-core 5.93.0 → 5.95.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.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ModelInfo } from './model-registry.js';
2
2
  export { ContextWindowOptions, DEFAULT_MAX_VIDEO_BYTES, HIDDEN_PROVIDERS, MODELS, ModelBilling, clearRuntimeModels, getAllModels, getAuthStorageKey, getAuthStorageKeys, getContextWindow, getDefaultModel, getDefaultThinkingLevel, getListedModels, getMaxThinkingLevel, getModel, getModelsForProvider, getProviderCatalog, getSummaryModel, getToolResultCharLimit, getVideoByteLimit, isHiddenProvider, isPaidModel, registerRuntimeModels, setProviderCatalog, usesOpenAICodexTransport, withoutPaidTier } from './model-registry.js';
3
3
  import { ThinkingLevel, Provider } from '@kenkaiiii/gg-ai';
4
- export { getClaudeCliUserAgent, getClaudeCodeVersion, pendingClaudeCodeVersionRefresh } from '@kenkaiiii/gg-ai';
5
4
  export { AppPaths, getAppPaths } from './paths.js';
6
5
 
7
6
  /** gg-ai's OPENROUTER_BASE_URL — where the openrouter transport goes with no override. */
@@ -201,13 +200,21 @@ interface OAuthCredentials {
201
200
  * scale the proactive-refresh threshold: short-lived tokens (e.g. Kimi's
202
201
  * 15-min access token) must refresh well before expiry, not 60s prior. */
203
202
  expiresIn?: number;
203
+ /** ChatGPT plan: the validated ID-token subject. Its presence routes OpenAI to the plan transport. */
204
204
  accountId?: string;
205
+ /** Retired Gemini Code Assist project ID; only read to recognise an old sign-in. */
205
206
  projectId?: string;
206
207
  baseUrl?: string;
208
+ /** ChatGPT plan: the client ID OpenAI issued at first sign-in (never `dynamic_agent_client`). */
209
+ clientId?: string;
210
+ /** ChatGPT plan: the last validated ID token, sent back as `id_token_hint`. */
211
+ idToken?: string;
212
+ /** ChatGPT plan: scopes the token response granted. */
213
+ scopes?: string[];
207
214
  /** ms timestamp until which this credential's usage window is exhausted.
208
215
  * Set by AuthStorage.markUsageExhausted() when the provider rejects with a
209
216
  * usage/quota stop; while in the future, resolution may skip this credential
210
- * in favor of a configured fallback (e.g. Kimi OAuth → Moonshot API key). */
217
+ * in favor of a configured fallback (e.g. ChatGPT plan → OpenAI API key). */
211
218
  usageExhaustedUntil?: number;
212
219
  }
213
220
  interface OAuthLoginCallbacks {
@@ -225,22 +232,135 @@ interface OAuthLoginCallbacks {
225
232
  onStatus: (message: string) => void;
226
233
  }
227
234
 
235
+ interface ClaudeCodePlatformAsset {
236
+ url: string;
237
+ /** npm `dist.integrity`: "sha512-<base64>". */
238
+ integrity: string;
239
+ bin: string;
240
+ }
241
+ interface ClaudeCodeManifest {
242
+ sdkVersion: string;
243
+ claudeCodeVersion: string;
244
+ platforms: Record<string, ClaudeCodePlatformAsset>;
245
+ }
246
+ declare const CLAUDE_CODE_MANIFEST: ClaudeCodeManifest;
247
+ /** auth.json key for "signed in with Claude through Claude Code". Holds only the marker. */
248
+ declare const CLAUDE_CODE_AUTH_KEY = "anthropic-claude-code";
249
+ declare const CLAUDE_CODE_AUTH_MARKER: {
250
+ readonly via: "claude-code";
251
+ };
252
+ declare function isClaudeCodeAuthMarker(value: unknown): boolean;
253
+ /** An old GG Claude sign-in token (`sk-ant-oat…`). It no longer works; the user must sign in again. */
254
+ declare function isLegacyClaudeOAuth(value: unknown): boolean;
255
+ type ClaudeCodeInstallProgress = {
256
+ phase: "download";
257
+ receivedBytes: number;
258
+ totalBytes?: number;
259
+ } | {
260
+ phase: "verify";
261
+ } | {
262
+ phase: "extract";
263
+ };
264
+ interface ClaudeCodeRuntimeOptions {
265
+ /** Defaults to `~/.gg/claude-code`. */
266
+ home?: string;
267
+ manifest?: ClaudeCodeManifest;
268
+ /** Platform key in the manifest; detected from the running process by default. */
269
+ platformKey?: string;
270
+ }
271
+ interface EnsureClaudeCodeOptions extends ClaudeCodeRuntimeOptions {
272
+ onProgress?: (progress: ClaudeCodeInstallProgress) => void;
273
+ signal?: AbortSignal;
274
+ fetch?: typeof fetch;
275
+ }
276
+ interface ClaudeCodeRunOptions extends ClaudeCodeRuntimeOptions {
277
+ /** Executable to run instead of the installed binary (tests). */
278
+ binary?: string;
279
+ /** Arguments placed before the `auth …` arguments (tests run a script via node). */
280
+ binaryArgs?: string[];
281
+ signal?: AbortSignal;
282
+ /** How long sign-in waits for the browser URL before falling back to the paste-code page. */
283
+ browserWaitMs?: number;
284
+ }
285
+ interface ClaudeCodeAuthStatus {
286
+ loggedIn: boolean;
287
+ /** "claude.ai" for a Claude subscription. */
288
+ authMethod?: string;
289
+ email?: string;
290
+ subscriptionType?: string;
291
+ }
292
+ interface PlatformInfo {
293
+ platform: NodeJS.Platform;
294
+ arch: string;
295
+ musl: boolean;
296
+ }
297
+ /** Manifest key for a platform, or null where Claude Code has no build. */
298
+ declare function claudeCodePlatformKey(info?: PlatformInfo): string | null;
299
+ declare function claudeCodeHome(opts?: ClaudeCodeRuntimeOptions): string;
300
+ declare function claudeCodeConfigDir(opts?: ClaudeCodeRuntimeOptions): string;
301
+ /** Where the pinned binary lives once installed (it may not exist yet). */
302
+ declare function claudeCodeBinaryPath(opts?: ClaudeCodeRuntimeOptions): string;
303
+ /** The installed binary for the pinned version, or null when it still needs downloading. */
304
+ declare function resolveClaudeCodeBinary(opts?: ClaudeCodeRuntimeOptions): string | null;
228
305
  /**
229
- * Storage key for Kimi Code OAuth credentials. Kept distinct from the
230
- * `moonshot` API-key entry so a user can configure BOTH and we always
231
- * prefer OAuth for the logical `moonshot` provider.
306
+ * Download, verify and unpack the pinned Claude Code binary if it isn't
307
+ * installed yet. Concurrent callers in this process share one download;
308
+ * other processes race safely because each unpacks into its own staging dir
309
+ * and only one rename wins.
232
310
  */
233
- declare const MOONSHOT_OAUTH_KEY = "moonshot-oauth";
311
+ declare function ensureClaudeCodeBinary(opts?: EnsureClaudeCodeOptions): Promise<string>;
312
+ /** Plain-language status line for an install progress event. */
313
+ declare function formatClaudeCodeInstallProgress(progress: ClaudeCodeInstallProgress): string;
314
+ /** Environment for every Claude Code process GG Coder starts: our own login, no stray keys. */
315
+ declare function claudeCodeEnv(opts?: ClaudeCodeRuntimeOptions, base?: NodeJS.ProcessEnv): Record<string, string>;
316
+ /** `claude auth status` for GG Coder's own login. Logged out when the binary is missing. */
317
+ declare function claudeCodeAuthStatus(opts?: ClaudeCodeRunOptions): Promise<ClaudeCodeAuthStatus>;
318
+ declare function logoutClaudeCode(opts?: ClaudeCodeRunOptions): Promise<void>;
319
+ /** The browser sign-in URL with Claude Code's localhost callback instead of the paste-code page. */
320
+ declare function claudeCodeCallbackUrl(manualUrl: string, captured: string): string | null;
234
321
  /**
235
- * Storage key for Grok (xAI) subscription OAuth credentials. Kept distinct from
236
- * the `xai` API-key entry for the same reason as Kimi's: a user may configure
237
- * BOTH — a SuperGrok/X Premium subscription plus a metered console key — and we
238
- * always prefer OAuth for the logical `xai` provider.
322
+ * Run `claude auth login` for GG Coder's own Claude login. The browser URL goes
323
+ * to `onOpenUrl`; Claude Code's localhost callback finishes sign-in, and a
324
+ * pasted code (from the fallback page) is accepted too.
239
325
  */
240
- declare const XAI_OAUTH_KEY = "xai-oauth";
326
+ declare function loginClaudeCode(callbacks: OAuthLoginCallbacks, opts?: ClaudeCodeRunOptions): Promise<void>;
327
+
241
328
  /**
242
- * A provider that can hold two credentials at once: a refreshable subscription
243
- * OAuth token and a static API key. One policy governs all of them — see
329
+ * Storage key for the ChatGPT plan sign-in (Continue with ChatGPT). Kept
330
+ * distinct from the `openai` entry, which holds an OpenAI API key, so a user can
331
+ * hold both: the plan serves first and the key covers while its usage is out.
332
+ */
333
+ declare const OPENAI_CHATGPT_KEY = "openai-chatgpt";
334
+ /**
335
+ * Storage key for a Kimi Code plan key (Kimi Code Console), served by
336
+ * `api.kimi.com/coding/v1` or `api.kimi.ai/coding/v1`. Kept distinct from the
337
+ * `moonshot` entry, which holds a pay-per-token Moonshot platform key.
338
+ */
339
+ declare const KIMI_CODE_KEY = "kimi-code";
340
+ /**
341
+ * A sign-in GG Coder no longer uses, because it posed as another vendor's app
342
+ * (Codex CLI, Kimi Code CLI, Grok CLI, Gemini CLI) or its token stopped
343
+ * working. The entry is never served; the user is asked once to reconnect.
344
+ */
345
+ interface RetiredSignIn {
346
+ /** Logical provider the credential belonged to. */
347
+ provider: string;
348
+ /** auth.json key holding the retired credential. */
349
+ storageKey: string;
350
+ /** What to tell the user, without CLI/app-specific instructions. */
351
+ message: string;
352
+ }
353
+ declare const RETIRED_SIGN_INS: readonly RetiredSignIn[];
354
+ /** Logical provider for a storage key (`kimi-code` → `moonshot`). */
355
+ declare function storageKeyProvider(storageKey: string): string;
356
+ /**
357
+ * API-key storage keys for a provider whose keys split into a plan key and a
358
+ * pay-per-token key, plan first. Single-key providers yield just their own key.
359
+ */
360
+ declare function apiKeyStorageKeys(provider: string): string[];
361
+ /**
362
+ * A provider that can hold two credentials at once: a refreshable plan sign-in
363
+ * and a static API key. One policy governs all of them — see
244
364
  * {@link DUAL_AUTH_PROVIDERS} — so adding a provider here is enough to give it
245
365
  * OAuth-first resolution, usage-exhaustion fallback, per-method logout and the
246
366
  * matching UI affordances.
@@ -280,9 +400,7 @@ declare const XIAOMI_CREDITS_KEY = "xiaomi-credits";
280
400
  /**
281
401
  * Storage key for a Gemini API key (Google AI Studio, billed per token), served
282
402
  * by the public Gemini API at {@link GEMINI_API_KEY_BASE_URL}. Kept distinct from
283
- * the `gemini` entry, which holds the Code Assist OAuth login: Google stopped
284
- * serving Code Assist to personal accounts on 2026-06-18, so an API key is how
285
- * those accounts reach Gemini at all, and a licensed account may hold both.
403
+ * the `gemini` entry, which held the retired Google sign-in.
286
404
  */
287
405
  declare const GEMINI_API_KEY = "gemini-api";
288
406
  /** Public Gemini API (AI Studio key). The transport routes on this base URL. */
@@ -323,9 +441,11 @@ declare class AuthStorage {
323
441
  private refreshLocks;
324
442
  private readonly detectMiniMaxRegion;
325
443
  private readonly detectXiaomiCluster;
444
+ private readonly detectKimiRegion;
326
445
  constructor(filePath?: string, options?: {
327
446
  detectMiniMaxBaseUrl?: (apiKey: string) => Promise<string | undefined>;
328
447
  detectXiaomiTokenPlanBaseUrl?: (apiKey: string) => Promise<string | undefined>;
448
+ detectKimiCodeBaseUrl?: (apiKey: string) => Promise<string | undefined>;
329
449
  });
330
450
  /** Path to the on-disk auth file. Useful for status output. */
331
451
  get path(): string;
@@ -342,11 +462,29 @@ declare class AuthStorage {
342
462
  */
343
463
  pickStorageKey(keys: string[]): Promise<string | undefined>;
344
464
  /**
345
- * True if the user has any usable auth for the logical provider. For a
346
- * dual-auth provider (Kimi/Grok) either the OAuth credential or the API key
347
- * satisfies it.
465
+ * True if the user has any usable auth for the logical provider: the plan
466
+ * sign-in or any of its API keys. A retired sign-in doesn't count.
348
467
  */
349
468
  hasProviderAuth(provider: string): Promise<boolean>;
469
+ /** True if any of `provider`'s API keys is stored. A retired sign-in in a key slot doesn't count. */
470
+ hasApiKey(provider: string): Promise<boolean>;
471
+ /**
472
+ * `provider`'s first stored API key, never its plan sign-in. For calls that
473
+ * only an API key may make (OpenAI's Images API isn't on the ChatGPT plan).
474
+ */
475
+ resolveApiKey(provider: string): Promise<OAuthCredentials>;
476
+ /** True when the user signed in with Claude through Claude Code (marker only, no token). */
477
+ hasClaudeCodeLogin(): Promise<boolean>;
478
+ /** Record a Claude Code sign-in. Claude Code keeps the token; auth.json gets only the marker. */
479
+ setClaudeCodeLogin(): Promise<void>;
480
+ /** Retired sign-ins still on disk, so the UI can ask the user to reconnect. */
481
+ legacySignIns(): Promise<RetiredSignIn[]>;
482
+ /**
483
+ * Remove `provider`'s retired sign-in, if any. True when one was removed, so
484
+ * the caller can tell the user once to reconnect.
485
+ */
486
+ takeLegacySignIn(provider: string): Promise<boolean>;
487
+ clearClaudeCodeLogin(): Promise<void>;
350
488
  /** Endpoint ids that currently have a `local:<id>` credential stored. */
351
489
  listLocalEndpointIds(): Promise<string[]>;
352
490
  /**
@@ -366,10 +504,10 @@ declare class AuthStorage {
366
504
  /**
367
505
  * The base URL on the credential that is active right now, if any.
368
506
  * Synchronous — call only after load()/resolveCredentials() populated the
369
- * snapshot. For a dual-auth provider this is the subscription endpoint (Kimi
370
- * For Coding, the Grok CLI proxy) whenever the OAuth entry is the one
371
- * resolveCredentials would serve (i.e. not currently usage-exhausted with an
372
- * API key configured).
507
+ * snapshot. For a dual-auth provider this is the plan endpoint whenever the
508
+ * sign-in is the one resolveCredentials would serve (i.e. not currently
509
+ * usage-exhausted with an API key configured); for a plan/pay-per-token key
510
+ * split it is the plan key's endpoint when one is stored.
373
511
  */
374
512
  getStoredBaseUrl(provider: string): string | undefined;
375
513
  load(): Promise<void>;
@@ -408,7 +546,16 @@ declare class AuthStorage {
408
546
  * and was deleted as invalid.
409
547
  */
410
548
  private withMiniMaxRegion;
411
- setCredentials(provider: string, creds: OAuthCredentials): Promise<void>;
549
+ /**
550
+ * A Kimi Code key saved without an endpoint gets the host that issued it
551
+ * (`api.kimi.com` or `api.kimi.ai`), saved so the check runs once.
552
+ */
553
+ private withKimiRegion;
554
+ /**
555
+ * Store `creds` under `storageKey`. Connecting a provider again also removes
556
+ * its retired sign-in, which is what the reconnect notice asked for.
557
+ */
558
+ setCredentials(storageKey: string, creds: OAuthCredentials): Promise<void>;
412
559
  /**
413
560
  * After a 401, move a regional key to the cluster that issued it instead of
414
561
  * treating it as invalid. Only the Xiaomi Token Plan is regional: every key
@@ -422,9 +569,9 @@ declare class AuthStorage {
422
569
  * Mark the credential stored under `storageKey` as usage-exhausted until
423
570
  * `resetsAt` (unix SECONDS, from the provider's rate-limit response) or a
424
571
  * 15-minute default when no reset time is known. While the mark is in the
425
- * future, `resolveCredentials("moonshot")` serves the Moonshot API key
426
- * instead of the subscription OAuth credential (when both are configured) — OAuth
427
- * stays the preferred credential and is retried automatically once the mark
572
+ * future, `resolveCredentials("openai")` serves the OpenAI API key instead of
573
+ * the ChatGPT plan sign-in (when both are configured) — the plan stays the
574
+ * preferred credential and is retried automatically once the mark
428
575
  * lapses. Persisted to auth.json so a restart (or another gg-app window)
429
576
  * doesn't burn a request rediscovering the same exhausted window. No-op if
430
577
  * nothing is stored under `storageKey`.
@@ -466,6 +613,82 @@ declare class NotLoggedInError extends Error {
466
613
  constructor(provider: string);
467
614
  }
468
615
 
616
+ type ClaudeCodeSignInOptions = EnsureClaudeCodeOptions & ClaudeCodeRunOptions;
617
+ /**
618
+ * "Sign in with Claude": download Claude Code if it is missing, run its browser
619
+ * sign-in, confirm it with `claude auth status`, then record the marker.
620
+ */
621
+ declare function signInWithClaudeCode(auth: Pick<AuthStorage, "setClaudeCodeLogin">, callbacks: OAuthLoginCallbacks, opts?: ClaudeCodeSignInOptions): Promise<ClaudeCodeAuthStatus>;
622
+ /** Sign out of Claude Code and drop the marker. The marker goes even if `claude auth logout` fails. */
623
+ declare function signOutOfClaudeCode(auth: Pick<AuthStorage, "clearClaudeCodeLogin">, opts?: ClaudeCodeRunOptions): Promise<void>;
624
+
625
+ type SubscriptionUsageProvider = "anthropic" | "moonshot";
626
+ /** Providers whose usage endpoint we call with a token. Claude reports its own (claude-code/usage.ts). */
627
+ type FetchedSubscriptionUsageProvider = Exclude<SubscriptionUsageProvider, "anthropic">;
628
+ interface SubscriptionUsageWindow {
629
+ kind: "current" | "weekly";
630
+ label: string;
631
+ usedPercent: number;
632
+ /** Unix epoch milliseconds. */
633
+ resetsAt?: number;
634
+ }
635
+ interface SubscriptionUsageSnapshot {
636
+ provider: SubscriptionUsageProvider;
637
+ displayName: string;
638
+ windows: SubscriptionUsageWindow[];
639
+ fetchedAt: number;
640
+ }
641
+ declare class SubscriptionUsageError extends Error {
642
+ readonly status?: number | undefined;
643
+ readonly retryAfterMs?: number | undefined;
644
+ constructor(message: string, status?: number | undefined, retryAfterMs?: number | undefined);
645
+ }
646
+ type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
647
+ interface FetchSubscriptionUsageOptions {
648
+ fetchFn?: FetchFn;
649
+ timeoutMs?: number;
650
+ now?: () => number;
651
+ }
652
+ /**
653
+ * Fetch plan quota windows with an already-resolved plan credential.
654
+ * Tokens stay server-side: callers expose only the normalized percentages and
655
+ * reset timestamps to their UI.
656
+ */
657
+ declare function fetchSubscriptionUsage(provider: FetchedSubscriptionUsageProvider, credentials: Pick<OAuthCredentials, "accessToken" | "baseUrl">, options?: FetchSubscriptionUsageOptions): Promise<SubscriptionUsageSnapshot>;
658
+
659
+ /** The parts of the SDK's `rate_limit_event.rate_limit_info` the usage meter reads. */
660
+ interface ClaudeCodeRateLimitInfo {
661
+ rateLimitType?: string;
662
+ /** Fraction of the window used, 0–1. */
663
+ utilization?: number;
664
+ /** Unix epoch seconds. */
665
+ resetsAt?: number;
666
+ /** Not in the SDK types yet; carries every window the response reported. */
667
+ unifiedWindows?: Record<string, {
668
+ utilization?: number;
669
+ resetsAt?: number;
670
+ } | undefined>;
671
+ }
672
+ /**
673
+ * Fold one SDK rate-limit report into the last snapshot. A report can carry a
674
+ * single window, so windows it doesn't mention keep their previous values.
675
+ */
676
+ declare function mergeClaudeCodeRateLimit(info: ClaudeCodeRateLimitInfo, previous: SubscriptionUsageSnapshot | null, now?: number): SubscriptionUsageSnapshot;
677
+ /**
678
+ * The latest Claude subscription usage Claude Code reported, from any GG
679
+ * process on this machine. Windows whose reset time has passed are dropped:
680
+ * their old percentage no longer holds and the new one isn't known yet.
681
+ */
682
+ declare function readClaudeCodeUsage(opts?: ClaudeCodeRuntimeOptions & {
683
+ now?: number;
684
+ }): Promise<SubscriptionUsageSnapshot | null>;
685
+ /** Save an SDK rate-limit report so the usage meter (app or CLI) can show it. */
686
+ declare function recordClaudeCodeRateLimit(info: ClaudeCodeRateLimitInfo, opts?: ClaudeCodeRuntimeOptions & {
687
+ now?: number;
688
+ }): Promise<void>;
689
+ /** Forget saved usage, e.g. on sign-out, so another account never inherits it. */
690
+ declare function clearClaudeCodeUsage(opts?: ClaudeCodeRuntimeOptions): Promise<void>;
691
+
469
692
  /**
470
693
  * MiniMax runs two separate platforms, and a key only works on the one that
471
694
  * issued it: Global (platform.minimax.io) or Mainland China
@@ -501,19 +724,41 @@ declare function detectXiaomiTokenPlanBaseUrl(apiKey: string, options?: {
501
724
  timeoutMs?: number;
502
725
  }): Promise<string | undefined>;
503
726
 
727
+ /**
728
+ * Kimi Code plan keys come from the Kimi Code Console and work on the host
729
+ * that issued them: `api.kimi.com` (kimi.com accounts) or `api.kimi.ai`
730
+ * (kimi.ai accounts). The other host rejects the key as invalid.
731
+ */
732
+ declare const KIMI_CODE_DEFAULT_BASE_URL = "https://api.kimi.com/coding/v1";
733
+ declare const KIMI_CODE_OVERSEAS_BASE_URL = "https://api.kimi.ai/coding/v1";
734
+ /**
735
+ * The Kimi Code base URL for the host that issued `apiKey`, found by asking
736
+ * both. Undefined when neither gives a clear answer (offline, timeouts);
737
+ * callers then use {@link KIMI_CODE_DEFAULT_BASE_URL} and may ask again later.
738
+ */
739
+ declare function detectKimiCodeBaseUrl(apiKey: string, options?: {
740
+ fetch?: typeof fetch;
741
+ timeoutMs?: number;
742
+ }): Promise<string | undefined>;
743
+ /** True if `baseUrl` is a Kimi Code plan endpoint (either host). */
744
+ declare function isKimiCodingEndpoint(baseUrl: string | undefined): boolean;
745
+
504
746
  type AuthMethod = "oauth" | "apikey";
505
747
  /**
506
- * One API-key option for a provider that splits auth across multiple distinct
507
- * endpoints/credentials (currently only Xiaomi: Token Plan vs. API Credits).
508
- * Each variant stores under its own auth.json key so a user can hold both at
509
- * once — the model registry picks which one a given model resolves via
510
- * `getAuthStorageKeys()`.
748
+ * One API-key option for a provider that takes two kinds of key: a plan key
749
+ * (the user's subscription) and a pay-per-token key. Each variant stores under
750
+ * its own auth.json key so a user can hold both at once; the model registry
751
+ * picks which one a model resolves via `getAuthStorageKeys()`.
511
752
  */
512
753
  interface ApiKeyVariant {
513
754
  /** Storage key in auth.json (distinct from `value` when multiple variants exist). */
514
755
  key: string;
515
756
  /** Display label, e.g. "Token Plan" or "API Credits". */
516
757
  label: string;
758
+ /** `plan` bills the user's subscription; `payg` bills per token. */
759
+ kind: "plan" | "payg";
760
+ /** What the user spends with this key, e.g. "Uses your Kimi Code plan." */
761
+ billing: string;
517
762
  /** Base URL stored alongside this variant's credential. */
518
763
  baseUrl?: string;
519
764
  }
@@ -531,17 +776,12 @@ interface AuthProviderMeta {
531
776
  /** Fixed base URL stored alongside an API key (e.g. Xiaomi's token plan). */
532
777
  apiKeyBaseUrl?: string;
533
778
  /**
534
- * When a provider's API-key auth splits across multiple endpoints, the
535
- * choices to present (in order). The first variant is the default. Absent
536
- * for every provider with a single API-key credential.
779
+ * When a provider takes both a plan key and a pay-per-token key, the choices
780
+ * to present (in order). The first variant is the default.
537
781
  */
538
782
  apiKeyVariants?: ApiKeyVariant[];
539
- /**
540
- * Per-method guidance overrides. Only providers whose methods differ in
541
- * billing/entitlement (the dual-auth ones) need these; everything else is
542
- * described generically by {@link describeAuthMethods}.
543
- */
544
- methodDetails?: Partial<Record<AuthMethod, AuthMethodMeta>>;
783
+ /** What each method means for this provider (billing, when to pick it). */
784
+ methodDetails: Partial<Record<AuthMethod, AuthMethodMeta>>;
545
785
  }
546
786
  /**
547
787
  * What one auth method means for a provider, so both UIs can answer "which do I
@@ -549,7 +789,7 @@ interface AuthProviderMeta {
549
789
  */
550
790
  interface AuthMethodMeta {
551
791
  method: AuthMethod;
552
- /** Button/row label, e.g. "Sign in with Grok (SuperGrok / X Premium)". */
792
+ /** Button/row label, e.g. "Continue with ChatGPT". */
553
793
  label: string;
554
794
  /** What the user spends on this method. */
555
795
  billing: string;
@@ -558,13 +798,18 @@ interface AuthMethodMeta {
558
798
  /** Prerequisite the user must already have, if any. */
559
799
  requires?: string;
560
800
  }
801
+ /**
802
+ * What a model needs when it runs only on some of its provider's keys (Kimi
803
+ * K2.8 Preview on a Kimi Code plan key, MiMo UltraSpeed on API Credits), named
804
+ * by the variants' labels. Undefined for a model any of the provider's keys runs.
805
+ */
806
+ declare function requiredKeyLabel(provider: string, storageKeys: string[]): string | undefined;
561
807
  /** Every provider a user can connect right now, in login-screen order. */
562
808
  declare const AUTH_PROVIDERS: AuthProviderMeta[];
563
809
  declare function getAuthProvider(value: string): AuthProviderMeta | undefined;
564
810
  /**
565
- * Per-method guidance for a provider, in resolution order. Providers with a
566
- * single method get a generic description; the dual-auth ones (Kimi, Grok) carry
567
- * explicit `methodDetails` because the choice changes what the user is billed.
811
+ * Per-method guidance for a provider, in resolution order. Every provider
812
+ * carries `methodDetails`; the generic fallback only covers a missing entry.
568
813
  */
569
814
  declare function describeAuthMethods(provider: string): AuthMethodMeta[];
570
815
  /**
@@ -577,160 +822,66 @@ declare function describeAuthMethods(provider: string): AuthMethodMeta[];
577
822
  */
578
823
  declare function authPriorityNote(provider: string): string | undefined;
579
824
 
580
- type SubscriptionUsageProvider = "anthropic" | "openai" | "moonshot";
581
- interface SubscriptionUsageWindow {
582
- kind: "current" | "weekly";
583
- label: string;
584
- usedPercent: number;
585
- /** Unix epoch milliseconds. */
586
- resetsAt?: number;
587
- }
588
- interface SubscriptionUsageSnapshot {
589
- provider: SubscriptionUsageProvider;
590
- displayName: string;
591
- windows: SubscriptionUsageWindow[];
592
- fetchedAt: number;
593
- }
594
- declare class SubscriptionUsageError extends Error {
595
- readonly status?: number | undefined;
596
- readonly retryAfterMs?: number | undefined;
597
- constructor(message: string, status?: number | undefined, retryAfterMs?: number | undefined);
598
- }
599
- type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
600
- interface FetchSubscriptionUsageOptions {
601
- fetchFn?: FetchFn;
602
- timeoutMs?: number;
603
- now?: () => number;
604
- }
605
- /**
606
- * Fetch subscription quota windows with an already-resolved OAuth credential.
607
- * Tokens stay server-side: callers expose only the normalized percentages and
608
- * reset timestamps to their UI.
609
- */
610
- declare function fetchSubscriptionUsage(provider: SubscriptionUsageProvider, credentials: Pick<OAuthCredentials, "accessToken" | "accountId">, options?: FetchSubscriptionUsageOptions): Promise<SubscriptionUsageSnapshot>;
611
-
612
825
  declare function generatePKCE(): Promise<{
613
826
  verifier: string;
614
827
  challenge: string;
615
828
  }>;
616
829
 
617
- declare function loginAnthropic(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
618
- declare function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials>;
619
-
620
- declare function loginOpenAI(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
621
- declare function refreshOpenAIToken(refreshToken: string): Promise<OAuthCredentials>;
622
-
623
- declare function loginGemini(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
624
- declare function refreshGeminiToken(refreshToken: string): Promise<OAuthCredentials>;
625
-
626
- /**
627
- * Kimi Code OAuth — Device Authorization Grant (RFC 8628).
628
- *
629
- * Mirrors MoonshotAI/kimi-code's managed-auth flow. Three form-encoded
630
- * POST endpoints against the OAuth host (default `https://auth.kimi.com`):
631
- *
632
- * - `/api/oauth/device_authorization` (client_id) → device + user code
633
- * - `/api/oauth/token` (grant_type=device_code) → poll until authorized
634
- * - `/api/oauth/token` (grant_type=refresh_token) → refresh access token
635
- *
636
- * Unlike Anthropic/OpenAI/Gemini (browser-redirect PKCE), this is a
637
- * device-code/poll flow: we show the user a URL + code, they authorize in a
638
- * browser on any device, and we poll for the token.
639
- *
640
- * After login the issued token is used against the managed coding API
641
- * (`https://api.kimi.com/coding/v1`, distinct from the `api.moonshot.ai`
642
- * API-key endpoint) via `Authorization: Bearer <access_token>`. We persist
643
- * that base URL on the credential so the runtime routes there automatically.
644
- */
645
-
646
- /** Managed coding API base URL the issued OAuth token is used against. */
647
- declare function kimiCodeBaseUrl(): string;
648
- /**
649
- * Headers the Kimi For Coding API requires on every model request. The
650
- * managed endpoint gates access to recognized coding agents: requests must
651
- * carry a `kimi_code_cli` platform identity and matching `User-Agent`, or the
652
- * server rejects with "only available for Coding Agents". Attach these to the
653
- * inference client's default headers whenever the Kimi OAuth token is used.
654
- */
655
- declare function kimiCodingHeaders(): Record<string, string>;
656
- /**
657
- * True if `baseUrl` targets the Kimi For Coding managed endpoint (the URL
658
- * persisted on Kimi OAuth credentials). Callers use this to decide whether to
659
- * attach `kimiCodingHeaders()` — the Moonshot API-key path uses a different
660
- * host and must NOT receive the coding-agent identity headers.
661
- */
662
- declare function isKimiCodingEndpoint(baseUrl: string | undefined): boolean;
663
- /**
664
- * Drive the Kimi device-code flow end-to-end. Shows the verification URL +
665
- * user code via callbacks, opens the browser, and polls until the user
666
- * authorizes (or a 15-minute local timeout elapses).
667
- */
668
- declare function loginKimi(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
669
- /** Exchange a refresh token for a fresh Kimi access token. */
670
- declare function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials>;
671
-
830
+ /** The scope that lets GG Coder use the user's ChatGPT plan. */
831
+ declare const CHATGPT_PLAN_SCOPE = "chatgpt.tokens.use.direct";
832
+ /** Responses API base for ChatGPT plan inference. */
833
+ declare const CHATGPT_PLAN_BASE_URL = "https://api.openai.com/v1";
834
+ /** Where users review and limit what GG Coder may use of their plan. */
835
+ declare const CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
836
+ declare const CHATGPT_PLAN_NOT_ENABLED_MESSAGE = "ChatGPT plan use isn't enabled for GG Coder. Allow Use your ChatGPT plan, or add an OpenAI API key.";
837
+ /** A sign-in that worked but can't be used to bill the user's ChatGPT plan. */
838
+ declare class ChatGPTPlanNotEnabledError extends Error {
839
+ constructor();
840
+ }
841
+ interface ChatGPTLoginOptions {
842
+ /** Defaults to `~/.gg/openai-host-id`. */
843
+ hostIdFile?: string;
844
+ /** Defaults to `~/.gg/openai-chatgpt-client.json`. */
845
+ registrationFile?: string;
846
+ /** The current sign-in, if any: its ID token skips the account picker. */
847
+ previous?: Pick<OAuthCredentials, "idToken">;
848
+ }
849
+ /** This machine's stable `ext_agent_host_id`, created on first use. */
850
+ declare function getChatGPTHostId(file?: string): Promise<string>;
672
851
  /**
673
- * Grok (xAI) subscription OAuth — Device Authorization Grant (RFC 8628).
674
- *
675
- * Two form-encoded POST endpoints against xAI's OIDC issuer (`https://auth.x.ai`,
676
- * advertised by its `/.well-known/openid-configuration`):
677
- *
678
- * - `/oauth2/device/code` (client_id + scope) → device + user code
679
- * - `/oauth2/token` (grant_type=device_code) → poll until authorized
680
- * - `/oauth2/token` (grant_type=refresh_token) → refresh access token
681
- *
682
- * Like Kimi (and unlike Anthropic/OpenAI/Gemini's browser-redirect PKCE) this is
683
- * a device-code/poll flow: show a URL + code, the user authorizes in a browser on
684
- * any device, we poll for the token. Deliberately chosen over the loopback PKCE
685
- * variant because xAI pins the Grok-CLI client's redirect to
686
- * `http://127.0.0.1:56121/callback` — a fixed port we cannot rebind if it's busy,
687
- * and unreachable from a container/SSH session. Device code has neither problem.
688
- *
689
- * The issued token is used against the Grok CLI's chat proxy
690
- * (`https://cli-chat-proxy.grok.com/v1`, distinct from the `api.x.ai` API-key
691
- * endpoint) — that is the surface the `grok-cli:access` scope grants, and it
692
- * bills against the user's SuperGrok / X Premium subscription instead of metered
693
- * API credits. We persist that base URL on the credential so the runtime routes
694
- * there automatically; `grokCliHeaders()` supplies the client identity the proxy
695
- * requires (attached centrally in gg-ai's `xai` transport).
696
- *
697
- * Caveats worth knowing, both observed in the wild and surfaced to users rather
698
- * than hidden here:
699
- * - The client id below is xAI's public Grok-CLI desktop client (no secret).
700
- * Every third-party implementation reuses it; xAI has not published a
701
- * partner-client program, so subscription OAuth is a best-effort path.
702
- * - xAI gates proxy access by subscription tier. A perfectly valid login can
703
- * still be refused at inference time, which is exactly why `xai` keeps its
704
- * API-key method as a fallback (see AuthStorage's dual-auth resolution).
852
+ * Continue with ChatGPT. Registers GG Coder for this ChatGPT account on first
853
+ * use (the user names and approves it in the browser), then reuses the issued
854
+ * client ID. Throws {@link ChatGPTPlanNotEnabledError} when the user signed in
855
+ * but didn't allow plan use.
705
856
  */
706
-
707
- /** Grok CLI chat-proxy base URL the issued OAuth token is used against. */
708
- declare function grokCliBaseUrl(): string;
857
+ declare function loginChatGPT(callbacks: OAuthLoginCallbacks, options?: ChatGPTLoginOptions): Promise<OAuthCredentials>;
709
858
  /**
710
- * Headers the Grok CLI chat proxy requires on every model request. It serves
711
- * only recognized Grok-CLI clients: without the token-auth marker and a client
712
- * version it refuses the request. `modelId` populates the model-override header
713
- * the proxy uses to route a request to the entitled model.
714
- *
715
- * Attach these ONLY to the proxy — the `api.x.ai` API-key path must not receive
716
- * them (see {@link isGrokCliEndpoint}).
859
+ * Model ids the signed-in ChatGPT account may use on its plan, from the same
860
+ * `/v1/models` catalog OpenAI tells apps to build their picker from. Hidden
861
+ * entries (`visibility` other than "list") are left out.
717
862
  */
718
- declare function grokCliHeaders(modelId?: string): Record<string, string>;
863
+ declare function fetchChatGPTPlanModels(accessToken: string, fetchFn?: typeof fetch): Promise<string[]>;
864
+ /** The callback query from a pasted URL or query string, or undefined for anything else. */
865
+ declare function parseCallbackInput(input: string): URLSearchParams | undefined;
719
866
  /**
720
- * True if `baseUrl` targets the Grok CLI chat proxy (the URL persisted on Grok
721
- * OAuth credentials). Callers use this to decide whether to attach
722
- * {@link grokCliHeaders} and whether a usage/permission rejection should fall
723
- * back to the xAI API key.
867
+ * Renew the plan token with the issued client ID. The response replaces the
868
+ * access token, its expiry, the granted scopes and the rotating refresh token.
724
869
  */
725
- declare function isGrokCliEndpoint(baseUrl: string | undefined): boolean;
870
+ declare function refreshChatGPTToken(creds: Pick<OAuthCredentials, "refreshToken" | "clientId">): Promise<OAuthCredentials>;
726
871
  /**
727
- * Drive the Grok device-code flow end-to-end. Shows the verification URL + user
728
- * code via callbacks, opens the browser, and polls until the user authorizes or
729
- * the device code expires (deadline set by the server).
872
+ * End the renewable session on sign-out. True when OpenAI confirmed it; callers
873
+ * clear local tokens either way and say so when it wasn't confirmed.
730
874
  */
731
- declare function loginXai(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
732
- /** Exchange a refresh token for a fresh Grok access token. */
733
- declare function refreshXaiToken(refreshToken: string): Promise<OAuthCredentials>;
875
+ declare function revokeChatGPTSignIn(creds: Pick<OAuthCredentials, "refreshToken" | "clientId">, options?: {
876
+ attempts?: number;
877
+ backoffMs?: number;
878
+ }): Promise<boolean>;
879
+ /** Check the ID token's RS256 signature against OpenAI's JWKS, then iss, aud, exp and nonce. */
880
+ declare function verifyIdToken(idToken: string, expected: {
881
+ clientId: string;
882
+ nonce: string;
883
+ now?: number;
884
+ }): Promise<Record<string, unknown>>;
734
885
 
735
886
  /**
736
887
  * Minimal Telegram Bot API client using raw fetch().
@@ -907,4 +1058,4 @@ interface AutoUpdater {
907
1058
  }
908
1059
  declare function createAutoUpdater(config: AutoUpdateConfig): AutoUpdater;
909
1060
 
910
- export { AUTH_PROVIDERS, type ApiKeyVariant, type AuthMethod, type AuthMethodMeta, type AuthProviderMeta, AuthStorage, type AutoUpdateConfig, type AutoUpdater, DEFAULT_LOCAL_ENDPOINTS, type DiscoverOptions, type DiscoveryResult, type DualAuthProvider, FALLBACK_CONTEXT_WINDOW, GEMINI_API_KEY, GEMINI_API_KEY_BASE_URL, type InlineButton, LOCAL_API_KEY_PLACEHOLDER, LOCAL_AUTH_KEY_PREFIX, type LocalEndpoint, type LocalEndpointKind, type LocalEndpointProbe, type LocalModel, type LogLevel, MINIMAX_CN_BASE_URL, MINIMAX_GLOBAL_BASE_URL, MOONSHOT_OAUTH_KEY, ModelInfo, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, OPENROUTER_DEFAULT_BASE_URL, PLAN_MODE_THINKING_CAP, type ParsedOpenRouterCatalog, type ProbeOptions, type ProgressCallback, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XAI_OAUTH_KEY, XIAOMI_CREDITS_KEY, XIAOMI_TOKEN_PLAN_BASE_URLS, authPriorityNote, clampThinkingForPlanMode, clearLocalDiscoveryCache, closeLogger, createAutoUpdater, decodeOggOpus, describeAuthMethods, detectMiniMaxBaseUrl, detectXiaomiTokenPlanBaseUrl, discoverLocalModels, downmixToMono, dualAuthProvider, dualAuthProviderByOAuthKey, endpointRoot, fetchSubscriptionUsage, findProbedModel, formatLocalModelId, generatePKCE, getAuthProvider, getLowestThinkingLevel, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, grokCliBaseUrl, grokCliHeaders, isGrokCliEndpoint, isKimiCodingEndpoint, isLocalModelId, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, kimiCodeBaseUrl, kimiCodingHeaders, localAuthStorageKey, localModelShortName, log, loginAnthropic, loginGemini, loginKimi, loginOpenAI, loginXai, oauthStorageKey, ollamaServedContext, openLog, parseLocalModelId, parseOpenRouterCatalog, probeEndpoint, providerStorageKeys, readStoredBaseUrlSync, refreshAnthropicToken, refreshGeminiToken, refreshKimiToken, refreshOpenAIToken, refreshXaiToken, registerLogCleanup, resample, setProgressCallback, toModelInfo, transcribeVoice, withFileLock };
1061
+ export { AUTH_PROVIDERS, type ApiKeyVariant, type AuthMethod, type AuthMethodMeta, type AuthProviderMeta, AuthStorage, type AutoUpdateConfig, type AutoUpdater, CHATGPT_PLAN_BASE_URL, CHATGPT_PLAN_NOT_ENABLED_MESSAGE, CHATGPT_PLAN_SCOPE, CHATGPT_USAGE_URL, CLAUDE_CODE_AUTH_KEY, CLAUDE_CODE_AUTH_MARKER, CLAUDE_CODE_MANIFEST, type ChatGPTLoginOptions, ChatGPTPlanNotEnabledError, type ClaudeCodeAuthStatus, type ClaudeCodeInstallProgress, type ClaudeCodeManifest, type ClaudeCodePlatformAsset, type ClaudeCodeRateLimitInfo, type ClaudeCodeRunOptions, type ClaudeCodeRuntimeOptions, type ClaudeCodeSignInOptions, DEFAULT_LOCAL_ENDPOINTS, type DiscoverOptions, type DiscoveryResult, type DualAuthProvider, type EnsureClaudeCodeOptions, FALLBACK_CONTEXT_WINDOW, type FetchedSubscriptionUsageProvider, GEMINI_API_KEY, GEMINI_API_KEY_BASE_URL, type InlineButton, KIMI_CODE_DEFAULT_BASE_URL, KIMI_CODE_KEY, KIMI_CODE_OVERSEAS_BASE_URL, LOCAL_API_KEY_PLACEHOLDER, LOCAL_AUTH_KEY_PREFIX, type LocalEndpoint, type LocalEndpointKind, type LocalEndpointProbe, type LocalModel, type LogLevel, MINIMAX_CN_BASE_URL, MINIMAX_GLOBAL_BASE_URL, ModelInfo, NotLoggedInError, type OAuthCredentials, type OAuthLoginCallbacks, OPENAI_CHATGPT_KEY, OPENROUTER_DEFAULT_BASE_URL, PLAN_MODE_THINKING_CAP, type ParsedOpenRouterCatalog, type ProbeOptions, type ProgressCallback, RETIRED_SIGN_INS, type RetiredSignIn, SubscriptionUsageError, type SubscriptionUsageProvider, type SubscriptionUsageSnapshot, type SubscriptionUsageWindow, TelegramBot, type TelegramConfig, type TelegramMessage, type TelegramUpdate, type TelegramVoiceMessage, XIAOMI_CREDITS_KEY, XIAOMI_TOKEN_PLAN_BASE_URLS, apiKeyStorageKeys, authPriorityNote, clampThinkingForPlanMode, claudeCodeAuthStatus, claudeCodeBinaryPath, claudeCodeCallbackUrl, claudeCodeConfigDir, claudeCodeEnv, claudeCodeHome, claudeCodePlatformKey, clearClaudeCodeUsage, clearLocalDiscoveryCache, closeLogger, createAutoUpdater, decodeOggOpus, describeAuthMethods, detectKimiCodeBaseUrl, detectMiniMaxBaseUrl, detectXiaomiTokenPlanBaseUrl, discoverLocalModels, downmixToMono, dualAuthProvider, dualAuthProviderByOAuthKey, endpointRoot, ensureClaudeCodeBinary, fetchChatGPTPlanModels, fetchSubscriptionUsage, findProbedModel, formatClaudeCodeInstallProgress, formatLocalModelId, generatePKCE, getAuthProvider, getChatGPTHostId, getLowestThinkingLevel, getNextThinkingLevel, getSessionId, getSupportedThinkingLevels, isClaudeCodeAuthMarker, isKimiCodingEndpoint, isLegacyClaudeOAuth, isLocalModelId, isLoggerOpen, isModelLoaded, isThinkingLevelSupported, localAuthStorageKey, localModelShortName, log, loginChatGPT, loginClaudeCode, logoutClaudeCode, mergeClaudeCodeRateLimit, oauthStorageKey, ollamaServedContext, openLog, parseCallbackInput, parseLocalModelId, parseOpenRouterCatalog, probeEndpoint, providerStorageKeys, readClaudeCodeUsage, readStoredBaseUrlSync, recordClaudeCodeRateLimit, refreshChatGPTToken, registerLogCleanup, requiredKeyLabel, resample, resolveClaudeCodeBinary, revokeChatGPTSignIn, setProgressCallback, signInWithClaudeCode, signOutOfClaudeCode, storageKeyProvider, toModelInfo, transcribeVoice, verifyIdToken, withFileLock };