opencode-antigravity-auth 1.6.5-beta.0 → 2.0.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.
Files changed (126) hide show
  1. package/README.md +36 -12
  2. package/dist/index.d.ts +10 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +6 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/src/antigravity/oauth.d.ts +0 -28
  7. package/dist/src/antigravity/oauth.d.ts.map +1 -1
  8. package/dist/src/antigravity/oauth.js +25 -103
  9. package/dist/src/antigravity/oauth.js.map +1 -1
  10. package/dist/src/constants.d.ts +53 -32
  11. package/dist/src/constants.d.ts.map +1 -1
  12. package/dist/src/constants.js +64 -56
  13. package/dist/src/constants.js.map +1 -1
  14. package/dist/src/hooks/auto-update-checker/cache.js +1 -1
  15. package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
  16. package/dist/src/hooks/auto-update-checker/checker.js +2 -2
  17. package/dist/src/hooks/auto-update-checker/index.d.ts +4 -4
  18. package/dist/src/hooks/auto-update-checker/index.js +6 -6
  19. package/dist/src/hooks/auto-update-checker/logging.js +1 -1
  20. package/dist/src/plugin/accounts.d.ts +9 -10
  21. package/dist/src/plugin/accounts.d.ts.map +1 -1
  22. package/dist/src/plugin/accounts.js +14 -13
  23. package/dist/src/plugin/accounts.js.map +1 -1
  24. package/dist/src/plugin/auth.d.ts +1 -1
  25. package/dist/src/plugin/cache/index.d.ts +1 -1
  26. package/dist/src/plugin/cache/index.js +1 -1
  27. package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
  28. package/dist/src/plugin/cache/signature-cache.js +1 -1
  29. package/dist/src/plugin/cache.d.ts +7 -5
  30. package/dist/src/plugin/cache.d.ts.map +1 -1
  31. package/dist/src/plugin/cache.js +20 -3
  32. package/dist/src/plugin/cache.js.map +1 -1
  33. package/dist/src/plugin/cli.d.ts +4 -6
  34. package/dist/src/plugin/cli.d.ts.map +1 -1
  35. package/dist/src/plugin/cli.js +5 -11
  36. package/dist/src/plugin/cli.js.map +1 -1
  37. package/dist/src/plugin/config/index.d.ts +2 -2
  38. package/dist/src/plugin/config/index.js +2 -2
  39. package/dist/src/plugin/config/loader.d.ts +1 -1
  40. package/dist/src/plugin/config/loader.js +2 -2
  41. package/dist/src/plugin/config/models-v2.d.ts +36 -0
  42. package/dist/src/plugin/config/models-v2.d.ts.map +1 -0
  43. package/dist/src/plugin/config/models-v2.js +51 -0
  44. package/dist/src/plugin/config/models-v2.js.map +1 -0
  45. package/dist/src/plugin/config/models.d.ts +1 -1
  46. package/dist/src/plugin/config/updater.d.ts +16 -3
  47. package/dist/src/plugin/config/updater.d.ts.map +1 -1
  48. package/dist/src/plugin/config/updater.js +53 -10
  49. package/dist/src/plugin/config/updater.js.map +1 -1
  50. package/dist/src/plugin/core/streaming/index.d.ts +2 -2
  51. package/dist/src/plugin/core/streaming/index.js +2 -2
  52. package/dist/src/plugin/core/streaming/transformer.d.ts +1 -1
  53. package/dist/src/plugin/core/streaming/transformer.js +1 -1
  54. package/dist/src/plugin/debug.d.ts +1 -1
  55. package/dist/src/plugin/debug.js +2 -2
  56. package/dist/src/plugin/fingerprint.d.ts +8 -5
  57. package/dist/src/plugin/fingerprint.d.ts.map +1 -1
  58. package/dist/src/plugin/fingerprint.js +23 -41
  59. package/dist/src/plugin/fingerprint.js.map +1 -1
  60. package/dist/src/plugin/logger.d.ts +1 -1
  61. package/dist/src/plugin/logger.js +2 -2
  62. package/dist/src/plugin/project.d.ts +4 -4
  63. package/dist/src/plugin/project.d.ts.map +1 -1
  64. package/dist/src/plugin/project.js +95 -94
  65. package/dist/src/plugin/project.js.map +1 -1
  66. package/dist/src/plugin/quota.d.ts +2 -2
  67. package/dist/src/plugin/quota.d.ts.map +1 -1
  68. package/dist/src/plugin/quota.js +12 -8
  69. package/dist/src/plugin/quota.js.map +1 -1
  70. package/dist/src/plugin/recovery/index.d.ts +3 -3
  71. package/dist/src/plugin/recovery/index.js +3 -3
  72. package/dist/src/plugin/recovery/storage.d.ts +1 -1
  73. package/dist/src/plugin/recovery/storage.js +1 -1
  74. package/dist/src/plugin/recovery.d.ts +3 -3
  75. package/dist/src/plugin/recovery.js +3 -3
  76. package/dist/src/plugin/refresh-queue.d.ts +2 -2
  77. package/dist/src/plugin/refresh-queue.js +2 -2
  78. package/dist/src/plugin/request-helpers.d.ts +1 -1
  79. package/dist/src/plugin/request-helpers.js +5 -5
  80. package/dist/src/plugin/request.d.ts +9 -6
  81. package/dist/src/plugin/request.d.ts.map +1 -1
  82. package/dist/src/plugin/request.js +23 -23
  83. package/dist/src/plugin/request.js.map +1 -1
  84. package/dist/src/plugin/search.js +7 -7
  85. package/dist/src/plugin/search.js.map +1 -1
  86. package/dist/src/plugin/server.js +1 -1
  87. package/dist/src/plugin/storage.d.ts +3 -5
  88. package/dist/src/plugin/storage.d.ts.map +1 -1
  89. package/dist/src/plugin/storage.js +56 -67
  90. package/dist/src/plugin/storage.js.map +1 -1
  91. package/dist/src/plugin/stores/signature-store.d.ts +1 -1
  92. package/dist/src/plugin/token.d.ts +1 -1
  93. package/dist/src/plugin/token.js +5 -5
  94. package/dist/src/plugin/transform/claude.d.ts +1 -1
  95. package/dist/src/plugin/transform/claude.js +1 -1
  96. package/dist/src/plugin/transform/cross-model-sanitizer.js +2 -2
  97. package/dist/src/plugin/transform/gemini.d.ts +1 -1
  98. package/dist/src/plugin/transform/index.d.ts +9 -9
  99. package/dist/src/plugin/transform/index.js +4 -4
  100. package/dist/src/plugin/transform/model-resolver.d.ts +12 -1
  101. package/dist/src/plugin/transform/model-resolver.d.ts.map +1 -1
  102. package/dist/src/plugin/transform/model-resolver.js +26 -4
  103. package/dist/src/plugin/transform/model-resolver.js.map +1 -1
  104. package/dist/src/plugin/transform/types.d.ts +1 -1
  105. package/dist/src/plugin/types.d.ts +26 -2
  106. package/dist/src/plugin/types.d.ts.map +1 -1
  107. package/dist/src/plugin/ui/auth-menu.d.ts +1 -4
  108. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  109. package/dist/src/plugin/ui/auth-menu.js +8 -16
  110. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  111. package/dist/src/plugin/ui/confirm.js +1 -1
  112. package/dist/src/plugin/ui/select.js +1 -1
  113. package/dist/src/plugin/version.d.ts +35 -6
  114. package/dist/src/plugin/version.d.ts.map +1 -1
  115. package/dist/src/plugin/version.js +138 -42
  116. package/dist/src/plugin/version.js.map +1 -1
  117. package/dist/src/plugin.d.ts +11 -8
  118. package/dist/src/plugin.d.ts.map +1 -1
  119. package/dist/src/plugin.js +97 -276
  120. package/dist/src/plugin.js.map +1 -1
  121. package/dist/src/v2.d.ts +4 -0
  122. package/dist/src/v2.d.ts.map +1 -0
  123. package/dist/src/v2.js +391 -0
  124. package/dist/src/v2.js.map +1 -0
  125. package/index.js +4 -0
  126. package/package.json +14 -6
