feat(antigravity): initial steps for Antigravity anti-ban alignment

Cherry-picked from decolua/9router#141 (author: LinearSakana / zx <me@char.moe>)

- Implement client identity spoofing with numeric enums (ideType: 9, pluginType: 2)
- Add runtime platform detection for User-Agent and metadata
- Implement per-connection session ID caching (binary-compatible format)
- Add ANTIGRAVITY_HEADERS (X-Client-Name, X-Client-Version, x-goog-api-client)
- Add X-Machine-Session-Id header injection
- Align metadata/mode parameters across all Antigravity API calls
- Implement double injection for system prompt (raw + [ignore] wrapped)
- Rename internal anti-loop header to x-request-source for anonymity

Skipped: commit 6 (signature side-channel caching) — kept DEFAULT_THINKING_GEMINI_SIGNATURE
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zx
2026-02-20 14:44:29 +07:00
committed by decolua
parent 9fbd6e619d
commit a229d79158
11 changed files with 309 additions and 121 deletions

View File

@@ -14,6 +14,7 @@ import {
GITHUB_CONFIG,
KIRO_CONFIG,
CURSOR_CONFIG,
getOAuthClientMetadata,
} from "./constants/oauth";
// Provider configurations
@@ -184,7 +185,8 @@ const PROVIDERS = {
"Content-Type": "application/json",
},
body: JSON.stringify({
metadata: { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" },
metadata: getOAuthClientMetadata(),
mode: 1,
}),
}
);
@@ -254,7 +256,7 @@ const PROVIDERS = {
"X-Goog-Api-Client": ANTIGRAVITY_CONFIG.loadCodeAssistApiClient,
"Client-Metadata": ANTIGRAVITY_CONFIG.loadCodeAssistClientMetadata,
};
const metadata = { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" };
const metadata = getOAuthClientMetadata();
// Fetch user info
const userInfoRes = await fetch(`${ANTIGRAVITY_CONFIG.userInfoUrl}?alt=json`, {
@@ -269,7 +271,7 @@ const PROVIDERS = {
const loadRes = await fetch(ANTIGRAVITY_CONFIG.loadCodeAssistEndpoint, {
method: "POST",
headers,
body: JSON.stringify({ metadata }),
body: JSON.stringify({ metadata, mode: 1 }),
});
if (loadRes.ok) {
const data = await loadRes.json();
@@ -295,7 +297,7 @@ const PROVIDERS = {
const onboardRes = await fetch(ANTIGRAVITY_CONFIG.onboardUserEndpoint, {
method: "POST",
headers,
body: JSON.stringify({ tierId, metadata, cloudaicompanionProject: projectId }),
body: JSON.stringify({ tierId, metadata, cloudaicompanionProject: projectId, mode: 1 }),
});
if (onboardRes.ok) {
const result = await onboardRes.json();
@@ -727,9 +729,9 @@ export function generateAuthData(providerName, redirectUri) {
*/
export async function exchangeTokens(providerName, code, redirectUri, codeVerifier, state) {
const provider = getProvider(providerName);
const tokens = await provider.exchangeToken(provider.config, code, redirectUri, codeVerifier, state);
let extra = null;
if (provider.postExchange) {
extra = await provider.postExchange(tokens);
@@ -761,9 +763,9 @@ export async function pollForToken(providerName, deviceCode, codeVerifier, extra
if (provider.flowType !== "device_code") {
throw new Error(`Provider ${providerName} does not support device code flow`);
}
const result = await provider.pollToken(provider.config, deviceCode, codeVerifier, extraData);
if (result.ok) {
// For device code flows, success is only when we have an access token
if (result.data.access_token) {
@@ -777,23 +779,23 @@ export async function pollForToken(providerName, deviceCode, codeVerifier, extra
// Check if it's still pending authorization
if (result.data.error === 'authorization_pending' || result.data.error === 'slow_down') {
// This is not a failure, just still waiting
return {
success: false,
error: result.data.error,
return {
success: false,
error: result.data.error,
errorDescription: result.data.error_description || result.data.message,
pending: result.data.error === 'authorization_pending'
};
} else {
// Actual error
return {
success: false,
error: result.data.error || 'no_access_token',
errorDescription: result.data.error_description || result.data.message || 'No access token received'
return {
success: false,
error: result.data.error || 'no_access_token',
errorDescription: result.data.error_description || result.data.message || 'No access token received'
};
}
}
}
return { success: false, error: result.data.error, errorDescription: result.data.error_description };
}