Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .claude/rules/config-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ paths:

## 内置 Provider

- 内置 Provider 维护在 `src-tauri/resources/builtin-providers.json`,是唯一供应商来源(不支持自定义),当前覆盖 Anthropic、DeepSeek、智谱 GLM、Kimi、MiniMax、小米 MiMo、OpenRouter、火山方舟、万界方舟和 Ollama。
- 内置 Provider 维护在 `src-tauri/resources/builtin-providers.json`,是唯一供应商来源(不支持自定义),当前覆盖 Anthropic、DeepSeek、智谱 GLM、Kimi、MiniMax、小米 MiMo、OpenRouter、火山方舟、万界方舟、OpenCode Go 和 Ollama。
- 新增 provider 时同步 `localizedName`、`slug`、`baseUrl`、`docUrl` 和模型 `category`。
- 配置编辑器的环境变量自动填充逻辑要覆盖默认 model 字段:`ANTHROPIC_MODEL`、`ANTHROPIC_DEFAULT_OPUS_MODEL`、`ANTHROPIC_DEFAULT_SONNET_MODEL`、`ANTHROPIC_DEFAULT_HAIKU_MODEL`、`CLAUDE_CODE_SUBAGENT_MODEL`。

Expand All @@ -75,7 +75,10 @@ paths:

- 权限编辑器只管理 `defaultMode`、`disableBypassPermissionsMode`、`allow`、`deny`、`ask`、`additionalDirectories`;写回时保留其它顶层字段,例如 `disableAutoMode`。
- 修复权限 dirty 问题时优先做局部语义比较,不要扩大到全局 dirty 系统。
- 状态行默认脚本按平台分发:非 Windows 用 `src-tauri/resources/statusline/default.sh`(Bash,依赖 jq),Windows 用 `src-tauri/resources/statusline/default.ps1`(PowerShell,免 jq)。安装走后端 `install_status_line_preset`:Windows 写入 `~/.claude/statusline.ps1` 并把 `command` 设为绝对正斜杠路径的 `powershell -NoProfile -ExecutionPolicy Bypass -File ...`;两份脚本功能需保持对齐。
- 状态行默认脚本按平台分发:非 Windows 用 `src-tauri/resources/statusline/default.sh`(Bash,依赖 jq),Windows 用 `src-tauri/resources/statusline/default.ps1`(PowerShell,免 jq)。安装走后端 `install_status_line_preset`:Windows 写入 `~/.claude/statusline.ps1` 并把 `command` 设为绝对正斜杠**且加引号**的 `powershell -NoProfile -ExecutionPolicy Bypass -File "..."`(用户名含空格时不加引号会截断参数);两份脚本功能需保持对齐。
- 两份脚本源文件都**不带 BOM**;Windows 落盘时由 `config.rs::expected_status_line_script()` 前置 UTF-8 BOM。Windows PowerShell 5.1 读取无 BOM 的 `.ps1` 时按系统代码页(简中 CP936)解码,UTF-8 中文注释错位后残留的悬空 lead byte 会吞掉行尾换行,使下一行代码并入注释并触发 `ParserError`,状态行整行无输出。给源文件加 BOM 会变成双 BOM,Bash 脚本加 BOM 会让 shebang 失效——两者都不要做。
- `expected_status_line_script()` 同时是落盘内容和幂等比较基准,写入与比较必须共用它。若只改一处,已带 BOM 的脚本会被误判为“用户自定义”,安装预设时要求覆盖确认并把 BOM 覆盖掉,故障复发。
- `default.ps1` 必须显式以 UTF-8 读取 stdin(`[Console]::OpenStandardInput()` + 无 BOM `UTF8Encoding` 的 `StreamReader`):PS 5.1 的 `[Console]::In` 按系统代码页解码,含中文目录名或 session_name 的 JSON 会乱码;直接设 `[Console]::InputEncoding` 在 stdin 已重定向时可能抛异常。赋给 `[Console]::OutputEncoding` 的实例也必须无 BOM,否则输出头可能混入 `EF BB BF`(MD5 处的 `[System.Text.Encoding]::UTF8.GetBytes()` 不输出 preamble,属正常用法)。

## 新增配置字段同步点

Expand Down
1 change: 1 addition & 0 deletions .claude/rules/tauri-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ paths:
| `project.rs` | 项目 Git 状态、worktree、分支/worktree 清理 preview/apply、本地数据清理 |
| `claude_directory.rs` | `~/.claude` 文件树、文件预览、创建、重命名、删除与外部打开 |
| `claude_directory_watcher.rs` | `~/.claude` 变更监听并广播 `claude-directory-changed` |
| `claude_cli.rs` | Claude CLI 解析与执行:优先当前 `PATH`,再查官方 native 安装目录与 macOS Homebrew 标准目录 |
| `native_open.rs` | 默认终端 / 编辑器跨平台启动、本机检测受支持工具清单 |
| `terminal_focus.rs` | macOS 上 `pid -> tty -> AppleScript` 聚焦 Terminal.app / iTerm / Ghostty;herdr 会话两跳聚焦编排 |
| `herdr.rs` | herdr 会话聚焦:socket API 客户端(NDJSON)、pane 定位(pid 精确 + cwd 兜底)、附着 client 进程发现 |
Expand Down
24 changes: 24 additions & 0 deletions docs/adr/0005-provider-auth-frontend-hardcode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# opencode-go 认证字段切换:前端按供应商 slug 硬编码,不建模进 provider 数据层

