fix(antigravity): implement automatic project ID discovery and prevent overwrite

This commit introduces automatic Cloud Code project discovery for the Antigravity provider. It ports the onboarding logic from gemini-core.js, handles missing credentials by triggering the auth flow, and ensures the discovered Project ID is correctly passed to the request payload builder, fixing the 403 Permission Denied error caused by random ID generation.
This commit is contained in:
lethanhson9901 2025-12-09 10:21:37 +07:00
parent 3ec219bcc7
commit abf32f6cb5

View file

@ -91,14 +91,14 @@ function generateProjectID() {
/** /**
* Gemini 格式请求转换为 Antigravity 格式 * Gemini 格式请求转换为 Antigravity 格式
*/ */
function geminiToAntigravity(modelName, payload) { function geminiToAntigravity(modelName, payload, projectId) {
// 深拷贝请求体,避免修改原始对象 // 深拷贝请求体,避免修改原始对象
let template = JSON.parse(JSON.stringify(payload)); let template = JSON.parse(JSON.stringify(payload));
// 设置基本字段 // 设置基本字段
template.model = modelName; template.model = modelName;
template.userAgent = 'antigravity'; template.userAgent = 'antigravity';
template.project = generateProjectID(); template.project = projectId || generateProjectID();
template.requestId = generateRequestID(); template.requestId = generateRequestID();
// 确保 request 对象存在 // 确保 request 对象存在
@ -235,15 +235,13 @@ export class AntigravityApiService {
await this.initializeAuth(); await this.initializeAuth();
if (!this.projectId) { if (!this.projectId) {
this.projectId = generateProjectID(); this.projectId = await this.discoverProjectAndModels();
console.log(`[Antigravity] Generated Project ID: ${this.projectId}`);
} else { } else {
console.log(`[Antigravity] Using provided Project ID: ${this.projectId}`); console.log(`[Antigravity] Using provided Project ID: ${this.projectId}`);
// 获取可用模型
await this.fetchAvailableModels();
} }
// 获取可用模型
await this.fetchAvailableModels();
this.isInitialized = true; this.isInitialized = true;
console.log(`[Antigravity] Initialization complete. Project ID: ${this.projectId}`); console.log(`[Antigravity] Initialization complete. Project ID: ${this.projectId}`);
} }
@ -381,6 +379,81 @@ export class AntigravityApiService {
return expiryTime <= (currentTime + refreshSkewMs); return expiryTime <= (currentTime + refreshSkewMs);
} }
async discoverProjectAndModels() {
if (this.projectId) {
console.log(`[Antigravity] Using pre-configured Project ID: ${this.projectId}`);
return this.projectId;
}
console.log('[Antigravity] Discovering Project ID...');
try {
const initialProjectId = "";
// Prepare client metadata
const clientMetadata = {
ideType: "IDE_UNSPECIFIED",
platform: "PLATFORM_UNSPECIFIED",
pluginType: "GEMINI",
duetProject: initialProjectId,
};
// Call loadCodeAssist to discover the actual project ID
const loadRequest = {
cloudaicompanionProject: initialProjectId,
metadata: clientMetadata,
};
const loadResponse = await this.callApi('loadCodeAssist', loadRequest);
// Check if we already have a project ID from the response
if (loadResponse.cloudaicompanionProject) {
console.log(`[Antigravity] Discovered existing Project ID: ${loadResponse.cloudaicompanionProject}`);
// 获取可用模型
await this.fetchAvailableModels();
return loadResponse.cloudaicompanionProject;
}
// If no existing project, we need to onboard
const defaultTier = loadResponse.allowedTiers?.find(tier => tier.isDefault);
const tierId = defaultTier?.id || 'free-tier';
const onboardRequest = {
tierId: tierId,
cloudaicompanionProject: initialProjectId,
metadata: clientMetadata,
};
let lroResponse = await this.callApi('onboardUser', onboardRequest);
// Poll until operation is complete with timeout protection
const MAX_RETRIES = 30; // Maximum number of retries (60 seconds total)
let retryCount = 0;
while (!lroResponse.done && retryCount < MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, 2000));
lroResponse = await this.callApi('onboardUser', onboardRequest);
retryCount++;
}
if (!lroResponse.done) {
throw new Error('Onboarding timeout: Operation did not complete within expected time.');
}
const discoveredProjectId = lroResponse.response?.cloudaicompanionProject?.id || initialProjectId;
console.log(`[Antigravity] Onboarded and discovered Project ID: ${discoveredProjectId}`);
// 获取可用模型
await this.fetchAvailableModels();
return discoveredProjectId;
} catch (error) {
console.error('[Antigravity] Failed to discover Project ID:', error.response?.data || error.message);
console.log('[Antigravity] Falling back to generated Project ID as last resort...');
const fallbackProjectId = generateProjectID();
console.log(`[Antigravity] Generated fallback Project ID: ${fallbackProjectId}`);
// 获取可用模型
await this.fetchAvailableModels();
return fallbackProjectId;
}
}
async fetchAvailableModels() { async fetchAvailableModels() {
console.log('[Antigravity] Fetching available models...'); console.log('[Antigravity] Fetching available models...');
@ -637,7 +710,7 @@ export class AntigravityApiService {
const actualModelName = alias2ModelName(selectedModel); const actualModelName = alias2ModelName(selectedModel);
// 将处理后的请求体转换为 Antigravity 格式 // 将处理后的请求体转换为 Antigravity 格式
const payload = geminiToAntigravity(actualModelName, { request: processedRequestBody }); const payload = geminiToAntigravity(actualModelName, { request: processedRequestBody }, this.projectId);
// 设置模型名称为实际模型名 // 设置模型名称为实际模型名
payload.model = actualModelName; payload.model = actualModelName;
@ -660,7 +733,7 @@ export class AntigravityApiService {
const actualModelName = alias2ModelName(selectedModel); const actualModelName = alias2ModelName(selectedModel);
// 将处理后的请求体转换为 Antigravity 格式 // 将处理后的请求体转换为 Antigravity 格式
const payload = geminiToAntigravity(actualModelName, { request: processedRequestBody }); const payload = geminiToAntigravity(actualModelName, { request: processedRequestBody }, this.projectId);
// 设置模型名称为实际模型名 // 设置模型名称为实际模型名
payload.model = actualModelName; payload.model = actualModelName;