Add Gemini CLI v1internal quota support

This commit is contained in:
Mas0nShi
2026-05-22 16:58:04 +08:00
parent 9533bd7043
commit ce02f1ae8c
17 changed files with 985 additions and 52 deletions
+55 -19
View File
@@ -658,12 +658,18 @@
>{{ getQuotaProgressMeterDisplayText(item) }}</span>
</div>
</div>
<div
v-if="keyUiStateMap[key.key_id]?.accountQuotaText"
class="text-[10px] leading-none text-muted-foreground tabular-nums"
>
{{ keyUiStateMap[key.key_id]?.accountQuotaText }}
</div>
</div>
<span
v-else-if="keyUiStateMap[key.key_id]?.quotaFallbackText"
v-else-if="keyUiStateMap[key.key_id]?.accountQuotaText || keyUiStateMap[key.key_id]?.quotaFallbackText"
:class="keyUiStateMap[key.key_id]?.quotaTextClass || ''"
>
{{ keyUiStateMap[key.key_id]?.quotaFallbackText }}
{{ keyUiStateMap[key.key_id]?.accountQuotaText || keyUiStateMap[key.key_id]?.quotaFallbackText }}
</span>
<span
v-else
@@ -1177,12 +1183,18 @@
>{{ getQuotaProgressMeterDisplayText(item) }}</span>
</div>
</div>
<div
v-if="keyUiStateMap[key.key_id]?.accountQuotaText"
class="text-[10px] leading-none text-muted-foreground tabular-nums"
>
{{ keyUiStateMap[key.key_id]?.accountQuotaText }}
</div>
</div>
<div
v-else-if="keyUiStateMap[key.key_id]?.quotaFallbackText"
v-else-if="keyUiStateMap[key.key_id]?.accountQuotaText || keyUiStateMap[key.key_id]?.quotaFallbackText"
:class="keyUiStateMap[key.key_id]?.quotaTextClass || ''"
>
{{ keyUiStateMap[key.key_id]?.quotaFallbackText }}
{{ keyUiStateMap[key.key_id]?.accountQuotaText || keyUiStateMap[key.key_id]?.quotaFallbackText }}
</div>
<div
v-else
@@ -1622,6 +1634,7 @@ import {
getOAuthStatusTitle as resolveOAuthStatusTitle,
} from '@/utils/providerKeyStatus'
import {
getGeminiCliAccountCreditsText,
getLegacyAccountQuotaText,
getQuotaDisplayText,
} from '@/utils/providerKeyQuota'
@@ -2329,6 +2342,7 @@ interface QuotaProgressItem {
resetAtSeconds?: number | null
resetSeconds?: number | null
updatedAtSeconds?: number | null
allowDynamicReset?: boolean
}
interface PoolCodexCycleStatsRow {
@@ -2358,6 +2372,7 @@ type PoolKeyUiState = {
canRefreshToken: boolean
planLabel: string
planClass: string
accountQuotaText: string | null
quotaFallbackText: string | null
quotaTextClass: string
importedAtRelative: string
@@ -2381,6 +2396,7 @@ const keyUiStateMap = computed<Record<string, PoolKeyUiState>>(() => {
for (const key of keyPage.value.keys) {
const visibleOAuthState = getVisibleOAuthState(key)
const oauthOrgBadge = getOAuthOrgBadge(key)
const accountQuotaText = getAccountQuotaText(key)
const quotaFallbackText = getQuotaFallbackText(key)
const planType = resolvePoolKeyPlanType(key)
const canRefreshToken = canRefreshOAuthCredential(key)
@@ -2399,8 +2415,11 @@ const keyUiStateMap = computed<Record<string, PoolKeyUiState>>(() => {
canRefreshToken,
planLabel: planType ? formatOAuthPlanType(planType) : '',
planClass: planType ? getOAuthPlanTypeClass(planType) : '',
accountQuotaText,
quotaFallbackText,
quotaTextClass: quotaFallbackText ? getQuotaTextClass(quotaFallbackText) : '',
quotaTextClass: accountQuotaText || quotaFallbackText
? getQuotaTextClass(accountQuotaText || quotaFallbackText || '')
: '',
importedAtRelative: formatPoolKeyImportedAt(key),
lastUsedRelative: key.last_used_at ? formatRelativeTime(key.last_used_at) : '-',
statsDisplay: buildPoolStatsDisplay(key, selectedProviderType.value, poolStatsMode.value),
@@ -2475,6 +2494,7 @@ function getPoolKeyAccountStatsMetrics(key: PoolKeyDetail): PoolStatsMetric[] {
const quotaRefreshSupported = computed(() => {
return selectedProviderType.value === 'codex'
|| selectedProviderType.value === 'kiro'
|| selectedProviderType.value === 'gemini_cli'
|| selectedProviderType.value === 'antigravity'
|| selectedProviderType.value === 'grok'
|| selectedProviderType.value === 'chatgpt_web'
@@ -3693,7 +3713,8 @@ function getQuotaProgressLabel(label: string): string {
}
function getQuotaProgressCountdown(item: QuotaProgressItem) {
if (!['5H', '周', 'Spark5H', 'Spark周', 'Auto', 'Fast', 'Expert', 'Heavy', 'Grok 4.3'].includes(item.label)) return null
const staticResetLabels = ['日', '5H', '周', 'Spark5H', 'Spark周', 'Auto', 'Fast', 'Expert', 'Heavy', 'Grok 4.3']
if (!item.allowDynamicReset && !staticResetLabels.includes(item.label)) return null
if (item.resetAtSeconds == null && item.resetSeconds == null) return null
return getCodexResetCountdown(
item.resetAtSeconds,
@@ -3739,6 +3760,10 @@ function getQuotaFallbackText(key: PoolKeyDetail): string | null {
return getQuotaDisplayText(key, selectedProviderType.value)
}
function getAccountQuotaText(key: PoolKeyDetail): string | null {
return getGeminiCliAccountCreditsText(key, selectedProviderType.value)
}
function getQuotaLabelOrder(label: string): number {
@@ -3908,6 +3933,12 @@ function getGrokQuotaWindowLabel(window: QuotaWindowSnapshot): string {
return GROK_QUOTA_MODE_LABELS[normalized] || GROK_QUOTA_MODE_LABELS[code.toLowerCase()] || label || code || '模式'
}
function getGeminiCliQuotaWindowLabel(window: QuotaWindowSnapshot): string {
const code = String(window.code || '').trim().replace(/^model:/i, '')
const label = String(window.label || window.model || code).trim()
return label || code || '模型'
}
function buildQuotaProgressItemsFromSnapshot(key: PoolKeyDetail): QuotaProgressItem[] {
const quota = getQuotaSnapshot(key)
if (!quota) return []
@@ -4021,19 +4052,24 @@ function buildQuotaProgressItemsFromSnapshot(key: PoolKeyDetail): QuotaProgressI
const windows = getQuotaSnapshotWindowsByScope(quota, 'model')
if (windows.length === 0) return []
const remainingPercents = windows
.map(getQuotaWindowRemainingPercent)
.filter((value): value is number => value != null)
if (remainingPercents.length === 0) return []
return [{
label: '最低',
remainingPercent: Math.min(...remainingPercents),
detail: `${windows.length} 模型`,
resetAtSeconds: null,
resetSeconds: null,
updatedAtSeconds: getQuotaSnapshotUpdatedAtSeconds(quota),
}]
const quotaResetAtSeconds = getQuotaSnapshotResetAtSeconds(quota)
const quotaResetSeconds = getQuotaSnapshotResetSeconds(quota)
return windows
.map((window): QuotaProgressItem | null => {
const remainingPercent = getQuotaWindowRemainingPercent(window)
?? (window?.is_exhausted === true ? 0 : null)
if (remainingPercent == null) return null
return {
label: getGeminiCliQuotaWindowLabel(window),
remainingPercent,
detail: getQuotaWindowValueText(window),
resetAtSeconds: normalizeUnixSeconds(window?.reset_at ?? quotaResetAtSeconds ?? null),
resetSeconds: normalizeRemainingSeconds(window?.reset_seconds ?? quotaResetSeconds ?? null),
updatedAtSeconds: getQuotaSnapshotUpdatedAtSeconds(quota),
allowDynamicReset: true,
}
})
.filter((item): item is QuotaProgressItem => item != null)
}
if (providerType === 'chatgpt_web') {