/** * headless-usage.ts — `sf headless usage` * * Purpose: expose live LLM-provider usage data (account tier, project, per-model * quota usage with reset windows) via the headless CLI so operators and CI can * see capacity state without launching the interactive UI. * * Today this covers the gemini-cli provider (the most quota-sensitive surface * because of AI Ultra's per-model windowed quotas). Other providers can be * added by extending the snapshot helper as their introspection APIs are * wired into dedicated provider packages. * * Consumer: headless.ts when command === "usage". */ import { type GeminiAccountSnapshot, snapshotGeminiCliAccount, } from "@singularity-forge/google-gemini-cli-provider"; export interface HandleUsageOptions { json?: boolean; } export interface HandleUsageResult { exitCode: number; } /** * Render a snapshot as a compact text table (default) or as JSON for machine * consumers. Always writes to stdout; never throws. */ export async function handleUsage( cwd: string, options: HandleUsageOptions = {}, ): Promise { let snapshot: GeminiAccountSnapshot | null; try { snapshot = await snapshotGeminiCliAccount(cwd); } catch (err) { const msg = err instanceof Error ? err.message : String(err); const payload = { provider: "google-gemini-cli", ok: false, error: msg, }; process.stdout.write( options.json ? `${JSON.stringify(payload)}\n` : `error: ${msg}\n`, ); return { exitCode: 1 }; } if (!snapshot) { const payload = { provider: "google-gemini-cli", ok: false, error: "No gemini-cli account snapshot — run `gemini auth login` and verify ~/.gemini/oauth_creds.json exists.", }; process.stdout.write( options.json ? `${JSON.stringify(payload)}\n` : `${payload.error}\n`, ); return { exitCode: 1 }; } if (options.json) { process.stdout.write( `${JSON.stringify({ provider: "google-gemini-cli", ok: true, snapshot })}\n`, ); return { exitCode: 0 }; } const lines: string[] = []; lines.push("Gemini CLI usage"); lines.push(""); lines.push(` project: ${snapshot.projectId}`); if (snapshot.userTierId || snapshot.userTierName) { lines.push( ` userTier: ${snapshot.userTierId ?? "?"}${snapshot.userTierName ? ` (${snapshot.userTierName})` : ""}`, ); } if (snapshot.paidTier?.id || snapshot.paidTier?.name) { lines.push( ` paidTier: ${snapshot.paidTier.id ?? "?"}${snapshot.paidTier.name ? ` — ${snapshot.paidTier.name}` : ""}`, ); } lines.push(""); lines.push(" Per-model quota:"); const modelW = Math.max( 20, ...snapshot.models.map((m) => m.modelId.length), ); for (const m of snapshot.models) { const usedPct = (m.usedFraction * 100).toFixed(1).padStart(5); const reset = m.resetTime ?? "-"; lines.push(` ${m.modelId.padEnd(modelW)} used=${usedPct}% reset=${reset}`); } process.stdout.write(`${lines.join("\n")}\n`); return { exitCode: 0 }; }