记录范围
本文只覆盖三件事:在 NixOS 上安装 Hermes、构建并启动 Hermes Desktop、以及过程中 Hermes 自身遇到的问题(现象 → 根因 → 修法 → 客观验证)。
本次会话另行处理的若干与本主题无关的事项不在本文范围内。
环境
| 项 | 值 |
|---|---|
| 系统 | NixOS 26.05 (Yarara) 26.05.11045.774debe7a0d1,内核 7.2.8 |
| 机型 | GPD G1619-04(GPD Win Max 2),Ryzen 7 7840U,核显 Radeon 780M |
| Hermes 版本 | v0.21.5+7696.g35a2a31(2026.9.24),上游 commit 35a2a31fa6 |
| 安装目录 | /home/cat/.hermes/hermes-agent,来源 git clone https://github.com/NousResearch/hermes-agent.git |
| CLI 入口 | ~/.local/bin/hermes(70 字节 shim)→ $HERMES_HOME/hermes-agent/.hermes/bin/hermes |
| 网络前提 | 本机直连 GitHub / npm 不通,只能经 HTTP 代理出网(127.0.0.1:7890)—— 安装脚本要下载预编译 uv,构建 Desktop 要装 npm 依赖,两者都依赖这条通路 |
一、NixOS 上安装 Hermes 的完整步骤
1. NixOS 侧前置(声明式,写进 /etc/nixos/configuration.nix)
| 配置项 | 作用 | 为什么在 NixOS 上必须 |
|---|---|---|
programs.nix-ld.enable = true; | 让非 Nix 打包的预编译 ELF 可执行 | 关键:安装脚本会从 GitHub 下载固定版 uv(预编译二进制),Desktop 是预编译 Electron 二进制 —— 没有 nix-ld,这两者都直接因找不到动态库而失败 |
programs.nix-ld.libraries = pkgs.lib.mkAfter (with pkgs; [ … ]); | 给 nix-ld 补 Electron 运行时库 | 默认库集只有 systemd / gcc / openssl,跑 Electron 远远不够,见第二节 |
environment.localBinInPath = true; | 把 ~/.local/bin 加进 PATH | Hermes 的 CLI 包装器就装在那里,否则 hermes 命令找不到 |
users.users.<用户>.linger = true; | 用户级 systemd 常驻 | gateway 与定时任务依赖 user systemd 常驻运行 |
environment.systemPackages = with pkgs; [ git curl gcc gnumake python3 pkg-config ]; | 安装与构建前置 | 安装脚本的 prerequisites 阶段硬性检查 git 与 curl(缺失直接 fail);Python 依赖树与原生模块编译需要 gcc / gnumake / pkg-config |
virtualisation.docker.enable = true;(可选) | 容器化 MCP / 沙箱 | 只有用容器型 MCP 服务或 docker 插件时才需要 |
nixpkgs.config.allowUnfree = true;(可选) | 允许非自由软件包 | 依赖里含 unfree 时必需 |
nix.settings.experimental-features = [ "nix-command" "flakes" ];(可选) | 启用 flake 路线 | 只有走 nix run / Nix 模块安装时才需要 |
2. 跑官方安装脚本
Hermes 官方安装器是 scripts/install.sh,分 9 个阶段依次执行:
prerequisites repository venv python-deps config products setup gateway complete
# 实际用法(仓库已 clone 的情况下)
cd /home/cat/.hermes/hermes-agent
./scripts/install.sh
脚本按平台自动挑 uv 的预编译包(linux-x64 / linux-arm64,以及 glibc / musl 变体,本机固定 uv 0.12.3),这一步正是 nix-ld 必须开启的原因。脚本对 NixOS 没有任何专门分支(仓库里 grep 不到 nix-ld / nixos),所以上面第 1 步的前置全靠 NixOS 配置自己提供。
3. 验证安装
hermes --version # 期望:Hermes Agent v0.21.5+…(2026.9.24)
which hermes # 期望:/home/cat/.local/bin/hermes
ls "$HERMES_HOME" # config.yaml / cron / skills / hermes-agent 等
若报缺 libatomic.so.1,是 glibc 版 Node 的常见依赖 —— nix-ld 默认库里已含 stdenv.cc.cc,确认它没被写覆盖即可。
二、Hermes Desktop 在 NixOS 上起不来(nix-ld 缺库,三轮才修完)
Desktop 不是官方分发的产物,而是本机构建:hermes desktop 走 apps/desktop 的 electron-builder(Electron 40.10.2 / electron-builder 27.0.0-alpha.6),产物在 apps/desktop/release/linux-unpacked/。构建文档明确写:macOS 出 DMG/ZIP、Windows 出 MSIX、Linux 的官方发布支线是关闭的(本地 builder 可出 AppImage),Node / npm / uv 不需要自己预装(由 PM 准备)。
构建成功、启动却立刻因缺共享库退出,分三轮才修完,每轮暴露的层面不同:
| 轮次 | 报错 | 根因 | 修复 |
|---|---|---|---|
| 1 | libglib-2.0.so.0: cannot open shared object file(实测 27 个库全部 not found) | nix-ld 虽已启用,但默认库集只有 systemd / gcc / openssl,完全没有 GTK / X11 运行时库 | pkgs.lib.mkAfter 往 programs.nix-ld.libraries 追加 glib / nss / nspr / dbus / atk / at-spi2-atk / at-spi2-core / cups / cairo / gtk3 / pango / libX11 系列 / libxcb / libxkbcommon / alsa-lib / libsecret / libXtst |
| 2 | 只剩 libgbm.so.1 | libgbm 是 nixpkgs 的独立属性;mesa 的 outputs 是 out / opencl / spirv2dxil / cross_tools / debug,不含 libgbm | 把无用的 mesa 换成 libgbm(其 lib/libgbm.so.1 已核实存在) |
| 3 | 程序已能启动,但 Chromium / ANGLE 报 Could not dlopen libGL.so.1,GPU 进程反复退出 | libGL / libEGL 是运行时 dlopen 加载的,不是 DT_NEEDED 链接依赖 —— 任何链接期分析都查不到 | 补 libglvnd(一次覆盖 libGL.so.1 / libEGL.so.1 / libGLX.so.0 / libGLdispatch.so.0) |
关键坑(容易反复踩)
ldd对这个二进制不可信。它的 ELF 解释器是 nix-ld 的桩(PT_INTERP=/lib64/ld-linux-x86-64.so.2),ldd会把所有库都误报成 not found —— 第一轮就是因此误判。要查链接期依赖必须自己解析 ELF 的 DT_NEEDED(脚本见附录)。programs.nix-ld.libraries必须用mkAfter追加。nixos 的 nix-ld 模块对该选项是直接赋值,列表类型选项两个普通赋值会冲突报错;pkgs.lib.mkAfter (...)才是在默认集后面追加、不替换原有库。- dlopen 类缺口只能从运行日志发现。第 3 轮就是靠运行时那两段报错才定位的;静态扫描(包括自写的 ELF 解析器)结构上不可能发现它。
- NixOS 的 libglvnd 已被打过补丁:内建 vendor 目录包含
/run/opengl-driver/share/glvnd/egl_vendor.d;本机存在50_mesa.json且指向 mesa 的libEGL_mesa.so.0绝对路径 —— 所以补上 libglvnd 后不需要再手动设__EGL_VENDOR_LIBRARY_FILENAMES。
最终配置(本机已生效)
programs.nix-ld.libraries = pkgs.lib.mkAfter (with pkgs; [
glib nss nspr dbus atk at-spi2-atk at-spi2-core cups cairo gtk3 pango
libglvnd # libGL.so.1 / libEGL.so.1 —— 运行时 dlopen 的目标
libgbm # 独立属性,不属于 mesa
libdrm expat libxcb libxkbcommon alsa-lib libsecret libXtst
libx11 libxcomposite libxdamage libxext libxfixes libxrandr libxcursor libxi
]);
验证方法(三层,缺一不可)
# 1) 链接期依赖:自写 ELF DT_NEEDED 解析器(ldd 不可信)
python3 /home/cat/.hermes/cache/scratch/elf-needs.py # 期望:仍然缺的 soname: 0 个
# 2) nix-ld 库目录是否到位(条目数会从 124 涨到 437)
ls /run/current-system/sw/share/nix-ld/lib/ | grep -E '^lib(GL|EGL|GLX|gbm|glib)'
# 3) 运行时:应用自己写的状态文件才是硬证据
cat ~/.config/Hermes/linux-gpu-fallback.json # 期望 {"state":"ok"}
cat ~/.config/Hermes/window-state.json # 有几何尺寸 = 窗口真的被创建过
判定成功的客观证据(本次实测):三个 fallback 状态文件全部为 {"state":"ok"};window-state.json 记录了 1439×899 的窗口几何;journal 显示 app-org.chromium.Chromium-22845.scope: Consumed 8.018s CPU over 23.536s, 529.9M memory peak —— 程序正常运行了 23.5 秒,不是秒崩。
残留的无害日志:UnitExists: Unit app-org.chromium.Chromium-<pid>.scope was already loaded。原因是 Chromium 对自己的 pid 命名 scope 调了两次 StartTransientUnit,journal 里能看到该 scope 随后被正常创建并统计到 CPU / 内存,功能不受影响。
三、trilium MCP 在 NixOS 上不注入
现象:会话里 mcp__trilium__* 工具完全没有出现。根因在 config.yaml 的 mcp_servers 配置 —— command 与 args 都是 Windows 路径,在 NixOS 上进程起不来,工具自然不注入:
trilium:
command: C:/Users/cat/scoop/apps/nodejs/current/node.exe
args:
- C:/Users/cat/AppData/Local/hermes/trilium-mcp/dist/index.js
修法:把 command 与 args 改成 NixOS 上的实际路径(node 与 trilium-mcp 的实际位置),重启 Hermes 后确认工具重新出现。
临时兜底(MCP 不可用时的唯一授权路径:ETAPI 一次性 curl)。此处有个血泪点 —— ETAPI 的认证头不带 Bearer:
# 正确:Authorization: <token>
# 错误:Authorization: Bearer <token> → 稳定 401 NOT_AUTHENTICATED(看起来像令牌失效)
curl -H "Authorization: $TRILIUM_ETAPI_TOKEN" "$TRILIUM_ETAPI_URL/notes/<id>/content"
# 改标题没有 PUT /title 端点(0.63.7 返回 404 Router not found),用 PATCH:
curl -X PATCH -H "Authorization: $TRILIUM_ETAPI_TOKEN" -H "Content-Type: application/json" \
--data '{"title":"新标题"}' "$TRILIUM_ETAPI_URL/notes/<id>"
四、待办(Hermes 相关)
- 已完成:三轮补齐(glib/gtk3 系列 → libgbm → libglvnd),已写入
/etc/nixos/configuration.nix并 rebuild 生效 - 证据:三个 fallback 状态文件
{"state":"ok"},窗口几何 1439×899 被记录
- 已完成:三轮补齐(glib/gtk3 系列 → libgbm → libglvnd),已写入
- 动作:command 改本机 node 路径,args 改本机
trilium-mcp/dist/index.js实际位置 - 复核:重启 Hermes 后
mcp__trilium__*工具出现在工具列表里
- 动作:command 改本机 node 路径,args 改本机
- 现状:electron-builder 的 linux target 是 AppImage,但官方 Linux 发布支线关闭,本机
hermes desktop只跑本地构建产物
- 现状:electron-builder 的 linux target 是 AppImage,但官方 Linux 发布支线关闭,本机
附录:相关文件
| 路径 | 用途 |
|---|---|
| /etc/nixos/configuration.nix | Hermes 前置与 nix-ld libraries(第 143–150 行一带) |
| /etc/nixos/configuration.nix.bak-2026-10-06 | nix-ld 修改前的配置备份 |
| /home/cat/.hermes/cache/scratch/nixos-nixld-stage.py | 幂等生成 nix-ld 修复后的 configuration.nix(先剥旧块再注入) |
| /home/cat/.hermes/cache/scratch/elf-needs.py | 自写 ELF DT_NEEDED 解析器,一次算出 Electron bundle 的全部缺失 soname |
| ~/.config/Hermes/ | Desktop 自己的状态文件(linux-gpu-fallback.json / window-state.json 等) |
| /home/cat/.hermes/hermes-agent/scripts/install.sh | 官方安装脚本(9 阶段) |
| /home/cat/.hermes/hermes-agent/apps/desktop/BUILDING.md | Desktop 构建文档(平台产物与前置) |