diff --git a/src/cliproxy/account-manager.ts b/src/cliproxy/account-manager.ts index fc450c7b..8906994b 100644 --- a/src/cliproxy/account-manager.ts +++ b/src/cliproxy/account-manager.ts @@ -47,6 +47,8 @@ export interface AccountInfo { pausedAt?: string; /** Account tier: free or paid (Pro/Ultra combined) */ tier?: AccountTier; + /** GCP Project ID (Antigravity only) - read-only, fetched from auth token */ + projectId?: string; } /** Provider accounts configuration */ @@ -293,7 +295,8 @@ export function registerAccount( provider: CLIProxyProvider, tokenFile: string, email?: string, - nickname?: string + nickname?: string, + projectId?: string ): AccountInfo { const registry = loadAccountsRegistry(); @@ -350,7 +353,7 @@ export function registerAccount( const isFirstAccount = Object.keys(providerAccounts.accounts).length === 0; // Create or update account - providerAccounts.accounts[accountId] = { + const accountMeta: Omit = { email, nickname: accountNickname, tokenFile, @@ -358,6 +361,13 @@ export function registerAccount( 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 if (isFirstAccount) { providerAccounts.default = accountId; @@ -671,13 +681,20 @@ export function discoverExistingAccounts(): void { // Register account with auto-generated nickname // Use mtime as lastUsedAt (when token was last modified = last auth/refresh) const lastModified = stats.mtime || stats.birthtime || new Date(); - providerAccounts.accounts[accountId] = { + const accountMeta: Omit = { email, nickname: generateNickname(email), tokenFile: file, createdAt: stats.birthtime?.toISOString() || new Date().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 { // Skip invalid files continue; diff --git a/src/cliproxy/auth/token-manager.ts b/src/cliproxy/auth/token-manager.ts index 436539cc..16e79c78 100644 --- a/src/cliproxy/auth/token-manager.ts +++ b/src/cliproxy/auth/token-manager.ts @@ -229,8 +229,15 @@ export function registerAccountFromToken( const content = fs.readFileSync(tokenPath, 'utf-8'); const data = JSON.parse(content); 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 { return null; } diff --git a/ui/src/components/cliproxy/provider-editor/account-item.tsx b/ui/src/components/cliproxy/provider-editor/account-item.tsx index 031a01b3..d61ea74d 100644 --- a/ui/src/components/cliproxy/provider-editor/account-item.tsx +++ b/ui/src/components/cliproxy/provider-editor/account-item.tsx @@ -25,6 +25,8 @@ import { Pause, Play, AlertCircle, + AlertTriangle, + FolderCode, } from 'lucide-react'; import { cn, @@ -167,6 +169,48 @@ export function AccountItem({ )} + {/* Project ID for Antigravity accounts - read-only */} + {account.provider === 'agy' && ( +
+ {account.projectId ? ( + + + +
+ + + {account.projectId} + +
+
+ +

GCP Project ID (read-only)

+
+
+
+ ) : ( + + + +
+ + Project ID: N/A +
+
+ +
+

Missing Project ID

+

+ This may cause errors. Remove the account and re-add it to fetch the + project ID. +

+
+
+
+
+ )} +
+ )} {account.lastUsedAt && (
diff --git a/ui/src/components/monitoring/auth-monitor/hooks.ts b/ui/src/components/monitoring/auth-monitor/hooks.ts index 1640f037..dd19b8bc 100644 --- a/ui/src/components/monitoring/auth-monitor/hooks.ts +++ b/ui/src/components/monitoring/auth-monitor/hooks.ts @@ -99,6 +99,7 @@ export function useAuthMonitorData(): AuthMonitorData { failureCount: failure, lastUsedAt: realStats?.lastUsedAt ?? account.lastUsedAt, color: ACCOUNT_COLORS[colorIndex % ACCOUNT_COLORS.length], + projectId: account.projectId, }; accountsList.push(row); providerData.accounts.push(row); diff --git a/ui/src/components/monitoring/auth-monitor/types.ts b/ui/src/components/monitoring/auth-monitor/types.ts index 1eb61953..408b1e71 100644 --- a/ui/src/components/monitoring/auth-monitor/types.ts +++ b/ui/src/components/monitoring/auth-monitor/types.ts @@ -12,6 +12,8 @@ export interface AccountRow { failureCount: number; lastUsedAt?: string; color: string; + /** GCP Project ID (Antigravity only) - read-only */ + projectId?: string; } export interface ProviderStats { diff --git a/ui/src/lib/api-client.ts b/ui/src/lib/api-client.ts index 22d67bb0..95ce900c 100644 --- a/ui/src/lib/api-client.ts +++ b/ui/src/lib/api-client.ts @@ -83,6 +83,8 @@ export interface OAuthAccount { pausedAt?: string; /** Account tier: free or paid (Pro/Ultra combined) */ tier?: 'free' | 'paid' | 'unknown'; + /** GCP Project ID (Antigravity only) - read-only */ + projectId?: string; } export interface AuthStatus {