fix: improve loadCodeAssist for Google One AI Pro accounts

- Add separate LOAD_CODE_ASSIST_ENDPOINTS (prod first) and
  LOAD_CODE_ASSIST_HEADERS (google-api-nodejs-client User-Agent)
- Add duetProject to metadata for project discovery
- Silent fallback when API returns success but no project
  (matches opencode-antigravity-auth behavior)
- Only warn when all endpoints fail with actual errors

Fixes #114, addresses discussion #113

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Badri Narayanan S
2026-01-13 18:11:45 +05:30
parent 99a06632ea
commit 70fd1baaa8
3 changed files with 57 additions and 13 deletions

View File

@@ -7,8 +7,8 @@
import {
ANTIGRAVITY_DB_PATH,
TOKEN_REFRESH_INTERVAL_MS,
ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS,
LOAD_CODE_ASSIST_ENDPOINTS,
LOAD_CODE_ASSIST_HEADERS,
DEFAULT_PROJECT_ID
} from '../constants.js';
import { refreshAccessToken } from '../auth/oauth.js';
@@ -113,31 +113,39 @@ export async function getProjectForAccount(account, token, projectCache) {
* @returns {Promise<string>} Project ID
*/
export async function discoverProject(token) {
for (const endpoint of ANTIGRAVITY_ENDPOINT_FALLBACKS) {
let lastError = null;
let gotSuccessfulResponse = false;
for (const endpoint of LOAD_CODE_ASSIST_ENDPOINTS) {
try {
const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...ANTIGRAVITY_HEADERS
...LOAD_CODE_ASSIST_HEADERS
},
body: JSON.stringify({
metadata: {
ideType: 'IDE_UNSPECIFIED',
platform: 'PLATFORM_UNSPECIFIED',
pluginType: 'GEMINI'
pluginType: 'GEMINI',
duetProject: DEFAULT_PROJECT_ID
}
})
});
if (!response.ok) {
const errorText = await response.text();
logger.warn(`[AccountManager] Project discovery failed at ${endpoint}: ${response.status} - ${errorText}`);
lastError = `${response.status} - ${errorText}`;
logger.debug(`[AccountManager] loadCodeAssist failed at ${endpoint}: ${lastError}`);
continue;
}
const data = await response.json();
gotSuccessfulResponse = true;
logger.debug(`[AccountManager] loadCodeAssist response from ${endpoint}:`, JSON.stringify(data));
if (typeof data.cloudaicompanionProject === 'string') {
logger.success(`[AccountManager] Discovered project: ${data.cloudaicompanionProject}`);
@@ -147,13 +155,21 @@ export async function discoverProject(token) {
logger.success(`[AccountManager] Discovered project: ${data.cloudaicompanionProject.id}`);
return data.cloudaicompanionProject.id;
}
// API returned success but no project - this is normal for Google One AI Pro accounts
// Silently fall back to default project (matches opencode-antigravity-auth behavior)
logger.debug(`[AccountManager] No project in response, using default: ${DEFAULT_PROJECT_ID}`);
return DEFAULT_PROJECT_ID;
} catch (error) {
logger.warn(`[AccountManager] Project discovery failed at ${endpoint}:`, error.message);
lastError = error.message;
logger.debug(`[AccountManager] loadCodeAssist error at ${endpoint}:`, error.message);
}
}
logger.warn(`[AccountManager] Project discovery failed for all endpoints. Using default project: ${DEFAULT_PROJECT_ID}`);
logger.warn(`[AccountManager] If you see 404 errors, your account may not have Gemini Code Assist enabled.`);
// Only warn if all endpoints failed with errors (not just missing project)
if (!gotSuccessfulResponse) {
logger.warn(`[AccountManager] loadCodeAssist failed for all endpoints: ${lastError}`);
}
return DEFAULT_PROJECT_ID;
}

View File

@@ -4,7 +4,13 @@
* Handles model listing and quota retrieval from the Cloud Code API.
*/
import { ANTIGRAVITY_ENDPOINT_FALLBACKS, ANTIGRAVITY_HEADERS, getModelFamily } from '../constants.js';
import {
ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS,
LOAD_CODE_ASSIST_ENDPOINTS,
LOAD_CODE_ASSIST_HEADERS,
getModelFamily
} from '../constants.js';
import { logger } from '../utils/logger.js';
/**
@@ -122,10 +128,10 @@ export async function getSubscriptionTier(token) {
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
...ANTIGRAVITY_HEADERS
...LOAD_CODE_ASSIST_HEADERS
};
for (const endpoint of ANTIGRAVITY_ENDPOINT_FALLBACKS) {
for (const endpoint of LOAD_CODE_ASSIST_ENDPOINTS) {
try {
const url = `${endpoint}/v1internal:loadCodeAssist`;
const response = await fetch(url, {
@@ -135,7 +141,8 @@ export async function getSubscriptionTier(token) {
metadata: {
ideType: 'IDE_UNSPECIFIED',
platform: 'PLATFORM_UNSPECIFIED',
pluginType: 'GEMINI'
pluginType: 'GEMINI',
duetProject: 'rising-fact-p41fc'
}
})
});

View File

@@ -57,6 +57,25 @@ export const ANTIGRAVITY_HEADERS = {
})
};
// Endpoint order for loadCodeAssist (prod first)
// loadCodeAssist works better on prod for fresh/unprovisioned accounts
export const LOAD_CODE_ASSIST_ENDPOINTS = [
ANTIGRAVITY_ENDPOINT_PROD,
ANTIGRAVITY_ENDPOINT_DAILY
];
// Hybrid headers specifically for loadCodeAssist
// Uses google-api-nodejs-client User-Agent (required for project discovery on some accounts)
export const LOAD_CODE_ASSIST_HEADERS = {
'User-Agent': 'google-api-nodejs-client/9.15.1',
'X-Goog-Api-Client': 'google-cloud-sdk vscode_cloudshelleditor/0.1',
'Client-Metadata': JSON.stringify({
ideType: 'IDE_UNSPECIFIED',
platform: 'PLATFORM_UNSPECIFIED',
pluginType: 'GEMINI'
})
};
// Default project ID if none can be discovered
export const DEFAULT_PROJECT_ID = 'rising-fact-p41fc';
@@ -171,6 +190,8 @@ export const MODEL_FALLBACK_MAP = {
export default {
ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_HEADERS,
LOAD_CODE_ASSIST_ENDPOINTS,
LOAD_CODE_ASSIST_HEADERS,
DEFAULT_PROJECT_ID,
TOKEN_REFRESH_INTERVAL_MS,
REQUEST_BODY_LIMIT,