## Context

opencode-go 是 Anthropic 兼容网关(端点 `https://opencode.ai/zen/go`),用 `x-api-key` 认证,对应 Claude Code 的 `ANTHROPIC_API_KEY`;而默认认证区展示的是 `ANTHROPIC_AUTH_TOKEN`(Bearer)。

两条前置约束:
- Provider 数据层(`builtin-providers.json`)只承载供应商**客观信息**——连接地址 `env.ANTHROPIC_BASE_URL`、模型映射与元数据,**不含认证密钥、不含认证方式**;认证密钥属于 Profile 的 `settings.env`。
- Claude Code 的认证语义是 `ANTHROPIC_AUTH_TOKEN`(Bearer)**优先**、`ANTHROPIC_API_KEY`(x-api-key)回退;后端 `resolve_model_test_request` 已按此实现,前端认证字段默认也只对应 `ANTHROPIC_AUTH_TOKEN`。

因此 opencode-go 这类"需要 x-api-key 认证"的供应商,其认证字段与默认 UI 冲突:若仍显示 `ANTHROPIC_AUTH_TOKEN`,用户填进去的 key 会被按 Bearer 发送,认证失败。

## Decision

1. **前端按 slug 硬编码切换**:`ProfileEditor.tsx` 用 `providerSlugFromId(providerId) === "opencode-go"` 判定,命中时认证区字段由 `ANTHROPIC_AUTH_TOKEN` 切换为 `ANTHROPIC_API_KEY`(label / placeholder / value / onChange 全部联动),并把 `ANTHROPIC_API_KEY` 追加进 `hiddenEnvKeys` 从通用环境变量分区隐藏。
2. **切换时清理互斥残留**:`applyProviderAutofill` 切到可解析的 opencode-go 时,在清空地址之外再置空 `ANTHROPIC_AUTH_TOKEN`——否则残留的 Bearer token 会被"Bearer 优先"语义遮蔽用户新填的 API Key,且两者都被隐藏、用户无从察觉。
3. **坚持"Bearer 优先、API_KEY 回退"不变式**:后端 `resolve_model_test_request` 保持通用回退,不感知具体供应商;前端只做展示层切换,不复制该认证选择逻辑。
4. **不把认证方式建模进 provider 数据层**:暂不引入 `authScheme` / `credentialEnvKey` 之类的 provider 字段。理由:当前仅 opencode-go 一个特例,数据层建模的收益尚未覆盖其同步成本(前端 schema、后端解析、契约、测试)。

## Consequences

- **硬编码特例会随供应商增加而扩散**:`=== "opencode-go"` 散落在 `ProfileEditor.tsx`、`config-workspace-utils.ts` 与测试。出现第二个 x-api-key(或其它非 Bearer)供应商时,应重新评估把认证方式建模进 provider 数据层,并回看本 ADR。
- **清理是单向的**:只清"切向 opencode-go"方向的 `ANTHROPIC_AUTH_TOKEN`;切走时保留 `ANTHROPIC_API_KEY`——x-api-key 是 Anthropic 兼容通用认证,切走后 Bearer 优先时它不遮蔽任何东西,属无害保留。
- **后端不感知供应商**:认证回退语义与真实 Claude Code 保持一致,新增供应商无需改后端认证逻辑;后端"Bearer 优先"不变式成为前端切换与残留清理的共同依据。
2 changes: 1 addition & 1 deletion docs/user-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ The scheme is registered for packaged installs on macOS / Windows / Linux; Linux

## Providers

Providers are all built-in and read-only. They carry only objective provider information (the connection endpoint `ANTHROPIC_BASE_URL`, the model mapping, and optional additional environment variables) and contain no authentication keys. They currently cover Anthropic, DeepSeek, Zhipu GLM Coding Plan, Kimi Code Plan, MiniMax Token Plan, Xiaomi MiMo Token Plan, OpenRouter, Volcengine Ark Coding Plan, Alibaba Cloud Bailian Coding Plan, Wanjie Ark, and Ollama.
Providers are all built-in and read-only. They carry only objective provider information (the connection endpoint `ANTHROPIC_BASE_URL`, the model mapping, and optional additional environment variables) and contain no authentication keys. They currently cover Anthropic, DeepSeek, Zhipu GLM Coding Plan, Kimi Code Plan, MiniMax Token Plan, Xiaomi MiMo Token Plan, OpenRouter, Volcengine Ark Coding Plan, Wanjie Ark, OpenCode Go, and Ollama.

