15 · Electron 桌面端
主进程如何监督 Server、管理窗口与 IPC,并把 SSH、WSL、更新和预览能力交给共享 UI。
桌面端是一个宿主,不是另一套后端
Electron 应用包含至少三个执行域:
- Main process:窗口、协议、更新、secret storage、SSH/WSL、backend supervisor;
- Preload:将严格筛选的 IPC API 暴露给 renderer;
- Renderer:复用
apps/web的 React 控制面,通过 hash router 和 platform adapter 使用桌面能力。
此外 main process 会启动一个或多个 T3 Server backend。Server 仍是 project/thread/Agent 的权威,main 只拥有“怎样运行并连接它”。
启动阶段与 Electron 约束
main.ts 进入 DesktopApp Effect runtime。
DesktopEarlyElectronStartup / DesktopPreReadyPlatform 处理必须在 app.ready 前设置的协议、命令行和平台行为;
之后才创建窗口、菜单、IPC 和 backend。
Electron 有不少“调用时机就是 API 契约”的能力,例如 custom scheme privilege 必须在 ready 前注册。把早期阶段拆出并测试,
比在一个 app.whenReady().then(...) 中混合所有初始化更可靠。
DesktopBackendManager
DesktopBackendManager.ts
负责单个 backend 的配置、启动、readiness、状态流、重启和停止;DesktopBackendPool.ts 管理多个环境/后端。
一个 backend 不是 spawn 成功就 ready。Manager 需要:
- 选择可执行入口、端口、userdata 与 auth 配置;
- 捕获 stdout/stderr 与 exit;
- 等待健康/readiness;
- 对预期 shutdown 与 crash 区分;
- 避免旧进程迟到事件覆盖新 generation 状态;
- 在窗口仍活着时按策略重启;
- App 退出时停止所有 backend。
这与客户端 connection supervisor 类似但不是同一个职责:BackendManager 管进程,supervisor 管到 endpoint 的网络 session。
Local environment auth
Renderer 即使和 main 在同一应用,也通过真实 RPC 连接 backend。DesktopLocalEnvironmentAuth.ts 为本地环境提供凭据/会话,
而不是因为“同机”就关闭鉴权。这样同一 backend 暴露给局域网/Tailscale 时不必切换成另一套安全模型。
敏感材料通过 Electron safe storage 或平台 secret backend 保存;Linux 有单独 fallback/实现。Preload 不应把长期 secret 原样暴露给 renderer, 只提供获得已授权 endpoint/session 所需的窄方法。
IPC 的边界
DesktopIpc.ts、
DesktopIpcHandlers.ts 和 channels.ts 定义 renderer 可调用的桌面操作。原则上:
- 业务数据走 Server RPC;
- 只有 Electron 特权能力走 IPC;
- preload 验证/限制参数,不暴露
ipcRenderer全对象; - handler 返回可序列化、可辨别的错误;
- renderer 通过 platform capability 判断功能是否存在。
如果项目/线程也走 IPC,浏览器和桌面会形成两套协议;如果任意文件/命令执行都暴露给 renderer,XSS 会升级为本机代码执行。
SSH 与 WSL 执行环境
DesktopSshEnvironment.ts 能启动/发现远端 T3 Server,并把它作为 execution environment 加入连接目录。密码/host-key 等交互通过
DesktopSshPasswordPrompts.ts 桥接到受控 UI。DesktopWslBackend.ts / DesktopWslEnvironment.ts 处理 Windows 路径、发行版和进程边界。
这两种能力再次验证“environment identity 与 launch/access method 分离”:
- SSH 是桌面替用户启动/连接远端 Server 的方式;
- WSL 是 Windows 上的另一个执行域;
- 一旦 endpoint 可用,renderer 仍使用同一个 client-runtime/RPC,不需要 SSH 专用聊天状态。
Custom protocol 与深链
ElectronProtocol.ts 和平台 URL handler 支持 t3code:// 等深链。处理流程需要:
- 单实例锁与第二实例参数转发;
- 在窗口未 ready 时暂存 URL;
- 验证 scheme/host/path,不把任意 URL 当命令;
- 路由到 connect/pair/thread 页面;
- macOS、Windows、Linux 启动参数差异。
深链经常包含配对信息,敏感 token 应放 URL fragment 或短期 code,并尽快兑换/清理。
更新状态机
Electron updater 不只是“检查版本”按钮。updates/updateMachine.ts 对 checking、available、downloading、ready、error、install 等状态建模,
updateChannels.ts 区分 channel,releaseNotes.ts 解析展示内容。更新必须协调:
- 当前 Agent/terminal 是否还在运行;
- 下载失败是否可重试;
- renderer 与内嵌 Server 版本是否兼容;
- 安装重启前能否优雅关闭 backend;
- 平台签名与安装器差异。
Privileged preview
桌面预览使用 Electron BrowserView/webContents 级能力,preview/Manager.ts、BrowserSession.ts、preload 与元素拾取 runtime
组成隔离边界。Web 页面只接收被挑选的 DOM metadata/截图/状态,不直接获得任意 guest page Node 能力。
对不可信本地开发站点,应禁用 Node integration、启用 context isolation,并严格限制 navigation/new-window 和 preload surface。
Shutdown
DesktopShutdown.ts / lifecycle 服务协调窗口、backend、preview、IPC 和更新。正确 shutdown 顺序要阻止新动作,再停止子服务,
最后退出 Electron;如果直接 app.quit() 后期待所有 finalizer 自行赶上,可能留下 Agent/PTY/SSH child process。
源码锚点
可迁移经验
- Electron main 管“进程和平台”,Server 管“业务和副作用”;不要因同包发布就抹掉边界。
- 后端进程 readiness 与网络连接 supervision 是两个状态机。
- 同机连接仍鉴权,renderer 不应因本地身份获得隐式超级权限。
- IPC 只暴露平台特权;可复用业务统一走网络契约。
- SSH/WSL 是环境启动与访问方式,连接成功后复用同一客户端运行时。