rip out antigravity from SF + pi-coding-agent UI/config layer
Antigravity (Google's IDE sandbox product, different from Gemini CLI) is removed from: src/onboarding.ts — drop from LLM_PROVIDER_IDS + OAuth-flow picker src/pi-migration.ts — drop from LLM_PROVIDER_IDS migration list src/web/onboarding-service.ts — drop from web-UI provider list src/tests/integration/web-onboarding-contract.test.ts — update contract src/resources/extensions/sf/doctor-providers.ts — drop from CLI_AUTH_PROVIDERS src/resources/extensions/sf/key-manager.ts — drop UI listing src/resources/extensions/sf-usage-bar/index.ts — delete entire quota fetcher block (~200 lines) packages/pi-coding-agent/src/cli/args.ts — drop PI_AI_ANTIGRAVITY_VERSION doc packages/pi-coding-agent/src/utils/proxy-server.ts — drop from claude provider chain Reason: antigravity has no vendor-published core library we can embed (unlike @google/gemini-cli-core for the Gemini CLI). Continuing to hand-roll OAuth against daily-cloudcode-pa.sandbox.googleapis.com is exactly the pattern Google has started banning for third-party tools. Removing the code removes the ban risk. pi-ai provider code, OAuth util, and models.generated entries for google-antigravity are removed in follow-up commits (separated for reviewability — each layer verified independently). Build passes. Note: this is a breaking change for any user who had google-antigravity configured — they'll need to migrate to google-gemini-cli (OAuth), google (API key), or google-vertex. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
233432d486
commit
59806f8cc5
9 changed files with 4 additions and 209 deletions
|
|
@ -338,7 +338,6 @@ ${chalk.bold("Environment Variables:")}
|
|||
PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
|
||||
PI_OFFLINE - Disable startup network operations when set to 1/true/yes
|
||||
PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/)
|
||||
PI_AI_ANTIGRAVITY_VERSION - Override Antigravity User-Agent version (e.g., 1.23.0)
|
||||
|
||||
${chalk.bold("Available Tools (default: read, bash, edit, write):")}
|
||||
read - Read file contents
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ const PROXY_FAMILY_PRIORITY: Array<{ match: RegExp; providers: string[] }> = [
|
|||
{ match: /^kimi-/i, providers: ["kimi-coding", "opencode", "opencode-go"] },
|
||||
// Gemini/Gemma: google direct > vertex (enterprise) > CLI (OAuth) > copilot
|
||||
{ match: /^gemini-|^gemma-/i, providers: ["google", "google-vertex", "google-gemini-cli", "github-copilot"] },
|
||||
// Claude: anthropic direct > opencode > google-antigravity > copilot
|
||||
{ match: /^claude-/i, providers: ["anthropic", "opencode", "google-antigravity", "github-copilot"] },
|
||||
// Claude: anthropic direct > opencode > copilot
|
||||
{ match: /^claude-/i, providers: ["anthropic", "opencode", "github-copilot"] },
|
||||
// GPT/OpenAI: openai direct > azure > copilot
|
||||
{ match: /^gpt-|^o[0-9]|^codex-/i, providers: ["openai", "azure-openai-responses", "github-copilot"] },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ const LLM_PROVIDER_IDS = [
|
|||
'github-copilot',
|
||||
'openai-codex',
|
||||
'google-gemini-cli',
|
||||
'google-antigravity',
|
||||
'google',
|
||||
'groq',
|
||||
'xai',
|
||||
|
|
@ -360,7 +359,6 @@ async function runLlmStep(p: ClackModule, pc: PicoModule, authStorage: AuthStora
|
|||
{ value: 'github-copilot', label: 'GitHub Copilot' },
|
||||
{ value: 'openai-codex', label: 'ChatGPT Plus/Pro (Codex)' },
|
||||
{ value: 'google-gemini-cli', label: 'Google Gemini CLI' },
|
||||
{ value: 'google-antigravity', label: 'Antigravity (Gemini 3, Claude, GPT-OSS)' },
|
||||
],
|
||||
})
|
||||
if (p.isCancel(provider)) return false
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ const LLM_PROVIDER_IDS = [
|
|||
'github-copilot',
|
||||
'openai-codex',
|
||||
'google-gemini-cli',
|
||||
'google-antigravity',
|
||||
'google',
|
||||
'groq',
|
||||
'xai',
|
||||
|
|
|
|||
|
|
@ -402,202 +402,6 @@ async function fetchGeminiUsage(_modelRegistry: any): Promise<UsageSnapshot> {
|
|||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Antigravity Usage
|
||||
// ============================================================================
|
||||
|
||||
type AntigravityAuth = {
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
expiresAt?: number;
|
||||
projectId?: string;
|
||||
};
|
||||
|
||||
function loadAntigravityAuthFromAuthJson(): AntigravityAuth | undefined {
|
||||
const data = loadAuthJson();
|
||||
if (!data) return undefined;
|
||||
|
||||
// Provider is called "google-antigravity" in sf/pi.
|
||||
const cred = data["google-antigravity"] ?? data["antigravity"] ?? data["anti-gravity"];
|
||||
if (!cred) return undefined;
|
||||
|
||||
const accessToken = typeof cred.access === "string" ? cred.access : undefined;
|
||||
if (!accessToken) return undefined;
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken: typeof cred.refresh === "string" ? cred.refresh : undefined,
|
||||
expiresAt: typeof cred.expires === "number" ? cred.expires : undefined,
|
||||
projectId: typeof cred.projectId === "string" ? cred.projectId : typeof cred.project_id === "string" ? cred.project_id : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadAntigravityAuth(modelRegistry: any): Promise<AntigravityAuth | undefined> {
|
||||
// Prefer model registry auth storage first (may auto-refresh).
|
||||
try {
|
||||
const accessToken = await Promise.resolve(modelRegistry?.authStorage?.getApiKey?.("google-antigravity"));
|
||||
const raw = await Promise.resolve(modelRegistry?.authStorage?.get?.("google-antigravity"));
|
||||
|
||||
const projectId = typeof raw?.projectId === "string" ? raw.projectId : undefined;
|
||||
const refreshToken = typeof raw?.refresh === "string" ? raw.refresh : undefined;
|
||||
const expiresAt = typeof raw?.expires === "number" ? raw.expires : undefined;
|
||||
|
||||
if (typeof accessToken === "string" && accessToken.length > 0) {
|
||||
return { accessToken, projectId, refreshToken, expiresAt };
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Fallback to auth.json
|
||||
const fromAuth = loadAntigravityAuthFromAuthJson();
|
||||
if (fromAuth) return fromAuth;
|
||||
|
||||
// Last resort: env var (won't have projectId; request will likely fail)
|
||||
if (process.env.ANTIGRAVITY_API_KEY) {
|
||||
return { accessToken: process.env.ANTIGRAVITY_API_KEY };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function refreshAntigravityAccessToken(refreshToken: string): Promise<{ accessToken: string; expiresAt?: number } | null> {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
// From the reference snippet in CodexBar issue #129.
|
||||
const clientId = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
||||
const clientSecret = "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";
|
||||
|
||||
const res = await fetch("https://oauth2.googleapis.com/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
refresh_token: refreshToken,
|
||||
grant_type: "refresh_token",
|
||||
}).toString(),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!res.ok) return null;
|
||||
const data = (await res.json()) as any;
|
||||
const accessToken = typeof data.access_token === "string" ? data.access_token : undefined;
|
||||
if (!accessToken) return null;
|
||||
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : undefined;
|
||||
return {
|
||||
accessToken,
|
||||
expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : undefined,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAntigravityUsage(modelRegistry: any): Promise<UsageSnapshot> {
|
||||
const auth = await loadAntigravityAuth(modelRegistry);
|
||||
if (!auth?.accessToken) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: "No credentials" };
|
||||
}
|
||||
|
||||
if (!auth.projectId) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: "Missing projectId" };
|
||||
}
|
||||
|
||||
let accessToken = auth.accessToken;
|
||||
|
||||
// Refresh if likely expired.
|
||||
if (auth.refreshToken && auth.expiresAt && auth.expiresAt < Date.now() + 5 * 60 * 1000) {
|
||||
const refreshed = await refreshAntigravityAccessToken(auth.refreshToken);
|
||||
if (refreshed?.accessToken) accessToken = refreshed.accessToken;
|
||||
}
|
||||
|
||||
const fetchModels = async (token: string): Promise<Response> => {
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
return fetch("https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "antigravity/1.12.4",
|
||||
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify({ project: auth.projectId }),
|
||||
signal: controller.signal,
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
let res = await fetchModels(accessToken);
|
||||
|
||||
if ((res.status === 401 || res.status === 403) && auth.refreshToken) {
|
||||
const refreshed = await refreshAntigravityAccessToken(auth.refreshToken);
|
||||
if (refreshed?.accessToken) {
|
||||
accessToken = refreshed.accessToken;
|
||||
res = await fetchModels(accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.status === 401 || res.status === 403) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: "Unauthorized" };
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: `HTTP ${res.status}` };
|
||||
}
|
||||
|
||||
const data = (await res.json()) as any;
|
||||
const models: Record<string, any> = data.models || {};
|
||||
|
||||
const getQuotaInfo = (modelKeys: string[]): { usedPercent: number; resetDescription?: string } | null => {
|
||||
for (const key of modelKeys) {
|
||||
const qi = models?.[key]?.quotaInfo;
|
||||
if (!qi) continue;
|
||||
// In practice (CodexBar issue #129), some models only provide resetTime.
|
||||
// Treat missing remainingFraction as 0% remaining (100% used), which matches Antigravity's behavior when quota is exhausted.
|
||||
const remainingFraction = typeof qi.remainingFraction === "number" ? qi.remainingFraction : 0;
|
||||
const usedPercent = Math.min(100, Math.max(0, (1 - remainingFraction) * 100));
|
||||
const resetTime = qi.resetTime ? new Date(qi.resetTime) : undefined;
|
||||
return { usedPercent, resetDescription: resetTime ? formatReset(resetTime) : undefined };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Quota groups from the reference snippet in CodexBar issue #129.
|
||||
const windows: RateWindow[] = [];
|
||||
|
||||
const claudeOrGptOss = getQuotaInfo([
|
||||
"claude-sonnet-4-5",
|
||||
"claude-sonnet-4-5-thinking",
|
||||
"claude-opus-4-5-thinking",
|
||||
"gpt-oss-120b-medium",
|
||||
]);
|
||||
if (claudeOrGptOss) {
|
||||
windows.push({ label: "Claude", usedPercent: claudeOrGptOss.usedPercent, resetDescription: claudeOrGptOss.resetDescription });
|
||||
}
|
||||
|
||||
const gemini3Pro = getQuotaInfo(["gemini-3-pro-high", "gemini-3-pro-low", "gemini-3-pro-preview"]);
|
||||
if (gemini3Pro) {
|
||||
windows.push({ label: "G3 Pro", usedPercent: gemini3Pro.usedPercent, resetDescription: gemini3Pro.resetDescription });
|
||||
}
|
||||
|
||||
const gemini3Flash = getQuotaInfo(["gemini-3-flash"]);
|
||||
if (gemini3Flash) {
|
||||
windows.push({ label: "G3 Flash", usedPercent: gemini3Flash.usedPercent, resetDescription: gemini3Flash.resetDescription });
|
||||
}
|
||||
|
||||
if (windows.length === 0) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: "No quota data" };
|
||||
}
|
||||
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows };
|
||||
} catch (e) {
|
||||
return { provider: "antigravity", displayName: "Antigravity", windows: [], error: String(e) };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Codex (OpenAI) Usage
|
||||
|
|
@ -954,12 +758,11 @@ class UsageComponent {
|
|||
Promise.race([p, new Promise<T>((r) => setTimeout(() => r(fallback), ms))]);
|
||||
|
||||
// Fetch usage and status in parallel
|
||||
const [claude, copilot, gemini, codex, antigravity, kiro, zai, claudeStatus, copilotStatus, geminiStatus, codexStatus] = await Promise.all([
|
||||
const [claude, copilot, gemini, codex, kiro, zai, claudeStatus, copilotStatus, geminiStatus, codexStatus] = await Promise.all([
|
||||
timeout(fetchClaudeUsage(), 6000, { provider: "anthropic", displayName: "Claude", windows: [], error: "Timeout" }),
|
||||
timeout(fetchCopilotUsage(this.modelRegistry), 6000, { provider: "copilot", displayName: "Copilot", windows: [], error: "Timeout" }),
|
||||
timeout(fetchGeminiUsage(this.modelRegistry), 6000, { provider: "gemini", displayName: "Gemini", windows: [], error: "Timeout" }),
|
||||
timeout(fetchCodexUsage(this.modelRegistry), 6000, { provider: "codex", displayName: "Codex", windows: [], error: "Timeout" }),
|
||||
timeout(fetchAntigravityUsage(this.modelRegistry), 6000, { provider: "antigravity", displayName: "Antigravity", windows: [], error: "Timeout" }),
|
||||
timeout(fetchKiroUsage(), 6000, { provider: "kiro", displayName: "Kiro", windows: [], error: "Timeout" }),
|
||||
timeout(fetchZaiUsage(), 6000, { provider: "zai", displayName: "z.ai", windows: [], error: "Timeout" }),
|
||||
timeout(fetchProviderStatus("anthropic"), 3000, { indicator: "unknown" as const }),
|
||||
|
|
@ -975,7 +778,7 @@ class UsageComponent {
|
|||
codex.status = codexStatus;
|
||||
|
||||
// Filter out providers with no data and no error (not configured)
|
||||
const allUsages = [claude, copilot, gemini, codex, antigravity, kiro, zai];
|
||||
const allUsages = [claude, copilot, gemini, codex, kiro, zai];
|
||||
this.usages = allUsages.filter(u => u.windows.length > 0 || u.error !== "No credentials" && u.error !== "kiro-cli not found" && u.error !== "No API key");
|
||||
this.loading = false;
|
||||
this.tui.requestRender();
|
||||
|
|
|
|||
|
|
@ -193,7 +193,6 @@ const CLI_AUTH_PROVIDERS = new Set([
|
|||
"claude-code",
|
||||
"openai-codex",
|
||||
"google-gemini-cli",
|
||||
"google-antigravity",
|
||||
]);
|
||||
|
||||
function checkLlmProviders(): ProviderCheckResult[] {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ export const PROVIDER_REGISTRY: ProviderInfo[] = [
|
|||
{ id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
|
||||
{ id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)",category: "llm", hasOAuth: true },
|
||||
{ id: "google-gemini-cli",label: "Google Gemini CLI", category: "llm", hasOAuth: true },
|
||||
{ id: "google-antigravity",label: "Antigravity", category: "llm", hasOAuth: true },
|
||||
{ id: "google", label: "Google (Gemini)", category: "llm", envVar: "GEMINI_API_KEY", dashboardUrl: "aistudio.google.com/apikey" },
|
||||
{ id: "groq", label: "Groq", category: "llm", envVar: "GROQ_API_KEY", dashboardUrl: "console.groq.com" },
|
||||
{ id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
|
||||
|
|
|
|||
|
|
@ -339,7 +339,6 @@ test("boot and onboarding routes expose locked required state plus explicitly sk
|
|||
"github-copilot",
|
||||
"openai-codex",
|
||||
"google-gemini-cli",
|
||||
"google-antigravity",
|
||||
"google",
|
||||
"groq",
|
||||
"xai",
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ const REQUIRED_PROVIDER_CATALOG: RequiredProviderCatalogEntry[] = [
|
|||
{ id: "github-copilot", label: "GitHub Copilot", supportsApiKey: false, supportsOAuth: true },
|
||||
{ id: "openai-codex", label: "ChatGPT Plus/Pro (Codex Subscription)", supportsApiKey: false, supportsOAuth: true },
|
||||
{ id: "google-gemini-cli", label: "Google Cloud Code Assist (Gemini CLI)", supportsApiKey: false, supportsOAuth: true },
|
||||
{ id: "google-antigravity", label: "Antigravity (Gemini 3, Claude, GPT-OSS)", supportsApiKey: false, supportsOAuth: true },
|
||||
{ id: "google", label: "Google (Gemini API)", supportsApiKey: true, supportsOAuth: false },
|
||||
{ id: "groq", label: "Groq", supportsApiKey: true, supportsOAuth: false },
|
||||
{ id: "xai", label: "xAI (Grok)", supportsApiKey: true, supportsOAuth: false },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue