import { createServer } from "node:http"; import { ANTIGRAVITY_REDIRECT_URI } from "../constants"; interface OAuthListenerOptions { /** * How long to wait for the OAuth redirect before timing out (in milliseconds). */ timeoutMs?: number; } export interface OAuthListener { /** * Resolves with the callback URL once Google redirects back to the local server. */ waitForCallback(): Promise; /** * Cleanly stop listening for callbacks. */ close(): Promise; } const redirectUri = new URL(ANTIGRAVITY_REDIRECT_URI); const callbackPath = redirectUri.pathname || "/"; /** * Starts a lightweight HTTP server that listens for the Antigravity OAuth redirect * and resolves with the captured callback URL. */ export async function startOAuthListener( { timeoutMs = 5 * 60 * 1000 }: OAuthListenerOptions = {}, ): Promise { const port = redirectUri.port ? Number.parseInt(redirectUri.port, 10) : redirectUri.protocol === "https:" ? 443 : 80; const origin = `${redirectUri.protocol}//${redirectUri.host}`; let settled = false; let resolveCallback: (url: URL) => void; let rejectCallback: (error: Error) => void; const callbackPromise = new Promise((resolve, reject) => { resolveCallback = (url: URL) => { if (settled) return; settled = true; if (timeoutHandle) clearTimeout(timeoutHandle); resolve(url); }; rejectCallback = (error: Error) => { if (settled) return; settled = true; if (timeoutHandle) clearTimeout(timeoutHandle); reject(error); }; }); const successResponse = ` Opencode Antigravity OAuth
Antigravity linked to Opencode

You're connected to Opencode

Your Google account is now linked to Opencode. You can close this window and continue in the CLI.

Close window

Need to reconnect later? Re-run the authentication command in Opencode.

`; const timeoutHandle = setTimeout(() => { rejectCallback(new Error("Timed out waiting for OAuth callback")); }, timeoutMs); timeoutHandle.unref?.(); const server = createServer((request, response) => { if (!request.url) { response.writeHead(400, { "Content-Type": "text/plain" }); response.end("Invalid request"); return; } const url = new URL(request.url, origin); if (url.pathname !== callbackPath) { response.writeHead(404, { "Content-Type": "text/plain" }); response.end("Not found"); return; } response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); response.end(successResponse); resolveCallback(url); setImmediate(() => { server.close(); }); }); await new Promise((resolve, reject) => { const handleError = (error: Error) => { server.off("error", handleError); reject(error); }; server.once("error", handleError); server.listen(port, "127.0.0.1", () => { server.off("error", handleError); resolve(); }); }); server.on("error", (error) => { rejectCallback(error instanceof Error ? error : new Error(String(error))); }); return { waitForCallback: () => callbackPromise, close: () => new Promise((resolve, reject) => { server.close((error) => { if (error && (error as NodeJS.ErrnoException).code !== "ERR_SERVER_NOT_RUNNING") { reject(error); return; } if (!settled) { rejectCallback(new Error("OAuth listener closed before callback")); } resolve(); }); }), }; }