PicoClaw Antigravity 认证与接入全指南:OAuth 2.0 + PKCE 登录、模型管理与自定义 Provider 实现
2026/9/20 10:42:06 网站建设 项目流程

PicoClaw Antigravity 认证与接入全指南:OAuth 2.0 + PKCE 登录、模型管理与自定义 Provider 实现

【免费下载链接】picoclawTiny, Fast, and Deployable anywhere — automate the mundane, unleash your creativity项目地址: https://gitcode.com/gh_mirrors/pi/picoclaw

本指南以仓库文档 ANTIGRAVITY_AUTH.pt-br.md 为主体,结合 pkg/auth、pkg/providers/oauth/antigravity_provider.go 等源码,系统讲解 PicoClaw 中 Google Antigravity(Cloud Code Assist)的认证机制、令牌生命周期、模型与用量接口,以及如何从零扩展一个全新的模型 Provider。读完本文,你将能够独立完成 Antigravity 的 OAuth 登录配置、模型列表查询与排障,并掌握 PicoClaw 插件化 Provider 的完整实现路径。


1. 概述:Antigravity 在 PicoClaw 中的角色

Antigravity(Google Cloud Code Assist)是 Google 支持的 AI 模型服务,通过 Google 云基础设施提供 Claude Opus 4.6、Gemini 系列等模型的访问能力。在 PicoClaw 中,Antigravity 被实现为一个完整的模型 Provider:它以OAuth 2.0 + PKCE完成用户认证,通过cloudcode-pa.googleapis.comv1internal系列接口获取项目信息、模型列表、配额与流式生成能力。

从仓库源码结构看,Antigravity 的实现横跨两个核心包:

  • pkg/auth:OAuth 流程、PKCE 生成、令牌刷新与凭据存储(auth.json);
  • pkg/providers/oauth/antigravity_provider.go:Provider 的Chat流式调用、SSE 解析、模型列表拉取与错误处理。

下文将沿着"认证 → 令牌 → 模型 → 用量 → 接入 → 扩展"的链路逐层展开。


2. 认证流程:OAuth 2.0 with PKCE

2.1 整体时序

Antigravity 使用OAuth 2.0 授权码模式 + PKCE(Proof Key for Code Exchange)保障认证安全,整体流程如下:

┌─────────────┐ ┌─────────────────┐ │ Client │ ───(1) Generate PKCE Pair────────> │ │ │ │ ───(2) Open Auth URL─────────────> │ Google OAuth │ │ │ │ Server │ │ │ <──(3) Redirect with Code───────── │ │ │ │ └─────────────────┘ │ │ ───(4) Exchange Code for Tokens──> │ Token URL │ │ │ │ │ │ │ <──(5) Access + Refresh Tokens──── │ │ └─────────────┘ └─────────────────┘

具体而言,PKCE 用于防止授权码被拦截后重放:客户端先生成随机的code_verifier,将它的 SHA-256 摘要code_challenge附在授权 URL 上;换取令牌时必须提交原始的code_verifier,服务器据此校验发起者身份。由于 Google OAuth 使用机密客户端(confidential client)模式,client_secret也会在令牌交换阶段一并提交。

2.2 步骤一:生成 PKCE 参数

function generatePkce(): { verifier: string; challenge: string } { const verifier = randomBytes(32).toString("hex"); const challenge = createHash("sha256").update(verifier).digest("base64url"); return { verifier, challenge }; }

在 PicoClaw 的 Go 实现中,对应逻辑位于 pkg/auth/pkce.go:它使用crypto/rand生成 64 字节随机数并以 base64url 编码为 verifier,再对 verifier 做 SHA-256 后同样以 base64url 编码为 challenge:

buf := make([]byte, 64) rand.Read(buf) verifier := base64.RawURLEncoding.EncodeToString(buf) hash := sha256.Sum256([]byte(verifier)) challenge := base64.RawURLEncoding.EncodeToString(hash[:])

2.3 步骤二:构造授权 URL

const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; const REDIRECT_URI = "http://localhost:51121/oauth-callback"; function buildAuthUrl(params: { challenge: string; state: string }): string { const url = new URL(AUTH_URL); url.searchParams.set("client_id", CLIENT_ID); url.searchParams.set("response_type", "code"); url.searchParams.set("redirect_uri", REDIRECT_URI); url.searchParams.set("scope", SCOPES.join(" ")); url.searchParams.set("code_challenge", params.challenge); url.searchParams.set("code_challenge_method", "S256"); url.searchParams.set("state", params.state); url.searchParams.set("access_type", "offline"); url.searchParams.set("prompt", "consent"); return url.toString(); }

