From 27e8813cae5379dbd8f9e700155812e59fe99389 Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Sun, 25 Jan 2026 17:12:45 -0500 Subject: [PATCH] fix(quota): fetch quota for paused accounts + simplify exhausted display - Remove early return for paused accounts in quota fetcher - Check both auth/ and auth-paused/ directories when reading auth data - Allows users to see quota status of paused accounts to know when to unpause - Remove "Exhausted" text in tooltip, just show 0% in red (saves width) --- src/cliproxy/quota-fetcher.ts | 97 ++++++++----------- .../account/flow-viz/account-card.tsx | 2 +- .../cliproxy/provider-editor/account-item.tsx | 2 +- 3 files changed, 45 insertions(+), 56 deletions(-) diff --git a/src/cliproxy/quota-fetcher.ts b/src/cliproxy/quota-fetcher.ts index df0accb9..f1b993d8 100644 --- a/src/cliproxy/quota-fetcher.ts +++ b/src/cliproxy/quota-fetcher.ts @@ -11,7 +11,7 @@ import { getAuthDir } from './config-generator'; import { CLIProxyProvider } from './types'; import { getProviderAccounts, - isAccountPaused, + getPausedDir, setAccountTier, type AccountInfo, type AccountTier, @@ -242,59 +242,60 @@ async function refreshAccessToken( * Read auth data from auth file (access token, project_id, expiry status) */ function readAuthData(provider: CLIProxyProvider, accountId: string): AuthData | null { - const authDir = getAuthDir(); - - // Check if auth directory exists - if (!fs.existsSync(authDir)) { - return null; - } + // Check both active and paused auth directories (quota needed for paused accounts too) + const authDirs = [getAuthDir(), getPausedDir()]; // Sanitize accountId (email) to match auth file naming: @ and . → _ const sanitizedId = sanitizeEmail(accountId); const prefix = provider === 'agy' ? 'antigravity-' : `${provider}-`; const expectedFile = `${prefix}${sanitizedId}.json`; - const filePath = path.join(authDir, expectedFile); - // Direct file access (most common case) - if (fs.existsSync(filePath)) { - try { - const content = fs.readFileSync(filePath, 'utf-8'); - const data = JSON.parse(content) as AntigravityAuthFile; - if (!data.access_token) return null; - return { - accessToken: data.access_token, - refreshToken: data.refresh_token || null, - projectId: data.project_id || null, - isExpired: isTokenExpired(data.expired), - expiresAt: data.expired || null, - }; - } catch { - return null; - } - } + for (const authDir of authDirs) { + if (!fs.existsSync(authDir)) continue; - // Fallback: scan directory for matching email in file content - const files = fs.readdirSync(authDir); - for (const file of files) { - if (file.startsWith(prefix) && file.endsWith('.json')) { - const candidatePath = path.join(authDir, file); + const filePath = path.join(authDir, expectedFile); + + // Direct file access (most common case) + if (fs.existsSync(filePath)) { try { - const content = fs.readFileSync(candidatePath, 'utf-8'); + const content = fs.readFileSync(filePath, 'utf-8'); const data = JSON.parse(content) as AntigravityAuthFile; - // Match by email field inside the auth file - if (data.email === accountId && data.access_token) { - return { - accessToken: data.access_token, - refreshToken: data.refresh_token || null, - projectId: data.project_id || null, - isExpired: isTokenExpired(data.expired), - expiresAt: data.expired || null, - }; - } + if (!data.access_token) continue; + return { + accessToken: data.access_token, + refreshToken: data.refresh_token || null, + projectId: data.project_id || null, + isExpired: isTokenExpired(data.expired), + expiresAt: data.expired || null, + }; } catch { continue; } } + + // Fallback: scan directory for matching email in file content + const files = fs.readdirSync(authDir); + for (const file of files) { + if (file.startsWith(prefix) && file.endsWith('.json')) { + const candidatePath = path.join(authDir, file); + try { + const content = fs.readFileSync(candidatePath, 'utf-8'); + const data = JSON.parse(content) as AntigravityAuthFile; + // Match by email field inside the auth file + if (data.email === accountId && data.access_token) { + return { + accessToken: data.access_token, + refreshToken: data.refresh_token || null, + projectId: data.project_id || null, + isExpired: isTokenExpired(data.expired), + expiresAt: data.expired || null, + }; + } + } catch { + continue; + } + } + } } return null; @@ -554,19 +555,7 @@ export async function fetchAccountQuota( }; } - // Check if account is paused (token moved to auth-paused/ directory) - if (isAccountPaused(provider, accountId)) { - const error = 'Account is paused'; - if (verbose) console.error(`[i] ${error}`); - return { - success: false, - models: [], - lastUpdated: Date.now(), - error, - }; - } - - // Read auth data from auth file + // Read auth data from auth file (checks both active and paused directories) const authData = readAuthData(provider, accountId); if (!authData) { const error = 'Auth file not found for account'; diff --git a/ui/src/components/account/flow-viz/account-card.tsx b/ui/src/components/account/flow-viz/account-card.tsx index 094b9038..6ddd4dde 100644 --- a/ui/src/components/account/flow-viz/account-card.tsx +++ b/ui/src/components/account/flow-viz/account-card.tsx @@ -240,7 +240,7 @@ export function AccountCard({ {m.displayName} - {m.exhausted ? 'Exhausted' : `${m.percentage}%`} + {m.percentage}% ))} diff --git a/ui/src/components/cliproxy/provider-editor/account-item.tsx b/ui/src/components/cliproxy/provider-editor/account-item.tsx index d353e2c4..ec71ce8a 100644 --- a/ui/src/components/cliproxy/provider-editor/account-item.tsx +++ b/ui/src/components/cliproxy/provider-editor/account-item.tsx @@ -370,7 +370,7 @@ export function AccountItem({ {m.displayName} - {m.exhausted ? 'Exhausted' : `${m.percentage}%`} + {m.percentage}% ))}