From 893270eb759fdab1335715f9be2cd03c03cd1733 Mon Sep 17 00:00:00 2001 From: new-Beginner <3205732937@qq.com> Date: Wed, 22 Jul 2026 16:16:25 +0800 Subject: [PATCH 1/7] =?UTF-8?q?antigravity=E5=8F=8D=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 6 +- README.md | 6 +- crates/gemini_oauth/src/antigravity/flow.rs | 105 ++++++++++++++++++ crates/gemini_oauth/src/antigravity/mod.rs | 5 +- crates/gemini_oauth/src/lib.rs | 4 +- crates/registry/src/presets_data.json | 1 - frontend/src/api/oauth.ts | 4 +- .../components/provider/OAuthLoginSection.vue | 88 +++++++++++---- .../components/provider/ProviderFormModal.vue | 12 +- frontend/src/i18n/en.ts | 5 + frontend/src/i18n/zh.ts | 5 + .../src/admin/handlers/antigravity_oauth.rs | 86 ++++++++++++-- .../fixtures/registry/builtin_presets.json | 3 +- 13 files changed, 287 insertions(+), 43 deletions(-) diff --git a/README.en.md b/README.en.md index e5541ef0d..aea7aeb6a 100644 --- a/README.en.md +++ b/README.en.md @@ -173,7 +173,7 @@ macOS builds are **not yet signed with an Apple Developer ID** and **not yet Not | Anthropic Messages (custom Claude-compatible) | ✅ (PR #153) | ✅ (PR #153) | ✅ (PR #153) | `apiFormat=anthropic_messages`; Claude preset pending real validation | | Grok Web (SuperGrok / X Premium+) | ✅ | ✅ | ✅ (v2.1.6 adds tool_calls flatten) | Experimental, TOS gray area, personal use only | | Grok Build (xAI grok CLI coding backend, `cli-chat-proxy.grok.com/v1`, responses) | 🧪 | 🧪 | 🧪 | **Login + end-to-end + token auto-refresh verified on a real machine (MOC-300, 2026-07-09)**. Native responses passthrough (reuses `ResponsesPassthroughMapper`, no new adapter/mapper); account login via OAuth2 **authorization code + PKCE** (`authScheme=grok_build_oauth`, click login → `auth.x.ai` OIDC discovery + PKCE, an in-app webview navigates the authorize page to clear the Cloudflare challenge; after authorization xAI **shows a code for the user to copy back into the app** to complete the token exchange (same as the official grok CLI; a local loopback `127.0.0.1:56121` is a fallback auto-capture) + auto refresh near expiry, credential stored in `grok-build-oauth.json`, **no local grok CLI install required**; the old device flow's browserless bare POST was blocked by CF, hence this change); outbound injects grok-shell client fingerprint headers (UA / x-xai-token-auth / x-grok-client-* / x-grok-model-override, see forward.rs `injects_grok_build_headers`) to avoid risk control. **Model mapping**: Codex's gpt-5.x names are mapped to grok models via `provider.models` slots, never passed through to the upstream (which wouldn't recognize them). Models: **grok-4.5** (500k, coding, supports reasoning effort high/medium/low; **replaces the retired grok-build**, MOC-319) / grok-composer-2.5-fast (200k); the model list can be fetched live from the real `/models` via the grok OAuth token (no preset edit needed). **Quota**: the Codex Usage panel shows grok's weekly quota (reverse-engineered `/usage show` → `billing?format=credits`, `creditUsagePercent` → remaining% + reset, MOC-307). TOS gray, hidden by default, personal use only | -| Google Antigravity OAuth | ✅ | ✅ | ✅ | Backend ready, UI pending; gemini family context 1M (suffixed ids previously fell back to 258k, MOC-221); **native image_gen support** (MOC-210; gemini-3.1-flash-image by default, overridable via the `gpt-image-1` model slot) | +| Google Antigravity OAuth | ✅ | ✅ | ✅ | Selectable and authorizable from the Providers page; gemini family context 1M (suffixed ids previously fell back to 258k, MOC-221); **native image_gen support** (MOC-210; gemini-3.1-flash-image by default, overridable via the `gpt-image-1` model slot) | | Zhipu GLM (5.2 / 5.1 / 4.7) | ✅ | ✅ | ✅ | OpenAI Chat-compatible reverse proxy; 5.2 is 1M context | | Zhipu GLM Coding (4.7 / 4.6) | ✅ | ✅ | ✅ | GLM Coding Plan subscription endpoint (`/api/coding/paas/v4`); full ZCode fingerprint headers (incl. runtime `X-Platform`) injected at the proxy code layer, fully aligned with the OAuth login path for the Coding Plan 150% quota bonus | | GLM (Z.ai) (`zai-login`) | ✅ | ✅ | ✅ | **OAuth — no API key required**; uses your GLM Coding Plan subscription quota; `api.z.ai/api/anthropic` Anthropic Messages wire + ZCode fingerprint headers; in-app OAuth login (no key) + GLM Coding Plan quota display | @@ -183,6 +183,8 @@ macOS builds are **not yet signed with an Apple Developer ID** and **not yet Not > **MCP tools (Codex 0.130+ `tool_search` mechanism)**: Codex 0.130+ defers server-side MCP tools (`mcp__notion__*` / `mcp__linear__*`, etc., plus the built-in `cat-webfetch` web_fetch / web_search) to `tool_search` instead of placing them directly in `tools[]`. The proxy wires the full chain on both the **chat path** and the **Gemini / Antigravity path** — discovering tools from `tool_search_output` → injecting them into the downstream `tools[]` / `functionDeclarations` → routing back upstream by `namespace` (chat #293; Gemini/Antigravity MOC-217). **Applies to all chat-compat + Gemini-family providers in the table above**; only the Responses passthrough row (last row, routes through the proxy but as a 1:1 passthrough — namespace bundles are not flattened) is excluded. Previously the Gemini / Antigravity path dropped `tool_search`, leaving all deferred MCP tools (including the built-in web_fetch / web_search) invisible to the model under those providers — fixed by MOC-217. Additionally, the proxy appends a "search-by-name" rule to the `tool_search` description (when a tool is referenced by name but absent from the current tool list, search that exact name before concluding it is unavailable) — the upstream discovery guidance for some connectors (e.g. Linear) is a single tagline with no tool names to guess, so models hitting hints like "use `get_issue`" would wrongly conclude the tool does not exist (MOC-296). +> **Using Antigravity OAuth**: choose `Antigravity` in Add Provider. The Base URL is filled with the Cloud Code upstream and the protocol control displays `Gemini` while retaining the dedicated `antigravity_oauth` route internally. Click **Authorize** and complete Google OAuth in the browser. If the local callback does not complete, paste the one-time code or full callback URL into the field and submit it. The code is used only for that token exchange and is never stored as an API key; after login, Fetch Models and normal provider saving work as usual. + ## Reasoning effort mapping (chat-compat `reasoning_effort`) How Codex's `low/medium/high/xhigh` is dispatched per chat-completions upstream (issue #254): @@ -377,7 +379,7 @@ This project focuses on **OpenAI Codex App** integration; it is **not** an offic Upstream API keys / OAuth tokens are stored locally in `~/.codex-app-transfer/` (Unix 0600 + atomic write); the forwarding service only listens on `127.0.0.1` and does not hijack the system proxy. Apart from the feedback feature, this tool performs no third-party network access. -Some experimental providers (Grok Web / Grok Build / Gemini CLI OAuth / Antigravity OAuth) involve upstream TOS gray areas — Grok Web reverse-proxies grok.com's Web backend, Grok Build reuses the xAI grok CLI coding backend (`cli-chat-proxy.grok.com`) with the grok-shell client fingerprint, Gemini CLI OAuth uses the undocumented internal endpoint `cloudcode-pa.googleapis.com/v1internal` — strictly limited to **personal use**, **must not** be deployed as a public service, **carries a real account-ban risk**, **users assume the risk**. These gray-area providers are **hidden by default** in the "Add provider" list; enable the **Show gray-area providers** setting to reveal them (MOC-91). +Some experimental providers (Grok Web / Grok Build / Gemini CLI OAuth / Antigravity OAuth) involve upstream TOS gray areas — Grok Web reverse-proxies grok.com's Web backend, Grok Build reuses the xAI grok CLI coding backend (`cli-chat-proxy.grok.com`) with the grok-shell client fingerprint, Gemini CLI OAuth uses the undocumented internal endpoint `cloudcode-pa.googleapis.com/v1internal` — strictly limited to **personal use**, **must not** be deployed as a public service, **carries a real account-ban risk**, **users assume the risk**. Antigravity is visible by default as a built-in OAuth provider and retains its prominent risk warning; other gray-area providers remain hidden until **Show gray-area providers** is enabled (MOC-91). ## Acknowledgements diff --git a/README.md b/README.md index 4ac831425..ad03ad605 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ macOS 暂未做 **Apple Developer ID 代码签名** 与 **Apple 公证(Notarizat | Anthropic Messages(custom Claude-compatible) | ✅(PR #153) | ✅(PR #153) | ✅(PR #153) | `apiFormat=anthropic_messages`,Claude preset 待真实验证后开放 | | Grok Web(SuperGrok / X Premium+) | ✅ | ✅ | ✅(v2.1.6 加 tool_calls flatten) | 实验性,TOS 灰色,仅本机个人使用 | | Grok Build(xAI grok CLI 编码后端,`cli-chat-proxy.grok.com/v1`,responses) | 🧪 | 🧪 | 🧪 | **登录 + 端到端 + token 自动刷新真机验证通过(MOC-300,2026-07-09)**。协议走原生 responses 透传(复用 `ResponsesPassthroughMapper`,无新 adapter/mapper);账号登录 OAuth2 **authorization code + PKCE**(`authScheme=grok_build_oauth`,点登录经 `auth.x.ai` OIDC discovery + PKCE,内置 webview 导航授权页过 Cloudflare challenge;xAI 授权后**显示 code 让用户复制粘回 app**完成换 token(与官方 grok CLI 一致,loopback `127.0.0.1:56121` 兜底自动捕获)+ 临期自动 refresh,凭证落 `grok-build-oauth.json`,**不依赖本地装 grok CLI**;原 device flow 的无浏览器裸 POST 被 CF 拦,故改此流程);出站注入 grok-shell 客户端指纹头(UA / x-xai-token-auth / x-grok-client-* / x-grok-model-override,见 forward.rs `injects_grok_build_headers`)避风控。**模型映射**:Codex 的 gpt-5.x 名经 `provider.models` 槽位映射为 grok 模型,不透传到上游(否则 cli-chat-proxy 不识别)。模型:**grok-4.5**(500k,编码主力,支持 reasoning effort 三档 high/medium/low;**替代已下线的 grok-build**,MOC-319)/ grok-composer-2.5-fast(200k);模型列表可经 grok OAuth token 动态拉真实 `/models`(免每次改 preset)。**额度**:Codex Usage 面板显示 grok 周额度(逆向 `/usage show` → `billing?format=credits`,`creditUsagePercent` → 剩余% + reset,MOC-307)。TOS 灰色、默认隐藏、仅本机个人使用 | -| Google Antigravity OAuth | ✅ | ✅ | ✅ | 后端就绪,UI 待 PR;gemini 全系上下文 1M(带后缀 id 此前误落 258k 兜底,MOC-221);**支持 Codex 内置 image_gen 工具原生出图**(MOC-210,gemini-3.1-flash-image;可 provider 覆盖) | +| Google Antigravity OAuth | ✅ | ✅ | ✅ | 提供商页可直接选择并完成 OAuth;gemini 全系上下文 1M(带后缀 id 此前误落 258k 兜底,MOC-221);**支持 Codex 内置 image_gen 工具原生出图**(MOC-210,gemini-3.1-flash-image;可 provider 覆盖) | | 智谱 GLM(5.2 / 5.1 / 4.7) | ✅ | ✅ | ✅ | OpenAI Chat 兼容反代;5.2 为 1M context | | 智谱 GLM Coding(4.7 / 4.6) | ✅ | ✅ | ✅ | GLM Coding Plan 订阅套餐专属端点(`/api/coding/paas/v4`);代理代码层注入完整 ZCode 指纹头(含运行时 `X-Platform`),与 OAuth 登录路径完全对齐,获取 Coding Plan 150% 配额加成 | | GLM(Z.ai)(`zai-login`) | ✅ | ✅ | ✅ | **OAuth 免 API key**,走你的 GLM Coding Plan 订阅额度;`api.z.ai/api/anthropic` Anthropic Messages wire + ZCode 指纹头;provider 配置内 OAuth 登录(免 key)+ GLM Coding Plan 额度显示 | @@ -182,6 +182,8 @@ macOS 暂未做 **Apple Developer ID 代码签名** 与 **Apple 公证(Notarizat > **MCP 工具(Codex 0.130+ `tool_search` 机制)**:Codex 0.130+ 把 server-side MCP 工具(`mcp__notion__*` / `mcp__linear__*` 等,以及内置 `cat-webfetch` 的 web_fetch / web_search)defer 到 `tool_search`,不再直接放进 `tools[]`。代理在 **chat 路径**与 **Gemini / Antigravity 路径**均已打通全链路 —— 从 `tool_search_output` 发现工具 → 注入下游 `tools[]` / `functionDeclarations` → 按 `namespace` 路由回上游(chat #293;Gemini/Antigravity MOC-217)。**上表所有 chat-compat + Gemini 系 provider 通用**;仅 Responses 协议透传(末行,经代理但 1:1 透传、不展平 namespace)不适用。此前 Gemini / Antigravity 路径漏接 `tool_search`,导致这些 provider 下所有 defer 的 MCP 工具(含内置 web_fetch / web_search)对模型不可见 —— MOC-217 已修复。另:代理会在 `tool_search` 的 description 末尾追加一条「按名补搜」规则(工具被按名引用但不在当前工具列表时,先用该名精确搜索再判不可用)—— 上游的发现指引对部分连接器(如 Linear)只有一句 tagline、无工具名可猜,模型遇到 "use `get_issue`" 这类提示会误判工具不存在(MOC-296)。 +> **Antigravity OAuth 使用**:在「添加提供商」的名称中选择 `Antigravity`,Base URL 会自动填入 Cloud Code 上游,协议栏显示 `Gemini`(底层仍保留独立的 `antigravity_oauth` 路由)。点「授权」后在浏览器完成 Google OAuth;本机回调失败时,可把一次性授权码或完整 callback URL 粘到右侧并提交。授权码只用于当次 token exchange,不会保存为 API Key;登录后可正常「获取模型」并保存 provider。 + ## 思考程度档位映射(chat 协议 `reasoning_effort`) Codex 的 `low/medium/high/xhigh` 在各 chat-completions 上游的处理方式(issue #254): @@ -378,7 +380,7 @@ v2.1.12+ 的客户端 **强制** RSA-3072 PKCS#1-v1.5-SHA256 验签 `latest.json 上游 API key / OAuth token 仅保存在本机 `~/.codex-app-transfer/`(Unix 0600 + atomic write);转发服务只监听 `127.0.0.1`,不接管系统代理,除反馈功能外不涉及第三方联网行为。 -部分实验性 provider(Grok Web / Grok Build / Gemini CLI OAuth / Antigravity OAuth)涉及上游 TOS 灰色地带 — Grok Web 反代 grok.com Web 后端协议、Grok Build 复用 xAI grok CLI 的编码后端(`cli-chat-proxy.grok.com`)+ grok-shell 客户端指纹、Gemini CLI OAuth 借用 `cloudcode-pa.googleapis.com/v1internal` 内部端点 — 严格限定**个人使用**,**不应**作为对外服务发布,且存在封号风险,**用户自担风险**。这些灰色 provider 在「添加提供商」列表中**默认隐藏**,需到设置页打开「**显示灰色提供商**」开关才出现(MOC-91)。 +部分实验性 provider(Grok Web / Grok Build / Gemini CLI OAuth / Antigravity OAuth)涉及上游 TOS 灰色地带 — Grok Web 反代 grok.com Web 后端协议、Grok Build 复用 xAI grok CLI 的编码后端(`cli-chat-proxy.grok.com`)+ grok-shell 客户端指纹、Gemini CLI OAuth 借用 `cloudcode-pa.googleapis.com/v1internal` 内部端点 — 严格限定**个人使用**,**不应**作为对外服务发布,且存在封号风险,**用户自担风险**。Antigravity 按内置 OAuth 功能默认显示并保留醒目风险提示;其他灰色 provider 在「添加提供商」列表中默认隐藏,需到设置页打开「**显示灰色提供商**」开关才出现(MOC-91)。 ## 致谢 diff --git a/crates/gemini_oauth/src/antigravity/flow.rs b/crates/gemini_oauth/src/antigravity/flow.rs index 43ef7adbd..fa1b47af6 100644 --- a/crates/gemini_oauth/src/antigravity/flow.rs +++ b/crates/gemini_oauth/src/antigravity/flow.rs @@ -59,15 +59,27 @@ enum CallbackResult { description: Option, }, Malformed, + Cancelled, } /// 跑完整 Antigravity OAuth code grant 流程,带 cancel signal 支持。 /// 详见父 crate `flow::run_oauth_flow_with_cancel` 文档,本 fn 仅 antigravity /// 常量替换。 pub async fn run_antigravity_oauth_flow_with_cancel( + http: &reqwest::Client, + config: &OauthFlowConfig, + cancel: Option>, +) -> Result { + run_antigravity_oauth_flow_with_cancel_and_manual_code(http, config, cancel, None).await +} + +/// 跑完整 Antigravity OAuth 流程,并允许 UI 把一次性授权码或完整 callback URL +/// 送入当前登录。自动 loopback callback 与手动 code 先到者胜。 +pub async fn run_antigravity_oauth_flow_with_cancel_and_manual_code( http: &reqwest::Client, config: &OauthFlowConfig, mut cancel: Option>, + mut manual_code: Option>, ) -> Result { // 1. bind loopback — antigravity 用**固定 port** 51121 let bind_addr: SocketAddr = format!("127.0.0.1:{ANTIGRAVITY_CALLBACK_PORT}") @@ -152,8 +164,18 @@ pub async fn run_antigravity_oauth_flow_with_cancel( None => std::future::pending::<()>().await, } }; + let manual_code_fut = async { + match manual_code.as_mut() { + Some(rx) => rx.await.ok(), + None => std::future::pending::>().await, + } + }; let callback = tokio::select! { result = rx => result.map_err(|_| FlowError::Timeout(config.callback_timeout))?, + manual = manual_code_fut => match manual { + Some(raw) => parse_manual_callback(&raw, &state), + None => CallbackResult::Cancelled, + }, _ = tokio::time::sleep(config.callback_timeout) => { server_handle.abort(); return Err(FlowError::Timeout(config.callback_timeout)); @@ -195,12 +217,54 @@ pub async fn run_antigravity_oauth_flow_with_cancel( description: Some("Google callback 既没 code 也没 error,极不正常".into()), }); } + CallbackResult::Cancelled => return Err(FlowError::Cancelled), }; // 8. POST /token 换 access_token exchange_antigravity_code_for_token(http, &code, &redirect_uri).await } +/// 手动路径支持裸 code、完整 callback URL 或 query 串。完整 URL 若带 state,仍走 +/// 常规 state 校验;裸 code 只会从当前 epoch 的 oneshot receiver 到达,因此绑定本次登录。 +fn parse_manual_callback(raw: &str, expected_state: &str) -> CallbackResult { + let input = raw.trim(); + if input.is_empty() { + return CallbackResult::Malformed; + } + let looks_like_callback = input.starts_with("http://") + || input.starts_with("https://") + || input.starts_with("code=") + || input.starts_with("state=") + || input.contains("&code=") + || input.contains("&state="); + if looks_like_callback { + let parsed = url::Url::parse(input) + .or_else(|_| url::Url::parse(&format!("http://localhost/?{input}"))) + .ok(); + if let Some(url) = parsed { + let code = url + .query_pairs() + .find(|(key, _)| key == "code") + .map(|(_, value)| value.into_owned()) + .filter(|value| !value.is_empty()); + if let Some(code) = code { + let state = url + .query_pairs() + .find(|(key, _)| key == "state") + .map(|(_, value)| value.into_owned()) + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| expected_state.to_owned()); + return CallbackResult::Code { code, state }; + } + } + return CallbackResult::Malformed; + } + CallbackResult::Code { + code: input.to_owned(), + state: expected_state.to_owned(), + } +} + /// 用 refresh_token 刷新 antigravity access_token。 pub async fn refresh_antigravity_access_token( http: &reqwest::Client, @@ -384,4 +448,45 @@ mod tests { fn antigravity_callback_port_is_fixed_51121() { assert_eq!(ANTIGRAVITY_CALLBACK_PORT, 51121); } + + #[test] + fn manual_callback_accepts_bare_code_for_current_state() { + match parse_manual_callback(" code-123 ", "state-1") { + CallbackResult::Code { code, state } => { + assert_eq!(code, "code-123"); + assert_eq!(state, "state-1"); + } + other => panic!("unexpected callback: {other:?}"), + } + } + + #[test] + fn manual_callback_extracts_code_and_preserves_returned_state() { + match parse_manual_callback( + "http://localhost:51121/oauth-callback?code=ABC123&state=returned-state", + "expected-state", + ) { + CallbackResult::Code { code, state } => { + assert_eq!(code, "ABC123"); + assert_eq!(state, "returned-state"); + } + other => panic!("unexpected callback: {other:?}"), + } + } + + #[test] + fn manual_callback_rejects_empty_or_missing_code() { + assert!(matches!( + parse_manual_callback(" ", "state-1"), + CallbackResult::Malformed + )); + assert!(matches!( + parse_manual_callback("state=state-1", "state-1"), + CallbackResult::Malformed + )); + assert!(matches!( + parse_manual_callback("http://localhost/callback?code=", "state-1"), + CallbackResult::Malformed + )); + } } diff --git a/crates/gemini_oauth/src/antigravity/mod.rs b/crates/gemini_oauth/src/antigravity/mod.rs index ff5f4d23b..0eb53eeb6 100644 --- a/crates/gemini_oauth/src/antigravity/mod.rs +++ b/crates/gemini_oauth/src/antigravity/mod.rs @@ -34,7 +34,10 @@ pub mod static_models; pub use cloud_code::{ bootstrap_project as antigravity_bootstrap_project, AntigravityClientMetadata, }; -pub use flow::{refresh_antigravity_access_token, run_antigravity_oauth_flow_with_cancel}; +pub use flow::{ + refresh_antigravity_access_token, run_antigravity_oauth_flow_with_cancel, + run_antigravity_oauth_flow_with_cancel_and_manual_code, +}; pub use models::{fetch_antigravity_available_models, AntigravityModelEntry}; pub use quota::{fetch_gemini_quota_summary, GeminiQuota, QuotaError, QuotaWindow}; pub use static_models::antigravity_static_models; diff --git a/crates/gemini_oauth/src/lib.rs b/crates/gemini_oauth/src/lib.rs index 69c2c3f4e..14f72be90 100644 --- a/crates/gemini_oauth/src/lib.rs +++ b/crates/gemini_oauth/src/lib.rs @@ -63,8 +63,8 @@ pub use token::{OauthToken, TokenError, TokenStore}; pub use antigravity::{ antigravity_bootstrap_project, antigravity_static_models, fetch_antigravity_available_models, fetch_gemini_quota_summary, refresh_antigravity_access_token, - run_antigravity_oauth_flow_with_cancel, AntigravityClientMetadata, AntigravityModelEntry, - GeminiQuota, QuotaError, QuotaWindow, + run_antigravity_oauth_flow_with_cancel, run_antigravity_oauth_flow_with_cancel_and_manual_code, + AntigravityClientMetadata, AntigravityModelEntry, GeminiQuota, QuotaError, QuotaWindow, }; // z.ai / bigmodel(GLM Coding Plan 账号登录)provider re-exports(parallel module, diff --git a/crates/registry/src/presets_data.json b/crates/registry/src/presets_data.json index 69d54eb03..08997341d 100644 --- a/crates/registry/src/presets_data.json +++ b/crates/registry/src/presets_data.json @@ -468,7 +468,6 @@ }, { "id": "antigravity-oauth", - "gray": true, "name": "Antigravity", "baseUrl": "https://daily-cloudcode-pa.googleapis.com", "baseUrlOptions": [ diff --git a/frontend/src/api/oauth.ts b/frontend/src/api/oauth.ts index 142649d44..f08fa4261 100644 --- a/frontend/src/api/oauth.ts +++ b/frontend/src/api/oauth.ts @@ -70,8 +70,8 @@ export function oauthCancelLogin(kind: OAuthKind) { const { base } = endpoint(kind) return api('DELETE', `${base}/login/cancel`) } -// [MOC-300] 手动粘 code(仅 grokBuild):xAI 授权页显示 code 让用户复制粘回,而非重定向到 -// loopback。粘回的 code 送到等待中的 login,唤醒它换 token。返回 {accepted}。 +// 手动粘 code(grokBuild / antigravity):送到等待中的长阻塞 login,唤醒它换 token。 +// antigravity 同时接受完整 callback URL;自动 loopback callback 仍可独立完成。返回 {accepted}。 export function oauthSubmitCode(kind: OAuthKind, code: string) { const { base } = endpoint(kind) return api<{ accepted: boolean; error?: string }>('POST', `${base}/submit-code`, { code }) diff --git a/frontend/src/components/provider/OAuthLoginSection.vue b/frontend/src/components/provider/OAuthLoginSection.vue index b71205c29..40024051a 100644 --- a/frontend/src/components/provider/OAuthLoginSection.vue +++ b/frontend/src/components/provider/OAuthLoginSection.vue @@ -1,6 +1,6 @@ + + + + diff --git a/frontend/src/i18n/en.ts b/frontend/src/i18n/en.ts index b8daba928..c8a7bf110 100644 --- a/frontend/src/i18n/en.ts +++ b/frontend/src/i18n/en.ts @@ -471,7 +471,7 @@ export default { "grokWeb.tosWarning": "⚠️ Reverse-proxying grok.com Web; grok TOS gray area. Personal use of your own SuperGrok account only; do not deploy as a public service.", "grokWeb.savedPlaceholder": "Saved — leave empty to keep current value", "providers.title": "Providers", - "providers.subtitle": "Manage configured API providers", + "providers.subtitle": "Manage Official Codex and third-party API providers", "providers.add": "Add Provider", "providers.restartCodexApp": "Restart Codex App", "providers.name": "Provider Name", @@ -484,6 +484,16 @@ export default { "providers.setDefault": "Set Default", "providers.enable": "Enable", "providers.enabled": "Enabled", + "providers.officialName": "Official Codex", + "providers.officialDirect": "Direct official access · ChatGPT sign-in preserved", + "providers.officialAccountReady": "Official account ready", + "providers.officialNotLoggedIn": "Official account not signed in", + "providers.officialLogin": "Official Sign-in", + "providers.officialCancelLogin": "Cancel Sign-in", + "providers.officialLoginFailed": "Official sign-in failed", + "providers.officialSwitchSuccess": "Switched to Official Codex", + "providers.officialSwitchFailed": "Failed to switch to Official Codex", + "providers.providerSwitchSuccess": "Switched to third-party provider", "providers.added": "Added", "providers.manageAndTest": "Manage & Test", "providers.testSpeed": "Speed Test", @@ -493,7 +503,7 @@ export default { "providers.usageQuerying": "Querying...", "providers.usageUnavailable": "Balance or usage not found", "providers.openDocsHint": "Click to open official docs", - "providers.empty": "No provider yet. Add one from presets.", + "providers.empty": "No third-party provider yet. Add one from presets.", "providers.keyPlaceholder": "sk-...", "providers.keySavedPlaceholder": "Saved. Use the eye button to view it.", "providers.modelMenuTitle": "OpenAI Model Menu", diff --git a/frontend/src/i18n/zh.ts b/frontend/src/i18n/zh.ts index 24ed1e79a..dd0e9110a 100644 --- a/frontend/src/i18n/zh.ts +++ b/frontend/src/i18n/zh.ts @@ -540,7 +540,7 @@ export default { "⚠️ 反代 grok.com Web 端,grok TOS 灰色,仅限个人使用本机 SuperGrok 账号,不应作为对外服务发布。", "grokWeb.savedPlaceholder": "已保存,留空则保持不变", "providers.title": "提供商", - "providers.subtitle": "管理已配置的 API 提供商", + "providers.subtitle": "管理官方 Codex 与第三方 API 提供商", "providers.add": "添加提供商", "providers.restartCodexApp": "重启 Codex App", "providers.name": "提供商名称", @@ -553,6 +553,16 @@ export default { "providers.setDefault": "设为默认", "providers.enable": "启用", "providers.enabled": "已启用", + "providers.officialName": "Codex 官方", + "providers.officialDirect": "官方直连 · 保留 ChatGPT 登录", + "providers.officialAccountReady": "官方账号已就绪", + "providers.officialNotLoggedIn": "尚未登录官方账号", + "providers.officialLogin": "官方登录", + "providers.officialCancelLogin": "取消登录", + "providers.officialLoginFailed": "官方登录失败", + "providers.officialSwitchSuccess": "已切换到 Codex 官方", + "providers.officialSwitchFailed": "切换 Codex 官方失败", + "providers.providerSwitchSuccess": "已切换到第三方提供商", "providers.added": "已添加", "providers.manageAndTest": "管理与测速", "providers.testSpeed": "测速", @@ -562,7 +572,7 @@ export default { "providers.usageQuerying": "查询中...", "providers.usageUnavailable": "暂未识别到余额/用量", "providers.openDocsHint": "点击打开官方文档", - "providers.empty": "还没有提供商,先从预设添加一个。", + "providers.empty": "还没有第三方提供商,可从预设添加一个。", "providers.keyPlaceholder": "sk-...", "providers.keySavedPlaceholder": "已填入,可点眼睛查看", "providers.modelMenuTitle": "OpenAI 模型菜单", diff --git a/frontend/src/pages/ProvidersPage.vue b/frontend/src/pages/ProvidersPage.vue index a9873c60e..3d54fc592 100644 --- a/frontend/src/pages/ProvidersPage.vue +++ b/frontend/src/pages/ProvidersPage.vue @@ -1,10 +1,16 @@