Skip to content

第 2 章:多进程架构与所有权边界 ​

目标:画清 Electron、daemon、runtime、SSH relay 与移动客户端之间的边界,并理解为什么 Orca 需要多个“主进程”。

1. 总体拓扑 ​

这里至少有四个长期进程:renderer、Electron main、PTY daemon、SSH relay。开启 headless serve 或移动端配对后,runtime RPC 又成为独立的公共控制面。

2. Electron renderer:组合与交互层 ​

renderer 拥有:

  • 当前导航、modal、sidebar、tab group 和编辑器 view;
  • xterm.js 实例及可见时的低延迟渲染;
  • Zustand 中的客户端缓存和 optimistic state;
  • 用户手势与本地 UI 生命周期。

renderer 不拥有:

  • OS 进程、PTY 存活事实;
  • Git worktree 是否真实存在;
  • SSH 连接或远端文件系统;
  • Browser guest WebContents;
  • 持久化文件中的加密秘密。

入口 App.tsx 很大,因为它承担启动 hydration、事件订阅、session capture、快捷键和根布局。真正的领域操作大多下沉到 store slices 与 lib/。

3. Preload:安全与类型边界 ​

src/preload/index.ts 使用 contextBridge 暴露有限 API。它的角色不是业务层,而是:

  1. 把 ipcRenderer.invoke/on 包装成显式方法;
  2. 共享 src/shared 类型,减少 renderer/main 漂移;
  3. 把 Electron 对象挡在隔离上下文外;
  4. 对部分 payload 做归一化和 admission。

preload 文件集中且庞大是一种审计取舍:虽然不够“模块化漂亮”,但安全评审可以在一个表面看到 renderer 被允许调用什么。

典型调用链:

text
renderer action
  -> window.api.worktrees.create(args)
  -> ipcRenderer.invoke('worktrees:create', args)
  -> ipcMain.handle(...)
  -> result structured clone
  -> Zustand state update

4. Electron main:本机控制平面 ​

main 进程拥有所有高权限本机资源:

  • Store 与用户数据路径;
  • 本地 Git、文件系统 watcher、shell;
  • local/daemon/SSH PTY provider 的路由;
  • SSH connection 与 relay 部署;
  • BrowserView/WebContents、CDP 和下载;
  • 自动更新、系统通知、托盘、权限;
  • Agent hook HTTP server 和账户凭证服务。

src/main/index.ts 是 composition root:它创建服务、连接回调、安装进程级错误处理,并在窗口创建时注册 IPC。

这也是为什么 main 入口有大量 import:它不是把业务写在一个文件里,而是在这里完成依赖装配。

5. PTY daemon:为什么 main 之外还要有进程宿主 ​

如果 PTY 直接由 Electron main 的生命周期决定,renderer reload 尚可恢复,但整个 app 更新或 main 崩溃会杀死 Agent。daemon provider 的目标是把 PTY 存活从 UI 进程生命周期中拆出去。

provider contract 定义在 pty-provider-contract.ts。核心能力包括:

  • spawn / attach / shutdown;
  • write / resize / sendSignal;
  • serialize / revive / getBufferSnapshot;
  • onData / onReplay / onExit;
  • pauseProducer / resumeProducer;
  • Agent session claim、incarnation 与 liveness probe。

Electron main 仍是路由和策略所有者;daemon 是具体 PTY 的进程所有者。两者之间通过 session id、incarnation 和 snapshot 对齐。

6. OrcaRuntimeService:统一的主机语义 ​

OrcaRuntimeService 不是简单 RPC handler 集合。它把桌面 UI 之外也需要的主机能力统一起来:

  • 管理 terminal handle 与 PTY 映射;
  • 维护 main-owned headless terminal model;
  • 接收 renderer 发布的 window graph;
  • 投影 mobile session tabs;
  • 暴露文件、Git、browser、automation 与 orchestration 方法;
  • 裁决多客户端对 tab/terminal 的修改;
  • 维护 Agent session host authority。

换句话说,Electron IPC 面向“同一桌面 app 的 renderer”,runtime RPC 面向“任何经过认证的客户端”。二者可能调用相同底层服务,但信任边界不同。

7. Runtime RPC:CLI、手机和 Web 的共同入口 ​

runtime-rpc.ts 提供:

  • 本地 socket / WebSocket server;
  • pairing offer 与 device scope;
  • request allowlist;
  • keepalive 与 long poll 配额;
  • 协议版本、能力与最小兼容版本;
  • terminal 二进制 stream 的升级通道。

RPC 方法按领域拆在 src/main/runtime/rpc/methods/,如 terminal.ts、worktree.ts、files.ts、browser-* 和 orchestration-*。

移动设备拿到的 scope 与 runtime 客户端不同,方法 allowlist 也不同。这阻止一个只应观察/操控 session 的设备调用主机管理接口。

8. SSH relay:把执行能力送到远端 ​

SSH 路径由三层组成:

relay 不是把任意 shell 暴露给 renderer。main 的 SshPtyProvider、SSH Git provider 等把本地 provider contract 翻译成 relay 方法,renderer 仍只看到统一语义。

9. 所有权矩阵 ​

资源renderermain/runtimedaemon/relay客户端可否声明
UI active tab主镜像/持久化无可以提交意图
tab / leaf 布局乐观编辑host 裁决成员关系无受 revision 约束
PTY 进程view路由/记录物理所有者不可以
终端快照view cacheheadless modelprovider snapshot只可请求
Git worktree显示操作协调实际执行主机只可请求
Agent 状态显示hook 权威与投影Agent/hook 产生不可以
browser guestUI frameBrowserManager执行主机通过命令操作
secrets不可见或脱敏safeStorage/服务必要时使用不可以回传明文

10. 事件与请求的两条方向 ​

请求:客户端发起,主机裁决 ​

create, send, resize, remove 等都有明确响应,常带 operation id 或 generation。

事件:资源侧产生,客户端接收 ​

PTY output、exit、hook status、file watcher、Git refresh、browser navigation 等由 main/runtime 主动推送。renderer 的 useIpcEvents.ts 汇聚这些事件,再交给 store action。

一个常见 bug 源是把事件当成请求的“附带结果”。例如 PTY 可能在 spawn 响应返回前已经 exit;Orca 必须在 provider 层把同一批 transport 中的 exit 与响应顺序结算。

11. 安全边界 ​

Orca 不是浏览器沙箱应用,它会执行用户选择的 Agent 和 shell。但仍有清晰边界:

  • renderer 只能通过 preload allowlist;
  • browser guest 与 app UI WebContents 身份分离;
  • plugin panel 有 CSP、token、消息大小和速率限制;
  • RPC 有 device scope 和方法 allowlist;
  • hook 事件绑定 launch token / pane identity;
  • JSON、terminal frame、文件路径都有尺寸或结构上限;
  • 远程未知状态不会被解释为“资源已死”。

12. 源码导航 ​