01
四层职责
Shared 共享契约,Preload 共享能力但不共享权限
Shared 只能放无 Electron/UI 依赖的类型与 provider 规则;Renderer 负责视图和本地交互状态;Preload 把 IPC 包装成 window.cth;Main 是唯一可信执行层。
交互图解
Electron 权限分层
01
RendererReact · Zustand · Pixi · xterm · Monaco
↓02
Preload / window.cthcontextBridge · typed IPC capabilities
↓03
Main ProcessPTY · FS · Git · SQLite · Hive · network
↓04
OS 与外部进程node-pty · git/gh · CLI agents · filesystem
02
为什么需要 Preload
页面不能直接 import Node,也不能拿到任意 IPC
如果把 ipcRenderer 或 fs 暴露给页面,任何 renderer 注入都能升级为本地文件/进程权限。当前 bridge 只暴露命名方法,Main 再做类型、路径、scheme 与状态校验。
- 01进程:pty:spawn / write / resize / kill 全部在 Main。
- 02文件:fs:* IPC 调用 main/fs.ts 的 safeJoin 等路径保护。
- 03Git/GitHub:child_process 调 git/gh,Renderer 只接收结构化结果。
- 04数据库:better-sqlite3 是 native、同步模块,只在 Main 打开。
03
事件返回
请求用 invoke/handle,流式事件用动态 channel
spawn/write 等请求用 Promise 风格 invoke/handle。PTY 输出与退出用 `pty:data:<id>`、`pty:exit:<id>` 推送;preload 返回取消订阅函数,renderer 的 terminalPool 全生命周期只订阅一次。
交互图解
Prompt 与终端输出往返
消息队列prompt 等待
安全投递门idle · quiet · no picker
pty:write × 2文本 → 140ms → Enter
CLI Agent模型 / tool loop
pty:data:idowner window
xterm + parser显示 + 状态
04
设计练习
把“给 Renderer 一个能力”改写成最小契约
好的 Preload API 以用户意图命名,参数窄、结果可序列化、错误可分类;Main 对路径、id 与枚举重新校验。
暴露 openProject(projectId) 比暴露 readFile(path) 更容易守住项目边界。