Skip to content

fix(hermes): Windows 上 Hermes 供应商配置不生效#4680

Open
thisTom wants to merge 3 commits into
farion1231:mainfrom
thisTom:fix/hermes-config-dir-resolution
Open

fix(hermes): Windows 上 Hermes 供应商配置不生效#4680
thisTom wants to merge 3 commits into
farion1231:mainfrom
thisTom:fix/hermes-config-dir-resolution

Conversation

@thisTom

@thisTom thisTom commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary / 概述

该 PR 修复 CC Switch 的 Hermes home/config directory 解析与 Hermes 自身行为不一致的问题。

Windows 上 Hermes 默认读取 %LOCALAPPDATA%\hermes,并且会优先读取非空 HERMES_HOME。此前 CC Switch 在未配置 hermes_config_dir override 时固定回落到 ~/.hermes,导致 provider config、Hermes skills 等写入 CC Switch 认为的目录,但 Hermes 实际不会读取。

场景 修改前 修改后
Windows 平台默认 Hermes home CC Switch 默认使用 ~/.hermes 对齐 Hermes:默认使用 %LOCALAPPDATA%\hermes,缺失或为空时回退 %USERPROFILE%\AppData\Local\hermes
用户或 Hermes 安装器设置了 HERMES_HOME CC Switch 仍可能写入 ~/.hermes 在没有 CC Switch 显式 override 时,优先使用非空 HERMES_HOME
CC Switch 显式配置 hermes_config_dir 作为覆盖路径 保持最高优先级,不改变现有用户配置
Provider config / Hermes skills 等复用 get_hermes_dir() 的功能 可能写入 Hermes 不读取的位置 统一使用与 Hermes 一致的 home directory
macOS / Linux Hermes 与 CC Switch 默认都使用 ~/.hermes 默认行为保持不变

Root Cause / 根因

get_hermes_dir() 之前只处理 CC Switch 的 settings.hermes_config_dir override,否则直接返回 ~/.hermes

但 Hermes 自身的 get_hermes_home() 会按以下顺序解析 home directory:

  1. in-process override;
  2. 非空 HERMES_HOME
  3. 平台默认目录。

其中 Windows 平台默认目录来自 LOCALAPPDATA,不是 ~/.hermes。因此在 Windows 上会出现:CC Switch 写入 C:\Users\<user>\.hermes\config.yaml,而 Hermes 实际读取 C:\Users\<user>\AppData\Local\hermes\config.yaml,表现为 CC Switch 中的 provider 编辑或 Hermes skills 同步不生效。

Fix / 修复方式

get_hermes_dir() 现在按以下优先级解析:

  1. settings.hermes_config_dir:CC Switch 显式 override,保持最高优先级;
  2. HERMES_HOME:trim 后非空则使用,行为对齐 Hermes;
  3. 平台默认目录:
Platform Default path
Windows %LOCALAPPDATA%\hermes,缺失或为空时回退 %USERPROFILE%\AppData\Local\hermes
macOS / Linux ~/.hermes

同时新增纯函数 windows_local_hermes_dir(),让 Windows LOCALAPPDATA 与 fallback 逻辑可以在非 Windows CI 上稳定覆盖。

两个实现选择是刻意保留的:

选择 原因
读取 LOCALAPPDATA 环境变量 Hermes 自身读取的就是该环境变量;这样在 Known Folder 重定向或 launcher 覆盖环境变量时,CC Switch 与 Hermes 仍保持一致。
保留 HERMES_HOME HERMES_HOME 是 Hermes 的一等 home directory 机制;忽略它会让 relocated / custom profile 安装重新写入错误目录。

Relationship to #3470 / 与 #3470 的关系

#3470 也在修复 Windows 默认目录从 ~/.hermes%LOCALAPPDATA%\hermes 的问题。

本 PR 与 #3470 的主要差异:

差异 本 PR 的处理
HERMES_HOME 保留并按 Hermes 自身顺序优先于平台默认目录。Codex/Claude 没有同等语义的 home override,但 Hermes 有。
Windows 默认目录来源 直接读取 LOCALAPPDATA 环境变量,而不是重新推导 Known Folder path,避免与 Hermes 实际读取值产生偏差。
测试覆盖 用纯函数覆盖 LOCALAPPDATA 存在、缺失、为空三类 Windows 路径分支,使该逻辑在非 Windows CI 上也能被测试。