需要申请的OAuth 作用域(Scopes)

const SCOPES = [ "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/cclog", "https://www.googleapis.com/auth/experimentsandconfigs", ];

仓库中 pkg/auth/oauth.go 的GoogleAntigravityOAuthConfig()定义了完全一致的作用域列表,并固定使用本地回调端口51121。源码还特别针对 Google 追加了两个参数(buildAuthorizeURL):access_type=offline(获取可用于刷新令牌的 refresh_token)与prompt=consent(强制每次授权弹窗,确保返回 refresh token)。同时生成随机的state参数(GenerateState()生成 32 字节 hex 串)用于 CSRF 防护——回调时若state不匹配会直接返回 "state mismatch" 错误。

2.4 步骤三:处理 OAuth 回调

PicoClaw 支持两种回调模式(对应 LoginBrowserWithOptions 的并行等待逻辑):

  • 自动模式(本地开发机):在本地端口 51121 启动 HTTP 回调服务器,浏览器完成授权后 Google 将用户重定向到http://localhost:<port>/auth/callback,回调处理器校验state并从 query 中提取code(参考 oauthCallbackHandler)。若端口被占用,源码会自动尝试其他端口并以实际端口重建 redirect_uri。
  • 手动模式(远程/无图形界面/WSL2):终端打印授权 URL,用户在其他设备浏览器完成登录后,把浏览器地址栏中的完整重定向 URL(或仅code参数)粘贴回终端。源码会尝试解析 URL 并从?code=中提取授权码,粘贴裸 code 也能直接工作。
function shouldUseManualOAuthFlow(isRemote: boolean): boolean { return isRemote || isWSL2Sync(); }

在命令行中,无浏览器环境可通过--no-browser强制走手动模式。此外整个登录等待有5 分钟超时,超时自动失败。

2.5 步骤四:用授权码交换令牌

const TOKEN_URL = "https://oauth2.googleapis.com/token"; async function exchangeCode(params: { code: string; verifier: string; }): Promise<{ access: string; refresh: string; expires: number }> { const response = await fetch(TOKEN_URL, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, code: params.code, grant_type: "authorization_code", redirect_uri: REDIRECT_URI, code_verifier: params.verifier, }), }); const data = await response.json(); return { access: data.access_token, refresh: data.refresh_token, expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000, // 5 min buffer }; }

注意这里的过期时间有意扣减了5 分钟 buffer,用于避免令牌在临界时刻过期导致的竞态。PicoClaw 的 Go 版本ExchangeCodeForTokens位于 pkg/auth/oauth.go,会根据令牌端点域名自动判定 provider 为google-antigravity,并在响应缺少refresh_token时保留旧值。

2.6 步骤五:拉取用户附加信息

用户邮箱:

async function fetchUserEmail(accessToken: string): Promise<string | undefined> { const response = await fetch( "https://www.googleapis.com/oauth2/v1/userinfo?alt=json", { headers: { Authorization: `Bearer ${accessToken}` } } ); const data = await response.json(); return data.email; }

PicoClaw 命令行在 cmd/picoclaw/internal/auth/helpers.go 中通过https://www.googleapis.com/oauth2/v2/userinfo获取邮箱并写入凭据的Email字段;获取失败仅打印警告,不阻断登录。

项目 ID(API 调用必需):

async function fetchProjectId(accessToken: string): Promise<string> { const headers = { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", "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", }), }; const response = await fetch( "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", { method: "POST", headers, body: JSON.stringify({ metadata: { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI", }, }), } ); const data = await response.json(); return data.cloudaicompanionProject || "rising-fact-p41fc"; // Valor padrão de fallback }

该逻辑在 Go 侧对应 FetchAntigravityProjectID,它解析响应 JSON 的cloudaicompanionProject字段。若拉取失败,createAntigravityTokenSource 会回退到与 OpenCode 一致的默认项目 IDrising-fact-p41fc


3. OAuth 实现细节与双流程模式

3.1 客户端凭据

注意:以下客户端凭据以 base64 编码内置于源码中,用于与 pi-ai / OpenCode 生态保持同步:

