mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-23 12:23:35 +00:00
feat(dashboard): add project_id display for Antigravity accounts
Display GCP project_id for Antigravity (agy) accounts in the Dashboard: - Add projectId field to AccountInfo interface (account-manager.ts) - Read project_id from auth files when discovering/registering accounts - Pass projectId through token-manager when registering new accounts - Add projectId to OAuthAccount type (api-client.ts) - Add projectId to AccountRow type (auth-monitor/types.ts) - Display project_id in account-item.tsx with FolderCode icon - Show N/A warning with amber tooltip if project_id is missing suggesting user remove and re-add account to fetch it Note: project_id is read-only and respects privacy mode blur.
This commit is contained in:
@@ -47,6 +47,8 @@ export interface AccountInfo {
|
|||||||
pausedAt?: string;
|
pausedAt?: string;
|
||||||
/** Account tier: free or paid (Pro/Ultra combined) */
|
/** Account tier: free or paid (Pro/Ultra combined) */
|
||||||
tier?: AccountTier;
|
tier?: AccountTier;
|
||||||
|
/** GCP Project ID (Antigravity only) - read-only, fetched from auth token */
|
||||||
|
projectId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Provider accounts configuration */
|
/** Provider accounts configuration */
|
||||||
@@ -293,7 +295,8 @@ export function registerAccount(
|
|||||||
provider: CLIProxyProvider,
|
provider: CLIProxyProvider,
|
||||||
tokenFile: string,
|
tokenFile: string,
|
||||||
email?: string,
|
email?: string,
|
||||||
nickname?: string
|
nickname?: string,
|
||||||
|
projectId?: string
|
||||||
): AccountInfo {
|
): AccountInfo {
|
||||||
const registry = loadAccountsRegistry();
|
const registry = loadAccountsRegistry();
|
||||||
|
|
||||||
@@ -350,7 +353,7 @@ export function registerAccount(
|
|||||||
const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0;
|
const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0;
|
||||||
|
|
||||||
// Create or update account
|
// Create or update account
|
||||||
providerAccounts.accounts[accountId] = {
|
const accountMeta: Omit<AccountInfo, 'id' | 'provider' | 'isDefault'> = {
|
||||||
email,
|
email,
|
||||||
nickname: accountNickname,
|
nickname: accountNickname,
|
||||||
tokenFile,
|
tokenFile,
|
||||||
@@ -358,6 +361,13 @@ export function registerAccount(
|
|||||||
lastUsedAt: new Date().toISOString(),
|
lastUsedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Include projectId for Antigravity accounts
|
||||||
|
if (provider === 'agy' && projectId) {
|
||||||
|
accountMeta.projectId = projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
providerAccounts.accounts[accountId] = accountMeta;
|
||||||
|
|
||||||
// Set as default if first account
|
// Set as default if first account
|
||||||
if (isFirstAccount) {
|
if (isFirstAccount) {
|
||||||
providerAccounts.default = accountId;
|
providerAccounts.default = accountId;
|
||||||
@@ -671,13 +681,20 @@ export function discoverExistingAccounts(): void {
|
|||||||
// Register account with auto-generated nickname
|
// Register account with auto-generated nickname
|
||||||
// Use mtime as lastUsedAt (when token was last modified = last auth/refresh)
|
// Use mtime as lastUsedAt (when token was last modified = last auth/refresh)
|
||||||
const lastModified = stats.mtime || stats.birthtime || new Date();
|
const lastModified = stats.mtime || stats.birthtime || new Date();
|
||||||
providerAccounts.accounts[accountId] = {
|
const accountMeta: Omit<AccountInfo, 'id' | 'provider' | 'isDefault'> = {
|
||||||
email,
|
email,
|
||||||
nickname: generateNickname(email),
|
nickname: generateNickname(email),
|
||||||
tokenFile: file,
|
tokenFile: file,
|
||||||
createdAt: stats.birthtime?.toISOString() || new Date().toISOString(),
|
createdAt: stats.birthtime?.toISOString() || new Date().toISOString(),
|
||||||
lastUsedAt: lastModified.toISOString(),
|
lastUsedAt: lastModified.toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Read project_id for Antigravity accounts (read-only field from auth token)
|
||||||
|
if (provider === 'agy' && data.project_id) {
|
||||||
|
accountMeta.projectId = data.project_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
providerAccounts.accounts[accountId] = accountMeta;
|
||||||
} catch {
|
} catch {
|
||||||
// Skip invalid files
|
// Skip invalid files
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -229,8 +229,15 @@ export function registerAccountFromToken(
|
|||||||
const content = fs.readFileSync(tokenPath, 'utf-8');
|
const content = fs.readFileSync(tokenPath, 'utf-8');
|
||||||
const data = JSON.parse(content);
|
const data = JSON.parse(content);
|
||||||
const email = data.email || undefined;
|
const email = data.email || undefined;
|
||||||
|
const projectId = data.project_id || undefined;
|
||||||
|
|
||||||
return registerAccount(provider, newestFile, email, nickname || generateNickname(email));
|
return registerAccount(
|
||||||
|
provider,
|
||||||
|
newestFile,
|
||||||
|
email,
|
||||||
|
nickname || generateNickname(email),
|
||||||
|
projectId
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
Pause,
|
Pause,
|
||||||
Play,
|
Play,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
|
AlertTriangle,
|
||||||
|
FolderCode,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
cn,
|
cn,
|
||||||
@@ -167,6 +169,48 @@ export function AccountItem({
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* Project ID for Antigravity accounts - read-only */}
|
||||||
|
{account.provider === 'agy' && (
|
||||||
|
<div className="flex items-center gap-1.5 mt-1">
|
||||||
|
{account.projectId ? (
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
|
<FolderCode className="w-3 h-3" />
|
||||||
|
<span className={cn('font-mono', privacyMode && PRIVACY_BLUR_CLASS)}>
|
||||||
|
{account.projectId}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom">
|
||||||
|
<p className="text-xs">GCP Project ID (read-only)</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
) : (
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="flex items-center gap-1 text-xs text-amber-600 dark:text-amber-500">
|
||||||
|
<AlertTriangle className="w-3 h-3" />
|
||||||
|
<span>Project ID: N/A</span>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="bottom" className="max-w-[250px]">
|
||||||
|
<div className="text-xs space-y-1">
|
||||||
|
<p className="font-medium text-amber-600">Missing Project ID</p>
|
||||||
|
<p>
|
||||||
|
This may cause errors. Remove the account and re-add it to fetch the
|
||||||
|
project ID.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{account.lastUsedAt && (
|
{account.lastUsedAt && (
|
||||||
<div className="flex items-center gap-1 text-xs text-muted-foreground mt-0.5">
|
<div className="flex items-center gap-1 text-xs text-muted-foreground mt-0.5">
|
||||||
<Clock className="w-3 h-3" />
|
<Clock className="w-3 h-3" />
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ export function useAuthMonitorData(): AuthMonitorData {
|
|||||||
failureCount: failure,
|
failureCount: failure,
|
||||||
lastUsedAt: realStats?.lastUsedAt ?? account.lastUsedAt,
|
lastUsedAt: realStats?.lastUsedAt ?? account.lastUsedAt,
|
||||||
color: ACCOUNT_COLORS[colorIndex % ACCOUNT_COLORS.length],
|
color: ACCOUNT_COLORS[colorIndex % ACCOUNT_COLORS.length],
|
||||||
|
projectId: account.projectId,
|
||||||
};
|
};
|
||||||
accountsList.push(row);
|
accountsList.push(row);
|
||||||
providerData.accounts.push(row);
|
providerData.accounts.push(row);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export interface AccountRow {
|
|||||||
failureCount: number;
|
failureCount: number;
|
||||||
lastUsedAt?: string;
|
lastUsedAt?: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
/** GCP Project ID (Antigravity only) - read-only */
|
||||||
|
projectId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderStats {
|
export interface ProviderStats {
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ export interface OAuthAccount {
|
|||||||
pausedAt?: string;
|
pausedAt?: string;
|
||||||
/** Account tier: free or paid (Pro/Ultra combined) */
|
/** Account tier: free or paid (Pro/Ultra combined) */
|
||||||
tier?: 'free' | 'paid' | 'unknown';
|
tier?: 'free' | 'paid' | 'unknown';
|
||||||
|
/** GCP Project ID (Antigravity only) - read-only */
|
||||||
|
projectId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthStatus {
|
export interface AuthStatus {
|
||||||
|
|||||||
Reference in New Issue
Block a user