把 Pi 做成 Windows 应用:Electron 还是 Tauri?
古董级程序员,从大厂到创业公司,现在还在一线做 AI 相关开发。微信公众号「字与码」会继续更新工程实践、新技术判断,以及这些年踩过的坑。文章若对你有用,欢迎顺手关注。
Pi 是一个很有意思的 Coding Agent。它没有急着把所有功能都塞进核心,而是提供 extensions、skills、prompt templates、RPC 和 SDK,让使用者自己决定它最后长成什么样。
于是很自然会冒出一个产品问题:能不能把 Pi 封装成 Windows 独立应用,让用户像安装普通桌面软件一样使用,而不是先装 Node.js、再跑 npm、最后打开终端?
答案是可以。真正需要选择的不是“能不能封装”,而是桌面壳和 Pi Runtime 之间怎么连接。
我把可行方案收敛成两条:
- Electron + React,直接在 Node.js 进程里调用 Pi SDK。
- Tauri + React,把 Pi 做成 Node Sidecar,通过 RPC 通信。
两条路都能做到最终用户不预装 Pi。差别主要落在开发复杂度、进程隔离、安装体积和扩展兼容性上。
先说清楚:不是把 pi.dev 套进 WebView
pi.dev 是官网和文档站,不是一个等待套壳的在线 IDE。单纯用 WebView 打开它,只会得到一个网站窗口,不会得到能读本地仓库、执行命令、保存会话的 Coding Agent。
真正需要封装的是 @earendil-works/pi-coding-agent 提供的运行时能力。Pi 官方给了四种运行形态:
- 交互式终端;
- print / JSON 事件流;
- 基于 stdin/stdout 的 RPC;
- 可嵌入 Node.js 应用的 SDK。
这让桌面端有了两种很自然的边界。Electron 自带 Node.js,可以直接吃 SDK;Tauri 的 WebView 没有 Node.js Runtime,更适合把 Pi 放进独立 Sidecar。