Custom providers are not supported. After you select a built-in provider under the "Provider" option in the configuration editor, its connection endpoint and model mapping are filled in automatically; you only need to add the authentication key and behavior settings. Clicking "View built-in providers" below that option opens a read-only overview where you can see each provider's name, ID, API endpoint, official documentation link, and recommended models.

Expand Down
2 changes: 1 addition & 1 deletion docs/user-manual.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ macOS / Windows / Linux 在安装包场景下均可注册 scheme;开发态下 Li

## 供应商 Provider

供应商均为内置且只读,只承载供应商客观信息(连接地址 `ANTHROPIC_BASE_URL`、模型映射与可选附加环境变量),不含认证密钥。当前覆盖 Anthropic、DeepSeek、智谱 GLM Coding Plan、Kimi Code Plan、MiniMax Token Plan、小米 MiMo Token Plan、OpenRouter、火山方舟 Coding Plan、万界方舟和 Ollama。
供应商均为内置且只读,只承载供应商客观信息(连接地址 `ANTHROPIC_BASE_URL`、模型映射与可选附加环境变量),不含认证密钥。当前覆盖 Anthropic、DeepSeek、智谱 GLM Coding Plan、Kimi Code Plan、MiniMax Token Plan、小米 MiMo Token Plan、OpenRouter、火山方舟 Coding Plan、万界方舟、OpenCode Go 和 Ollama。

不支持自定义供应商。在配置编辑器的「供应商」选项处选择一个内置供应商后,其连接地址与模型映射会自动带入;你只需补充认证密钥与行为设置。点击该选项下方的「查看内置供应商」可打开只读一览,查看每个供应商的名称、ID、API 地址、官方文档链接和推荐模型。

Expand Down
20 changes: 20 additions & 0 deletions src-tauri/resources/builtin-providers.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@
"docUrl": "https://openrouter.ai/docs/guides/coding-agents/claude-code-integration",
"models": []
},
{
"name": "OpenCode Go",
"localizedName": { "zh": "OpenCode Go", "en": "OpenCode Go" },
"slug": "opencode-go",
"baseUrl": "https://opencode.ai/zen/go",
"docUrl": "https://opencode.ai/docs/zh-cn/go",
"env": {
"ANTHROPIC_MODEL": "deepseek-v4-pro[1m]",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "deepseek-v4-pro[1m]",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "deepseek-v4-flash[1m]",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-v4-flash",
"CLAUDE_CODE_SUBAGENT_MODEL": "deepseek-v4-flash"
},
"models": [
{ "id": "deepseek-v4-pro[1m]", "name": "DeepSeek V4 Pro" },
{ "id": "deepseek-v4-flash[1m]", "name": "DeepSeek V4 Flash" },
{ "id": "minimax-m3", "name": "MiniMax M3" },
{ "id": "qwen3.8-max", "name": "Qwen3.8 Max" }
]
},
{
"name": "Ollama",
"localizedName": { "zh": "Ollama", "en": "Ollama" },
Expand Down
16 changes: 13 additions & 3 deletions src-tauri/resources/statusline/default.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

# 状态行追求健壮而非严格:单个字段异常不应导致整行无输出
$ErrorActionPreference = 'SilentlyContinue'
# 强制 UTF-8 输出,避免 -> 等字符被系统代码页破坏
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 强制 UTF-8 输入输出,避免 -> 等字符与中文被系统代码页破坏。
# 必须用无 BOM 实例:[System.Text.Encoding]::UTF8 带 preamble,PS 5.1 下可能把 EF BB BF 混进输出头
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
[Console]::OutputEncoding = $Utf8NoBom

# ── ANSI 颜色常量(用拼接构造,避免字符串插值把 $var[ 当作索引)──
$ESC = [char]27
Expand Down Expand Up @@ -100,7 +102,15 @@ function Format-K($n) {
}

# ── 读取并解析 stdin JSON ──────────────────────────────────
$stdin = [Console]::In.ReadToEnd()
# 显式以 UTF-8 读取标准输入:PS 5.1 的 [Console]::In 按系统代码页解码,
# Claude Code 传入的 UTF-8 JSON 一旦含中文(目录名、session_name)就会乱码。
# 直接设 [Console]::InputEncoding 在 stdin 已重定向时可能抛异常,故改用显式编码的 StreamReader。
try {
$stdinReader = New-Object System.IO.StreamReader([Console]::OpenStandardInput(), $Utf8NoBom)
$stdin = $stdinReader.ReadToEnd()
} catch {
$stdin = [Console]::In.ReadToEnd()
}
if ([string]::IsNullOrWhiteSpace($stdin)) { exit 0 }
try { $data = $stdin | ConvertFrom-Json } catch { exit 0 }

Expand Down
Loading