mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-29 18:05:37 +08:00
feat: add official subscription quota display for Gemini
- Read Gemini OAuth credentials from macOS Keychain (gemini-cli-oauth) or legacy file (~/.gemini/oauth_creds.json) - Auto-refresh expired access tokens using refresh_token (Google OAuth tokens expire in ~1h, unlike Claude/Codex) - Two-step API: loadCodeAssist for project ID, then retrieveUserQuota for per-model quota buckets - Classify models into Pro/Flash/Flash Lite categories, show min remaining fraction as utilization percentage - Extend isOfficialProvider() for Gemini (no API key + no base URL) - Parameterize expiredHint i18n key with tool name for all three apps
This commit is contained in:
@@ -16,6 +16,10 @@ const TIER_I18N_KEYS: Record<string, string> = {
|
||||
seven_day: "subscription.sevenDay",
|
||||
seven_day_opus: "subscription.sevenDayOpus",
|
||||
seven_day_sonnet: "subscription.sevenDaySonnet",
|
||||
// Gemini 模型分类
|
||||
gemini_pro: "subscription.geminiPro",
|
||||
gemini_flash: "subscription.geminiFlash",
|
||||
gemini_flash_lite: "subscription.geminiFlashLite",
|
||||
};
|
||||
|
||||
/** 根据使用百分比返回颜色 class */
|
||||
@@ -123,7 +127,7 @@ const SubscriptionQuotaFooter: React.FC<SubscriptionQuotaFooterProps> = ({
|
||||
<div>
|
||||
<span className="font-medium">{t("subscription.expired")}</span>
|
||||
<span className="ml-2 text-amber-500/70 dark:text-amber-400/70">
|
||||
{t("subscription.expiredHint")}
|
||||
{t("subscription.expiredHint", { tool: appId })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -68,6 +68,15 @@ function isOfficialProvider(provider: Provider, appId: AppId): boolean {
|
||||
const apiKey = config?.auth?.OPENAI_API_KEY;
|
||||
return !apiKey || (typeof apiKey === "string" && apiKey.trim() === "");
|
||||
}
|
||||
if (appId === "gemini") {
|
||||
// 无 GEMINI_API_KEY 且无 GOOGLE_GEMINI_BASE_URL → Google OAuth 官方模式
|
||||
const apiKey = config?.env?.GEMINI_API_KEY;
|
||||
const baseUrl = config?.env?.GOOGLE_GEMINI_BASE_URL;
|
||||
return (
|
||||
(!apiKey || (typeof apiKey === "string" && apiKey.trim() === "")) &&
|
||||
(!baseUrl || (typeof baseUrl === "string" && baseUrl.trim() === ""))
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2283,11 +2283,14 @@
|
||||
"sevenDay": "7-Day",
|
||||
"sevenDayOpus": "7-Day (Opus)",
|
||||
"sevenDaySonnet": "7-Day (Sonnet)",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
"utilization": "{{value}}%",
|
||||
"resetsIn": "Resets in {{time}}",
|
||||
"extraUsage": "Extra Usage",
|
||||
"expired": "Session expired",
|
||||
"expiredHint": "Run the claude command to refresh your login",
|
||||
"expiredHint": "Run the {{tool}} command to refresh your login",
|
||||
"queryFailed": "Query failed",
|
||||
"refresh": "Refresh"
|
||||
}
|
||||
|
||||
@@ -2283,11 +2283,14 @@
|
||||
"sevenDay": "7日間",
|
||||
"sevenDayOpus": "7日間(Opus)",
|
||||
"sevenDaySonnet": "7日間(Sonnet)",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
"utilization": "{{value}}%",
|
||||
"resetsIn": "{{time}}後にリセット",
|
||||
"extraUsage": "超過使用量",
|
||||
"expired": "セッション期限切れ",
|
||||
"expiredHint": "claudeコマンドを実行してログインを更新してください",
|
||||
"expiredHint": "{{tool}}コマンドを実行してログインを更新してください",
|
||||
"queryFailed": "クエリに失敗しました",
|
||||
"refresh": "更新"
|
||||
}
|
||||
|
||||
@@ -2283,11 +2283,14 @@
|
||||
"sevenDay": "7天",
|
||||
"sevenDayOpus": "7天(Opus)",
|
||||
"sevenDaySonnet": "7天(Sonnet)",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
"utilization": "{{value}}%",
|
||||
"resetsIn": "{{time}}后重置",
|
||||
"extraUsage": "超额用量",
|
||||
"expired": "会话已过期",
|
||||
"expiredHint": "请运行 claude 命令刷新登录",
|
||||
"expiredHint": "请运行 {{tool}} 命令刷新登录",
|
||||
"queryFailed": "查询失败",
|
||||
"refresh": "刷新"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user