Windows 工作电脑上,multica 通过自定义运行时,连接局域网内 Linux 中运行的 Devin-cli 智能体
一、背景
Devin CLI 是 Cognition 推出的命令行编码智能体,支持 ACP(Agent Client Protocol)协议,可被任何 ACP-aware 客户端作为子进程驱动,通过 stdin/stdout 交换 JSON-RPC 消息。
multica 是一个多智能体运行时管理器,能在同一台机器上统一调度多种编码智能体(Claude、Codex、Copilot、Hermes 等)。它在 Windows 上以 daemon 形式常驻,启动时会扫描 PATH,确认至少存在一个它认识的 agent CLI,然后按 profile 配置 exec 对应的可执行文件。
实际工作中常见这样一种拓扑:
- 工作机是 Windows,multica 装在这里,方便用 Web UI 统一管理。
- Devin CLI 装在局域网内一台 Linux 上(开发环境、算力、凭证都在那边),不想在 Windows 上再装一份。
目标是让 Windows 上的 multica 把 Linux 上的 Devin 当成一个本地运行时来调度。
二、原理
核心难点是:multica daemon 只会用 exec.Command 在本机启动可执行文件,不会跨机调用;而 Devin CLI 在另一台 Linux 上。解决办法是利用 ACP 协议”JSON-RPC over stdio”的特性——stdio 可以被 SSH 透明转发,于是跨机调用在协议层等价于本地调用。
整条链路如下:
分三层解决三个问题:
1. 协议层:ACP over stdio 天然可转发
devin acp 子命令启动一个 ACP server,通过 stdin/stdout 收发 JSON-RPC 消息,不依赖任何 socket、端口、PTY。SSH 的默认行为就是把远端进程的 stdio 接到本地,所以 ssh user@host devin acp 在协议上和直接 devin acp 完全等价。multica 只看到本地一个子进程的 stdio,无需任何改造。
2. 安全层:SSH 强制命令限制密钥能力
daemon 是无人值守后台进程,不能用交互式密码登录,必须用密钥。但这把密钥如果泄露,攻击者就能 SSH 进 Linux 执行任意命令,风险不可接受。解决办法是在 Linux 的 ~/.ssh/authorized_keys 里给这把公钥加 command="..." 强制命令:
这样无论 SSH 客户端在命令行写什么,远端都只执行 devin acp,其它一律忽略。配合 no-pty,no-port-forwarding,... 等限制,这把钥匙被锁死成”只能启动 Devin ACP server”的单功能钥匙,泄露风险可控。
3. 发现层:借白名单名字过 daemon 预检
multica daemon 启动时有一个硬编码白名单预检:claude, codebuddy, codex, copilot, opencode, deveco, openclaw, hermes, pi, cursor-agent, kimi, kiro-cli, agy, qodercli, traecli, grok。PATH 上一个都不认识就直接退出,profile 配置再正确也救不回来。devin / devin-acp 不在白名单里。
解决办法是借 hermes 这个名字——它本身是个通用协议族名,不是某个具体产品,适合作为自定义运行时的载体。把 wrapper 文件命名为 hermes.cmd,profile 的 --command-name 也用 hermes,daemon 预检就能找到、放行。wrapper 内部内容不变,仍然是 SSH 转发到 devin acp。
三、完整步骤
Linux 侧(一次性准备)
1. 确认 sshd 运行且 devin 可用
2. 生成专用密钥对(无 passphrase)
3. 装入 authorized_keys,带强制命令与限制
4. 修复家目录权限(sshd StrictModes 要求)
sshd 默认开启 StrictModes,家目录若对 group/other 可写(如 777),会拒绝使用 authorized_keys。收窄到 755 即可,无需 sudo:
5. 验证强制命令生效
期望输出是 Devin ACP server 的启动日志(Starting ACP server),而不是 shadow 文件内容——说明强制命令把任意命令都拦截了。
6. 记录连接信息
- 主机:局域网 IP(如
192.168.9.101,优先)/ 公网 IP(如113.98.232.61) - 用户:
guotao - 端口:22
- 私钥文件:
/tmp/devin-multica(内容要传到 Windows)
Windows 侧(PowerShell,每步可复现)
第 1 步:保存私钥
第 2 步:锁紧私钥权限
Windows 自带 ssh.exe 和 Linux 一样严格,私钥若能被其他用户读会拒绝使用:
第 3 步:建 wrapper 目录并加入 PATH
第 4 步:创建 hermes.cmd wrapper
关键:文件名必须是 hermes(在 multica 白名单里),不是 devin-acp。 IP 用你实测能连通的那个,局域网优先。
要点:
- wrapper 里不带任何远端命令参数——Linux 侧的强制命令会自动把它变成
devin acp,无论 SSH 客户端发什么。 -o BatchMode=yes保证 daemon 无人值守时不会因任何交互提示卡死。-o ConnectTimeout=10防止网络问题时 daemon 永久挂起。
第 5 步:测试 wrapper 端到端
期望输出:
然后挂起等输入,Ctrl+C 退出。看到这个就说明 SSH + 强制命令 + ACP over stdio 全链路通了。
常见问题:
Permission denied (publickey)→ 第 2 步权限没锁紧,重做 icaclsConnection timed out→ 换 IP 重写 wrapperUNPROTECTED PRIVATE KEY FILE→ 同上,权限问题
第 6 步:把 $multica 永久化(可选但推荐)
第 7 步:创建 multica runtime profile
关键:--command-name hermes,不是 devin-acp。 daemon 预检按白名单扫 PATH,hermes 在白名单里,devin-acp 不在。
记下返回的 profile-id(下面用 代替)。
第 8 步:钉死绝对路径(绕开 daemon PATH 解析)
daemon 被桌面端拉起时继承的 PATH 比交互式 PowerShell 窄,用 set-path 给绝对路径最稳。单行写,不要用反引号续行(反引号后不能有任何字符,否则续行失效):
第 9 步:重启 daemon 并验证
daemon status 应显示 running,agents 列表里出现 hermes/Devin。
第 10 步:Web UI 确认
打开 Multica Web UI → Runtimes,应看到 Devin 自定义运行时显示「在线」。
四、踩过的两个坑
坑 1:PowerShell 反引号续行
反引号 ` 后面不能有任何字符(包括空格),否则续行不生效,命令被拆成两半,第二行被当成新语句报 Missing expression after unary operator '--'。长命令一律单行写,或用括号续行。
坑 2:command-name 必须是白名单里的名字
daemon 启动预检是硬编码白名单扫描,devin-acp 不在名单里,无论怎么 set-path 都过不了预检,daemon 直接退出 1。借 hermes 这个通用协议族名字,wrapper 内容不变,就能过预检。这是整个方案能成立的关键一招。
五、验证清单
| 项 | 命令 | 预期 |
|---|---|---|
| 私钥权限 | icacls "$env:USERPROFILE\.ssh\devin-multica" | 只有你自己一条 |
| wrapper 能跑 | & "$env:USERPROFILE\.local\bin\hermes.cmd" | ACP server 启动日志 |
| profile 已注册 | & $multica runtime profile list | COMMAND_NAME=hermes |
| daemon 运行 | & $multica daemon status | running |
| Web UI | Multica → Runtimes | Devin 显示在线 |
六、安全说明
- 专用密钥对独立于个人 SSH 密钥,泄露影响面只限于”启动 Devin ACP server”。
- 强制命令 +
no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding把这把钥匙锁成单功能钥匙,无法用于其它任何操作。 - 私钥在 Windows 上用 icacls 锁到仅本人可读,避免被同机其它用户盗用。
- 通信走 SSH 加密通道,ACP 协议内容(含 Devin 凭证)不裸传。
- 如需进一步收紧,可在 Linux 侧用
iptables/ufw限制 22 端口来源 IP 为 Windows 工作机的固定地址。