opencode-antigravity-auth 1.6.5-beta.0 → 2.1.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 +34 -694
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/src/antigravity/oauth.d.ts +0 -28
- package/dist/src/antigravity/oauth.d.ts.map +1 -1
- package/dist/src/antigravity/oauth.js +25 -103
- package/dist/src/antigravity/oauth.js.map +1 -1
- package/dist/src/constants.d.ts +53 -32
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +64 -56
- package/dist/src/constants.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/cache.js +1 -1
- package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
- package/dist/src/hooks/auto-update-checker/checker.js +2 -2
- package/dist/src/hooks/auto-update-checker/index.d.ts +4 -4
- package/dist/src/hooks/auto-update-checker/index.js +6 -6
- package/dist/src/hooks/auto-update-checker/logging.js +1 -1
- package/dist/src/plugin/account-management.d.ts +67 -0
- package/dist/src/plugin/account-management.d.ts.map +1 -0
- package/dist/src/plugin/account-management.js +174 -0
- package/dist/src/plugin/account-management.js.map +1 -0
- package/dist/src/plugin/account-rpc.d.ts +70 -0
- package/dist/src/plugin/account-rpc.d.ts.map +1 -0
- package/dist/src/plugin/account-rpc.js +28 -0
- package/dist/src/plugin/account-rpc.js.map +1 -0
- package/dist/src/plugin/accounts.d.ts +32 -19
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +203 -34
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/auth.d.ts +1 -1
- package/dist/src/plugin/cache/index.d.ts +1 -1
- package/dist/src/plugin/cache/index.js +1 -1
- package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
- package/dist/src/plugin/cache/signature-cache.js +1 -1
- package/dist/src/plugin/cache.d.ts +7 -5
- package/dist/src/plugin/cache.d.ts.map +1 -1
- package/dist/src/plugin/cache.js +20 -3
- package/dist/src/plugin/cache.js.map +1 -1
- package/dist/src/plugin/cli.d.ts +4 -6
- package/dist/src/plugin/cli.d.ts.map +1 -1
- package/dist/src/plugin/cli.js +5 -11
- package/dist/src/plugin/cli.js.map +1 -1
- package/dist/src/plugin/config/index.d.ts +2 -2
- package/dist/src/plugin/config/index.js +2 -2
- package/dist/src/plugin/config/loader.d.ts +1 -1
- package/dist/src/plugin/config/loader.js +2 -2
- package/dist/src/plugin/config/models-v2.d.ts +54 -0
- package/dist/src/plugin/config/models-v2.d.ts.map +1 -0
- package/dist/src/plugin/config/models-v2.js +62 -0
- package/dist/src/plugin/config/models-v2.js.map +1 -0
- package/dist/src/plugin/config/models.d.ts +1 -1
- package/dist/src/plugin/config/updater.d.ts +16 -3
- package/dist/src/plugin/config/updater.d.ts.map +1 -1
- package/dist/src/plugin/config/updater.js +53 -10
- package/dist/src/plugin/config/updater.js.map +1 -1
- package/dist/src/plugin/core/streaming/index.d.ts +2 -2
- package/dist/src/plugin/core/streaming/index.js +2 -2
- package/dist/src/plugin/core/streaming/transformer.d.ts +1 -1
- package/dist/src/plugin/core/streaming/transformer.js +1 -1
- package/dist/src/plugin/debug.d.ts +1 -1
- package/dist/src/plugin/debug.js +2 -2
- package/dist/src/plugin/fingerprint.d.ts +8 -5
- package/dist/src/plugin/fingerprint.d.ts.map +1 -1
- package/dist/src/plugin/fingerprint.js +23 -41
- package/dist/src/plugin/fingerprint.js.map +1 -1
- package/dist/src/plugin/logger.d.ts +1 -1
- package/dist/src/plugin/logger.js +2 -2
- package/dist/src/plugin/project.d.ts +4 -4
- package/dist/src/plugin/project.d.ts.map +1 -1
- package/dist/src/plugin/project.js +95 -94
- package/dist/src/plugin/project.js.map +1 -1
- package/dist/src/plugin/quota.d.ts +35 -3
- package/dist/src/plugin/quota.d.ts.map +1 -1
- package/dist/src/plugin/quota.js +54 -25
- package/dist/src/plugin/quota.js.map +1 -1
- package/dist/src/plugin/recovery/index.d.ts +3 -3
- package/dist/src/plugin/recovery/index.js +3 -3
- package/dist/src/plugin/recovery/storage.d.ts +1 -1
- package/dist/src/plugin/recovery/storage.js +1 -1
- package/dist/src/plugin/recovery.d.ts +3 -3
- package/dist/src/plugin/recovery.js +3 -3
- package/dist/src/plugin/refresh-queue.d.ts +6 -3
- package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
- package/dist/src/plugin/refresh-queue.js +23 -6
- package/dist/src/plugin/refresh-queue.js.map +1 -1
- package/dist/src/plugin/request-helpers.d.ts +1 -1
- package/dist/src/plugin/request-helpers.js +5 -5
- package/dist/src/plugin/request.d.ts +9 -6
- package/dist/src/plugin/request.d.ts.map +1 -1
- package/dist/src/plugin/request.js +23 -23
- package/dist/src/plugin/request.js.map +1 -1
- package/dist/src/plugin/search.js +7 -7
- package/dist/src/plugin/search.js.map +1 -1
- package/dist/src/plugin/server.js +1 -1
- package/dist/src/plugin/storage.d.ts +18 -5
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +117 -69
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/stores/signature-store.d.ts +1 -1
- package/dist/src/plugin/token.d.ts +2 -4
- package/dist/src/plugin/token.d.ts.map +1 -1
- package/dist/src/plugin/token.js +48 -8
- package/dist/src/plugin/token.js.map +1 -1
- package/dist/src/plugin/transform/claude.d.ts +1 -1
- package/dist/src/plugin/transform/claude.js +1 -1
- package/dist/src/plugin/transform/cross-model-sanitizer.js +2 -2
- package/dist/src/plugin/transform/gemini.d.ts +1 -1
- package/dist/src/plugin/transform/index.d.ts +9 -9
- package/dist/src/plugin/transform/index.js +4 -4
- package/dist/src/plugin/transform/model-resolver.d.ts +9 -1
- package/dist/src/plugin/transform/model-resolver.d.ts.map +1 -1
- package/dist/src/plugin/transform/model-resolver.js +34 -4
- 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/types.d.ts +31 -2
- package/dist/src/plugin/types.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.d.ts +1 -4
- package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.js +8 -16
- package/dist/src/plugin/ui/auth-menu.js.map +1 -1
- package/dist/src/plugin/ui/confirm.js +1 -1
- package/dist/src/plugin/ui/select.js +1 -1
- package/dist/src/plugin/version.d.ts +35 -6
- package/dist/src/plugin/version.d.ts.map +1 -1
- package/dist/src/plugin/version.js +138 -42
- package/dist/src/plugin/version.js.map +1 -1
- package/dist/src/plugin.d.ts +11 -8
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/plugin.js +170 -291
- package/dist/src/plugin.js.map +1 -1
- package/dist/src/tui.d.ts +64 -0
- package/dist/src/tui.d.ts.map +1 -0
- package/dist/src/tui.js +124 -0
- package/dist/src/tui.js.map +1 -0
- package/dist/src/v2.d.ts +4 -0
- package/dist/src/v2.d.ts.map +1 -0
- package/dist/src/v2.js +672 -0
- package/dist/src/v2.js.map +1 -0
- package/index.js +4 -0
- package/package.json +21 -7
- package/tui.js +1 -0
package/dist/src/plugin.js
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
|
-
import { tool } from "@opencode-ai/plugin";
|
|
3
|
-
import { ANTIGRAVITY_DEFAULT_PROJECT_ID,
|
|
4
|
-
import { authorizeAntigravity, exchangeAntigravity } from "./antigravity/oauth";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { executeSearch } from "./plugin/search";
|
|
2
|
+
import { tool } from "@opencode-ai/plugin/tool";
|
|
3
|
+
import { ANTIGRAVITY_DEFAULT_PROJECT_ID, ANTIGRAVITY_ENDPOINT, ANTIGRAVITY_PROVIDER_ID, getAntigravityHeaders, getEndpointFallbacks, } from "./constants.js";
|
|
4
|
+
import { authorizeAntigravity, exchangeAntigravity } from "./antigravity/oauth.js";
|
|
5
|
+
import { accessTokenExpired, isOAuthAuth, parseRefreshParts, formatRefreshParts } from "./plugin/auth.js";
|
|
6
|
+
import { promptAddAnotherAccount, promptLoginMode, promptProjectId } from "./plugin/cli.js";
|
|
7
|
+
import { ensureProjectContext } from "./plugin/project.js";
|
|
8
|
+
import { startAntigravityDebugRequest, logAntigravityDebugResponse, logAccountContext, logRateLimitEvent, logRateLimitSnapshot, logResponseBody, logModelFamily, isDebugEnabled, getLogFilePath, initializeDebug, } from "./plugin/debug.js";
|
|
9
|
+
import { buildThinkingWarmupBody, isGenerativeLanguageRequest, prepareAntigravityRequest, transformAntigravityResponse, } from "./plugin/request.js";
|
|
10
|
+
import { resolveModelWithTier } from "./plugin/transform/model-resolver.js";
|
|
11
|
+
import { isEmptyResponseBody, createSyntheticErrorResponse, } from "./plugin/request-helpers.js";
|
|
12
|
+
import { EmptyResponseError } from "./plugin/errors.js";
|
|
13
|
+
import { AntigravityTokenRefreshError, refreshAccessToken } from "./plugin/token.js";
|
|
14
|
+
import { startOAuthListener } from "./plugin/server.js";
|
|
15
|
+
import { clearAccounts, loadAccounts, saveAccountCredentials, saveAccounts, saveAccountsReplace, updateAccountCredentials, updateAccounts } from "./plugin/storage.js";
|
|
16
|
+
import { AccountManager, parseRateLimitReason, calculateBackoffMs, computeSoftQuotaCacheTtlMs } from "./plugin/accounts.js";
|
|
17
|
+
import { createAutoUpdateCheckerHook } from "./hooks/auto-update-checker/index.js";
|
|
18
|
+
import { loadConfig, initRuntimeConfig } from "./plugin/config/index.js";
|
|
19
|
+
import { createSessionRecoveryHook, getRecoverySuccessToast } from "./plugin/recovery.js";
|
|
20
|
+
import { checkAccountsQuota } from "./plugin/quota.js";
|
|
21
|
+
import { acquireDiskSignatureCache } from "./plugin/cache.js";
|
|
22
|
+
import { createProactiveRefreshQueue } from "./plugin/refresh-queue.js";
|
|
23
|
+
import { initLogger, createLogger } from "./plugin/logger.js";
|
|
24
|
+
import { initHealthTracker, getHealthTracker, initTokenTracker, getTokenTracker } from "./plugin/rotation.js";
|
|
25
|
+
import { refreshAntigravityVersion, ANTIGRAVITY_VERSION_REFRESH_INTERVAL_MS } from "./plugin/version.js";
|
|
26
|
+
import { executeSearch } from "./plugin/search.js";
|
|
28
27
|
const MAX_OAUTH_ACCOUNTS = 10;
|
|
29
28
|
const MAX_WARMUP_SESSIONS = 1000;
|
|
30
29
|
const MAX_WARMUP_RETRIES = 2;
|
|
@@ -99,7 +98,19 @@ async function triggerAsyncQuotaRefreshForAccount(accountManager, accountIndex,
|
|
|
99
98
|
quotaRefreshInProgressByEmail.delete(accountKey);
|
|
100
99
|
return;
|
|
101
100
|
}
|
|
102
|
-
const results = await checkAccountsQuota([singleAccount], client, providerId);
|
|
101
|
+
const results = await checkAccountsQuota([singleAccount], client, providerId, { onAccountUpdated: saveAccountCredentials });
|
|
102
|
+
const updatedAccount = results[0]?.updatedAccount;
|
|
103
|
+
if (updatedAccount) {
|
|
104
|
+
const saved = await updateAccounts(storage => {
|
|
105
|
+
const current = storage.accounts.find(item => singleAccount.id ? item.id === singleAccount.id : item.refreshToken === singleAccount.refreshToken);
|
|
106
|
+
if (current?.refreshToken === singleAccount.refreshToken)
|
|
107
|
+
updateAccountCredentials(current, updatedAccount);
|
|
108
|
+
});
|
|
109
|
+
const current = saved?.accounts.find(item => singleAccount.id ? item.id === singleAccount.id : item.refreshToken === updatedAccount.refreshToken);
|
|
110
|
+
if (current && account.parts.refreshToken === singleAccount.refreshToken) {
|
|
111
|
+
accountManager.updateFromAuth(account, { ...accountManager.toAuthDetails(account), refresh: formatRefreshParts(current) }, singleAccount.refreshToken);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
103
114
|
if (results[0]?.status === "ok" && results[0]?.quota?.groups) {
|
|
104
115
|
accountManager.updateQuotaCache(accountIndex, results[0].quota.groups);
|
|
105
116
|
accountManager.requestSaveToDisk();
|
|
@@ -259,16 +270,6 @@ function extractVerificationErrorDetails(bodyText) {
|
|
|
259
270
|
let validationRequired = lowerBody.includes("validation_required");
|
|
260
271
|
let message;
|
|
261
272
|
const verificationUrls = new Set();
|
|
262
|
-
// Detect SUBSCRIPTION_REQUIRED / Gemini Code Assist license errors
|
|
263
|
-
const isSubscriptionRequired = lowerBody.includes("subscription_required") ||
|
|
264
|
-
lowerBody.includes("lack a gemini code assist license");
|
|
265
|
-
// Detect Cloud Code Private API not enabled errors
|
|
266
|
-
const isApiEnableRequired = lowerBody.includes("cloud code private api has not been used") ||
|
|
267
|
-
lowerBody.includes("cloudcode-pa.googleapis.com/overview");
|
|
268
|
-
// Mark as validation required if we detect these specific error types
|
|
269
|
-
if (isSubscriptionRequired || isApiEnableRequired) {
|
|
270
|
-
validationRequired = true;
|
|
271
|
-
}
|
|
272
273
|
const collectUrlsFromText = (text) => {
|
|
273
274
|
for (const match of text.matchAll(/https:\/\/accounts\.google\.com\/[^\s"'<>]+/gi)) {
|
|
274
275
|
if (match[0]) {
|
|
@@ -351,27 +352,15 @@ function extractVerificationErrorDetails(bodyText) {
|
|
|
351
352
|
const fallback = decodedBody
|
|
352
353
|
.split("\n")
|
|
353
354
|
.map((line) => line.trim())
|
|
354
|
-
.find((line) => line && !line.startsWith("data:") && /(verify|validation|required
|
|
355
|
+
.find((line) => line && !line.startsWith("data:") && /(verify|validation|required)/i.test(line));
|
|
355
356
|
if (fallback) {
|
|
356
357
|
message = fallback;
|
|
357
358
|
}
|
|
358
359
|
}
|
|
359
|
-
// Classify the verification type
|
|
360
|
-
let verificationType = "unknown";
|
|
361
|
-
if (isSubscriptionRequired) {
|
|
362
|
-
verificationType = "gemini-cli";
|
|
363
|
-
}
|
|
364
|
-
else if (isApiEnableRequired) {
|
|
365
|
-
verificationType = "api-enable";
|
|
366
|
-
}
|
|
367
|
-
else if (verificationUrls.size > 0 && [...verificationUrls].some(u => u.includes("accounts.google.com"))) {
|
|
368
|
-
verificationType = "google-account";
|
|
369
|
-
}
|
|
370
360
|
return {
|
|
371
361
|
validationRequired,
|
|
372
362
|
message,
|
|
373
363
|
verifyUrl: selectBestVerificationUrl([...verificationUrls]),
|
|
374
|
-
verificationType,
|
|
375
364
|
};
|
|
376
365
|
}
|
|
377
366
|
async function verifyAccountAccess(account, client, providerId) {
|
|
@@ -412,11 +401,12 @@ async function verifyAccountAccess(account, client, providerId) {
|
|
|
412
401
|
Authorization: `Bearer ${refreshedAuth.access}`,
|
|
413
402
|
"Content-Type": "application/json",
|
|
414
403
|
};
|
|
415
|
-
if (projectId) {
|
|
416
|
-
headers["x-goog-user-project"] = projectId;
|
|
417
|
-
}
|
|
418
404
|
const requestBody = {
|
|
405
|
+
project: projectId,
|
|
419
406
|
model: "gemini-3-flash",
|
|
407
|
+
requestType: "agent",
|
|
408
|
+
userAgent: "antigravity",
|
|
409
|
+
requestId: `agent-${crypto.randomUUID()}`,
|
|
420
410
|
request: {
|
|
421
411
|
model: "gemini-3-flash",
|
|
422
412
|
contents: [{ role: "user", parts: [{ text: "ping" }] }],
|
|
@@ -427,7 +417,7 @@ async function verifyAccountAccess(account, client, providerId) {
|
|
|
427
417
|
const timeoutId = setTimeout(() => controller.abort(), 20000);
|
|
428
418
|
let response;
|
|
429
419
|
try {
|
|
430
|
-
response = await fetch(`${
|
|
420
|
+
response = await fetch(`${ANTIGRAVITY_ENDPOINT}/v1internal:streamGenerateContent?alt=sse`, {
|
|
431
421
|
method: "POST",
|
|
432
422
|
headers,
|
|
433
423
|
body: JSON.stringify(requestBody),
|
|
@@ -459,14 +449,12 @@ async function verifyAccountAccess(account, client, providerId) {
|
|
|
459
449
|
status: "blocked",
|
|
460
450
|
message: extracted.message ?? "Google requires additional account verification.",
|
|
461
451
|
verifyUrl: extracted.verifyUrl,
|
|
462
|
-
verificationType: extracted.verificationType,
|
|
463
452
|
};
|
|
464
453
|
}
|
|
465
454
|
const fallbackMessage = extracted.message ?? `Request failed (${response.status} ${response.statusText}).`;
|
|
466
455
|
return {
|
|
467
456
|
status: "error",
|
|
468
457
|
message: fallbackMessage,
|
|
469
|
-
verificationType: extracted.verificationType !== "unknown" ? extracted.verificationType : undefined,
|
|
470
458
|
};
|
|
471
459
|
}
|
|
472
460
|
async function promptAccountIndexForVerification(accounts) {
|
|
@@ -507,7 +495,7 @@ async function promptOpenVerificationUrl() {
|
|
|
507
495
|
const answer = (await promptOAuthCallbackValue("Open verification URL in your browser now? [Y/n]: ")).trim().toLowerCase();
|
|
508
496
|
return answer === "" || answer === "y" || answer === "yes";
|
|
509
497
|
}
|
|
510
|
-
function markStoredAccountVerificationRequired(account, reason, verifyUrl
|
|
498
|
+
function markStoredAccountVerificationRequired(account, reason, verifyUrl) {
|
|
511
499
|
let changed = false;
|
|
512
500
|
const wasVerificationRequired = account.verificationRequired === true;
|
|
513
501
|
if (!wasVerificationRequired) {
|
|
@@ -528,11 +516,6 @@ function markStoredAccountVerificationRequired(account, reason, verifyUrl, verif
|
|
|
528
516
|
account.verificationUrl = normalizedUrl;
|
|
529
517
|
changed = true;
|
|
530
518
|
}
|
|
531
|
-
const normalizedType = verificationType?.trim();
|
|
532
|
-
if (normalizedType && account.verificationRequiredType !== normalizedType) {
|
|
533
|
-
account.verificationRequiredType = normalizedType;
|
|
534
|
-
changed = true;
|
|
535
|
-
}
|
|
536
519
|
if (account.enabled !== false) {
|
|
537
520
|
account.enabled = false;
|
|
538
521
|
changed = true;
|
|
@@ -554,10 +537,6 @@ function clearStoredAccountVerificationRequired(account, enableIfRequired = fals
|
|
|
554
537
|
account.verificationRequiredReason = undefined;
|
|
555
538
|
changed = true;
|
|
556
539
|
}
|
|
557
|
-
if (account.verificationRequiredType !== undefined) {
|
|
558
|
-
account.verificationRequiredType = undefined;
|
|
559
|
-
changed = true;
|
|
560
|
-
}
|
|
561
540
|
if (account.verificationUrl !== undefined) {
|
|
562
541
|
account.verificationUrl = undefined;
|
|
563
542
|
changed = true;
|
|
@@ -636,7 +615,7 @@ function clampInt(value, min, max) {
|
|
|
636
615
|
}
|
|
637
616
|
return Math.min(max, Math.max(min, Math.floor(value)));
|
|
638
617
|
}
|
|
639
|
-
async function persistAccountPool(results, replaceAll = false) {
|
|
618
|
+
export async function persistAccountPool(results, replaceAll = false) {
|
|
640
619
|
if (results.length === 0) {
|
|
641
620
|
return;
|
|
642
621
|
}
|
|
@@ -1026,18 +1005,25 @@ function sleep(ms, signal) {
|
|
|
1026
1005
|
/**
|
|
1027
1006
|
* Creates an Antigravity OAuth plugin for a specific provider ID.
|
|
1028
1007
|
*/
|
|
1029
|
-
export const createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
|
|
1008
|
+
export const createAntigravityPlugin = (providerId) => async ({ client, directory, runtimeVersion = 1, fetch: sendFetch = globalThis.fetch, canUseAccount }) => {
|
|
1030
1009
|
// Load configuration from files and environment variables
|
|
1031
1010
|
const config = loadConfig(directory);
|
|
1032
1011
|
initRuntimeConfig(config);
|
|
1033
1012
|
// Cached getAuth function for tool access
|
|
1034
1013
|
let cachedGetAuth = null;
|
|
1014
|
+
const refreshQueues = new Set();
|
|
1015
|
+
const quotaRefreshes = new Set();
|
|
1016
|
+
const transportManagers = new Set();
|
|
1017
|
+
let disposed = false;
|
|
1018
|
+
let managerGeneration = 0;
|
|
1035
1019
|
// Initialize debug with config
|
|
1036
1020
|
initializeDebug(config);
|
|
1037
1021
|
// Initialize structured logger for TUI integration
|
|
1038
1022
|
initLogger(client);
|
|
1039
1023
|
// Fetch latest Antigravity version from remote API (non-blocking, falls back to hardcoded)
|
|
1040
|
-
await
|
|
1024
|
+
await refreshAntigravityVersion();
|
|
1025
|
+
const versionRefreshTimer = setInterval(() => { void refreshAntigravityVersion(); }, ANTIGRAVITY_VERSION_REFRESH_INTERVAL_MS);
|
|
1026
|
+
versionRefreshTimer.unref?.();
|
|
1041
1027
|
// Initialize health tracker for hybrid strategy
|
|
1042
1028
|
if (config.health_score) {
|
|
1043
1029
|
initHealthTracker({
|
|
@@ -1060,19 +1046,21 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1060
1046
|
}
|
|
1061
1047
|
// Initialize disk signature cache if keep_thinking is enabled
|
|
1062
1048
|
// This integrates with the in-memory cacheSignature/getCachedSignature functions
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1049
|
+
const releaseSignatureCache = config.keep_thinking
|
|
1050
|
+
? acquireDiskSignatureCache(config.signature_cache)
|
|
1051
|
+
: undefined;
|
|
1066
1052
|
// Initialize session recovery hook with full context
|
|
1067
|
-
const sessionRecovery =
|
|
1068
|
-
|
|
1053
|
+
const sessionRecovery = runtimeVersion === 1
|
|
1054
|
+
? createSessionRecoveryHook({ client, directory }, config)
|
|
1055
|
+
: null;
|
|
1056
|
+
const updateChecker = runtimeVersion === 1 ? createAutoUpdateCheckerHook(client, directory, {
|
|
1069
1057
|
showStartupToast: true,
|
|
1070
1058
|
autoUpdate: config.auto_update,
|
|
1071
|
-
});
|
|
1059
|
+
}) : null;
|
|
1072
1060
|
// Event handler for session recovery and updates
|
|
1073
1061
|
const eventHandler = async (input) => {
|
|
1074
1062
|
// Forward to update checker
|
|
1075
|
-
await updateChecker
|
|
1063
|
+
await updateChecker?.event(input);
|
|
1076
1064
|
// Track if this is a child session (subagent, background task)
|
|
1077
1065
|
// This is used to filter toasts based on toast_scope config
|
|
1078
1066
|
if (input.event.type === "session.created") {
|
|
@@ -1169,6 +1157,27 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1169
1157
|
},
|
|
1170
1158
|
});
|
|
1171
1159
|
return {
|
|
1160
|
+
async flush() {
|
|
1161
|
+
managerGeneration += 1;
|
|
1162
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1163
|
+
refreshQueues.clear();
|
|
1164
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1165
|
+
transportManagers.clear();
|
|
1166
|
+
},
|
|
1167
|
+
async dispose() {
|
|
1168
|
+
if (disposed)
|
|
1169
|
+
return;
|
|
1170
|
+
disposed = true;
|
|
1171
|
+
managerGeneration += 1;
|
|
1172
|
+
releaseSignatureCache?.();
|
|
1173
|
+
clearInterval(versionRefreshTimer);
|
|
1174
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1175
|
+
refreshQueues.clear();
|
|
1176
|
+
await Promise.allSettled([...quotaRefreshes]);
|
|
1177
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1178
|
+
transportManagers.clear();
|
|
1179
|
+
cachedGetAuth = null;
|
|
1180
|
+
},
|
|
1172
1181
|
event: eventHandler,
|
|
1173
1182
|
tool: {
|
|
1174
1183
|
google_search: googleSearchTool,
|
|
@@ -1176,11 +1185,22 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1176
1185
|
auth: {
|
|
1177
1186
|
provider: providerId,
|
|
1178
1187
|
loader: async (getAuth, provider) => {
|
|
1188
|
+
if (disposed)
|
|
1189
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
1190
|
+
const expectedGeneration = managerGeneration;
|
|
1191
|
+
if (runtimeVersion === 2) {
|
|
1192
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1193
|
+
refreshQueues.clear();
|
|
1194
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1195
|
+
transportManagers.clear();
|
|
1196
|
+
}
|
|
1179
1197
|
// Cache getAuth for tool access
|
|
1180
1198
|
cachedGetAuth = getAuth;
|
|
1181
1199
|
const auth = await getAuth();
|
|
1182
1200
|
// If OpenCode has no valid OAuth auth, clear any stale account storage
|
|
1183
1201
|
if (!isOAuthAuth(auth)) {
|
|
1202
|
+
if (runtimeVersion === 2)
|
|
1203
|
+
return {};
|
|
1184
1204
|
try {
|
|
1185
1205
|
await clearAccounts();
|
|
1186
1206
|
}
|
|
@@ -1194,8 +1214,22 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1194
1214
|
const authParts = parseRefreshParts(auth.refresh);
|
|
1195
1215
|
const storedAccounts = await loadAccounts();
|
|
1196
1216
|
// Note: AccountManager now ensures the current auth is always included in accounts
|
|
1197
|
-
const accountManager = await AccountManager.loadFromDisk(auth);
|
|
1217
|
+
const accountManager = await AccountManager.loadFromDisk(auth, runtimeVersion === 2);
|
|
1218
|
+
if (runtimeVersion === 2)
|
|
1219
|
+
accountManager.useManagedStorage();
|
|
1220
|
+
const abandonManager = async () => {
|
|
1221
|
+
await accountManager.retire();
|
|
1222
|
+
transportManagers.delete(accountManager);
|
|
1223
|
+
throw new Error(disposed ? "Antigravity plugin has been disposed" : "Antigravity accounts changed");
|
|
1224
|
+
};
|
|
1225
|
+
if (disposed || managerGeneration !== expectedGeneration)
|
|
1226
|
+
return abandonManager();
|
|
1198
1227
|
activeAccountManager = accountManager;
|
|
1228
|
+
transportManagers.add(accountManager);
|
|
1229
|
+
if (runtimeVersion === 2)
|
|
1230
|
+
await accountManager.saveToDisk();
|
|
1231
|
+
if (disposed || managerGeneration !== expectedGeneration)
|
|
1232
|
+
return abandonManager();
|
|
1199
1233
|
if (accountManager.getAccountCount() > 0) {
|
|
1200
1234
|
accountManager.requestSaveToDisk();
|
|
1201
1235
|
}
|
|
@@ -1209,6 +1243,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1209
1243
|
});
|
|
1210
1244
|
refreshQueue.setAccountManager(accountManager);
|
|
1211
1245
|
refreshQueue.start();
|
|
1246
|
+
refreshQueues.add(refreshQueue);
|
|
1212
1247
|
}
|
|
1213
1248
|
if (isDebugEnabled()) {
|
|
1214
1249
|
const logPath = getLogFilePath();
|
|
@@ -1231,14 +1266,20 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1231
1266
|
}
|
|
1232
1267
|
}
|
|
1233
1268
|
return {
|
|
1234
|
-
apiKey: "",
|
|
1269
|
+
apiKey: "antigravity-oauth",
|
|
1235
1270
|
async fetch(input, init) {
|
|
1236
1271
|
if (!isGenerativeLanguageRequest(input)) {
|
|
1237
|
-
return
|
|
1272
|
+
return sendFetch(input, init);
|
|
1238
1273
|
}
|
|
1274
|
+
const checkLifetime = () => {
|
|
1275
|
+
if (disposed || (runtimeVersion === 2 && managerGeneration !== expectedGeneration))
|
|
1276
|
+
throw new Error("Antigravity accounts changed; retry the request");
|
|
1277
|
+
};
|
|
1278
|
+
checkLifetime();
|
|
1239
1279
|
const latestAuth = await getAuth();
|
|
1280
|
+
checkLifetime();
|
|
1240
1281
|
if (!isOAuthAuth(latestAuth)) {
|
|
1241
|
-
return
|
|
1282
|
+
return sendFetch(input, init);
|
|
1242
1283
|
}
|
|
1243
1284
|
if (accountManager.getAccountCount() === 0) {
|
|
1244
1285
|
throw new Error("No Antigravity accounts configured. Run `opencode auth login`.");
|
|
@@ -1246,6 +1287,10 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1246
1287
|
const urlString = toUrlString(input);
|
|
1247
1288
|
const family = getModelFamilyFromUrl(urlString);
|
|
1248
1289
|
const model = extractModelFromUrl(urlString);
|
|
1290
|
+
const eligible = (account) => !canUseAccount || canUseAccount({ id: account.id, refreshToken: account.parts.refreshToken }, model ?? "");
|
|
1291
|
+
if (!accountManager.getAccounts().some(account => account.enabled !== false && eligible(account))) {
|
|
1292
|
+
throw new Error("No enabled Antigravity account has access to this model. Refresh available models from /antigravity.");
|
|
1293
|
+
}
|
|
1249
1294
|
const debugLines = [];
|
|
1250
1295
|
const pushDebug = (line) => {
|
|
1251
1296
|
if (!isDebugEnabled())
|
|
@@ -1297,30 +1342,30 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1297
1342
|
if (family !== "gemini")
|
|
1298
1343
|
return false;
|
|
1299
1344
|
// Use AccountManager method which properly checks for disabled/cooling-down accounts
|
|
1300
|
-
return accountManager.hasOtherAccountWithAntigravityAvailable(currentAccount.index, family, model);
|
|
1345
|
+
return accountManager.hasOtherAccountWithAntigravityAvailable(currentAccount.index, family, model, eligible);
|
|
1301
1346
|
};
|
|
1302
1347
|
while (true) {
|
|
1303
1348
|
// Check for abort at the start of each iteration
|
|
1304
1349
|
checkAborted();
|
|
1305
|
-
const accountCount = accountManager.
|
|
1350
|
+
const accountCount = accountManager.getEnabledAccounts().filter(eligible).length;
|
|
1306
1351
|
const routingDecision = resolveHeaderRoutingDecision(urlString, family, config);
|
|
1307
1352
|
const { cliFirst, preferredHeaderStyle, explicitQuota, allowQuotaFallback, } = routingDecision;
|
|
1308
1353
|
if (accountCount === 0) {
|
|
1309
1354
|
throw new Error("No Antigravity accounts available. Run `opencode auth login`.");
|
|
1310
1355
|
}
|
|
1311
1356
|
const softQuotaCacheTtlMs = computeSoftQuotaCacheTtlMs(config.soft_quota_cache_ttl_minutes, config.quota_refresh_interval_minutes);
|
|
1312
|
-
let account = accountManager.getCurrentOrNextForFamily(family, model, config.account_selection_strategy, preferredHeaderStyle, config.pid_offset_enabled, config.soft_quota_threshold_percent, softQuotaCacheTtlMs);
|
|
1357
|
+
let account = accountManager.getCurrentOrNextForFamily(family, model, config.account_selection_strategy, preferredHeaderStyle, config.pid_offset_enabled, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, eligible);
|
|
1313
1358
|
if (!account && allowQuotaFallback) {
|
|
1314
1359
|
const alternateHeaderStyle = preferredHeaderStyle === "antigravity" ? "gemini-cli" : "antigravity";
|
|
1315
|
-
account = accountManager.getCurrentOrNextForFamily(family, model, config.account_selection_strategy, alternateHeaderStyle, config.pid_offset_enabled, config.soft_quota_threshold_percent, softQuotaCacheTtlMs);
|
|
1360
|
+
account = accountManager.getCurrentOrNextForFamily(family, model, config.account_selection_strategy, alternateHeaderStyle, config.pid_offset_enabled, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, eligible);
|
|
1316
1361
|
if (account) {
|
|
1317
1362
|
pushDebug(`selected-by-fallback idx=${account.index} preferred=${preferredHeaderStyle} alternate=${alternateHeaderStyle}`);
|
|
1318
1363
|
}
|
|
1319
1364
|
}
|
|
1320
1365
|
if (!account) {
|
|
1321
|
-
if (accountManager.areAllAccountsOverSoftQuota(family, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, model)) {
|
|
1366
|
+
if (accountManager.areAllAccountsOverSoftQuota(family, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, model, eligible)) {
|
|
1322
1367
|
const threshold = config.soft_quota_threshold_percent;
|
|
1323
|
-
const softQuotaWaitMs = accountManager.getMinWaitTimeForSoftQuota(family, threshold, softQuotaCacheTtlMs, model);
|
|
1368
|
+
const softQuotaWaitMs = accountManager.getMinWaitTimeForSoftQuota(family, threshold, softQuotaCacheTtlMs, model, eligible);
|
|
1324
1369
|
const maxWaitMs = (config.max_rate_limit_wait_seconds ?? 300) * 1000;
|
|
1325
1370
|
if (softQuotaWaitMs === null || (maxWaitMs > 0 && softQuotaWaitMs > maxWaitMs)) {
|
|
1326
1371
|
const waitTimeFormatted = softQuotaWaitMs ? formatWaitTime(softQuotaWaitMs) : "unknown";
|
|
@@ -1340,7 +1385,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1340
1385
|
}
|
|
1341
1386
|
const strictWait = !allowQuotaFallback;
|
|
1342
1387
|
// All accounts are rate-limited - wait and retry
|
|
1343
|
-
const waitMs = accountManager.getMinWaitTimeForFamily(family, model, preferredHeaderStyle, strictWait) || 60_000;
|
|
1388
|
+
const waitMs = accountManager.getMinWaitTimeForFamily(family, model, preferredHeaderStyle, strictWait, eligible) || 60_000;
|
|
1344
1389
|
const waitSecValue = Math.max(1, Math.ceil(waitMs / 1000));
|
|
1345
1390
|
pushDebug(`all-rate-limited family=${family} accounts=${accountCount} waitMs=${waitMs}`);
|
|
1346
1391
|
if (isDebugEnabled()) {
|
|
@@ -1408,7 +1453,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1408
1453
|
continue;
|
|
1409
1454
|
}
|
|
1410
1455
|
resetAccountFailureState(account.index);
|
|
1411
|
-
accountManager.updateFromAuth(account, refreshed);
|
|
1456
|
+
accountManager.updateFromAuth(account, refreshed, parseRefreshParts(authRecord.refresh).refreshToken);
|
|
1412
1457
|
authRecord = refreshed;
|
|
1413
1458
|
try {
|
|
1414
1459
|
await accountManager.saveToDisk();
|
|
@@ -1419,9 +1464,24 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1419
1464
|
}
|
|
1420
1465
|
catch (error) {
|
|
1421
1466
|
if (error instanceof AntigravityTokenRefreshError && error.code === "invalid_grant") {
|
|
1422
|
-
|
|
1467
|
+
if (runtimeVersion === 2) {
|
|
1468
|
+
const rejectedToken = parseRefreshParts(authRecord.refresh).refreshToken;
|
|
1469
|
+
const saved = await updateAccounts(storage => {
|
|
1470
|
+
const current = storage.accounts.find(item => account.id ? item.id === account.id : item.refreshToken === rejectedToken);
|
|
1471
|
+
if (current?.refreshToken === rejectedToken)
|
|
1472
|
+
current.enabled = false;
|
|
1473
|
+
});
|
|
1474
|
+
const current = saved?.accounts.find(item => account.id ? item.id === account.id : item.refreshToken === rejectedToken);
|
|
1475
|
+
if (current && current.refreshToken !== rejectedToken) {
|
|
1476
|
+
accountManager.updateFromAuth(account, { type: "oauth", refresh: formatRefreshParts(current) });
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
const removed = runtimeVersion === 2
|
|
1481
|
+
? accountManager.setAccountEnabled(account.index, false)
|
|
1482
|
+
: accountManager.removeAccount(account);
|
|
1423
1483
|
if (removed) {
|
|
1424
|
-
log.warn("
|
|
1484
|
+
log.warn("Revoked account excluded from rotation - reauthenticate via `opencode auth login`");
|
|
1425
1485
|
try {
|
|
1426
1486
|
await accountManager.saveToDisk();
|
|
1427
1487
|
}
|
|
@@ -1481,7 +1541,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1481
1541
|
}
|
|
1482
1542
|
if (projectContext.auth.refresh !== authRecord.refresh ||
|
|
1483
1543
|
projectContext.auth.access !== authRecord.access) {
|
|
1484
|
-
accountManager.updateFromAuth(account, projectContext.auth);
|
|
1544
|
+
accountManager.updateFromAuth(account, projectContext.auth, parseRefreshParts(authRecord.refresh).refreshToken);
|
|
1485
1545
|
authRecord = projectContext.auth;
|
|
1486
1546
|
try {
|
|
1487
1547
|
await accountManager.saveToDisk();
|
|
@@ -1521,7 +1581,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1521
1581
|
});
|
|
1522
1582
|
try {
|
|
1523
1583
|
pushDebug("thinking-warmup: start");
|
|
1524
|
-
const warmupResponse = await
|
|
1584
|
+
const warmupResponse = await sendFetch(warmupUrl, warmupInit);
|
|
1525
1585
|
const transformed = await transformAntigravityResponse(warmupResponse, true, warmupDebugContext, prepared.requestedModel, projectId, warmupUrl, prepared.effectiveModel, prepared.sessionId);
|
|
1526
1586
|
await transformed.text();
|
|
1527
1587
|
markWarmupSuccess(prepared.sessionId);
|
|
@@ -1548,7 +1608,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1548
1608
|
// Antigravity-first fallback: exhaust antigravity across ALL accounts before gemini-cli
|
|
1549
1609
|
if (allowQuotaFallback && family === "gemini" && headerStyle === "antigravity") {
|
|
1550
1610
|
// Check if ANY other account has antigravity available
|
|
1551
|
-
if (accountManager.hasOtherAccountWithAntigravityAvailable(account.index, family, model)) {
|
|
1611
|
+
if (accountManager.hasOtherAccountWithAntigravityAvailable(account.index, family, model, eligible)) {
|
|
1552
1612
|
// Switch to another account with antigravity (preserve antigravity priority)
|
|
1553
1613
|
pushDebug(`antigravity rate-limited on account ${account.index}, but available on other accounts. Switching.`);
|
|
1554
1614
|
shouldSwitchAccount = true;
|
|
@@ -1602,19 +1662,14 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1602
1662
|
// Track capacity retries per endpoint to prevent infinite loops
|
|
1603
1663
|
let capacityRetryCount = 0;
|
|
1604
1664
|
let lastEndpointIndex = -1;
|
|
1605
|
-
|
|
1665
|
+
const endpoints = getEndpointFallbacks(headerStyle);
|
|
1666
|
+
for (let i = 0; i < endpoints.length; i++) {
|
|
1606
1667
|
// Reset capacity retry counter when switching to a new endpoint
|
|
1607
1668
|
if (i !== lastEndpointIndex) {
|
|
1608
1669
|
capacityRetryCount = 0;
|
|
1609
1670
|
lastEndpointIndex = i;
|
|
1610
1671
|
}
|
|
1611
|
-
const currentEndpoint =
|
|
1612
|
-
// Skip sandbox endpoints for Gemini CLI models - they only work with Antigravity quota
|
|
1613
|
-
// Gemini CLI models must use production endpoint (cloudcode-pa.googleapis.com)
|
|
1614
|
-
if (headerStyle === "gemini-cli" && currentEndpoint !== ANTIGRAVITY_ENDPOINT_PROD) {
|
|
1615
|
-
pushDebug(`Skipping sandbox endpoint ${currentEndpoint} for gemini-cli headerStyle`);
|
|
1616
|
-
continue;
|
|
1617
|
-
}
|
|
1672
|
+
const currentEndpoint = endpoints[i];
|
|
1618
1673
|
try {
|
|
1619
1674
|
const prepared = prepareAntigravityRequest(input, init, accessToken, projectContext.effectiveProjectId, currentEndpoint, headerStyle, forceThinkingRecovery, {
|
|
1620
1675
|
claudeToolHardening: config.claude_tool_hardening,
|
|
@@ -1659,7 +1714,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1659
1714
|
if (config.account_selection_strategy === 'hybrid') {
|
|
1660
1715
|
tokenConsumed = getTokenTracker().consume(account.index);
|
|
1661
1716
|
}
|
|
1662
|
-
const response = await
|
|
1717
|
+
const response = await sendFetch(prepared.request, prepared.init);
|
|
1663
1718
|
pushDebug(`status=${response.status} ${response.statusText}`);
|
|
1664
1719
|
// Handle 429 rate limit (or Service Overloaded) with improved logic
|
|
1665
1720
|
if (response.status === 429 || response.status === 503 || response.status === 529) {
|
|
@@ -1838,27 +1893,12 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1838
1893
|
if (extracted.validationRequired) {
|
|
1839
1894
|
const verificationReason = extracted.message ?? "Google requires account verification.";
|
|
1840
1895
|
const cooldownMs = 10 * 60 * 1000;
|
|
1841
|
-
accountManager.markAccountVerificationRequired(account.index, verificationReason, extracted.verifyUrl
|
|
1896
|
+
accountManager.markAccountVerificationRequired(account.index, verificationReason, extracted.verifyUrl);
|
|
1842
1897
|
accountManager.markAccountCoolingDown(account, cooldownMs, "validation-required");
|
|
1843
1898
|
accountManager.markRateLimited(account, cooldownMs, family, headerStyle, model);
|
|
1844
1899
|
const label = account.email || `Account ${account.index + 1}`;
|
|
1845
1900
|
if (accountManager.shouldShowAccountToast(account.index, 60000)) {
|
|
1846
|
-
|
|
1847
|
-
switch (extracted.verificationType) {
|
|
1848
|
-
case "gemini-cli":
|
|
1849
|
-
toastMessage = `⚠ ${label} needs Gemini CLI login. Run 'opencode auth login' → Gemini CLI Login.`;
|
|
1850
|
-
break;
|
|
1851
|
-
case "api-enable":
|
|
1852
|
-
toastMessage = `⚠ ${label}: Cloud Code API not enabled. Run 'opencode auth login' → Verify accounts.`;
|
|
1853
|
-
break;
|
|
1854
|
-
case "google-account":
|
|
1855
|
-
toastMessage = `⚠ ${label} needs Google verification. Run 'opencode auth login' → Verify accounts.`;
|
|
1856
|
-
break;
|
|
1857
|
-
default:
|
|
1858
|
-
toastMessage = `⚠ ${label} needs verification. Run 'opencode auth login' and use Verify accounts.`;
|
|
1859
|
-
break;
|
|
1860
|
-
}
|
|
1861
|
-
await showToast(toastMessage, "warning");
|
|
1901
|
+
await showToast(`⚠ ${label} needs verification. Run 'opencode auth login' and use Verify accounts.`, "warning");
|
|
1862
1902
|
accountManager.markToastShown(account.index);
|
|
1863
1903
|
}
|
|
1864
1904
|
pushDebug(`verification-required: disabled account ${account.index}`);
|
|
@@ -1871,7 +1911,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1871
1911
|
const shouldRetryEndpoint = (response.status === 403 ||
|
|
1872
1912
|
response.status === 404 ||
|
|
1873
1913
|
response.status >= 500);
|
|
1874
|
-
if (shouldRetryEndpoint && i <
|
|
1914
|
+
if (shouldRetryEndpoint && i < endpoints.length - 1) {
|
|
1875
1915
|
await logResponseBody(debugContext, response, response.status);
|
|
1876
1916
|
lastFailure = createFailureContext(response);
|
|
1877
1917
|
continue;
|
|
@@ -1881,7 +1921,11 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1881
1921
|
account.consecutiveFailures = 0;
|
|
1882
1922
|
getHealthTracker().recordSuccess(account.index);
|
|
1883
1923
|
accountManager.markAccountUsed(account.index);
|
|
1884
|
-
|
|
1924
|
+
if (!disposed) {
|
|
1925
|
+
const quotaRefresh = triggerAsyncQuotaRefreshForAccount(accountManager, account.index, client, providerId, config.quota_refresh_interval_minutes);
|
|
1926
|
+
quotaRefreshes.add(quotaRefresh);
|
|
1927
|
+
void quotaRefresh.then(() => quotaRefreshes.delete(quotaRefresh), () => quotaRefreshes.delete(quotaRefresh));
|
|
1928
|
+
}
|
|
1885
1929
|
}
|
|
1886
1930
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1887
1931
|
note: response.ok ? "Success" : `Error ${response.status}`,
|
|
@@ -1973,7 +2017,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1973
2017
|
headers: { "Content-Type": "application/json" }
|
|
1974
2018
|
});
|
|
1975
2019
|
}
|
|
1976
|
-
if (i <
|
|
2020
|
+
if (i < endpoints.length - 1) {
|
|
1977
2021
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1978
2022
|
continue;
|
|
1979
2023
|
}
|
|
@@ -2035,19 +2079,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2035
2079
|
let status = 'unknown';
|
|
2036
2080
|
if (acc.verificationRequired) {
|
|
2037
2081
|
status = 'verification-required';
|
|
2038
|
-
// Backfill verificationRequiredType for accounts flagged before this field existed
|
|
2039
|
-
if (!acc.verificationRequiredType && acc.verificationRequiredReason) {
|
|
2040
|
-
const lowerReason = acc.verificationRequiredReason.toLowerCase();
|
|
2041
|
-
if (lowerReason.includes("subscription_required") || lowerReason.includes("gemini code assist license")) {
|
|
2042
|
-
acc.verificationRequiredType = "gemini-cli";
|
|
2043
|
-
}
|
|
2044
|
-
else if (lowerReason.includes("cloud code private api has not been used") || lowerReason.includes("cloudcode-pa.googleapis.com/overview")) {
|
|
2045
|
-
acc.verificationRequiredType = "api-enable";
|
|
2046
|
-
}
|
|
2047
|
-
else if (lowerReason.includes("accounts.google.com")) {
|
|
2048
|
-
acc.verificationRequiredType = "google-account";
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
2082
|
}
|
|
2052
2083
|
else {
|
|
2053
2084
|
const rateLimits = acc.rateLimitResetTimes;
|
|
@@ -2075,7 +2106,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2075
2106
|
status,
|
|
2076
2107
|
isCurrentAccount: idx === (existingStorage.activeIndex ?? 0),
|
|
2077
2108
|
enabled: acc.enabled !== false,
|
|
2078
|
-
verificationRequiredType: acc.verificationRequiredType,
|
|
2079
2109
|
};
|
|
2080
2110
|
});
|
|
2081
2111
|
menuResult = await promptLoginMode(existingAccounts);
|
|
@@ -2218,95 +2248,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2218
2248
|
}
|
|
2219
2249
|
continue;
|
|
2220
2250
|
}
|
|
2221
|
-
if (menuResult.mode === "gemini-cli-login") {
|
|
2222
|
-
if (existingStorage.accounts.length === 0) {
|
|
2223
|
-
console.log("\nNo accounts available. Add an account first.\n");
|
|
2224
|
-
continue;
|
|
2225
|
-
}
|
|
2226
|
-
// Show account picker
|
|
2227
|
-
const accountIndex = await promptAccountIndexForVerification(existingStorage.accounts.map((acc, idx) => ({
|
|
2228
|
-
email: acc.email,
|
|
2229
|
-
index: idx,
|
|
2230
|
-
})));
|
|
2231
|
-
if (accountIndex === undefined) {
|
|
2232
|
-
console.log("\nGemini CLI login cancelled.\n");
|
|
2233
|
-
continue;
|
|
2234
|
-
}
|
|
2235
|
-
const targetAccount = existingStorage.accounts[accountIndex];
|
|
2236
|
-
if (!targetAccount) {
|
|
2237
|
-
console.log(`\nAccount ${accountIndex + 1} not found.\n`);
|
|
2238
|
-
continue;
|
|
2239
|
-
}
|
|
2240
|
-
const accountLabel = targetAccount.email || `Account ${accountIndex + 1}`;
|
|
2241
|
-
console.log(`\nStarting Gemini CLI login for ${accountLabel}...`);
|
|
2242
|
-
console.log("This will open your browser to authorize Gemini Code Assist.\n");
|
|
2243
|
-
let oauthListener;
|
|
2244
|
-
try {
|
|
2245
|
-
oauthListener = await startOAuthListener();
|
|
2246
|
-
const geminiRedirectUri = ANTIGRAVITY_REDIRECT_URI;
|
|
2247
|
-
const authorization = await authorizeGeminiCli(geminiRedirectUri, targetAccount.email);
|
|
2248
|
-
const opened = await openBrowser(authorization.url);
|
|
2249
|
-
if (opened) {
|
|
2250
|
-
console.log("Opened authorization URL in your browser.");
|
|
2251
|
-
}
|
|
2252
|
-
else {
|
|
2253
|
-
console.log("Could not open browser. Please open this URL manually:");
|
|
2254
|
-
console.log(`\n${authorization.url}\n`);
|
|
2255
|
-
}
|
|
2256
|
-
console.log("Waiting for authorization...\n");
|
|
2257
|
-
const callbackUrl = await oauthListener.waitForCallback();
|
|
2258
|
-
const callbackParams = new URL(callbackUrl, "http://localhost").searchParams;
|
|
2259
|
-
const code = callbackParams.get("code");
|
|
2260
|
-
if (!code) {
|
|
2261
|
-
console.log("✗ Authorization failed: no code received.\n");
|
|
2262
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2263
|
-
continue;
|
|
2264
|
-
}
|
|
2265
|
-
const result = await exchangeGeminiCli(code, authorization.verifier, geminiRedirectUri);
|
|
2266
|
-
if (result.type === "success") {
|
|
2267
|
-
const provisionedEmail = result.email || accountLabel;
|
|
2268
|
-
console.log(`✓ Gemini CLI access provisioned for ${provisionedEmail}`);
|
|
2269
|
-
// Verify the account actually works now
|
|
2270
|
-
console.log("Verifying account access...");
|
|
2271
|
-
const postVerification = await verifyAccountAccess(targetAccount, client, providerId);
|
|
2272
|
-
if (postVerification.status === "ok") {
|
|
2273
|
-
// Clear any verification flags and re-enable the account
|
|
2274
|
-
if (targetAccount.verificationRequired) {
|
|
2275
|
-
const { changed } = clearStoredAccountVerificationRequired(targetAccount, true);
|
|
2276
|
-
if (changed) {
|
|
2277
|
-
await saveAccounts(existingStorage);
|
|
2278
|
-
}
|
|
2279
|
-
activeAccountManager?.clearAccountVerificationRequired(accountIndex, true);
|
|
2280
|
-
console.log(`✓ ${provisionedEmail} verified and re-enabled.\n`);
|
|
2281
|
-
}
|
|
2282
|
-
else if (targetAccount.enabled === false) {
|
|
2283
|
-
targetAccount.enabled = true;
|
|
2284
|
-
await saveAccounts(existingStorage);
|
|
2285
|
-
activeAccountManager?.setAccountEnabled(accountIndex, true);
|
|
2286
|
-
console.log(`✓ ${provisionedEmail} verified and re-enabled.\n`);
|
|
2287
|
-
}
|
|
2288
|
-
else {
|
|
2289
|
-
console.log(`✓ ${provisionedEmail} verified and ready for requests.\n`);
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
else {
|
|
2293
|
-
console.log(`⚠ Verification probe still failing: ${postVerification.message}`);
|
|
2294
|
-
console.log("The API may take a few minutes to propagate. Try verifying again shortly.\n");
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
else {
|
|
2298
|
-
console.log(`✗ Gemini CLI login failed: ${result.error}\n`);
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
catch (error) {
|
|
2302
|
-
console.log(`✗ Gemini CLI login error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
2303
|
-
}
|
|
2304
|
-
finally {
|
|
2305
|
-
oauthListener?.close();
|
|
2306
|
-
}
|
|
2307
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2308
|
-
continue;
|
|
2309
|
-
}
|
|
2310
2251
|
if (menuResult.mode === "verify" || menuResult.mode === "verify-all") {
|
|
2311
2252
|
const verifyAll = menuResult.mode === "verify-all" || menuResult.verifyAll === true;
|
|
2312
2253
|
if (verifyAll) {
|
|
@@ -2338,11 +2279,11 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2338
2279
|
continue;
|
|
2339
2280
|
}
|
|
2340
2281
|
if (verification.status === "blocked") {
|
|
2341
|
-
const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl
|
|
2282
|
+
const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl);
|
|
2342
2283
|
if (changed) {
|
|
2343
2284
|
storageUpdated = true;
|
|
2344
2285
|
}
|
|
2345
|
-
activeAccountManager?.markAccountVerificationRequired(i, verification.message, verification.verifyUrl
|
|
2286
|
+
activeAccountManager?.markAccountVerificationRequired(i, verification.message, verification.verifyUrl);
|
|
2346
2287
|
blockedCount += 1;
|
|
2347
2288
|
console.log("needs verification");
|
|
2348
2289
|
const verifyUrl = verification.verifyUrl ?? account.verificationUrl;
|
|
@@ -2390,68 +2331,9 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2390
2331
|
const account = existingStorage.accounts[verifyAccountIndex];
|
|
2391
2332
|
if (!account) {
|
|
2392
2333
|
console.log(`\nAccount ${verifyAccountIndex + 1} not found.\n`);
|
|
2393
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2394
2334
|
continue;
|
|
2395
2335
|
}
|
|
2396
2336
|
const label = account.email || `Account ${verifyAccountIndex + 1}`;
|
|
2397
|
-
// Smart verify: if account is flagged as needing Gemini CLI login,
|
|
2398
|
-
// trigger the OAuth flow directly instead of just probing
|
|
2399
|
-
if (account.verificationRequired && account.verificationRequiredType === "gemini-cli") {
|
|
2400
|
-
console.log(`\n${label} needs Gemini CLI login. Starting OAuth flow...\n`);
|
|
2401
|
-
let oauthListener;
|
|
2402
|
-
try {
|
|
2403
|
-
oauthListener = await startOAuthListener();
|
|
2404
|
-
const geminiRedirectUri = ANTIGRAVITY_REDIRECT_URI;
|
|
2405
|
-
const authorization = await authorizeGeminiCli(geminiRedirectUri, account.email);
|
|
2406
|
-
const opened = await openBrowser(authorization.url);
|
|
2407
|
-
if (opened) {
|
|
2408
|
-
console.log("Opened authorization URL in your browser.");
|
|
2409
|
-
}
|
|
2410
|
-
else {
|
|
2411
|
-
console.log("Could not open browser. Please open this URL manually:");
|
|
2412
|
-
console.log(`\n${authorization.url}\n`);
|
|
2413
|
-
}
|
|
2414
|
-
console.log("Waiting for authorization...\n");
|
|
2415
|
-
const callbackUrl = await oauthListener.waitForCallback();
|
|
2416
|
-
const callbackParams = new URL(callbackUrl, "http://localhost").searchParams;
|
|
2417
|
-
const code = callbackParams.get("code");
|
|
2418
|
-
if (!code) {
|
|
2419
|
-
console.log("✗ Authorization failed: no code received.\n");
|
|
2420
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2421
|
-
continue;
|
|
2422
|
-
}
|
|
2423
|
-
const result = await exchangeGeminiCli(code, authorization.verifier, geminiRedirectUri);
|
|
2424
|
-
if (result.type === "success") {
|
|
2425
|
-
// Verify the account actually works now
|
|
2426
|
-
console.log("Verifying account access...");
|
|
2427
|
-
const postVerification = await verifyAccountAccess(account, client, providerId);
|
|
2428
|
-
if (postVerification.status === "ok") {
|
|
2429
|
-
const { changed } = clearStoredAccountVerificationRequired(account, true);
|
|
2430
|
-
if (changed) {
|
|
2431
|
-
await saveAccounts(existingStorage);
|
|
2432
|
-
}
|
|
2433
|
-
activeAccountManager?.clearAccountVerificationRequired(verifyAccountIndex, true);
|
|
2434
|
-
console.log(`✓ ${label} Gemini CLI access provisioned and verified. Account re-enabled.\n`);
|
|
2435
|
-
}
|
|
2436
|
-
else {
|
|
2437
|
-
console.log(`✓ Gemini CLI login completed for ${result.email || label}.`);
|
|
2438
|
-
console.log(`⚠ Verification probe still failing: ${postVerification.message}`);
|
|
2439
|
-
console.log("The API may take a few minutes to propagate. Try verifying again shortly.\n");
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
else {
|
|
2443
|
-
console.log(`✗ Gemini CLI login failed: ${result.error}\n`);
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
catch (error) {
|
|
2447
|
-
console.log(`✗ Gemini CLI login error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
2448
|
-
}
|
|
2449
|
-
finally {
|
|
2450
|
-
oauthListener?.close();
|
|
2451
|
-
}
|
|
2452
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
2337
|
console.log(`\nChecking verification status for ${label}...\n`);
|
|
2456
2338
|
const verification = await verifyAccountAccess(account, client, providerId);
|
|
2457
2339
|
if (verification.status === "ok") {
|
|
@@ -2466,15 +2348,14 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2466
2348
|
else {
|
|
2467
2349
|
console.log(`✓ ${label} is ready for requests.\n`);
|
|
2468
2350
|
}
|
|
2469
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2470
2351
|
continue;
|
|
2471
2352
|
}
|
|
2472
2353
|
if (verification.status === "blocked") {
|
|
2473
|
-
const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl
|
|
2354
|
+
const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl);
|
|
2474
2355
|
if (changed) {
|
|
2475
2356
|
await saveAccounts(existingStorage);
|
|
2476
2357
|
}
|
|
2477
|
-
activeAccountManager?.markAccountVerificationRequired(verifyAccountIndex, verification.message, verification.verifyUrl
|
|
2358
|
+
activeAccountManager?.markAccountVerificationRequired(verifyAccountIndex, verification.message, verification.verifyUrl);
|
|
2478
2359
|
const verifyUrl = verification.verifyUrl ?? account.verificationUrl;
|
|
2479
2360
|
console.log(`⚠ ${label} needs Google verification before it can be used.`);
|
|
2480
2361
|
if (verification.message) {
|
|
@@ -2496,11 +2377,9 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
2496
2377
|
else {
|
|
2497
2378
|
console.log("No verification URL was returned. Try re-authenticating this account.\n");
|
|
2498
2379
|
}
|
|
2499
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2500
2380
|
continue;
|
|
2501
2381
|
}
|
|
2502
2382
|
console.log(`✗ ${label}: ${verification.message}\n`);
|
|
2503
|
-
await promptOAuthCallbackValue("\nPress Enter to return to menu...");
|
|
2504
2383
|
continue;
|
|
2505
2384
|
}
|
|
2506
2385
|
break;
|