Related Issue / 关联 Issue

Fixes #3178
Fixes #3811
Related #3470

已检索 HERMES_HOMELOCALAPPDATA hermesccswitch hermes 模型 等相关 issues。#2973 已由 #3267 处理;#4648 是 CN 版 Hermes state.db 路径探测问题,和本 PR 修复的 Hermes home/config/skills 目录解析不是同一范围。

Screenshots / 截图

Real-machine verification on Windows. This PR only changes Rust-side path resolution (no UI changes); the screenshots below are end-to-end proof that the config now lands where Hermes actually reads it.

Before — Hermes' model picker does not list the baomiao-oneapi provider. CC Switch wrote config.yaml to ~/.hermes, which Hermes on Windows never reads, so the provider configured in CC Switch was invisible to Hermes.

Before: Hermes does not list the baomiao-oneapi provider

After — Hermes lists the baomiao-oneapi provider with its 4 models (deepseek-v4-flash, deepseek-v4-pro, kimi-k2.7, qwen3.7-plus). CC Switch now writes to %LOCALAPPDATA%\hermes, where Hermes actually reads its config.

After: Hermes lists the baomiao-oneapi provider and its 4 models

Windows 真机验证。本 PR 仅改 Rust 侧目录解析(无 UI 改动);上面两张图是端到端证据——修复前 Hermes 列表里没有 baomiao-oneapi(配置被写到 ~/.hermes,Win 上 Hermes 不读);修复后写到 %LOCALAPPDATA%\hermes,Hermes 正常列出该供应商及其 4 个模型。

Testing / 测试

新增/覆盖的关键测试:

  • hermes_home_env_takes_precedence_over_platform_default
  • settings_override_takes_precedence_over_hermes_home
  • blank_hermes_home_falls_through_to_platform_default
  • default_hermes_dir_without_override_is_platform_correct
  • windows_local_hermes_dir_uses_localappdata_when_set
  • windows_local_hermes_dir_falls_back_to_home_when_localappdata_missing_or_empty
检查项 结果
cargo fmt --check --manifest-path src-tauri/Cargo.toml 通过
cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings 通过
cargo test --manifest-path src-tauri/Cargo.toml 通过
GitHub Actions / Backend Checks 通过
GitHub Actions / Frontend Checks 通过,59 个测试文件 / 361 个测试

Source References / 参考

Out of Scope / 非本次范围

Checklist / 检查清单

  • 已搜索相关 open/closed issues,并只关联本 PR 实际覆盖的问题
  • 已补充 Hermes 目录解析优先级与 Windows fallback 的回归测试
  • PR 只包含上游仓库相关改动,无本地调试文件或内部上下文
  • 无 UI 变更,因此无需截图
  • GitHub Actions Backend / Frontend checks 均通过

