fix(coding-agent): fix Google Cloud project discovery

Use correct API response format (cloudaicompanionProject) and proper
onboarding flow with tierId and metadata. Add retry logic for project
provisioning which may take time.
This commit is contained in:
Mario Zechner 2025-12-20 10:29:09 +01:00
parent b6fe07b618
commit 1294239a7a

View file

@ -90,50 +90,108 @@ function startCallbackServer(): Promise<{ server: Server; getCode: () => Promise
}); });
} }
interface LoadCodeAssistPayload {
cloudaicompanionProject?: string;
currentTier?: { id?: string };
allowedTiers?: Array<{ id?: string; isDefault?: boolean }>;
}
interface OnboardUserPayload {
done?: boolean;
response?: {
cloudaicompanionProject?: { id?: string };
};
}
/**
* Wait helper for onboarding retries
*/
function wait(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Get default tier ID from allowed tiers
*/
function getDefaultTierId(allowedTiers?: Array<{ id?: string; isDefault?: boolean }>): string | undefined {
if (!allowedTiers || allowedTiers.length === 0) return undefined;
const defaultTier = allowedTiers.find((t) => t.isDefault);
return defaultTier?.id ?? allowedTiers[0]?.id;
}
/** /**
* Discover or provision a Google Cloud project for the user * Discover or provision a Google Cloud project for the user
*/ */
async function discoverProject(accessToken: string): Promise<string> { async function discoverProject(accessToken: string, onProgress?: (message: string) => void): Promise<string> {
// Try to load existing projects via loadCodeAssist const headers = {
const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, { Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "gl-node/22.17.0",
};
// Try to load existing project via loadCodeAssist
onProgress?.("Checking for existing Cloud Code Assist project...");
const loadResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
method: "POST", method: "POST",
headers: { headers,
Authorization: `Bearer ${accessToken}`, body: JSON.stringify({
"Content-Type": "application/json", metadata: {
"User-Agent": "google-api-nodejs-client/9.15.1", ideType: "IDE_UNSPECIFIED",
"X-Goog-Api-Client": "gl-node/22.17.0", platform: "PLATFORM_UNSPECIFIED",
}, pluginType: "GEMINI",
body: JSON.stringify({}), },
}),
}); });
if (response.ok) { if (loadResponse.ok) {
const data = (await response.json()) as { projects?: Array<{ projectId: string }> }; const data = (await loadResponse.json()) as LoadCodeAssistPayload;
if (data.projects && data.projects.length > 0) {
return data.projects[0].projectId; // If we have an existing project, use it
if (data.cloudaicompanionProject) {
return data.cloudaicompanionProject;
} }
}
// Try to onboard the user if no projects found // Otherwise, try to onboard with the FREE tier
const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, { const tierId = getDefaultTierId(data.allowedTiers) ?? "FREE";
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "gl-node/22.17.0",
},
body: JSON.stringify({}),
});
if (onboardResponse.ok) { onProgress?.("Provisioning Cloud Code Assist project (this may take a moment)...");
const data = (await onboardResponse.json()) as { projectId?: string };
if (data.projectId) { // Onboard with retries (the API may take time to provision)
return data.projectId; for (let attempt = 0; attempt < 10; attempt++) {
const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
method: "POST",
headers,
body: JSON.stringify({
tierId,
metadata: {
ideType: "IDE_UNSPECIFIED",
platform: "PLATFORM_UNSPECIFIED",
pluginType: "GEMINI",
},
}),
});
if (onboardResponse.ok) {
const onboardData = (await onboardResponse.json()) as OnboardUserPayload;
const projectId = onboardData.response?.cloudaicompanionProject?.id;
if (onboardData.done && projectId) {
return projectId;
}
}
// Wait before retrying
if (attempt < 9) {
onProgress?.(`Waiting for project provisioning (attempt ${attempt + 2}/10)...`);
await wait(3000);
}
} }
} }
throw new Error( throw new Error(
"Could not discover or provision a Google Cloud project. Please ensure you have access to Google Cloud Code Assist.", "Could not discover or provision a Google Cloud project. " +
"Please ensure you have access to Google Cloud Code Assist (Gemini CLI).",
); );
} }
@ -239,8 +297,7 @@ export async function loginGoogleCloud(
const email = await getUserEmail(tokenData.access_token); const email = await getUserEmail(tokenData.access_token);
// Discover project // Discover project
onProgress?.("Discovering Google Cloud project..."); const projectId = await discoverProject(tokenData.access_token, onProgress);
const projectId = await discoverProject(tokenData.access_token);
// Calculate expiry time (current time + expires_in seconds - 5 min buffer) // Calculate expiry time (current time + expires_in seconds - 5 min buffer)
const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000; const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;