mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 20:15:40 +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:
1 parent
b2b8a85af6
commit
27e8813cae
3 files changed
+45
-56
No files matched your search
@@ -11,7 +11,7 @@ import { getAuthDir } from './config-generator';
|
|||||||
import { CLIProxyProvider } from './types';
|
import { CLIProxyProvider } from './types';
|
||||||
import {
|
import {
|
||||||
getProviderAccounts,
|
getProviderAccounts,
|
||||||
isAccountPaused,
|
getPausedDir,
|
||||||
setAccountTier,
|
setAccountTier,
|
||||||
type AccountInfo,
|
type AccountInfo,
|
||||||
type AccountTier,
|
type AccountTier,
|
||||||
@@ -242,59 +242,60 @@ async function refreshAccessToken(
|
|||||||
* Read auth data from auth file (access token, project_id, expiry status)
|
* Read auth data from auth file (access token, project_id, expiry status)
|
||||||
*/
|
*/
|
||||||
function readAuthData(provider: CLIProxyProvider, accountId: string): AuthData | null {
|
function readAuthData(provider: CLIProxyProvider, accountId: string): AuthData | null {
|
||||||
const authDir = getAuthDir();
|
// Check both active and paused auth directories (quota needed for paused accounts too)
|
||||||
|
const authDirs = [getAuthDir(), getPausedDir()];
|
||||||
// Check if auth directory exists
|
|
||||||
if (!fs.existsSync(authDir)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize accountId (email) to match auth file naming: @ and . → _
|
// Sanitize accountId (email) to match auth file naming: @ and . → _
|
||||||
const sanitizedId = sanitizeEmail(accountId);
|
const sanitizedId = sanitizeEmail(accountId);
|
||||||
const prefix = provider === 'agy' ? 'antigravity-' : `${provider}-`;
|
const prefix = provider === 'agy' ? 'antigravity-' : `${provider}-`;
|
||||||
const expectedFile = `${prefix}${sanitizedId}.json`;
|
const expectedFile = `${prefix}${sanitizedId}.json`;
|
||||||
const filePath = path.join(authDir, expectedFile);
|
|
||||||
|
|
||||||
// Direct file access (most common case)
|
for (const authDir of authDirs) {
|
||||||
if (fs.existsSync(filePath)) {
|
if (!fs.existsSync(authDir)) continue;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: scan directory for matching email in file content
|
const filePath = path.join(authDir, expectedFile);
|
||||||
const files = fs.readdirSync(authDir);
|
|
||||||
for (const file of files) {
|
// Direct file access (most common case)
|
||||||
if (file.startsWith(prefix) && file.endsWith('.json')) {
|
if (fs.existsSync(filePath)) {
|
||||||
const candidatePath = path.join(authDir, file);
|
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(candidatePath, 'utf-8');
|
const content = fs.readFileSync(filePath, 'utf-8');
|
||||||
const data = JSON.parse(content) as AntigravityAuthFile;
|
const data = JSON.parse(content) as AntigravityAuthFile;
|
||||||
// Match by email field inside the auth file
|
if (!data.access_token) continue;
|
||||||
if (data.email === accountId && data.access_token) {
|
return {
|
||||||
return {
|
accessToken: data.access_token,
|
||||||
accessToken: data.access_token,
|
refreshToken: data.refresh_token || null,
|
||||||
refreshToken: data.refresh_token || null,
|
projectId: data.project_id || null,
|
||||||
projectId: data.project_id || null,
|
isExpired: isTokenExpired(data.expired),
|
||||||
isExpired: isTokenExpired(data.expired),
|
expiresAt: data.expired || null,
|
||||||
expiresAt: data.expired || null,
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
continue;
|
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;
|
return null;
|
||||||
@@ -554,19 +555,7 @@ export async function fetchAccountQuota(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if account is paused (token moved to auth-paused/ directory)
|
// Read auth data from auth file (checks both active and paused directories)
|
||||||
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
|
|
||||||
const authData = readAuthData(provider, accountId);
|
const authData = readAuthData(provider, accountId);
|
||||||
if (!authData) {
|
if (!authData) {
|
||||||
const error = 'Auth file not found for account';
|
const error = 'Auth file not found for account';
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ export function AccountCard({
|
|||||||
{m.displayName}
|
{m.displayName}
|
||||||
</span>
|
</span>
|
||||||
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
||||||
{m.exhausted ? 'Exhausted' : `${m.percentage}%`}
|
{m.percentage}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ export function AccountItem({
|
|||||||
{m.displayName}
|
{m.displayName}
|
||||||
</span>
|
</span>
|
||||||
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
<span className={cn('font-mono', m.exhausted && 'text-red-500')}>
|
||||||
{m.exhausted ? 'Exhausted' : `${m.percentage}%`}
|
{m.percentage}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in new issue
Block a user