第 8 章:SSH、WSL 与远程运行时
1. 四种执行位置
| 位置 | 控制通道 | PTY 所有者 | Git/文件执行者 |
|---|---|---|---|
| 本机 native | Electron IPC / local call | local/daemon provider | main process |
| 本机 WSL | 本机 provider + wsl.exe | WSL shell/daemon | WSL-aware runner |
| Direct SSH | SSH multiplex | remote relay | remote relay |
| Paired runtime | encrypted WebSocket RPC | remote Orca runtime | remote runtime |
UI 希望它们“都像 workspace”,但实现不能抹掉差异。路径语法、Git 版本、shell、账户 home、延迟、断线语义都属于 host。
2. 先解析执行主机,再选择 provider
Repo/Workspace 保存 connectionId 与 executionHostId。请求进入 main/renderer 后,先解析:
- workspace 属于 local、SSH 还是 runtime;
- provider 当前是否注册且 generation 匹配;
- 若是 Windows local,是否实际路由到 WSL;
- 能力是否在该 host 可用;
- 是否允许 fallback。
fallback 不能跨越所有权。例如 SSH provider 暂时不可用时,绝不能在本机相同路径执行 git。
3. SSH 连接建立
Orca 支持 ~/.ssh/config alias、ssh -G 解析、key、agent、ProxyCommand、ForwardAgent 与系统 SSH fallback。
显式 Orca 设置优先于 config;config alias 的默认 host/22 则允许被 ssh -G 的 HostName/Port 填充。
关键文件:ssh-connection.ts、ssh-connection-utils.ts。
4. Relay 的部署与发现
连接成功后,Orca 在远端 home 下部署版本化 relay。部署需要处理:
- OS/arch 与包版本;
- 上传临时文件和原子替换;
- SFTP namespace 与 shell home 不一致;
- 多客户端同时部署锁;
- 旧 relay 兼容和滚动更新;
- 不可写 home 或缺失工具。
relay 不是长期开放端口;通常运行在 SSH channel 内,通过 framed protocol 提供服务。
5. Channel multiplexer
同一个 SSH 连接承载:
- control request/response;
- notifications;
- PTY output;
- Git 大结果流;
- keepalive 与 ACK。
SshChannelMultiplexer 为 request 分配 id,设置 timeout,并按 lane 调度写入。Frame decoder 有最大消息、每 turn 帧数、字节数与时间预算,防止一个大 frame 长时间堵住 Electron event loop。
高优先级交互不能被后台 PTY 洪流淹没;同时 ordinary request 的 unacked timestamp 数量有界。
6. SSH PTY provider
SshPtyProvider 实现与 local provider 相同的 IPtyProvider,但行为是 RPC 翻译:
spawn-> relaypty.spawn;write/resize-> notification;attach-> replay / snapshot / live stream;listProcesses-> 远端事实;shutdown-> 带 deadline 的远端事务。
返回的 app-facing PTY id 必须全局可路由,不能直接泄漏 relay 内部 handle。
7. Connection generation 与迟到事件
同一个 SSH target 断线重连后会产生新 generation。所有回调和 mutation expectation 都要带 generation:
target A generation 41 disconnects
target A generation 42 connects
late event from 41 arrives -> reject如果不做这层隔离,旧连接的 exit、port list 或 repo catalog 会覆盖新连接事实。
8. 断线为何不等于退出
Direct SSH 的断线只证明控制通道不可用,不能证明:
- remote PTY 已退出;
- Git worktree 已删除;
- Agent provider session 已结束;
- port forward 不再存在。
因此 UI 可以标 disconnected,metadata 可以保留,但不能释放唯一执行 claim。重连后先列举远端 process/lease,再决定 reattach 或清理。
9. Remote Agent Session Host Authority
最危险的场景:两个客户端同时恢复同一 Agent,会不会启动两个?Orca 的结构化流程使用 claim identity 和 operation replay。
claim 由 host-only signer 根据 agent/provider、worktree、machine/principal、runtime namespace 等生成摘要,不直接暴露原始 provider session id。
三个结果
- 无 owner:原子创建并登记;
- 有 live owner:adopt / attach;
- owner 未知或冲突:fail closed。
为什么 operation id 还不够
operation id 只能让“同一次请求重试”幂等;两个不同客户端可能有两个 operation id。claim identity 才能表达“它们想运行的是同一个逻辑 Agent 会话”。
完整设计见 remote-agent-session-host-authority.md。
10. 能力协商与安全 fallback
新客户端面对旧 host/daemon/relay 时,先探测 agent-session.host-authority.v1 等 capability。
只在以下情况回退 legacy:
- dispatch 前能力明确不可用;
- host 在任何 side effect 前返回稳定
legacy_required; - 旧 host 返回
method_not_found。
timeout、malformed response、未知错误都不能 fallback,因为请求可能已经 spawn。此时再走 legacy 会复制 Agent。
11. Paired runtime 与 direct SSH 的区别
Direct SSH
桌面 main 持有 SSH 连接,远端 relay 是能力代理。适合连接普通开发机。
Paired runtime
远端运行完整 Orca runtime,可在 headless serve 中维护 workspace graph、browser、automations 与 terminal model;客户端通过配对 token 和 WebSocket RPC 访问。
paired runtime 更像远程“Orca 主机”,direct SSH 更像把特定资源 provider 搬到远端。
12. WSL:本机中的另一个执行主机
WSL 的难点与 SSH 类似:
- Windows path 与 Linux path 转换;
- WSL distro 是 capability/cache scope;
git,gh, Agent CLI 可能只存在某一侧;- managed CLI/hook 需要注入 guest;
- sleep/wake 后 cwd 与 OSC 7 可能漂移。
Orca 不把 WSL 简化成 shell 参数,而是把 distro 贯穿 PTY、Git、hook 与账户 target。
13. 远程文件、Git 和端口
relay/runtime 还提供:
- 文件 list/read/write/watch;
- Git status/diff/worktree/branch;
- GitHub/GitLab CLI 的 host-aware 调用;
- detected ports 与 forwarding;
- remote browser/emulator 能力;
- skills 与 AI Vault 扫描。
每类结果都要打 host stamp,renderer 合并时不能让不同 host 上相同 repo id 的数据碰撞。
14. 源码导航
- SSH session:
src/main/ssh/ssh-relay-session.ts - multiplexer:
src/main/ssh/ssh-channel-multiplexer.ts - frame decoder:
src/main/ssh/relay-frame-decoder.ts - SSH PTY provider:
src/main/providers/ssh-pty-provider.ts - relay handlers:
src/relay/ - runtime environments:
src/shared/runtime-environments.ts