feat(usage): add official subscription quota template with unified tier rendering

Changes:
- Add official_subscription template type for Claude/Codex/Gemini
- Replace implicit 'category=official auto-query' with explicit opt-in template
- Default disabled; users enable via usage script modal with configurable interval
- Unify tier→label mapping across subscription and script paths via labeled_tier_parts()
- Fix tray rendering: week aliases (seven_day/opus/sonnet) now use highest utilization
- Add depth guard: official_subscription checks enabled flag in query_provider_usage_inner
- Add cache invalidation symmetry: invalidate_subscription() for disabled providers
- i18n: add templateOfficialSubscription + hint in zh/en/ja/zh-TW

Backend (Rust):
- provider.rs: add TEMPLATE_TYPE_OFFICIAL_SUBSCRIPTION branch, flatten SubscriptionQuota→UsageData
- tray.rs: extract labeled_tier_parts() shared by both summary functions, use max_by for multi-alias groups
- usage_cache.rs: add invalidate_subscription() method
- Test coverage: add week-alias highest-utilization tests for both paths

Frontend (TypeScript):
- UsageScriptModal: add official_subscription to templates, auto-detect for official providers
- ProviderCard: gate useUsageQuery with !isOfficialSubscriptionUsage, pass autoQueryInterval to footer
- SubscriptionQuotaFooter: accept autoQueryInterval prop, default 0 (disabled)
- constants.ts: add TEMPLATE_TYPES.OFFICIAL_SUBSCRIPTION

Fixes tier rendering regression where:
- Claude/Codex: seven_day was missed (only weekly_limit matched) → lost 7-day window in tray
- Gemini: gemini_pro/flash/flash_lite fell through to fallback → leaked machine names
- Multi-window (opus+sonnet): find() took first, not worst → underestimated utilization and emoji color

All tests pass (cargo test + cargo clippy clean).
This commit is contained in:
Jason
2026-06-05 19:02:23 +08:00
parent 03a9296c1f
commit 473f21971d
13 changed files with 459 additions and 173 deletions
+22 -8
View File
@@ -14,7 +14,7 @@ import UsageFooter from "@/components/UsageFooter";
import SubscriptionQuotaFooter from "@/components/SubscriptionQuotaFooter";
import CopilotQuotaFooter from "@/components/CopilotQuotaFooter";
import CodexOauthQuotaFooter from "@/components/CodexOauthQuotaFooter";
import { PROVIDER_TYPES } from "@/config/constants";
import { PROVIDER_TYPES, TEMPLATE_TYPES } from "@/config/constants";
import { isHermesReadOnlyProvider } from "@/config/hermesProviderPresets";
import { ProviderHealthBadge } from "@/components/providers/ProviderHealthBadge";
import { FailoverPriorityBadge } from "@/components/providers/FailoverPriorityBadge";
@@ -192,6 +192,13 @@ export function ProviderCard({
const usageEnabled = provider.meta?.usage_script?.enabled ?? false;
const isOfficial = isOfficialProvider(provider, appId);
const supportsOfficialSubscription =
isOfficial && ["claude", "codex", "gemini"].includes(appId);
const isOfficialSubscriptionUsage =
provider.meta?.usage_script?.templateType ===
TEMPLATE_TYPES.OFFICIAL_SUBSCRIPTION;
const officialSubscriptionEnabled =
supportsOfficialSubscription && usageEnabled && isOfficialSubscriptionUsage;
const isOfficialBlockedByProxy =
isProxyTakeover && (provider.category === "official" || isOfficial);
const isCopilot =
@@ -231,7 +238,7 @@ export function ProviderCard({
: 0;
const { data: usage } = useUsageQuery(provider.id, appId, {
enabled: usageEnabled,
enabled: usageEnabled && !isOfficial && !isOfficialSubscriptionUsage,
autoQueryInterval,
});
@@ -469,11 +476,16 @@ export function ProviderCard({
isCurrent={isCurrent}
/>
) : isOfficial ? (
<SubscriptionQuotaFooter
appId={appId}
inline={true}
isCurrent={isCurrent}
/>
officialSubscriptionEnabled ? (
<SubscriptionQuotaFooter
appId={appId}
inline={true}
isCurrent={isCurrent}
autoQueryInterval={
provider.meta?.usage_script?.autoQueryInterval ?? 0
}
/>
) : null
) : hasMultiplePlans ? (
<div className="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400">
<span className="font-medium">
@@ -540,7 +552,9 @@ export function ProviderCard({
: undefined
}
onConfigureUsage={
isOfficial || isCopilot || isCodexOauth
(isOfficial && !supportsOfficialSubscription) ||
isCopilot ||
isCodexOauth
? undefined
: () => onConfigureUsage(provider)
}