fix(oauth): scope antigravity header fixes to loadCodeAssist/onboardUser only

Google fingerprints User-Agent/Client-Metadata on loadCodeAssist and
onboardUser, silently refusing to provision a cloudaicompanionProject
when they don't match the real IDE. Split antigravity's headers out of
the shared gemini-cli constants instead of overwriting them, so the fix
doesn't touch gemini-cli or any other provider.

Inspired by #3000 (thanks @stoXmod for flagging the resource-exhausted
issue), rewritten to keep gemini-cli untouched.
This commit is contained in:
decolua 2026-08-05 16:40:09 +07:00
parent 41588bea01
commit 35f86e5828
5 changed files with 15 additions and 12 deletions

View file

@ -156,6 +156,13 @@ export const LOAD_CODE_ASSIST_HEADERS = {
"Client-Metadata": JSON.stringify({ ideType: IDE_TYPE.ANTIGRAVITY, platform: getPlatformEnum(), pluginType: PLUGIN_TYPE.GEMINI }), "Client-Metadata": JSON.stringify({ ideType: IDE_TYPE.ANTIGRAVITY, platform: getPlatformEnum(), pluginType: PLUGIN_TYPE.GEMINI }),
}; };
// Real Antigravity IDE doesn't send X-Goog-Api-Client/Client-Metadata on loadCodeAssist/onboardUser —
// Google's backend fingerprints those and silently refuses to provision a cloudaicompanionProject.
export const ANTIGRAVITY_LOAD_CODE_ASSIST_HEADERS = {
"Content-Type": "application/json",
"User-Agent": ANTIGRAVITY_IDE_USER_AGENT,
};
export const LOAD_CODE_ASSIST_METADATA = { export const LOAD_CODE_ASSIST_METADATA = {
ideType: IDE_TYPE.ANTIGRAVITY, ideType: IDE_TYPE.ANTIGRAVITY,
platform: getPlatformEnum(), platform: getPlatformEnum(),

View file

@ -76,8 +76,7 @@ export default {
apiVersion: "v1internal", apiVersion: "v1internal",
loadCodeAssistEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", loadCodeAssistEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
onboardUserEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser", onboardUserEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",
loadCodeAssistUserAgent: "google-api-nodejs-client/9.15.1", loadCodeAssistUserAgent: ANTIGRAVITY_IDE_USER_AGENT,
loadCodeAssistApiClient: "google-cloud-sdk vscode_cloudshelleditor/0.1",
refreshLeadMs: 300000, refreshLeadMs: 300000,
}, },
features: { features: {

View file

@ -7,7 +7,7 @@
* This significantly reduces the risk of being flagged by Google's anti-abuse systems. * This significantly reduces the risk of being flagged by Google's anti-abuse systems.
*/ */
import { CLOUD_CODE_API, LOAD_CODE_ASSIST_HEADERS, LOAD_CODE_ASSIST_METADATA } from "../config/appConstants.js"; import { CLOUD_CODE_API, LOAD_CODE_ASSIST_HEADERS, ANTIGRAVITY_LOAD_CODE_ASSIST_HEADERS, LOAD_CODE_ASSIST_METADATA } from "../config/appConstants.js";
// ─── Cache ──────────────────────────────────────────────────────────────────── // ─── Cache ────────────────────────────────────────────────────────────────────
// connectionId -> { projectId: string, fetchedAt: number } // connectionId -> { projectId: string, fetchedAt: number }
@ -157,9 +157,10 @@ export function removeConnection(connectionId) {
*/ */
async function fetchProjectId(accessToken, signal, provider) { async function fetchProjectId(accessToken, signal, provider) {
const endpoints = CLOUD_CODE_API[provider] || CLOUD_CODE_API["gemini-cli"]; const endpoints = CLOUD_CODE_API[provider] || CLOUD_CODE_API["gemini-cli"];
const headers = provider === "antigravity" ? ANTIGRAVITY_LOAD_CODE_ASSIST_HEADERS : LOAD_CODE_ASSIST_HEADERS;
const response = await fetch(endpoints.loadCodeAssist, { const response = await fetch(endpoints.loadCodeAssist, {
method: "POST", method: "POST",
headers: { ...LOAD_CODE_ASSIST_HEADERS, "Authorization": `Bearer ${accessToken}` }, headers: { ...headers, "Authorization": `Bearer ${accessToken}` },
body: JSON.stringify({ metadata: LOAD_CODE_ASSIST_METADATA }), body: JSON.stringify({ metadata: LOAD_CODE_ASSIST_METADATA }),
signal signal
}); });
@ -186,7 +187,7 @@ async function fetchProjectId(accessToken, signal, provider) {
} }
} }
return onboardUser(accessToken, tierID, signal, endpoints); return onboardUser(accessToken, tierID, signal, endpoints, provider);
} }
/** /**
@ -197,10 +198,11 @@ async function fetchProjectId(accessToken, signal, provider) {
* @param {AbortSignal} externalSignal – propagated from the connection's AbortController * @param {AbortSignal} externalSignal – propagated from the connection's AbortController
* @returns {Promise<string|null>} * @returns {Promise<string|null>}
*/ */
async function onboardUser(accessToken, tierID, externalSignal, endpoints) { async function onboardUser(accessToken, tierID, externalSignal, endpoints, provider) {
console.log(`[ProjectId] Onboarding user with tier: ${tierID}`); console.log(`[ProjectId] Onboarding user with tier: ${tierID}`);
const reqBody = { tierId: tierID, metadata: LOAD_CODE_ASSIST_METADATA }; const reqBody = { tierId: tierID, metadata: LOAD_CODE_ASSIST_METADATA };
const headers = provider === "antigravity" ? ANTIGRAVITY_LOAD_CODE_ASSIST_HEADERS : LOAD_CODE_ASSIST_HEADERS;
const MAX_ATTEMPTS = 5; const MAX_ATTEMPTS = 5;
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
@ -216,7 +218,7 @@ async function onboardUser(accessToken, tierID, externalSignal, endpoints) {
try { try {
const response = await fetch(endpoints.onboardUser, { const response = await fetch(endpoints.onboardUser, {
method: "POST", method: "POST",
headers: { ...LOAD_CODE_ASSIST_HEADERS, "Authorization": `Bearer ${accessToken}` }, headers: { ...headers, "Authorization": `Bearer ${accessToken}` },
body: JSON.stringify(reqBody), body: JSON.stringify(reqBody),
signal: localCtrl.signal signal: localCtrl.signal
}); });

View file

@ -39,13 +39,10 @@ const antigravity = {
return await response.json(); return await response.json();
}, },
postExchange: async (tokens) => { postExchange: async (tokens) => {
// Numeric enums matching Antigravity binary ClientMetadata
const loadHeaders = { const loadHeaders = {
"Authorization": `Bearer ${tokens.access_token}`, "Authorization": `Bearer ${tokens.access_token}`,
"Content-Type": "application/json", "Content-Type": "application/json",
"User-Agent": ANTIGRAVITY_CONFIG.loadCodeAssistUserAgent, "User-Agent": ANTIGRAVITY_CONFIG.loadCodeAssistUserAgent,
"X-Goog-Api-Client": ANTIGRAVITY_CONFIG.loadCodeAssistApiClient,
"Client-Metadata": ANTIGRAVITY_CONFIG.loadCodeAssistClientMetadata,
"x-request-source": "local", "x-request-source": "local",
}; };
const metadata = getOAuthClientMetadata(); const metadata = getOAuthClientMetadata();

View file

@ -85,8 +85,6 @@ export class AntigravityService {
"Authorization": `Bearer ${accessToken}`, "Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json", "Content-Type": "application/json",
"User-Agent": this.config.loadCodeAssistUserAgent, "User-Agent": this.config.loadCodeAssistUserAgent,
"X-Goog-Api-Client": this.config.loadCodeAssistApiClient,
"Client-Metadata": this.config.loadCodeAssistClientMetadata,
}; };
} }