@@ -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, ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_ENDPOINT_PROD, ANTIGRAVITY_PROVIDER_ID, ANTIGRAVITY_REDIRECT_URI, getAntigravityHeaders, } from "./constants";
4
- import { authorizeAntigravity, exchangeAntigravity } from "./antigravity/oauth";
5
- import { authorizeGeminiCli, exchangeGeminiCli } from "./antigravity/oauth";
6
- import { accessTokenExpired, isOAuthAuth, parseRefreshParts, formatRefreshParts } from "./plugin/auth";
7
- import { promptAddAnotherAccount, promptLoginMode, promptProjectId } from "./plugin/cli";
8
- import { ensureProjectContext } from "./plugin/project";
9
- import { startAntigravityDebugRequest, logAntigravityDebugResponse, logAccountContext, logRateLimitEvent, logRateLimitSnapshot, logResponseBody, logModelFamily, isDebugEnabled, getLogFilePath, initializeDebug, } from "./plugin/debug";
10
- import { buildThinkingWarmupBody, isGenerativeLanguageRequest, prepareAntigravityRequest, transformAntigravityResponse, } from "./plugin/request";
11
- import { resolveModelWithTier } from "./plugin/transform/model-resolver";
12
- import { isEmptyResponseBody, createSyntheticErrorResponse, } from "./plugin/request-helpers";
13
- import { EmptyResponseError } from "./plugin/errors";
14
- import { AntigravityTokenRefreshError, refreshAccessToken } from "./plugin/token";
15
- import { startOAuthListener } from "./plugin/server";
16
- import { clearAccounts, loadAccounts, saveAccounts, saveAccountsReplace } from "./plugin/storage";
17
- import { AccountManager, parseRateLimitReason, calculateBackoffMs, computeSoftQuotaCacheTtlMs } from "./plugin/accounts";
18
- import { createAutoUpdateCheckerHook } from "./hooks/auto-update-checker";
19
- import { loadConfig, initRuntimeConfig } from "./plugin/config";
20
- import { createSessionRecoveryHook, getRecoverySuccessToast } from "./plugin/recovery";
21
- import { checkAccountsQuota } from "./plugin/quota";
22
- import { initDiskSignatureCache } from "./plugin/cache";
23
- import { createProactiveRefreshQueue } from "./plugin/refresh-queue";
24
- import { initLogger, createLogger } from "./plugin/logger";
25
- import { initHealthTracker, getHealthTracker, initTokenTracker, getTokenTracker } from "./plugin/rotation";
26
- import { initAntigravityVersion } from "./plugin/version";
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, saveAccounts, saveAccountsReplace } 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;
@@ -259,16 +258,6 @@ function extractVerificationErrorDetails(bodyText) {
259
258
  let validationRequired = lowerBody.includes("validation_required");
260
259
  let message;
261
260
  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
261
  const collectUrlsFromText = (text) => {
273
262
  for (const match of text.matchAll(/https:\/\/accounts\.google\.com\/[^\s"'<>]+/gi)) {
274
263
  if (match[0]) {
@@ -351,27 +340,15 @@ function extractVerificationErrorDetails(bodyText) {
351
340
  const fallback = decodedBody
352
341
  .split("\n")
353
342
  .map((line) => line.trim())
354
- .find((line) => line && !line.startsWith("data:") && /(verify|validation|required|subscription|license)/i.test(line));
343
+ .find((line) => line && !line.startsWith("data:") && /(verify|validation|required)/i.test(line));
355
344
  if (fallback) {
356
345
  message = fallback;
357
346
  }
358
347
  }
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
348
  return {
371
349
  validationRequired,
372
350
  message,
373
351
  verifyUrl: selectBestVerificationUrl([...verificationUrls]),
374
- verificationType,
375
352
  };
376
353
  }
377
354
  async function verifyAccountAccess(account, client, providerId) {
@@ -412,11 +389,12 @@ async function verifyAccountAccess(account, client, providerId) {
412
389
  Authorization: `Bearer ${refreshedAuth.access}`,
413
390
  "Content-Type": "application/json",
414
391
  };
415
- if (projectId) {
416
- headers["x-goog-user-project"] = projectId;
417
- }
418
392
  const requestBody = {
393
+ project: projectId,
419
394
  model: "gemini-3-flash",
395
+ requestType: "agent",
396
+ userAgent: "antigravity",
397
+ requestId: `agent-${crypto.randomUUID()}`,
420
398
  request: {
421
399
  model: "gemini-3-flash",
422
400
  contents: [{ role: "user", parts: [{ text: "ping" }] }],
@@ -427,7 +405,7 @@ async function verifyAccountAccess(account, client, providerId) {
427
405
  const timeoutId = setTimeout(() => controller.abort(), 20000);
428
406
  let response;
429
407
  try {
430
- response = await fetch(`${ANTIGRAVITY_ENDPOINT_PROD}/v1internal:streamGenerateContent?alt=sse`, {
408
+ response = await fetch(`${ANTIGRAVITY_ENDPOINT}/v1internal:streamGenerateContent?alt=sse`, {
431
409
  method: "POST",
432
410
  headers,
433
411
  body: JSON.stringify(requestBody),
@@ -459,14 +437,12 @@ async function verifyAccountAccess(account, client, providerId) {
459
437
  status: "blocked",
460
438
  message: extracted.message ?? "Google requires additional account verification.",
461
439
  verifyUrl: extracted.verifyUrl,
462
- verificationType: extracted.verificationType,
463
440
  };
464
441
  }
465
442
  const fallbackMessage = extracted.message ?? `Request failed (${response.status} ${response.statusText}).`;
466
443
  return {
467
444
  status: "error",
468
445
  message: fallbackMessage,
469
- verificationType: extracted.verificationType !== "unknown" ? extracted.verificationType : undefined,
470
446
  };
471
447
  }
472
448
  async function promptAccountIndexForVerification(accounts) {
@@ -507,7 +483,7 @@ async function promptOpenVerificationUrl() {
507
483
  const answer = (await promptOAuthCallbackValue("Open verification URL in your browser now? [Y/n]: ")).trim().toLowerCase();
508
484
  return answer === "" || answer === "y" || answer === "yes";
509
485
  }
510
- function markStoredAccountVerificationRequired(account, reason, verifyUrl, verificationType) {
486
+ function markStoredAccountVerificationRequired(account, reason, verifyUrl) {
511
487
  let changed = false;
512
488
  const wasVerificationRequired = account.verificationRequired === true;
513
489
  if (!wasVerificationRequired) {
@@ -528,11 +504,6 @@ function markStoredAccountVerificationRequired(account, reason, verifyUrl, verif
528
504
  account.verificationUrl = normalizedUrl;
529
505
  changed = true;
530
506
  }
531
- const normalizedType = verificationType?.trim();
532
- if (normalizedType && account.verificationRequiredType !== normalizedType) {
533
- account.verificationRequiredType = normalizedType;
534
- changed = true;
535
- }
536
507
  if (account.enabled !== false) {
537
508
  account.enabled = false;
538
509
  changed = true;
@@ -554,10 +525,6 @@ function clearStoredAccountVerificationRequired(account, enableIfRequired = fals
554
525
  account.verificationRequiredReason = undefined;
555
526
  changed = true;
556
527
  }
557
- if (account.verificationRequiredType !== undefined) {
558
- account.verificationRequiredType = undefined;
559
- changed = true;
560
- }
561
528
  if (account.verificationUrl !== undefined) {
562
529
  account.verificationUrl = undefined;
563
530
  changed = true;
@@ -636,7 +603,7 @@ function clampInt(value, min, max) {
636
603
  }
637
604
  return Math.min(max, Math.max(min, Math.floor(value)));
638
605
  }
639
- async function persistAccountPool(results, replaceAll = false) {
606
+ export async function persistAccountPool(results, replaceAll = false) {
640
607
  if (results.length === 0) {
641
608
  return;
642
609
  }
@@ -1026,18 +993,23 @@ function sleep(ms, signal) {
1026
993
  /**
1027
994
  * Creates an Antigravity OAuth plugin for a specific provider ID.
1028
995
  */
1029
- export const createAntigravityPlugin = (providerId) => async ({ client, directory }) => {
996
+ export const createAntigravityPlugin = (providerId) => async ({ client, directory, runtimeVersion = 1, fetch: sendFetch = globalThis.fetch }) => {
1030
997
  // Load configuration from files and environment variables
1031
998
  const config = loadConfig(directory);
1032
999
  initRuntimeConfig(config);
1033
1000
  // Cached getAuth function for tool access
1034
1001
  let cachedGetAuth = null;
1002
+ const refreshQueues = new Set();
1003
+ const transportManagers = new Set();
1004
+ let disposed = false;
1035
1005
  // Initialize debug with config
1036
1006
  initializeDebug(config);
1037
1007
  // Initialize structured logger for TUI integration
1038
1008
  initLogger(client);
1039
1009
  // Fetch latest Antigravity version from remote API (non-blocking, falls back to hardcoded)
1040
- await initAntigravityVersion();
1010
+ await refreshAntigravityVersion();
1011
+ const versionRefreshTimer = setInterval(() => { void refreshAntigravityVersion(); }, ANTIGRAVITY_VERSION_REFRESH_INTERVAL_MS);
1012
+ versionRefreshTimer.unref?.();
1041
1013
  // Initialize health tracker for hybrid strategy
1042
1014
  if (config.health_score) {
1043
1015
  initHealthTracker({
@@ -1060,19 +1032,21 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1060
1032
  }
1061
1033
  // Initialize disk signature cache if keep_thinking is enabled
1062
1034
  // This integrates with the in-memory cacheSignature/getCachedSignature functions
1063
- if (config.keep_thinking) {
1064
- initDiskSignatureCache(config.signature_cache);
1065
- }
1035
+ const releaseSignatureCache = config.keep_thinking
1036
+ ? acquireDiskSignatureCache(config.signature_cache)
1037
+ : undefined;
1066
1038
  // Initialize session recovery hook with full context
1067
- const sessionRecovery = createSessionRecoveryHook({ client, directory }, config);
1068
- const updateChecker = createAutoUpdateCheckerHook(client, directory, {
1039
+ const sessionRecovery = runtimeVersion === 1
1040
+ ? createSessionRecoveryHook({ client, directory }, config)
1041
+ : null;
1042
+ const updateChecker = runtimeVersion === 1 ? createAutoUpdateCheckerHook(client, directory, {
1069
1043
  showStartupToast: true,
1070
1044
  autoUpdate: config.auto_update,
1071
- });
1045
+ }) : null;
1072
1046
  // Event handler for session recovery and updates
1073
1047
  const eventHandler = async (input) => {
1074
1048
  // Forward to update checker
1075
- await updateChecker.event(input);
1049
+ await updateChecker?.event(input);
1076
1050
  // Track if this is a child session (subagent, background task)
1077
1051
  // This is used to filter toasts based on toast_scope config
1078
1052
  if (input.event.type === "session.created") {
@@ -1169,6 +1143,23 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1169
1143
  },
1170
1144
  });
1171
1145
  return {
1146
+ async flush() {
1147
+ await Promise.allSettled([...transportManagers].map((manager) => manager.flushSaveToDisk()));
1148
+ transportManagers.clear();
1149
+ },
1150
+ async dispose() {
1151
+ if (disposed)
1152
+ return;
1153
+ disposed = true;
1154
+ releaseSignatureCache?.();
1155
+ clearInterval(versionRefreshTimer);
1156
+ for (const queue of refreshQueues)
1157
+ queue.stop();
1158
+ refreshQueues.clear();
1159
+ await Promise.allSettled([...transportManagers].map((manager) => manager.flushSaveToDisk()));
1160
+ transportManagers.clear();
1161
+ cachedGetAuth = null;
1162
+ },
1172
1163
  event: eventHandler,
1173
1164
  tool: {
1174
1165
  google_search: googleSearchTool,
@@ -1176,11 +1167,22 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1176
1167
  auth: {
1177
1168
  provider: providerId,
1178
1169
  loader: async (getAuth, provider) => {
1170
+ if (disposed)
1171
+ throw new Error("Antigravity plugin has been disposed");
1172
+ if (runtimeVersion === 2) {
1173
+ for (const queue of refreshQueues)
1174
+ queue.stop();
1175
+ refreshQueues.clear();
1176
+ await Promise.allSettled([...transportManagers].map((manager) => manager.flushSaveToDisk()));
1177
+ transportManagers.clear();
1178
+ }
1179
1179
  // Cache getAuth for tool access
1180
1180
  cachedGetAuth = getAuth;
1181
1181
  const auth = await getAuth();
1182
1182
  // If OpenCode has no valid OAuth auth, clear any stale account storage
1183
1183
  if (!isOAuthAuth(auth)) {
1184
+ if (runtimeVersion === 2)
1185
+ return {};
1184
1186
  try {
1185
1187
  await clearAccounts();
1186
1188
  }
@@ -1195,7 +1197,10 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1195
1197
  const storedAccounts = await loadAccounts();
1196
1198
  // Note: AccountManager now ensures the current auth is always included in accounts
1197
1199
  const accountManager = await AccountManager.loadFromDisk(auth);
1200
+ if (disposed)
1201
+ throw new Error("Antigravity plugin has been disposed");
1198
1202
  activeAccountManager = accountManager;
1203
+ transportManagers.add(accountManager);
1199
1204
  if (accountManager.getAccountCount() > 0) {
1200
1205
  accountManager.requestSaveToDisk();
1201
1206
  }
@@ -1209,6 +1214,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1209
1214
  });
1210
1215
  refreshQueue.setAccountManager(accountManager);
1211
1216
  refreshQueue.start();
1217
+ refreshQueues.add(refreshQueue);
1212
1218
  }
1213
1219
  if (isDebugEnabled()) {
1214
1220
  const logPath = getLogFilePath();
@@ -1231,14 +1237,14 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1231
1237
  }
1232
1238
  }
1233
1239
  return {
1234
- apiKey: "",
1240
+ apiKey: "antigravity-oauth",
1235
1241
  async fetch(input, init) {
1236
1242
  if (!isGenerativeLanguageRequest(input)) {
1237
- return fetch(input, init);
1243
+ return sendFetch(input, init);
1238
1244
  }
1239
1245
  const latestAuth = await getAuth();
1240
1246
  if (!isOAuthAuth(latestAuth)) {
1241
- return fetch(input, init);
1247
+ return sendFetch(input, init);
1242
1248
  }
1243
1249
  if (accountManager.getAccountCount() === 0) {
1244
1250
  throw new Error("No Antigravity accounts configured. Run `opencode auth login`.");
@@ -1521,7 +1527,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1521
1527
  });
1522
1528
  try {
1523
1529
  pushDebug("thinking-warmup: start");
1524
- const warmupResponse = await fetch(warmupUrl, warmupInit);
1530
+ const warmupResponse = await sendFetch(warmupUrl, warmupInit);
1525
1531
  const transformed = await transformAntigravityResponse(warmupResponse, true, warmupDebugContext, prepared.requestedModel, projectId, warmupUrl, prepared.effectiveModel, prepared.sessionId);
1526
1532
  await transformed.text();
1527
1533
  markWarmupSuccess(prepared.sessionId);
@@ -1602,19 +1608,14 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1602
1608
  // Track capacity retries per endpoint to prevent infinite loops
1603
1609
  let capacityRetryCount = 0;
1604
1610
  let lastEndpointIndex = -1;
1605
- for (let i = 0; i < ANTIGRAVITY_ENDPOINT_FALLBACKS.length; i++) {
1611
+ const endpoints = getEndpointFallbacks(headerStyle);
1612
+ for (let i = 0; i < endpoints.length; i++) {
1606
1613
  // Reset capacity retry counter when switching to a new endpoint
1607
1614
  if (i !== lastEndpointIndex) {
1608
1615
  capacityRetryCount = 0;
1609
1616
  lastEndpointIndex = i;
1610
1617
  }
1611
- const currentEndpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i];
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
- }
1618
+ const currentEndpoint = endpoints[i];
1618
1619
  try {
1619
1620
  const prepared = prepareAntigravityRequest(input, init, accessToken, projectContext.effectiveProjectId, currentEndpoint, headerStyle, forceThinkingRecovery, {
1620
1621
  claudeToolHardening: config.claude_tool_hardening,
@@ -1659,7 +1660,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1659
1660
  if (config.account_selection_strategy === 'hybrid') {
1660
1661
  tokenConsumed = getTokenTracker().consume(account.index);
1661
1662
  }
1662
- const response = await fetch(prepared.request, prepared.init);
1663
+ const response = await sendFetch(prepared.request, prepared.init);
1663
1664
  pushDebug(`status=${response.status} ${response.statusText}`);
1664
1665
  // Handle 429 rate limit (or Service Overloaded) with improved logic
1665
1666
  if (response.status === 429 || response.status === 503 || response.status === 529) {
@@ -1838,27 +1839,12 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1838
1839
  if (extracted.validationRequired) {
1839
1840
  const verificationReason = extracted.message ?? "Google requires account verification.";
1840
1841
  const cooldownMs = 10 * 60 * 1000;
1841
- accountManager.markAccountVerificationRequired(account.index, verificationReason, extracted.verifyUrl, extracted.verificationType);
1842
+ accountManager.markAccountVerificationRequired(account.index, verificationReason, extracted.verifyUrl);
1842
1843
  accountManager.markAccountCoolingDown(account, cooldownMs, "validation-required");
1843
1844
  accountManager.markRateLimited(account, cooldownMs, family, headerStyle, model);
1844
1845
  const label = account.email || `Account ${account.index + 1}`;
1845
1846
  if (accountManager.shouldShowAccountToast(account.index, 60000)) {
1846
- let toastMessage;
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");
1847
+ await showToast(`⚠ ${label} needs verification. Run 'opencode auth login' and use Verify accounts.`, "warning");
1862
1848
  accountManager.markToastShown(account.index);
1863
1849
  }
1864
1850
  pushDebug(`verification-required: disabled account ${account.index}`);
@@ -1871,7 +1857,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1871
1857
  const shouldRetryEndpoint = (response.status === 403 ||
1872
1858
  response.status === 404 ||
1873
1859
  response.status >= 500);
1874
- if (shouldRetryEndpoint && i < ANTIGRAVITY_ENDPOINT_FALLBACKS.length - 1) {
1860
+ if (shouldRetryEndpoint && i < endpoints.length - 1) {
1875
1861
  await logResponseBody(debugContext, response, response.status);
1876
1862
  lastFailure = createFailureContext(response);
1877
1863
  continue;
@@ -1973,7 +1959,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
1973
1959
  headers: { "Content-Type": "application/json" }
1974
1960
  });
1975
1961
  }
1976
- if (i < ANTIGRAVITY_ENDPOINT_FALLBACKS.length - 1) {
1962
+ if (i < endpoints.length - 1) {
1977
1963
  lastError = error instanceof Error ? error : new Error(String(error));
1978
1964
  continue;
1979
1965
  }
@@ -2035,19 +2021,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2035
2021
  let status = 'unknown';
2036
2022
  if (acc.verificationRequired) {
2037
2023
  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
2024
  }
2052
2025
  else {
2053
2026
  const rateLimits = acc.rateLimitResetTimes;
@@ -2075,7 +2048,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2075
2048
  status,
2076
2049
  isCurrentAccount: idx === (existingStorage.activeIndex ?? 0),
2077
2050
  enabled: acc.enabled !== false,
2078
- verificationRequiredType: acc.verificationRequiredType,
2079
2051
  };
2080
2052
  });
2081
2053
  menuResult = await promptLoginMode(existingAccounts);
@@ -2218,95 +2190,6 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2218
2190
  }
2219
2191
  continue;
2220
2192
  }
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
2193
  if (menuResult.mode === "verify" || menuResult.mode === "verify-all") {
2311
2194
  const verifyAll = menuResult.mode === "verify-all" || menuResult.verifyAll === true;
2312
2195
  if (verifyAll) {
@@ -2338,11 +2221,11 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2338
2221
  continue;
2339
2222
  }
2340
2223
  if (verification.status === "blocked") {
2341
- const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl, verification.verificationType);
2224
+ const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl);
2342
2225
  if (changed) {
2343
2226
  storageUpdated = true;
2344
2227
  }
2345
- activeAccountManager?.markAccountVerificationRequired(i, verification.message, verification.verifyUrl, verification.verificationType);
2228
+ activeAccountManager?.markAccountVerificationRequired(i, verification.message, verification.verifyUrl);
2346
2229
  blockedCount += 1;
2347
2230
  console.log("needs verification");
2348
2231
  const verifyUrl = verification.verifyUrl ?? account.verificationUrl;
@@ -2390,68 +2273,9 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2390
2273
  const account = existingStorage.accounts[verifyAccountIndex];
2391
2274
  if (!account) {
2392
2275
  console.log(`\nAccount ${verifyAccountIndex + 1} not found.\n`);
2393
- await promptOAuthCallbackValue("\nPress Enter to return to menu...");
2394
2276
  continue;
2395
2277
  }
2396
2278
  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
2279
  console.log(`\nChecking verification status for ${label}...\n`);
2456
2280
  const verification = await verifyAccountAccess(account, client, providerId);
2457
2281
  if (verification.status === "ok") {
@@ -2466,15 +2290,14 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2466
2290
  else {
2467
2291
  console.log(`✓ ${label} is ready for requests.\n`);
2468
2292
  }
2469
- await promptOAuthCallbackValue("\nPress Enter to return to menu...");
2470
2293
  continue;
2471
2294
  }
2472
2295
  if (verification.status === "blocked") {
2473
- const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl, verification.verificationType);
2296
+ const changed = markStoredAccountVerificationRequired(account, verification.message, verification.verifyUrl);
2474
2297
  if (changed) {
2475
2298
  await saveAccounts(existingStorage);
2476
2299
  }
2477
- activeAccountManager?.markAccountVerificationRequired(verifyAccountIndex, verification.message, verification.verifyUrl, verification.verificationType);
2300
+ activeAccountManager?.markAccountVerificationRequired(verifyAccountIndex, verification.message, verification.verifyUrl);
2478
2301
  const verifyUrl = verification.verifyUrl ?? account.verificationUrl;
2479
2302
  console.log(`⚠ ${label} needs Google verification before it can be used.`);
2480
2303
  if (verification.message) {
@@ -2496,11 +2319,9 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
2496
2319
  else {
2497
2320
  console.log("No verification URL was returned. Try re-authenticating this account.\n");
2498
2321
  }
2499
- await promptOAuthCallbackValue("\nPress Enter to return to menu...");
2500
2322
  continue;
2501
2323
  }
2502
2324
  console.log(`✗ ${label}: ${verification.message}\n`);
2503
- await promptOAuthCallbackValue("\nPress Enter to return to menu...");
2504
2325
  continue;
2505
2326
  }
2506
2327
  break;