第 4 章:启动、IPC 与状态同步
1. 启动不是一个 app.whenReady()
Orca 启动要同时恢复 UI、持久化数据、PTY、Agent 状态、SSH、runtime RPC 和窗口。粗略时序如下:
很多启动 bug 都来自 barrier 顺序错误:例如 renderer 尚未注册 serializer 就收到 PTY replay,或旧 session layout 覆盖刚刚由 host 退休的 terminal。
2. Main 的三段启动
2.1 Electron 就绪前
src/main/index.ts 在 app.ready 前处理:
- userData/profile 路径;
- 单实例锁与 packaged CLI 重定向;
- GPU fallback、网络兼容和进程错误 guard;
- dev parent / signal 生命周期;
- Windows ACL 与 shell PATH hydration。
这些操作必须早于窗口和 Store,因为它们会改变数据目录、命令行开关或进程退出策略。
2.2 服务构建
随后创建长期服务:
Store、Stats 与 usage store;OrcaRuntimeService、OrcaRuntimeRpcServer;- rate limit、account、automation、plugin 服务;
- Agent hook server、browser/emulator bridge;
- daemon/local/SSH PTY provider。
入口文件像依赖注入容器:构造对象,并把 runtime、store、window 与 callback 接起来。
2.3 首个窗口服务
窗口存在后才能绑定可信 webContentsId、菜单、剪贴板、UI IPC 和推送事件。因此 registerCoreHandlers 会更新当前可信 renderer id,并用 registered 避免 macOS 再次打开窗口时重复 ipcMain.handle()。
3. IPC handler 的分层
register-core-handlers.ts 是 IPC 目录。它注册约几十类 handler:
| 类别 | 示例 |
|---|---|
| 基础资源 | filesystem、session、settings、shell |
| 工作区 | repos、worktrees、workspace space、ports |
| Agent | agent hooks、accounts、skills、AI Vault、native chat |
| Provider | GitHub、GitLab、Linear、Jira、hosted review |
| 设备 | browser、emulator、speech、notifications |
| 平台 | runtime、runtime environments、plugins、updater |
IPC handler 通常做五件事:
- schema/shape 校验;
- 找到 Store 中的 repo/worktree/host;
- 选择本地、SSH 或 runtime provider;
- 执行资源操作;
- 更新持久化并向 renderer 广播。
这不是单纯 controller 层;它经常是“本机桌面客户端”的事务协调者。
4. Zustand store 如何组合
renderer 的 store/index.ts 将 40 多个 slice 合成 AppState:
export const useAppStore = create<AppState>()((...a) => ({
...createRepoSlice(...a),
...createWorktreeSlice(...a),
...createTerminalSlice(...a),
...createTabsSlice(...a),
...createUISlice(...a),
...createGitHubSlice(...a),
...createBrowserSlice(...a),
...createAgentStatusSlice(...a)
// ...
}))这种设计的优点是跨 slice 操作可以直接 get();代价是任何 selector 过宽都会造成 fanout。源码专门提供 listener census、selector benchmark 和 memory profile contributor,说明 store 规模本身已成为性能对象。
Slice 的三种状态
- 持久化设置:sidebar、theme、card fields;
- 资源镜像:repos、worktrees、agent status、rate limits;
- 瞬时协调状态:pending create、retry ledger、modal、optimistic tickets。
不要把第二类当作事实源,也不要把第三类持久化。
5. Hydration 是合并,不是覆盖
renderer 启动会读取:
- persisted UI/settings;
- repos/projects/groups/folder workspaces;
- worktree metadata 与实际 Git scan;
- workspace session(tab、layout、sleeping sessions);
- hook status snapshot;
- runtime environments 与 remote host catalog。
不同数据有不同权威:
| 数据 | 权威来源 | renderer 行为 |
|---|---|---|
| sidebar width | persisted UI | 直接恢复并可写回 |
| Git worktree 存在 | host Git scan | metadata 只做 fallback |
| live PTY | provider/runtime | 只 attach,不凭 session 生成 |
| terminal layout | session + host revision | 合并,不能复活 retired leaf |
| Agent status | hook server snapshot | 按 pane authority 接受 |
| SSH host | connection store | 离线保留但标 disconnected |
6. Renderer 事件汇聚
useIpcEvents.ts 订阅主进程事件,例如:
- PTY data / exit / restore marker;
- Agent status set/clear;
- worktree/head/base/remote branch 变化;
- file watcher、notifications、usage 与 update;
- SSH connection、ports、remote workspace;
- GitHub PR refresh、browser 与 emulator 状态。
事件处理的关键不是“收到后 setState”,而是 admission:
- generation 是否仍是当前连接;
- event 的 host/connection 是否与 workspace authority 一致;
- pane 是否已被关闭或替换;
- snapshot version 是否比本地新;
- optimistic mutation 是否仍拥有该字段。
7. Runtime graph:把 renderer 布局投影给 host
renderer 有完整 UI layout,但 runtime 需要知道哪些 tab/leaf 对外可见。RuntimeSyncWindowGraph 只传必要字段:
type RuntimeSyncWindowGraph = {
tabs: RuntimeSyncedTab[]
leaves: RuntimeSyncedLeaf[]
mobileSessionTabs?: RuntimeMobileSessionTabsSnapshot[]
}runtime 返回 graph status、epoch、authoritative window id 和 Agent orchestration context。rendererGraphEpoch 用来区分 reload 前后的发布。
这是一种“UI 描述布局,host 裁决资源”的双向同步:
- renderer 可以声明 tab 标题、顺序和 split;
- host 可以拒绝已退出 PTY 的 leaf 重新出现;
- headless 模式可在没有 renderer 时维护自己的 session snapshot;
- desktop window 恢复后再接管 presentation。
8. 持久化写入的节奏
主 Store 使用 debounce 避免每个 UI 小动作都写盘,同时提供同步 flush 给退出和 terminal retirement 等关键边界。
大致有三档:
- 普通 UI 更新:debounced save;
- session capture:周期性和 quit-time;
- 权威成员变化:同步写入后再对外发布。
第三档很重要。若 PTY exit 已从内存 snapshot 移除,但磁盘仍保留旧 leaf,进程崩溃后会把“幽灵终端”恢复回来。
9. 常见一致性模式
Generation
连接、scan、spawn、watcher 用递增 generation 丢弃迟到结果。
Operation ticket
字段级 optimistic 更新携带 ticket;只允许当前 ticket 回滚,防止早请求失败覆盖后请求成功。
Snapshot version / graph epoch
用于全量投影的单调顺序。
Incarnation
用于同一逻辑 PTY id 背后的物理进程替换。
Revision fence
host-owned terminal membership 的持久化修订,阻止 stale renderer resurrect。
10. 一次设置更新的完整链
以隐藏休眠 workspace 为例:
敏感或 main-owned 字段不会通过同一个 UI patch surface 暴露,避免 renderer 写入 telemetry ledger、secret 或 host revision。
11. 源码导航
- 启动入口:
src/main/index.ts - 启动模块:
src/main/startup/ - IPC 注册:
src/main/ipc/register-core-handlers.ts - preload:
src/preload/index.ts - store 合成:
src/renderer/src/store/index.ts - 根事件 hook:
src/renderer/src/hooks/useIpcEvents.ts - graph 同步:
src/renderer/src/runtime/sync-runtime-graph.ts