hermes update 悄悄换了默认频道:不再追每个提交,只跟正式版

周三早上你按老习惯敲下 hermes update,等着它把 main 上那几十个新提交拉下来。结果它只回了一行:✓ No newer release: stable v0.21.6; this checkout is newer and waits for the next release…,顺带告诉你落后 main 两百多个提交。你没动过任何配置,它却说自己跟的是「正式版」。
这不是坏了,是默认行为换了:官方源码安装的 hermes update 现在默认走 stable 频道——也就是 GitHub 上最新一个已发布的正式版,而不是 main 上的每一个提交。这篇把新规则讲清楚,包括最容易踩的一条:「默认只往前走」。
先搞清楚 stable 到底指向什么
stable = 官方仓库最新一个已发布的 GitHub Release,而且必须同时满足:
- 不是草稿(draft)、不是预发布(prerelease);
- tag 是严格的
vX.Y.Z语义化版本(三个数字,没有后缀)。
命中之后,更新目标就是这个 tag 对应的确切提交,而不是「tag 当时的分支头」。所以你不会在两次更新之间被动漂移到一半的新代码上。
这里有个历史细节值得知道:以前官方发布走的是日期版本号(v2026.9.24 这种),现在语义版本才有资格进 stable 通道——日期式标签永远不会被当成正式版。
频道存在哪里:不是全局配置,而是按安装存
频道记录写在 config.yaml 里,但不是一个全局的 update.channel:
update:
installs:
a4f3b2c1d0e9f8a7: # 安装 id(安装根路径的 sha16)
path: /home/u/.hermes/hermes-agent
channel: stable
为什么要这么绕?因为同一个 ~/.hermes 常常被多个安装共用——宿主机上的 Hermes、Docker 里的 gateway、桌面版都挂载同一份配置。如果只有一个全局开关,你给开发用的 checkout 切到 canary,就会把桌面版的更新源一起翻掉。
想知道当前这个安装的 id 和路径:
hermes update --install-id
三个命令,别记混
| 命令 | 作用 |
|---|---|
hermes update |
按当前频道更新 |
hermes update --check |
只对比目标,不拉取、不装依赖、不重启 |
hermes update --plan |
打印更新计划:安装类型(git/Docker/Nix/apt)、要重启的每个服务及其 supervisor |
hermes update --set-channel stable |
把频道持久化到这个安装,本身不执行更新 |
hermes update --channel main |
只影响这一次运行,什么都不保存 |
hermes update --install-id |
打印本安装的身份与路径 |
最容易搞混的是 --set-channel 和 --channel:前者写进配置,后者用完就丢。想临时看一眼 main 上有什么,用 --channel main 就够了,不会改变以后的行为。
最容易踩的规则:默认只往前走
切换默认频道这件事,官方特意做得保守,规则是**「从未主动选过频道的安装,默认只向前移动」**:
- 如果你的 checkout 里已经包含了最新的那个正式版(比如之前一直跟 main、早就跑在前面),它留在原地,直到有更新的正式版发布才移动;
- 只有显式执行
--set-channel stable,才算你明确同意「对齐到那个确切 release 提交」——哪怕那意味着代码回退。
这条规则同时管着 --check、实际应用,以及桌面版和顶部提示条用的被动检测。所以你不会因为一次更新被悄悄降级,但也不会永远停在旧代码上。
另外两类安装不受影响:
- fork 和镜像保持 main。它们拿不到官方 release,没有 stable 可跟;
- 打包安装(MSIX、Store、bundled、electron-updater、外部托管)频道在构建时就烧进包里了,用户改不了。
桌面版:Settings → About 多了个开关
源码安装的桌面版,在 Settings → About → Updates 里多了一行 Update channel,可以在 Stable releases 和 Every commit 之间切。只有源码安装(posix-handoff / windows-handoff)会显示这一行,打包版永远看不到它,因为它们的频道是固定的。
交互上有个小细节做得挺对:
- main → stable 会先弹确认框,因为这一步可能把你带到更旧的代码;
- stable → main 直接切,因为它永远不会让你「回退」。
命令行和界面写的是同一份记录,所以在桌面版切了之后,hermes update --check 看到的状态是立刻一致的。
顺带修掉的三个坑
这次一起进来的还有几个配套修复,都挺实用:
1. 稳定版查询以前不带凭证,会撞上匿名限流。 旧代码用一个裸的 HTTP 请求去读 releases/latest,用的是 GitHub 每小时 60 次的匿名配额——哪怕你已经配了 GH_TOKEN 或登录了 gh。在共享出口 IP(办公室、CI、Docker)后面很容易用光,然后 403 被显示成一句 ✗ Could not resolve the stable source channel: No published stable release could be verified,看起来像「根本没有正式版」。现在查询会走 token 阶梯(环境变量 → gh auth token → 匿名),并且把限流如实报成限流,附上重置时间;匿名请求还会提示你设置 GITHUB_TOKEN。查不到时依然停在原地,不会动你的 checkout。
2. 浅克隆 / 离线 checkout 不会被往回拽。 如果你的浅克隆或离线副本已经比最新正式版新,更新不会再把它「修正」回那个 release。
3. 桌面版的 Node 依赖装不上,不再拖垮 TUI 和 Web UI。 这条对无头服务器特别有用:npm ci 是把 ui-tui、web 和 apps/desktop 合成一次装的,而只有桌面版会拉 node-pty、electron 这类需要编译器的原生模块。于是一台从没跑过桌面版、只有 g++ 9 的 Ubuntu 服务器(g++ 9 不认 -std=gnu++20)会让整次装依赖失败,TUI 和 Web UI 也就永远构建不出来,每次启动都报「source update could not be finished automatically (6 attempts)」。现在这种情况会自动重试一次,跳过桌面版只装 TUI 和 Web UI,然后把桌面版记成一笔待办:Desktop app build owed: desktop dependencies failed。同时它会提示一句很实用的出路:如果这台机器根本不跑桌面版,hermes uninstall --gui 可以把桌面版从 checkout 里摘掉,以后的更新就不用再为它装原生依赖了。
你该怎么选
- 想稳(默认,也是大多数人的选择):什么都不用做,继续
hermes update; - 想跟最前沿:
hermes update --set-channel main,恢复「每个提交都追」的老行为; - 只想临时看一眼:
hermes update --channel main --check,不改变长期配置; - 不确定自己在哪个频道:
hermes update --check的第一行会直接告诉你。
一条经验:如果你在自建的 fork 上做二次开发,注意官方 release 跟你无关,你的默认仍然是 main——这也是对的,因为 fork 的更新源应该由你自己决定。
小结
这次改动把「更新」从一个持续摄入的流,变成了一个有版本边界的台阶:只在正式版之间移动,默认不回退,频道按安装隔离,查询带上你自己的身份。对每天跑 hermes update 的人来说,唯一要记住的是三点:默认 stable、--set-channel 才会持久化、要用 main 得自己说。
想先了解更新流程的整体设计,可以看我们的 hermes update 命令参考、这篇讲优雅升级的旧文,以及磁盘被更新撑爆怎么救。当前正式版是 v0.21.6,刚装好还没跑起来的话,从安装指南开始。