mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
Fixed: #421
feat(antigravity): implement project ID retrieval and integration in payload processing
This commit is contained in:
@@ -509,7 +509,14 @@ func (e *AntigravityExecutor) buildRequest(ctx context.Context, auth *cliproxyau
|
|||||||
requestURL.WriteString(url.QueryEscape(alt))
|
requestURL.WriteString(url.QueryEscape(alt))
|
||||||
}
|
}
|
||||||
|
|
||||||
payload = geminiToAntigravity(modelName, payload)
|
// Extract project_id from auth metadata if available
|
||||||
|
projectID := ""
|
||||||
|
if auth != nil && auth.Metadata != nil {
|
||||||
|
if pid, ok := auth.Metadata["project_id"].(string); ok {
|
||||||
|
projectID = strings.TrimSpace(pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload = geminiToAntigravity(modelName, payload, projectID)
|
||||||
payload, _ = sjson.SetBytes(payload, "model", alias2ModelName(modelName))
|
payload, _ = sjson.SetBytes(payload, "model", alias2ModelName(modelName))
|
||||||
|
|
||||||
if strings.Contains(modelName, "claude") {
|
if strings.Contains(modelName, "claude") {
|
||||||
@@ -676,9 +683,9 @@ func antigravityBaseURLFallbackOrder(auth *cliproxyauth.Auth) []string {
|
|||||||
return []string{base}
|
return []string{base}
|
||||||
}
|
}
|
||||||
return []string{
|
return []string{
|
||||||
|
antigravityBaseURLProd,
|
||||||
antigravityBaseURLDaily,
|
antigravityBaseURLDaily,
|
||||||
antigravityBaseURLAutopush,
|
antigravityBaseURLAutopush,
|
||||||
// antigravityBaseURLProd,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,10 +709,16 @@ func resolveCustomAntigravityBaseURL(auth *cliproxyauth.Auth) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func geminiToAntigravity(modelName string, payload []byte) []byte {
|
func geminiToAntigravity(modelName string, payload []byte, projectID string) []byte {
|
||||||
template, _ := sjson.Set(string(payload), "model", modelName)
|
template, _ := sjson.Set(string(payload), "model", modelName)
|
||||||
template, _ = sjson.Set(template, "userAgent", "antigravity")
|
template, _ = sjson.Set(template, "userAgent", "antigravity")
|
||||||
|
|
||||||
|
// Use real project ID from auth if available, otherwise generate random (legacy fallback)
|
||||||
|
if projectID != "" {
|
||||||
|
template, _ = sjson.Set(template, "project", projectID)
|
||||||
|
} else {
|
||||||
template, _ = sjson.Set(template, "project", generateProjectID())
|
template, _ = sjson.Set(template, "project", generateProjectID())
|
||||||
|
}
|
||||||
template, _ = sjson.Set(template, "requestId", generateRequestID())
|
template, _ = sjson.Set(template, "requestId", generateRequestID())
|
||||||
template, _ = sjson.Set(template, "request.sessionId", generateSessionID())
|
template, _ = sjson.Set(template, "request.sessionId", generateSessionID())
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -127,6 +128,18 @@ func (AntigravityAuthenticator) Login(ctx context.Context, cfg *config.Config, o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch project ID via loadCodeAssist (same approach as Gemini CLI)
|
||||||
|
projectID := ""
|
||||||
|
if tokenResp.AccessToken != "" {
|
||||||
|
fetchedProjectID, errProject := fetchAntigravityProjectID(ctx, tokenResp.AccessToken, httpClient)
|
||||||
|
if errProject != nil {
|
||||||
|
log.Warnf("antigravity: failed to fetch project ID: %v", errProject)
|
||||||
|
} else {
|
||||||
|
projectID = fetchedProjectID
|
||||||
|
log.Infof("antigravity: obtained project ID %s", projectID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
metadata := map[string]any{
|
metadata := map[string]any{
|
||||||
"type": "antigravity",
|
"type": "antigravity",
|
||||||
@@ -139,6 +152,9 @@ func (AntigravityAuthenticator) Login(ctx context.Context, cfg *config.Config, o
|
|||||||
if email != "" {
|
if email != "" {
|
||||||
metadata["email"] = email
|
metadata["email"] = email
|
||||||
}
|
}
|
||||||
|
if projectID != "" {
|
||||||
|
metadata["project_id"] = projectID
|
||||||
|
}
|
||||||
|
|
||||||
fileName := sanitizeAntigravityFileName(email)
|
fileName := sanitizeAntigravityFileName(email)
|
||||||
label := email
|
label := email
|
||||||
@@ -147,6 +163,9 @@ func (AntigravityAuthenticator) Login(ctx context.Context, cfg *config.Config, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Antigravity authentication successful")
|
fmt.Println("Antigravity authentication successful")
|
||||||
|
if projectID != "" {
|
||||||
|
fmt.Printf("Using GCP project: %s\n", projectID)
|
||||||
|
}
|
||||||
return &coreauth.Auth{
|
return &coreauth.Auth{
|
||||||
ID: fileName,
|
ID: fileName,
|
||||||
Provider: "antigravity",
|
Provider: "antigravity",
|
||||||
@@ -291,3 +310,84 @@ func sanitizeAntigravityFileName(email string) string {
|
|||||||
replacer := strings.NewReplacer("@", "_", ".", "_")
|
replacer := strings.NewReplacer("@", "_", ".", "_")
|
||||||
return fmt.Sprintf("antigravity-%s.json", replacer.Replace(email))
|
return fmt.Sprintf("antigravity-%s.json", replacer.Replace(email))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Antigravity API constants for project discovery
|
||||||
|
const (
|
||||||
|
antigravityAPIEndpoint = "https://cloudcode-pa.googleapis.com"
|
||||||
|
antigravityAPIVersion = "v1internal"
|
||||||
|
antigravityAPIUserAgent = "google-api-nodejs-client/9.15.1"
|
||||||
|
antigravityAPIClient = "google-cloud-sdk vscode_cloudshelleditor/0.1"
|
||||||
|
antigravityClientMetadata = `{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}`
|
||||||
|
)
|
||||||
|
|
||||||
|
// fetchAntigravityProjectID retrieves the project ID for the authenticated user via loadCodeAssist.
|
||||||
|
// This uses the same approach as Gemini CLI to get the cloudaicompanionProject.
|
||||||
|
func fetchAntigravityProjectID(ctx context.Context, accessToken string, httpClient *http.Client) (string, error) {
|
||||||
|
// Call loadCodeAssist to get the project
|
||||||
|
loadReqBody := map[string]any{
|
||||||
|
"metadata": map[string]string{
|
||||||
|
"ideType": "IDE_UNSPECIFIED",
|
||||||
|
"platform": "PLATFORM_UNSPECIFIED",
|
||||||
|
"pluginType": "GEMINI",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rawBody, errMarshal := json.Marshal(loadReqBody)
|
||||||
|
if errMarshal != nil {
|
||||||
|
return "", fmt.Errorf("marshal request body: %w", errMarshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointURL := fmt.Sprintf("%s/%s:loadCodeAssist", antigravityAPIEndpoint, antigravityAPIVersion)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpointURL, strings.NewReader(string(rawBody)))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("create request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", antigravityAPIUserAgent)
|
||||||
|
req.Header.Set("X-Goog-Api-Client", antigravityAPIClient)
|
||||||
|
req.Header.Set("Client-Metadata", antigravityClientMetadata)
|
||||||
|
|
||||||
|
resp, errDo := httpClient.Do(req)
|
||||||
|
if errDo != nil {
|
||||||
|
return "", fmt.Errorf("execute request: %w", errDo)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if errClose := resp.Body.Close(); errClose != nil {
|
||||||
|
log.Errorf("antigravity loadCodeAssist: close body error: %v", errClose)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
bodyBytes, errRead := io.ReadAll(resp.Body)
|
||||||
|
if errRead != nil {
|
||||||
|
return "", fmt.Errorf("read response: %w", errRead)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
||||||
|
return "", fmt.Errorf("request failed with status %d: %s", resp.StatusCode, strings.TrimSpace(string(bodyBytes)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var loadResp map[string]any
|
||||||
|
if errDecode := json.Unmarshal(bodyBytes, &loadResp); errDecode != nil {
|
||||||
|
return "", fmt.Errorf("decode response: %w", errDecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract projectID from response
|
||||||
|
projectID := ""
|
||||||
|
if id, ok := loadResp["cloudaicompanionProject"].(string); ok {
|
||||||
|
projectID = strings.TrimSpace(id)
|
||||||
|
}
|
||||||
|
if projectID == "" {
|
||||||
|
if projectMap, ok := loadResp["cloudaicompanionProject"].(map[string]any); ok {
|
||||||
|
if id, okID := projectMap["id"].(string); okID {
|
||||||
|
projectID = strings.TrimSpace(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if projectID == "" {
|
||||||
|
return "", fmt.Errorf("no cloudaicompanionProject in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectID, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user