Files
rootandClaude Opus 5.5 003815591c feat(agent): 运行时从 pi-agent-core 0.99.2 迁到 pi-durable 1.1.0 + pi-ai 1.1.0
- PiAgentRuntime 改用 pi-durable Harness:每个 run 一个 JSONL 存储目录 sessionDir/<runId>/ + root 对话;
  系统提示 / 能力工具 / 白名单与预算钩子装在每次运行自己的扩展里;watchEvents 翻译成 AgentRunEvent
- 挂起(工具内 close)立即封口,调用保持未完成,重开后 resume 重放;replayGate 用调用级 memo
- 旧格式 <runId>.jsonl 打开时抛 LegacySessionError;skills 加载与 <skill> 块格式内置(源自 pi-agent-core,MIT)
- tool_call_end 新增可选 durationMs;用量取 pi.usage 合计(整段会话总量)
- Azure provider 改名 azure,迁移 0093 改存量;移除 pi-agent-core,锁 pi-durable/pi-ai/chord 1.1.0
- 文档:ADR-003、pi-providers、runbook、agent README、CHANGELOG

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-08 13:42:36 +08:00

23 KiB
Raw Permalink Blame History

Pi 模型上游与订阅登录(pi-ai / pi-coding-agent 0.87.1)

2026-10-08 升级补充:Agent 运行时换成 @earendil-works/pi-durable 1.1.0 + pi-ai 1.1.0(+ @earendil-works/chord 1.1.0),不再依赖 pi-agent-core(1.0 起它只剩 Agent 与循环,harness 移到 pi-durable)。provider 目录仍是 42 个内置;pi-ai 1.0.3 把 azure-openai-responses 改名为 azure(api id 与 AZURE_OPENAI_* 配置名不变),合同枚举随之改名,迁移 0093 改已存的模型服务。登录流程、能力表与设置页映射不变。运行时的变化见 ADR-003「2026-10-08 迁到 pi-durable」。

2026-10-01 升级补充:当前接入目标已更新为 pi-agent-core/pi-ai 0.99.2,执行结果见 全面升级记录(已删除,见 git ceb66f8)。下文保留 0.87.1 的研究基线,不用历史计数验收新版。

新版实际目录为 42 个内置 provider(新增 TypeSafe 分类服务),合同和目录回归已同步;TypeSafe 没有 Agent 聊天模型,目录说明已标明。OAuth 共 9 个 provider:新增 openai 原生 Sign in with ChatGPT,浏览器流程与回调端口 1455;现有 openai-codex 仍保留 device/browser 两种模式。新版 openai 是动态注册的公开客户端流程,没有照搬旧 Codex 客户端身份提示。来源:0.99.2 OpenAI provider、原生 ChatGPT OAuth。

Pi 原生 MCP 客户端可复用,但本次依赖升级没有新增外部 MCP 工具配置;本项目对外 MCP 服务端、权限和审批仍按既有设计运行。

只读研究结论,依据本机 ~/.cache/deno/npm/registry.npmjs.org/@earendil-works/{pi-ai,pi-coding-agent}/0.87.1 的 dist 源码与随包 docs(docs/providers.md、models.md、custom-provider.md)。 目的:设置页支持 pi 的全部上游与订阅式登录。结论以代码为准,未核实的不写。