const decode = (s: string) => Buffer.from(s, "base64").toString(); const CLIENT_ID = decode( "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==" ); const CLIENT_SECRET = decode("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=");

Go 实现见 GoogleAntigravityOAuthConfig,通过decodeBase64解出同样的凭据,并配置Issuer: https://accounts.google.com/o/oauth2/v2TokenURL: https://oauth2.googleapis.com/token、回调端口51121

3.2 两种 OAuth 流程模式

  1. 自动流程(本地带浏览器):自动唤起默认浏览器(OpenBrowser 按 darwin/linux/windows 分别调用openxdg-opencmd /c start),本地回调服务器捕获重定向,首次授权后无需额外交互;
  2. 手动流程(远程 / 无图形界面 / WSL2):打印 URL 供手动复制,用户在外置浏览器完成认证后,将完整重定向 URL 粘贴回终端。
function shouldUseManualOAuthFlow(isRemote: boolean): boolean { return isRemote || isWSL2Sync(); }

两种模式共用同一套"浏览器回调 + 终端粘贴"双通道等待机制(select同时监听回调通道与 stdin),任一通道先完成即胜出,显著提升无头服务器的接入体验。


4. 令牌管理:凭据结构、刷新与持久化

4.1 认证凭据结构

type OAuthCredential = { type: "oauth"; provider: "google-antigravity"; access: string; // 访问令牌 refresh: string; // 刷新令牌 expires: number; // 过期时间戳(epoch 毫秒) email?: string; // 用户邮箱 projectId?: string; // Google Cloud 项目 ID };

Go 侧对应的结构体是 pkg/auth/store.go 的AuthCredential,JSON 字段为access_tokenrefresh_tokenaccount_idexpires_attime.Time)、providerauth_methodemailproject_id

4.2 刷新机制与 5 分钟缓冲

凭据包含 refresh_token,可在 access_token 过期时换取新令牌。过期时间在生成时已扣除 5 分钟缓冲,避免竞态;Go 侧再叠加两道防线(store.go):

func (c *AuthCredential) IsExpired() bool { ... } // 已过期 func (c *AuthCredential) NeedsRefresh() bool { return time.Now().Add(5 * time.Minute).After(c.ExpiresAt) // 剩余不足 5 分钟即视为待刷新 }

实际刷新发生在两个位置:CLI 的auth models命令在查询前主动刷新(helpers.go),以及 Provider 每次 Chat 调用时通过 createAntigravityTokenSource 惰性刷新——NeedsRefresh()为真且存在 refresh_token 时,调用auth.RefreshAccessToken并用新令牌覆盖写入存储。刷新实现见 RefreshAccessToken,会保留刷新响应中缺失的 Email、ProjectID 等字段。

4.3 凭据存储:~/.picoclaw/auth.json

认证配置保存在~/.picoclaw/auth.json(路径由 config.GetHome 推导,见 authFilePath):

{ "credentials": { "google-antigravity": { "access_token": "ya29...", "refresh_token": "1//...", "expires_at": "2026-01-01T00:00:00Z", "provider": "google-antigravity", "auth_method": "oauth", "email": "user@example.com", "project_id": "my-project-id" } } }

存储层(pkg/auth/store.go)具备两项关键健壮性设计:

  • 别名归一化canonicalProviderantigravity归一化为google-antigravityLoadStore还会合并同一 provider 下的重复条目、优先保留未过期且使用规范名的凭据(normalizeStore);
  • 原子写入SaveStore使用 pkg/fileutil 的WriteFileAtomic0600权限写入,并对闪存介质显式 sync,避免掉电损坏凭据文件。

5. 模型列表拉取

5.1 接口调用

