Files
qiming/qimingclaw/crates/agent-electron-client/docs/architecture/dependency-and-services-lifecycle.md

10 KiB
Raw Permalink Blame History

客户端依赖(服务)安装、升级与启动流程

本文档梳理 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.tscheckAllDependenciescompareVersions(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 等可安装项自动或手动安装,全部就绪后进入下一步。
  • 完成回调SetupDependenciesonCompleteSetupWizard.handleDepsComplete 处理:置 dependenciesReady = true然后进入「基础设置」步骤或快捷初始化Quick Init

4.2 与 Quick Init 的关系

  • 若依赖已全部就绪且存在快捷初始化配置,会优先走 Quick Init不再重复安装依赖。
  • 依赖步骤不可跳过:必须先完成依赖检测/安装,再进入基础设置与登录。

5. 主界面下「必需依赖缺失」触发的重装流程

5.1 触发条件

  • 用户已进入主界面(isSetupComplete === true)。
  • 应用在进入主界面后执行一次 dependencies.checkAll()
  • 仅当存在状态为 missingerror 的必需依赖时,置 needsRequiredDepsReinstall = true
  • outdated 不触发:已安装但版本低于配置时,不强制全屏依赖安装,用户可在依赖 Tab 自行升级。

5.2 流程

  1. 渲染层:needsRequiredDepsReinstall === true 时,全屏展示 SetupDependencies(与向导内为同一组件)。
  2. 用户完成依赖安装后,onComplete 被调用:
    • setNeedsRequiredDepsReinstall(false),回到主界面。
    • 再调用 restartAllServices()(内部先停后启),使新依赖生效。
  3. 不改变 isSetupComplete,不重新走向导的账号登录等步骤。

5.3 涉及代码

  • App.tsxneedsRequiredDepsReinstall 状态、checkRequiredDepsuseEffect、全屏 SetupDependenciesonCompleterestartAllServices()

6. 依赖 Tab 下手动安装 / 升级

6.1 入口

  • 主界面 → 依赖 TabDependenciesPage)。
  • 可对单项点击「安装」/「升级」,或使用「全部安装」/「全部升级」/「安装并升级」。

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 全屏 SetupDependenciesonComplete setNeedsRequiredDepsReinstall(false) 后调用 restartAllServices()(即 services.restartAll()
依赖 Tab 单项安装/升级成功 DependenciesPage.handleInstallSingleDep 调用 restartServicesAfterDepChange()services.restartAll()
依赖 Tab 批量安装/升级且至少成功一项 DependenciesPage.handleInstallAllDeps 同上 restartServicesAfterDepChange()
  • restartAll:主进程 processHandlers.tsservices:restartAll IPC handler 的实现会先停止 Agent、ComputerServer、FileServer、Lanproxy不停 MCP),再按顺序启动 MCP → Agent → ComputerServer → FileServer → Lanproxy。MCP 在 restartAll 中只做 start/verify不先停。
  • stopAll:主进程 processHandlers.tsservices:stopAll IPC handler 会停止所有服务包括 MCP
  • 用户登出:渲染进程 ClientPage.handleLogout 在停止 agent/fileServer/lanproxy/mcp 后,会单独调用 computerServer.stop(),避免登出后进程残留导致端口冲突;详见 认证机制与 SavedKey 生命周期
  • serviceManager.tsserviceManager.restartAllServices() 是另一套实现(供 main.ts 内部和 Tray 菜单使用),行为与 IPC restartAll 略有不同:会先停 MCP 再启动,且不包含 ComputerServer。渲染进程通过 window.electronAPI.services.restartAll() 调用的是 processHandlers.ts 中的 IPC handler 版本。
  • 无需单独 stopAllrestartAll 内部已包含停止逻辑Agent / ComputerServer / FileServer / Lanproxy若需停止所有服务含 MCP且不重启可调用 services.stopAll()

9. 涉及文件与入口汇总

类型 路径 / 说明
依赖配置与检测 src/main/services/system/dependencies.tsSETUP_REQUIRED_DEPENDENCIEScheckAllDependenciesinstallNpmPackageinstallMissingDependenciessyncInitDependenciescompareVersions
启动同步 src/main/bootstrap/startup.ts:启动后根据 getInitDepsState 与版本比较调用 syncInitDependencies
IPC src/main/ipc/dependencyHandlers.tsdependencies:checkAlldependencies:installPackage 等;processHandlers.tsservices:stopAllservices:restartAll
服务管理器 src/main/window/serviceManager.tsrestartAllServicesstopAllServices(供 main.ts / Tray 使用,行为与 IPC handler 略有不同,见 section 8
初始化向导 src/renderer/components/setup/SetupWizard.tsxSetupDependencies.tsx:向导内依赖步骤与完成回调
主界面重装 src/renderer/App.tsxneedsRequiredDepsReinstall、全屏 SetupDependenciescheckRequiredDepsrestartAllServices
依赖 Tab src/renderer/components/pages/DependenciesPage.tsx:单项/批量安装与 restartServicesAfterDepChange
持久化 ~/.qimingclaw/.init-deps-state.json(或当前数据目录):应用版本与各包上次同步版本,读写在 dependencies.tsgetInitDepsState / setInitDepsState

10. 相关文档