On Windows, CC Switch hardcoded `~/.hermes` as the Hermes config directory,
but Hermes resolves it through `get_hermes_home()`: `HERMES_HOME` env var,
then a platform default (Windows `%LOCALAPPDATA%\hermes`, mac/Linux
`~/.hermes`). So CC Switch wrote configs where Hermes never reads them and
provider changes had no effect (refs farion1231#3178).

`get_hermes_dir()` now mirrors Hermes' own resolution order:
  1. `settings.hermes_config_dir` — CC Switch explicit override
  2. `HERMES_HOME` env var — trimmed, non-empty, taken as-is (no `~`
     expansion, matching Hermes' `Path(val)`)
  3. platform default — Windows reads the `LOCALAPPDATA` env var (the exact
     value Hermes reads, not a re-derived Known-Folder path) and falls back
     to `~\AppData\Local\hermes`; mac/Linux keep `~/.hermes`

Relation to farion1231#3470: that PR dropped `HERMES_HOME` for "consistency with
Codex/Claude". But unlike those tools Hermes treats `HERMES_HOME` as a
first-class mechanism (its own Windows installer sets it), so ignoring it
reintroduces the same mismatch for relocated installs. This keeps it.

Tests cover HERMES_HOME precedence, settings-override > HERMES_HOME, blank
HERMES_HOME fall-through, the platform-correct default, and a pure
`windows_local_hermes_dir` helper exercising the LOCALAPPDATA-set and
LOCALAPPDATA-empty fallback paths on every host.
@thisTom thisTom changed the title fix(hermes): resolve config dir via HERMES_HOME and platform defaults fix(hermes): 对齐 HERMES_HOME 与平台默认配置目录 Jun 26, 2026
@qiyou011

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 24df0a8cb6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/hermes_config.rs
…APPDATA

`get_hermes_dir()` now consults `HERMES_HOME` (all platforms) and `LOCALAPPDATA`
(Windows). The `with_test_home` helper only neutralized `CC_SWITCH_TEST_HOME`,
so an ambient `HERMES_HOME` — which Hermes' own Windows installer sets — would
make tests that write `get_hermes_config_path()` escape the temp home and touch
a real Hermes config (and the same hazard exists via `LOCALAPPDATA` on Windows).

Clear and restore both env vars in `with_test_home`. This is safe: only this
module reads those env vars, and `dirs` uses the Known-Folder API rather than the
env var. Adds a test asserting both are neutralized inside `with_test_home`.

Addresses the Codex review comment on farion1231#4680.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e20d361e4f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/hermes_config.rs
`windows_local_hermes_dir` checked `OsStr::is_empty()` on the raw value, but
Hermes does `os.environ.get("LOCALAPPDATA", "").strip()`. A whitespace/padded
`LOCALAPPDATA` would make CC Switch write under the literal padded path while
Hermes trims and falls back, hiding the config from Hermes. Trim before the
empty check, matching the existing `HERMES_HOME` handling. Adds
`windows_local_hermes_dir_trims_localappdata`.

Addresses the Codex review comment on farion1231#4680.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1becac58e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fn default_hermes_dir() -> PathBuf {
windows_local_hermes_dir(
std::env::var_os("LOCALAPPDATA").as_deref(),
&crate::config::get_home_dir(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use Hermes' home fallback for blank LOCALAPPDATA

On Windows when LOCALAPPDATA is unset or blank, upstream _get_platform_default_hermes_home falls back to Path.home() / "AppData" / "Local", but this call passes CC Switch's get_home_dir(), which resolves the profile via Windows Known Folder instead of the USERPROFILE/HOMEDRIVE environment values Python uses. In stripped launcher/service environments where LOCALAPPDATA is blank and Python's home env points elsewhere, CC Switch writes under the Known Folder profile while Hermes reads its Path.home() fallback, hiding the config again.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@farion1231 大佬评估下, Codex、Claude 对抗性的评审结果是建议不再修改:

我核查了这条 P2,结论是:这个边界技术上成立,但倾向于本 PR 暂不继续改代码。

原因如下:

  1. 这条路径只会在 Windows 下 LOCALAPPDATA 未设置或为空时触发。正常的 CC Switch 桌面 App 运行环境中,LOCALAPPDATA 应该始终存在;而只要它存在,本 PR 已经直接读取并 trim 这个 env var,与 Hermes 自身读取的值保持一致。

  2. blank LOCALAPPDATA 之后的 fallback 目前使用 get_home_dir() 是有意保持 CC Switch 内部一致性。get_home_dir() 是项目里统一的 home 解析入口,Windows 下使用 Known Folder profile,并且已经被 ~/.claude~/.codex、CC Switch 数据目录,以及 macOS/Linux 的 ~/.hermes 分支复用。

  3. 如果只为了 Hermes 的这个极窄 fallback 单独增加 USERPROFILE / HOMEDRIVE / HOMEPATH 解析,会让 get_hermes_dir() 成为项目里唯一一条 Python Path.home() 风格的例外路径,同时也会扩大测试里需要隔离和 restore 的全局 env 范围。

所以我的判断是:主路径已经和 Hermes 对齐,这个 fallback 分支更适合保持 CC Switch 现有 home 语义。如果维护者认为即使在 blank LOCALAPPDATA 的异常环境下也必须完全对齐 Python Path.home(),我也可以再补一个小的 focused helper 和对应测试。

@thisTom thisTom changed the title fix(hermes): 对齐 HERMES_HOME 与平台默认配置目录 fix(hermes): provider config not taking effect on Windows Jun 26, 2026
@thisTom thisTom changed the title fix(hermes): provider config not taking effect on Windows fix(hermes): Windows 上 Hermes 供应商配置不生效 Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hermes Skill 同步路径未检测 HERMES_HOME 环境变量,导致 Symlink 写入错误目录 ccswitch+hermes模型使用问题

2 participants