antigravity-usage 0.2.5 → 0.2.7

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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <a href="https://packagephobia.com/result?p=antigravity-usage"><img src="https://packagephobia.com/badge?p=antigravity-usage" alt="install size" /></a>
9
9
  <a href="https://www.npmjs.com/package/antigravity-usage"><img src="https://img.shields.io/npm/dt/antigravity-usage" alt="NPM Downloads" /></a>
10
10
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /></a>
11
- <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg" alt="Node.js Version" /></a>
11
+ <a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg" alt="Node.js Version" /></a>
12
12
  </p>
13
13
 
14
14
  <p align="center">
@@ -49,6 +49,13 @@ Want to check quota for **multiple accounts** or when your IDE is closed?
49
49
  antigravity-usage login
50
50
  ```
51
51
 
52
+ ### 1a. Manual Login (Headless/SSH)
53
+ If you are on a headless server or cannot open a browser locally:
54
+ ```bash
55
+ antigravity-usage login --manual
56
+ ```
57
+ Follow the on-screen instructions to paste the authentication URL into your local browser and copy the result back.
58
+
52
59
  ### 2. Add more accounts
53
60
  ```bash
54
61
  antigravity-usage accounts add
@@ -109,8 +116,14 @@ To keep the CLI snappy and avoid hitting API rate limits:
109
116
  antigravity-usage quota --refresh
110
117
  ```
111
118
 
112
- ### 📱 Responsive UI
113
- Tables automatically adapt to your terminal size, switching between "Compact" and "Spacious" views to show you the most relevant data without wrapping.
119
+
120
+ ### 🎯 Focused Model View
121
+ By default, `antigravity-usage` hides "autocomplete" models (like `gemini-2.5-flash-002`) to reduce clutter, as these typically share quota with their main counterparts or are less relevant for tracking.
122
+
123
+ To see **ALL** available models, including autocomplete ones:
124
+ ```bash
125
+ antigravity-usage quota --all-models
126
+ ```
114
127
 
115
128
  ---
116
129
 
@@ -124,6 +137,7 @@ antigravity-usage # Auto-detect (Local -> Cloud)
124
137
  antigravity-usage --all # Fetch ALL accounts
125
138
  antigravity-usage --method local # Force local IDE connection
126
139
  antigravity-usage --method google # Force google IDE connection
140
+ antigravity-usage --all-models # Show ALL models (including autocomplete)
127
141
  antigravity-usage --json # Output JSON for scripts
128
142
  antigravity-usage --version # Show version number
129
143
  ```
