156 lines
10 KiB
Markdown
156 lines
10 KiB
Markdown
# 客户端依赖(服务)安装、升级与启动流程
|
||
|
||
> 本文档梳理 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 流程
|
||
|
||
1. 渲染层:`needsRequiredDepsReinstall === true` 时,全屏展示 `SetupDependencies`(与向导内为同一组件)。
|
||
2. 用户完成依赖安装后,`onComplete` 被调用:
|
||
- 先 `setNeedsRequiredDepsReinstall(false)`,回到主界面。
|
||
- 再调用 `restartAllServices()`(内部先停后启),使新依赖生效。
|
||
3. 不改变 `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:restartAll` IPC handler 的实现会先停止 Agent、ComputerServer、FileServer、Lanproxy(**不停 MCP**),再按顺序启动 MCP → Agent → ComputerServer → FileServer → Lanproxy。MCP 在 restartAll 中只做 start/verify,不先停。
|
||
- **stopAll**:主进程 `processHandlers.ts` 中 `services:stopAll` IPC handler 会停止所有服务**包括 MCP**。
|
||
- **用户登出**:渲染进程 `ClientPage.handleLogout` 在停止 agent/fileServer/lanproxy/mcp 后,会单独调用 `computerServer.stop()`,避免登出后进程残留导致端口冲突;详见 [认证机制与 SavedKey 生命周期](./auth-savedkey-lifecycle.md#退出登录)。
|
||
- **serviceManager.ts**:`serviceManager.restartAllServices()` 是另一套实现(供 `main.ts` 内部和 Tray 菜单使用),行为与 IPC `restartAll` 略有不同:会先停 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. 相关文档
|
||
|
||
- [依赖版本固定与安装/升级行为](./dependency-version-pinning.md) — installVersion、同步、文案与不降级约定。
|
||
- [Quick Init](./QUICK-INIT.md) — 快捷初始化与依赖步骤不可跳过的约定。
|