From ebb80df24a8cc80eb54db9a688ae7eafcd0f1640 Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Wed, 14 Jan 2026 16:44:36 +0800 Subject: [PATCH 1/2] fix(quota): include project_id in antigravity quota requests --- src/components/quota/quotaConfigs.ts | 41 ++++++++++++++++++++++++++-- src/services/api/authFiles.ts | 8 ++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/components/quota/quotaConfigs.ts b/src/components/quota/quotaConfigs.ts index 6a023d0..f1870e3 100644 --- a/src/components/quota/quotaConfigs.ts +++ b/src/components/quota/quotaConfigs.ts @@ -18,7 +18,7 @@ import type { GeminiCliQuotaBucketState, GeminiCliQuotaState } from '@/types'; -import { apiCallApi, getApiCallErrorMessage } from '@/services/api'; +import { apiCallApi, authFilesApi, getApiCallErrorMessage } from '@/services/api'; import { ANTIGRAVITY_QUOTA_URLS, ANTIGRAVITY_REQUEST_HEADERS, @@ -55,6 +55,8 @@ type QuotaUpdater = T | ((prev: T) => T); type QuotaType = 'antigravity' | 'codex' | 'gemini-cli'; +const DEFAULT_ANTIGRAVITY_PROJECT_ID = 'bamboo-precept-lgxtn'; + export interface QuotaStore { antigravityQuota: Record; codexQuota: Record; @@ -82,6 +84,38 @@ export interface QuotaConfig { renderQuotaItems: (quota: TState, t: TFunction, helpers: QuotaRenderHelpers) => ReactNode; } +const resolveAntigravityProjectId = async (file: AuthFileItem): Promise => { + try { + const text = await authFilesApi.downloadText(file.name); + const trimmed = text.trim(); + if (!trimmed) return DEFAULT_ANTIGRAVITY_PROJECT_ID; + + const parsed = JSON.parse(trimmed) as Record; + const topLevel = normalizeStringValue(parsed.project_id ?? parsed.projectId); + if (topLevel) return topLevel; + + const installed = + parsed.installed && typeof parsed.installed === 'object' && parsed.installed !== null + ? (parsed.installed as Record) + : null; + const installedProjectId = installed + ? normalizeStringValue(installed.project_id ?? installed.projectId) + : null; + if (installedProjectId) return installedProjectId; + + const web = + parsed.web && typeof parsed.web === 'object' && parsed.web !== null + ? (parsed.web as Record) + : null; + const webProjectId = web ? normalizeStringValue(web.project_id ?? web.projectId) : null; + if (webProjectId) return webProjectId; + } catch { + return DEFAULT_ANTIGRAVITY_PROJECT_ID; + } + + return DEFAULT_ANTIGRAVITY_PROJECT_ID; +}; + const fetchAntigravityQuota = async ( file: AuthFileItem, t: TFunction @@ -92,6 +126,9 @@ const fetchAntigravityQuota = async ( throw new Error(t('antigravity_quota.missing_auth_index')); } + const projectId = await resolveAntigravityProjectId(file); + const requestBody = JSON.stringify({ project_id: projectId }); + let lastError = ''; let lastStatus: number | undefined; let priorityStatus: number | undefined; @@ -104,7 +141,7 @@ const fetchAntigravityQuota = async ( method: 'POST', url, header: { ...ANTIGRAVITY_REQUEST_HEADERS }, - data: '{}' + data: requestBody }); if (result.statusCode < 200 || result.statusCode >= 300) { diff --git a/src/services/api/authFiles.ts b/src/services/api/authFiles.ts index 054b1cf..47a93f0 100644 --- a/src/services/api/authFiles.ts +++ b/src/services/api/authFiles.ts @@ -56,6 +56,14 @@ export const authFilesApi = { deleteAll: () => apiClient.delete('/auth-files', { params: { all: true } }), + downloadText: async (name: string): Promise => { + const response = await apiClient.getRaw(`/auth-files/download?name=${encodeURIComponent(name)}`, { + responseType: 'blob' + }); + const blob = response.data as Blob; + return blob.text(); + }, + // OAuth 排除模型 async getOauthExcludedModels(): Promise> { const data = await apiClient.get('/oauth-excluded-models'); From a44257eddaf989e68de3ac995c77aa26fe8bffd4 Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Wed, 14 Jan 2026 17:13:07 +0800 Subject: [PATCH 2/2] fix(antigravity): enhance error handling and support multiple request bodies --- src/components/quota/quotaConfigs.ts | 86 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/src/components/quota/quotaConfigs.ts b/src/components/quota/quotaConfigs.ts index f1870e3..e36b050 100644 --- a/src/components/quota/quotaConfigs.ts +++ b/src/components/quota/quotaConfigs.ts @@ -116,6 +116,11 @@ const resolveAntigravityProjectId = async (file: AuthFileItem): Promise return DEFAULT_ANTIGRAVITY_PROJECT_ID; }; +const isAntigravityUnknownFieldError = (message: string): boolean => { + const normalized = message.toLowerCase(); + return normalized.includes('unknown name') && normalized.includes('cannot find field'); +}; + const fetchAntigravityQuota = async ( file: AuthFileItem, t: TFunction @@ -127,7 +132,7 @@ const fetchAntigravityQuota = async ( } const projectId = await resolveAntigravityProjectId(file); - const requestBody = JSON.stringify({ project_id: projectId }); + const requestBodies = [JSON.stringify({ projectId }), JSON.stringify({ project: projectId })]; let lastError = ''; let lastStatus: number | undefined; @@ -135,46 +140,55 @@ const fetchAntigravityQuota = async ( let hadSuccess = false; for (const url of ANTIGRAVITY_QUOTA_URLS) { - try { - const result = await apiCallApi.request({ - authIndex, - method: 'POST', - url, - header: { ...ANTIGRAVITY_REQUEST_HEADERS }, - data: requestBody - }); + for (let attempt = 0; attempt < requestBodies.length; attempt++) { + try { + const result = await apiCallApi.request({ + authIndex, + method: 'POST', + url, + header: { ...ANTIGRAVITY_REQUEST_HEADERS }, + data: requestBodies[attempt] + }); - if (result.statusCode < 200 || result.statusCode >= 300) { - lastError = getApiCallErrorMessage(result); - lastStatus = result.statusCode; - if (result.statusCode === 403 || result.statusCode === 404) { - priorityStatus ??= result.statusCode; + if (result.statusCode < 200 || result.statusCode >= 300) { + lastError = getApiCallErrorMessage(result); + lastStatus = result.statusCode; + if (result.statusCode === 403 || result.statusCode === 404) { + priorityStatus ??= result.statusCode; + } + if ( + result.statusCode === 400 && + isAntigravityUnknownFieldError(lastError) && + attempt < requestBodies.length - 1 + ) { + continue; + } + break; } - continue; - } - hadSuccess = true; - const payload = parseAntigravityPayload(result.body ?? result.bodyText); - const models = payload?.models; - if (!models || typeof models !== 'object' || Array.isArray(models)) { - lastError = t('antigravity_quota.empty_models'); - continue; - } + hadSuccess = true; + const payload = parseAntigravityPayload(result.body ?? result.bodyText); + const models = payload?.models; + if (!models || typeof models !== 'object' || Array.isArray(models)) { + lastError = t('antigravity_quota.empty_models'); + continue; + } - const groups = buildAntigravityQuotaGroups(models as AntigravityModelsPayload); - if (groups.length === 0) { - lastError = t('antigravity_quota.empty_models'); - continue; - } + const groups = buildAntigravityQuotaGroups(models as AntigravityModelsPayload); + if (groups.length === 0) { + lastError = t('antigravity_quota.empty_models'); + continue; + } - return groups; - } catch (err: unknown) { - lastError = err instanceof Error ? err.message : t('common.unknown_error'); - const status = getStatusFromError(err); - if (status) { - lastStatus = status; - if (status === 403 || status === 404) { - priorityStatus ??= status; + return groups; + } catch (err: unknown) { + lastError = err instanceof Error ? err.message : t('common.unknown_error'); + const status = getStatusFromError(err); + if (status) { + lastStatus = status; + if (status === 403 || status === 404) { + priorityStatus ??= status; + } } } }