Electron:Pi SDK 直接进主进程
Electron 同时带 Chromium 和 Node.js。它的主进程就是一个 Node.js 环境,可以加载 npm 包、访问文件系统,也可以创建额外的 Utility Process。官方的进程模型文档对这条边界写得很清楚:渲染进程负责界面,主进程和 Utility Process 承担系统能力与高风险工作。
开发时只需要把 Pi 作为项目依赖:
npm install @earendil-works/pi-coding-agent
然后在主进程或单独的 Agent Utility Process 里创建会话:
import {
createAgentSession,
ModelRuntime,
SessionManager,
} from "@earendil-works/pi-coding-agent"
const modelRuntime = await ModelRuntime.create()
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
modelRuntime,
})
await session.prompt("检查当前项目并给出测试建议")
这里有一个容易说错的点:开发项目安装了 Pi npm 包,不等于最终用户要先安装 Pi。
Electron 打包时会把 Node Runtime、应用代码和依赖一起放进安装包。用户拿到 .exe、NSIS 或 MSIX 安装包后,不需要执行 npm install,也不需要让全局 pi 出现在 PATH 里。Electron 官方的应用打包说明本来就是把运行时和应用资源一起交付。
我不会让 Pi 直接跑在渲染进程里。更稳妥的边界是:
React Renderer
│ 受限 IPC
Electron Main
│ MessagePort
Agent Utility Process
│
Pi SDK + workspace + provider
React 页面只拿到经过白名单约束的 IPC,例如发送 prompt、批准一次工具调用、读取会话摘要。API Key、文件读写、命令执行都留在 Agent 进程。这样即使前端出现 XSS,也不至于直接获得完整 Node 权限。
Electron 这条路最大的优点是直接。Pi SDK、界面和桌面运行时都在 TypeScript 生态里,事件对象不用再翻译成另一套协议,extensions 和动态 npm 依赖也更容易兼容。
代价同样明显:Chromium 和 Node.js 都要随应用发布,包会更大;如果 Agent 卡死或某个扩展崩溃,必须做好 Utility Process 的退出、重启和会话恢复,不能把所有工作都压在 Electron 主进程里。
Tauri:不要硬嵌 SDK,走 RPC Sidecar
Tauri 的前端也是 Web 技术,但窗口底层使用系统 WebView,核心进程是 Rust。它没有给 React 页面提供一个完整 Node.js Runtime,所以不能照搬 Electron 的 SDK 接法。
最稳妥的结构是:
React WebView
│ Tauri IPC
Rust Core
│ stdin / stdout
Pi Node Sidecar
│ pi --mode rpc
Git Bash + workspace + provider
Pi 的 RPC 模式就是为 IDE、自定义 UI 和非 Node 应用准备的。客户端向 stdin 写一行 JSON 命令,Pi 从 stdout 返回响应并持续推送事件。prompt、steer、follow-up、abort、模型切换、会话状态、工具执行和图片输入都已经有协议定义。
启动形式很简单:
pi --mode rpc --session-dir <app-data>/sessions
但桌面应用不能假设用户已经全局安装了 pi。正确做法是把 Node 应用打成自包含的 Sidecar,再由 Tauri 一起打包。Tauri 官方不仅支持在 externalBin 中嵌入外部二进制,还专门提供了将 Node.js 应用打成自包含 Sidecar的说明,目标就是让最终用户无需安装 Node.js。
这样一来,用户安装的仍然是一个应用:
MyPiDesktop.exe
resources/
pi-sidecar.exe
bash/
defaults/
Tauri 负责窗口、系统菜单、更新和安全权限;Sidecar 负责 Pi Runtime。Pi 崩溃时可以独立重启,不会带走窗口;前端也不需要接触模型密钥或文件系统。
RPC 有一个不太起眼但必须做对的细节:它采用严格的 LF 分隔 JSONL。官方明确提醒,不要用会把 Unicode 行分隔符也当换行的通用 line reader。客户端应维护字节缓冲区,只按 \n 切记录,必要时去掉末尾的 \r。
Tauri 的麻烦不在“能不能启动进程”,而在打包以后还要支持什么。把固定版本 Pi 编进单个 Sidecar 不难;如果还希望用户安装任意 Pi Package、动态 TypeScript extension 和原生 npm 依赖,单文件打包就可能和动态模块加载冲突。此时要么随 Sidecar 携带一个受控 Node Runtime,要么限制扩展范围,不能一边承诺任意插件,一边把运行环境裁得只剩一个不可变 exe。
用户不需要装 Pi,但 Bash 还在
无论 Electron 还是 Tauri,Pi 的 npm 包都可以随应用发布。Windows 上真正绕不过去的是 Bash。
Pi 官方的 Windows 配置说明列出了查找顺序:
settings.json指定的自定义 shell;C:\Program Files\Git\bin\bash.exe;- PATH 中其他可用的
bash.exe。
对于开发者工具,最省事的方案是首次启动检查 Git for Windows,缺少时给出安装引导。要做面向普通用户的“一次安装即可用”,则可以随应用携带 Portable Git Bash,并通过 Pi 设置指向固定路径。
这套方案不依赖 WSL。开发机、构建机和最终用户的电脑都只需要 Windows;Pi 使用应用自带的 Git Bash,用户不必启用 WSL、安装 Linux 发行版或完成额外初始化。
所以“无需预装”的完整含义应该是:
| 组件 | Electron + SDK | Tauri + Sidecar |
|---|---|---|
| 全局 Pi CLI | 不需要 | 不需要 |
| Node.js | Electron 自带 | 编进 Sidecar 或随应用携带 |
| Chromium | Electron 自带 | 使用系统 WebView2 |
| Bash | 检测 Git Bash 或随包携带 | 检测 Git Bash 或随包携带 |
| 模型凭证 | 用户仍需登录或配置 | 用户仍需登录或配置 |
Tauri 的壳通常更轻,但如果最终同时带上 Node Sidecar、Pi、Portable Git Bash 和模型相关资源,体积优势会比一个普通 CRUD 桌面应用小。不能只拿空项目安装包比较。
真正费时间的是安全边界
桌面窗口做出来并不难,难的是让一个能执行命令的 Agent 进入普通用户电脑后仍然可控。
Pi 的安全文档明确说明,它没有内置沙箱。Pi 以启动它的用户权限运行,能读写该用户可访问的文件,也能执行普通本地进程。Project Trust 只控制项目资源是否自动加载,不是命令和文件访问的隔离边界。
因此桌面版至少要补上这些能力:
- 工作区选择与路径白名单,默认不允许跨工作区写入;
- 命令、写文件、删除文件和外部网络访问的审批;
- 工具调用、退出码、diff 和用户决策的审计记录;
- API Key 写入 Windows Credential Manager,而不是 LocalStorage;
- extension 和 package 安装前展示来源、版本和能力;
- 无人值守任务运行在 Windows Sandbox、容器或远端受控环境。
Electron 和 Tauri 都不会自动替你解决这些问题。Tauri 的 capability 配置能缩小“前端可以调用哪些本地命令”,Electron 的 context isolation 和 IPC 白名单也能缩小渲染进程权限,但 Pi Runtime 自己仍然是一个拥有本地权限的进程。
Electron 和 Tauri 怎么选
如果目标是尽快做出一个可用原型,我会选 Electron。
原因不是 Electron 更先进,而是它和 Pi SDK 的边界最短:同一套 TypeScript 类型、同一个 npm 生态、最少的协议胶水。先把项目选择、聊天流、工具调用、diff 审批、模型切换和会话恢复跑通,比一开始优化几十 MB 安装体积更有价值。
如果目标是长期维护的正式 Windows 产品,我会认真考虑 Tauri + RPC Sidecar。
Sidecar 带来的进程边界很清楚:Rust Core 管应用生命周期,Pi Runtime 可以独立升级、崩溃和重启;前端只接触经过授权的命令。代价是多了一层 JSONL 协议、Sidecar 构建矩阵和 Node 动态生态兼容。
| 维度 | Electron + Pi SDK | Tauri + Pi RPC Sidecar |
|---|---|---|
| 接入速度 | 快 | 较慢 |
| Pi API 使用 | 直接调用 | RPC 映射 |
| 进程隔离 | 需要主动拆 Utility Process | Sidecar 天然独立 |
| 动态扩展兼容 | 更好 | 取决于 Sidecar 打包策略 |
| UI Runtime | 自带 Chromium | 系统 WebView2 |
| Rust 要求 | 无 | 有 |
| 故障恢复 | 自己管理 Agent 子进程 | 独立重启 Sidecar |
| 适合阶段 | MVP、快速验证 | 正式产品、长期维护 |
还有一条折中路线:即使第一版选 Electron,也不要让 React 组件直接依赖 Pi SDK 的所有类型。先定义一层自己的 AgentRuntime 接口:
interface AgentRuntime {
prompt(input: PromptInput): Promise<void>
abort(): Promise<void>
approve(requestId: string): Promise<void>
subscribe(listener: (event: AgentEvent) => void): () => void
getSession(): Promise<SessionSnapshot>
}
Electron 版实现 SdkAgentRuntime,以后如果需要迁移 Tauri,再实现 RpcAgentRuntime。这样迁移的是运行时适配层,而不是重写整套 UI。
Tauri 版本直接在 Windows 里开发
如果已经决定做 Tauri 版本,我建议直接在 Windows 原生环境里使用 Codex,不把 WSL 作为主开发环境。
这不是编辑器偏好,而是构建目标决定的。Tauri 的 Windows 开发环境需要 Microsoft C++ Build Tools、WebView2 和 Rust stable-msvc 工具链;Pi Sidecar 最后也必须生成 Windows .exe。Tauri 打包外部二进制时还会按目标 triple 查找文件,例如:
src-tauri/binaries/
pi-sidecar-x86_64-pc-windows-msvc.exe
在 WSL 里直接构建,默认得到的是 Linux 目标,不是可以放进 Windows 安装包的 Sidecar。即使再做交叉编译,后面仍要回到 Windows 验证 WebView2、文件路径、进程退出、Credential Manager、Portable Git Bash、NSIS / MSI 和代码签名,省不下真正麻烦的部分。
我会把项目放在 Windows 文件系统,例如 D:\work\pi-desktop,让 Codex 从 PowerShell 或 Windows Terminal 中工作:
pnpm install
pnpm tauri dev
pnpm test
pnpm tauri build
开发机安装 Node.js LTS、Rust stable-msvc、Visual Studio C++ Build Tools 和 WebView2。项目的构建脚本再把 Pi 编成自包含的 Windows Sidecar,并把 Portable Git Bash 一起放入安装包。最终交付物从开发到运行都不需要 WSL。
CI 也使用 Windows Runner 生成安装包。正式对外发布时再接入 Windows 代码签名;否则用户从浏览器下载安装包时,可能遇到 SmartScreen 的未受信任提示。Tauri 的 Windows 签名文档也把签名和 Windows 安装包构建放在同一条发布链路里。
我会怎么落第一版
第一版只做一条完整闭环:
选择项目
→ 登录或配置模型
→ 创建会话
→ 展示流式输出和工具调用
→ 审批危险动作
→ 查看 diff 与测试结果
→ 恢复历史会话
不急着做插件市场、多 Agent 调度和云同步。先验证普通开发者能否在不打开终端的情况下,理解 Pi 正在做什么,并在关键节点接管。
如果团队主要是 TypeScript,首版直接 Electron + Pi SDK。等产品边界稳定,再根据安装体积、内存、崩溃隔离和原生能力的真实数据决定是否迁移 Tauri。
如果一开始就明确要做长期维护的 Windows 客户端,团队也有 Rust 经验,那就直接在 Windows 原生环境采用 Tauri + Pi RPC Sidecar。但要把 Sidecar 当成正式子产品:独立版本、独立日志、健康检查、优雅退出、崩溃恢复和协议兼容都不能省。
最终用户不该关心里面有没有 Pi、Node 或 Bash。他只应该安装一个经过签名的应用,选择一个项目,然后清楚地看到 Agent 读了什么、改了什么、为什么需要一次授权。
这才是“把 Pi 做成 Windows 应用”真正要完成的工作。
参考资料
- Pi Documentation
- Pi SDK 与 Coding Agent 源码
- Pi RPC Mode
- Pi Windows Setup
- Pi Security
- Electron Process Model
- Electron Application Packaging
- Tauri Embedding External Binaries
- Tauri Node.js Sidecar
微信公众号
欢迎关注「字与码」
如果这篇文章对你有用,也欢迎在微信里继续关注后续更新。
X / Twitter
关注 @ax2_zicode
更即时的技术观察、新文章提醒和一些短想法会发在 X 上。