const BASE_URL = "https://cloudcode-pa.googleapis.com"; async function fetchAvailableModels( accessToken: string, projectId: string ): Promise<Model[]> { const headers = { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", "User-Agent": "antigravity", "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1", }; const response = await fetch( `${BASE_URL}/v1internal:fetchAvailableModels`, { method: "POST", headers, body: JSON.stringify({ project: projectId }), } ); const data = await response.json(); // 返回带配额信息的模型列表 return Object.entries(data.models).map(([modelId, modelInfo]) => ({ id: modelId, displayName: modelInfo.displayName, quotaInfo: { remainingFraction: modelInfo.quotaInfo?.remainingFraction, resetTime: modelInfo.quotaInfo?.resetTime, isExhausted: modelInfo.quotaInfo?.isExhausted, }, })); }

5.2 响应格式

type FetchAvailableModelsResponse = { models?: Record<string, { displayName?: string; quotaInfo?: { remainingFraction?: number | string; resetTime?: string; // ISO 8601 时间戳 isExhausted?: boolean; }; }>; };

Go 侧实现为 FetchAntigravityModels,请求体为{"project": projectID},将models映射解析为AntigravityModelInfo{ID, DisplayName, IsExhausted}。源码还做了一件贴心的事:兜底补齐——无论接口返回与否,都会确保gemini-3-flashgemini-3-flash-preview出现在列表中,避免默认模型不可用时列表为空。

CLI 侧,picoclaw auth models命令(authModelsCmd)负责在终端以/✗ (quota exhausted)标记每个模型的可用状态,方便用户一眼判断哪些模型配额已耗尽。


6. 用量追踪

6.1 拉取用量数据

export async function fetchAntigravityUsage( token: string, timeoutMs: number ): Promise<ProviderUsageSnapshot> { // 1. 拉取积分与套餐信息 const loadCodeAssistRes = await fetch( `${BASE_URL}/v1internal:loadCodeAssist`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ metadata: { ideType: "ANTIGRAVITY", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI", }, }), } ); // 提取积分信息 const { availablePromptCredits, planInfo, currentTier } = data; // 2. 拉取各模型配额 const modelsRes = await fetch( `${BASE_URL}/v1internal:fetchAvailableModels`, { method: "POST", headers: { Authorization: `Bearer ${token}` }, body: JSON.stringify({ project: projectId }), } ); // 构造用量窗口 return { provider: "google-antigravity", displayName: "Google Antigravity", windows: [ { label: "Credits", usedPercent: calculateUsedPercent(available, monthly) }, // 各模型独立配额... ], plan: currentTier?.name || planType, }; }

6.2 用量响应结构

type ProviderUsageSnapshot = { provider: "google-antigravity"; displayName: string; windows: UsageWindow[]; plan?: string; error?: string; }; type UsageWindow = { label: string; // "Credits" 或模型 ID usedPercent: number; // 0-100 resetAt?: number; // 配额重置时间戳 };

用量数据汇总自两个端点:loadCodeAssist提供整体积分(availablePromptCredits)、套餐(planInfo)与当前档位(currentTier),fetchAvailableModels提供各模型独立配额。二者合并后可同时呈现"总积分消耗"与"单模型配额"两级视角。


7. Provider 插件结构与扩展接口

7.1 插件定义

const antigravityPlugin = { id: "google-antigravity-auth", name: "Google Antigravity Auth", description: "OAuth flow for Google Antigravity (Cloud Code Assist)", configSchema: emptyPluginConfigSchema(), register(api: PicoClawPluginApi) { api.registerProvider({ id: "google-antigravity", label: "Google Antigravity", docsPath: "/providers/models", aliases: ["antigravity"], auth: [ { id: "oauth", label: "Google OAuth", hint: "PKCE + localhost callback", kind: "oauth", run: async (ctx: ProviderAuthContext) => { // 在此实现 OAuth }, }, ], }); }, };

7.2 ProviderAuthContext

type ProviderAuthContext = { config: PicoClawConfig; agentDir?: string; workspaceDir?: string; prompter: WizardPrompter; // UI 提示/通知 runtime: RuntimeEnv; // 日志等 isRemote: boolean; // 是否远程执行 openUrl: (url: string) => Promise<void>; // 浏览器打开器 oauth: { createVpsAwareHandlers: Function; }; };

7.3 ProviderAuthResult

type ProviderAuthResult = { profiles: Array<{ profileId: string; credential: AuthProfileCredential; }>; configPatch?: Partial<PicoClawConfig>; defaultModel?: string; notes?: string[]; };

在 Go 仓库中,上述 TS 插件的对应物是 pkg/providers/factory_provider.go 中的协议分发分支:

case "antigravity": return finalizeProviderFromConfig(NewAntigravityProvider(), modelID, cfg)

NewAntigravityProvider(antigravity_provider.go)不依赖 API Key,而是注入一个tokenSource闭包:每次调用从auth.json读取凭据、按需刷新、必要时回退默认 project ID,再返回(accessToken, projectID)。底层 HTTP 客户端超时设为 120 秒,适配长流式响应。


8. 集成要求

8.1 环境 / 依赖

  • Go ≥ 1.25;
  • PicoClaw 代码库(pkg/providers/pkg/auth/);
  • 标准库cryptonet/http包(PKCE、签名、HTTP 调用所需)。

8.2 必需的 API 请求头

const REQUIRED_HEADERS = { "Authorization": `Bearer ${accessToken}`, "Content-Type": "application/json", "User-Agent": "antigravity", // 或 "google-api-nodejs-client/9.15.1" "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1", }; // 调用 loadCodeAssist 时还需附加: const CLIENT_METADATA = { ideType: "ANTIGRAVITY", // 或 "IDE_UNSPECIFIED" platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI", };

Go 侧实际发送的请求头(antigravity_provider.go)与此完全对齐,并额外增加了Accept: text/event-stream(SSE 必需)以及带版本号的User-Agent: antigravity/1.15.8 linux/amd64

8.3 模型 Schema 清理(Google/Gemini 兼容)

Antigravity 底层走 Gemini 兼容模型,因此工具 Schema 必须先做清洗,剔除 Gemini 不支持的 JSON Schema 关键字:

const GOOGLE_SCHEMA_UNSUPPORTED_KEYWORDS = new Set([ "patternProperties", "additionalProperties", "$schema", "$id", "$ref", "$defs", "definitions", "examples", "minLength", "maxLength", "minimum", "maximum", "multipleOf", "pattern", "format", "minItems", "maxItems", "uniqueItems", "minProperties", "maxProperties", ]); // 发送前清理 schema function cleanToolSchemaForGemini(schema: Record<string, unknown>): unknown { // 移除不支持的关键字 // 确保顶层有 type: "object" // 展平 anyOf/oneOf 联合类型 }

PicoClaw 的 Go 实现位于 pkg/providers/common/google_schema.go:SanitizeSchemaForGoogle将 JSON Schema 收敛到 Google/Gemini 函数声明接受的保守子集——解析本地$ref、折叠anyOf/oneOf/allOf组合关键字、剥离高级关键字,并递归限制最大深度 64(maxGeminiSchemaDepth);SanitizeSchemaForGemini作为兼容别名保留。顶层只要含properties就强制补type: "object",确保函数声明格式合法。该清洗可通过配置项ToolSchemaTransform(取值为""关闭 /"simple"开启)启用,见 pkg/providers/tool_schema_transform.go 与 factory_provider.go,并有对应的 tool_schema_transform_test.go 单测覆盖。

8.4 思考块(Thought Blocks)处理(Claude 模型)

经 Antigravity 访问 Claude 模型时,思考块需要特殊处理:

const ANTIGRAVITY_SIGNATURE_RE = /^[A-Za-z0-9+/]+={0,2}$/; export function sanitizeAntigravityThinkingBlocks( messages: AgentMessage[] ): AgentMessage[] { // 校验思考签名 // 规范化签名字段 // 丢弃未签名的思考块 }

从源码结构看,antigravity_provider.go 为消息 part 定义了ThoughtSignatureThoughtSignatureSnake(snake_case 兼容字段)两个签名槽位,发送时两者同时写入;解析响应时用 extractPartThoughtSignature 优先取驼峰、回退蛇形字段。带thought: true标记的 part 会被单独归入ReasoningContent,不混入正文;工具调用的签名则透传给后续轮次,保证多轮工具调用链的签名一致性。


9. API 端点汇总

9.1 认证端点

端点方法用途
https://accounts.google.com/o/oauth2/v2/authGETOAuth 授权
https://oauth2.googleapis.com/tokenPOST令牌交换/刷新
https://www.googleapis.com/oauth2/v1/userinfoGET用户信息(邮箱)

9.2 Cloud Code Assist 端点

端点方法用途
https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssistPOST加载项目信息、积分、套餐
https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModelsPOST列出可用模型及配额
https://cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=ssePOST聊天流式生成

9.3 Chat API 请求格式

v1internal:streamGenerateContent期望一个封装标准 Gemini 请求的 envelope:

{ "project": "your-project-id", "model": "model-id", "request": { "contents": [...], "systemInstruction": {...}, "generationConfig": {...}, "tools": [...] }, "requestType": "agent", "userAgent": "antigravity", "requestId": "agent-timestamp-random" }

Go 侧的 envelope 构造见 antigravity_provider.go:requestId形如agent-<UnixMilli>-<9位随机串>requestType固定为"agent";内部request由 buildRequest 将系统消息映射为systemInstruction、用户/助手消息映射为contents(角色user/model)、工具调用映射为functionCall/functionResponsepart,max_tokenstemperature则透传至generationConfig

9.4 SSE 响应格式

每条 SSE 消息(data: {...})都封装在response字段中:

{ "response": { "candidates": [...], "usageMetadata": {...}, "modelVersion": "...", "responseId": "..." }, "traceId": "...", "metadata": {} }

Go 的 SSE 解析器 parseSSEResponse 逐行扫描data:前缀(遇[DONE]终止),解析response.candidates[].content.parts:普通文本拼入Contentthought: true拼入ReasoningContentfunctionCall转为ToolCall(并携带签名),usageMetadata折算为 token 用量。finishReason会被归一化:MAX_TOKENSlength,有工具调用 →tool_calls,其余 →stop若最终文本为空且无工具调用,则判定为受限模型的空响应错误——这正是下文错误处理一节要展开的场景。


10. 配置

10.1 config.json 配置

{ "model_list": [ { "model_name": "gemini-flash", "model": "antigravity/gemini-3-flash", "auth_method": "oauth" } ], "agents": { "defaults": { "model_name": "gemini-flash" } } }

配置仓库中的默认模型入口见 pkg/config/defaults.go,model字段的 provider 前缀(antigravity/google-antigravity/)会被 factory_provider.go 路由到NewAntigravityProvider()。实际调用时 Chat 还会再做一次前缀剥离与默认模型兜底:模型为空或为antigravity/google-antigravity时,回落到gemini-3-flash

10.2 认证凭据存储位置

认证凭据统一保存在~/.picoclaw/auth.json,结构如第 4.3 节所示。该文件与config.json分离,令牌绝不写入模型配置,降低误提交密钥的风险。


11. 在 PicoClaw 中创建全新 Provider

PicoClaw 的 Provider 以 Go 包形式位于pkg/providers/。接入新 Provider 的完整步骤:

11.1 创建 Provider 文件

pkg/providers/ └── your_provider.go

11.2 实现 Provider 接口

实现定义于pkg/providers/types.goProvider接口:

package providers type YourProvider struct { apiKey string apiBase string } func NewYourProvider(apiKey, apiBase, proxy string) *YourProvider { if apiBase == "" { apiBase = "https://api.your-provider.com/v1" } return &YourProvider{apiKey: apiKey, apiBase: apiBase} } func (p *YourProvider) Chat(ctx context.Context, messages []Message, tools []Tool, cb StreamCallback) error { // 实现带流式的对话补全 }

11.3 在 Factory 中注册

pkg/providers/factory.go的协议 switch 中注册(实际分发逻辑在 factory_provider.go,Antigravity 分支见第 7.3 节):

case "your-provider": return NewYourProvider(sel.apiKey, sel.apiBase, sel.proxy), nil

11.4 添加默认配置(可选)

pkg/config/defaults.go增加默认条目:

{ ModelName: "your-model", Model: "your-provider/model-name", APIKey: "", },

11.5 添加认证支持(可选)

若 Provider 需要 OAuth 或特殊认证,在cmd/picoclaw/internal/auth/helpers.go增加分支(Antigravity 分支见 authLoginGoogleAntigravity,登录成功后会拉取邮箱与项目 ID、把默认模型切换为gemini-flash并自动改写config.jsonmodel_list):

case "your-provider": authLoginYourProvider()

11.6 通过 config.json 配置

{ "model_list": [ { "model_name": "your-model", "model": "your-provider/model-name", "api_keys": ["your-api-key"], "api_base": "https://api.your-provider.com/v1" } ] }

12. 验证你的实现

12.1 CLI 命令

# 使用某 Provider 登录认证 picoclaw auth login --provider your-provider # 列出模型(针对 Antigravity) picoclaw auth models # 启动网关 picoclaw gateway # 使用指定模型运行 Agent picoclaw agent -m "Hello" --model your-model

Antigravity 相关命令的完整集合:

picoclaw auth login --provider google-antigravity # 或别名 antigravity picoclaw auth login --provider google-antigravity --no-browser # 无头服务器手动流程 picoclaw auth models # 列出带配额状态的模型 picoclaw auth status # 查看各 Provider 登录状态/过期时间/用量 picoclaw auth logout --provider google-antigravity # 登出并清理凭据

其中--no-browserauth models均对应源码 helpers.go 中的实现;auth status会逐一显示每个凭据的MethodStatus(active / needs refresh / expired)、Email、Project 与过期时间。

12.2 测试用环境变量

# 覆盖默认模型 export PICOCLAW_AGENTS_DEFAULTS_MODEL=your-model # 覆盖 Provider 配置 export PICOCLAW_MODEL_LIST='[{"model_name":"your-model","model":"your-provider/model-name","api_keys":["..."]}]'

13. 常见错误处理

13.1 限流(HTTP 429)

当项目/模型配额耗尽时,Antigravity 返回 429 错误,错误响应的details中通常携带quotaResetDelay

{ "error": { "code": 429, "message": "You have exhausted your capacity on this model. Your quota will reset after 4h30m28s.", "status": "RESOURCE_EXHAUSTED", "details": [ { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "metadata": { "quotaResetDelay": "4h30m28.060903746s" } } ] } }

Go 侧的 parseAntigravityError 会遍历details提取quotaResetDelay,拼入错误信息返回,方便用户在日志中直接看到"还需等待多久",而非只有抽象的 429。

13.2 空响应(受限模型)

部分模型会出现在可用列表里,但实际返回 200 OK + 空 SSE 流——通常是该模型处于预览/受限状态,当前项目无权限使用。

处理方式:将空响应视为错误,提示用户该模型可能对其项目受限或无效。Go 实现见 Chat:Content == ""且无工具调用时报错antigravity: model returned an empty response (this model might be invalid or restricted)


14. 排障清单

现象处理方式
"Token expired"(令牌过期)重新登录刷新令牌:picoclaw auth login --provider antigravity
"Gemini for Google Cloud is not enabled"在 Google Cloud Console 中启用对应 API
"Project not found"(项目不存在)确认 Google Cloud 项目已启用所需 API;确认登录时正确获取了项目 ID
模型不出现在列表确认 OAuth 登录成功;检查凭据存储~/.picoclaw/auth.json;重新执行picoclaw auth login --provider antigravity

补充两条源码级排查建议:凭据若长期未刷新,可先执行picoclaw auth status查看NeedsRefresh状态;若project_id为空,auth models会明确报错"no project id stored. Try logging in again",此时重新登录即可自动补齐。


15. 注意事项总结

  1. Google Cloud 项目:Antigravity 要求在你的 Google Cloud 项目中启用Gemini for Google Cloud
  2. 配额:使用 Google Cloud 项目配额(无单独计费);
  3. 模型访问:可用模型取决于 Google Cloud 项目的配置;
  4. 思考块:经 Antigravity 访问 Claude 模型时,思考块需要携带签名并做专门处理;
  5. Schema 清洗:工具 Schema 必须剔除 Gemini 不支持的 JSON Schema 关键字后再发送。

16. 参考资源

源码文件(以仓库当前实现为准):

  • pkg/providers/oauth/antigravity_provider.go — Antigravity Provider 实现(原文档引用的pkg/providers/antigravity_provider.go在当前仓库中已移入oauth子目录)
  • pkg/auth/oauth.go — OAuth 流程实现(授权 URL、回调、令牌交换与刷新)
  • pkg/auth/pkce.go — PKCE 参数生成
  • pkg/auth/store.go — 认证凭据存储(~/.picoclaw/auth.json
  • pkg/providers/factory_provider.go — Provider Factory 与协议路由
  • pkg/providers/types.go — Provider 接口定义
  • pkg/providers/common/google_schema.go — Google/Gemini Schema 清洗
  • cmd/picoclaw/internal/auth/helpers.go — CLI 认证命令(login/logout/status/models)

文档:

  • docs/guides/ANTIGRAVITY_USAGE.md — Antigravity 使用指南(原文档引用的docs/ANTIGRAVITY_USAGE.md在当前仓库中位于docs/guides/下,另有.zh.md中文版)
  • docs/migration/model-list-migration.md — 模型列表迁移指南
  • docs/security/ANTIGRAVITY_AUTH.zh.md — 本文的简体中文姊妹篇(若存在)

【免费下载链接】picoclawTiny, Fast, and Deployable anywhere — automate the mundane, unleash your creativity项目地址: https://gitcode.com/gh_mirrors/pi/picoclaw

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询