0. 一句话结论

  • pi-ai 内置 41 个 provider(providers/all.js 的 builtinProviders()),模型表为随包静态目录(providers/data/*.json,生成时间 2026-09-22),只有 Radius 支持动态拉取。
  • 订阅/OAuth 登录共 8 种:Anthropic(Claude Pro/Max)、OpenAI Codex(ChatGPT Plus/Pro)、GitHub Copilot、xAI(SuperGrok/X Premium)、Kimi Code、Meta(Muse)、OpenRouter(OAuth 换永久 key)、Radius(pi 自家网关)。
  • OpenCode Go / OpenCode Zen 没有 OAuth:两者都只能用 API Key(OPENCODE_API_KEY,在 opencode.ai 控制台生成),订阅体现在 key 上。
  • Google Gemini CLI / Antigravity 的 OAuth 在 0.87.1 已不存在(全包 grep antigravity|gemini-cli|cloudcode-pa 无结果)。Google 只剩 google(Gemini API Key)和 google-vertex(API Key / ADC / 服务账号)。
  • 登录流程完全通过可注入的 AuthInteraction 回调(prompt/notify)驱动,可以在无浏览器服务端由 Web 设置页复现,不用 fork pi。

1. pi-ai provider 注册表

1.1 关键 API(实际签名,dist/models.d.ts)

createModels(options?: { credentials?: CredentialStore; modelsStore?: ModelsStore; authContext?: AuthContext }): MutableModels
createProvider<TApi>(input: {
  id: string; name?: string; baseUrl?: string; headers?: ProviderHeaders;
  auth: ProviderAuth;                        // { apiKey?: ApiKeyAuth; oauth?: OAuthAuth },至少一个
  models: readonly Model<TApi>[];            // 静态基线
  fetchModels?: (ctx: RefreshModelsContext) => Promise<readonly Model<TApi>[]>; // 动态覆盖层
  filterModels?: (models, credential) => readonly Model<TApi>[];
  api: ProviderStreams | Partial<Record<TApi, ProviderStreams>>;   // 按 model.api 分派
}): Provider<TApi>
Models.getModels(provider?: string): readonly Model<Api>[]           // 同步,取最近一次已知列表
Models.getModel(provider: string, id: string): Model<Api> | undefined
Models.getAvailable(providerId?, opts?): Promise<readonly Model<Api>[]> // 仅鉴权完整的 provider,且经 filterModels
Models.refresh({ providers?, force?, allowNetwork?, signal? })      // 只对动态 provider 生效
Models.checkAuth(providerId) / getAuth(providerId | model, overrides?)
Models.login(providerId, "api_key" | "oauth", interaction: AuthInteraction): Promise<Credential>
Models.logout(providerId)
getSupportedThinkingLevels(model) / clampThinkingLevel(model, level)  // "off"|"minimal"|"low"|"medium"|"high"|"xhigh"|"max"

子路径导出:@earendil-works/pi-ai/providers/<id>(如 providers/openai-codex 导出 openaiCodexProvider())、providers/all(builtinProviders()、builtinModels()、getBuiltinModels(p)、getBuiltinProviders())、api/<name>.lazy。

1.2 通用能力(所有 provider 一致)

  • 流式:全部走 stream/streamSimple 事件流(text/thinking/toolcall delta)。工具调用:10 种 API 适配器全部支持。
  • thinking:Model.reasoning: boolean + 可选 thinkingLevelMap(null 表示该档不支持);请求时 streamSimple(..., { reasoning: ThinkingLevel }),用 clampThinkingLevel 夹到模型支持的档位。OpenAI 兼容端点的参数格式由 compat.thinkingFormat 决定(openai/openrouter/deepseek/together/baseten/zai/qwen/qwen-chat-template/chat-template/string-thinking/ant-ling)。
  • 缓存:通用请求选项 cacheRetention: "none"|"short"|"long" + sessionId(做缓存亲和/路由),由各适配器映射;目录里只有 anthropic 的 15 个模型声明了 promptCache 寿命。
  • baseUrl 在 Model 级(每个模型自带 baseUrl);provider 级 baseUrl 只是元数据。覆盖做法 = 克隆模型改 baseUrl(pi-coding-agent 的 provider-composer.js 就是 config.baseUrl ?? model.baseUrl)。OAuth 的 toAuth() 也可返回 baseUrl(Copilot 按 token 计算代理域名)。

1.3 内置 provider 一览(id / 认证 / API / 目录模型数)

API Key 型的 key 同时支持环境变量(下表)和存储凭据;“OAuth”栏为订阅登录名称。

id 名称 认证 API 模型数
anthropic Anthropic Key ANTHROPIC_API_KEY(另认 ANTHROPIC_OAUTH_TOKEN、ANTHROPIC_AUTH_TOKEN)+ OAuth Claude Pro/Max anthropic-messages 15
openai OpenAI Key OPENAI_API_KEY openai-responses 41
openai-codex OpenAI Codex 仅 OAuth(ChatGPT Plus/Pro) openai-codex-responses(SSE/WebSocket) 8
azure-openai-responses Azure OpenAI Key AZURE_OPENAI_API_KEY + AZURE_OPENAI_BASE_URL 或 _RESOURCE_NAME azure-openai-responses 41
github-copilot GitHub Copilot Key COPILOT_GITHUB_TOKEN + OAuth(device code) anthropic/openai-completions/openai-responses 混合 32
google Google Gemini Key GEMINI_API_KEY google-generative-ai 22
google-vertex Vertex AI Key GOOGLE_CLOUD_API_KEY / ADC / 服务账号文件 + project/location google-vertex 14
amazon-bedrock Bedrock Bearer token / AWS profile / 凭据链 bedrock-converse-stream 165
xai xAI Key XAI_API_KEY + OAuth(SuperGrok/X Premium,device code) openai-responses 4
openrouter OpenRouter Key OPENROUTER_API_KEY + OAuth(PKCE 换永久 key) anthropic/openai-completions 386
opencode OpenCode Zen Key OPENCODE_API_KEY anthropic/google/openai 三种混合 73
opencode-go OpenCode Go Key OPENCODE_API_KEY(同一个变量) anthropic/openai-completions/openai-responses 30
kimi-coding Kimi For Coding Key KIMI_API_KEY + OAuth Kimi Code(device code) anthropic-messages 4
meta Meta Key META_API_KEY + OAuth Muse(device code) openai-responses 5
radius Radius(pi 网关) Key RADIUS_API_KEY + OAuth;动态模型表 pi-messages 30
vercel-ai-gateway Vercel AI Gateway Key AI_GATEWAY_API_KEY anthropic-messages 246
cloudflare-ai-gateway CF AI Gateway Key CLOUDFLARE_API_KEY + env 中 ACCOUNT_ID/GATEWAY_ID 三种混合 51
cloudflare-workers-ai CF Workers AI Key + CLOUDFLARE_ACCOUNT_ID openai-completions 18
mistral Mistral Key MISTRAL_API_KEY mistral-conversations 33
deepseek / groq / cerebras / together / baseten / fireworks / huggingface / nvidia / ant-ling 各自 Key(DEEPSEEK_API_KEY、GROQ_API_KEY、CEREBRAS_API_KEY、TOGETHER_API_KEY、BASETEN_API_KEY、FIREWORKS_API_KEY、HF_TOKEN、NVIDIA_API_KEY、ANT_LING_API_KEY) openai-completions(fireworks 另有 anthropic) 2/7/2/22/21/33/76/19/3
moonshotai / moonshotai-cn Moonshot 国际/国内 Key MOONSHOT_API_KEY openai-completions 4/4
minimax / minimax-cn MiniMax 国际/国内 Key MINIMAX_API_KEY / MINIMAX_CN_API_KEY anthropic-messages 3/3
zai / zai-coding-cn Z.AI Coding Plan 国际/国内 Key ZAI_API_KEY / ZAI_CODING_CN_API_KEY openai-completions 7/4
qwen-token-plan / -cn / -individual 阿里云百炼 Token Plan Key QWEN_TOKEN_PLAN_API_KEY / _CN_API_KEY(individual 复用前者) openai-completions 20/20/9
xiaomi / xiaomi-token-plan-cn/-ams/-sgp 小米 MiMo Key XIAOMI_API_KEY / XIAOMI_TOKEN_PLAN_{CN,AMS,SGP}_API_KEY openai-completions 6/4/4/4

另有 faux(测试假模型,已在用)。模型列表来源:除 Radius 外全部是随包 JSON(不联网);Copilot 有 filterModels,按 OAuth 凭据里的 availableModelIds 过滤;OpenCode 系列请求自动带 x-opencode-session(取 sessionId)。

1.4 OpenAI 兼容通用上游(自定义端点)

pi-ai 没有“generic provider”工厂,做法就是我们现在 packages/agent/src/pi/model.ts 的写法:createProvider({ id, auth, models:[{ id, api:"openai-completions", baseUrl, reasoning, input, cost, contextWindow, maxTokens, compat? , headers? }], api: openAICompletionsApi() })。pi-coding-agent models.json 的 provider 字段可参照:name, baseUrl, apiKey, api, headers, authHeader, compat, modelOverrides, models[];常用 compat:supportsDeveloperRole、supportsReasoningEffort、maxTokensField、thinkingFormat、supportsUsageInStreaming、supportsStrictMode。api 也可选 anthropic-messages/openai-responses/google-generative-ai,对应“Anthropic 兼容 / Responses 兼容 / Gemini 兼容”端点。

2. 订阅/OAuth 登录(实现在 pi-ai dist/auth/oauth/*,pi-coding-agent 只做 UI 与存储)

/login 命令(core/slash-commands.js + modes/interactive/interactive-mode.js)只是把 AuthPrompt/AuthEvent 渲染到 TUI,流程本身全在 pi-ai 的 OAuthAuth.login(interaction)。

provider 流程 回调 / 手工粘贴 token 形状与刷新 请求时如何用(toAuth)
anthropic 浏览器 PKCE,claude.ai/oauth/authorize,token platform.claude.com/v1/oauth/token 监听 127.0.0.1:53692/callback;同时给 manual_code 提示,可粘 redirect URL 或 code#state;state = PKCE verifier access/refresh,expires 提前 5 分钟;refresh_token grant { apiKey: access };适配器识别 sk-ant-oat 后改 Bearer,加 beta claude-code-20250219,oauth-2025-04-20,强制注入 “You are Claude Code…” 系统提示并把工具名改成 Claude Code 风格
openai-codex 先 select:浏览器 PKCE(auth.openai.com,redirect http://localhost:1455/auth/callback)或 device code(auth.openai.com/codex/device,15 分钟) 浏览器模式同时有回调监听与 manual_code(接受完整 URL / code#state / code= 串 / 裸 code) access/refresh/expires + accountId(从 JWT 的 chatgpt_account_id 解出);refresh_token grant { apiKey: access },适配器从 token 解 accountId 加请求头,baseUrl chatgpt.com/backend-api
github-copilot 先 text 问 GHE 域名(空=github.com),然后 device code(/login/device/code,scope read:user) 无回调,轮询 refresh 存 GitHub OAuth token,access 是 Copilot 短期 token(expires_at 提前 5 分钟);另存 enterpriseUrl、availableModelIds;登录时还会自动对需开启的模型调 /models/{id}/policy { apiKey: access, baseUrl: 按 token 计算的代理域名 },固定伪装 VS Code Copilot Chat 头
xai device code(auth.x.ai/oauth2/device/code) 轮询 access/refresh,缺省 1h,提前 5 分钟 { apiKey: access }
kimi-coding device code(auth.kimi.com) 轮询 access/refresh;refresh 最多重试 3 次 { headers: { Authorization: Bearer access } }
meta device code(auth.meta.com/oidc/device/...),再用身份 token 调 api.meta.ai/muse-code/key 铸 24h key 轮询 refresh 存身份 token,access 存铸出的 key;“刷新”= 重新铸 key(auth.meta.com 不支持 refresh grant) { apiKey: access }
openrouter 浏览器 PKCE,openrouter.ai/auth?callback_url=...,换 api/v1/auth/keys 监听 127.0.0.1:<随机端口>/oauth/callback/<uuid> + manual_code 得到永久 API key,expires = MAX_SAFE_INTEGER,refresh="",不刷新 { apiKey: access }
radius 先 select:浏览器(127.0.0.1:1456/oauth/callback)或 device code;网关默认 radius.pi.dev 同上 access/refresh,提前 60s;凭据中带 gatewayConfig(模型目录) { apiKey: access }

通用机制:

  • AuthInteraction = { signal?, prompt(p: AuthPrompt): Promise<string>, notify(e: AuthEvent): void }。AuthPrompt.type ∈ text | secret | select | manual_code;AuthEvent.type ∈ info | auth_url | device_code | progress。浏览器型流程会同时等回调和 manual_code,谁先到用谁。
  • 存储:CredentialStore { read, list, modify(providerId, fn), delete },按 provider id 一份凭据。pi-coding-agent 的 auth.json(~/.pi/agent/auth.json,0600,proper-lockfile 加锁)形如: { "anthropic": { "type":"oauth", "access":"…", "refresh":"…", "expires": 1790000000000 }, "openai-codex": { "type":"oauth", …, "accountId":"…" }, "opencode": { "type":"api_key", "key":"…" }, "cloudflare-workers-ai": { "type":"api_key", "key":"…", "env": { "CLOUDFLARE_ACCOUNT_ID":"…" } } };key 以 ! 开头表示执行命令取值。
  • 刷新:Models.getAuth() 内按需刷新——剩余 < 5 分钟(或 overrides.minOAuthValidityMs)时在 modify 锁内双重检查、调 oauth.refresh()、写回;失败抛 ModelsError(code:"oauth"),旧凭据保留,需重新登录。没有后台定时刷新。
  • 与 createProvider 衔接:OAuth token 不当作“配置的 apiKey”,而是 toAuth(credential) 在每次请求前转成 ModelAuth { apiKey?, headers?, baseUrl? }。我们只要把自己的 CredentialStore 交给 createModels({ credentials }),内置 provider 就会自动使用并刷新 OAuth 凭据。

3. 无浏览器服务端完成 OAuth

**可行,且不用改 pi。**做法:服务端调用 models.login(providerId, "oauth", interaction),interaction 由我们实现成“挂起式”:

  1. POST /oauth/start:建 pending login(内存,TTL 15 分钟,带 AbortController),启动 login();prompt(select) 用 start 请求里的 method(browser/device_code)作答,prompt(text) 用 enterpriseDomain 等参数作答;notify(auth_url|device_code) 写进 pending 记录;start 在拿到第一个事件后返回 { loginId, authUrl? , userCode?, verificationUri?, expiresIn? }。
  2. 用户在自己的浏览器授权。浏览器型的 redirect 指向用户本机的 localhost,页面会打不开,这是预期的:让用户复制地址栏里的完整 URL。
  3. POST /oauth/complete { loginId, input }:把 input 交给挂起的 manual_code Promise;pi 自己解析 URL/code#state、校验 state、换 token;成功后经我们的 CredentialStore.modify 写 secret 表。device code 型不需要 complete,前端轮询 GET /oauth/status?loginId= 直到 done/failed。

各 provider 的回调情况:

  • 必须粘回 URL(redirect 固定 localhost,无法改成我们的域名):Anthropic(53692)、OpenAI Codex 浏览器模式(1455)、Radius 浏览器模式(1456)。redirect_uri 与 client_id 在授权服务器登记,不能换成公网地址。
  • 优先走 device code(不用粘 URL,体验最好):Codex(可选)、Copilot、xAI、Kimi、Meta、Radius(可选)。Codex 的 device code 若返回 404,说明账号未开启该方式,需回退浏览器模式。
  • OpenRouter:callback_url 由客户端自由指定,理论上可直接回调到我们的设置页;但 pi 的实现写死本地监听,用 pi 就走“粘 URL”;要直回调需自己实现(流程很简单:PKCE + POST /api/v1/auth/keys)。

注意事项:

  • pi 仍会在服务端 listen 这些 localhost 端口(对方连不上,无害)。Anthropic 端口被占时会直接 reject 导致登录失败(Codex/Radius 会降级为只等粘贴)→ 同一 provider 的登录要串行,容器内别占 53692/1455/1456。
  • pending login 只能活在 api 进程内存(interaction 是闭包),多实例部署需粘性路由,或者直接限制单实例处理登录。
  • OAuth 模块通过 process.versions.node 判断后动态 import("node:http"/"node:crypto"),Deno 的 npm 兼容层满足条件,但要做一次 spike 实测(尤其 Codex 的 _randomBytes 是异步赋值)。

4. 映射方案

4.1 contracts settings.ts:pi 段改为“上游列表 + 默认上游”

export const PiUpstream = z.object({
  id: Id,                                   // 我们的上游 id(同一 provider 可有多个,例如两个 Codex 账号)
  label: z.string().min(1).max(64),
  provider: z.string().min(1),              // pi provider id(见 §1.3)或 "custom-openai" | "custom-anthropic" | "fake"
  authKind: z.enum(["api_key", "oauth", "ambient", "none"]),
  apiKeySecretRef: Id.nullable(),           // api_key:secret 表引用
  oauthSecretRef: Id.nullable(),            // oauth:secret 表中存 pi Credential JSON(access/refresh/expires/+扩展字段)
  providerEnv: z.record(z.string(), z.string()).nullable(), // CF ACCOUNT_ID、Vertex project/location、Azure resource 等非密值
  baseUrl: z.url().nullable(),              // 仅 api_key/custom 允许;OAuth 型置 null
  api: z.enum(["openai-completions","openai-responses","anthropic-messages","google-generative-ai"]).nullable(), // 仅 custom
  compat: z.record(z.string(), z.unknown()).nullable(),     // 仅 custom
  model: z.string().min(1).nullable(),
  thinkingLevel: ThinkingLevel,
  note: z.string().max(500).nullable(),
  status: z.enum(["ok", "needs_login", "refresh_failed", "untested"]), // 服务端维护,只读
});
export const PiSettings = z.object({
  upstreams: z.array(PiUpstream).max(32),
  defaultUpstreamId: Id.nullable(),
  contextBudget, compaction, subagentsEnabled,   // 保持原字段
});

能力/任务级 upstreamId? 覆盖默认上游(没有就用 defaultUpstreamId)。迁移:旧 {provider, baseUrl, model, apiKeySecretRef} 生成一条 upstream 并设为默认。

4.2 运行时装配

  • 每个上游建一个 Models 实例,里面只放它对应的那个内置 provider(providers/<id> 工厂),credentials 用一个只映射到本上游 secret 的 CredentialStore 适配器。这样绕开“pi 按 provider id 只存一份凭据”的限制,同一 provider 的多个账号可以并存。
  • baseUrl 覆盖:包一层 provider,getModels: () => base.getModels().map(m => ({ ...m, baseUrl }))。
  • thinking:clampThinkingLevel(model, upstream.thinkingLevel) 后传 reasoning。
  • ambient 型(Bedrock 凭据链、Vertex ADC)依赖服务端环境;设置页只做提示,不存密钥。

4.3 UI 流程

  • 新增上游 → 从 /pi/providers 能力表选 provider(按“订阅登录 / API Key / 云凭据 / 自定义兼容端点”分组;OpenCode Go 和 Zen 放在 API Key 组并注明“opencode 订阅 key”)。
  • API Key 型:填 key(和所需 env)→ 保存 → “测试连通”。
  • OAuth 型三步:① 选方式(有 device code 的默认选它)→ ② 显示授权链接或 userCode + 验证地址(复制按钮、倒计时)→ ③ 浏览器型:粘贴地址栏 URL 提交;device 型:自动轮询。成功后显示账号信息(Codex accountId、Copilot 域名)和过期时间。
  • 模型下拉:/models 返回 getAvailable() 的结果(包括 id、name、reasoning、支持的 thinking 档位、contextWindow、input),同时允许手填模型 id(目录滞后于上游时用)。

4.4 后端端点

端点 作用
GET /pi/providers 能力表:id、name、authKinds、oauth{name,isSubscription,methods:["browser"
GET/POST/PATCH/DELETE /pi/upstreams[/{id}] CRUD;密钥写 secret 表,响应只回 ref 与掩码
POST /pi/upstreams/{id}/oauth/start { method, enterpriseDomain? } → { loginId, authUrl?, userCode?, verificationUri?, expiresIn }
POST /pi/upstreams/{id}/oauth/complete { loginId, input }(粘贴的 URL / code)
GET /pi/upstreams/{id}/oauth/status `pending
POST /pi/upstreams/{id}/test getAuth + 发一个 1 token 的 completeSimple,回延迟与错误分类
GET /pi/upstreams/{id}/models getAvailable(),动态 provider 先 refresh()
POST /pi/upstreams/{id}/logout 删除 secret,状态改为 needs_login(不会撤销提供方侧的授权)

刷新:worker 定时任务(每 10 分钟)对 OAuth 上游调 models.getAuth(providerId, { minOAuthValidityMs: 30*60_000 }),让 pi 在锁内刷新并写回 secret;OpenRouter(永不过期)跳过。ModelsError.code==="oauth" → 状态改为 refresh_failed,创建待处理事项“上游 X 需要重新登录”;Agent 选上游时跳过不可用的,按配置回落到默认上游或报错。secret 表的 modify 要做行级锁(SELECT … FOR UPDATE),因为 api 和 worker 两个进程都可能刷新(refresh token 会轮换,并发刷新会让其中一方失效)。

5. 版本与许可

  • pi-ai / pi-coding-agent 均为 MIT。OAuth client_id 全部明文或 base64 写在公开源码里:Anthropic 9d1c250a-e61b-44d9-88ed-5944d1962f5e(Claude Code 的客户端)、Codex app_EMoamEEZ73f0CkXaXp7hrann(Codex CLI)、Copilot Iv1.b507a08c87ecfe98(VS Code Copilot)、xAI b1a00492-…、Kimi 17e5f671-…、Meta 1031625952748946、Radius pi-gateway。这些是公共客户端(PKCE / device code,没有 secret)。技术上可以直接复用;复用边界最好是“调用 pi-ai 的登录实现”,不要把 ID 抄进我们自己的代码,这样上游改 ID 或流程时跟着升级就行。
  • 升级时需要回归:目录 JSON 随版本变化(模型会增删);OAuth 端点和伪装头(Copilot 的 Editor-Version、Anthropic 的 Claude Code 身份)随时可能被提供方收紧。锁定 0.87.1,每次升级重新跑本文件的核对。
  • 条款风险提醒:这些订阅登录借用的是官方 CLI/IDE 的客户端身份(Anthropic 路径还会把请求伪装成 Claude Code)。把个人订阅用于自动化后台服务可能不符合各家的用户条款或使用政策,账号有被限流或封禁的风险。上线前请逐家确认条款,设置页也要对用户明示(这里不下法律结论)。