10 KiB
10 KiB
客户端依赖(服务)安装、升级与启动流程
本文档梳理 Electron 客户端应用依赖安装(初始化 / 手动)、升级、启动时同步以及安装或升级后的服务停止与重启全流程,便于维护与排查。
1. 概述
- 必需依赖:由
SETUP_REQUIRED_DEPENDENCIES定义,不随应用打包,需在「初始化」或「依赖 Tab」中安装到~/.qimingclaw/(或项目配置的数据目录)下的node_modules。 - 版本判定:以当前已真实安装的版本为准;用户可在依赖 Tab 下手动升级,已安装版本 ≥ 配置的
installVersion即视为就绪,不降级。 - 安装/升级后:在主界面下(含重装流程和依赖 Tab),依赖安装或升级成功后会调用
services.restartAll()重启所有服务,使新二进制生效。初始化向导中的依赖安装不触发重启(此时服务尚未启动,向导完成后由自动重连流程启动服务)。
2. 必需依赖列表(SETUP_REQUIRED_DEPENDENCIES)
| 名称 | 显示名 | 类型 | 说明 | 固定版本(installVersion) |
|---|---|---|---|---|
| uv | uv | bundled | Python 包管理器(已集成) | minVersion: 0.5.0 |
| pnpm | pnpm 包管理器 | npm-local | Node 包管理器 | 10.30.3 |
| qiming-file-server | 文件服务 | npm-local | 工作目录文件远程管理 | 1.2.2 |
| qimingcode | Agent 引擎 | bundled | 应用内集成 Agent 执行引擎 | 1.1.68 |
| qiming-mcp-stdio-proxy | MCP 服务 | npm-local | MCP 协议聚合代理 | 1.4.6 |
| claude-code-acp-ts | ACP 协议 | npm-local | 引擎统一适配 | 0.16.1 |
- 配置位置:
src/main/services/system/dependencies.ts中的SETUP_REQUIRED_DEPENDENCIES。 - 检测:
checkAllDependencies()仅检查上述列表,返回每项状态:installed|bundled|missing|outdated|error。
3. 依赖状态与版本判定
- installed / bundled:已就绪,无需操作。
- missing:未安装,需要安装。
- outdated:已安装但当前版本 < 配置的
installVersion,可升级;不强制进入全屏依赖安装(用户可在依赖 Tab 自行升级)。 - error:检测或安装过程出错。
版本比较约定:
- 以当前已安装版本与配置的
installVersion比较。 - 仅当「未安装」或「已装版本 < installVersion」时标记为需安装/升级;已装 ≥ 目标则视为已就绪,不降级。
- 实现见
dependencies.ts中checkAllDependencies的compareVersions(installed, target)逻辑。
4. 初始化安装(首次 / 向导内)
4.1 入口与流程
- 入口:应用启动后若
setupService.isSetupCompleted()为 false,渲染初始化向导(SetupWizard)。 - 向导阶段 1:依赖检测与安装(
SetupDependencies)。- 调用
dependencies.checkAll(),若有任一项非installed/bundled(包括outdated),则进入依赖安装 UI。 - 注意:向导内
outdated也会触发安装阶段(确保首次设置时所有依赖都是最新),而主界面下outdated不触发全屏重装(见 section 5.1)。 - 先检查系统依赖(如 uv);若缺失则提示并阻塞。
- 对 npm-local 等可安装项自动或手动安装,全部就绪后进入下一步。
- 调用
- 完成回调:
SetupDependencies的onComplete由SetupWizard.handleDepsComplete处理:置dependenciesReady = true,然后进入「基础设置」步骤或快捷初始化(Quick Init)。
4.2 与 Quick Init 的关系
- 若依赖已全部就绪且存在快捷初始化配置,会优先走 Quick Init,不再重复安装依赖。
- 依赖步骤不可跳过:必须先完成依赖检测/安装,再进入基础设置与登录。
5. 主界面下「必需依赖缺失」触发的重装流程
5.1 触发条件
- 用户已进入主界面(
isSetupComplete === true)。 - 应用在进入主界面后执行一次
dependencies.checkAll()。 - 仅当存在状态为
missing或error的必需依赖时,置needsRequiredDepsReinstall = true。 - outdated 不触发:已安装但版本低于配置时,不强制全屏依赖安装,用户可在依赖 Tab 自行升级。
5.2 流程
- 渲染层:
needsRequiredDepsReinstall === true时,全屏展示SetupDependencies(与向导内为同一组件)。 - 用户完成依赖安装后,
onComplete被调用:- 先
setNeedsRequiredDepsReinstall(false),回到主界面。 - 再调用
restartAllServices()(内部先停后启),使新依赖生效。
- 先
- 不改变
isSetupComplete,不重新走向导的账号登录等步骤。
5.3 涉及代码
App.tsx:needsRequiredDepsReinstall状态、checkRequiredDeps的useEffect、全屏SetupDependencies的onComplete与restartAllServices()。
6. 依赖 Tab 下手动安装 / 升级
6.1 入口
- 主界面 → 依赖 Tab(
DependenciesPage)。 - 可对单项点击「安装」/「升级」,或使用「全部安装」/「全部升级」/「安装并升级」。
6.2 行为
- 单项:调用
dependencies.installPackage(name, options?),可选options.version(如installVersion);成功后调用restartServicesAfterDepChange()。 - 批量:遍历需安装/升级的依赖依次安装;若有任意一项成功,则调用
restartServicesAfterDepChange()。 - restartServicesAfterDepChange:内部调用
services.restartAll()(主进程内先停后启),并提示「正在重启服务…」「服务已重启」或失败提示。
6.3 版本与文案
- 以当前已安装版本为准;
outdated时按钮为「升级」,否则为「安装」。 - 批量时按缺失/过期组合显示「全部安装」「全部升级」或「安装并升级」;完成提示区分「依赖安装完成」「依赖升级完成」「依赖安装并升级完成」。
7. 应用启动时的依赖同步(syncInitDependencies)
- 目的:应用升级后,若配置中的
installVersion或应用版本发生变化,将依赖同步到新版本。 - 触发:主进程启动任务(
bootstrap/startup.ts)中,通过getInitDepsState()读取上次同步状态,与当前app.getVersion()及各包installVersion逐一比较。满足以下任一条件即触发syncInitDependencies():- 应用版本(
appVersion)与上次记录不同(含首次无记录); - 任一包的
installVersion与上次记录的对应值不同(含上次无该包记录)。
- 应用版本(
- 行为:对
SETUP_REQUIRED_DEPENDENCIES中带installVersion的 npm-local 包,若未安装或已装版本低于配置,则安装到指定版本,并写回~/.qimingclaw/.init-deps-state.json(或当前数据目录下的同名文件)。 - 不降级:已安装版本 ≥
installVersion的包不会被执行安装,避免覆盖用户手动升级的更高版本。
8. 安装或升级后的服务停止与重启
以下任一场景在「依赖安装或升级成功」后,都会执行先停止再重启所有相关服务,使新二进制生效:
| 场景 | 位置 | 行为 |
|---|---|---|
| 主界面重装依赖完成 | App.tsx 全屏 SetupDependencies 的 onComplete |
setNeedsRequiredDepsReinstall(false) 后调用 restartAllServices()(即 services.restartAll()) |
| 依赖 Tab 单项安装/升级成功 | DependenciesPage.handleInstallSingleDep |
调用 restartServicesAfterDepChange() → services.restartAll() |
| 依赖 Tab 批量安装/升级且至少成功一项 | DependenciesPage.handleInstallAllDeps |
同上 restartServicesAfterDepChange() |
- restartAll:主进程
processHandlers.ts中services:restartAllIPC handler 的实现会先停止 Agent、ComputerServer、FileServer、Lanproxy(不停 MCP),再按顺序启动 MCP → Agent → ComputerServer → FileServer → Lanproxy。MCP 在 restartAll 中只做 start/verify,不先停。 - stopAll:主进程
processHandlers.ts中services:stopAllIPC handler 会停止所有服务包括 MCP。 - 用户登出:渲染进程
ClientPage.handleLogout在停止 agent/fileServer/lanproxy/mcp 后,会单独调用computerServer.stop(),避免登出后进程残留导致端口冲突;详见 认证机制与 SavedKey 生命周期。 - serviceManager.ts:
serviceManager.restartAllServices()是另一套实现(供main.ts内部和 Tray 菜单使用),行为与 IPCrestartAll略有不同:会先停 MCP 再启动,且不包含 ComputerServer。渲染进程通过window.electronAPI.services.restartAll()调用的是processHandlers.ts中的 IPC handler 版本。 - 无需单独 stopAll:
restartAll内部已包含停止逻辑(Agent / ComputerServer / FileServer / Lanproxy);若需停止所有服务(含 MCP)且不重启,可调用services.stopAll()。
9. 涉及文件与入口汇总
| 类型 | 路径 / 说明 |
|---|---|
| 依赖配置与检测 | src/main/services/system/dependencies.ts:SETUP_REQUIRED_DEPENDENCIES、checkAllDependencies、installNpmPackage、installMissingDependencies、syncInitDependencies、compareVersions |
| 启动同步 | src/main/bootstrap/startup.ts:启动后根据 getInitDepsState 与版本比较调用 syncInitDependencies |
| IPC | src/main/ipc/dependencyHandlers.ts:dependencies:checkAll、dependencies:installPackage 等;processHandlers.ts:services:stopAll、services:restartAll |
| 服务管理器 | src/main/window/serviceManager.ts:restartAllServices、stopAllServices(供 main.ts / Tray 使用,行为与 IPC handler 略有不同,见 section 8) |
| 初始化向导 | src/renderer/components/setup/SetupWizard.tsx、SetupDependencies.tsx:向导内依赖步骤与完成回调 |
| 主界面重装 | src/renderer/App.tsx:needsRequiredDepsReinstall、全屏 SetupDependencies、checkRequiredDeps、restartAllServices |
| 依赖 Tab | src/renderer/components/pages/DependenciesPage.tsx:单项/批量安装与 restartServicesAfterDepChange |
| 持久化 | ~/.qimingclaw/.init-deps-state.json(或当前数据目录):应用版本与各包上次同步版本,读写在 dependencies.ts 的 getInitDepsState / setInitDepsState |
10. 相关文档
- 依赖版本固定与安装/升级行为 — installVersion、同步、文案与不降级约定。
- Quick Init — 快捷初始化与依赖步骤不可跳过的约定。