import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import { getApiKey, loginAntigravity, refreshAntigravityToken } from "./auth/index.js"; import { DEFAULT_ENDPOINT, endpointCandidates } from "./client/index.js"; import { getLastDiagnostics, runWithDiagnostics } from "./diagnostics/index.js"; import { ANTIGRAVITY_MODELS, PROVIDER_ID, PROVIDER_NAME } from "./models/index.js"; import { ANTIGRAVITY_API, streamAntigravity } from "./stream/index.js"; import { fetchAccountUsage, formatModelsList, formatUsageSummary, resolveApiKeyFromContext, } from "./usage/index.js"; import { prewarmConnection, redactSecrets } from "./utils/index.js"; /** * Pi's interactive `notify` writes into the chat transcript. `console.log` in that * mode prints to the raw terminal and paints over the TUI. Use one channel only. */ function emitCommandOutput( ctx: ExtensionCommandContext, text: string, type: "info" | "warning" | "error" = "info", ): void { if (ctx.hasUI) { ctx.ui.notify(text, type); return; } if (type === "warning" || type === "error") console.error(text); else console.log(text); } async function withUsage( ctx: ExtensionCommandContext, fn: (usage: Awaited>) => string, ): Promise { try { const apiKey = await resolveApiKeyFromContext(ctx); if (!apiKey) { emitCommandOutput( ctx, "No Antigravity credentials. Run /login antigravity first.", "warning", ); return; } if (ctx.hasUI) ctx.ui.notify("Fetching Antigravity usage…", "info"); const usage = await runWithDiagnostics(() => fetchAccountUsage(apiKey)); emitCommandOutput(ctx, fn(usage)); } catch (error) { const msg = error instanceof Error ? error.message : String(error); emitCommandOutput(ctx, `Antigravity usage failed: ${msg}`, "warning"); } } export default function (pi: ExtensionAPI): void { // Open the TLS connection up front so the first message of a session does not pay // the handshake. Opt out with ANTIGRAVITY_NO_PREWARM=1. const primaryEndpoint = endpointCandidates()[0]; if (primaryEndpoint) prewarmConnection(primaryEndpoint); pi.registerProvider(PROVIDER_ID, { name: PROVIDER_NAME, baseUrl: DEFAULT_ENDPOINT, api: ANTIGRAVITY_API, models: ANTIGRAVITY_MODELS, oauth: { name: PROVIDER_NAME, login: loginAntigravity, refreshToken: refreshAntigravityToken, getApiKey, }, streamSimple: streamAntigravity, }); pi.registerCommand("antigravity.usage", { description: "Show Antigravity shared quota pools (Gemini / Claude+GPT, 5h + weekly)", handler: async (_args, ctx) => { await withUsage(ctx, formatUsageSummary); }, }); pi.registerCommand("antigravity.models", { description: "List Antigravity runtime models + remaining pool fraction", handler: async (args, ctx) => { const all = /\ball\b/i.test(args || ""); await withUsage(ctx, (usage) => formatModelsList(usage, { all })); }, }); pi.registerCommand("antigravity.doctor", { description: "Show sanitized Antigravity provider diagnostics", handler: async (_args, ctx) => { const d = getLastDiagnostics(); const lines = [ `provider=${PROVIDER_ID}`, `lastResolvedRuntimeModel=${d.resolvedRuntimeModel || "none"}`, `availableModels=${d.availableModels || "none"}`, `matchedModel=${d.matchedModelDebug || "none"}`, `lastEndpoint=${d.endpoint || "none"}`, `lastStatus=${d.status ?? "none"}`, `lastProjectId=${d.projectId || "none"}`, ...(d.latencyMs !== undefined ? [`lastLatencyMs=${d.latencyMs}`] : []), `lastError=${d.error ? redactSecrets(d.error) : "none"}`, "transport=native-streamSimple", "runtimeCli=not-used", "commands=/antigravity.usage /antigravity.models /antigravity.doctor", ]; emitCommandOutput(ctx, `Antigravity doctor\n${lines.join("\n")}`); }, }); }