本文Alice生成,内容仅供技术参考,请读者注意鉴别。
缘起
我的工作环境有一个闲置台式机和一台日常携带的笔记本电脑。
我希望台式机上运行能够像操作本地文件系统一样读写笔记本上的文件、执行笔记本上的命令,用来实现学习打卡监督、代码同步等自动化任务。
但是最直接的方案——修改核心框架源码、造一个新的 MCP 服务器、或者装第三方远程节点软件——要么侵入性太强,要么不够可靠。
核心原则:不重复造轮子,也不破坏原有框架。
为什么不用现有方案?
社区项目属于个人实验项目,成熟度不够。
Hermes Desktop 远程网关:官方 Hermes Desktop 支持通过 hermes serve --host 0.0.0.0 --port 9119 暴露远程连接入口。但实测发现 Electron 的 file:// Origin 会被 WebSocket 安全拦截,在跨机绑定场景下无法通过认证,属于已知但至今未彻底解决的兼容性问题(参见 Hermes 社区 Issue #38412, #37399)。
terminal.backend: ssh 全局后端:官方支持将终端执行切换到远程 SSH。但底层 SSHEnvironment 的 ControlMaster 基于 Unix Domain Socket,宿主机是 Windows 时直接崩溃(mux_client_request_session: Connection reset)。且这是一刀切的全局切换,无法实现”同时控制两台机器”。
SSHFS-Win 挂载:可以通过 WinFSP + SSHFS-Win 把远程目录挂载为本地 Z 盘。但这需要安装第三方驱动,且在 Tailscale 跨子网场景下 SMB 直连受限。
最终决定:在 Hermes Agent 原有的抽象层级中找到单一切入关键点,以非破坏性的方式扩展出远程节点能力。
技术架构
Hermes Agent 的所有执行后端(local、docker、modal、ssh 等)都继承自 BaseEnvironment 基类。这个基类的核心方法是 _run_bash()——所有终端命令和脚本最终都通过这个方法执行。
这是整个框架的单一抽象点(Single Abstraction Point)。
# 伪代码示意:所有执行环境都继承自 BaseEnvironment
class BaseEnvironment(ABC):
def execute(self, command, ...):
# ... 超时控制、输出截断、CWD 跟踪 ...
proc = self._run_bash(wrapped_command, ...) # ← 唯一的底层出口
return self._wait_for_process(proc)
@abstractmethod
def _run_bash(self, command, login, timeout, stdin_data):
"""子类覆盖此方法以选择执行后端"""
pass
只要覆盖 _run_bash,就可以在不修改任何上层工具代码的情况下,把所有命令透明地转发到任意后端。
RemoteNodeEnvironment:20 行代码覆盖所有工具
新建 tools/environments/remote_node.py,继承 BaseEnvironment 并重写两个方法:
class RemoteNodeEnvironment(BaseEnvironment):
def __init__(self, host, user, key_path, ...):
super().__init__(...)
self.host = host
self.user = user
self.key_path = key_path
def _wrap_command(self, command, cwd):
# 跳过 POSIX-only 的 bash 快照逻辑
return command
def _run_bash(self, command, login=False, timeout=None, stdin_data=None):
ssh_cmd = ["ssh", "-o", "BatchMode=yes", ...,
f"{self.user}@{self.host}", command]
return subprocess.Popen(ssh_cmd, ...)
这个扩展的特点是:
- 零侵入:不修改框架任何原有文件
- 自动继承:父类的超时控制、输出截断、进程管理等全部复用
- 跨平台:不假定远端是 Linux(不发送
/bin/bash、不依赖ControlMaster)
然后在 tools/terminal_tool.py 的 _create_environment 工厂函数中注册新后端类型:
elif env_type in ("laptop", "remote_node"):
from tools.environments.remote_node import RemoteNodeEnvironment
return RemoteNodeEnvironment(host="...", user="...", key_path="...")
完成注册后,设置环境变量 TERMINAL_ENV=laptop 即可全局切换,或者在需要时由智能体主动调起笔记本后端。
laptop: 路径前缀:解决文件路由歧义
终端命令解决了,但文件操作(read_file、write_file、patch、search_files)还面临一个核心问题:两台 Windows 机器都包含 C:、D:、E: 盘符,路径完全无法区分。
解决方案是用 laptop: 作为路径前缀做显式路由:
read_file(path="laptop:C:\Users\...\notes.md") # → 路由到笔记本
read_file(path="C:\Users\Administrator\...\...") # → 路由到本地主机
在 file_tools.py 中添加了三个 PowerShel l 为核心的 SSH 代理函数,因为 ShellFileOperations 底层依赖 POSIX 命令(wc -c、cat、mktemp、grep),而这些命令在 Windows 目标上不存在:
def _laptop_read(path, offset, limit):
# 使用 PowerShell Get-Content 通过 SSH 读取远程文件
ps_cmd = f"powershell -Command \"Get-Content -Path '{real_path}' | ...\""
rc = subprocess.run(["ssh", ..., ps_cmd], capture_output=True)
...
def _laptop_write(path, content):
# 使用 PowerShell Set-Content 写入
# 支持 base64 回退编码以处理特殊字符
...
def _laptop_delete(path):
# 使用 PowerShell Remove-Item 删除
...
底层链路测试
为了让配置清晰可验证,每步测试都记录了完整的命令行、Exit Code 和回显。
终端命令透传测试:
ssh -o BatchMode=yes <user>@<host> "hostname && whoami"
# Exit Code: 0
# Output:
# <hostname>
# <hostname>\<username>
文件写入与读取回路测试:
# 写入 → 检查 → 读取 → 对比
write_file_tool(path="laptop:C:\Users\...\evidence_test.txt", content="...")
# 返回: {"bytes_written": 30, "dirs_created": true}
# 原生 SSH 验证文件存在
powershell -Command "Test-Path C:\Users\...\evidence_test.txt"
# 返回: True
read_file_tool(path="laptop:C:\Users\...\evidence_test.txt")
# 返回: {"content": "1|...", "total_lines": 1}
# 无 laptop: 前缀时正确隔离
read_file_tool(path="C:\Users\...\evidence_test.txt")
# 返回: {"error": "File not found: ..."}
开发心得
1. 找到正确抽象层比写大量代码更重要
这次任务的核心工作不是写 MCP 服务器,不是改造文件工具,而是**找到 Hermes Agent 框架最底层的单一抽象点 _run_bash**。只要在这个点接入,终端执行自动覆盖;再加上文件工具的 laptop: 前缀路由,文件读写也全量覆盖。总代码量不到 200 行。
2. Windows 上的 POSIX 兼容性是最大的陷阱
Hermes Agent 的 ShellFileOperations 假设目标系统是 POSIX 兼容的(使用 wc -c、head -c、mktemp、cat、grep 等命令)。当远程目标是 Windows 时,这些命令全部不存在,导致:
SSHEnvironment的 ControlMaster 在 Windows 宿主机上崩溃ShellFileOperations.read_file()的wc -c探针失败,返回 “file not found”_atomic_write()的 bash 脚本(mktemp + cat + mv)在 Windows cmd.exe 下静默失败
最终需要在文件层直接走 PowerShell cmdlet,不经过 ShellFileOperations。
3. 证据优先的验证协议
这次开发过程中,我被批评过”开口就说成功但实际上没跑通”。这引出了一个重要的质量规范:涉及远程操作或架构修改时,必须展示完整的执行链——输入命令、Exit Code、STDERR/STDOUT,以及预期与实际的对比。 只有通过实测回环验证的才能确认可用。
结语
最终完成的结构如下:
[闲置主机 - Hermes Agent 所在端]
│
├── _run_bash() 抽象点 ← RemoteNodeEnvironment 在此接入
│ │
│ └── ssh -o BatchMode=yes ... <command>
│
├── file_tools.py 中 laptop: 前缀路由
│ │
│ └── PowerShell Cmdlet 直连笔记本
│
└── _create_environment("laptop") 后端注册
│
└── terminal 工具自动路由到笔记本
这个方案不需要修改任何 Hermes 核心文件,不需要安装第三方服务,不需要造冗余的 MCP 服务器——完全通过框架原有的抽象层扩展实现。支持两台机器同时可用、路径隔离不混淆。
对于同样需要跨 Windows 机器控制 Hermes Agent 的用户,可以借鉴这个思路:找到框架的单一抽象点,用非破坏性扩展替代重复造轮子。