opencode-antigravity-auth 1.4.2 → 1.4.3-beta.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/README.md +56 -54
- package/dist/src/plugin/accounts.d.ts +46 -3
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +187 -8
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/config/loader.d.ts.map +1 -1
- package/dist/src/plugin/config/loader.js +7 -4
- package/dist/src/plugin/config/loader.js.map +1 -1
- package/dist/src/plugin/config/schema.d.ts +19 -0
- package/dist/src/plugin/config/schema.d.ts.map +1 -1
- package/dist/src/plugin/config/schema.js +55 -0
- package/dist/src/plugin/config/schema.js.map +1 -1
- package/dist/src/plugin/debug.d.ts +26 -0
- package/dist/src/plugin/debug.d.ts.map +1 -1
- package/dist/src/plugin/debug.js +69 -1
- package/dist/src/plugin/debug.js.map +1 -1
- package/dist/src/plugin/quota.d.ts +10 -0
- package/dist/src/plugin/quota.d.ts.map +1 -1
- package/dist/src/plugin/quota.js +88 -7
- package/dist/src/plugin/quota.js.map +1 -1
- package/dist/src/plugin/recovery.d.ts.map +1 -1
- package/dist/src/plugin/recovery.js +2 -0
- package/dist/src/plugin/recovery.js.map +1 -1
- package/dist/src/plugin/request.d.ts.map +1 -1
- package/dist/src/plugin/request.js +30 -15
- package/dist/src/plugin/request.js.map +1 -1
- package/dist/src/plugin/storage.d.ts +19 -0
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +44 -4
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/transform/model-resolver.d.ts +7 -9
- package/dist/src/plugin/transform/model-resolver.d.ts.map +1 -1
- package/dist/src/plugin/transform/model-resolver.js +12 -37
- package/dist/src/plugin/transform/model-resolver.js.map +1 -1
- package/dist/src/plugin/transform/types.d.ts +1 -1
- package/dist/src/plugin/transform/types.d.ts.map +1 -1
- package/dist/src/plugin.d.ts +7 -0
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/plugin.js +262 -50
- package/dist/src/plugin.js.map +1 -1
- package/package.json +2 -2
package/dist/src/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
|
-
import { ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_PROVIDER_ID } from "./constants";
|
|
3
|
+
import { ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_PROD, ANTIGRAVITY_PROVIDER_ID } from "./constants";
|
|
4
4
|
import { authorizeAntigravity, exchangeAntigravity } from "./antigravity/oauth";
|
|
5
5
|
import { accessTokenExpired, isOAuthAuth, parseRefreshParts } from "./plugin/auth";
|
|
6
6
|
import { promptAddAnotherAccount, promptLoginMode, promptProjectId } from "./plugin/cli";
|
|
@@ -13,7 +13,7 @@ import { EmptyResponseError } from "./plugin/errors";
|
|
|
13
13
|
import { AntigravityTokenRefreshError, refreshAccessToken } from "./plugin/token";
|
|
14
14
|
import { startOAuthListener } from "./plugin/server";
|
|
15
15
|
import { clearAccounts, loadAccounts, saveAccounts } from "./plugin/storage";
|
|
16
|
-
import { AccountManager, parseRateLimitReason, calculateBackoffMs } from "./plugin/accounts";
|
|
16
|
+
import { AccountManager, parseRateLimitReason, calculateBackoffMs, computeSoftQuotaCacheTtlMs } from "./plugin/accounts";
|
|
17
17
|
import { createAutoUpdateCheckerHook } from "./hooks/auto-update-checker";
|
|
18
18
|
import { loadConfig, initRuntimeConfig } from "./plugin/config";
|
|
19
19
|
import { createSessionRecoveryHook, getRecoverySuccessToast } from "./plugin/recovery";
|
|
@@ -33,13 +33,18 @@ function getCapacityBackoffDelay(consecutiveFailures) {
|
|
|
33
33
|
}
|
|
34
34
|
const warmupAttemptedSessionIds = new Set();
|
|
35
35
|
const warmupSucceededSessionIds = new Set();
|
|
36
|
+
// Track if this plugin instance is running in a child session (subagent, background task)
|
|
37
|
+
// Used to filter toasts based on toast_scope config
|
|
38
|
+
let isChildSession = false;
|
|
39
|
+
let childSessionParentID = undefined;
|
|
36
40
|
const log = createLogger("plugin");
|
|
37
41
|
// Module-level toast debounce to persist across requests (fixes toast spam)
|
|
38
42
|
const rateLimitToastCooldowns = new Map();
|
|
39
43
|
const RATE_LIMIT_TOAST_COOLDOWN_MS = 5000;
|
|
40
44
|
const MAX_TOAST_COOLDOWN_ENTRIES = 100;
|
|
41
|
-
// Track if "all accounts
|
|
42
|
-
let
|
|
45
|
+
// Track if "all accounts blocked" toasts were shown to prevent spam in while loop
|
|
46
|
+
let softQuotaToastShown = false;
|
|
47
|
+
let rateLimitToastShown = false;
|
|
43
48
|
function cleanupToastCooldowns() {
|
|
44
49
|
if (rateLimitToastCooldowns.size > MAX_TOAST_COOLDOWN_ENTRIES) {
|
|
45
50
|
const now = Date.now();
|
|
@@ -61,8 +66,47 @@ function shouldShowRateLimitToast(message) {
|
|
|
61
66
|
rateLimitToastCooldowns.set(toastKey, now);
|
|
62
67
|
return true;
|
|
63
68
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
69
|
+
function resetAllAccountsBlockedToasts() {
|
|
70
|
+
softQuotaToastShown = false;
|
|
71
|
+
rateLimitToastShown = false;
|
|
72
|
+
}
|
|
73
|
+
const quotaRefreshInProgressByEmail = new Set();
|
|
74
|
+
async function triggerAsyncQuotaRefreshForAccount(accountManager, accountIndex, client, providerId, intervalMinutes) {
|
|
75
|
+
if (intervalMinutes <= 0)
|
|
76
|
+
return;
|
|
77
|
+
const accounts = accountManager.getAccounts();
|
|
78
|
+
const account = accounts[accountIndex];
|
|
79
|
+
if (!account || account.enabled === false)
|
|
80
|
+
return;
|
|
81
|
+
const accountKey = account.email ?? `idx-${accountIndex}`;
|
|
82
|
+
if (quotaRefreshInProgressByEmail.has(accountKey))
|
|
83
|
+
return;
|
|
84
|
+
const intervalMs = intervalMinutes * 60 * 1000;
|
|
85
|
+
const age = account.cachedQuotaUpdatedAt != null
|
|
86
|
+
? Date.now() - account.cachedQuotaUpdatedAt
|
|
87
|
+
: Infinity;
|
|
88
|
+
if (age < intervalMs)
|
|
89
|
+
return;
|
|
90
|
+
quotaRefreshInProgressByEmail.add(accountKey);
|
|
91
|
+
try {
|
|
92
|
+
const accountsForCheck = accountManager.getAccountsForQuotaCheck();
|
|
93
|
+
const singleAccount = accountsForCheck[accountIndex];
|
|
94
|
+
if (!singleAccount) {
|
|
95
|
+
quotaRefreshInProgressByEmail.delete(accountKey);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const results = await checkAccountsQuota([singleAccount], client, providerId);
|
|
99
|
+
if (results[0]?.status === "ok" && results[0]?.quota?.groups) {
|
|
100
|
+
accountManager.updateQuotaCache(accountIndex, results[0].quota.groups);
|
|
101
|
+
accountManager.requestSaveToDisk();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
log.debug(`quota-refresh-failed email=${accountKey}`, { error: String(err) });
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
quotaRefreshInProgressByEmail.delete(accountKey);
|
|
109
|
+
}
|
|
66
110
|
}
|
|
67
111
|
function trackWarmupAttempt(sessionId) {
|
|
68
112
|
if (warmupSucceededSessionIds.has(sessionId)) {
|
|
@@ -650,6 +694,22 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
650
694
|
const eventHandler = async (input) => {
|
|
651
695
|
// Forward to update checker
|
|
652
696
|
await updateChecker.event(input);
|
|
697
|
+
// Track if this is a child session (subagent, background task)
|
|
698
|
+
// This is used to filter toasts based on toast_scope config
|
|
699
|
+
if (input.event.type === "session.created") {
|
|
700
|
+
const props = input.event.properties;
|
|
701
|
+
if (props?.info?.parentID) {
|
|
702
|
+
isChildSession = true;
|
|
703
|
+
childSessionParentID = props.info.parentID;
|
|
704
|
+
log.debug("child-session-detected", { parentID: props.info.parentID });
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
// Reset for root sessions - important when plugin instance is reused
|
|
708
|
+
isChildSession = false;
|
|
709
|
+
childSessionParentID = undefined;
|
|
710
|
+
log.debug("root-session-detected", {});
|
|
711
|
+
}
|
|
712
|
+
}
|
|
653
713
|
// Handle session recovery
|
|
654
714
|
if (sessionRecovery && input.event.type === "session.error") {
|
|
655
715
|
const props = input.event.properties;
|
|
@@ -674,15 +734,18 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
674
734
|
body: { parts: [{ type: "text", text: config.resume_text }] },
|
|
675
735
|
query: { directory },
|
|
676
736
|
}).catch(() => { });
|
|
677
|
-
// Show success toast
|
|
737
|
+
// Show success toast (respects toast_scope for child sessions)
|
|
678
738
|
const successToast = getRecoverySuccessToast();
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
739
|
+
log.debug("recovery-toast", { ...successToast, isChildSession, toastScope: config.toast_scope });
|
|
740
|
+
if (!(config.toast_scope === "root_only" && isChildSession)) {
|
|
741
|
+
await client.tui.showToast({
|
|
742
|
+
body: {
|
|
743
|
+
title: successToast.title,
|
|
744
|
+
message: successToast.message,
|
|
745
|
+
variant: "success",
|
|
746
|
+
},
|
|
747
|
+
}).catch(() => { });
|
|
748
|
+
}
|
|
686
749
|
}
|
|
687
750
|
}
|
|
688
751
|
}
|
|
@@ -822,12 +885,20 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
822
885
|
// Use while(true) loop to handle rate limits with backoff
|
|
823
886
|
// This ensures we wait and retry when all accounts are rate-limited
|
|
824
887
|
const quietMode = config.quiet_mode;
|
|
825
|
-
|
|
888
|
+
const toastScope = config.toast_scope;
|
|
889
|
+
// Helper to show toast without blocking on abort (respects quiet_mode and toast_scope)
|
|
826
890
|
const showToast = async (message, variant) => {
|
|
891
|
+
// Always log to debug regardless of toast filtering
|
|
892
|
+
log.debug("toast", { message, variant, isChildSession, toastScope });
|
|
827
893
|
if (quietMode)
|
|
828
894
|
return;
|
|
829
895
|
if (abortSignal?.aborted)
|
|
830
896
|
return;
|
|
897
|
+
// Filter toasts for child sessions when toast_scope is "root_only"
|
|
898
|
+
if (toastScope === "root_only" && isChildSession) {
|
|
899
|
+
log.debug("toast-suppressed-child-session", { message, variant, parentID: childSessionParentID });
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
831
902
|
if (variant === "warning" && message.toLowerCase().includes("rate")) {
|
|
832
903
|
if (!shouldShowRateLimitToast(message)) {
|
|
833
904
|
return;
|
|
@@ -845,8 +916,8 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
845
916
|
const hasOtherAccountWithAntigravity = (currentAccount) => {
|
|
846
917
|
if (family !== "gemini")
|
|
847
918
|
return false;
|
|
848
|
-
|
|
849
|
-
return
|
|
919
|
+
// Use AccountManager method which properly checks for disabled/cooling-down accounts
|
|
920
|
+
return accountManager.hasOtherAccountWithAntigravityAvailable(currentAccount.index, family, model);
|
|
850
921
|
};
|
|
851
922
|
while (true) {
|
|
852
923
|
// Check for abort at the start of each iteration
|
|
@@ -855,8 +926,29 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
855
926
|
if (accountCount === 0) {
|
|
856
927
|
throw new Error("No Antigravity accounts available. Run `opencode auth login`.");
|
|
857
928
|
}
|
|
858
|
-
const
|
|
929
|
+
const softQuotaCacheTtlMs = computeSoftQuotaCacheTtlMs(config.soft_quota_cache_ttl_minutes, config.quota_refresh_interval_minutes);
|
|
930
|
+
const account = accountManager.getCurrentOrNextForFamily(family, model, config.account_selection_strategy, 'antigravity', config.pid_offset_enabled, config.soft_quota_threshold_percent, softQuotaCacheTtlMs);
|
|
859
931
|
if (!account) {
|
|
932
|
+
if (accountManager.areAllAccountsOverSoftQuota(family, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, model)) {
|
|
933
|
+
const threshold = config.soft_quota_threshold_percent;
|
|
934
|
+
const softQuotaWaitMs = accountManager.getMinWaitTimeForSoftQuota(family, threshold, softQuotaCacheTtlMs, model);
|
|
935
|
+
const maxWaitMs = (config.max_rate_limit_wait_seconds ?? 300) * 1000;
|
|
936
|
+
if (softQuotaWaitMs === null || (maxWaitMs > 0 && softQuotaWaitMs > maxWaitMs)) {
|
|
937
|
+
const waitTimeFormatted = softQuotaWaitMs ? formatWaitTime(softQuotaWaitMs) : "unknown";
|
|
938
|
+
await showToast(`All accounts over ${threshold}% quota threshold. Resets in ${waitTimeFormatted}.`, "error");
|
|
939
|
+
throw new Error(`Quota protection: All ${accountCount} account(s) are over ${threshold}% usage for ${family}. ` +
|
|
940
|
+
`Quota resets in ${waitTimeFormatted}. ` +
|
|
941
|
+
`Add more accounts, wait for quota reset, or set soft_quota_threshold_percent: 100 to disable.`);
|
|
942
|
+
}
|
|
943
|
+
const waitSecValue = Math.max(1, Math.ceil(softQuotaWaitMs / 1000));
|
|
944
|
+
pushDebug(`all-over-soft-quota family=${family} accounts=${accountCount} waitMs=${softQuotaWaitMs}`);
|
|
945
|
+
if (!softQuotaToastShown) {
|
|
946
|
+
await showToast(`All ${accountCount} account(s) over ${threshold}% quota. Waiting ${formatWaitTime(softQuotaWaitMs)}...`, "warning");
|
|
947
|
+
softQuotaToastShown = true;
|
|
948
|
+
}
|
|
949
|
+
await sleep(softQuotaWaitMs, abortSignal);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
860
952
|
const headerStyle = getHeaderStyleFromUrl(urlString, family);
|
|
861
953
|
const explicitQuota = isExplicitQuotaFromUrl(urlString);
|
|
862
954
|
// All accounts are rate-limited - wait and retry
|
|
@@ -882,16 +974,16 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
882
974
|
`Quota resets in ${waitTimeFormatted}. ` +
|
|
883
975
|
`Add more accounts with \`opencode auth login\` or wait and retry.`);
|
|
884
976
|
}
|
|
885
|
-
if (!
|
|
977
|
+
if (!rateLimitToastShown) {
|
|
886
978
|
await showToast(`All ${accountCount} account(s) rate-limited for ${family}. Waiting ${waitSecValue}s...`, "warning");
|
|
887
|
-
|
|
979
|
+
rateLimitToastShown = true;
|
|
888
980
|
}
|
|
889
981
|
// Wait for the rate-limit cooldown to expire, then retry
|
|
890
982
|
await sleep(waitMs, abortSignal);
|
|
891
983
|
continue;
|
|
892
984
|
}
|
|
893
985
|
// Account is available - reset the toast flag
|
|
894
|
-
|
|
986
|
+
resetAllAccountsBlockedToasts();
|
|
895
987
|
pushDebug(`selected idx=${account.index} email=${account.email ?? ""} family=${family} accounts=${accountCount} strategy=${config.account_selection_strategy}`);
|
|
896
988
|
if (isDebugEnabled()) {
|
|
897
989
|
logAccountContext("Selected", {
|
|
@@ -1054,9 +1146,8 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1054
1146
|
// Try endpoint fallbacks with single header style based on model suffix
|
|
1055
1147
|
let shouldSwitchAccount = false;
|
|
1056
1148
|
// Determine header style from model suffix:
|
|
1057
|
-
// -
|
|
1058
|
-
// -
|
|
1059
|
-
// - Claude models -> always use Antigravity
|
|
1149
|
+
// - Gemini models default to Antigravity
|
|
1150
|
+
// - Claude models always use Antigravity
|
|
1060
1151
|
let headerStyle = getHeaderStyleFromUrl(urlString, family);
|
|
1061
1152
|
const explicitQuota = isExplicitQuotaFromUrl(urlString);
|
|
1062
1153
|
pushDebug(`headerStyle=${headerStyle} explicit=${explicitQuota}`);
|
|
@@ -1065,8 +1156,29 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1065
1156
|
}
|
|
1066
1157
|
// Check if this header style is rate-limited for this account
|
|
1067
1158
|
if (accountManager.isRateLimitedForHeaderStyle(account, family, headerStyle, model)) {
|
|
1068
|
-
//
|
|
1069
|
-
if (config.quota_fallback && !explicitQuota && family === "gemini") {
|
|
1159
|
+
// Antigravity-first fallback: exhaust antigravity across ALL accounts before gemini-cli
|
|
1160
|
+
if (config.quota_fallback && !explicitQuota && family === "gemini" && headerStyle === "antigravity") {
|
|
1161
|
+
// Check if ANY other account has antigravity available
|
|
1162
|
+
if (accountManager.hasOtherAccountWithAntigravityAvailable(account.index, family, model)) {
|
|
1163
|
+
// Switch to another account with antigravity (preserve antigravity priority)
|
|
1164
|
+
pushDebug(`antigravity rate-limited on account ${account.index}, but available on other accounts. Switching.`);
|
|
1165
|
+
shouldSwitchAccount = true;
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
// All accounts exhausted antigravity - fall back to gemini-cli on this account
|
|
1169
|
+
const alternateStyle = accountManager.getAvailableHeaderStyle(account, family, model);
|
|
1170
|
+
if (alternateStyle && alternateStyle !== headerStyle) {
|
|
1171
|
+
await showToast(`Antigravity quota exhausted on all accounts. Using Gemini CLI quota.`, "warning");
|
|
1172
|
+
headerStyle = alternateStyle;
|
|
1173
|
+
pushDebug(`all-accounts antigravity exhausted, quota fallback: ${headerStyle}`);
|
|
1174
|
+
}
|
|
1175
|
+
else {
|
|
1176
|
+
shouldSwitchAccount = true;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
else if (config.quota_fallback && !explicitQuota && family === "gemini") {
|
|
1181
|
+
// gemini-cli rate-limited - try alternate style (antigravity) on same account
|
|
1070
1182
|
const alternateStyle = accountManager.getAvailableHeaderStyle(account, family, model);
|
|
1071
1183
|
if (alternateStyle && alternateStyle !== headerStyle) {
|
|
1072
1184
|
const quotaName = headerStyle === "gemini-cli" ? "Gemini CLI" : "Antigravity";
|
|
@@ -1098,6 +1210,12 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1098
1210
|
lastEndpointIndex = i;
|
|
1099
1211
|
}
|
|
1100
1212
|
const currentEndpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i];
|
|
1213
|
+
// Skip sandbox endpoints for Gemini CLI models - they only work with Antigravity quota
|
|
1214
|
+
// Gemini CLI models must use production endpoint (cloudcode-pa.googleapis.com)
|
|
1215
|
+
if (headerStyle === "gemini-cli" && currentEndpoint !== ANTIGRAVITY_ENDPOINT_PROD) {
|
|
1216
|
+
pushDebug(`Skipping sandbox endpoint ${currentEndpoint} for gemini-cli headerStyle`);
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1101
1219
|
try {
|
|
1102
1220
|
const prepared = prepareAntigravityRequest(input, init, accessToken, projectContext.effectiveProjectId, currentEndpoint, headerStyle, forceThinkingRecovery, {
|
|
1103
1221
|
claudeToolHardening: config.claude_tool_hardening,
|
|
@@ -1333,6 +1451,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1333
1451
|
account.consecutiveFailures = 0;
|
|
1334
1452
|
getHealthTracker().recordSuccess(account.index);
|
|
1335
1453
|
accountManager.markAccountUsed(account.index);
|
|
1454
|
+
void triggerAsyncQuotaRefreshForAccount(accountManager, account.index, client, providerId, config.quota_refresh_interval_minutes);
|
|
1336
1455
|
}
|
|
1337
1456
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1338
1457
|
note: response.ok ? "Success" : `Error ${response.status}`,
|
|
@@ -1509,39 +1628,129 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1509
1628
|
});
|
|
1510
1629
|
menuResult = await promptLoginMode(existingAccounts);
|
|
1511
1630
|
if (menuResult.mode === "check") {
|
|
1512
|
-
console.log("\
|
|
1631
|
+
console.log("\n📊 Checking quotas for all accounts...\n");
|
|
1513
1632
|
const results = await checkAccountsQuota(existingStorage.accounts, client, providerId);
|
|
1633
|
+
let storageUpdated = false;
|
|
1514
1634
|
for (const res of results) {
|
|
1515
1635
|
const label = res.email || `Account ${res.index + 1}`;
|
|
1516
1636
|
const disabledStr = res.disabled ? " (disabled)" : "";
|
|
1517
|
-
console.log(
|
|
1637
|
+
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
1638
|
+
console.log(` ${label}${disabledStr}`);
|
|
1639
|
+
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
|
1518
1640
|
if (res.status === "error") {
|
|
1519
|
-
console.log(`
|
|
1641
|
+
console.log(` ❌ Error: ${res.error}\n`);
|
|
1520
1642
|
continue;
|
|
1521
1643
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1644
|
+
// ANSI color codes
|
|
1645
|
+
const colors = {
|
|
1646
|
+
red: '\x1b[31m',
|
|
1647
|
+
orange: '\x1b[33m', // Yellow/orange
|
|
1648
|
+
green: '\x1b[32m',
|
|
1649
|
+
reset: '\x1b[0m',
|
|
1650
|
+
};
|
|
1651
|
+
// Get color based on remaining percentage
|
|
1652
|
+
const getColor = (remaining) => {
|
|
1653
|
+
if (typeof remaining !== 'number')
|
|
1654
|
+
return colors.reset;
|
|
1655
|
+
if (remaining < 0.2)
|
|
1656
|
+
return colors.red;
|
|
1657
|
+
if (remaining < 0.6)
|
|
1658
|
+
return colors.orange;
|
|
1659
|
+
return colors.green;
|
|
1536
1660
|
};
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1661
|
+
// Helper to create colored progress bar
|
|
1662
|
+
const createProgressBar = (remaining, width = 20) => {
|
|
1663
|
+
if (typeof remaining !== 'number')
|
|
1664
|
+
return '░'.repeat(width) + ' ???';
|
|
1665
|
+
const filled = Math.round(remaining * width);
|
|
1666
|
+
const empty = width - filled;
|
|
1667
|
+
const color = getColor(remaining);
|
|
1668
|
+
const bar = `${color}${'█'.repeat(filled)}${colors.reset}${'░'.repeat(empty)}`;
|
|
1669
|
+
const pct = `${color}${Math.round(remaining * 100)}%${colors.reset}`.padStart(4 + color.length + colors.reset.length);
|
|
1670
|
+
return `${bar} ${pct}`;
|
|
1671
|
+
};
|
|
1672
|
+
// Helper to format reset time with days support
|
|
1673
|
+
const formatReset = (resetTime) => {
|
|
1674
|
+
if (!resetTime)
|
|
1675
|
+
return '';
|
|
1676
|
+
const ms = Date.parse(resetTime) - Date.now();
|
|
1677
|
+
if (ms <= 0)
|
|
1678
|
+
return ' (resetting...)';
|
|
1679
|
+
const hours = ms / (1000 * 60 * 60);
|
|
1680
|
+
if (hours >= 24) {
|
|
1681
|
+
const days = Math.floor(hours / 24);
|
|
1682
|
+
const remainingHours = Math.floor(hours % 24);
|
|
1683
|
+
if (remainingHours > 0) {
|
|
1684
|
+
return ` (resets in ${days}d ${remainingHours}h)`;
|
|
1685
|
+
}
|
|
1686
|
+
return ` (resets in ${days}d)`;
|
|
1687
|
+
}
|
|
1688
|
+
return ` (resets in ${formatWaitTime(ms)})`;
|
|
1689
|
+
};
|
|
1690
|
+
// Display Gemini CLI Quota first (as requested - swap order)
|
|
1691
|
+
const hasGeminiCli = res.geminiCliQuota && res.geminiCliQuota.models.length > 0;
|
|
1692
|
+
console.log(`\n ┌─ Gemini CLI Quota`);
|
|
1693
|
+
if (!hasGeminiCli) {
|
|
1694
|
+
const errorMsg = res.geminiCliQuota?.error || "No Gemini CLI quota available";
|
|
1695
|
+
console.log(` │ └─ ${errorMsg}`);
|
|
1696
|
+
}
|
|
1697
|
+
else {
|
|
1698
|
+
const models = res.geminiCliQuota.models;
|
|
1699
|
+
models.forEach((model, idx) => {
|
|
1700
|
+
const isLast = idx === models.length - 1;
|
|
1701
|
+
const connector = isLast ? "└─" : "├─";
|
|
1702
|
+
const bar = createProgressBar(model.remainingFraction);
|
|
1703
|
+
const reset = formatReset(model.resetTime);
|
|
1704
|
+
const modelName = model.modelId.padEnd(29);
|
|
1705
|
+
console.log(` │ ${connector} ${modelName} ${bar}${reset}`);
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
// Display Antigravity Quota second
|
|
1709
|
+
const hasAntigravity = res.quota && Object.keys(res.quota.groups).length > 0;
|
|
1710
|
+
console.log(` │`);
|
|
1711
|
+
console.log(` └─ Antigravity Quota`);
|
|
1712
|
+
if (!hasAntigravity) {
|
|
1713
|
+
const errorMsg = res.quota?.error || "No quota information available";
|
|
1714
|
+
console.log(` └─ ${errorMsg}`);
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
const groups = res.quota.groups;
|
|
1718
|
+
const groupEntries = [
|
|
1719
|
+
{ name: "Claude", data: groups.claude },
|
|
1720
|
+
{ name: "Gemini 3 Pro", data: groups["gemini-pro"] },
|
|
1721
|
+
{ name: "Gemini 3 Flash", data: groups["gemini-flash"] },
|
|
1722
|
+
].filter(g => g.data);
|
|
1723
|
+
groupEntries.forEach((g, idx) => {
|
|
1724
|
+
const isLast = idx === groupEntries.length - 1;
|
|
1725
|
+
const connector = isLast ? "└─" : "├─";
|
|
1726
|
+
const bar = createProgressBar(g.data.remainingFraction);
|
|
1727
|
+
const reset = formatReset(g.data.resetTime);
|
|
1728
|
+
const modelName = g.name.padEnd(29);
|
|
1729
|
+
console.log(` ${connector} ${modelName} ${bar}${reset}`);
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
console.log("");
|
|
1733
|
+
// Cache quota data for soft quota protection
|
|
1734
|
+
if (res.quota?.groups) {
|
|
1735
|
+
const acc = existingStorage.accounts[res.index];
|
|
1736
|
+
if (acc) {
|
|
1737
|
+
acc.cachedQuota = res.quota.groups;
|
|
1738
|
+
acc.cachedQuotaUpdatedAt = Date.now();
|
|
1739
|
+
storageUpdated = true;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1540
1742
|
if (res.updatedAccount) {
|
|
1541
|
-
existingStorage.accounts[res.index] =
|
|
1542
|
-
|
|
1743
|
+
existingStorage.accounts[res.index] = {
|
|
1744
|
+
...res.updatedAccount,
|
|
1745
|
+
cachedQuota: res.quota?.groups,
|
|
1746
|
+
cachedQuotaUpdatedAt: Date.now(),
|
|
1747
|
+
};
|
|
1748
|
+
storageUpdated = true;
|
|
1543
1749
|
}
|
|
1544
1750
|
}
|
|
1751
|
+
if (storageUpdated) {
|
|
1752
|
+
await saveAccounts(existingStorage);
|
|
1753
|
+
}
|
|
1545
1754
|
console.log("");
|
|
1546
1755
|
continue;
|
|
1547
1756
|
}
|
|
@@ -1978,10 +2187,10 @@ function getHeaderStyleFromUrl(urlString, family) {
|
|
|
1978
2187
|
}
|
|
1979
2188
|
const modelWithSuffix = extractModelFromUrlWithSuffix(urlString);
|
|
1980
2189
|
if (!modelWithSuffix) {
|
|
1981
|
-
return "
|
|
2190
|
+
return "antigravity";
|
|
1982
2191
|
}
|
|
1983
2192
|
const { quotaPreference } = resolveModelWithTier(modelWithSuffix);
|
|
1984
|
-
return quotaPreference
|
|
2193
|
+
return quotaPreference === "gemini-cli" ? "antigravity" : (quotaPreference ?? "antigravity");
|
|
1985
2194
|
}
|
|
1986
2195
|
function isExplicitQuotaFromUrl(urlString) {
|
|
1987
2196
|
const modelWithSuffix = extractModelFromUrlWithSuffix(urlString);
|
|
@@ -1991,4 +2200,7 @@ function isExplicitQuotaFromUrl(urlString) {
|
|
|
1991
2200
|
const { explicitQuota } = resolveModelWithTier(modelWithSuffix);
|
|
1992
2201
|
return explicitQuota ?? false;
|
|
1993
2202
|
}
|
|
2203
|
+
export const __testExports = {
|
|
2204
|
+
getHeaderStyleFromUrl,
|
|
2205
|
+
};
|
|
1994
2206
|
//# sourceMappingURL=plugin.js.map
|