oh-my-opencode 0.4.1 → 0.4.2
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.ko.md +3 -6
- package/README.md +3 -6
- package/dist/config/schema.d.ts +1 -0
- package/dist/index.js +1509 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6330,6 +6330,1481 @@ function createAutoUpdateCheckerHook(ctx) {
|
|
|
6330
6330
|
}
|
|
6331
6331
|
};
|
|
6332
6332
|
}
|
|
6333
|
+
// src/auth/antigravity/constants.ts
|
|
6334
|
+
var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
6335
|
+
var ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
6336
|
+
var ANTIGRAVITY_CALLBACK_PORT = 51121;
|
|
6337
|
+
var ANTIGRAVITY_REDIRECT_URI = `http://localhost:${ANTIGRAVITY_CALLBACK_PORT}/oauth-callback`;
|
|
6338
|
+
var ANTIGRAVITY_SCOPES = [
|
|
6339
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
6340
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
6341
|
+
"https://www.googleapis.com/auth/userinfo.profile",
|
|
6342
|
+
"https://www.googleapis.com/auth/cclog",
|
|
6343
|
+
"https://www.googleapis.com/auth/experimentsandconfigs"
|
|
6344
|
+
];
|
|
6345
|
+
var ANTIGRAVITY_ENDPOINT_FALLBACKS = [
|
|
6346
|
+
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
6347
|
+
"https://autopush-cloudcode-pa.sandbox.googleapis.com",
|
|
6348
|
+
"https://cloudcode-pa.googleapis.com"
|
|
6349
|
+
];
|
|
6350
|
+
var ANTIGRAVITY_API_VERSION = "v1internal";
|
|
6351
|
+
var ANTIGRAVITY_HEADERS = {
|
|
6352
|
+
"User-Agent": "google-api-nodejs-client/9.15.1",
|
|
6353
|
+
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
6354
|
+
"Client-Metadata": JSON.stringify({
|
|
6355
|
+
ideType: "IDE_UNSPECIFIED",
|
|
6356
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
6357
|
+
pluginType: "GEMINI"
|
|
6358
|
+
})
|
|
6359
|
+
};
|
|
6360
|
+
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
6361
|
+
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
6362
|
+
var GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
|
|
6363
|
+
var ANTIGRAVITY_TOKEN_REFRESH_BUFFER_MS = 60000;
|
|
6364
|
+
var SKIP_THOUGHT_SIGNATURE_VALIDATOR = "skip_thought_signature_validator";
|
|
6365
|
+
// node_modules/jose/dist/browser/lib/buffer_utils.js
|
|
6366
|
+
var encoder = new TextEncoder;
|
|
6367
|
+
var decoder = new TextDecoder;
|
|
6368
|
+
var MAX_INT32 = 2 ** 32;
|
|
6369
|
+
|
|
6370
|
+
// node_modules/jose/dist/browser/runtime/base64url.js
|
|
6371
|
+
var encodeBase64 = (input) => {
|
|
6372
|
+
let unencoded = input;
|
|
6373
|
+
if (typeof unencoded === "string") {
|
|
6374
|
+
unencoded = encoder.encode(unencoded);
|
|
6375
|
+
}
|
|
6376
|
+
const CHUNK_SIZE = 32768;
|
|
6377
|
+
const arr = [];
|
|
6378
|
+
for (let i = 0;i < unencoded.length; i += CHUNK_SIZE) {
|
|
6379
|
+
arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE)));
|
|
6380
|
+
}
|
|
6381
|
+
return btoa(arr.join(""));
|
|
6382
|
+
};
|
|
6383
|
+
var encode = (input) => {
|
|
6384
|
+
return encodeBase64(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
6385
|
+
};
|
|
6386
|
+
var decodeBase64 = (encoded) => {
|
|
6387
|
+
const binary = atob(encoded);
|
|
6388
|
+
const bytes = new Uint8Array(binary.length);
|
|
6389
|
+
for (let i = 0;i < binary.length; i++) {
|
|
6390
|
+
bytes[i] = binary.charCodeAt(i);
|
|
6391
|
+
}
|
|
6392
|
+
return bytes;
|
|
6393
|
+
};
|
|
6394
|
+
var decode = (input) => {
|
|
6395
|
+
let encoded = input;
|
|
6396
|
+
if (encoded instanceof Uint8Array) {
|
|
6397
|
+
encoded = decoder.decode(encoded);
|
|
6398
|
+
}
|
|
6399
|
+
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
|
|
6400
|
+
try {
|
|
6401
|
+
return decodeBase64(encoded);
|
|
6402
|
+
} catch {
|
|
6403
|
+
throw new TypeError("The input to be decoded is not correctly encoded.");
|
|
6404
|
+
}
|
|
6405
|
+
};
|
|
6406
|
+
|
|
6407
|
+
// node_modules/jose/dist/browser/util/base64url.js
|
|
6408
|
+
var exports_base64url = {};
|
|
6409
|
+
__export(exports_base64url, {
|
|
6410
|
+
encode: () => encode2,
|
|
6411
|
+
decode: () => decode2
|
|
6412
|
+
});
|
|
6413
|
+
var encode2 = encode;
|
|
6414
|
+
var decode2 = decode;
|
|
6415
|
+
// node_modules/@openauthjs/openauth/dist/esm/pkce.js
|
|
6416
|
+
function generateVerifier(length) {
|
|
6417
|
+
const buffer = new Uint8Array(length);
|
|
6418
|
+
crypto.getRandomValues(buffer);
|
|
6419
|
+
return exports_base64url.encode(buffer);
|
|
6420
|
+
}
|
|
6421
|
+
async function generateChallenge(verifier, method) {
|
|
6422
|
+
if (method === "plain")
|
|
6423
|
+
return verifier;
|
|
6424
|
+
const encoder2 = new TextEncoder;
|
|
6425
|
+
const data = encoder2.encode(verifier);
|
|
6426
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
6427
|
+
return exports_base64url.encode(new Uint8Array(hash));
|
|
6428
|
+
}
|
|
6429
|
+
async function generatePKCE(length = 64) {
|
|
6430
|
+
if (length < 43 || length > 128) {
|
|
6431
|
+
throw new Error("Code verifier length must be between 43 and 128 characters");
|
|
6432
|
+
}
|
|
6433
|
+
const verifier = generateVerifier(length);
|
|
6434
|
+
const challenge = await generateChallenge(verifier, "S256");
|
|
6435
|
+
return {
|
|
6436
|
+
verifier,
|
|
6437
|
+
challenge,
|
|
6438
|
+
method: "S256"
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
|
|
6442
|
+
// src/auth/antigravity/oauth.ts
|
|
6443
|
+
async function generatePKCEPair() {
|
|
6444
|
+
const pkce = await generatePKCE();
|
|
6445
|
+
return {
|
|
6446
|
+
verifier: pkce.verifier,
|
|
6447
|
+
challenge: pkce.challenge,
|
|
6448
|
+
method: pkce.method
|
|
6449
|
+
};
|
|
6450
|
+
}
|
|
6451
|
+
function encodeState(state) {
|
|
6452
|
+
const json = JSON.stringify(state);
|
|
6453
|
+
return Buffer.from(json, "utf8").toString("base64url");
|
|
6454
|
+
}
|
|
6455
|
+
function decodeState(encoded) {
|
|
6456
|
+
const normalized = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
6457
|
+
const padded = normalized.padEnd(normalized.length + (4 - normalized.length % 4) % 4, "=");
|
|
6458
|
+
const json = Buffer.from(padded, "base64").toString("utf8");
|
|
6459
|
+
const parsed = JSON.parse(json);
|
|
6460
|
+
if (typeof parsed.verifier !== "string") {
|
|
6461
|
+
throw new Error("Missing PKCE verifier in state");
|
|
6462
|
+
}
|
|
6463
|
+
return {
|
|
6464
|
+
verifier: parsed.verifier,
|
|
6465
|
+
projectId: typeof parsed.projectId === "string" ? parsed.projectId : undefined
|
|
6466
|
+
};
|
|
6467
|
+
}
|
|
6468
|
+
async function buildAuthURL(projectId, clientId = ANTIGRAVITY_CLIENT_ID, port = ANTIGRAVITY_CALLBACK_PORT) {
|
|
6469
|
+
const pkce = await generatePKCEPair();
|
|
6470
|
+
const state = {
|
|
6471
|
+
verifier: pkce.verifier,
|
|
6472
|
+
projectId
|
|
6473
|
+
};
|
|
6474
|
+
const redirectUri = `http://localhost:${port}/oauth-callback`;
|
|
6475
|
+
const url = new URL(GOOGLE_AUTH_URL);
|
|
6476
|
+
url.searchParams.set("client_id", clientId);
|
|
6477
|
+
url.searchParams.set("redirect_uri", redirectUri);
|
|
6478
|
+
url.searchParams.set("response_type", "code");
|
|
6479
|
+
url.searchParams.set("scope", ANTIGRAVITY_SCOPES.join(" "));
|
|
6480
|
+
url.searchParams.set("state", encodeState(state));
|
|
6481
|
+
url.searchParams.set("code_challenge", pkce.challenge);
|
|
6482
|
+
url.searchParams.set("code_challenge_method", "S256");
|
|
6483
|
+
url.searchParams.set("access_type", "offline");
|
|
6484
|
+
url.searchParams.set("prompt", "consent");
|
|
6485
|
+
return {
|
|
6486
|
+
url: url.toString(),
|
|
6487
|
+
verifier: pkce.verifier
|
|
6488
|
+
};
|
|
6489
|
+
}
|
|
6490
|
+
async function exchangeCode(code, verifier, clientId = ANTIGRAVITY_CLIENT_ID, clientSecret = ANTIGRAVITY_CLIENT_SECRET, port = ANTIGRAVITY_CALLBACK_PORT) {
|
|
6491
|
+
const redirectUri = `http://localhost:${port}/oauth-callback`;
|
|
6492
|
+
const params = new URLSearchParams({
|
|
6493
|
+
client_id: clientId,
|
|
6494
|
+
client_secret: clientSecret,
|
|
6495
|
+
code,
|
|
6496
|
+
grant_type: "authorization_code",
|
|
6497
|
+
redirect_uri: redirectUri,
|
|
6498
|
+
code_verifier: verifier
|
|
6499
|
+
});
|
|
6500
|
+
const response = await fetch(GOOGLE_TOKEN_URL, {
|
|
6501
|
+
method: "POST",
|
|
6502
|
+
headers: {
|
|
6503
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6504
|
+
},
|
|
6505
|
+
body: params
|
|
6506
|
+
});
|
|
6507
|
+
if (!response.ok) {
|
|
6508
|
+
const errorText = await response.text();
|
|
6509
|
+
throw new Error(`Token exchange failed: ${response.status} - ${errorText}`);
|
|
6510
|
+
}
|
|
6511
|
+
const data = await response.json();
|
|
6512
|
+
return {
|
|
6513
|
+
access_token: data.access_token,
|
|
6514
|
+
refresh_token: data.refresh_token,
|
|
6515
|
+
expires_in: data.expires_in,
|
|
6516
|
+
token_type: data.token_type
|
|
6517
|
+
};
|
|
6518
|
+
}
|
|
6519
|
+
async function fetchUserInfo(accessToken) {
|
|
6520
|
+
const response = await fetch(`${GOOGLE_USERINFO_URL}?alt=json`, {
|
|
6521
|
+
headers: {
|
|
6522
|
+
Authorization: `Bearer ${accessToken}`
|
|
6523
|
+
}
|
|
6524
|
+
});
|
|
6525
|
+
if (!response.ok) {
|
|
6526
|
+
throw new Error(`Failed to fetch user info: ${response.status}`);
|
|
6527
|
+
}
|
|
6528
|
+
const data = await response.json();
|
|
6529
|
+
return {
|
|
6530
|
+
email: data.email || "",
|
|
6531
|
+
name: data.name,
|
|
6532
|
+
picture: data.picture
|
|
6533
|
+
};
|
|
6534
|
+
}
|
|
6535
|
+
function startCallbackServer(timeoutMs = 5 * 60 * 1000) {
|
|
6536
|
+
let server = null;
|
|
6537
|
+
let timeoutId = null;
|
|
6538
|
+
let resolveCallback = null;
|
|
6539
|
+
let rejectCallback = null;
|
|
6540
|
+
const cleanup = () => {
|
|
6541
|
+
if (timeoutId) {
|
|
6542
|
+
clearTimeout(timeoutId);
|
|
6543
|
+
timeoutId = null;
|
|
6544
|
+
}
|
|
6545
|
+
if (server) {
|
|
6546
|
+
server.stop();
|
|
6547
|
+
server = null;
|
|
6548
|
+
}
|
|
6549
|
+
};
|
|
6550
|
+
server = Bun.serve({
|
|
6551
|
+
port: 0,
|
|
6552
|
+
fetch(request) {
|
|
6553
|
+
const url = new URL(request.url);
|
|
6554
|
+
if (url.pathname === "/oauth-callback") {
|
|
6555
|
+
const code = url.searchParams.get("code") || "";
|
|
6556
|
+
const state = url.searchParams.get("state") || "";
|
|
6557
|
+
const error = url.searchParams.get("error") || undefined;
|
|
6558
|
+
let responseBody;
|
|
6559
|
+
if (code && !error) {
|
|
6560
|
+
responseBody = "<html><body><h1>Login successful</h1><p>You can close this window.</p></body></html>";
|
|
6561
|
+
} else {
|
|
6562
|
+
responseBody = "<html><body><h1>Login failed</h1><p>Please check the CLI output.</p></body></html>";
|
|
6563
|
+
}
|
|
6564
|
+
setTimeout(() => {
|
|
6565
|
+
cleanup();
|
|
6566
|
+
if (resolveCallback) {
|
|
6567
|
+
resolveCallback({ code, state, error });
|
|
6568
|
+
}
|
|
6569
|
+
}, 100);
|
|
6570
|
+
return new Response(responseBody, {
|
|
6571
|
+
status: 200,
|
|
6572
|
+
headers: { "Content-Type": "text/html" }
|
|
6573
|
+
});
|
|
6574
|
+
}
|
|
6575
|
+
return new Response("Not Found", { status: 404 });
|
|
6576
|
+
}
|
|
6577
|
+
});
|
|
6578
|
+
const actualPort = server.port;
|
|
6579
|
+
const waitForCallback = () => {
|
|
6580
|
+
return new Promise((resolve4, reject) => {
|
|
6581
|
+
resolveCallback = resolve4;
|
|
6582
|
+
rejectCallback = reject;
|
|
6583
|
+
timeoutId = setTimeout(() => {
|
|
6584
|
+
cleanup();
|
|
6585
|
+
reject(new Error("OAuth callback timeout"));
|
|
6586
|
+
}, timeoutMs);
|
|
6587
|
+
});
|
|
6588
|
+
};
|
|
6589
|
+
return {
|
|
6590
|
+
port: actualPort,
|
|
6591
|
+
waitForCallback,
|
|
6592
|
+
close: cleanup
|
|
6593
|
+
};
|
|
6594
|
+
}
|
|
6595
|
+
// src/auth/antigravity/token.ts
|
|
6596
|
+
function isTokenExpired(tokens) {
|
|
6597
|
+
const expirationTime = tokens.timestamp + tokens.expires_in * 1000;
|
|
6598
|
+
return Date.now() >= expirationTime - ANTIGRAVITY_TOKEN_REFRESH_BUFFER_MS;
|
|
6599
|
+
}
|
|
6600
|
+
async function refreshAccessToken(refreshToken, clientId = ANTIGRAVITY_CLIENT_ID, clientSecret = ANTIGRAVITY_CLIENT_SECRET) {
|
|
6601
|
+
const params = new URLSearchParams({
|
|
6602
|
+
grant_type: "refresh_token",
|
|
6603
|
+
refresh_token: refreshToken,
|
|
6604
|
+
client_id: clientId,
|
|
6605
|
+
client_secret: clientSecret
|
|
6606
|
+
});
|
|
6607
|
+
const response = await fetch(GOOGLE_TOKEN_URL, {
|
|
6608
|
+
method: "POST",
|
|
6609
|
+
headers: {
|
|
6610
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
6611
|
+
},
|
|
6612
|
+
body: params
|
|
6613
|
+
});
|
|
6614
|
+
if (!response.ok) {
|
|
6615
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
6616
|
+
throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
6617
|
+
}
|
|
6618
|
+
const data = await response.json();
|
|
6619
|
+
return {
|
|
6620
|
+
access_token: data.access_token,
|
|
6621
|
+
refresh_token: data.refresh_token || refreshToken,
|
|
6622
|
+
expires_in: data.expires_in,
|
|
6623
|
+
token_type: data.token_type
|
|
6624
|
+
};
|
|
6625
|
+
}
|
|
6626
|
+
function parseStoredToken(stored) {
|
|
6627
|
+
const parts = stored.split("|");
|
|
6628
|
+
const [refreshToken, projectId, managedProjectId] = parts;
|
|
6629
|
+
return {
|
|
6630
|
+
refreshToken: refreshToken || "",
|
|
6631
|
+
projectId: projectId || undefined,
|
|
6632
|
+
managedProjectId: managedProjectId || undefined
|
|
6633
|
+
};
|
|
6634
|
+
}
|
|
6635
|
+
function formatTokenForStorage(refreshToken, projectId, managedProjectId) {
|
|
6636
|
+
return `${refreshToken}|${projectId}|${managedProjectId || ""}`;
|
|
6637
|
+
}
|
|
6638
|
+
// src/auth/antigravity/project.ts
|
|
6639
|
+
var projectContextCache = new Map;
|
|
6640
|
+
var CODE_ASSIST_METADATA = {
|
|
6641
|
+
ideType: "IDE_UNSPECIFIED",
|
|
6642
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
6643
|
+
pluginType: "GEMINI"
|
|
6644
|
+
};
|
|
6645
|
+
function extractProjectId(project) {
|
|
6646
|
+
if (!project) {
|
|
6647
|
+
return;
|
|
6648
|
+
}
|
|
6649
|
+
if (typeof project === "string") {
|
|
6650
|
+
const trimmed = project.trim();
|
|
6651
|
+
return trimmed || undefined;
|
|
6652
|
+
}
|
|
6653
|
+
if (typeof project === "object" && "id" in project) {
|
|
6654
|
+
const id = project.id;
|
|
6655
|
+
if (typeof id === "string") {
|
|
6656
|
+
const trimmed = id.trim();
|
|
6657
|
+
return trimmed || undefined;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
return;
|
|
6661
|
+
}
|
|
6662
|
+
async function callLoadCodeAssistAPI(accessToken) {
|
|
6663
|
+
const requestBody = {
|
|
6664
|
+
metadata: CODE_ASSIST_METADATA
|
|
6665
|
+
};
|
|
6666
|
+
const headers = {
|
|
6667
|
+
Authorization: `Bearer ${accessToken}`,
|
|
6668
|
+
"Content-Type": "application/json",
|
|
6669
|
+
"User-Agent": ANTIGRAVITY_HEADERS["User-Agent"],
|
|
6670
|
+
"X-Goog-Api-Client": ANTIGRAVITY_HEADERS["X-Goog-Api-Client"],
|
|
6671
|
+
"Client-Metadata": ANTIGRAVITY_HEADERS["Client-Metadata"]
|
|
6672
|
+
};
|
|
6673
|
+
for (const baseEndpoint of ANTIGRAVITY_ENDPOINT_FALLBACKS) {
|
|
6674
|
+
const url = `${baseEndpoint}/${ANTIGRAVITY_API_VERSION}:loadCodeAssist`;
|
|
6675
|
+
try {
|
|
6676
|
+
const response = await fetch(url, {
|
|
6677
|
+
method: "POST",
|
|
6678
|
+
headers,
|
|
6679
|
+
body: JSON.stringify(requestBody)
|
|
6680
|
+
});
|
|
6681
|
+
if (!response.ok) {
|
|
6682
|
+
continue;
|
|
6683
|
+
}
|
|
6684
|
+
const data = await response.json();
|
|
6685
|
+
return data;
|
|
6686
|
+
} catch {
|
|
6687
|
+
continue;
|
|
6688
|
+
}
|
|
6689
|
+
}
|
|
6690
|
+
return null;
|
|
6691
|
+
}
|
|
6692
|
+
async function fetchProjectContext(accessToken) {
|
|
6693
|
+
const cached = projectContextCache.get(accessToken);
|
|
6694
|
+
if (cached) {
|
|
6695
|
+
return cached;
|
|
6696
|
+
}
|
|
6697
|
+
const response = await callLoadCodeAssistAPI(accessToken);
|
|
6698
|
+
const projectId = response ? extractProjectId(response.cloudaicompanionProject) : undefined;
|
|
6699
|
+
const result = {
|
|
6700
|
+
cloudaicompanionProject: projectId || ""
|
|
6701
|
+
};
|
|
6702
|
+
if (projectId) {
|
|
6703
|
+
projectContextCache.set(accessToken, result);
|
|
6704
|
+
}
|
|
6705
|
+
return result;
|
|
6706
|
+
}
|
|
6707
|
+
function clearProjectContextCache(accessToken) {
|
|
6708
|
+
if (accessToken) {
|
|
6709
|
+
projectContextCache.delete(accessToken);
|
|
6710
|
+
} else {
|
|
6711
|
+
projectContextCache.clear();
|
|
6712
|
+
}
|
|
6713
|
+
}
|
|
6714
|
+
// src/auth/antigravity/request.ts
|
|
6715
|
+
function buildRequestHeaders(accessToken) {
|
|
6716
|
+
return {
|
|
6717
|
+
Authorization: `Bearer ${accessToken}`,
|
|
6718
|
+
"Content-Type": "application/json",
|
|
6719
|
+
"User-Agent": ANTIGRAVITY_HEADERS["User-Agent"],
|
|
6720
|
+
"X-Goog-Api-Client": ANTIGRAVITY_HEADERS["X-Goog-Api-Client"],
|
|
6721
|
+
"Client-Metadata": ANTIGRAVITY_HEADERS["Client-Metadata"]
|
|
6722
|
+
};
|
|
6723
|
+
}
|
|
6724
|
+
function extractModelFromBody(body) {
|
|
6725
|
+
const model = body.model;
|
|
6726
|
+
if (typeof model === "string" && model.trim()) {
|
|
6727
|
+
return model.trim();
|
|
6728
|
+
}
|
|
6729
|
+
return;
|
|
6730
|
+
}
|
|
6731
|
+
function extractModelFromUrl(url) {
|
|
6732
|
+
const match = url.match(/\/models\/([^:]+):/);
|
|
6733
|
+
if (match && match[1]) {
|
|
6734
|
+
return match[1];
|
|
6735
|
+
}
|
|
6736
|
+
return;
|
|
6737
|
+
}
|
|
6738
|
+
function extractActionFromUrl(url) {
|
|
6739
|
+
const match = url.match(/\/models\/[^:]+:(\w+)/);
|
|
6740
|
+
if (match && match[1]) {
|
|
6741
|
+
return match[1];
|
|
6742
|
+
}
|
|
6743
|
+
return;
|
|
6744
|
+
}
|
|
6745
|
+
function buildAntigravityUrl(baseEndpoint, action, streaming) {
|
|
6746
|
+
const query = streaming ? "?alt=sse" : "";
|
|
6747
|
+
return `${baseEndpoint}/${ANTIGRAVITY_API_VERSION}:${action}${query}`;
|
|
6748
|
+
}
|
|
6749
|
+
function getDefaultEndpoint() {
|
|
6750
|
+
return ANTIGRAVITY_ENDPOINT_FALLBACKS[0];
|
|
6751
|
+
}
|
|
6752
|
+
function generateRequestId() {
|
|
6753
|
+
return `agent-${crypto.randomUUID()}`;
|
|
6754
|
+
}
|
|
6755
|
+
function wrapRequestBody(body, projectId, modelName, sessionId) {
|
|
6756
|
+
const requestPayload = { ...body };
|
|
6757
|
+
delete requestPayload.model;
|
|
6758
|
+
return {
|
|
6759
|
+
project: projectId,
|
|
6760
|
+
model: modelName,
|
|
6761
|
+
userAgent: "antigravity",
|
|
6762
|
+
requestId: generateRequestId(),
|
|
6763
|
+
request: {
|
|
6764
|
+
...requestPayload,
|
|
6765
|
+
sessionId
|
|
6766
|
+
}
|
|
6767
|
+
};
|
|
6768
|
+
}
|
|
6769
|
+
function debugLog4(message) {
|
|
6770
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
6771
|
+
console.log(`[antigravity-request] ${message}`);
|
|
6772
|
+
}
|
|
6773
|
+
}
|
|
6774
|
+
function injectThoughtSignatureIntoFunctionCalls(body, signature) {
|
|
6775
|
+
const effectiveSignature = signature || SKIP_THOUGHT_SIGNATURE_VALIDATOR;
|
|
6776
|
+
debugLog4(`[TSIG][INJECT] signature=${effectiveSignature.substring(0, 30)}... (${signature ? "provided" : "default"})`);
|
|
6777
|
+
debugLog4(`[TSIG][INJECT] body keys: ${Object.keys(body).join(", ")}`);
|
|
6778
|
+
const contents = body.contents;
|
|
6779
|
+
if (!contents || !Array.isArray(contents)) {
|
|
6780
|
+
debugLog4(`[TSIG][INJECT] No contents array! Has messages: ${!!body.messages}`);
|
|
6781
|
+
return body;
|
|
6782
|
+
}
|
|
6783
|
+
debugLog4(`[TSIG][INJECT] Found ${contents.length} content blocks`);
|
|
6784
|
+
let injectedCount = 0;
|
|
6785
|
+
const modifiedContents = contents.map((content) => {
|
|
6786
|
+
if (!content.parts || !Array.isArray(content.parts)) {
|
|
6787
|
+
return content;
|
|
6788
|
+
}
|
|
6789
|
+
const modifiedParts = content.parts.map((part) => {
|
|
6790
|
+
if (part.functionCall && !part.thoughtSignature) {
|
|
6791
|
+
injectedCount++;
|
|
6792
|
+
return {
|
|
6793
|
+
...part,
|
|
6794
|
+
thoughtSignature: effectiveSignature
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
return part;
|
|
6798
|
+
});
|
|
6799
|
+
return { ...content, parts: modifiedParts };
|
|
6800
|
+
});
|
|
6801
|
+
debugLog4(`[TSIG][INJECT] injected signature into ${injectedCount} functionCall(s)`);
|
|
6802
|
+
return { ...body, contents: modifiedContents };
|
|
6803
|
+
}
|
|
6804
|
+
function isStreamingRequest(url, body) {
|
|
6805
|
+
const action = extractActionFromUrl(url);
|
|
6806
|
+
if (action === "streamGenerateContent") {
|
|
6807
|
+
return true;
|
|
6808
|
+
}
|
|
6809
|
+
if (body.stream === true) {
|
|
6810
|
+
return true;
|
|
6811
|
+
}
|
|
6812
|
+
return false;
|
|
6813
|
+
}
|
|
6814
|
+
function transformRequest(options) {
|
|
6815
|
+
const {
|
|
6816
|
+
url,
|
|
6817
|
+
body,
|
|
6818
|
+
accessToken,
|
|
6819
|
+
projectId,
|
|
6820
|
+
sessionId,
|
|
6821
|
+
modelName,
|
|
6822
|
+
endpointOverride,
|
|
6823
|
+
thoughtSignature
|
|
6824
|
+
} = options;
|
|
6825
|
+
const effectiveModel = modelName || extractModelFromBody(body) || extractModelFromUrl(url) || "gemini-3-pro-preview";
|
|
6826
|
+
const streaming = isStreamingRequest(url, body);
|
|
6827
|
+
const action = streaming ? "streamGenerateContent" : "generateContent";
|
|
6828
|
+
const endpoint = endpointOverride || getDefaultEndpoint();
|
|
6829
|
+
const transformedUrl = buildAntigravityUrl(endpoint, action, streaming);
|
|
6830
|
+
const headers = buildRequestHeaders(accessToken);
|
|
6831
|
+
if (streaming) {
|
|
6832
|
+
headers["Accept"] = "text/event-stream";
|
|
6833
|
+
}
|
|
6834
|
+
const bodyWithSignature = injectThoughtSignatureIntoFunctionCalls(body, thoughtSignature);
|
|
6835
|
+
const wrappedBody = wrapRequestBody(bodyWithSignature, projectId, effectiveModel, sessionId);
|
|
6836
|
+
return {
|
|
6837
|
+
url: transformedUrl,
|
|
6838
|
+
headers,
|
|
6839
|
+
body: wrappedBody,
|
|
6840
|
+
streaming
|
|
6841
|
+
};
|
|
6842
|
+
}
|
|
6843
|
+
// src/auth/antigravity/response.ts
|
|
6844
|
+
function extractUsageFromHeaders(headers) {
|
|
6845
|
+
const cached = headers.get("x-antigravity-cached-content-token-count");
|
|
6846
|
+
const total = headers.get("x-antigravity-total-token-count");
|
|
6847
|
+
const prompt = headers.get("x-antigravity-prompt-token-count");
|
|
6848
|
+
const candidates = headers.get("x-antigravity-candidates-token-count");
|
|
6849
|
+
if (!cached && !total && !prompt && !candidates) {
|
|
6850
|
+
return;
|
|
6851
|
+
}
|
|
6852
|
+
const usage = {};
|
|
6853
|
+
if (cached) {
|
|
6854
|
+
const parsed = parseInt(cached, 10);
|
|
6855
|
+
if (!isNaN(parsed)) {
|
|
6856
|
+
usage.cachedContentTokenCount = parsed;
|
|
6857
|
+
}
|
|
6858
|
+
}
|
|
6859
|
+
if (total) {
|
|
6860
|
+
const parsed = parseInt(total, 10);
|
|
6861
|
+
if (!isNaN(parsed)) {
|
|
6862
|
+
usage.totalTokenCount = parsed;
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
if (prompt) {
|
|
6866
|
+
const parsed = parseInt(prompt, 10);
|
|
6867
|
+
if (!isNaN(parsed)) {
|
|
6868
|
+
usage.promptTokenCount = parsed;
|
|
6869
|
+
}
|
|
6870
|
+
}
|
|
6871
|
+
if (candidates) {
|
|
6872
|
+
const parsed = parseInt(candidates, 10);
|
|
6873
|
+
if (!isNaN(parsed)) {
|
|
6874
|
+
usage.candidatesTokenCount = parsed;
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6877
|
+
return Object.keys(usage).length > 0 ? usage : undefined;
|
|
6878
|
+
}
|
|
6879
|
+
function extractRetryAfterMs(response, errorBody) {
|
|
6880
|
+
const retryAfterHeader = response.headers.get("Retry-After");
|
|
6881
|
+
if (retryAfterHeader) {
|
|
6882
|
+
const seconds = parseFloat(retryAfterHeader);
|
|
6883
|
+
if (!isNaN(seconds) && seconds > 0) {
|
|
6884
|
+
return Math.ceil(seconds * 1000);
|
|
6885
|
+
}
|
|
6886
|
+
}
|
|
6887
|
+
const retryAfterMsHeader = response.headers.get("retry-after-ms");
|
|
6888
|
+
if (retryAfterMsHeader) {
|
|
6889
|
+
const ms = parseInt(retryAfterMsHeader, 10);
|
|
6890
|
+
if (!isNaN(ms) && ms > 0) {
|
|
6891
|
+
return ms;
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
if (!errorBody) {
|
|
6895
|
+
return;
|
|
6896
|
+
}
|
|
6897
|
+
const error = errorBody.error;
|
|
6898
|
+
if (!error?.details || !Array.isArray(error.details)) {
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6901
|
+
const retryInfo = error.details.find((detail) => detail["@type"] === "type.googleapis.com/google.rpc.RetryInfo");
|
|
6902
|
+
if (!retryInfo?.retryDelay || typeof retryInfo.retryDelay !== "string") {
|
|
6903
|
+
return;
|
|
6904
|
+
}
|
|
6905
|
+
const match = retryInfo.retryDelay.match(/^([\d.]+)s$/);
|
|
6906
|
+
if (match?.[1]) {
|
|
6907
|
+
const seconds = parseFloat(match[1]);
|
|
6908
|
+
if (!isNaN(seconds) && seconds > 0) {
|
|
6909
|
+
return Math.ceil(seconds * 1000);
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
return;
|
|
6913
|
+
}
|
|
6914
|
+
function parseErrorBody(text) {
|
|
6915
|
+
try {
|
|
6916
|
+
const parsed = JSON.parse(text);
|
|
6917
|
+
if (parsed.error && typeof parsed.error === "object") {
|
|
6918
|
+
const errorObj = parsed.error;
|
|
6919
|
+
return {
|
|
6920
|
+
message: String(errorObj.message || "Unknown error"),
|
|
6921
|
+
type: errorObj.type ? String(errorObj.type) : undefined,
|
|
6922
|
+
code: errorObj.code
|
|
6923
|
+
};
|
|
6924
|
+
}
|
|
6925
|
+
if (parsed.message && typeof parsed.message === "string") {
|
|
6926
|
+
return {
|
|
6927
|
+
message: parsed.message,
|
|
6928
|
+
type: parsed.type ? String(parsed.type) : undefined,
|
|
6929
|
+
code: parsed.code
|
|
6930
|
+
};
|
|
6931
|
+
}
|
|
6932
|
+
return;
|
|
6933
|
+
} catch {
|
|
6934
|
+
return {
|
|
6935
|
+
message: text || "Unknown error"
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
6939
|
+
async function transformResponse(response) {
|
|
6940
|
+
const headers = new Headers(response.headers);
|
|
6941
|
+
const usage = extractUsageFromHeaders(headers);
|
|
6942
|
+
if (!response.ok) {
|
|
6943
|
+
const text = await response.text();
|
|
6944
|
+
const error = parseErrorBody(text);
|
|
6945
|
+
const retryAfterMs = extractRetryAfterMs(response, error ? { error } : undefined);
|
|
6946
|
+
let errorBody;
|
|
6947
|
+
try {
|
|
6948
|
+
errorBody = JSON.parse(text);
|
|
6949
|
+
} catch {
|
|
6950
|
+
errorBody = { error: { message: text } };
|
|
6951
|
+
}
|
|
6952
|
+
const retryMs = extractRetryAfterMs(response, errorBody) ?? retryAfterMs;
|
|
6953
|
+
if (retryMs) {
|
|
6954
|
+
headers.set("Retry-After", String(Math.ceil(retryMs / 1000)));
|
|
6955
|
+
headers.set("retry-after-ms", String(retryMs));
|
|
6956
|
+
}
|
|
6957
|
+
return {
|
|
6958
|
+
response: new Response(text, {
|
|
6959
|
+
status: response.status,
|
|
6960
|
+
statusText: response.statusText,
|
|
6961
|
+
headers
|
|
6962
|
+
}),
|
|
6963
|
+
usage,
|
|
6964
|
+
retryAfterMs: retryMs,
|
|
6965
|
+
error
|
|
6966
|
+
};
|
|
6967
|
+
}
|
|
6968
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
6969
|
+
const isJson = contentType.includes("application/json");
|
|
6970
|
+
if (!isJson) {
|
|
6971
|
+
return { response, usage };
|
|
6972
|
+
}
|
|
6973
|
+
try {
|
|
6974
|
+
const text = await response.text();
|
|
6975
|
+
const parsed = JSON.parse(text);
|
|
6976
|
+
let transformedBody = parsed;
|
|
6977
|
+
if (parsed.response !== undefined) {
|
|
6978
|
+
transformedBody = parsed.response;
|
|
6979
|
+
}
|
|
6980
|
+
return {
|
|
6981
|
+
response: new Response(JSON.stringify(transformedBody), {
|
|
6982
|
+
status: response.status,
|
|
6983
|
+
statusText: response.statusText,
|
|
6984
|
+
headers
|
|
6985
|
+
}),
|
|
6986
|
+
usage
|
|
6987
|
+
};
|
|
6988
|
+
} catch {
|
|
6989
|
+
return { response, usage };
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
function transformSseLine(line) {
|
|
6993
|
+
if (!line.startsWith("data:")) {
|
|
6994
|
+
return line;
|
|
6995
|
+
}
|
|
6996
|
+
const json = line.slice(5).trim();
|
|
6997
|
+
if (!json || json === "[DONE]") {
|
|
6998
|
+
return line;
|
|
6999
|
+
}
|
|
7000
|
+
try {
|
|
7001
|
+
const parsed = JSON.parse(json);
|
|
7002
|
+
if (parsed.response !== undefined) {
|
|
7003
|
+
return `data: ${JSON.stringify(parsed.response)}`;
|
|
7004
|
+
}
|
|
7005
|
+
return line;
|
|
7006
|
+
} catch {
|
|
7007
|
+
return line;
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
function createSseTransformStream() {
|
|
7011
|
+
const decoder2 = new TextDecoder;
|
|
7012
|
+
const encoder2 = new TextEncoder;
|
|
7013
|
+
let buffer = "";
|
|
7014
|
+
return new TransformStream({
|
|
7015
|
+
transform(chunk, controller) {
|
|
7016
|
+
buffer += decoder2.decode(chunk, { stream: true });
|
|
7017
|
+
const lines = buffer.split(`
|
|
7018
|
+
`);
|
|
7019
|
+
buffer = lines.pop() || "";
|
|
7020
|
+
for (const line of lines) {
|
|
7021
|
+
const transformed = transformSseLine(line);
|
|
7022
|
+
controller.enqueue(encoder2.encode(transformed + `
|
|
7023
|
+
`));
|
|
7024
|
+
}
|
|
7025
|
+
},
|
|
7026
|
+
flush(controller) {
|
|
7027
|
+
if (buffer) {
|
|
7028
|
+
const transformed = transformSseLine(buffer);
|
|
7029
|
+
controller.enqueue(encoder2.encode(transformed));
|
|
7030
|
+
}
|
|
7031
|
+
}
|
|
7032
|
+
});
|
|
7033
|
+
}
|
|
7034
|
+
async function transformStreamingResponse(response) {
|
|
7035
|
+
const headers = new Headers(response.headers);
|
|
7036
|
+
const usage = extractUsageFromHeaders(headers);
|
|
7037
|
+
if (!response.ok) {
|
|
7038
|
+
const text = await response.text();
|
|
7039
|
+
const error = parseErrorBody(text);
|
|
7040
|
+
let errorBody;
|
|
7041
|
+
try {
|
|
7042
|
+
errorBody = JSON.parse(text);
|
|
7043
|
+
} catch {
|
|
7044
|
+
errorBody = { error: { message: text } };
|
|
7045
|
+
}
|
|
7046
|
+
const retryAfterMs = extractRetryAfterMs(response, errorBody);
|
|
7047
|
+
if (retryAfterMs) {
|
|
7048
|
+
headers.set("Retry-After", String(Math.ceil(retryAfterMs / 1000)));
|
|
7049
|
+
headers.set("retry-after-ms", String(retryAfterMs));
|
|
7050
|
+
}
|
|
7051
|
+
return {
|
|
7052
|
+
response: new Response(text, {
|
|
7053
|
+
status: response.status,
|
|
7054
|
+
statusText: response.statusText,
|
|
7055
|
+
headers
|
|
7056
|
+
}),
|
|
7057
|
+
usage,
|
|
7058
|
+
retryAfterMs,
|
|
7059
|
+
error
|
|
7060
|
+
};
|
|
7061
|
+
}
|
|
7062
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
7063
|
+
const isEventStream = contentType.includes("text/event-stream") || response.url.includes("alt=sse");
|
|
7064
|
+
if (!isEventStream) {
|
|
7065
|
+
const text = await response.text();
|
|
7066
|
+
try {
|
|
7067
|
+
const parsed = JSON.parse(text);
|
|
7068
|
+
let transformedBody2 = parsed;
|
|
7069
|
+
if (parsed.response !== undefined) {
|
|
7070
|
+
transformedBody2 = parsed.response;
|
|
7071
|
+
}
|
|
7072
|
+
return {
|
|
7073
|
+
response: new Response(JSON.stringify(transformedBody2), {
|
|
7074
|
+
status: response.status,
|
|
7075
|
+
statusText: response.statusText,
|
|
7076
|
+
headers
|
|
7077
|
+
}),
|
|
7078
|
+
usage
|
|
7079
|
+
};
|
|
7080
|
+
} catch {
|
|
7081
|
+
return {
|
|
7082
|
+
response: new Response(text, {
|
|
7083
|
+
status: response.status,
|
|
7084
|
+
statusText: response.statusText,
|
|
7085
|
+
headers
|
|
7086
|
+
}),
|
|
7087
|
+
usage
|
|
7088
|
+
};
|
|
7089
|
+
}
|
|
7090
|
+
}
|
|
7091
|
+
if (!response.body) {
|
|
7092
|
+
return { response, usage };
|
|
7093
|
+
}
|
|
7094
|
+
headers.delete("content-length");
|
|
7095
|
+
headers.delete("content-encoding");
|
|
7096
|
+
headers.set("content-type", "text/event-stream; charset=utf-8");
|
|
7097
|
+
const transformStream = createSseTransformStream();
|
|
7098
|
+
const transformedBody = response.body.pipeThrough(transformStream);
|
|
7099
|
+
return {
|
|
7100
|
+
response: new Response(transformedBody, {
|
|
7101
|
+
status: response.status,
|
|
7102
|
+
statusText: response.statusText,
|
|
7103
|
+
headers
|
|
7104
|
+
}),
|
|
7105
|
+
usage
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
7108
|
+
function isStreamingResponse(response) {
|
|
7109
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
7110
|
+
return contentType.includes("text/event-stream") || response.url.includes("alt=sse");
|
|
7111
|
+
}
|
|
7112
|
+
// src/auth/antigravity/tools.ts
|
|
7113
|
+
function normalizeToolsForGemini(tools) {
|
|
7114
|
+
if (!tools || tools.length === 0) {
|
|
7115
|
+
return;
|
|
7116
|
+
}
|
|
7117
|
+
const functionDeclarations = [];
|
|
7118
|
+
for (const tool of tools) {
|
|
7119
|
+
if (!tool || typeof tool !== "object") {
|
|
7120
|
+
continue;
|
|
7121
|
+
}
|
|
7122
|
+
const toolType = tool.type ?? "function";
|
|
7123
|
+
if (toolType === "function" && tool.function) {
|
|
7124
|
+
const declaration = {
|
|
7125
|
+
name: tool.function.name
|
|
7126
|
+
};
|
|
7127
|
+
if (tool.function.description) {
|
|
7128
|
+
declaration.description = tool.function.description;
|
|
7129
|
+
}
|
|
7130
|
+
if (tool.function.parameters) {
|
|
7131
|
+
declaration.parameters = tool.function.parameters;
|
|
7132
|
+
} else {
|
|
7133
|
+
declaration.parameters = { type: "object", properties: {} };
|
|
7134
|
+
}
|
|
7135
|
+
functionDeclarations.push(declaration);
|
|
7136
|
+
} else if (toolType !== "function" && process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7137
|
+
console.warn(`[antigravity-tools] Unsupported tool type: "${toolType}". Tool will be skipped.`);
|
|
7138
|
+
}
|
|
7139
|
+
}
|
|
7140
|
+
if (functionDeclarations.length === 0) {
|
|
7141
|
+
return;
|
|
7142
|
+
}
|
|
7143
|
+
return { functionDeclarations };
|
|
7144
|
+
}
|
|
7145
|
+
// src/auth/antigravity/thinking.ts
|
|
7146
|
+
function shouldIncludeThinking(model) {
|
|
7147
|
+
if (!model || typeof model !== "string") {
|
|
7148
|
+
return false;
|
|
7149
|
+
}
|
|
7150
|
+
const lowerModel = model.toLowerCase();
|
|
7151
|
+
if (lowerModel.endsWith("-high")) {
|
|
7152
|
+
return true;
|
|
7153
|
+
}
|
|
7154
|
+
if (lowerModel.includes("thinking")) {
|
|
7155
|
+
return true;
|
|
7156
|
+
}
|
|
7157
|
+
return false;
|
|
7158
|
+
}
|
|
7159
|
+
function isThinkingPart(part) {
|
|
7160
|
+
if (part.thought === true) {
|
|
7161
|
+
return true;
|
|
7162
|
+
}
|
|
7163
|
+
if (part.type === "thinking" || part.type === "reasoning") {
|
|
7164
|
+
return true;
|
|
7165
|
+
}
|
|
7166
|
+
return false;
|
|
7167
|
+
}
|
|
7168
|
+
function extractThinkingBlocks(response) {
|
|
7169
|
+
const thinkingBlocks = [];
|
|
7170
|
+
if (response.candidates && Array.isArray(response.candidates)) {
|
|
7171
|
+
for (const candidate of response.candidates) {
|
|
7172
|
+
const parts = candidate.content?.parts;
|
|
7173
|
+
if (!parts || !Array.isArray(parts)) {
|
|
7174
|
+
continue;
|
|
7175
|
+
}
|
|
7176
|
+
for (let i = 0;i < parts.length; i++) {
|
|
7177
|
+
const part = parts[i];
|
|
7178
|
+
if (!part || typeof part !== "object") {
|
|
7179
|
+
continue;
|
|
7180
|
+
}
|
|
7181
|
+
if (isThinkingPart(part)) {
|
|
7182
|
+
const block = {
|
|
7183
|
+
text: part.text || "",
|
|
7184
|
+
index: thinkingBlocks.length
|
|
7185
|
+
};
|
|
7186
|
+
if (part.thought === true && part.thoughtSignature) {
|
|
7187
|
+
block.signature = part.thoughtSignature;
|
|
7188
|
+
} else if (part.signature) {
|
|
7189
|
+
block.signature = part.signature;
|
|
7190
|
+
}
|
|
7191
|
+
thinkingBlocks.push(block);
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
}
|
|
7196
|
+
if (response.content && Array.isArray(response.content)) {
|
|
7197
|
+
for (let i = 0;i < response.content.length; i++) {
|
|
7198
|
+
const item = response.content[i];
|
|
7199
|
+
if (!item || typeof item !== "object") {
|
|
7200
|
+
continue;
|
|
7201
|
+
}
|
|
7202
|
+
if (item.type === "thinking" || item.type === "reasoning") {
|
|
7203
|
+
thinkingBlocks.push({
|
|
7204
|
+
text: item.text || "",
|
|
7205
|
+
signature: item.signature,
|
|
7206
|
+
index: thinkingBlocks.length
|
|
7207
|
+
});
|
|
7208
|
+
}
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
const combinedThinking = thinkingBlocks.map((b) => b.text).join(`
|
|
7212
|
+
|
|
7213
|
+
`);
|
|
7214
|
+
return {
|
|
7215
|
+
thinkingBlocks,
|
|
7216
|
+
combinedThinking,
|
|
7217
|
+
hasThinking: thinkingBlocks.length > 0
|
|
7218
|
+
};
|
|
7219
|
+
}
|
|
7220
|
+
function transformCandidateThinking(candidate) {
|
|
7221
|
+
if (!candidate || typeof candidate !== "object") {
|
|
7222
|
+
return candidate;
|
|
7223
|
+
}
|
|
7224
|
+
const content = candidate.content;
|
|
7225
|
+
if (!content || typeof content !== "object" || !Array.isArray(content.parts)) {
|
|
7226
|
+
return candidate;
|
|
7227
|
+
}
|
|
7228
|
+
const thinkingTexts = [];
|
|
7229
|
+
const transformedParts = content.parts.map((part) => {
|
|
7230
|
+
if (part && typeof part === "object" && part.thought === true) {
|
|
7231
|
+
thinkingTexts.push(part.text || "");
|
|
7232
|
+
return {
|
|
7233
|
+
...part,
|
|
7234
|
+
type: "reasoning",
|
|
7235
|
+
thought: undefined
|
|
7236
|
+
};
|
|
7237
|
+
}
|
|
7238
|
+
return part;
|
|
7239
|
+
});
|
|
7240
|
+
const result = {
|
|
7241
|
+
...candidate,
|
|
7242
|
+
content: { ...content, parts: transformedParts }
|
|
7243
|
+
};
|
|
7244
|
+
if (thinkingTexts.length > 0) {
|
|
7245
|
+
result.reasoning_content = thinkingTexts.join(`
|
|
7246
|
+
|
|
7247
|
+
`);
|
|
7248
|
+
}
|
|
7249
|
+
return result;
|
|
7250
|
+
}
|
|
7251
|
+
function transformAnthropicThinking(content) {
|
|
7252
|
+
if (!content || !Array.isArray(content)) {
|
|
7253
|
+
return content;
|
|
7254
|
+
}
|
|
7255
|
+
return content.map((block) => {
|
|
7256
|
+
if (block && typeof block === "object" && block.type === "thinking") {
|
|
7257
|
+
return {
|
|
7258
|
+
type: "reasoning",
|
|
7259
|
+
text: block.text || "",
|
|
7260
|
+
...block.signature ? { signature: block.signature } : {}
|
|
7261
|
+
};
|
|
7262
|
+
}
|
|
7263
|
+
return block;
|
|
7264
|
+
});
|
|
7265
|
+
}
|
|
7266
|
+
function transformResponseThinking(response) {
|
|
7267
|
+
if (!response || typeof response !== "object") {
|
|
7268
|
+
return response;
|
|
7269
|
+
}
|
|
7270
|
+
const result = { ...response };
|
|
7271
|
+
if (Array.isArray(result.candidates)) {
|
|
7272
|
+
result.candidates = result.candidates.map(transformCandidateThinking);
|
|
7273
|
+
}
|
|
7274
|
+
if (Array.isArray(result.content)) {
|
|
7275
|
+
result.content = transformAnthropicThinking(result.content);
|
|
7276
|
+
}
|
|
7277
|
+
return result;
|
|
7278
|
+
}
|
|
7279
|
+
// src/auth/antigravity/thought-signature-store.ts
|
|
7280
|
+
var signatureStore = new Map;
|
|
7281
|
+
var sessionIdStore = new Map;
|
|
7282
|
+
function setThoughtSignature(sessionKey, signature) {
|
|
7283
|
+
if (sessionKey && signature) {
|
|
7284
|
+
signatureStore.set(sessionKey, signature);
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7287
|
+
function getThoughtSignature(sessionKey) {
|
|
7288
|
+
return signatureStore.get(sessionKey);
|
|
7289
|
+
}
|
|
7290
|
+
function getOrCreateSessionId(fetchInstanceId, sessionId) {
|
|
7291
|
+
if (sessionId) {
|
|
7292
|
+
sessionIdStore.set(fetchInstanceId, sessionId);
|
|
7293
|
+
return sessionId;
|
|
7294
|
+
}
|
|
7295
|
+
const existing = sessionIdStore.get(fetchInstanceId);
|
|
7296
|
+
if (existing) {
|
|
7297
|
+
return existing;
|
|
7298
|
+
}
|
|
7299
|
+
const n = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
7300
|
+
const newSessionId = `-${n}`;
|
|
7301
|
+
sessionIdStore.set(fetchInstanceId, newSessionId);
|
|
7302
|
+
return newSessionId;
|
|
7303
|
+
}
|
|
7304
|
+
// src/auth/antigravity/message-converter.ts
|
|
7305
|
+
function debugLog5(message) {
|
|
7306
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7307
|
+
console.log(`[antigravity-converter] ${message}`);
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
function convertOpenAIToGemini(messages, thoughtSignature) {
|
|
7311
|
+
debugLog5(`Converting ${messages.length} messages, signature: ${thoughtSignature ? "present" : "none"}`);
|
|
7312
|
+
const contents = [];
|
|
7313
|
+
for (const msg of messages) {
|
|
7314
|
+
if (msg.role === "system") {
|
|
7315
|
+
contents.push({
|
|
7316
|
+
role: "user",
|
|
7317
|
+
parts: [{ text: typeof msg.content === "string" ? msg.content : "" }]
|
|
7318
|
+
});
|
|
7319
|
+
continue;
|
|
7320
|
+
}
|
|
7321
|
+
if (msg.role === "user") {
|
|
7322
|
+
const parts = convertContentToParts(msg.content);
|
|
7323
|
+
contents.push({ role: "user", parts });
|
|
7324
|
+
continue;
|
|
7325
|
+
}
|
|
7326
|
+
if (msg.role === "assistant") {
|
|
7327
|
+
const parts = [];
|
|
7328
|
+
if (msg.content) {
|
|
7329
|
+
parts.push(...convertContentToParts(msg.content));
|
|
7330
|
+
}
|
|
7331
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
7332
|
+
for (const toolCall of msg.tool_calls) {
|
|
7333
|
+
let args = {};
|
|
7334
|
+
try {
|
|
7335
|
+
args = JSON.parse(toolCall.function.arguments);
|
|
7336
|
+
} catch {
|
|
7337
|
+
args = {};
|
|
7338
|
+
}
|
|
7339
|
+
const part = {
|
|
7340
|
+
functionCall: {
|
|
7341
|
+
name: toolCall.function.name,
|
|
7342
|
+
args
|
|
7343
|
+
}
|
|
7344
|
+
};
|
|
7345
|
+
part.thoughtSignature = thoughtSignature || SKIP_THOUGHT_SIGNATURE_VALIDATOR;
|
|
7346
|
+
debugLog5(`Injected signature into functionCall: ${toolCall.function.name} (${thoughtSignature ? "provided" : "default"})`);
|
|
7347
|
+
parts.push(part);
|
|
7348
|
+
}
|
|
7349
|
+
}
|
|
7350
|
+
if (parts.length > 0) {
|
|
7351
|
+
contents.push({ role: "model", parts });
|
|
7352
|
+
}
|
|
7353
|
+
continue;
|
|
7354
|
+
}
|
|
7355
|
+
if (msg.role === "tool") {
|
|
7356
|
+
let response = {};
|
|
7357
|
+
try {
|
|
7358
|
+
response = typeof msg.content === "string" ? JSON.parse(msg.content) : { result: msg.content };
|
|
7359
|
+
} catch {
|
|
7360
|
+
response = { result: msg.content };
|
|
7361
|
+
}
|
|
7362
|
+
const toolName = msg.name || "unknown";
|
|
7363
|
+
contents.push({
|
|
7364
|
+
role: "user",
|
|
7365
|
+
parts: [{
|
|
7366
|
+
functionResponse: {
|
|
7367
|
+
name: toolName,
|
|
7368
|
+
response
|
|
7369
|
+
}
|
|
7370
|
+
}]
|
|
7371
|
+
});
|
|
7372
|
+
continue;
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
debugLog5(`Converted to ${contents.length} content blocks`);
|
|
7376
|
+
return contents;
|
|
7377
|
+
}
|
|
7378
|
+
function convertContentToParts(content) {
|
|
7379
|
+
if (!content) {
|
|
7380
|
+
return [{ text: "" }];
|
|
7381
|
+
}
|
|
7382
|
+
if (typeof content === "string") {
|
|
7383
|
+
return [{ text: content }];
|
|
7384
|
+
}
|
|
7385
|
+
const parts = [];
|
|
7386
|
+
for (const part of content) {
|
|
7387
|
+
if (part.type === "text" && part.text) {
|
|
7388
|
+
parts.push({ text: part.text });
|
|
7389
|
+
} else if (part.type === "image_url" && part.image_url?.url) {
|
|
7390
|
+
const url = part.image_url.url;
|
|
7391
|
+
if (url.startsWith("data:")) {
|
|
7392
|
+
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
|
7393
|
+
if (match) {
|
|
7394
|
+
parts.push({
|
|
7395
|
+
inlineData: {
|
|
7396
|
+
mimeType: match[1],
|
|
7397
|
+
data: match[2]
|
|
7398
|
+
}
|
|
7399
|
+
});
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7404
|
+
return parts.length > 0 ? parts : [{ text: "" }];
|
|
7405
|
+
}
|
|
7406
|
+
function hasOpenAIMessages(body) {
|
|
7407
|
+
return Array.isArray(body.messages) && body.messages.length > 0;
|
|
7408
|
+
}
|
|
7409
|
+
function convertRequestBody(body, thoughtSignature) {
|
|
7410
|
+
if (!hasOpenAIMessages(body)) {
|
|
7411
|
+
debugLog5("No messages array found, returning body as-is");
|
|
7412
|
+
return body;
|
|
7413
|
+
}
|
|
7414
|
+
const messages = body.messages;
|
|
7415
|
+
const contents = convertOpenAIToGemini(messages, thoughtSignature);
|
|
7416
|
+
const converted = { ...body };
|
|
7417
|
+
delete converted.messages;
|
|
7418
|
+
converted.contents = contents;
|
|
7419
|
+
debugLog5(`Converted body: messages \u2192 contents (${contents.length} blocks)`);
|
|
7420
|
+
return converted;
|
|
7421
|
+
}
|
|
7422
|
+
// src/auth/antigravity/fetch.ts
|
|
7423
|
+
function debugLog6(message) {
|
|
7424
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7425
|
+
console.log(`[antigravity-fetch] ${message}`);
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
function isRetryableError(status) {
|
|
7429
|
+
if (status === 0)
|
|
7430
|
+
return true;
|
|
7431
|
+
if (status === 429)
|
|
7432
|
+
return true;
|
|
7433
|
+
if (status >= 500 && status < 600)
|
|
7434
|
+
return true;
|
|
7435
|
+
return false;
|
|
7436
|
+
}
|
|
7437
|
+
async function isRetryableResponse(response) {
|
|
7438
|
+
if (isRetryableError(response.status))
|
|
7439
|
+
return true;
|
|
7440
|
+
if (response.status === 403) {
|
|
7441
|
+
try {
|
|
7442
|
+
const text = await response.clone().text();
|
|
7443
|
+
if (text.includes("SUBSCRIPTION_REQUIRED") || text.includes("Gemini Code Assist license")) {
|
|
7444
|
+
debugLog6(`[RETRY] 403 SUBSCRIPTION_REQUIRED detected, will retry with next endpoint`);
|
|
7445
|
+
return true;
|
|
7446
|
+
}
|
|
7447
|
+
} catch {}
|
|
7448
|
+
}
|
|
7449
|
+
return false;
|
|
7450
|
+
}
|
|
7451
|
+
async function attemptFetch(options) {
|
|
7452
|
+
const { endpoint, url, init, accessToken, projectId, sessionId, modelName, thoughtSignature } = options;
|
|
7453
|
+
debugLog6(`Trying endpoint: ${endpoint}`);
|
|
7454
|
+
try {
|
|
7455
|
+
const rawBody = init.body;
|
|
7456
|
+
if (rawBody !== undefined && typeof rawBody !== "string") {
|
|
7457
|
+
debugLog6(`Non-string body detected (${typeof rawBody}), signaling pass-through`);
|
|
7458
|
+
return "pass-through";
|
|
7459
|
+
}
|
|
7460
|
+
let parsedBody = {};
|
|
7461
|
+
if (rawBody) {
|
|
7462
|
+
try {
|
|
7463
|
+
parsedBody = JSON.parse(rawBody);
|
|
7464
|
+
} catch {
|
|
7465
|
+
parsedBody = {};
|
|
7466
|
+
}
|
|
7467
|
+
}
|
|
7468
|
+
debugLog6(`[BODY] Keys: ${Object.keys(parsedBody).join(", ")}`);
|
|
7469
|
+
debugLog6(`[BODY] Has contents: ${!!parsedBody.contents}, Has messages: ${!!parsedBody.messages}`);
|
|
7470
|
+
if (parsedBody.contents) {
|
|
7471
|
+
const contents = parsedBody.contents;
|
|
7472
|
+
debugLog6(`[BODY] contents length: ${contents.length}`);
|
|
7473
|
+
contents.forEach((c, i) => {
|
|
7474
|
+
debugLog6(`[BODY] contents[${i}].role: ${c.role}, parts: ${JSON.stringify(c.parts).substring(0, 200)}`);
|
|
7475
|
+
});
|
|
7476
|
+
}
|
|
7477
|
+
if (parsedBody.tools && Array.isArray(parsedBody.tools)) {
|
|
7478
|
+
const normalizedTools = normalizeToolsForGemini(parsedBody.tools);
|
|
7479
|
+
if (normalizedTools) {
|
|
7480
|
+
parsedBody.tools = normalizedTools;
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
if (hasOpenAIMessages(parsedBody)) {
|
|
7484
|
+
debugLog6(`[CONVERT] Converting OpenAI messages to Gemini contents`);
|
|
7485
|
+
parsedBody = convertRequestBody(parsedBody, thoughtSignature);
|
|
7486
|
+
debugLog6(`[CONVERT] After conversion - Has contents: ${!!parsedBody.contents}`);
|
|
7487
|
+
}
|
|
7488
|
+
const transformed = transformRequest({
|
|
7489
|
+
url,
|
|
7490
|
+
body: parsedBody,
|
|
7491
|
+
accessToken,
|
|
7492
|
+
projectId,
|
|
7493
|
+
sessionId,
|
|
7494
|
+
modelName,
|
|
7495
|
+
endpointOverride: endpoint,
|
|
7496
|
+
thoughtSignature
|
|
7497
|
+
});
|
|
7498
|
+
debugLog6(`[REQ] streaming=${transformed.streaming}, url=${transformed.url}`);
|
|
7499
|
+
const response = await fetch(transformed.url, {
|
|
7500
|
+
method: init.method || "POST",
|
|
7501
|
+
headers: transformed.headers,
|
|
7502
|
+
body: JSON.stringify(transformed.body),
|
|
7503
|
+
signal: init.signal
|
|
7504
|
+
});
|
|
7505
|
+
debugLog6(`[RESP] status=${response.status} content-type=${response.headers.get("content-type") ?? ""} url=${response.url}`);
|
|
7506
|
+
if (!response.ok && await isRetryableResponse(response)) {
|
|
7507
|
+
debugLog6(`Endpoint failed: ${endpoint} (status: ${response.status}), trying next`);
|
|
7508
|
+
return null;
|
|
7509
|
+
}
|
|
7510
|
+
return response;
|
|
7511
|
+
} catch (error) {
|
|
7512
|
+
debugLog6(`Endpoint failed: ${endpoint} (${error instanceof Error ? error.message : "Unknown error"}), trying next`);
|
|
7513
|
+
return null;
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
function extractSignatureFromResponse(parsed) {
|
|
7517
|
+
if (!parsed.candidates || !Array.isArray(parsed.candidates)) {
|
|
7518
|
+
return;
|
|
7519
|
+
}
|
|
7520
|
+
for (const candidate of parsed.candidates) {
|
|
7521
|
+
const parts = candidate.content?.parts;
|
|
7522
|
+
if (!parts || !Array.isArray(parts)) {
|
|
7523
|
+
continue;
|
|
7524
|
+
}
|
|
7525
|
+
for (const part of parts) {
|
|
7526
|
+
const sig = part.thoughtSignature || part.thought_signature;
|
|
7527
|
+
if (sig && typeof sig === "string") {
|
|
7528
|
+
return sig;
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
}
|
|
7532
|
+
return;
|
|
7533
|
+
}
|
|
7534
|
+
async function transformResponseWithThinking(response, modelName, fetchInstanceId) {
|
|
7535
|
+
const streaming = isStreamingResponse(response);
|
|
7536
|
+
let result;
|
|
7537
|
+
if (streaming) {
|
|
7538
|
+
result = await transformStreamingResponse(response);
|
|
7539
|
+
} else {
|
|
7540
|
+
result = await transformResponse(response);
|
|
7541
|
+
}
|
|
7542
|
+
if (streaming) {
|
|
7543
|
+
return result.response;
|
|
7544
|
+
}
|
|
7545
|
+
try {
|
|
7546
|
+
const text = await result.response.clone().text();
|
|
7547
|
+
debugLog6(`[TSIG][RESP] Response text length: ${text.length}`);
|
|
7548
|
+
const parsed = JSON.parse(text);
|
|
7549
|
+
debugLog6(`[TSIG][RESP] Parsed keys: ${Object.keys(parsed).join(", ")}`);
|
|
7550
|
+
debugLog6(`[TSIG][RESP] Has candidates: ${!!parsed.candidates}, count: ${parsed.candidates?.length ?? 0}`);
|
|
7551
|
+
const signature = extractSignatureFromResponse(parsed);
|
|
7552
|
+
debugLog6(`[TSIG][RESP] Signature extracted: ${signature ? signature.substring(0, 30) + "..." : "NONE"}`);
|
|
7553
|
+
if (signature) {
|
|
7554
|
+
setThoughtSignature(fetchInstanceId, signature);
|
|
7555
|
+
debugLog6(`[TSIG][STORE] Stored signature for ${fetchInstanceId}`);
|
|
7556
|
+
} else {
|
|
7557
|
+
debugLog6(`[TSIG][WARN] No signature found in response!`);
|
|
7558
|
+
}
|
|
7559
|
+
if (shouldIncludeThinking(modelName)) {
|
|
7560
|
+
const thinkingResult = extractThinkingBlocks(parsed);
|
|
7561
|
+
if (thinkingResult.hasThinking) {
|
|
7562
|
+
const transformed = transformResponseThinking(parsed);
|
|
7563
|
+
return new Response(JSON.stringify(transformed), {
|
|
7564
|
+
status: result.response.status,
|
|
7565
|
+
statusText: result.response.statusText,
|
|
7566
|
+
headers: result.response.headers
|
|
7567
|
+
});
|
|
7568
|
+
}
|
|
7569
|
+
}
|
|
7570
|
+
} catch {}
|
|
7571
|
+
return result.response;
|
|
7572
|
+
}
|
|
7573
|
+
function createAntigravityFetch(getAuth, client, providerId, clientId, clientSecret) {
|
|
7574
|
+
let cachedTokens = null;
|
|
7575
|
+
let cachedProjectId = null;
|
|
7576
|
+
const fetchInstanceId = crypto.randomUUID();
|
|
7577
|
+
return async (url, init = {}) => {
|
|
7578
|
+
debugLog6(`Intercepting request to: ${url}`);
|
|
7579
|
+
const auth = await getAuth();
|
|
7580
|
+
if (!auth.access || !auth.refresh) {
|
|
7581
|
+
throw new Error("Antigravity: No authentication tokens available");
|
|
7582
|
+
}
|
|
7583
|
+
const refreshParts = parseStoredToken(auth.refresh);
|
|
7584
|
+
if (!cachedTokens) {
|
|
7585
|
+
cachedTokens = {
|
|
7586
|
+
type: "antigravity",
|
|
7587
|
+
access_token: auth.access,
|
|
7588
|
+
refresh_token: refreshParts.refreshToken,
|
|
7589
|
+
expires_in: auth.expires ? Math.floor((auth.expires - Date.now()) / 1000) : 3600,
|
|
7590
|
+
timestamp: auth.expires ? auth.expires - 3600 * 1000 : Date.now()
|
|
7591
|
+
};
|
|
7592
|
+
} else {
|
|
7593
|
+
cachedTokens.access_token = auth.access;
|
|
7594
|
+
cachedTokens.refresh_token = refreshParts.refreshToken;
|
|
7595
|
+
}
|
|
7596
|
+
if (isTokenExpired(cachedTokens)) {
|
|
7597
|
+
debugLog6("Token expired, refreshing...");
|
|
7598
|
+
try {
|
|
7599
|
+
const newTokens = await refreshAccessToken(refreshParts.refreshToken, clientId, clientSecret);
|
|
7600
|
+
cachedTokens = {
|
|
7601
|
+
type: "antigravity",
|
|
7602
|
+
access_token: newTokens.access_token,
|
|
7603
|
+
refresh_token: newTokens.refresh_token,
|
|
7604
|
+
expires_in: newTokens.expires_in,
|
|
7605
|
+
timestamp: Date.now()
|
|
7606
|
+
};
|
|
7607
|
+
clearProjectContextCache();
|
|
7608
|
+
const formattedRefresh = formatTokenForStorage(newTokens.refresh_token, refreshParts.projectId || "", refreshParts.managedProjectId);
|
|
7609
|
+
await client.set(providerId, {
|
|
7610
|
+
access: newTokens.access_token,
|
|
7611
|
+
refresh: formattedRefresh,
|
|
7612
|
+
expires: Date.now() + newTokens.expires_in * 1000
|
|
7613
|
+
});
|
|
7614
|
+
debugLog6("Token refreshed successfully");
|
|
7615
|
+
} catch (error) {
|
|
7616
|
+
throw new Error(`Antigravity: Token refresh failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7617
|
+
}
|
|
7618
|
+
}
|
|
7619
|
+
if (!cachedProjectId) {
|
|
7620
|
+
const projectContext = await fetchProjectContext(cachedTokens.access_token);
|
|
7621
|
+
cachedProjectId = projectContext.cloudaicompanionProject || "";
|
|
7622
|
+
debugLog6(`[PROJECT] Fetched project ID: "${cachedProjectId}"`);
|
|
7623
|
+
}
|
|
7624
|
+
const projectId = cachedProjectId;
|
|
7625
|
+
debugLog6(`[PROJECT] Using project ID: "${projectId}"`);
|
|
7626
|
+
let modelName;
|
|
7627
|
+
if (init.body) {
|
|
7628
|
+
try {
|
|
7629
|
+
const body = typeof init.body === "string" ? JSON.parse(init.body) : init.body;
|
|
7630
|
+
if (typeof body.model === "string") {
|
|
7631
|
+
modelName = body.model;
|
|
7632
|
+
}
|
|
7633
|
+
} catch {}
|
|
7634
|
+
}
|
|
7635
|
+
const maxEndpoints = Math.min(ANTIGRAVITY_ENDPOINT_FALLBACKS.length, 3);
|
|
7636
|
+
const sessionId = getOrCreateSessionId(fetchInstanceId);
|
|
7637
|
+
const thoughtSignature = getThoughtSignature(fetchInstanceId);
|
|
7638
|
+
debugLog6(`[TSIG][GET] sessionId=${sessionId}, signature=${thoughtSignature ? thoughtSignature.substring(0, 20) + "..." : "none"}`);
|
|
7639
|
+
for (let i = 0;i < maxEndpoints; i++) {
|
|
7640
|
+
const endpoint = ANTIGRAVITY_ENDPOINT_FALLBACKS[i];
|
|
7641
|
+
const response = await attemptFetch({
|
|
7642
|
+
endpoint,
|
|
7643
|
+
url,
|
|
7644
|
+
init,
|
|
7645
|
+
accessToken: cachedTokens.access_token,
|
|
7646
|
+
projectId,
|
|
7647
|
+
sessionId,
|
|
7648
|
+
modelName,
|
|
7649
|
+
thoughtSignature
|
|
7650
|
+
});
|
|
7651
|
+
if (response === "pass-through") {
|
|
7652
|
+
debugLog6("Non-string body detected, passing through with auth headers");
|
|
7653
|
+
const headersWithAuth = {
|
|
7654
|
+
...init.headers,
|
|
7655
|
+
Authorization: `Bearer ${cachedTokens.access_token}`
|
|
7656
|
+
};
|
|
7657
|
+
return fetch(url, { ...init, headers: headersWithAuth });
|
|
7658
|
+
}
|
|
7659
|
+
if (response) {
|
|
7660
|
+
debugLog6(`Success with endpoint: ${endpoint}`);
|
|
7661
|
+
const transformedResponse = await transformResponseWithThinking(response, modelName || "", fetchInstanceId);
|
|
7662
|
+
return transformedResponse;
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
const errorMessage = `All Antigravity endpoints failed after ${maxEndpoints} attempts`;
|
|
7666
|
+
debugLog6(errorMessage);
|
|
7667
|
+
return new Response(JSON.stringify({
|
|
7668
|
+
error: {
|
|
7669
|
+
message: errorMessage,
|
|
7670
|
+
type: "endpoint_failure",
|
|
7671
|
+
code: "all_endpoints_failed"
|
|
7672
|
+
}
|
|
7673
|
+
}), {
|
|
7674
|
+
status: 503,
|
|
7675
|
+
statusText: "Service Unavailable",
|
|
7676
|
+
headers: { "Content-Type": "application/json" }
|
|
7677
|
+
});
|
|
7678
|
+
};
|
|
7679
|
+
}
|
|
7680
|
+
// src/auth/antigravity/plugin.ts
|
|
7681
|
+
var GOOGLE_PROVIDER_ID = "google";
|
|
7682
|
+
function isOAuthAuth(auth) {
|
|
7683
|
+
return auth.type === "oauth";
|
|
7684
|
+
}
|
|
7685
|
+
async function createGoogleAntigravityAuthPlugin({
|
|
7686
|
+
client
|
|
7687
|
+
}) {
|
|
7688
|
+
let cachedClientId = ANTIGRAVITY_CLIENT_ID;
|
|
7689
|
+
let cachedClientSecret = ANTIGRAVITY_CLIENT_SECRET;
|
|
7690
|
+
const authHook = {
|
|
7691
|
+
provider: GOOGLE_PROVIDER_ID,
|
|
7692
|
+
loader: async (auth, provider) => {
|
|
7693
|
+
const currentAuth = await auth();
|
|
7694
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7695
|
+
console.log("[antigravity-plugin] loader called");
|
|
7696
|
+
console.log("[antigravity-plugin] auth type:", currentAuth?.type);
|
|
7697
|
+
console.log("[antigravity-plugin] auth keys:", Object.keys(currentAuth || {}));
|
|
7698
|
+
}
|
|
7699
|
+
if (!isOAuthAuth(currentAuth)) {
|
|
7700
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7701
|
+
console.log("[antigravity-plugin] NOT OAuth auth, returning empty");
|
|
7702
|
+
}
|
|
7703
|
+
return {};
|
|
7704
|
+
}
|
|
7705
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7706
|
+
console.log("[antigravity-plugin] OAuth auth detected, creating custom fetch");
|
|
7707
|
+
}
|
|
7708
|
+
cachedClientId = provider.options?.clientId || ANTIGRAVITY_CLIENT_ID;
|
|
7709
|
+
cachedClientSecret = provider.options?.clientSecret || ANTIGRAVITY_CLIENT_SECRET;
|
|
7710
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1" && (cachedClientId !== ANTIGRAVITY_CLIENT_ID || cachedClientSecret !== ANTIGRAVITY_CLIENT_SECRET)) {
|
|
7711
|
+
console.log("[antigravity-plugin] Using custom credentials from provider.options");
|
|
7712
|
+
}
|
|
7713
|
+
const authClient = {
|
|
7714
|
+
set: async (providerId, authData) => {
|
|
7715
|
+
await client.auth.set({
|
|
7716
|
+
body: {
|
|
7717
|
+
type: "oauth",
|
|
7718
|
+
access: authData.access || "",
|
|
7719
|
+
refresh: authData.refresh || "",
|
|
7720
|
+
expires: authData.expires || 0
|
|
7721
|
+
},
|
|
7722
|
+
path: { id: providerId }
|
|
7723
|
+
});
|
|
7724
|
+
}
|
|
7725
|
+
};
|
|
7726
|
+
const getAuth = async () => {
|
|
7727
|
+
const authState = await auth();
|
|
7728
|
+
if (isOAuthAuth(authState)) {
|
|
7729
|
+
return {
|
|
7730
|
+
access: authState.access,
|
|
7731
|
+
refresh: authState.refresh,
|
|
7732
|
+
expires: authState.expires
|
|
7733
|
+
};
|
|
7734
|
+
}
|
|
7735
|
+
return {};
|
|
7736
|
+
};
|
|
7737
|
+
const antigravityFetch = createAntigravityFetch(getAuth, authClient, GOOGLE_PROVIDER_ID, cachedClientId, cachedClientSecret);
|
|
7738
|
+
return {
|
|
7739
|
+
fetch: antigravityFetch,
|
|
7740
|
+
apiKey: "antigravity-oauth"
|
|
7741
|
+
};
|
|
7742
|
+
},
|
|
7743
|
+
methods: [
|
|
7744
|
+
{
|
|
7745
|
+
type: "oauth",
|
|
7746
|
+
label: "OAuth with Google (Antigravity)",
|
|
7747
|
+
authorize: async () => {
|
|
7748
|
+
const serverHandle = startCallbackServer();
|
|
7749
|
+
const { url, verifier } = await buildAuthURL(undefined, cachedClientId, serverHandle.port);
|
|
7750
|
+
return {
|
|
7751
|
+
url,
|
|
7752
|
+
instructions: "Complete the sign-in in your browser. We'll automatically detect when you're done.",
|
|
7753
|
+
method: "auto",
|
|
7754
|
+
callback: async () => {
|
|
7755
|
+
try {
|
|
7756
|
+
const result = await serverHandle.waitForCallback();
|
|
7757
|
+
if (result.error) {
|
|
7758
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7759
|
+
console.error(`[antigravity-plugin] OAuth error: ${result.error}`);
|
|
7760
|
+
}
|
|
7761
|
+
return { type: "failed" };
|
|
7762
|
+
}
|
|
7763
|
+
if (!result.code) {
|
|
7764
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7765
|
+
console.error("[antigravity-plugin] No authorization code received");
|
|
7766
|
+
}
|
|
7767
|
+
return { type: "failed" };
|
|
7768
|
+
}
|
|
7769
|
+
const state = decodeState(result.state);
|
|
7770
|
+
if (state.verifier !== verifier) {
|
|
7771
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7772
|
+
console.error("[antigravity-plugin] PKCE verifier mismatch");
|
|
7773
|
+
}
|
|
7774
|
+
return { type: "failed" };
|
|
7775
|
+
}
|
|
7776
|
+
const tokens = await exchangeCode(result.code, verifier, cachedClientId, cachedClientSecret, serverHandle.port);
|
|
7777
|
+
try {
|
|
7778
|
+
const userInfo = await fetchUserInfo(tokens.access_token);
|
|
7779
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7780
|
+
console.log(`[antigravity-plugin] Authenticated as: ${userInfo.email}`);
|
|
7781
|
+
}
|
|
7782
|
+
} catch {}
|
|
7783
|
+
const projectContext = await fetchProjectContext(tokens.access_token);
|
|
7784
|
+
const formattedRefresh = formatTokenForStorage(tokens.refresh_token, projectContext.cloudaicompanionProject || "", projectContext.managedProjectId);
|
|
7785
|
+
return {
|
|
7786
|
+
type: "success",
|
|
7787
|
+
access: tokens.access_token,
|
|
7788
|
+
refresh: formattedRefresh,
|
|
7789
|
+
expires: Date.now() + tokens.expires_in * 1000
|
|
7790
|
+
};
|
|
7791
|
+
} catch (error) {
|
|
7792
|
+
serverHandle.close();
|
|
7793
|
+
if (process.env.ANTIGRAVITY_DEBUG === "1") {
|
|
7794
|
+
console.error(`[antigravity-plugin] OAuth flow failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7795
|
+
}
|
|
7796
|
+
return { type: "failed" };
|
|
7797
|
+
}
|
|
7798
|
+
}
|
|
7799
|
+
};
|
|
7800
|
+
}
|
|
7801
|
+
}
|
|
7802
|
+
]
|
|
7803
|
+
};
|
|
7804
|
+
return {
|
|
7805
|
+
auth: authHook
|
|
7806
|
+
};
|
|
7807
|
+
}
|
|
6333
7808
|
// src/features/claude-code-command-loader/loader.ts
|
|
6334
7809
|
import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync11 } from "fs";
|
|
6335
7810
|
import { homedir as homedir9 } from "os";
|
|
@@ -6484,11 +7959,11 @@ import { join as join26, basename as basename2 } from "path";
|
|
|
6484
7959
|
function parseToolsConfig(toolsStr) {
|
|
6485
7960
|
if (!toolsStr)
|
|
6486
7961
|
return;
|
|
6487
|
-
const
|
|
6488
|
-
if (
|
|
7962
|
+
const tools2 = toolsStr.split(",").map((t) => t.trim()).filter(Boolean);
|
|
7963
|
+
if (tools2.length === 0)
|
|
6489
7964
|
return;
|
|
6490
7965
|
const result = {};
|
|
6491
|
-
for (const tool of
|
|
7966
|
+
for (const tool of tools2) {
|
|
6492
7967
|
result[tool.toLowerCase()] = true;
|
|
6493
7968
|
}
|
|
6494
7969
|
return result;
|
|
@@ -6939,9 +8414,9 @@ function getConfigPaths() {
|
|
|
6939
8414
|
function loadAllConfigs() {
|
|
6940
8415
|
const paths = getConfigPaths();
|
|
6941
8416
|
const configs = new Map;
|
|
6942
|
-
const
|
|
6943
|
-
if (
|
|
6944
|
-
configs.set("project",
|
|
8417
|
+
const project2 = loadJsonFile(paths.project);
|
|
8418
|
+
if (project2)
|
|
8419
|
+
configs.set("project", project2);
|
|
6945
8420
|
const user = loadJsonFile(paths.user);
|
|
6946
8421
|
if (user)
|
|
6947
8422
|
configs.set("user", user);
|
|
@@ -7266,13 +8741,13 @@ stderr: ${stderr}` : ""));
|
|
|
7266
8741
|
return;
|
|
7267
8742
|
const reader = this.proc.stderr.getReader();
|
|
7268
8743
|
const read = async () => {
|
|
7269
|
-
const
|
|
8744
|
+
const decoder2 = new TextDecoder;
|
|
7270
8745
|
try {
|
|
7271
8746
|
while (true) {
|
|
7272
8747
|
const { done, value } = await reader.read();
|
|
7273
8748
|
if (done)
|
|
7274
8749
|
break;
|
|
7275
|
-
const text =
|
|
8750
|
+
const text = decoder2.decode(value);
|
|
7276
8751
|
this.stderrBuffer.push(text);
|
|
7277
8752
|
if (this.stderrBuffer.length > 100) {
|
|
7278
8753
|
this.stderrBuffer.shift();
|
|
@@ -7300,7 +8775,7 @@ stderr: ${stderr}` : ""));
|
|
|
7300
8775
|
return -1;
|
|
7301
8776
|
}
|
|
7302
8777
|
processBuffer() {
|
|
7303
|
-
const
|
|
8778
|
+
const decoder2 = new TextDecoder;
|
|
7304
8779
|
const CONTENT_LENGTH = [67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58];
|
|
7305
8780
|
const CRLF_CRLF = [13, 10, 13, 10];
|
|
7306
8781
|
const LF_LF = [10, 10];
|
|
@@ -7318,7 +8793,7 @@ stderr: ${stderr}` : ""));
|
|
|
7318
8793
|
}
|
|
7319
8794
|
if (headerEnd === -1)
|
|
7320
8795
|
break;
|
|
7321
|
-
const header =
|
|
8796
|
+
const header = decoder2.decode(this.buffer.slice(0, headerEnd));
|
|
7322
8797
|
const match = header.match(/Content-Length:\s*(\d+)/i);
|
|
7323
8798
|
if (!match)
|
|
7324
8799
|
break;
|
|
@@ -7327,7 +8802,7 @@ stderr: ${stderr}` : ""));
|
|
|
7327
8802
|
const end = start + len;
|
|
7328
8803
|
if (this.buffer.length < end)
|
|
7329
8804
|
break;
|
|
7330
|
-
const content =
|
|
8805
|
+
const content = decoder2.decode(this.buffer.slice(start, end));
|
|
7331
8806
|
this.buffer = this.buffer.slice(end);
|
|
7332
8807
|
try {
|
|
7333
8808
|
const msg = JSON.parse(content);
|
|
@@ -7997,13 +9472,13 @@ __export(exports_external, {
|
|
|
7997
9472
|
enum: () => _enum2,
|
|
7998
9473
|
endsWith: () => _endsWith,
|
|
7999
9474
|
encodeAsync: () => encodeAsync2,
|
|
8000
|
-
encode: () =>
|
|
9475
|
+
encode: () => encode4,
|
|
8001
9476
|
emoji: () => emoji2,
|
|
8002
9477
|
email: () => email2,
|
|
8003
9478
|
e164: () => e1642,
|
|
8004
9479
|
discriminatedUnion: () => discriminatedUnion,
|
|
8005
9480
|
decodeAsync: () => decodeAsync2,
|
|
8006
|
-
decode: () =>
|
|
9481
|
+
decode: () => decode4,
|
|
8007
9482
|
date: () => date3,
|
|
8008
9483
|
custom: () => custom,
|
|
8009
9484
|
cuid2: () => cuid22,
|
|
@@ -8134,9 +9609,9 @@ __export(exports_core2, {
|
|
|
8134
9609
|
formatError: () => formatError,
|
|
8135
9610
|
flattenError: () => flattenError,
|
|
8136
9611
|
encodeAsync: () => encodeAsync,
|
|
8137
|
-
encode: () =>
|
|
9612
|
+
encode: () => encode3,
|
|
8138
9613
|
decodeAsync: () => decodeAsync,
|
|
8139
|
-
decode: () =>
|
|
9614
|
+
decode: () => decode3,
|
|
8140
9615
|
config: () => config,
|
|
8141
9616
|
clone: () => clone,
|
|
8142
9617
|
_xid: () => _xid,
|
|
@@ -9264,11 +10739,11 @@ var _encode = (_Err) => (schema, value, _ctx) => {
|
|
|
9264
10739
|
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" }) : { direction: "backward" };
|
|
9265
10740
|
return _parse(_Err)(schema, value, ctx);
|
|
9266
10741
|
};
|
|
9267
|
-
var
|
|
10742
|
+
var encode3 = /* @__PURE__ */ _encode($ZodRealError);
|
|
9268
10743
|
var _decode = (_Err) => (schema, value, _ctx) => {
|
|
9269
10744
|
return _parse(_Err)(schema, value, _ctx);
|
|
9270
10745
|
};
|
|
9271
|
-
var
|
|
10746
|
+
var decode3 = /* @__PURE__ */ _decode($ZodRealError);
|
|
9272
10747
|
var _encodeAsync = (_Err) => async (schema, value, _ctx) => {
|
|
9273
10748
|
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" }) : { direction: "backward" };
|
|
9274
10749
|
return _parseAsync(_Err)(schema, value, ctx);
|
|
@@ -10410,9 +11885,9 @@ var $ZodE164 = /* @__PURE__ */ $constructor("$ZodE164", (inst, def) => {
|
|
|
10410
11885
|
def.pattern ?? (def.pattern = e164);
|
|
10411
11886
|
$ZodStringFormat.init(inst, def);
|
|
10412
11887
|
});
|
|
10413
|
-
function isValidJWT(
|
|
11888
|
+
function isValidJWT(token2, algorithm = null) {
|
|
10414
11889
|
try {
|
|
10415
|
-
const tokensParts =
|
|
11890
|
+
const tokensParts = token2.split(".");
|
|
10416
11891
|
if (tokensParts.length !== 3)
|
|
10417
11892
|
return false;
|
|
10418
11893
|
const [header] = tokensParts;
|
|
@@ -17960,10 +19435,10 @@ function _property(property, schema, params) {
|
|
|
17960
19435
|
...normalizeParams(params)
|
|
17961
19436
|
});
|
|
17962
19437
|
}
|
|
17963
|
-
function _mime(
|
|
19438
|
+
function _mime(types9, params) {
|
|
17964
19439
|
return new $ZodCheckMimeType({
|
|
17965
19440
|
check: "mime_type",
|
|
17966
|
-
mime:
|
|
19441
|
+
mime: types9,
|
|
17967
19442
|
...normalizeParams(params)
|
|
17968
19443
|
});
|
|
17969
19444
|
}
|
|
@@ -19156,8 +20631,8 @@ var parse3 = /* @__PURE__ */ _parse(ZodRealError);
|
|
|
19156
20631
|
var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
|
|
19157
20632
|
var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
|
|
19158
20633
|
var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
|
|
19159
|
-
var
|
|
19160
|
-
var
|
|
20634
|
+
var encode4 = /* @__PURE__ */ _encode(ZodRealError);
|
|
20635
|
+
var decode4 = /* @__PURE__ */ _decode(ZodRealError);
|
|
19161
20636
|
var encodeAsync2 = /* @__PURE__ */ _encodeAsync(ZodRealError);
|
|
19162
20637
|
var decodeAsync2 = /* @__PURE__ */ _decodeAsync(ZodRealError);
|
|
19163
20638
|
var safeEncode2 = /* @__PURE__ */ _safeEncode(ZodRealError);
|
|
@@ -19191,8 +20666,8 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
|
|
|
19191
20666
|
inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
|
|
19192
20667
|
inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
|
|
19193
20668
|
inst.spa = inst.safeParseAsync;
|
|
19194
|
-
inst.encode = (data, params) =>
|
|
19195
|
-
inst.decode = (data, params) =>
|
|
20669
|
+
inst.encode = (data, params) => encode4(inst, data, params);
|
|
20670
|
+
inst.decode = (data, params) => decode4(inst, data, params);
|
|
19196
20671
|
inst.encodeAsync = async (data, params) => encodeAsync2(inst, data, params);
|
|
19197
20672
|
inst.decodeAsync = async (data, params) => decodeAsync2(inst, data, params);
|
|
19198
20673
|
inst.safeEncode = (data, params) => safeEncode2(inst, data, params);
|
|
@@ -19873,7 +21348,7 @@ var ZodFile = /* @__PURE__ */ $constructor("ZodFile", (inst, def) => {
|
|
|
19873
21348
|
ZodType.init(inst, def);
|
|
19874
21349
|
inst.min = (size, params) => inst.check(_minSize(size, params));
|
|
19875
21350
|
inst.max = (size, params) => inst.check(_maxSize(size, params));
|
|
19876
|
-
inst.mime = (
|
|
21351
|
+
inst.mime = (types9, params) => inst.check(_mime(Array.isArray(types9) ? types9 : [types9], params));
|
|
19877
21352
|
});
|
|
19878
21353
|
function file(params) {
|
|
19879
21354
|
return _file(ZodFile, params);
|
|
@@ -20607,12 +22082,12 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
20607
22082
|
if (!existsSync25(cacheDir)) {
|
|
20608
22083
|
mkdirSync8(cacheDir, { recursive: true });
|
|
20609
22084
|
}
|
|
20610
|
-
const
|
|
20611
|
-
if (!
|
|
20612
|
-
throw new Error(`HTTP ${
|
|
22085
|
+
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
22086
|
+
if (!response2.ok) {
|
|
22087
|
+
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
20613
22088
|
}
|
|
20614
22089
|
const archivePath = join29(cacheDir, assetName);
|
|
20615
|
-
const arrayBuffer = await
|
|
22090
|
+
const arrayBuffer = await response2.arrayBuffer();
|
|
20616
22091
|
await Bun.write(archivePath, arrayBuffer);
|
|
20617
22092
|
await extractZip2(archivePath, cacheDir);
|
|
20618
22093
|
if (existsSync25(archivePath)) {
|
|
@@ -22693,7 +24168,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
22693
24168
|
disabled_mcps: exports_external.array(McpNameSchema).optional(),
|
|
22694
24169
|
disabled_agents: exports_external.array(AgentNameSchema).optional(),
|
|
22695
24170
|
agents: AgentOverridesSchema.optional(),
|
|
22696
|
-
claude_code: ClaudeCodeConfigSchema.optional()
|
|
24171
|
+
claude_code: ClaudeCodeConfigSchema.optional(),
|
|
24172
|
+
google_auth: exports_external.boolean().optional()
|
|
22697
24173
|
});
|
|
22698
24174
|
// src/index.ts
|
|
22699
24175
|
import * as fs6 from "fs";
|
|
@@ -22783,7 +24259,9 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
22783
24259
|
const backgroundNotificationHook = createBackgroundNotificationHook(backgroundManager);
|
|
22784
24260
|
const backgroundTools = createBackgroundTools(backgroundManager, ctx.client);
|
|
22785
24261
|
const callOmoAgent = createCallOmoAgent(ctx, backgroundManager);
|
|
24262
|
+
const googleAuthHooks = pluginConfig.google_auth ? await createGoogleAntigravityAuthPlugin(ctx) : null;
|
|
22786
24263
|
return {
|
|
24264
|
+
...googleAuthHooks ? { auth: googleAuthHooks.auth } : {},
|
|
22787
24265
|
tool: {
|
|
22788
24266
|
...builtinTools,
|
|
22789
24267
|
...backgroundTools,
|