copilot-api-plus 1.0.55 → 1.0.57
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.en.md +862 -0
- package/README.md +866 -841
- package/dist/auth-BfBWPtWx.js +3 -0
- package/dist/{auth-D_mymhYC.js → auth-C7a3n_4O.js} +107 -11
- package/dist/auth-C7a3n_4O.js.map +1 -0
- package/dist/{error-BYsVGb6T.js → error-CvvAyU1E.js} +5 -2
- package/dist/{error-BYsVGb6T.js.map → error-CvvAyU1E.js.map} +1 -1
- package/dist/error-Djpro28X.js +2 -0
- package/dist/get-models-DIOdRXYx.js +4 -0
- package/dist/{get-models-DhYpjJVG.js → get-models-DmIjteNk.js} +2 -2
- package/dist/{get-models-DhYpjJVG.js.map → get-models-DmIjteNk.js.map} +1 -1
- package/dist/get-user-CGhBmkXO.js +3 -0
- package/dist/{get-user-wlP5uMaW.js → get-user-DOv07Myc.js} +2 -2
- package/dist/{get-user-wlP5uMaW.js.map → get-user-DOv07Myc.js.map} +1 -1
- package/dist/main.js +324 -107
- package/dist/main.js.map +1 -1
- package/dist/token-CoKq3Guw.js +5 -0
- package/dist/{token-Bg5qiNBd.js → token-D8U-wBLK.js} +3 -3
- package/dist/{token-Bg5qiNBd.js.map → token-D8U-wBLK.js.map} +1 -1
- package/package.json +2 -2
- package/dist/auth-D_mymhYC.js.map +0 -1
- package/dist/auth-DreFwlx2.js +0 -3
- package/dist/error-4DW6q2Mo.js +0 -2
- package/dist/get-models-PKzVxQmq.js +0 -4
- package/dist/get-user-CtEiwKow.js +0 -3
- package/dist/token-B_0VZjlS.js +0 -5
package/dist/main.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as ensurePaths, t as PATHS } from "./paths-BdbyVdad.js";
|
|
3
3
|
import { t as state } from "./state-CRpaW-qc.js";
|
|
4
|
-
import { c as githubHeaders, n as GITHUB_API_BASE_URL, o as copilotBaseUrl, s as copilotHeaders } from "./get-user-
|
|
5
|
-
import { n as forwardError, t as HTTPError } from "./error-
|
|
6
|
-
import { a as cacheModels, c as isNullish, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken } from "./token-
|
|
7
|
-
import {
|
|
4
|
+
import { c as githubHeaders, n as GITHUB_API_BASE_URL, o as copilotBaseUrl, s as copilotHeaders } from "./get-user-DOv07Myc.js";
|
|
5
|
+
import { n as forwardError, t as HTTPError } from "./error-CvvAyU1E.js";
|
|
6
|
+
import { a as cacheModels, c as isNullish, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken } from "./token-D8U-wBLK.js";
|
|
7
|
+
import { C as recordAccountFailure, _ as rotateAccount, f as getValidAccessToken, l as getCurrentAccountIndex, n as clearAntigravityAuth, o as getAntigravityAuthPath, r as disableCurrentAccount, s as getApiKey, u as getCurrentProjectId, w as recordAccountSuccess } from "./auth-C7a3n_4O.js";
|
|
8
8
|
import { n as getZenAuthPath, t as clearZenAuth } from "./auth-DIDShcrD.js";
|
|
9
|
-
import { n as getAntigravityUsage, r as isThinkingModel, t as getAntigravityModels } from "./get-models-
|
|
9
|
+
import { n as getAntigravityUsage, r as isThinkingModel, t as getAntigravityModels } from "./get-models-DmIjteNk.js";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { defineCommand, runMain } from "citty";
|
|
12
12
|
import consola from "consola";
|
|
@@ -531,7 +531,7 @@ function initProxyFromEnv() {
|
|
|
531
531
|
* Add a new Antigravity account via OAuth
|
|
532
532
|
*/
|
|
533
533
|
async function addAccount() {
|
|
534
|
-
const { setupAntigravity, loadAntigravityAuth } = await import("./auth-
|
|
534
|
+
const { setupAntigravity, loadAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
535
535
|
const existingAuth = await loadAntigravityAuth();
|
|
536
536
|
if (existingAuth && existingAuth.accounts.length > 0) {
|
|
537
537
|
const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
|
|
@@ -543,7 +543,7 @@ async function addAccount() {
|
|
|
543
543
|
* List all Antigravity accounts
|
|
544
544
|
*/
|
|
545
545
|
async function listAccounts() {
|
|
546
|
-
const { loadAntigravityAuth } = await import("./auth-
|
|
546
|
+
const { loadAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
547
547
|
const auth = await loadAntigravityAuth();
|
|
548
548
|
if (!auth || auth.accounts.length === 0) {
|
|
549
549
|
consola.info("No Antigravity accounts configured");
|
|
@@ -564,7 +564,7 @@ async function listAccounts() {
|
|
|
564
564
|
* Remove an Antigravity account by index
|
|
565
565
|
*/
|
|
566
566
|
async function removeAccount(index) {
|
|
567
|
-
const { loadAntigravityAuth, saveAntigravityAuth } = await import("./auth-
|
|
567
|
+
const { loadAntigravityAuth, saveAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
568
568
|
const auth = await loadAntigravityAuth();
|
|
569
569
|
if (!auth || auth.accounts.length === 0) {
|
|
570
570
|
consola.error("No Antigravity accounts configured");
|
|
@@ -583,7 +583,7 @@ async function removeAccount(index) {
|
|
|
583
583
|
* Clear all Antigravity accounts
|
|
584
584
|
*/
|
|
585
585
|
async function clearAccounts() {
|
|
586
|
-
const { clearAntigravityAuth } = await import("./auth-
|
|
586
|
+
const { clearAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
587
587
|
if (await consola.prompt("Are you sure you want to remove all Antigravity accounts?", {
|
|
588
588
|
type: "confirm",
|
|
589
589
|
initial: false
|
|
@@ -1225,6 +1225,214 @@ function modelLogger() {
|
|
|
1225
1225
|
};
|
|
1226
1226
|
}
|
|
1227
1227
|
//#endregion
|
|
1228
|
+
//#region src/services/antigravity/background-detection.ts
|
|
1229
|
+
/**
|
|
1230
|
+
* Antigravity Background Detection
|
|
1231
|
+
*
|
|
1232
|
+
* Detects agent/background requests and optionally downgrades
|
|
1233
|
+
* expensive models to cheaper alternatives to preserve quota.
|
|
1234
|
+
*
|
|
1235
|
+
* Detection uses multi-signal scoring — not a single heuristic:
|
|
1236
|
+
* 1. Explicit header: X-Request-Type: background (definitive)
|
|
1237
|
+
* 2. tool_calls / tool role pattern (strong agent signal)
|
|
1238
|
+
* 3. High density of assistant messages (weak signal alone)
|
|
1239
|
+
*
|
|
1240
|
+
* Controlled by env var: ANTIGRAVITY_BACKGROUND_DOWNGRADE=1
|
|
1241
|
+
*/
|
|
1242
|
+
const BACKGROUND_DOWNGRADE_ENABLED = process.env.ANTIGRAVITY_BACKGROUND_DOWNGRADE === "1";
|
|
1243
|
+
/**
|
|
1244
|
+
* Score threshold for downgrade (0–1).
|
|
1245
|
+
* 0.6 means we need at least one strong signal.
|
|
1246
|
+
*/
|
|
1247
|
+
const AGENT_SCORE_THRESHOLD = .6;
|
|
1248
|
+
/**
|
|
1249
|
+
* Model downgrade mapping: expensive → cheaper
|
|
1250
|
+
*/
|
|
1251
|
+
const DOWNGRADE_MAP = {
|
|
1252
|
+
"claude-sonnet-4-5": "gemini-2.5-flash",
|
|
1253
|
+
"claude-sonnet-4-5-thinking": "gemini-2.5-flash-thinking",
|
|
1254
|
+
"claude-opus-4-5-thinking": "claude-sonnet-4-5-thinking",
|
|
1255
|
+
"gemini-2.5-pro": "gemini-2.5-flash",
|
|
1256
|
+
"gemini-3-pro-high": "gemini-3-flash",
|
|
1257
|
+
"gemini-3-pro-low": "gemini-3-flash"
|
|
1258
|
+
};
|
|
1259
|
+
/**
|
|
1260
|
+
* Multi-signal agent detection.
|
|
1261
|
+
*
|
|
1262
|
+
* Scores the request on a 0–1 scale:
|
|
1263
|
+
* - Explicit header "X-Request-Type: background" → 1.0 (instant)
|
|
1264
|
+
* - Any message with tool_calls field → +0.5
|
|
1265
|
+
* - Any message with role "tool" → +0.4
|
|
1266
|
+
* - assistant messages ≥ 60% of all messages → +0.2
|
|
1267
|
+
* - More than 6 total messages → +0.1
|
|
1268
|
+
*
|
|
1269
|
+
* Score ≥ 0.6 → treated as agent/background request.
|
|
1270
|
+
*/
|
|
1271
|
+
function detectAgent(messages, headers) {
|
|
1272
|
+
const signals = [];
|
|
1273
|
+
const requestType = headers?.get("x-request-type");
|
|
1274
|
+
if (requestType === "background" || requestType === "agent") return {
|
|
1275
|
+
isAgent: true,
|
|
1276
|
+
score: 1,
|
|
1277
|
+
signals: ["explicit-header"]
|
|
1278
|
+
};
|
|
1279
|
+
let score = 0;
|
|
1280
|
+
const total = messages.length;
|
|
1281
|
+
if (total === 0) return {
|
|
1282
|
+
isAgent: false,
|
|
1283
|
+
score: 0,
|
|
1284
|
+
signals: []
|
|
1285
|
+
};
|
|
1286
|
+
if (messages.some((m) => m.tool_calls !== void 0 && m.tool_calls !== null)) {
|
|
1287
|
+
score += .5;
|
|
1288
|
+
signals.push("tool_calls");
|
|
1289
|
+
}
|
|
1290
|
+
if (messages.some((m) => m.role === "tool")) {
|
|
1291
|
+
score += .4;
|
|
1292
|
+
signals.push("tool-role");
|
|
1293
|
+
}
|
|
1294
|
+
const assistantCount = messages.filter((m) => m.role === "assistant").length;
|
|
1295
|
+
if (total >= 3 && assistantCount / total >= .6) {
|
|
1296
|
+
score += .2;
|
|
1297
|
+
signals.push("high-assistant-density");
|
|
1298
|
+
}
|
|
1299
|
+
if (total > 6) {
|
|
1300
|
+
score += .1;
|
|
1301
|
+
signals.push("long-conversation");
|
|
1302
|
+
}
|
|
1303
|
+
return {
|
|
1304
|
+
isAgent: score >= AGENT_SCORE_THRESHOLD,
|
|
1305
|
+
score: Math.min(score, 1),
|
|
1306
|
+
signals
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Optionally downgrade a model for background/agent requests.
|
|
1311
|
+
*
|
|
1312
|
+
* Returns the original model if:
|
|
1313
|
+
* - ANTIGRAVITY_BACKGROUND_DOWNGRADE is not "1"
|
|
1314
|
+
* - The request is not detected as agent/background
|
|
1315
|
+
* - No downgrade mapping exists for the model
|
|
1316
|
+
*/
|
|
1317
|
+
function maybeDowngradeModel(model, messages, headers) {
|
|
1318
|
+
if (!BACKGROUND_DOWNGRADE_ENABLED) return model;
|
|
1319
|
+
const result = detectAgent(messages, headers);
|
|
1320
|
+
if (!result.isAgent) return model;
|
|
1321
|
+
const downgraded = DOWNGRADE_MAP[model];
|
|
1322
|
+
if (!downgraded) return model;
|
|
1323
|
+
consola.info(`[background-detection] agent detected (score=${result.score.toFixed(2)}, signals=[${result.signals.join(",")}]), downgrading ${model} → ${downgraded}`);
|
|
1324
|
+
return downgraded;
|
|
1325
|
+
}
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/services/antigravity/circuit-breaker.ts
|
|
1328
|
+
const FAILURE_THRESHOLD = 3;
|
|
1329
|
+
const RESET_TIMEOUT_MS = 3e4;
|
|
1330
|
+
const HALF_OPEN_SUCCESS_THRESHOLD = 2;
|
|
1331
|
+
const CircuitState = {
|
|
1332
|
+
CLOSED: "CLOSED",
|
|
1333
|
+
OPEN: "OPEN",
|
|
1334
|
+
HALF_OPEN: "HALF_OPEN"
|
|
1335
|
+
};
|
|
1336
|
+
const breakers = /* @__PURE__ */ new Map();
|
|
1337
|
+
function getOrCreate(family) {
|
|
1338
|
+
const existing = breakers.get(family);
|
|
1339
|
+
if (existing) return existing;
|
|
1340
|
+
const fresh = {
|
|
1341
|
+
state: CircuitState.CLOSED,
|
|
1342
|
+
failureCount: 0,
|
|
1343
|
+
successCount: 0,
|
|
1344
|
+
lastFailureTime: 0
|
|
1345
|
+
};
|
|
1346
|
+
breakers.set(family, fresh);
|
|
1347
|
+
return fresh;
|
|
1348
|
+
}
|
|
1349
|
+
function getModelFamily(model) {
|
|
1350
|
+
if (model.includes("claude")) return "claude";
|
|
1351
|
+
if (model.includes("gemini")) return "gemini";
|
|
1352
|
+
return "other";
|
|
1353
|
+
}
|
|
1354
|
+
function canExecute(family) {
|
|
1355
|
+
const breaker = getOrCreate(family);
|
|
1356
|
+
switch (breaker.state) {
|
|
1357
|
+
case CircuitState.CLOSED: return true;
|
|
1358
|
+
case CircuitState.OPEN:
|
|
1359
|
+
if (Date.now() - breaker.lastFailureTime >= RESET_TIMEOUT_MS) {
|
|
1360
|
+
consola.info(`[circuit-breaker] ${family}: OPEN -> HALF_OPEN`);
|
|
1361
|
+
breaker.state = CircuitState.HALF_OPEN;
|
|
1362
|
+
breaker.successCount = 0;
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
return false;
|
|
1366
|
+
case CircuitState.HALF_OPEN: return true;
|
|
1367
|
+
default: return true;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
function recordSuccess(family) {
|
|
1371
|
+
const breaker = getOrCreate(family);
|
|
1372
|
+
switch (breaker.state) {
|
|
1373
|
+
case CircuitState.HALF_OPEN:
|
|
1374
|
+
breaker.successCount++;
|
|
1375
|
+
if (breaker.successCount >= HALF_OPEN_SUCCESS_THRESHOLD) {
|
|
1376
|
+
consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> CLOSED`);
|
|
1377
|
+
breaker.state = CircuitState.CLOSED;
|
|
1378
|
+
breaker.failureCount = 0;
|
|
1379
|
+
breaker.successCount = 0;
|
|
1380
|
+
}
|
|
1381
|
+
break;
|
|
1382
|
+
case CircuitState.CLOSED:
|
|
1383
|
+
breaker.failureCount = 0;
|
|
1384
|
+
break;
|
|
1385
|
+
default: break;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
function recordFailure(family) {
|
|
1389
|
+
const breaker = getOrCreate(family);
|
|
1390
|
+
breaker.failureCount++;
|
|
1391
|
+
breaker.lastFailureTime = Date.now();
|
|
1392
|
+
switch (breaker.state) {
|
|
1393
|
+
case CircuitState.CLOSED:
|
|
1394
|
+
if (breaker.failureCount >= FAILURE_THRESHOLD) {
|
|
1395
|
+
consola.info(`[circuit-breaker] ${family}: CLOSED -> OPEN`);
|
|
1396
|
+
breaker.state = CircuitState.OPEN;
|
|
1397
|
+
}
|
|
1398
|
+
break;
|
|
1399
|
+
case CircuitState.HALF_OPEN:
|
|
1400
|
+
consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> OPEN`);
|
|
1401
|
+
breaker.state = CircuitState.OPEN;
|
|
1402
|
+
break;
|
|
1403
|
+
default: break;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
function getBackoffDelay(family, baseDelay) {
|
|
1407
|
+
const breaker = breakers.get(family);
|
|
1408
|
+
if (!breaker || breaker.failureCount === 0) return baseDelay;
|
|
1409
|
+
const delay = baseDelay * Math.pow(2, breaker.failureCount - 1);
|
|
1410
|
+
return Math.min(delay, 3e4);
|
|
1411
|
+
}
|
|
1412
|
+
function parseRetryDelay$3(errorText) {
|
|
1413
|
+
try {
|
|
1414
|
+
const errorData = JSON.parse(errorText);
|
|
1415
|
+
const details = errorData.error?.details ?? [];
|
|
1416
|
+
for (const detail of details) {
|
|
1417
|
+
if (detail["@type"]?.includes("RetryInfo") && detail.retryDelay) {
|
|
1418
|
+
const match = /(\d+(?:\.\d+)?)s/.exec(detail.retryDelay);
|
|
1419
|
+
if (match) return Math.ceil(Number.parseFloat(match[1]) * 1e3);
|
|
1420
|
+
}
|
|
1421
|
+
if (detail.quotaResetDelay) {
|
|
1422
|
+
const match = /(\d+(?:\.\d+)?)(?:ms|s)/.exec(detail.quotaResetDelay);
|
|
1423
|
+
if (match) {
|
|
1424
|
+
const value = Number.parseFloat(match[1]);
|
|
1425
|
+
return detail.quotaResetDelay.includes("ms") ? Math.ceil(value) : Math.ceil(value * 1e3);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
const message = errorData.error?.message ?? "";
|
|
1430
|
+
const resetMatch = /quota will reset after (\d+(?:\.\d+)?)s/i.exec(message);
|
|
1431
|
+
if (resetMatch) return Math.ceil(Number.parseFloat(resetMatch[1]) * 1e3);
|
|
1432
|
+
} catch {}
|
|
1433
|
+
return 500;
|
|
1434
|
+
}
|
|
1435
|
+
//#endregion
|
|
1228
1436
|
//#region src/services/antigravity/stream-parser.ts
|
|
1229
1437
|
/**
|
|
1230
1438
|
* Create initial stream state
|
|
@@ -1582,12 +1790,17 @@ function createErrorResponse$1(message, type, status, details) {
|
|
|
1582
1790
|
* Create chat completion with Antigravity or standard Gemini API
|
|
1583
1791
|
* Priority: API Key > OAuth
|
|
1584
1792
|
*/
|
|
1585
|
-
async function createAntigravityChatCompletion(request) {
|
|
1793
|
+
async function createAntigravityChatCompletion(request, requestHeaders) {
|
|
1794
|
+
const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
|
|
1795
|
+
const effectiveRequest = effectiveModel !== request.model ? {
|
|
1796
|
+
...request,
|
|
1797
|
+
model: effectiveModel
|
|
1798
|
+
} : request;
|
|
1586
1799
|
const apiKey = getApiKey();
|
|
1587
|
-
if (apiKey) return await createWithApiKey(
|
|
1800
|
+
if (apiKey) return await createWithApiKey(effectiveRequest, apiKey);
|
|
1588
1801
|
const accessToken = await getValidAccessToken();
|
|
1589
1802
|
if (!accessToken) return createErrorResponse$1("No valid authentication available. Please set GEMINI_API_KEY environment variable or run OAuth login.", "auth_error", 401);
|
|
1590
|
-
return await createWithOAuth(
|
|
1803
|
+
return await createWithOAuth(effectiveRequest, accessToken);
|
|
1591
1804
|
}
|
|
1592
1805
|
/**
|
|
1593
1806
|
* Create chat completion using API Key (standard Gemini API)
|
|
@@ -1602,7 +1815,7 @@ async function createWithApiKey(request, apiKey) {
|
|
|
1602
1815
|
headers: { "Content-Type": "application/json" },
|
|
1603
1816
|
body: JSON.stringify(body)
|
|
1604
1817
|
});
|
|
1605
|
-
if (!response.ok) return await handleApiError$1(response);
|
|
1818
|
+
if (!response.ok) return (await handleApiError$1(response)).response;
|
|
1606
1819
|
return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
|
|
1607
1820
|
} catch (error) {
|
|
1608
1821
|
consola.error("Gemini API request error:", error);
|
|
@@ -1612,29 +1825,63 @@ async function createWithApiKey(request, apiKey) {
|
|
|
1612
1825
|
/**
|
|
1613
1826
|
* Create chat completion using OAuth (Antigravity private API)
|
|
1614
1827
|
* Note: Both Gemini and Claude models use the same endpoint and Gemini-style format
|
|
1828
|
+
*
|
|
1829
|
+
* Features:
|
|
1830
|
+
* - Circuit breaker per model family
|
|
1831
|
+
* - Retry with exponential backoff on 429/503
|
|
1832
|
+
* - Token refresh between attempts
|
|
1615
1833
|
*/
|
|
1616
|
-
|
|
1834
|
+
const MAX_RETRIES$4 = 5;
|
|
1835
|
+
async function createWithOAuth(request, _initialAccessToken) {
|
|
1617
1836
|
const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL : ANTIGRAVITY_NO_STREAM_URL;
|
|
1618
1837
|
const body = buildAntigravityRequestBody(request);
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
const
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1838
|
+
const bodyString = JSON.stringify(body);
|
|
1839
|
+
for (let attempt = 0; attempt <= MAX_RETRIES$4; attempt++) {
|
|
1840
|
+
const family = getModelFamily(request.model);
|
|
1841
|
+
if (!canExecute(family)) {
|
|
1842
|
+
consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
|
|
1843
|
+
await sleep(1e3);
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
const accessToken = await getValidAccessToken();
|
|
1847
|
+
if (!accessToken) return createErrorResponse$1("No valid Antigravity access token available.", "auth_error", 401);
|
|
1848
|
+
try {
|
|
1849
|
+
const response = await fetch(endpoint, {
|
|
1850
|
+
method: "POST",
|
|
1851
|
+
headers: {
|
|
1852
|
+
Host: ANTIGRAVITY_API_HOST,
|
|
1853
|
+
"User-Agent": ANTIGRAVITY_USER_AGENT$1,
|
|
1854
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1855
|
+
"Content-Type": "application/json",
|
|
1856
|
+
"Accept-Encoding": "gzip"
|
|
1857
|
+
},
|
|
1858
|
+
body: bodyString
|
|
1859
|
+
});
|
|
1860
|
+
if (response.ok) {
|
|
1861
|
+
recordSuccess(family);
|
|
1862
|
+
recordAccountSuccess(await getCurrentAccountIndex());
|
|
1863
|
+
return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
|
|
1864
|
+
}
|
|
1865
|
+
const errorResult = await handleApiError$1(response);
|
|
1866
|
+
if (errorResult.shouldRetry && attempt < MAX_RETRIES$4) {
|
|
1867
|
+
recordFailure(family);
|
|
1868
|
+
recordAccountFailure(await getCurrentAccountIndex());
|
|
1869
|
+
const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
|
|
1870
|
+
consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$4})`);
|
|
1871
|
+
await sleep(backoffDelay);
|
|
1872
|
+
continue;
|
|
1873
|
+
}
|
|
1874
|
+
return errorResult.response;
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
consola.error("Antigravity request error:", error);
|
|
1877
|
+
if (attempt < MAX_RETRIES$4) {
|
|
1878
|
+
await sleep(500);
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
return createErrorResponse$1(`Request failed: ${String(error)}`, "request_error", 500);
|
|
1882
|
+
}
|
|
1637
1883
|
}
|
|
1884
|
+
return createErrorResponse$1("Max retries exceeded", "api_error", 429);
|
|
1638
1885
|
}
|
|
1639
1886
|
/**
|
|
1640
1887
|
* Handle API error response
|
|
@@ -1643,8 +1890,19 @@ async function handleApiError$1(response) {
|
|
|
1643
1890
|
const errorText = await response.text();
|
|
1644
1891
|
consola.error(`Antigravity error: ${response.status} ${errorText}`);
|
|
1645
1892
|
if (response.status === 403) await disableCurrentAccount();
|
|
1646
|
-
if (response.status === 429 || response.status === 503)
|
|
1647
|
-
|
|
1893
|
+
if (response.status === 429 || response.status === 503) {
|
|
1894
|
+
await rotateAccount();
|
|
1895
|
+
return {
|
|
1896
|
+
shouldRetry: true,
|
|
1897
|
+
retryDelayMs: parseRetryDelay$3(errorText),
|
|
1898
|
+
response: createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText)
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
return {
|
|
1902
|
+
shouldRetry: false,
|
|
1903
|
+
retryDelayMs: 0,
|
|
1904
|
+
response: createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText)
|
|
1905
|
+
};
|
|
1648
1906
|
}
|
|
1649
1907
|
/**
|
|
1650
1908
|
* Generate request ID
|
|
@@ -1809,7 +2067,7 @@ function extractNonStreamContent(parts) {
|
|
|
1809
2067
|
const app$1 = new Hono();
|
|
1810
2068
|
app$1.post("/", async (c) => {
|
|
1811
2069
|
const body = await c.req.json();
|
|
1812
|
-
const response = await createAntigravityChatCompletion(body);
|
|
2070
|
+
const response = await createAntigravityChatCompletion(body, c.req.raw.headers);
|
|
1813
2071
|
const headers = new Headers(response.headers);
|
|
1814
2072
|
if (body.stream) return new Response(response.body, {
|
|
1815
2073
|
status: response.status,
|
|
@@ -2049,31 +2307,6 @@ function rotateEndpoint() {
|
|
|
2049
2307
|
consola.info(`Rotating endpoint: ${ANTIGRAVITY_ENDPOINTS[oldIndex]} → ${ANTIGRAVITY_ENDPOINTS[currentEndpointIndex]}`);
|
|
2050
2308
|
}
|
|
2051
2309
|
const ANTIGRAVITY_USER_AGENT = "antigravity/1.11.3 windows/amd64";
|
|
2052
|
-
const rateLimitTracker = {};
|
|
2053
|
-
function getModelFamily(model) {
|
|
2054
|
-
if (model.includes("claude")) return "claude";
|
|
2055
|
-
if (model.includes("gemini")) return "gemini";
|
|
2056
|
-
return "other";
|
|
2057
|
-
}
|
|
2058
|
-
function trackRateLimit(model) {
|
|
2059
|
-
const family = getModelFamily(model);
|
|
2060
|
-
if (!rateLimitTracker[family]) rateLimitTracker[family] = {
|
|
2061
|
-
lastLimitTime: 0,
|
|
2062
|
-
consecutiveErrors: 0
|
|
2063
|
-
};
|
|
2064
|
-
rateLimitTracker[family].lastLimitTime = Date.now();
|
|
2065
|
-
rateLimitTracker[family].consecutiveErrors++;
|
|
2066
|
-
}
|
|
2067
|
-
function clearRateLimitTracker(model) {
|
|
2068
|
-
const family = getModelFamily(model);
|
|
2069
|
-
if (rateLimitTracker[family]) rateLimitTracker[family].consecutiveErrors = 0;
|
|
2070
|
-
}
|
|
2071
|
-
function getBackoffDelay(model, baseDelay) {
|
|
2072
|
-
const info = rateLimitTracker[getModelFamily(model)];
|
|
2073
|
-
if (!info) return baseDelay;
|
|
2074
|
-
const multiplier = Math.min(Math.pow(2, info.consecutiveErrors - 1), 60);
|
|
2075
|
-
return Math.min(baseDelay * multiplier, 3e4);
|
|
2076
|
-
}
|
|
2077
2310
|
/**
|
|
2078
2311
|
* Extract text from system content (can be string or array)
|
|
2079
2312
|
*/
|
|
@@ -2243,18 +2476,29 @@ function createErrorResponse(type, message, status) {
|
|
|
2243
2476
|
*
|
|
2244
2477
|
* Features:
|
|
2245
2478
|
* - Endpoint fallback (daily → prod)
|
|
2246
|
-
* -
|
|
2479
|
+
* - Circuit breaker per model family (CLOSED/OPEN/HALF_OPEN)
|
|
2247
2480
|
* - Exponential backoff for consecutive errors
|
|
2248
2481
|
* - Smart retry for short delays (≤5s on same endpoint)
|
|
2249
2482
|
*/
|
|
2250
2483
|
const MAX_RETRIES$3 = 5;
|
|
2251
2484
|
const MAX_ENDPOINT_RETRIES = 2;
|
|
2252
|
-
async function executeAntigravityRequest(request) {
|
|
2253
|
-
const
|
|
2485
|
+
async function executeAntigravityRequest(request, requestHeaders) {
|
|
2486
|
+
const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
|
|
2487
|
+
const effectiveRequest = effectiveModel !== request.model ? {
|
|
2488
|
+
...request,
|
|
2489
|
+
model: effectiveModel
|
|
2490
|
+
} : request;
|
|
2491
|
+
const body = buildGeminiRequest(effectiveRequest, await getCurrentProjectId());
|
|
2254
2492
|
let endpointRetries = 0;
|
|
2255
2493
|
for (let attempt = 0; attempt <= MAX_RETRIES$3; attempt++) {
|
|
2494
|
+
const family = getModelFamily(effectiveRequest.model);
|
|
2495
|
+
if (!canExecute(family)) {
|
|
2496
|
+
consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
|
|
2497
|
+
await sleep(1e3);
|
|
2498
|
+
continue;
|
|
2499
|
+
}
|
|
2256
2500
|
const host = getCurrentHost();
|
|
2257
|
-
const endpoint =
|
|
2501
|
+
const endpoint = effectiveRequest.stream ? getStreamUrl(host) : getNoStreamUrl(host);
|
|
2258
2502
|
const accessToken = await getValidAccessToken();
|
|
2259
2503
|
if (!accessToken) return createErrorResponse("authentication_error", "No valid Antigravity access token available.", 401);
|
|
2260
2504
|
try {
|
|
@@ -2270,13 +2514,16 @@ async function executeAntigravityRequest(request) {
|
|
|
2270
2514
|
body: JSON.stringify(body)
|
|
2271
2515
|
});
|
|
2272
2516
|
if (response.ok) {
|
|
2273
|
-
|
|
2274
|
-
|
|
2517
|
+
recordSuccess(getModelFamily(effectiveRequest.model));
|
|
2518
|
+
recordAccountSuccess(await getCurrentAccountIndex());
|
|
2519
|
+
return effectiveRequest.stream ? transformStreamResponse(response, effectiveRequest.model) : await transformNonStreamResponse(response, effectiveRequest.model);
|
|
2275
2520
|
}
|
|
2276
|
-
const errorResult = await handleApiError(response,
|
|
2521
|
+
const errorResult = await handleApiError(response, effectiveRequest.model);
|
|
2277
2522
|
if (errorResult.shouldRetry && attempt < MAX_RETRIES$3) {
|
|
2278
|
-
|
|
2279
|
-
|
|
2523
|
+
const family = getModelFamily(effectiveRequest.model);
|
|
2524
|
+
recordFailure(family);
|
|
2525
|
+
recordAccountFailure(await getCurrentAccountIndex());
|
|
2526
|
+
const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
|
|
2280
2527
|
if (backoffDelay <= 5e3 || endpointRetries >= MAX_ENDPOINT_RETRIES) {
|
|
2281
2528
|
consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$3})`);
|
|
2282
2529
|
await sleep(backoffDelay);
|
|
@@ -2304,38 +2551,8 @@ async function executeAntigravityRequest(request) {
|
|
|
2304
2551
|
}
|
|
2305
2552
|
return createErrorResponse("api_error", "Max retries exceeded", 429);
|
|
2306
2553
|
}
|
|
2307
|
-
async function createAntigravityMessages(request) {
|
|
2308
|
-
return antigravityQueue.enqueue(() => executeAntigravityRequest(request));
|
|
2309
|
-
}
|
|
2310
|
-
/**
|
|
2311
|
-
* Parse retry delay from error response
|
|
2312
|
-
* Supports multiple formats:
|
|
2313
|
-
* - RetryInfo.retryDelay: "3.5s"
|
|
2314
|
-
* - quotaResetDelay: "3000ms" or "3s"
|
|
2315
|
-
* - message: "Your quota will reset after 3s"
|
|
2316
|
-
*/
|
|
2317
|
-
function parseRetryDelay$3(errorText) {
|
|
2318
|
-
try {
|
|
2319
|
-
const errorData = JSON.parse(errorText);
|
|
2320
|
-
const details = errorData.error?.details ?? [];
|
|
2321
|
-
for (const detail of details) {
|
|
2322
|
-
if (detail["@type"]?.includes("RetryInfo") && detail.retryDelay) {
|
|
2323
|
-
const match = /(\d+(?:\.\d+)?)s/.exec(detail.retryDelay);
|
|
2324
|
-
if (match) return Math.ceil(Number.parseFloat(match[1]) * 1e3);
|
|
2325
|
-
}
|
|
2326
|
-
if (detail.quotaResetDelay) {
|
|
2327
|
-
const match = /(\d+(?:\.\d+)?)(?:ms|s)/.exec(detail.quotaResetDelay);
|
|
2328
|
-
if (match) {
|
|
2329
|
-
const value = Number.parseFloat(match[1]);
|
|
2330
|
-
return detail.quotaResetDelay.includes("ms") ? Math.ceil(value) : Math.ceil(value * 1e3);
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
const message = errorData.error?.message ?? "";
|
|
2335
|
-
const resetMatch = /quota will reset after (\d+(?:\.\d+)?)s/i.exec(message);
|
|
2336
|
-
if (resetMatch) return Math.ceil(Number.parseFloat(resetMatch[1]) * 1e3);
|
|
2337
|
-
} catch {}
|
|
2338
|
-
return 500;
|
|
2554
|
+
async function createAntigravityMessages(request, requestHeaders) {
|
|
2555
|
+
return antigravityQueue.enqueue(() => executeAntigravityRequest(request, requestHeaders));
|
|
2339
2556
|
}
|
|
2340
2557
|
/**
|
|
2341
2558
|
* Handle API error response
|
|
@@ -2525,7 +2742,7 @@ antigravityMessagesRoute.post("/", async (c) => {
|
|
|
2525
2742
|
try {
|
|
2526
2743
|
const body = await c.req.json();
|
|
2527
2744
|
consola.debug("Antigravity message request:", body.model);
|
|
2528
|
-
const response = await createAntigravityMessages(body);
|
|
2745
|
+
const response = await createAntigravityMessages(body, c.req.raw.headers);
|
|
2529
2746
|
if (body.stream) {
|
|
2530
2747
|
const headers = new Headers();
|
|
2531
2748
|
headers.set("Content-Type", "text/event-stream");
|
|
@@ -3874,7 +4091,7 @@ async function runServer(options) {
|
|
|
3874
4091
|
} else if (options.antigravity) {
|
|
3875
4092
|
consola.info("Google Antigravity mode enabled");
|
|
3876
4093
|
state.antigravityMode = true;
|
|
3877
|
-
const { loadAntigravityAuth, setupAntigravity, getCurrentAccount, hasApiKey, getApiKey, setOAuthCredentials } = await import("./auth-
|
|
4094
|
+
const { loadAntigravityAuth, setupAntigravity, getCurrentAccount, hasApiKey, getApiKey, setOAuthCredentials } = await import("./auth-BfBWPtWx.js");
|
|
3878
4095
|
if (options.antigravityClientId && options.antigravityClientSecret) {
|
|
3879
4096
|
setOAuthCredentials(options.antigravityClientId, options.antigravityClientSecret);
|
|
3880
4097
|
consola.info("Using provided OAuth credentials from CLI");
|
|
@@ -3903,7 +4120,7 @@ async function runServer(options) {
|
|
|
3903
4120
|
}
|
|
3904
4121
|
if (!await getCurrentAccount() && !hasApiKey()) throw new Error("No enabled Antigravity accounts available");
|
|
3905
4122
|
}
|
|
3906
|
-
const { getAntigravityModels } = await import("./get-models-
|
|
4123
|
+
const { getAntigravityModels } = await import("./get-models-DIOdRXYx.js");
|
|
3907
4124
|
const models = await getAntigravityModels();
|
|
3908
4125
|
state.antigravityModels = models;
|
|
3909
4126
|
consola.info(`Available Antigravity models: \n${models.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
@@ -3913,7 +4130,7 @@ async function runServer(options) {
|
|
|
3913
4130
|
state.githubToken = options.githubToken;
|
|
3914
4131
|
consola.info("Using provided GitHub token");
|
|
3915
4132
|
try {
|
|
3916
|
-
const { getGitHubUser } = await import("./get-user-
|
|
4133
|
+
const { getGitHubUser } = await import("./get-user-CGhBmkXO.js");
|
|
3917
4134
|
const user = await getGitHubUser();
|
|
3918
4135
|
consola.info(`Logged in as ${user.login}`);
|
|
3919
4136
|
} catch (error) {
|
|
@@ -3924,10 +4141,10 @@ async function runServer(options) {
|
|
|
3924
4141
|
try {
|
|
3925
4142
|
await setupCopilotToken();
|
|
3926
4143
|
} catch (error) {
|
|
3927
|
-
const { HTTPError } = await import("./error-
|
|
4144
|
+
const { HTTPError } = await import("./error-Djpro28X.js");
|
|
3928
4145
|
if (error instanceof HTTPError && error.response.status === 401) {
|
|
3929
4146
|
consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
|
|
3930
|
-
const { clearGithubToken } = await import("./token-
|
|
4147
|
+
const { clearGithubToken } = await import("./token-CoKq3Guw.js");
|
|
3931
4148
|
await clearGithubToken();
|
|
3932
4149
|
consola.info("Please restart to re-authenticate");
|
|
3933
4150
|
}
|