package/dist/index.js CHANGED
@@ -48,6 +48,7 @@ function success(message) {
48
48
  import { createServer } from "http";
49
49
  import { URL as URL2, URLSearchParams } from "url";
50
50
  import open from "open";
51
+ import inquirer from "inquirer";
51
52
 
52
53
  // src/accounts/types.ts
53
54
  var DEFAULT_CONFIG = {
@@ -722,6 +723,33 @@ async function resolveProjectId(accessToken) {
722
723
  return { projectId: void 0, tierId: void 0 };
723
724
  }
724
725
  }
726
+ async function completeLogin(code, redirectUri) {
727
+ const tokenResponse = await exchangeCodeForTokens(code, redirectUri);
728
+ const email = await getUserEmail(tokenResponse.access_token);
729
+ let projectId;
730
+ try {
731
+ const projectResult = await resolveProjectId(tokenResponse.access_token);
732
+ projectId = projectResult.projectId;
733
+ if (projectId) {
734
+ debug("oauth", `Project ID resolved: ${projectId}`);
735
+ } else {
736
+ debug("oauth", "No project ID obtained (will fetch on demand)");
737
+ }
738
+ } catch (err) {
739
+ debug("oauth", "Failed to resolve project ID during login (will fetch on demand)", err);
740
+ }
741
+ const tokens = {
742
+ accessToken: tokenResponse.access_token,
743
+ refreshToken: tokenResponse.refresh_token || "",
744
+ expiresAt: Date.now() + tokenResponse.expires_in * 1e3,
745
+ email,
746
+ projectId
747
+ };
748
+ if (email) {
749
+ getAccountManager().addAccount(tokens, email);
750
+ }
751
+ return { success: true, email };
752
+ }
725
753
  async function startOAuthFlow(options = {}) {
726
754
  const port = await getAvailablePort(options.port);
727
755
  const redirectUri = `http://127.0.0.1:${port}/callback`;
@@ -737,6 +765,43 @@ async function startOAuthFlow(options = {}) {
737
765
  state
738
766
  });
739
767
  const authUrl = `${OAUTH_CONFIG.authUrl}?${authParams.toString()}`;
768
+ if (options.manual) {
769
+ info("");
770
+ info("MANUAL LOGIN MODE");
771
+ info("1. Copy this URL and open it in your browser:");
772
+ info(authUrl);
773
+ info("");
774
+ info("2. Login with your Google account.");
775
+ info("3. You will be redirected to a localhost URL (which may fail to load).");
776
+ info("4. Copy that ENTIRE localhost URL and paste it below.");
777
+ info("");
778
+ const { pastedUrl } = await inquirer.prompt([
779
+ {
780
+ type: "input",
781
+ name: "pastedUrl",
782
+ message: "Paste the full redirect URL here:",
783
+ validate: (input) => input.trim().length > 0 ? true : "Please paste the URL"
784
+ }
785
+ ]);
786
+ try {
787
+ const url = new URL2(pastedUrl.trim());
788
+ const code = url.searchParams.get("code");
789
+ const returnedState = url.searchParams.get("state");
790
+ const errorParam = url.searchParams.get("error");
791
+ if (errorParam) {
792
+ return { success: false, error: errorParam };
793
+ }
794
+ if (!code || returnedState !== state) {
795
+ return { success: false, error: "Invalid URL: Missing code or state mismatch" };
796
+ }
797
+ return await completeLogin(code, redirectUri);
798
+ } catch (err) {
799
+ if (err instanceof Error) {
800
+ return { success: false, error: err.message };
801
+ }
802
+ return { success: false, error: "Invalid URL format" };
803
+ }
804
+ }
740
805
  return new Promise((resolve) => {
741
806
  let resolved = false;
742
807
  const server = createServer(async (req, res) => {
@@ -763,43 +828,20 @@ async function startOAuthFlow(options = {}) {
763
828
  return;
764
829
  }
765
830
  try {
766
- const tokenResponse = await exchangeCodeForTokens(code, redirectUri);
767
- const email = await getUserEmail(tokenResponse.access_token);
768
- let projectId;
769
- try {
770
- const projectResult = await resolveProjectId(tokenResponse.access_token);
771
- projectId = projectResult.projectId;
772
- if (projectId) {
773
- debug("oauth", `Project ID resolved: ${projectId}`);
774
- } else {
775
- debug("oauth", "No project ID obtained (will fetch on demand)");
776
- }
777
- } catch (err) {
778
- debug("oauth", "Failed to resolve project ID during login (will fetch on demand)", err);
779
- }
780
- const tokens = {
781
- accessToken: tokenResponse.access_token,
782
- refreshToken: tokenResponse.refresh_token || "",
783
- expiresAt: Date.now() + tokenResponse.expires_in * 1e3,
784
- email,
785
- projectId
786
- };
787
- if (email) {
788
- getAccountManager().addAccount(tokens, email);
789
- }
831
+ const result = await completeLogin(code, redirectUri);
790
832
  res.writeHead(200, { "Content-Type": "text/html" });
791
833
  res.end(`
792
834
  <html>
793
835
  <body style="font-family: system-ui; padding: 40px; text-align: center;">
794
836
  <h1>Login Successful!</h1>
795
- <p>You are now logged in${email ? ` as <strong>${email}</strong>` : ""}.</p>
837
+ <p>You are now logged in${result.email ? ` as <strong>${result.email}</strong>` : ""}.</p>
796
838
  <p>You can close this window and return to the terminal.</p>
797
839
  </body>
798
840
  </html>
799
841
  `);
800
842
  resolved = true;
801
843
  server.close();
802
- resolve({ success: true, email });
844
+ resolve(result);
803
845
  } catch (err) {
804
846
  res.writeHead(500, { "Content-Type": "text/html" });
805
847
  res.end("<html><body><h1>Login Failed</h1><p>Token exchange failed.</p></body></html>");
@@ -1203,7 +1245,8 @@ async function loginCommand(options) {
1203
1245
  }
1204
1246
  const result = await startOAuthFlow({
1205
1247
  noBrowser: options.noBrowser,
1206
- port: options.port
1248
+ port: options.port,
1249
+ manual: options.manual
1207
1250
  });
1208
1251
  if (result.success) {
1209
1252
  resetTokenManager();
@@ -1730,7 +1773,8 @@ function parseModelInfo(modelId, model) {
1730
1773
  remainingPercentage: quotaInfo?.remainingFraction,
1731
1774
  isExhausted: quotaInfo?.isExhausted ?? quotaInfo?.remainingFraction === 0,
1732
1775
  resetTime: quotaInfo?.resetTime,
1733
- timeUntilResetMs: parseResetTime(quotaInfo?.resetTime)
1776
+ timeUntilResetMs: parseResetTime(quotaInfo?.resetTime),
1777
+ isAutocompleteOnly: modelId.includes("gemini-2.5") || (model.displayName || "").includes("Gemini 2.5")
1734
1778
  };
1735
1779
  }
1736
1780
  function parsePromptCredits(response) {
@@ -2366,7 +2410,8 @@ function parseModelQuota(model) {
2366
2410
  remainingPercentage: quota?.remainingPercentage,
2367
2411
  isExhausted: model.isExhausted ?? quota?.remainingPercentage === 0,
2368
2412
  resetTime: quota?.resetTime,
2369
- timeUntilResetMs: quota?.timeUntilResetMs
2413
+ timeUntilResetMs: quota?.timeUntilResetMs,
2414
+ isAutocompleteOnly: model.modelId.includes("gemini-2.5") || (model.label || "").includes("Gemini 2.5") || (model.displayName || "").includes("Gemini 2.5")
2370
2415
  };
2371
2416
  }
2372
2417
 
@@ -2465,7 +2510,7 @@ function formatRemaining(model) {
2465
2510
  if (pct >= 25) return `\u{1F7E0} ${pct}%`;
2466
2511
  return `\u{1F534} ${pct}%`;
2467
2512
  }
2468
- function printQuotaTable(snapshot) {
2513
+ function printQuotaTable(snapshot, options = {}) {
2469
2514
  const timestamp = new Date(snapshot.timestamp).toLocaleString();
2470
2515
  console.log();
2471
2516
  console.log(`\u{1F4CA} Antigravity Quota Status (via ${snapshot.method.toUpperCase()})`);
@@ -2480,8 +2525,8 @@ function printQuotaTable(snapshot) {
2480
2525
  }
2481
2526
  console.log(` ${userParts.join(" | ")}`);
2482
2527
  }
2483
- console.log();
2484
- if (snapshot.models.length > 0) {
2528
+ const visibleModels = options.allModels ? snapshot.models : snapshot.models.filter((m) => !m.isAutocompleteOnly);
2529
+ if (visibleModels.length > 0) {
2485
2530
  const table = new Table2({
2486
2531
  head: ["Model", "Remaining", "Resets In"],
2487
2532
  style: {
@@ -2489,7 +2534,7 @@ function printQuotaTable(snapshot) {
2489
2534
  border: ["gray"]
2490
2535
  }
2491
2536
  });
2492
- for (const model of snapshot.models) {
2537
+ for (const model of visibleModels) {
2493
2538
  table.push([
2494
2539
  model.label,
2495
2540
  formatRemaining(model),
@@ -2499,6 +2544,9 @@ function printQuotaTable(snapshot) {
2499
2544
  console.log(table.toString());
2500
2545
  } else {
2501
2546
  console.log("No model quota information available.");
2547
+ if (!options.allModels && snapshot.models.some((m) => m.isAutocompleteOnly)) {
2548
+ console.log("Tip: Use --all-models to see autocomplete models.");
2549
+ }
2502
2550
  }
2503
2551
  console.log();
2504
2552
  }
@@ -2575,13 +2623,16 @@ function renderAccountsTable(accounts) {
2575
2623
  }
2576
2624
  function formatQuotaRemainingBar(remainingPercentage) {
2577
2625
  const width = 10;
2578
- const filled = Math.round(remainingPercentage / 100 * width);
2579
- const empty = width - filled;
2580
2626
  const filledChar = "\u2588";
2581
2627
  const emptyChar = "\u2591";
2628
+ if (remainingPercentage === void 0) {
2629
+ return `${emptyChar.repeat(width)} N/A`;
2630
+ }
2631
+ const filled = Math.round(remainingPercentage / 100 * width);
2632
+ const empty = width - filled;
2582
2633
  return `${filledChar.repeat(filled)}${emptyChar.repeat(empty)} ${Math.round(remainingPercentage)}%`;
2583
2634
  }
2584
- function renderAllQuotaTable(results) {
2635
+ function renderAllQuotaTable(results, options = {}) {
2585
2636
  if (results.length === 0) {
2586
2637
  console.log("\n\u{1F4ED} No accounts found.");
2587
2638
  console.log("\n\u{1F4A1} Run `antigravity-usage login` to add an account.\n");
@@ -2592,7 +2643,8 @@ function renderAllQuotaTable(results) {
2592
2643
  if (a.status !== "error" && b.status === "error") return -1;
2593
2644
  if (a.status === "error" && b.status === "error") return 0;
2594
2645
  const getRemaining = (result) => {
2595
- const firstModel = result.snapshot?.models?.[0];
2646
+ const models = options.allModels ? result.snapshot?.models : result.snapshot?.models?.filter((m) => !m.isAutocompleteOnly);
2647
+ const firstModel = models?.[0];
2596
2648
  if (!firstModel) return -1;
2597
2649
  if (firstModel.isExhausted) return 0;
2598
2650
  return firstModel.remainingPercentage ?? -1;
@@ -2643,15 +2695,17 @@ function renderAllQuotaTable(results) {
2643
2695
  credits = `${pc.available} / ${pc.monthly}`;
2644
2696
  }
2645
2697
  let quotaRemaining = "-";
2646
- if (snapshot?.models && snapshot.models.length > 0) {
2647
- const minRemaining = Math.min(
2648
- ...snapshot.models.filter((m) => m.remainingPercentage !== void 0).map((m) => m.remainingPercentage)
2649
- );
2650
- if (isFinite(minRemaining)) {
2651
- const remainingPct = minRemaining * 100;
2652
- quotaRemaining = formatQuotaRemainingBar(remainingPct);
2653
- } else if (snapshot.models.some((m) => m.isExhausted)) {
2698
+ const models = snapshot?.models || [];
2699
+ const relevantModels = options.allModels ? models : models.filter((m) => !m.isAutocompleteOnly);
2700
+ if (relevantModels.length > 0) {
2701
+ const percentages = relevantModels.filter((m) => m.remainingPercentage !== void 0).map((m) => m.remainingPercentage);
2702
+ if (percentages.length > 0) {
2703
+ const minRemaining = Math.min(...percentages);
2704
+ quotaRemaining = formatQuotaRemainingBar(minRemaining * 100);
2705
+ } else if (relevantModels.some((m) => m.isExhausted)) {
2654
2706
  quotaRemaining = "\u274C EXHAUSTED";
2707
+ } else {
2708
+ quotaRemaining = formatQuotaRemainingBar(void 0);
2655
2709
  }
2656
2710
  }
2657
2711
  table.push([
@@ -2713,7 +2767,7 @@ async function fetchSingleAccountQuota(options) {
2713
2767
  if (options.json) {
2714
2768
  printQuotaJson(snapshot);
2715
2769
  } else {
2716
- printQuotaTable(snapshot);
2770
+ printQuotaTable(snapshot, { allModels: options.allModels });
2717
2771
  }
2718
2772
  } finally {
2719
2773
  if (accountSwitched && originalActiveEmail) {
@@ -2787,7 +2841,7 @@ async function fetchAllAccountsQuota(options) {
2787
2841
  if (options.json) {
2788
2842
  console.log(JSON.stringify(results, null, 2));
2789
2843
  } else {
2790
- renderAllQuotaTable(results);
2844
+ renderAllQuotaTable(results, { allModels: options.allModels });
2791
2845
  }
2792
2846
  }
2793
2847
  async function fetchQuotaForAccount(email, method) {
@@ -3137,7 +3191,7 @@ async function accountsCommand(subcommand, args, options) {
3137
3191
  }
3138
3192
 
3139
3193
  // src/commands/wakeup.ts
3140
- import inquirer from "inquirer";
3194
+ import inquirer2 from "inquirer";
3141
3195
  import Table4 from "cli-table3";
3142
3196
 
3143
3197
  // src/wakeup/types.ts
@@ -3835,7 +3889,7 @@ async function configureWakeup() {
3835
3889
  console.log(" antigravity-usage login\n");
3836
3890
  return;
3837
3891
  }
3838
- const { enabled } = await inquirer.prompt([{
3892
+ const { enabled } = await inquirer2.prompt([{
3839
3893
  type: "confirm",
3840
3894
  name: "enabled",
3841
3895
  message: "Enable auto wake-up?",
@@ -3847,7 +3901,7 @@ async function configureWakeup() {
3847
3901
  console.log("\n\u2705 Auto wake-up disabled");
3848
3902
  return;
3849
3903
  }
3850
- const { triggerMode } = await inquirer.prompt([{
3904
+ const { triggerMode } = await inquirer2.prompt([{
3851
3905
  type: "list",
3852
3906
  name: "triggerMode",
3853
3907
  message: "Trigger mode:",
@@ -3859,7 +3913,7 @@ async function configureWakeup() {
3859
3913
  }]);
3860
3914
  config.wakeOnReset = triggerMode === "reset";
3861
3915
  if (!config.wakeOnReset) {
3862
- const { scheduleMode } = await inquirer.prompt([{
3916
+ const { scheduleMode } = await inquirer2.prompt([{
3863
3917
  type: "list",
3864
3918
  name: "scheduleMode",
3865
3919
  message: "Schedule type:",
@@ -3872,7 +3926,7 @@ async function configureWakeup() {
3872
3926
  }]);
3873
3927
  config.scheduleMode = scheduleMode;
3874
3928
  if (scheduleMode === "interval") {
3875
- const { intervalHours } = await inquirer.prompt([{
3929
+ const { intervalHours } = await inquirer2.prompt([{
3876
3930
  type: "number",
3877
3931
  name: "intervalHours",
3878
3932
  message: "Trigger every N hours:",
@@ -3881,7 +3935,7 @@ async function configureWakeup() {
3881
3935
  }]);
3882
3936
  config.intervalHours = intervalHours;
3883
3937
  } else if (scheduleMode === "daily") {
3884
- const { dailyTime } = await inquirer.prompt([{
3938
+ const { dailyTime } = await inquirer2.prompt([{
3885
3939
  type: "input",
3886
3940
  name: "dailyTime",
3887
3941
  message: "Time to trigger (HH:MM):",
@@ -3890,7 +3944,7 @@ async function configureWakeup() {
3890
3944
  }]);
3891
3945
  config.dailyTimes = [dailyTime];
3892
3946
  } else if (scheduleMode === "custom") {
3893
- const { cronExpression } = await inquirer.prompt([{
3947
+ const { cronExpression } = await inquirer2.prompt([{
3894
3948
  type: "input",
3895
3949
  name: "cronExpression",
3896
3950
  message: "Cron expression (min hour day month weekday):",
@@ -3899,7 +3953,7 @@ async function configureWakeup() {
3899
3953
  config.cronExpression = cronExpression;
3900
3954
  }
3901
3955
  } else {
3902
- const { resetCooldown } = await inquirer.prompt([{
3956
+ const { resetCooldown } = await inquirer2.prompt([{
3903
3957
  type: "number",
3904
3958
  name: "resetCooldown",
3905
3959
  message: "Cooldown between triggers (minutes):",
@@ -3912,7 +3966,7 @@ async function configureWakeup() {
3912
3966
  console.log("\n \u{1F4E6} Models: claude-sonnet-4-5, gemini-3-flash, gemini-3-pro-low");
3913
3967
  console.log(" (Triggers both Claude and Gemini families)");
3914
3968
  if (accounts.length > 1) {
3915
- const { selectedAccounts } = await inquirer.prompt([{
3969
+ const { selectedAccounts } = await inquirer2.prompt([{
3916
3970
  type: "checkbox",
3917
3971
  name: "selectedAccounts",
3918
3972
  message: "Select accounts to use:",
@@ -3926,14 +3980,14 @@ async function configureWakeup() {
3926
3980
  } else {
3927
3981
  config.selectedAccounts = void 0;
3928
3982
  }
3929
- const { customPrompt } = await inquirer.prompt([{
3983
+ const { customPrompt } = await inquirer2.prompt([{
3930
3984
  type: "input",
3931
3985
  name: "customPrompt",
3932
3986
  message: 'Custom wake-up prompt (leave empty for default "hi"):',
3933
3987
  default: config.customPrompt || ""
3934
3988
  }]);
3935
3989
  config.customPrompt = customPrompt || void 0;
3936
- const { maxTokens } = await inquirer.prompt([{
3990
+ const { maxTokens } = await inquirer2.prompt([{
3937
3991
  type: "number",
3938
3992
  name: "maxTokens",
3939
3993
  message: "Max output tokens (0 = no limit):",
@@ -3947,7 +4001,7 @@ async function configureWakeup() {
3947
4001
  console.log(` Models: ${config.selectedModels.join(", ")}`);
3948
4002
  console.log(` Accounts: ${config.selectedAccounts?.join(", ") || "Active account"}`);
3949
4003
  if (!config.wakeOnReset && isCronSupported()) {
3950
- const { installNow } = await inquirer.prompt([{
4004
+ const { installNow } = await inquirer2.prompt([{
3951
4005
  type: "confirm",
3952
4006
  name: "installNow",
3953
4007
  message: "Install to system cron now?",
@@ -4059,7 +4113,7 @@ async function runTestTrigger() {
4059
4113
  }
4060
4114
  let accountEmail = accounts[0];
4061
4115
  if (accounts.length > 1) {
4062
- const { selectedAccount } = await inquirer.prompt([{
4116
+ const { selectedAccount } = await inquirer2.prompt([{
4063
4117
  type: "list",
4064
4118
  name: "selectedAccount",
4065
4119
  message: "Select account:",
@@ -4068,13 +4122,13 @@ async function runTestTrigger() {
4068
4122
  accountEmail = selectedAccount;
4069
4123
  }
4070
4124
  const config = loadWakeupConfig();
4071
- const { modelId } = await inquirer.prompt([{
4125
+ const { modelId } = await inquirer2.prompt([{
4072
4126
  type: "input",
4073
4127
  name: "modelId",
4074
4128
  message: "Model ID to test:",
4075
4129
  default: config?.selectedModels[0] || "claude-sonnet-4-5"
4076
4130
  }]);
4077
- const { prompt } = await inquirer.prompt([{
4131
+ const { prompt } = await inquirer2.prompt([{
4078
4132
  type: "input",
4079
4133
  name: "prompt",
4080
4134
  message: "Test prompt:",
@@ -4195,10 +4249,10 @@ program.name("antigravity-usage").description("CLI tool to check Antigravity mod
4195
4249
  setDebugMode(true);
4196
4250
  }
4197
4251
  });
4198
- program.command("login").description("Authenticate with Google (adds a new account)").option("--no-browser", "Do not open browser, print URL instead").option("-p, --port <port>", "Port for OAuth callback server", parseInt).action(loginCommand);
4252
+ program.command("login").description("Authenticate with Google (adds a new account)").option("--no-browser", "Do not open browser, print URL instead").option("--manual", "Manual login flow (copy-paste URL)").option("-p, --port <port>", "Port for OAuth callback server", parseInt).action(loginCommand);
4199
4253
  program.command("logout [email]").description("Remove stored credentials").option("--all", "Logout from all accounts").action((email, options) => logoutCommand(options, email));
4200
4254
  program.command("status").description("Show current authentication status").option("--all", "Show status for all accounts").option("-a, --account <email>", "Show status for specific account").action(statusCommand);
4201
- program.command("quota", { isDefault: true }).description("Fetch and display quota information").option("--json", "Output as JSON").option("-m, --method <method>", "Method to use: auto (default), local, or google", "auto").option("--all", "Show quota for all accounts").option("-a, --account <email>", "Show quota for specific account").option("--refresh", "Force refresh (ignore cache)").action(quotaCommand);
4255
+ program.command("quota", { isDefault: true }).description("Fetch and display quota information").option("--json", "Output as JSON").option("-m, --method <method>", "Method to use: auto (default), local, or google", "auto").option("--all", "Show quota for all accounts").option("-a, --account <email>", "Show quota for specific account").option("--refresh", "Force refresh (ignore cache)").option("--all-models", "Include autocomplete models (Gemini 2.5) in quota display").action(quotaCommand);
4202
4256
  var accountsCmd = program.command("accounts").description("Manage multiple accounts");
4203
4257
  accountsCmd.command("list").description("List all accounts").option("--refresh", "Show refresh tip").action((options) => accountsCommand("list", [], options));
4204
4258
  accountsCmd.command("add").description("Add a new account (triggers OAuth login)").action(() => accountsCommand("add", [], {}));