opencode-antigravity-auth 2.0.0 → 2.1.1
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 +32 -710
- package/dist/index.d.ts +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 +23 -9
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +190 -22
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/config/models-v2.d.ts +19 -1
- package/dist/src/plugin/config/models-v2.d.ts.map +1 -1
- package/dist/src/plugin/config/models-v2.js +49 -38
- package/dist/src/plugin/config/models-v2.js.map +1 -1
- package/dist/src/plugin/quota.d.ts +33 -1
- package/dist/src/plugin/quota.d.ts.map +1 -1
- package/dist/src/plugin/quota.js +43 -18
- package/dist/src/plugin/quota.js.map +1 -1
- package/dist/src/plugin/refresh-queue.d.ts +4 -1
- package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
- package/dist/src/plugin/refresh-queue.js +21 -4
- package/dist/src/plugin/refresh-queue.js.map +1 -1
- package/dist/src/plugin/storage.d.ts +15 -0
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +61 -2
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/token.d.ts +1 -3
- package/dist/src/plugin/token.d.ts.map +1 -1
- package/dist/src/plugin/token.js +43 -3
- package/dist/src/plugin/token.js.map +1 -1
- package/dist/src/plugin/transform/model-resolver.d.ts +2 -5
- package/dist/src/plugin/transform/model-resolver.d.ts.map +1 -1
- package/dist/src/plugin/transform/model-resolver.js +9 -1
- package/dist/src/plugin/transform/model-resolver.js.map +1 -1
- package/dist/src/plugin/types.d.ts +5 -0
- package/dist/src/plugin/types.d.ts.map +1 -1
- package/dist/src/plugin.d.ts +3 -3
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/plugin.js +84 -26
- 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.map +1 -1
- package/dist/src/v2.js +444 -112
- package/dist/src/v2.js.map +1 -1
- package/package.json +9 -3
- package/tui.js +1 -0
package/dist/src/plugin.js
CHANGED
|
@@ -12,7 +12,7 @@ import { isEmptyResponseBody, createSyntheticErrorResponse, } from "./plugin/req
|
|
|
12
12
|
import { EmptyResponseError } from "./plugin/errors.js";
|
|
13
13
|
import { AntigravityTokenRefreshError, refreshAccessToken } from "./plugin/token.js";
|
|
14
14
|
import { startOAuthListener } from "./plugin/server.js";
|
|
15
|
-
import { clearAccounts, loadAccounts, saveAccounts, saveAccountsReplace } from "./plugin/storage.js";
|
|
15
|
+
import { clearAccounts, loadAccounts, saveAccountCredentials, saveAccounts, saveAccountsReplace, updateAccountCredentials, updateAccounts } from "./plugin/storage.js";
|
|
16
16
|
import { AccountManager, parseRateLimitReason, calculateBackoffMs, computeSoftQuotaCacheTtlMs } from "./plugin/accounts.js";
|
|
17
17
|
import { createAutoUpdateCheckerHook } from "./hooks/auto-update-checker/index.js";
|
|
18
18
|
import { loadConfig, initRuntimeConfig } from "./plugin/config/index.js";
|
|
@@ -98,7 +98,19 @@ async function triggerAsyncQuotaRefreshForAccount(accountManager, accountIndex,
|
|
|
98
98
|
quotaRefreshInProgressByEmail.delete(accountKey);
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
-
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
|
+
}
|
|
102
114
|
if (results[0]?.status === "ok" && results[0]?.quota?.groups) {
|
|
103
115
|
accountManager.updateQuotaCache(accountIndex, results[0].quota.groups);
|
|
104
116
|
accountManager.requestSaveToDisk();
|
|
@@ -993,15 +1005,17 @@ function sleep(ms, signal) {
|
|
|
993
1005
|
/**
|
|
994
1006
|
* Creates an Antigravity OAuth plugin for a specific provider ID.
|
|
995
1007
|
*/
|
|
996
|
-
export const createAntigravityPlugin = (providerId) => async ({ client, directory, runtimeVersion = 1, fetch: sendFetch = globalThis.fetch }) => {
|
|
1008
|
+
export const createAntigravityPlugin = (providerId) => async ({ client, directory, runtimeVersion = 1, fetch: sendFetch = globalThis.fetch, canUseAccount }) => {
|
|
997
1009
|
// Load configuration from files and environment variables
|
|
998
1010
|
const config = loadConfig(directory);
|
|
999
1011
|
initRuntimeConfig(config);
|
|
1000
1012
|
// Cached getAuth function for tool access
|
|
1001
1013
|
let cachedGetAuth = null;
|
|
1002
1014
|
const refreshQueues = new Set();
|
|
1015
|
+
const quotaRefreshes = new Set();
|
|
1003
1016
|
const transportManagers = new Set();
|
|
1004
1017
|
let disposed = false;
|
|
1018
|
+
let managerGeneration = 0;
|
|
1005
1019
|
// Initialize debug with config
|
|
1006
1020
|
initializeDebug(config);
|
|
1007
1021
|
// Initialize structured logger for TUI integration
|
|
@@ -1144,19 +1158,23 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1144
1158
|
});
|
|
1145
1159
|
return {
|
|
1146
1160
|
async flush() {
|
|
1147
|
-
|
|
1161
|
+
managerGeneration += 1;
|
|
1162
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1163
|
+
refreshQueues.clear();
|
|
1164
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1148
1165
|
transportManagers.clear();
|
|
1149
1166
|
},
|
|
1150
1167
|
async dispose() {
|
|
1151
1168
|
if (disposed)
|
|
1152
1169
|
return;
|
|
1153
1170
|
disposed = true;
|
|
1171
|
+
managerGeneration += 1;
|
|
1154
1172
|
releaseSignatureCache?.();
|
|
1155
1173
|
clearInterval(versionRefreshTimer);
|
|
1156
|
-
|
|
1157
|
-
queue.stop();
|
|
1174
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1158
1175
|
refreshQueues.clear();
|
|
1159
|
-
await Promise.allSettled([...
|
|
1176
|
+
await Promise.allSettled([...quotaRefreshes]);
|
|
1177
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1160
1178
|
transportManagers.clear();
|
|
1161
1179
|
cachedGetAuth = null;
|
|
1162
1180
|
},
|
|
@@ -1169,11 +1187,11 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1169
1187
|
loader: async (getAuth, provider) => {
|
|
1170
1188
|
if (disposed)
|
|
1171
1189
|
throw new Error("Antigravity plugin has been disposed");
|
|
1190
|
+
const expectedGeneration = managerGeneration;
|
|
1172
1191
|
if (runtimeVersion === 2) {
|
|
1173
|
-
|
|
1174
|
-
queue.stop();
|
|
1192
|
+
await Promise.allSettled([...refreshQueues].map((queue) => queue.stop()));
|
|
1175
1193
|
refreshQueues.clear();
|
|
1176
|
-
await Promise.allSettled([...transportManagers].map((manager) => manager.
|
|
1194
|
+
await Promise.allSettled([...transportManagers].map((manager) => manager.retire()));
|
|
1177
1195
|
transportManagers.clear();
|
|
1178
1196
|
}
|
|
1179
1197
|
// Cache getAuth for tool access
|
|
@@ -1196,11 +1214,22 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1196
1214
|
const authParts = parseRefreshParts(auth.refresh);
|
|
1197
1215
|
const storedAccounts = await loadAccounts();
|
|
1198
1216
|
// Note: AccountManager now ensures the current auth is always included in accounts
|
|
1199
|
-
const accountManager = await AccountManager.loadFromDisk(auth);
|
|
1200
|
-
if (
|
|
1201
|
-
|
|
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();
|
|
1202
1227
|
activeAccountManager = accountManager;
|
|
1203
1228
|
transportManagers.add(accountManager);
|
|
1229
|
+
if (runtimeVersion === 2)
|
|
1230
|
+
await accountManager.saveToDisk();
|
|
1231
|
+
if (disposed || managerGeneration !== expectedGeneration)
|
|
1232
|
+
return abandonManager();
|
|
1204
1233
|
if (accountManager.getAccountCount() > 0) {
|
|
1205
1234
|
accountManager.requestSaveToDisk();
|
|
1206
1235
|
}
|
|
@@ -1242,7 +1271,13 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1242
1271
|
if (!isGenerativeLanguageRequest(input)) {
|
|
1243
1272
|
return sendFetch(input, init);
|
|
1244
1273
|
}
|
|
1274
|
+
const checkLifetime = () => {
|
|
1275
|
+
if (disposed || (runtimeVersion === 2 && managerGeneration !== expectedGeneration))
|
|
1276
|
+
throw new Error("Antigravity accounts changed; retry the request");
|
|
1277
|
+
};
|
|
1278
|
+
checkLifetime();
|
|
1245
1279
|
const latestAuth = await getAuth();
|
|
1280
|
+
checkLifetime();
|
|
1246
1281
|
if (!isOAuthAuth(latestAuth)) {
|
|
1247
1282
|
return sendFetch(input, init);
|
|
1248
1283
|
}
|
|
@@ -1252,6 +1287,10 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1252
1287
|
const urlString = toUrlString(input);
|
|
1253
1288
|
const family = getModelFamilyFromUrl(urlString);
|
|
1254
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
|
+
}
|
|
1255
1294
|
const debugLines = [];
|
|
1256
1295
|
const pushDebug = (line) => {
|
|
1257
1296
|
if (!isDebugEnabled())
|
|
@@ -1303,30 +1342,30 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1303
1342
|
if (family !== "gemini")
|
|
1304
1343
|
return false;
|
|
1305
1344
|
// Use AccountManager method which properly checks for disabled/cooling-down accounts
|
|
1306
|
-
return accountManager.hasOtherAccountWithAntigravityAvailable(currentAccount.index, family, model);
|
|
1345
|
+
return accountManager.hasOtherAccountWithAntigravityAvailable(currentAccount.index, family, model, eligible);
|
|
1307
1346
|
};
|
|
1308
1347
|
while (true) {
|
|
1309
1348
|
// Check for abort at the start of each iteration
|
|
1310
1349
|
checkAborted();
|
|
1311
|
-
const accountCount = accountManager.
|
|
1350
|
+
const accountCount = accountManager.getEnabledAccounts().filter(eligible).length;
|
|
1312
1351
|
const routingDecision = resolveHeaderRoutingDecision(urlString, family, config);
|
|
1313
1352
|
const { cliFirst, preferredHeaderStyle, explicitQuota, allowQuotaFallback, } = routingDecision;
|
|
1314
1353
|
if (accountCount === 0) {
|
|
1315
1354
|
throw new Error("No Antigravity accounts available. Run `opencode auth login`.");
|
|
1316
1355
|
}
|
|
1317
1356
|
const softQuotaCacheTtlMs = computeSoftQuotaCacheTtlMs(config.soft_quota_cache_ttl_minutes, config.quota_refresh_interval_minutes);
|
|
1318
|
-
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);
|
|
1319
1358
|
if (!account && allowQuotaFallback) {
|
|
1320
1359
|
const alternateHeaderStyle = preferredHeaderStyle === "antigravity" ? "gemini-cli" : "antigravity";
|
|
1321
|
-
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);
|
|
1322
1361
|
if (account) {
|
|
1323
1362
|
pushDebug(`selected-by-fallback idx=${account.index} preferred=${preferredHeaderStyle} alternate=${alternateHeaderStyle}`);
|
|
1324
1363
|
}
|
|
1325
1364
|
}
|
|
1326
1365
|
if (!account) {
|
|
1327
|
-
if (accountManager.areAllAccountsOverSoftQuota(family, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, model)) {
|
|
1366
|
+
if (accountManager.areAllAccountsOverSoftQuota(family, config.soft_quota_threshold_percent, softQuotaCacheTtlMs, model, eligible)) {
|
|
1328
1367
|
const threshold = config.soft_quota_threshold_percent;
|
|
1329
|
-
const softQuotaWaitMs = accountManager.getMinWaitTimeForSoftQuota(family, threshold, softQuotaCacheTtlMs, model);
|
|
1368
|
+
const softQuotaWaitMs = accountManager.getMinWaitTimeForSoftQuota(family, threshold, softQuotaCacheTtlMs, model, eligible);
|
|
1330
1369
|
const maxWaitMs = (config.max_rate_limit_wait_seconds ?? 300) * 1000;
|
|
1331
1370
|
if (softQuotaWaitMs === null || (maxWaitMs > 0 && softQuotaWaitMs > maxWaitMs)) {
|
|
1332
1371
|
const waitTimeFormatted = softQuotaWaitMs ? formatWaitTime(softQuotaWaitMs) : "unknown";
|
|
@@ -1346,7 +1385,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1346
1385
|
}
|
|
1347
1386
|
const strictWait = !allowQuotaFallback;
|
|
1348
1387
|
// All accounts are rate-limited - wait and retry
|
|
1349
|
-
const waitMs = accountManager.getMinWaitTimeForFamily(family, model, preferredHeaderStyle, strictWait) || 60_000;
|
|
1388
|
+
const waitMs = accountManager.getMinWaitTimeForFamily(family, model, preferredHeaderStyle, strictWait, eligible) || 60_000;
|
|
1350
1389
|
const waitSecValue = Math.max(1, Math.ceil(waitMs / 1000));
|
|
1351
1390
|
pushDebug(`all-rate-limited family=${family} accounts=${accountCount} waitMs=${waitMs}`);
|
|
1352
1391
|
if (isDebugEnabled()) {
|
|
@@ -1414,7 +1453,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1414
1453
|
continue;
|
|
1415
1454
|
}
|
|
1416
1455
|
resetAccountFailureState(account.index);
|
|
1417
|
-
accountManager.updateFromAuth(account, refreshed);
|
|
1456
|
+
accountManager.updateFromAuth(account, refreshed, parseRefreshParts(authRecord.refresh).refreshToken);
|
|
1418
1457
|
authRecord = refreshed;
|
|
1419
1458
|
try {
|
|
1420
1459
|
await accountManager.saveToDisk();
|
|
@@ -1425,9 +1464,24 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1425
1464
|
}
|
|
1426
1465
|
catch (error) {
|
|
1427
1466
|
if (error instanceof AntigravityTokenRefreshError && error.code === "invalid_grant") {
|
|
1428
|
-
|
|
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);
|
|
1429
1483
|
if (removed) {
|
|
1430
|
-
log.warn("
|
|
1484
|
+
log.warn("Revoked account excluded from rotation - reauthenticate via `opencode auth login`");
|
|
1431
1485
|
try {
|
|
1432
1486
|
await accountManager.saveToDisk();
|
|
1433
1487
|
}
|
|
@@ -1487,7 +1541,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1487
1541
|
}
|
|
1488
1542
|
if (projectContext.auth.refresh !== authRecord.refresh ||
|
|
1489
1543
|
projectContext.auth.access !== authRecord.access) {
|
|
1490
|
-
accountManager.updateFromAuth(account, projectContext.auth);
|
|
1544
|
+
accountManager.updateFromAuth(account, projectContext.auth, parseRefreshParts(authRecord.refresh).refreshToken);
|
|
1491
1545
|
authRecord = projectContext.auth;
|
|
1492
1546
|
try {
|
|
1493
1547
|
await accountManager.saveToDisk();
|
|
@@ -1554,7 +1608,7 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1554
1608
|
// Antigravity-first fallback: exhaust antigravity across ALL accounts before gemini-cli
|
|
1555
1609
|
if (allowQuotaFallback && family === "gemini" && headerStyle === "antigravity") {
|
|
1556
1610
|
// Check if ANY other account has antigravity available
|
|
1557
|
-
if (accountManager.hasOtherAccountWithAntigravityAvailable(account.index, family, model)) {
|
|
1611
|
+
if (accountManager.hasOtherAccountWithAntigravityAvailable(account.index, family, model, eligible)) {
|
|
1558
1612
|
// Switch to another account with antigravity (preserve antigravity priority)
|
|
1559
1613
|
pushDebug(`antigravity rate-limited on account ${account.index}, but available on other accounts. Switching.`);
|
|
1560
1614
|
shouldSwitchAccount = true;
|
|
@@ -1867,7 +1921,11 @@ export const createAntigravityPlugin = (providerId) => async ({ client, director
|
|
|
1867
1921
|
account.consecutiveFailures = 0;
|
|
1868
1922
|
getHealthTracker().recordSuccess(account.index);
|
|
1869
1923
|
accountManager.markAccountUsed(account.index);
|
|
1870
|
-
|
|
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
|
+
}
|
|
1871
1929
|
}
|
|
1872
1930
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1873
1931
|
note: response.ok ? "Success" : `Error ${response.status}`,
|