本文引用的文件
本仓库中的 ai-electron 是一个基于 ElectronEgg(ee-core、ee-bin)和 Electron 的桌面客户端工程。它同时包含:
ai-electron/frontend,使用 pnpm/Vite 构建。ai-electron/electron,入口为 TypeScript,最终由 ee-core/ElectronEgg 启动。ai-electron/cmd,由 electron-builder 消费,生成 Windows、macOS、Linux 安装包。ai-electron/package.json 中声明了 ee-core、electron-updater、electron-builder、better-sqlite3 重建工具等。该文档聚焦于“如何构建、如何打包、如何发布”,而不是完整业务代码分析。读者应能据此理解 dev、build、start、build-w/m/l 等命令的执行顺序,并知道在本地或 CI 中生成可分发的安装包。
graph TB
subgraph "ai-electron"
P["package.json<br/>版本、scripts、依赖"]
EE["electron/main.ts<br/>ElectronEgg 入口"]
CFG["electron/config/*<br/>默认配置、生产配置、窗口选项"]
CMD["cmd/*<br/>electron-builder 打包配置"]
OUT["out/*<br/>打包输出目录"]
FE_BIN["frontend/bin/*<br/>前端安装/构建/启动脚本"]
end
subgraph "前端"
FE_PKG["frontend/package.json<br/>pnpm + Vite"]
end
P --> EE
P --> CMD
P --> FE_BIN
EE --> CFG
CMD --> OUT
FE_BIN --> FE_PKG
图表来源
章节来源
ai-electron/package.json 中的 scripts 是客户端构建与发布的统一入口。核心命令如下:
| 命令 | 作用 | 执行顺序与关键点 |
|---|---|---|
dev |
开发模式运行客户端 | 调用 ee-bin dev,由 ElectronEgg 启动 Electron 主进程、加载 preload、渲染前端资源。 |
start |
启动已构建产物 | 调用 ee-bin start,直接启动打包后的主进程,不重新编译前端。 |
build |
完整构建并加密 | 依次执行:构建前端 → 构建 Electron → 对 ee-bin 进行字节码加密。 |
build-frontend |
仅构建前端 | 调用 ee-bin build --cmds=frontend,再通过 ee-bin move --flag=frontend_dist 移动前端构建产物。 |
build-electron |
仅构建 Electron | 调用 ee-bin build --cmds=electron,将 TypeScript 主进程编译为 JavaScript 并准备打包。 |
build-w |
构建 Windows 安装包 | 调用 ee-bin build --cmds=win64,最终由 electron-builder 生成 NSIS 安装包。 |
build-we |
构建 Windows 扩展目标 | 调用 ee-bin build --cmds=win_e,用于 Windows 特殊构建场景。 |
build-m |
构建 macOS ARM64 安装包 | 调用 ee-bin build --cmds=mac_arm64,对应 Apple Silicon 架构。 |
build-m-x86 |
构建 macOS x86_64 安装包 | 调用 ee-bin build --cmds=mac,对应 Intel Mac。 |
build-m-arm64 |
构建 macOS ARM64 安装包 | 与 build-m 等价,显式指定 mac_arm64。 |
build-l |
构建 Linux 安装包 | 调用 ee-bin build --cmds=linux,最终生成 deb 包。 |
re-sqlite |
重建 better-sqlite3 | 调用 electron-rebuild -f -w better-sqlite3,解决原生模块 ABI 不匹配问题。 |
icon |
图标处理 | 调用 ee-bin icon,属于 ElectronEgg 提供的辅助能力。 |
test / test:watch |
单元测试 | 使用 Vitest。 |
typecheck |
TypeScript 类型检查 | 使用 tsc --noEmi。 |
从依赖角度看,这些命令依赖以下外部组件:
cmd/builder*.json 生成安装包。generic provider 对接更新服务器。章节来源
前端目录下 frontend/bin 中的脚本主要用于本地开发体验,它们不是 Electron 打包的主路径,但有助于理解前端构建过程。
| 脚本 | 用途 | 行为 |
|---|---|---|
install.sh |
前端依赖安装 | 切换到前端根目录执行 pnpm install,再回到 bin。 |
build.sh |
前端构建 | 切换到前端根目录执行 pnpm build,再回到 bin。 |
startup.sh |
前端开发启动 | 切换到前端根目录执行 pnpm dev,再回到 bin。 |
install.bat |
Windows 前端依赖安装 | 切换盘符与目录后执行 npm run install,然后暂停。 |
build.bat |
Windows 前端构建 | 切换盘符与目录后执行 npm run build,然后暂停。 |
startup.bat |
Windows 前端开发启动 | 切换盘符与目录后执行 pnpm dev,然后暂停。 |
需要注意:Windows 批处理脚本中混用了 npm run install 和 pnpm dev;实际使用时应优先确保 Node 包管理器一致。推荐在 CI 和本地都固定使用 pnpm,避免锁文件和依赖解析差异。
在 Electron 主配置中,前端静态资源通过 mainServer 指向 /public/dist/index.html。也就是说:
frontend/dist。打包时,前端构建产物会被纳入 Electron 应用资源。
flowchart TD
Start["开始"] --> InstallFE["安装前端依赖"]
InstallFE --> BuildFE["前端构建 pnpm build"]
BuildFE --> Dist["产出 frontend/dist"]
Dist --> LoadMain["Electron 主进程加载 /public/dist/index.html"]
LoadMain --> AppReady["ElectronEgg 应用就绪"]
AppReady --> End["客户端运行"]
图表来源
章节来源
build 命令定义了完整的 Electron 客户端构建链:
flowchart TD
A["npm run build"] --> B["build-frontend<br/>ee-bin build --cmds=frontend"]
B --> C["move frontend_dist<br/>ee-bin move --flag=frontend_dist"]
C --> D["build-electron<br/>ee-bin build --cmds=electron"]
D --> E["encrypt<br/>ee-bin encrypt"]
E --> F["打包产物进入 out 目录"]
图表来源
执行顺序可以理解为:
ee-bin build --cmds=frontend 构建前端资源。ee-bin move --flag=frontend_dist 把前端构建产物移动到 Electron 应用期望的位置。ee-bin build --cmds=electron 编译主进程 TypeScript。ee-bin encrypt 对 ee-bin 相关代码进行加密,增强分发安全性。build-w、build-m、build-l 会触发 electron-builder,读取 cmd/builder*.json 生成安装包。Electron 入口由 package.json 的 main 字段指向 ./public/electron/main.js,而源码位于 electron/main.ts。启动流程大致为:
sequenceDiagram
participant OS as "操作系统"
participant EE as "ElectronEgg"
participant Main as "main.ts"
participant Life as "Lifecycle"
participant Win as "主窗口"
OS->>EE: 启动 Electron 应用
EE->>Main: 加载主进程入口
Main->>EE: new ElectronEgg()
Main->>Life: 注册 ready、electron-app-ready、window-ready、before-close
Main->>EE: register(preload)
Main->>EE: app.run()
EE->>Life: ready()
EE->>Life: electronAppReady()
EE->>Win: 创建主窗口
EE->>Life: windowReady()
图表来源
章节来源
Windows 打包配置定义在 cmd/builder.json 中,目标为 NSIS 安装包。关键特征包括:
qingjian-ai-client。com.zsjz.qingjian.client。out。${productName}-${os}-${version}-${arch}.${ext} 模板。target 为 nsis。
flowchart TD
WStart["build-w / build-we"] --> WBuilder["electron-builder 读取 builder.json"]
WBuilder --> WNSIS["生成 NSIS 安装包"]
WNSIS --> WOut["写入 ai-electron/out"]
图表来源
macOS 有两个构建配置文件:
builder-mac.json:通用 macOS 配置。builder-mac-arm64.json:ARM64 专用配置。当前这两个文件的 productName 仍为 ee,appId 仍为 com.bilibili.ee,与主项目的 qingjian-ai-client 不一致。这是发布时需要特别注意的配置差异:如果直接使用这些模板,可能产生不符合产品命名和签名预期的产物。
macOS 配置的关键点:
build/extraResources 映射到应用资源目录。hardenedRuntime 关闭。
flowchart TD
MStart["build-m / build-m-arm64 / build-m-x86"] --> MBuilder["electron-builder 读取 mac 配置"]
MBuilder --> MApp["生成 macOS 应用包"]
MApp --> MOut["写入 ai-electron/out"]
图表来源
Linux 打包配置定义在 cmd/builder.json 和 cmd/builder-linux.json 中,目标为 deb。关键特征包括:
qingjian-ai-client。com.zsjz.qingjian.client。out。${productName}-${os}-${version}-${arch}.${ext} 模板。publish 使用 generic provider,URL 为空。
flowchart TD
LStart["build-l"] --> LBuilder["electron-builder 读取 linux 配置"]
LBuilder --> LDeb["生成 deb 安装包"]
LDeb --> LOut["写入 ai-electron/out"]
图表来源
| 平台 | 主要命令 | 主要目标 | 产物模板 | 备注 |
|---|---|---|---|---|
| Windows | build-w、build-we |
NSIS 安装包 | ${productName}-${os}-${version}-${arch}.${ext} |
支持提权、自定义安装路径、桌面快捷方式 |
| macOS | build-m、build-m-arm64、build-m-x86 |
macOS 应用包 | ${productName}-${os}-${version}-${arch}.${ext} |
当前模板 productName/appId 与主产品不一致,需核对 |
| Linux | build-l |
deb 包 | ${productName}-${os}-${version}-${arch}.${ext} |
分类为 Utility |
章节来源
ai-electron/package.json 中声明了 electron-updater,这表明客户端具备基于 electron-updater 的自动更新能力。更新机制通常由 Electron 主进程在应用启动或用户主动检查更新时触发。
electron-builder 的 publish 配置位于 cmd/builder.json 和 builder-linux.json 等文件中,统一使用:
{
"provider": "generic",
"url": ""
}
这意味着:
ai-electron/package.json 中声明的版本为 200.0.0。electron-builder 通常会读取 package.json 的 version 作为安装包版本,并据此生成 latest.yml。因此:
当前仓库中没有看到 electron-updater 的前端或主进程实现细节,但结合 electron-updater 和 generic provider,常见行为如下:
回滚策略:
对于无法自动回滚的情况,可提供“禁用更新”“选择版本安装”等降级手段。
flowchart TD
Client["客户端"] --> Check["检查更新"]
Check --> Server["更新服务器"]
Server --> Meta["返回 latest.yml"]
Meta --> Diff{"是否需要增量更新"}
Diff -->|是| DownloadDiff["下载增量补丁"]
Diff -->|否| DownloadFull["下载完整安装包"]
DownloadDiff --> Install["安装更新"]
DownloadFull --> Install
Install --> Success["更新成功"]
Install --> Rollback["出现异常时回滚或提示卸载重装"]
图表来源
章节来源
ai-electron 目录下安装依赖。npm run dev 或 pnpm dev 启动 Electron 客户端。npm run start 或 pnpm start。cd ai-electron
npm run build
等价于:
npm run build-frontend
npm run build-electron
ee-bin encrypt
| 平台 | 命令 |
|---|---|
| Windows | npm run build-w |
| macOS ARM64 | npm run build-m 或 npm run build-m-arm64 |
| macOS x86_64 | npm run build-m-x86 |
| Linux | npm run build-l |
打包输出目录由 electron-builder 配置决定,位于 ai-electron/out。Windows 通常生成 NSIS 安装包,Linux 通常生成 deb 包,macOS 生成应用包。
CI 中建议明确以下步骤:
如需要签名和公证,应在对应平台构建任务中完成。
flowchart TD
CI["CI 触发"] --> Checkout["检出代码"]
Checkout --> SetupNode["设置 Node/pnpm 版本"]
SetupNode --> InstallDeps["安装 ai-electron 依赖"]
InstallDeps --> BuildFrontend["构建前端"]
BuildFrontend --> BuildElectron["构建 Electron"]
BuildElectron --> Encrypt["字节码加密"]
Encrypt --> PackageWin["打包 Windows"]
Encrypt --> PackageMac["打包 macOS"]
Encrypt --> PackageLinux["打包 Linux"]
PackageWin --> Upload["上传产物"]
PackageMac --> Upload
PackageLinux --> Upload
Upload --> Publish["发布到更新服务器"]
图表来源
章节来源
现象
原因
Electron 对 Node.js 版本有较强约束。不同 Electron 版本内置的 Node 版本不同,项目使用 Electron ^37.x,应使用与之兼容的 Node.js LTS 版本。
处理方法
现象
启动时报 missing module、ABI mismatch、native addon 加载失败等错误。
原因
better-sqlite3 是 native addon,必须使用与 Electron 匹配的 Node ABI 编译。如果 Node 版本变化、Electron 版本变化,或 CI 环境缓存不一致,就可能失败。
处理方法
npm run re-sqlite
该命令等价于:
electron-rebuild -f -w better-sqlite3
如果仍然失败,可尝试:
章节来源
现象
pnpm build 报错。处理方法
pnpm install。frontend/bin/build.sh 会进入前端目录执行 pnpm build。章节来源
现象
原因
macOS 签名和公证需要有效的开发者证书、Apple ID、正确的 Bundle Identifier 以及合适的代码签名配置。当前 macOS 打包配置中 hardenedRuntime 为 false,且 productName 和 appId 与主项目不一致,容易引发签名预期不符。
处理方法
章节来源
现象
原因
当前 electron-builder 的 publish URL 为空,意味着没有绑定正式更新服务器地址。此外,electron-updater 的更新行为还可能受运行时配置影响。
处理方法
章节来源
现象
原因
主窗口尺寸计算依赖屏幕 workAreaSize,并在 windowReady 中设置 bounds。同时,lifecycle 中处理 second-instance 事件,优先聚焦智能工作台窗口,否则聚焦主窗口。
处理方法
config.default.ts 中 mainServer 的 indexPath 是否指向有效前端页面。章节来源
统一包管理器
前端脚本同时出现 npm 和 pnpm,建议在项目和 CI 中固定 pnpm,避免依赖树不一致。
固定 Node 版本
使用 .nvmrc、.node-version 或 CI 的 actions/setup-node 固定 Node 版本,确保 Electron、ee-core、better-sqlite3 的 ABI 一致。
规范版本号管理
所有发布应以 package.json 的 version 为准。CI 可通过命令行更新版本号,再触发构建。
修正 macOS 打包模板
当前 builder-mac.json 和 builder-mac-arm64.json 的 productName 和 appId 与主产品不一致,正式发布前必须修正,否则会影响签名、分发和更新。
明确更新服务器
将 electron-builder 的 publish URL 指向真实更新服务器,并在 CI 中部署 latest.yml 和安装包。
区分开发、预发布、正式发布
devbuild + 三端打包 + 签名 + 公证 + 发布到更新服务器。保留回滚能力
保留最近两个稳定版本的安装包和 latest.yml,以便出现问题时快速回退。
自动化测试与类型检查
在 CI 中执行 typecheck、test,减少发布风险。
谨慎使用字节码加密
ee-bin encrypt 可以提升安全性,但也可能增加调试难度。建议在正式构建中使用,开发时使用 dev。
关注 ElectronEgg 文档
本项目大量依赖 ElectronEgg 的能力,包括生命周期、构建、加密、打包等。遇到 ee-bin 相关问题时,应参考 ElectronEgg 官方文档。
章节来源