| | /** |
| | * Extension to re-enable Google Antigravity logins and models in oh-my-pi >= 18.18.4, |
| | * and bypass the artificial "ineligible account" location/free-tier block. |
| | * |
| | * Upstream PR #18622 added a disabled rule to google-antigravity in the catalog, |
| | * and google-antigravity-project has a rigid check that throws if free-tier is |
| | * flagged as ineligible, even when the user has valid OAuth credentials, paid tier, |
| | * or an active cloudaicompanionProject. |
| | */ |
| | import * as compatAuth from "@oh-my-pi/pi-catalog/compat/auth"; |
| | import { getProviderDefinition, registerOAuthProvider } from "@oh-my-pi/pi-ai"; |
| | import { Database } from "bun:sqlite"; |
| | import { getAgentDbPath } from "@oh-my-pi/pi-utils"; |
| | // 1. Clear d status and remove restrictive built-in hooks |
| | const policy = compatAuth.authPolicyFor("google-antigravity"); |
| | if (policy) { |
| | delete (policy as any).disabled; |
| | if (policy.login) { |
| | delete (policy.login as any).afterExchange; |
| | } |
| | if (policy.refresh) { |
| | delete (policy.refresh as any).afterRefresh; |
| | } | | | } | | | // 2. Wrap provider definition to bypass the eligibility check |
| | const def = getProviderDefinition("google-antigravity"); |
| | const formatApiKey = (creds: any) => |
| | JSON.stringify({ |
| | token: creds.access, | | | projectId: creds.projectId, | | | refreshToken: creds.refresh, | | | expiresAt: creds.expires, | | | email: creds.email, | | | accountId: creds.accountId, |
| | }); |
| | if (def && def.login) { |
| | const originalLogin = def.login; | | | const originalRefreshToken = def.refreshToken; | | | const resolveProjectId = async ( | | | accessToken: string, | | | onProgress?: (msg: string) => void, | | | signal?: AbortSignal | | | ): Promise<string> => { | | | let projectId = | | | process.env.GOOGLE_CLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT_ID; | | | if (!projectId) { | | | onProgress?.("Checking Antigravity project status..."); | | | try { |
| | const res = await fetch( |
| | "https://daily-cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", |
| | { | | | method: "POST", |
| | headers: { |
| | Authorization: `Bearer ${accessToken}`, |
| | "Content-Type": "application/json", | | | "User-Agent": "antigravity", | | | }, | | | body: JSON.stringify({ metadata: { ideType: "ANTIGRAVITY" } }), | | | signal, | | | } |
| | ); |
| | if (res.ok) { |
| | const data = (await res.json()) as any; |
| | projectId = | | | typeof data.cloudaicompanionProject === "string" | | | ? data.cloudaicompanionProject | | | : data.cloudaicompanionProject?.id; | | | } | | | } catch {} | | | } | | | return projectId || "aicode-consumers"; |
| | }; |
| | const customLogin = async (ctrl: any) => { |
| | // def.login handles local callback server and OAuth token exchange with Google | | | const creds = await originalLogin(ctrl); | | | // Resolve project without the artificial free-tier location eligibility block | | | const projectId = | | | creds.projectId || | | | (await resolveProjectId(creds.access, ctrl.onProgress, ctrl.signal)); | | | return { | | | ...creds, | | | projectId, |
| | }; |
| | }; |
| | const customRefreshToken = async (creds: any, signal?: AbortSignal) => { |
| | const refreshed = await originalRefreshToken(creds, signal); |
| | return { | | | ...refreshed, | | | projectId: creds.projectId || refreshed.projectId || "aicode-consumers", |
| | }; |
| | }; |
| | registerOAuthProvider({ |
| | id: "google-antigravity", |
| | name: def.name, | | | login: customLogin, | | | refreshToken: customRefreshToken, | | | getApiKey: formatApiKey, |
| | }); |
| | registerOAuthProvider({ |
| | id: "antigravity", | | | name: def.name, | | | storeCredentialsAs: "google-antigravity", | | | login: customLogin, | | | refreshToken: customRefreshToken, | | | getApiKey: formatApiKey, | | | }); | | | } | | | // 3. Bump auth_change_revision so CredentialPool picks up credentials | | | try { |
| | const db = new Database(getAgentDbPath()); |
| | db.run("UPDATE auth_change_revision SET revision = revision + 1 WHERE id = 1"); |
| | db.close(); |
| | } catch {} |
| | export default function (api: any) { |
| | if (api.extension) { |
| | api.extension.registersProviders = true; | | | } |
| | api.on("session_start", async (_event: any, ctx: any) => { |
| | if (ctx.model && ctx.model.provider !== "google-antigravity") { |
| | const target = ctx.modelRegistry.find("google-antigravity", "gemini-3.8-flash"); |
| | if (target) { |
| | await api.runtime.setModel(target); |
| | } | | | } | | | }); | | | } |