# Phase 004 — 砍桌面端 + 官网 + 清理依赖 | 项 | 值 | |---|---| | 目标 | 无 Electron、无官网;hermes CLI 改用户自装;依赖表与 lockfile 一次性收干净 | | 前置 | Phase 003 通过 | | 产出 | `npm ci && npm run build && npm run test:e2e` 全绿;gateway 自启仍工作 | | 参考 | `docs/03-施工蓝图.md` §6 | --- ## 0. 已核实:gateway 自启零迁移 原蓝图担心的「gateway 自启逻辑一半长在 Electron 里」**不存在**。 `packages/server/src/index.ts` 的 `startRuntimeServicesBeforeListen()` / `startRuntimeServicesAfterListen()` 已经调用 `ensureProfileGatewaysRunning()`。 Electron 侧只有两处: 1. `gateway-autostart.ts` 里 `shouldRecoverWindowsDesktopGatewayOrphans()` 依赖 `HERMES_DESKTOP` 2. 注册在 `/api/desktop/shutdown` 的关闭回调 **本 Phase 只删这两处,不做任何迁移。** --- ## 1. 功能任务 1 — 删除 desktop 与 website Phase 001 已删掉 `packages/desktop` / `packages/website` / 相关 workflow。本 Phase 处理**引用方**。 ### 1.1 根 `package.json` scripts 删: ``` desktop:install desktop:prepare-runtime desktop:prepare-python desktop:dev build:desktop build:desktop:mac build:desktop:win build:desktop:linux dev:website build:website preview:website claude # I 组配套:claude --dangerously-skip-permissions ``` `mcu:*` 系列:Phase 001 已删 `esp32-c3`,这些 script 也要删。若你后续要做语音硬件,另开 Phase 恢复。 保留:`start` `dev` `dev:client` `dev:server` `build` `harness:check` `preview` `test` `test:watch` `test:coverage` `test:e2e` `test:e2e:ui` `openapi:generate` ### 1.2 server 侧 | 文件 | 改动 | |---|---| | `src/index.ts` | 删 `registerDesktopShutdownRoute` import 与调用、删 `desktopShutdownHandler` 变量与赋值、删 `isDesktopEnv()` | | `src/index.ts` | **注意**:`bindShutdown` 主流程要保留,它也服务正常关停。只删桌面分支 | | `services/shutdown.ts` | 删 `desktop` 分支,保留主流程 | | `controllers/auth.ts` | `requiresCredentialChange: process.env.HERMES_DESKTOP === 'true'` → 改常量 `false` 或删该字段 | | `services/hermes/gateway-autostart.ts` | 删 `shouldRecoverWindowsDesktopGatewayOrphans()` 及 index.ts 里对它的调用 | | `services/hermes/agent-bridge/manager.ts` | 删 `isDesktopEnv()` 用法 | | `services/hermes/studio-mcp-autoinject.ts` | 同上 | ### 1.3 client 侧 | 文件 | 改动 | |---|---| | `src/utils/desktop-bridge.ts` | **整个文件删除** | | `src/App.vue` | 删 `desktopBridge` import、`isDesktopShell` / `isDesktopPetRoute` / `hasDesktopTitleBar`、`DesktopTitleBar` 组件、`WebPet` | | `src/main.ts` | 删 `desktopBridge` 调用 | | `src/router/index.ts` | 删 `hermesDesktop` 路由注入逻辑 | | `src/utils/completion-notification.ts` | 删本地 `desktopBridge` 副本 | | `src/api/client.ts` | 删对应 import 与分支 | | `src/components/layout/AppSidebar.vue` | 删对应 import | | `src/components/layout/DesktopTitleBar.vue` | **整个文件删除** | | `src/components/auth/DefaultCredentialPrompt.vue` | 删 `desktopBridge` 相关 | | `src/views/hermes/DesktopPetView.vue` | **整个文件删除** | | `src/components/hermes/pets/WebPet.vue` | 若无其他引用则删;有引用先解引用 | ### 1.4 测试 ```bash rm -f tests/client/desktop-titlebar.test.ts rm -f tests/client/app-web-pet.test.ts ``` e2e 里的 desktop 用例移除。 --- ## 2. 功能任务 2 — hermes CLI 改用户自装 砍桌面端后 hermes 不再捆绑。改动全部集中在两个文件。 ### 2.1 `hermes-path.ts` `resolveHermesBin()` 已是 `HERMES_BIN` → `'hermes'` 的优先级链,**保持不变**。 改 `bundledCliPythonForWindows()`:删掉「找捆绑运行时」的假设,改为—— - `HERMES_BIN` 指向 `hermes.exe` 时,在同级 / 上级目录找 `python.exe` - 找不到就用 `hermes.exe` 直调(hermes 官方 Windows 包自带 launcher,不一定需要 `-m hermes_cli.main`) ### 2.2 `hermes-process.ts` - **不新增 shell 拼接**。所有调用继续走 `execHermesWithBin` / `spawnHermesWithBin`,参数数组 - 加一个 `isHermesCliAvailable()` 探测函数 ### 2.3 引导页(用户可见) 启动时若探测不到 hermes CLI,前端显示引导: ``` 未检测到 hermes CLI 请先安装: pip install hermes-agent 或设置环境变量指定路径: HERMES_BIN=/path/to/hermes 安装后重启本服务。 ``` **中英双语都要写**(`zh.ts` + `en.ts`)。 落点建议:新增 `packages/client/src/views/hermes/HermesMissingView.vue`,在 `router/index.ts` 里作为前置守卫(与 `LoginView` 同级,登录前判断)。 --- ## 3. 功能任务 3 — 依赖与 lockfile 收口 **这是本 Phase 最需要小心的部分**:`npm ci` 要求 `package.json` 与 `package-lock.json` 严格一致,任何增删都会让它失败。 ### 3.1 删依赖 `package.json` 的 `dependencies` / `devDependencies` 删: ``` electron electron-builder @electron/* electron-store electron-updater (若 devDeps 里有) ``` 先跑一次定位: ```bash grep -n "electron" package.json ``` ### 3.2 重生成 lockfile ```bash # 删旧 lock,用当前 package.json 重装,版本不主动升级 rm -f package-lock.json npm install --package-lock-only --ignore-scripts ``` `--package-lock-only` 只更新 lock 不装包,**不会升级已存在的版本**(依赖树解析会沿用现有版本范围)。 ```bash npm ci --ignore-scripts npm run build ``` ### 3.3 校验版本没被偷偷升级 ```bash # 对比新旧 lock 里几个关键包的版本 git diff package-lock.json | grep -E "^\+.*\"(vue|koa|socket.io|better-sqlite3|vite|pinia)\"" # 期望:无新增版本行 ``` --- ## 4. 功能任务 4 — Dockerfile / CI 同步 | 文件 | 改动 | |---|---| | `Dockerfile` | 删 electron 相关 build 步骤与产物拷贝 | | `docker-compose.yml` | 删 electron 挂载 | | `.github/workflows/build.yml` | 确认无 desktop 步骤 | | `.github/workflows/webui-release.yml` | Phase 002 已去品牌,确认打包产物名已改 | ### 4.1 `fetch-depth: 0` 必须保留 `harness-check.mjs` 靠 `git diff` 判断链路变更。CI 里 `actions/checkout` 缺 `fetch-depth: 0` 会让检测失效。 --- ## 5. 验收 ### 5.1 命令 ```bash cd /Users/jack/source/MyProject/js/hermes-ui grep -rn "desktop\|electron\|Electron" packages/ --include="*.ts" --include="*.vue" \ | grep -v "bridge_runtime.py" # 期望:零命中(python 的 HERMES_DESKTOP 判断例外,见 §6) grep -n "electron" package.json # 期望:零命中 npm ci --ignore-scripts npm run build npm run test npm run test:e2e ``` ### 5.2 手工 - [ ] `npm run dev` 正常启动 - [ ] 启动后 gateway 自动拉起(看日志) - [ ] 聊天正常 - [ ] 故意把 `HERMES_BIN` 指向不存在的路径 → 出现引导页而非白屏报错 - [ ] Docker 构建成功且容器内聊天正常 --- ## 6. 红线:这两处 `HERMES_DESKTOP` **保留不动** `packages/server/src/services/hermes/agent-bridge/python/bridge_runtime.py` L74 / L119: 这是 Windows 下隐藏子进程控制台窗口的逻辑。删掉会导致 Windows 用户运行 git / 命令时**弹黑窗**。 虽然新项目是 Web 端、不再有 Electron 壳,但 Windows 用户从命令行起 Node 依然会弹。 **判断依据**:这两个 env 在新项目里永远为 `undefined`,条件分支永不进入 → 逻辑上等价于删除,但**保留代码不影响正确性**,删除则影响。留。 --- ## 7. 风险 | # | 风险 | 缓解 | |---|---|---| | P4-1 | 删 `bindShutdown` 时误伤正常关停 | 只删桌面分支,主流程保留;手工 Ctrl+C 验证一次 | | P4-2 | lockfile 重生成时依赖被升级 | `git diff package-lock.json` 逐项确认,锁版本 | | P4-3 | Windows 用户失去捆绑 Python | 引导页 + README 显著位置写安装步骤。这是既定决策 | | P4-4 | e2e 有 desktop 用例残留 | `test:e2e` 必跑 | | P4-5 | `WebPet.vue` 被多处引用 | 删前先 `grep -rn "WebPet"` | | P4-6 | `DefaultCredentialPrompt` 在 auth.ts 有对应字段 | 两边一起改 | --- ## 8. 完成标志 **项目此时功能完整、界面还是旧的 Naive UI。** 进入 Phase 005。 从此开始才允许改样式代码。