fix(quota): add unprovisioned account detection with actionable message

- Add isUnprovisioned flag to QuotaResult interface
- Detect when account is authenticated but lacks project ID
- Show actionable message: "Sign in to Antigravity app to activate quota."
- Refactor getProjectId retry logic for cleaner error propagation
This commit is contained in:
kaitranntt
2025-12-29 13:48:56 -05:00
parent e3a71fc893
commit ecfdcdef78
+16 -12
View File
@@ -38,6 +38,8 @@ export interface QuotaResult {
isExpired?: boolean; isExpired?: boolean;
/** ISO timestamp when token expires/expired */ /** ISO timestamp when token expires/expired */
expiresAt?: string; expiresAt?: string;
/** True if account hasn't been activated in official Antigravity app */
isUnprovisioned?: boolean;
} }
/** Google Cloud Code API endpoints */ /** Google Cloud Code API endpoints */
@@ -257,7 +259,7 @@ function readAuthData(provider: CLIProxyProvider, accountId: string): AuthData |
*/ */
async function getProjectId( async function getProjectId(
accessToken: string accessToken: string
): Promise<{ projectId: string | null; error?: string }> { ): Promise<{ projectId: string | null; error?: string; isUnprovisioned?: boolean }> {
const url = `${ANTIGRAVITY_API_BASE}/${ANTIGRAVITY_API_VERSION}:loadCodeAssist`; const url = `${ANTIGRAVITY_API_BASE}/${ANTIGRAVITY_API_VERSION}:loadCodeAssist`;
const controller = new AbortController(); const controller = new AbortController();
@@ -304,7 +306,12 @@ async function getProjectId(
} }
if (!projectId?.trim()) { if (!projectId?.trim()) {
return { projectId: null, error: 'No project ID in response' }; // Account authenticated but not provisioned - user needs to sign in via Antigravity app
return {
projectId: null,
error: 'Sign in to Antigravity app to activate quota.',
isUnprovisioned: true,
};
} }
return { projectId: projectId.trim() }; return { projectId: projectId.trim() };
@@ -465,30 +472,27 @@ export async function fetchAccountQuota(
// Get project ID - prefer stored value, fallback to API call // Get project ID - prefer stored value, fallback to API call
let projectId = authData.projectId; let projectId = authData.projectId;
if (!projectId) { if (!projectId) {
const projectResult = await getProjectId(accessToken); let lastProjectResult = await getProjectId(accessToken);
if (!projectResult.projectId) { if (!lastProjectResult.projectId) {
// If project ID fetch fails, it might be token issue - try refresh if we haven't // If project ID fetch fails, it might be token issue - try refresh if we haven't
if (authData.refreshToken && accessToken === authData.accessToken) { if (authData.refreshToken && accessToken === authData.accessToken) {
const refreshResult = await refreshAccessToken(authData.refreshToken); const refreshResult = await refreshAccessToken(authData.refreshToken);
if (refreshResult.accessToken) { if (refreshResult.accessToken) {
accessToken = refreshResult.accessToken; accessToken = refreshResult.accessToken;
const retryResult = await getProjectId(accessToken); lastProjectResult = await getProjectId(accessToken);
if (retryResult.projectId) {
projectId = retryResult.projectId;
}
} }
} }
if (!projectId) { if (!lastProjectResult.projectId) {
return { return {
success: false, success: false,
models: [], models: [],
lastUpdated: Date.now(), lastUpdated: Date.now(),
error: projectResult.error || 'Failed to retrieve project ID', error: lastProjectResult.error || 'Failed to retrieve project ID',
isUnprovisioned: lastProjectResult.isUnprovisioned,
}; };
} }
} else {
projectId = projectResult.projectId;
} }
projectId = lastProjectResult.projectId;
} }
// Fetch models with quota // Fetch models with quota