mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +00:00
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)
This commit is contained in:
@@ -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';
|
||||
|
||||
@@ -240,7 +240,7 @@ export function AccountCard({
|
||||
{m.displayName}
|
||||
</span>
|
||||
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
||||
{m.exhausted ? 'Exhausted' : `${m.percentage}%`}
|
||||
{m.percentage}%
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -370,7 +370,7 @@ export function AccountItem({
|
||||
{m.displayName}
|
||||
</span>
|
||||
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
||||
{m.exhausted ? 'Exhausted' : `${m.percentage}%`}
|
||||
{m.percentage}%
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user