"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.antigravityOAuthProvider = void 0;exports.loginAntigravity = loginAntigravity;exports.refreshAntigravityToken = refreshAntigravityToken; var _pkce = require("./pkce.js");function _interopRequireWildcard(e, t) {if ("function" == typeof WeakMap) var r = new WeakMap(),n = new WeakMap();return (_interopRequireWildcard = function (e, t) {if (!t && e && e.__esModule) return e;var o,i,f = { __proto__: null, default: e };if (null === e || "object" != typeof e && "function" != typeof e) return f;if (o = t ? n : r) {if (o.has(e)) return o.get(e);o.set(e, f);}for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]);return f;})(e, t);} /** * Antigravity OAuth flow (Gemini 3, Claude, GPT-OSS via Google Cloud) * Uses different OAuth credentials than google-gemini-cli for access to additional models. * * NOTE: This module uses Node.js http.createServer for the OAuth callback. * It is only intended for CLI use, not browser environments. */let _createServer = null;let _httpImportPromise = null;if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {_httpImportPromise = Promise.resolve().then(() => jitiImport("node:http").then((m) => _interopRequireWildcard(m))).then((m) => {_createServer = m.createServer;}); } // Antigravity OAuth credentials (different from Gemini CLI) const decode = (s) => atob(s); const CLIENT_ID = decode("MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ=="); const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY="); const REDIRECT_URI = "http://localhost:51121/oauth-callback"; // Antigravity requires additional scopes const SCOPES = [ "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/cclog", "https://www.googleapis.com/auth/experimentsandconfigs"]; const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; const TOKEN_URL = "https://oauth2.googleapis.com/token"; // Fallback project ID when discovery fails const DEFAULT_PROJECT_ID = "rising-fact-p41fc"; /** * Start a local HTTP server to receive the OAuth callback */ async function getNodeCreateServer() { if (_createServer) return _createServer; if (_httpImportPromise) { await _httpImportPromise; } if (_createServer) return _createServer; throw new Error("Antigravity OAuth is only available in Node.js environments"); } async function startCallbackServer() { const createServer = await getNodeCreateServer(); return new Promise((resolve, reject) => { let result = null; let cancelled = false; const server = createServer((req, res) => { const url = new URL(req.url || "", `http://localhost:51121`); if (url.pathname === "/oauth-callback") { const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const error = url.searchParams.get("error"); if (error) { res.writeHead(400, { "Content-Type": "text/html" }); res.end(`
Error: ${error}
You can close this window.
`); return; } if (code && state) { res.writeHead(200, { "Content-Type": "text/html" }); res.end(`You can close this window and return to the terminal.
`); result = { code, state }; } else { res.writeHead(400, { "Content-Type": "text/html" }); res.end(`Missing code or state parameter.
`); } } else { res.writeHead(404); res.end(); } }); server.on("error", (err) => { reject(err); }); server.listen(51121, "127.0.0.1", () => { resolve({ server, cancelWait: () => { cancelled = true; }, waitForCode: async () => { const sleep = () => new Promise((r) => setTimeout(r, 100)); while (!result && !cancelled) { await sleep(); } return result; } }); }); }); } /** * Parse redirect URL to extract code and state */ function parseRedirectUrl(input) { const value = input.trim(); if (!value) return {}; try { const url = new URL(value); return { code: url.searchParams.get("code") ?? undefined, state: url.searchParams.get("state") ?? undefined }; } catch { // Not a URL, return empty return {}; } } /** * Discover or provision a project for the user */ async function discoverProject(accessToken, onProgress) { const headers = { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", "User-Agent": "google-api-nodejs-client/9.15.1", "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1", "Client-Metadata": JSON.stringify({ ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" }) }; // Try endpoints in order: prod first, then sandbox const endpoints = ["https://cloudcode-pa.googleapis.com", "https://daily-cloudcode-pa.sandbox.googleapis.com"]; onProgress?.("Checking for existing project..."); for (const endpoint of endpoints) { try { const loadResponse = await fetch(`${endpoint}/v1internal:loadCodeAssist`, { method: "POST", headers, body: JSON.stringify({ metadata: { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" } }) }); if (loadResponse.ok) { const data = await loadResponse.json(); // Handle both string and object formats if (typeof data.cloudaicompanionProject === "string" && data.cloudaicompanionProject) { return data.cloudaicompanionProject; } if (data.cloudaicompanionProject && typeof data.cloudaicompanionProject === "object" && data.cloudaicompanionProject.id) { return data.cloudaicompanionProject.id; } } } catch { // Try next endpoint }} // Use fallback project ID onProgress?.("Using default project..."); return DEFAULT_PROJECT_ID; } /** * Get user email from the access token */ async function getUserEmail(accessToken) { try { const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", { headers: { Authorization: `Bearer ${accessToken}` } }); if (response.ok) { const data = await response.json(); return data.email; } } catch { // Ignore errors, email is optional }return undefined; } /** * Refresh Antigravity token */ async function refreshAntigravityToken(refreshToken, projectId) { const response = await fetch(TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, refresh_token: refreshToken, grant_type: "refresh_token" }) }); if (!response.ok) { const error = await response.text(); throw new Error(`Antigravity token refresh failed: ${error}`); } const data = await response.json(); return { refresh: data.refresh_token || refreshToken, access: data.access_token, expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000, projectId }; } /** * Login with Antigravity OAuth * * @param onAuth - Callback with URL and optional instructions * @param onProgress - Optional progress callback * @param onManualCodeInput - Optional promise that resolves with user-pasted redirect URL. * Races with browser callback - whichever completes first wins. */ async function loginAntigravity(onAuth, onProgress, onManualCodeInput) { const { verifier, challenge } = await (0, _pkce.generatePKCE)(); // Start local server for callback onProgress?.("Starting local server for OAuth callback..."); const server = await startCallbackServer(); let code; try { // Build authorization URL const authParams = new URLSearchParams({ client_id: CLIENT_ID, response_type: "code", redirect_uri: REDIRECT_URI, scope: SCOPES.join(" "), code_challenge: challenge, code_challenge_method: "S256", state: verifier, access_type: "offline", prompt: "consent" }); const authUrl = `${AUTH_URL}?${authParams.toString()}`; // Notify caller with URL to open onAuth({ url: authUrl, instructions: "Complete the sign-in in your browser." }); // Wait for the callback, racing with manual input if provided onProgress?.("Waiting for OAuth callback..."); if (onManualCodeInput) { // Race between browser callback and manual input let manualInput; let manualError; const manualPromise = onManualCodeInput(). then((input) => { manualInput = input; server.cancelWait(); }). catch((err) => { manualError = err instanceof Error ? err : new Error(String(err)); server.cancelWait(); }); const result = await server.waitForCode(); // If manual input was cancelled, throw that error if (manualError) { throw manualError; } if (result?.code) { // Browser callback won - verify state if (result.state !== verifier) { throw new Error("OAuth state mismatch - possible CSRF attack"); } code = result.code; } else if (manualInput) { // Manual input won const parsed = parseRedirectUrl(manualInput); if (parsed.state && parsed.state !== verifier) { throw new Error("OAuth state mismatch - possible CSRF attack"); } code = parsed.code; } // If still no code, wait for manual promise and try that if (!code) { await manualPromise; if (manualError) { throw manualError; } if (manualInput) { const parsed = parseRedirectUrl(manualInput); if (parsed.state && parsed.state !== verifier) { throw new Error("OAuth state mismatch - possible CSRF attack"); } code = parsed.code; } } } else { // Original flow: just wait for callback const result = await server.waitForCode(); if (result?.code) { if (result.state !== verifier) { throw new Error("OAuth state mismatch - possible CSRF attack"); } code = result.code; } } if (!code) { throw new Error("No authorization code received"); } // Exchange code for tokens onProgress?.("Exchanging authorization code for tokens..."); const tokenResponse = await fetch(TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code, grant_type: "authorization_code", redirect_uri: REDIRECT_URI, code_verifier: verifier }) }); if (!tokenResponse.ok) { const error = await tokenResponse.text(); throw new Error(`Token exchange failed: ${error}`); } const tokenData = await tokenResponse.json(); if (!tokenData.refresh_token) { throw new Error("No refresh token received. Please try again."); } // Get user email onProgress?.("Getting user info..."); const email = await getUserEmail(tokenData.access_token); // Discover project const projectId = await discoverProject(tokenData.access_token, onProgress); // Calculate expiry time (current time + expires_in seconds - 5 min buffer) const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000; const credentials = { refresh: tokenData.refresh_token, access: tokenData.access_token, expires: expiresAt, projectId, email }; return credentials; } finally { server.server.close(); } } const antigravityOAuthProvider = exports.antigravityOAuthProvider = { id: "google-antigravity", name: "Antigravity (Gemini 3, Claude, GPT-OSS)", usesCallbackServer: true, async login(callbacks) { return loginAntigravity(callbacks.onAuth, callbacks.onProgress, callbacks.onManualCodeInput); }, async refreshToken(credentials) { const creds = credentials; if (!creds.projectId) { throw new Error("Antigravity credentials missing projectId"); } return refreshAntigravityToken(creds.refresh, creds.projectId); }, getApiKey(credentials) { const creds = credentials; return JSON.stringify({ token: creds.access, projectId: creds.projectId }); } }; /* v9-8458eafc5007e424 */