pi update

This commit is contained in:
Jonas H
2026-04-10 09:01:25 +02:00
parent 7106db51b5
commit 4666776bda
16 changed files with 3042 additions and 35 deletions

View File

@@ -36,8 +36,8 @@ export function writeUsageCache(cache: UsageCache): void {
} catch {}
}
export type ProviderKey = "codex" | "claude" | "zai" | "gemini" | "antigravity";
export type OAuthProviderId = "openai-codex" | "anthropic" | "google-gemini-cli" | "google-antigravity";
export type ProviderKey = "codex" | "claude" | "zai" | "gemini" | "antigravity" | "opencode-go";
export type OAuthProviderId = "openai-codex" | "anthropic" | "google-gemini-cli" | "google-antigravity" | "opencode-go";
export interface AuthData {
"openai-codex"?: { access?: string; refresh?: string; expires?: number };
@@ -45,6 +45,7 @@ export interface AuthData {
zai?: { key?: string; access?: string; refresh?: string; expires?: number };
"google-gemini-cli"?: { access?: string; refresh?: string; projectId?: string; expires?: number };
"google-antigravity"?: { access?: string; refresh?: string; projectId?: string; expires?: number };
"opencode-go"?: { key?: string; access?: string };
}
export interface UsageData {
@@ -61,6 +62,127 @@ export interface UsageData {
export type UsageByProvider = Record<ProviderKey, UsageData | null>;
// OpenCode Go usage tracking (local, since no API exists yet)
export interface OpenCodeGoLocalUsage {
/** Dollar value used in the 5-hour window */
fiveHourUsed: number;
/** Dollar value used in the weekly window */
weeklyUsed: number;
/** Dollar value used in the monthly window */
monthlyUsed: number;
/** Timestamp of the last update */
lastUpdated: number;
/** When the current 5-hour window started */
fiveHourWindowStart: number;
/** When the current week started (Unix ms) */
weekStart: number;
/** When the current month started (Unix ms) */
monthStart: number;
}
const OPENCODE_GO_USAGE_FILE = path.join(os.homedir(), ".pi", "agent", "opencode-go-usage.json");
const OPENCODE_GO_FIVE_HOUR_LIMIT = 12;
const OPENCODE_GO_WEEKLY_LIMIT = 30;
const OPENCODE_GO_MONTHLY_LIMIT = 60;
const OPENCODE_GO_FIVE_HOUR_MS = 5 * 60 * 60 * 1000;
export function readOpenCodeGoUsage(): OpenCodeGoLocalUsage | null {
try {
const raw = fs.readFileSync(OPENCODE_GO_USAGE_FILE, "utf-8");
const parsed = JSON.parse(raw);
if (typeof parsed?.lastUpdated === "number") return parsed as OpenCodeGoLocalUsage;
} catch {}
return null;
}
export function writeOpenCodeGoUsage(usage: OpenCodeGoLocalUsage): void {
try {
const dir = path.dirname(OPENCODE_GO_USAGE_FILE);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
const tmp = `${OPENCODE_GO_USAGE_FILE}.tmp-${process.pid}-${Date.now()}`;
fs.writeFileSync(tmp, JSON.stringify(usage, null, 2));
fs.renameSync(tmp, OPENCODE_GO_USAGE_FILE);
} catch {}
}
export function resetOpenCodeGoUsageIfNeeded(existing: OpenCodeGoLocalUsage | null): OpenCodeGoLocalUsage {
const now = Date.now();
const nowDate = new Date(now);
// Start with defaults
let usage: OpenCodeGoLocalUsage = existing ?? {
fiveHourUsed: 0,
weeklyUsed: 0,
monthlyUsed: 0,
lastUpdated: now,
fiveHourWindowStart: now,
weekStart: now,
monthStart: now,
};
// Reset 5-hour window if expired
if (now - usage.fiveHourWindowStart >= OPENCODE_GO_FIVE_HOUR_MS) {
usage.fiveHourUsed = 0;
usage.fiveHourWindowStart = now;
}
// Reset weekly window (Monday-based)
const dayOfWeek = nowDate.getDay();
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
const thisMonday = new Date(nowDate);
thisMonday.setDate(nowDate.getDate() - daysSinceMonday);
thisMonday.setHours(0, 0, 0, 0);
if (usage.weekStart < thisMonday.getTime()) {
usage.weeklyUsed = 0;
usage.weekStart = thisMonday.getTime();
}
// Reset monthly window (1st of month)
const thisMonthStart = new Date(nowDate.getFullYear(), nowDate.getMonth(), 1);
if (usage.monthStart < thisMonthStart.getTime()) {
usage.monthlyUsed = 0;
usage.monthStart = thisMonthStart.getTime();
}
usage.lastUpdated = now;
return usage;
}
export function addOpenCodeGoSpend(dollars: number): void {
let usage = resetOpenCodeGoUsageIfNeeded(readOpenCodeGoUsage());
usage.fiveHourUsed += dollars;
usage.weeklyUsed += dollars;
usage.monthlyUsed += dollars;
usage.lastUpdated = Date.now();
writeOpenCodeGoUsage(usage);
}
export function getOpenCodeGoUsageData(): UsageData {
const usage = resetOpenCodeGoUsageIfNeeded(readOpenCodeGoUsage());
if (!usage) {
return { session: 0, weekly: 0, error: "no local usage data" };
}
const sessionPct = Math.min(100, (usage.fiveHourUsed / OPENCODE_GO_FIVE_HOUR_LIMIT) * 100);
const weeklyPct = Math.min(100, (usage.weeklyUsed / OPENCODE_GO_WEEKLY_LIMIT) * 100);
// Calculate resets
const fiveHourEnd = usage.fiveHourWindowStart + OPENCODE_GO_FIVE_HOUR_MS;
const fiveHourRemaining = Math.max(0, fiveHourEnd - Date.now());
const weekEnd = usage.weekStart + 7 * 24 * 60 * 60 * 1000;
const weekRemaining = Math.max(0, weekEnd - Date.now());
return {
session: sessionPct,
weekly: weeklyPct,
sessionResetsIn: formatDuration(Math.round(fiveHourRemaining / 1000)),
weeklyResetsIn: formatDuration(Math.round(weekRemaining / 1000)),
extraSpend: usage.monthlyUsed,
extraLimit: OPENCODE_GO_MONTHLY_LIMIT,
};
}
export interface UsageEndpoints {
zai: string;
gemini: string;
@@ -600,12 +722,14 @@ export function detectProvider(
if (typeof model === "string") return null;
const provider = (model.provider || "").toLowerCase();
const id = (model.id || "").toLowerCase();
if (provider === "openai-codex") return "codex";
if (provider === "anthropic") return "claude";
if (provider === "zai") return "zai";
if (provider === "google-gemini-cli") return "gemini";
if (provider === "google-antigravity") return "antigravity";
if (provider === "opencode-go" || id.startsWith("opencode-go/")) return "opencode-go";
return null;
}
@@ -615,6 +739,7 @@ export function providerToOAuthProviderId(active: ProviderKey | null): OAuthProv
if (active === "claude") return "anthropic";
if (active === "gemini") return "google-gemini-cli";
if (active === "antigravity") return "google-antigravity";
if (active === "opencode-go") return "opencode-go";
return null;
}
@@ -629,6 +754,9 @@ export function canShowForProvider(active: ProviderKey | null, auth: AuthData |
if (active === "antigravity") {
return !!(auth["google-antigravity"]?.access || auth["google-antigravity"]?.refresh) && !!endpoints.antigravity;
}
if (active === "opencode-go") {
return !!(auth["opencode-go"]?.key || auth["opencode-go"]?.access);
}
return false;
}
@@ -654,6 +782,7 @@ export async function fetchAllUsages(config: FetchAllUsagesConfig = {}): Promise
zai: null,
gemini: null,
antigravity: null,
"opencode-go": null,
};
if (!auth) return results;
@@ -733,6 +862,11 @@ export async function fetchAllUsages(config: FetchAllUsagesConfig = {}): Promise
}
}
// OpenCode Go uses local tracking (no public API yet)
if (authData["opencode-go"]?.key || authData["opencode-go"]?.access) {
results["opencode-go"] = getOpenCodeGoUsageData();
}
await Promise.all(tasks);
return results;
}