Windows 原生安装指南 — Early Beta
原生 Windows 支持仍处于 early beta 阶段。它能装、能跑,并通过了我们的 Windows footgun lint,但还没有像 Linux/macOS/WSL2 那样经历过同等规模的实战考验。请预期会有一些粗糙的边缘——尤其是子进程处理、路径细节和非 ASCII 控制台输出方面。遇到问题时请提交 issue,并附上复现步骤。如果你今天就需要一套久经考验的配置,请改用 WSL2 下的 Linux/macOS 安装器。
Hermes 在 Windows 10 和 Windows 11 上原生运行——不需要 WSL,不需要 Cygwin,也不需要 Docker。这一页是深入说明:哪些功能原生可用、哪些只能在 WSL 下用、安装器实际上做了什么,以及你可能需要调整的 Windows 专属开关。
如果你只是想安装,首页或安装页上的一行命令就够了。等到有什么让你困惑时再回来看这一页。
如果你更想要一个真正的 POSIX 环境(比如使用 Dashboard 内嵌终端、fork 语义、Linux 风格的文件监听等),请参见 Windows (WSL2) 指南。两者可以干净地共存:原生数据存放在 %LOCALAPPDATA%\hermes 下,WSL 数据存放在 ~/.hermes 下。
一行命令安装
打开 PowerShell(或 Windows Terminal),运行:
irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1 | iex
不需要管理员权限。安装器会把 Hermes 安装到 %LOCALAPPDATA%\hermes\,并把 hermes 加入你的 User PATH——安装完成后请打开一个新的终端窗口。
安装器选项(要传参数必须使用 scriptblock 形式):
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.ps1))) -NoVenv -SkipSetup -Branch main
| 参数 | 默认值 | 用途 |
|---|---|---|
-Branch | main | 克隆指定分支(用于测试 PR) |
-NoVenv | 关闭 | 跳过 venv 创建(高级用法——你自己管理 Python) |
-SkipSetup | 关闭 | 跳过安装后的 hermes setup 向导 |
-HermesHome | %LOCALAPPDATA%\hermes | 覆盖数据目录 |
-InstallDir | %LOCALAPPDATA%\hermes\hermes-agent | 覆盖代码目录 |
安装器到底做了什么
从上到下依次执行:
- 引导
uv—— Astral 的高性能 Python 管理器。安装到%USERPROFILE%\.local\bin。 - 通过
uv安装 Python 3.11。不需要预先安装任何 Python。 - 安装 Node.js 22(优先 winget,否则从可移植 Node tarball 解压到
%LOCALAPPDATA%\hermes\node)。用于 browser tool 和 WhatsApp 桥接。 - 安装可移植版 Git —— 如果 PATH 上已经有
git,安装器会直接复用;否则会从官方git-for-windowsrelease 下载一个精简、自包含的 PortableGit(约 45 MB)到%LOCALAPPDATA%\hermes\git。无需管理员权限,不写入 Windows 安装注册表,不会干扰机器上的其他东西。 - 克隆仓库到
%LOCALAPPDATA%\hermes\hermes-agent,并在内部创建 virtualenv。 - 分级
uv pip install—— 先尝试.[all],如果某个git+https依赖在被限流的 GitHub 上抽风,会回退到逐步缩小的依赖集合([messaging,dashboard,ext]→[messaging]→.)。避免"一个依赖出问题就掉到裸装"这种失败模式。 - 基于
.env自动安装消息平台 SDK —— 如果存在TELEGRAM_BOT_TOKEN/DISCORD_BOT_TOKEN/SLACK_BOT_TOKEN/SLACK_APP_TOKEN/WHATSAPP_ENABLED,会运行python -m ensurepip --upgrade和针对性的pip install调用,确保每个平台的 SDK 都能真正 import。 - 设置
HERMES_GIT_BASH_PATH指向解析到的bash.exe,这样 Hermes 在新的 shell 里也能确定性地找到 bash。 - 把
%LOCALAPPDATA%\hermes\bin加入 User PATH —— 在你打开新终端后,hermes命令就能直接使用。 - 运行
hermes setup—— 正常的首次运行向导(模型、provider、toolset)。可用-SkipSetup跳过。
能力矩阵
除了 Dashboard 内嵌终端面板之外,所有功能在 Windows 上都原生可用。
| 功能 | Windows 原生 | WSL2 |
|---|---|---|
CLI(hermes chat、hermes setup、hermes gateway、…) | ✓ | ✓ |
交互式 TUI(hermes --tui) | ✓ | ✓ |
| 消息 Gateway(Telegram、Discord、Slack、WhatsApp,15+ 平台) | ✓ | ✓ |
| Cron 调度器 | ✓ | ✓ |
| Browser tool(通过 Node 驱动 Chromium) | ✓ | ✓ |
| MCP servers(stdio 和 HTTP) | ✓ | ✓ |
| 本地 Ollama / LM Studio / llama-server | ✓ | ✓(通过 WSL 网络) |
| Web Dashboard(会话、任务、指标、配置) | ✓ | ✓ |
Dashboard /chat 内嵌终端面板 | ✗(需要 POSIX PTY) | ✓ |
| 登录时自启动 | ✓(schtasks) | ✓(systemd) |
Dashboard 的 /chat 标签页通过 POSIX PTY(ptyprocess)嵌入了一个真实终端。原生 Windows 没有等价原语;Python 的 pywinpty / Windows ConPTY 理论上可以工作,但需要一份独立实现——目前作为后续工作处理。Dashboard 的其他部分都原生可用——只有这一个标签页会显示"请改用 WSL2"的提示。
Hermes 在 Windows 上是怎么跑 shell 命令的
Hermes 的 terminal tool 通过 Git Bash 来执行命令,和 Claude Code 是同一套策略。这避免了重写每一个工具就能填平 POSIX-vs-Windows 的鸿沟。
bash.exe 的解析顺序:
- 设置了
HERMES_GIT_BASH_PATH环境变量时优先使用它。 %LOCALAPPDATA%\hermes\git\usr\bin\bash.exe(安装器自带的 PortableGit)。%LOCALAPPDATA%\hermes\git\bin\bash.exe(旧版 Git-for-Windows 布局)。- 系统的 Git-for-Windows 安装(
%ProgramFiles%\Git\bin\bash.exe等)。 - 兜底:MSYS2、Cygwin 或任何在 PATH 上的
bash.exe。
安装器会显式设置 HERMES_GIT_BASH_PATH,这样新启动的 PowerShell 不必重复探测。如果你想让 Hermes 用某个特定的 bash——比如系统的 Git Bash 或通过软链指向 WSL 内的 bash——可以覆盖这个变量。
坑点: MinGit 的目录结构和完整版 Git-for-Windows 不一样——bash 在 usr\bin\bash.exe,不是 bin\bash.exe。Hermes 两个位置都会检查。如果你手动解压 MinGit zip,记得选非 busybox 版本(MinGit-*-64-bit.zip,不是 MinGit-*-busybox*.zip)——busybox 构建只带 ash 而不是 bash,coreutils 也大多缺失。
Windows 上的 UTF-8 控制台
Python 在 Windows 上默认的 stdio 使用控制台的当前代码页(通常是 cp1252 或 cp437)。Hermes 的横幅、斜杠命令列表、tool feed、Rich 面板和 skill 描述里都包含 Unicode。如果不做处理,任何一处都可能报 UnicodeEncodeError: 'charmap' codec can't encode character…。
修复在 hermes_cli/stdio.py::configure_windows_stdio() 里完成,每个入口点(cli.py::main、hermes_cli/main.py::main、gateway/run.py::main)都会在很早期调用它。它会:
- 通过
kernel32.SetConsoleCP/SetConsoleOutputCP把控制台代码页切到 CP_UTF8(65001)。 - 把
sys.stdout/sys.stderr/sys.stdin重新配置为 UTF-8,且errors='replace'。 - 设置
PYTHONIOENCODING=utf-8和PYTHONUTF8=1(用setdefault,所以用户显式设置的值优先),让 Python 子进程也继承 UTF-8。 - 如果
EDITOR和VISUAL都没设置,则设EDITOR=notepad(参见下面的 Editor 一节)。
幂等。在非 Windows 上是 no-op。
关掉它: 在环境里设 HERMES_DISABLE_WINDOWS_UTF8=1 会回退到旧的 cp1252 stdio 路径。用于二分定位编码 bug 时有用;正常使用基本不需要。
编辑器(Ctrl-X Ctrl-E、/edit)
#21561 之前,在 Windows 上按 Ctrl-X Ctrl-E 或输入 /edit 会静默无效。prompt_toolkit 内置了一份 POSIX 绝对路径的兜底列表(/usr/bin/nano、/usr/bin/pico、/usr/bin/vi、…),在 Windows 上永远解析不到——哪怕装了完整的 Git for Windows。
Hermes 的 Windows stdio 垫片现在会把 EDITOR=notepad 设成默认值。Notepad 随每个 Windows 安装一起出货,并且能作为阻塞式编辑器使用——subprocess.call(["notepad", file]) 会一直阻塞到窗口关闭。
用户的覆盖仍然优先(在 setdefault 之前会先检查):
| 编辑器 | PowerShell 命令 |
|---|---|
| VS Code | $env:EDITOR = "code --wait" |
| Notepad++ | $env:EDITOR = "'C:\Program Files\Notepad++\notepad++.exe' -multiInst -nosession" |
| Neovim | $env:EDITOR = "nvim" |
| Helix | $env:EDITOR = "hx" |
VS Code 上的 --wait 参数至关重要——没有它,编辑器会立即返回,Hermes 拿到的是个空 buffer。
把它写进 PowerShell profile,让设置永久生效:
# 在 $PROFILE 里
$env:EDITOR = "code --wait"
或者在系统设置里把它设成 User 环境变量,这样每个新开的 shell 都能看到。
在 CLI 里用 Ctrl+Enter 换行
Windows Terminal 会把 Ctrl+Enter 作为一个独立的按键序列透传过来。Hermes 把它绑定为"插入换行",让你能在 CLI 里组合多行 prompt,而不必退回到 Esc-然后-Enter。在 Windows Terminal、VS Code 集成终端,以及任何遵守 VT 转义序列的现代 Windows 控制台里都能用。
在老式 cmd.exe 控制台里,Ctrl+Enter 会被折叠成普通 Enter——这种情况下用 Esc Enter,或者升级到 Windows Terminal(免费,Windows 11 默认安装)。
让 Gateway 在 Windows 登录时自动启动
hermes gateway install 在 Windows 上使用 Scheduled Tasks,并以 Startup 文件夹作为兜底——不需要管理员权限。
安装
hermes gateway install
幕后发生的事情:
schtasks /Create /SC ONLOGON /RL LIMITED /TN HermesGateway—— 注册一个登录时以标准(非提权)权限运行的任务。无 UAC 弹窗。- 如果 schtasks 被组策略禁用,会回退到把一个
start /min cmd.exe /d /c <wrapper>快捷方式写入%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup。效果一样,做法稍粗糙。 - 通过
pythonw.exe以 detached 方式启动 gateway——而不是python.exe。pythonw.exe没有附带控制台,因此能免疫来自兄弟进程的CTRL_C_EVENT广播(这是个真实问题,过去曾经在你 Ctrl+C 同进程组里任何东西时把 gateway 顺带杀死)。
启动时使用的 flag:DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW | CREATE_BREAKAWAY_FROM_JOB。
管理
hermes gateway status # 合并视图:schtasks + Startup 文件夹 + 运行中 PID
hermes gateway start # 立即启动计划任务
hermes gateway stop # SIGTERM 的等价(通过 psutil 调 TerminateProcess)
hermes gateway restart
hermes gateway uninstall # 移除 schtasks 项、Startup 快捷方式、pid 文件
hermes gateway status 是幂等的——连着调一千次也绝不会意外杀掉 gateway。(PR #21561 之前它确实会静默杀掉,因为 os.kill(pid, 0) 在 C 层和 CTRL_C_EVENT 撞到一起——如果你想了解前因后果,请看下面"进程管理内部细节"。)
为什么不用 Windows Service?
服务需要管理员权限来安装,并且把 gateway 的生命周期绑定到机器开机,而不是用户登录。典型的 Hermes 用户想要的是:登录 → gateway 可用,登出 → gateway 退出。Scheduled Tasks 正好做到了这一点,且不需要提权。如果你确实想要一个 service,可以手动用 nssm 或 sc create——但你大概率并不需要。
数据目录布局
| 路径 | 内容 |
|---|---|
%LOCALAPPDATA%\hermes\hermes-agent\ | Git 检出 + venv。可以放心 Remove-Item -Recurse 然后重装。 |
%LOCALAPPDATA%\hermes\git\ | PortableGit(仅在安装器自动配置时存在)。 |
%LOCALAPPDATA%\hermes\node\ | 可移植 Node.js(仅在安装器自动配置时存在)。 |
%LOCALAPPDATA%\hermes\bin\ | hermes.cmd 垫片,已加入 User PATH。 |
%USERPROFILE%\.hermes\ | 你的配置、auth、skills、会话、日志。重装也不会动它。 |
这种切分是有意为之:%LOCALAPPDATA%\hermes 是可丢弃的基础设施(你可以整个删掉,一行命令再装回来)。%USERPROFILE%\.hermes 才是你的数据——配置、记忆、技能、会话历史——它的形状和 Linux 安装完全一致。把它在多台机器之间镜像,你的 Hermes 就跟着你走。
覆盖 HERMES_HOME: 设置该环境变量指向其他数据目录。和 Linux 上行为相同。
Browser tool
Browser tool 通过 agent-browser(一个 Node helper)驱动 Chromium。在 Windows 上:
- 安装器通过 npm 把
agent-browser加到 PATH。 shutil.which("agent-browser", path=...)会自动选到.cmd垫片——CreateProcessW无法直接执行没有扩展名的 shebang 脚本,所以 Hermes 总是解析到.CMDwrapper。不要手动调用 shebang 脚本本身,永远走.cmd。- Playwright Chromium 会在第一次运行时自动安装(
npx playwright install chromium)。如果安装失败,hermes doctor会把它报出来并给出修复提示。
在 Windows 上运行 Hermes —— 实践要点
安装后的 PATH
安装器通过 [Environment]::SetEnvironmentVariable 把 %LOCALAPPDATA%\hermes\bin 加到了你的 User PATH。已经打开的终端拿不到这个变化——安装完成后请新开一个 PowerShell 窗口(或 Windows Terminal 标签页)。是关掉重开,而不是手动 $env:PATH += …,除非你清楚自己在做什么。
验证:
Get-Command hermes # 应该输出 C:\Users\<你>\AppData\Local\hermes\bin\hermes.cmd
hermes --version
环境变量
Hermes 同时尊重 $env:X(进程级)和 User 环境变量(永久级,在系统属性 → 环境变量里设置)。把 API key 放到 %USERPROFILE%\.hermes\.env 里是常规做法——和 Linux 一致:
OPENROUTER_API_KEY=sk-or-...
TELEGRAM_BOT_TOKEN=...
不要把密钥放到 User 环境变量里,除非你确实希望每个 Windows 进程都能看到它(这通常不是你想要的)。
Windows 专属环境变量
这些只对 Windows 原生安装生效:
| 变量 | 作用 |
|---|---|
HERMES_GIT_BASH_PATH | 覆盖 bash.exe 的解析。可以指向任意 bash——完整 Git-for-Windows、通过软链指向 WSL bash、MSYS2、Cygwin。安装器会自动设置。 |
HERMES_DISABLE_WINDOWS_UTF8 | 设为 1 禁用 UTF-8 stdio 垫片,回退到 locale 代码页。用于二分定位编码 bug。 |
EDITOR / VISUAL | /edit 和 Ctrl-X Ctrl-E 使用的编辑器。两者都未设置时 Hermes 默认用 notepad。 |
卸载
在 PowerShell 里:
hermes uninstall
这是干净路径——会移除 schtasks 项、Startup 文件夹快捷方式、hermes.cmd 垫片,删除 %LOCALAPPDATA%\hermes\hermes-agent\,并精简 User PATH。它不会动 %USERPROFILE%\.hermes\(你的配置、auth、skills、会话、日志),方便你之后重装。
要全部清干净:
hermes uninstall
Remove-Item -Recurse -Force "$env:USERPROFILE\.hermes"
Remove-Item -Recurse -Force "$env:LOCALAPPDATA\hermes"
hermes uninstall 子命令也能处理 schtasks 任务名不一样的情况(老版本可能注册了不同的名字)——它按安装路径而不是按硬编码任务名来搜。
进程管理内部细节
这部分是背景资料——除非你在排查"它把自己杀了"这种诡异问题,否则可以跳过。
在 Linux 和 macOS 上,POSIX 习惯用法 os.kill(pid, 0) 是一个 no-op 权限检查:"这个 PID 还活着吗?我能向它发信号吗?"在 Windows 上,Python 的 os.kill 会把 sig=0 映射到 CTRL_C_EVENT——它们在整数值 0 上撞车——并通过 GenerateConsoleCtrlEvent(0, pid) 路由出去,这个调用会向整个包含目标 PID 的控制台进程组广播 Ctrl+C。这就是 bpo-14484,从 2012 年就开着。它不会被修复,因为修了会破坏依赖当前行为的脚本。
后果:任何在 Windows 上通过 os.kill(pid, 0) 来"检查这个 PID 是否还活着"的代码路径,实际上都在静默杀掉目标。Hermes 把所有这样的位置(11 个文件里的 14 处)都迁移到了 gateway.status._pid_exists(),这个函数底层用的是 psutil.pid_exists()(在 Windows 上又会用 OpenProcess + GetExitCodeProcess——不发信号)。如果你写插件或补丁,请直接用 psutil.pid_exists() 或 gateway.status._pid_exists()——绝不要用 os.kill(pid, 0)。
scripts/check-windows-footguns.py 在 CI 里把这条规则强制执行:任何新的 os.kill(pid, 0) 调用都会让 Windows footguns (blocking) 检查失败,除非该行带 # windows-footgun: ok — <reason> 标记。
常见坑
安装完立刻报 hermes: command not found。
打开一个新的 PowerShell 窗口。安装器把 %LOCALAPPDATA%\hermes\bin 加到了 User PATH,但已有的 shell 需要重启才能拿到新值。在此期间你可以用 & "$env:LOCALAPPDATA\hermes\bin\hermes.cmd" 直接执行。
运行某个工具时报 WinError 193: %1 is not a valid Win32 application。
你触发了一次绕过 .cmd 垫片的 shebang 脚本调用。Hermes 通过 shutil.which(cmd, path=local_bin) 解析命令,这样 PATHEXT 才能选到 .CMD——如果你是通过硬编码路径调工具,请改用 .cmd 变体(比如 npx.cmd 而不是 npx)。
[scriptblock]::Create(...) 报 The assignment expression is not valid。
你下载的 install.ps1 带了 UTF-8 BOM。irm | iex 形式会自动剥掉 BOM;[scriptblock]::Create((irm ...)) 不会。改用简单的 irm | iex 形式重跑,或者手动下载脚本并用 [IO.File]::WriteAllText($path, $text, (New-Object Text.UTF8Encoding $false)) 保存为不带 BOM 的版本。
重启之后 Gateway 跑不起来。
看一眼 hermes gateway status——它会把 schtasks 项、Startup 文件夹快捷方式(如果有)和实时 PID 合并展示。如果 schtasks 已注册但没在跑,可能是组策略禁掉了 ONLOGON 触发器。运行 schtasks /Query /TN HermesGateway /V /FO LIST 看任务的失败原因,或者卸载后用 HERMES_GATEWAY_FORCE_STARTUP=1 重装回退到 Startup 文件夹路径。
设了 $env:EDITOR 之后 /edit 还是没反应。
你只是设到了当前进程;关掉再开一个 shell,或者在系统属性 → 环境变量里以 User scope 设置。在新 PowerShell 窗口里用 echo $env:EDITOR 验证。
Browser tool 起得来,但工具调用超时。
Chromium 在第一次运行时会自动安装。如果安装失败(GitHub 限流、Playwright CDN 抽风),运行 hermes doctor——它会指出缺失的 Chromium 并打印精确的 npx playwright install chromium 修复命令。
agent-browser 报奇怪的 Node 版本错误。
安装器在 %LOCALAPPDATA%\hermes\node 装了 Node 22,但你的 PATH 里可能有更老的系统 Node 18 排在前面。要么把 Hermes 的 node 目录在 PATH 里前移,要么在不需要其他 Node 的情况下卸载系统 Node。
中文 / 日语 / 阿拉伯字符在 CLI 里显示成 ?。
UTF-8 stdio 垫片没有生效。检查 HERMES_DISABLE_WINDOWS_UTF8 是否没有被设置(Get-ChildItem env:HERMES_DISABLE_WINDOWS_UTF8)。如果它确实是空的但仍然出现 ?,说明控制台宿主(极旧的 cmd.exe)根本不支持 UTF-8——切到 Windows Terminal。
Gateway 发不了 Telegram 图片——报 "BadRequest: payload contains invalid characters"。
这并非 Windows 特有,但有时候在 Windows 上首先暴露。通常是因为你的文件路径在 JSON body 里带了未转义的反斜杠。Telegram 应该收到的是 Hermes 标准化过的路径,而不是原始 Windows 路径——如果你在自定义插件里看到这个错误,请确认你传的是 Hermes 提供的路径,而不是来自用户输入的 str(Path(...))。
git pull 之后出现"在另一台机器上能跑"的编码诡异问题。
如果你用非 UTF-8 编辑器(老版本 Windows 上的 Notepad、某些中文输入法)在 Windows 上编辑过 Hermes 配置或 skill,文件可能被存成了带 BOM 的版本。Hermes 在大多数配置读取时会容忍 utf-8-sig,但折叠 YAML 标量(description: >)里的 BOM 会让 YAML 解析静默失败。把文件重新存成不带 BOM 的纯 UTF-8。
接下来去哪里
- 安装 —— 完整的安装页,覆盖 Linux/macOS/WSL2/Termux。
- Windows (WSL2) 指南 —— 如果你想要 POSIX 语义或 Dashboard 终端面板。
- CLI 参考 —— 每个
hermes子命令。 - FAQ —— 与 Windows 无关的常见问题。
- 消息 Gateway —— 在 Windows 上跑 Telegram/Discord/Slack。