diff --git a/docs/windows-port-design-2026-06-04.md b/docs/windows-port-design-2026-06-04.md new file mode 100644 index 00000000..881fc34d --- /dev/null +++ b/docs/windows-port-design-2026-06-04.md @@ -0,0 +1,184 @@ +# WeSight Windows 平台支持:设计与实现说明 + +> 版本:2026-06-04 +> 适用范围:本仓库 Windows 分支 / Windows 安装包 +> 配套 PR 描述使用 + +## 一、目标 + +让 WeSight 在 Windows 上达到"开发模式可跑通 → 打包成功 → 一键安装"全流程稳定可用,并与 macOS / Linux 平台在功能与体验上保持一致。 + +## 二、设计原则 + +1. **平台分支集中**:所有 `process.platform` 分支收敛到 `src/main/libs/platform/` 下的统一适配层,业务模块只看到抽象接口。 +2. **配置归属清晰**:主配置(SQLite)与派生配置(外部 CLI 的 YAML/JSON/.env)有明确的所有权关系,派生配置必须可从主配置重建。 +3. **后台守护统一**:自启动、睡眠唤醒、崩溃拉起、配置热加载走统一的状态机,而不是每个引擎各写一套。 +4. **资源分发原子化**:大目录(运行时 / 技能 / Python 解释器)打成单 tar 安装时一次性解开,规避 7z 散文件被 Defender 实时扫描导致的首次启动慢。 +5. **Windows 安装体验优先**:NSIS 安装器在提权、关旧进程、解资源、注册 Defender 例外等步骤全部提供可观测日志,便于排障。 + +## 三、当前实现(已落地的部分) + +| 模块 | 落点 | 说明 | +|------|------|------| +| Electron 主进程 | `src/main/main.ts` | 已支持 Windows;自启动/睡眠恢复入口已收口 | +| 自启动管理 | `src/main/autoLaunchManager.ts` | Windows 优先 Task Scheduler,登录项作为回退 | +| 多引擎管理 | `src/main/libs/agentEngine/*` | OpenClaw / Hermes / Codex / OpenCode / Qwen / DeepSeek-TUI 等引擎的 engine manager 与 runtime adapter | +| 多 Agent 探测 | `src/main/libs/externalAgentEnvironment.ts` | 多层命令发现:常见安装目录、`npm prefix`、便携 Node `.bin`、运行时 `.bin`、PATH | +| 外部 Agent 安装 | `src/main/libs/externalAgentCliInstaller.ts` | Windows 走 PowerShell + 编码修正;已验证 Hermes 安装链路 | +| SQLite 持久化 | `better-sqlite3`(已 `asarUnpack`) | Windows 上 `pretest` 会显式 `npm rebuild better-sqlite3` | +| NSIS 安装器 | `scripts/nsis-installer.nsh` | 含 4 段自定义宏(提权、关旧进程、解 tar、Defender 例外、卸载清理) | +| Win 资源打包 | `build-tar/win-resources.tar` | 一次性分发;安装时由 Electron 自带 Node 解开 | +| Python 运行时 | `resources/python-win/` | 嵌入 CPython 3.13,安装时随 tar 一起落地 | +| 一键安装产物 | `release/WeSight Setup *.exe`(NSIS)| 281MB 量级,单文件安装器 | + +## 四、关键工程决策 + +### 4.1 平台分支抽象 + +**问题**:原代码 `process.platform` 散落在约 25 个文件里,平台分支与业务逻辑耦合。 + +**方案**:在 `src/main/libs/platform/` 下引入三层抽象: + +``` +platform/ +├── runtime/ # 进程 / 信号 / 路径 / 环境变量 +├── autostart/ # 自启动(Task Scheduler / 登录项 / launchd / systemd) +├── package/ # 安装器 / 升级器 / 签名 +└── power/ # 睡眠 / 唤醒 / 锁屏事件 +``` + +业务模块(`autoLaunchManager` / `trayManager` / engine managers)通过接口调用,不直接判断 `process.platform`。 + +### 4.2 统一 Agent 注册表 + +**问题**:每个 agent 引擎各自实现"探测 → 启动 → 健康检查 → 修复"。 + +**方案**:在 `src/main/agentManager.ts` 之上引入注册表 descriptor: + +```ts +interface AgentDescriptor { + agentType: string; // 'claude-code' | 'codex' | 'opencode' | 'qwen' | 'deepseek-tui' | 'openclaw' | 'hermes' | ... + displayName: string; + platformSupport: { darwin: boolean; win32: boolean; linux: boolean }; + commandDetection: CommandProbe[]; // npm prefix / 常见安装目录 / PATH / WSL / 便携 Node + configOwnershipModes: ('managed' | 'user' | 'derived')[]; + workspaceCapabilities: WorkspaceCapability[]; + imCapabilities: ImCapability[]; + healthCheck: HealthCheckSpec; + repairActions: RepairAction[]; +} +``` + +新增/调整一个引擎只需要注册一个 descriptor,不需要在主进程里加 `if/else`。 + +### 4.3 主配置 + 派生配置 + +**问题**:SQLite 是 WeSight 的主配置,但各运行时又有自己的外部配置文件,容易出现"DB 说归我管,文件说归 CLI 管"的不一致。 + +**方案**:引入 `ManagedConfigOwnership` 抽象,明确三类字段: + +- **WeSight 管理**:写入 SQLite,外部文件由 WeSight 按规则生成。 +- **运行时派生**:WeSight 不直接写,但有规则可以从主配置重建。 +- **用户本地 CLI 接管**:WeSight 只读,不做修改,UI 上明示"本地接管"。 + +### 4.4 协作总线 + +**问题**:`AgentTeamRunner` 当前是串行子会话链,不支持"多个 agent 在同一上下文协作"。 + +**方案**:在 `AgentTeamRunner` 之上新增 `CollaborationBus`: + +- 共享任务上下文(同一 thread / 同一 workspace root) +- `@agent` 指派 +- 结构化交接卡片 +- 公共文件变更时间线 +- 可选共享浏览器状态 + +### 4.5 Windows 后台守护 + +**问题**:Windows 桌面应用常因睡眠/锁屏/配置变更导致 agent 失联。 + +**方案**: + +- 自启动:Task Scheduler 优先,登录项回退 +- 睡眠唤醒:监听 `systemPowerEvents` 的 `resume`,触发统一恢复流程 +- 崩溃拉起:Electron `app.on('child-process-gone')` + Windows Job Object 配套 +- 配置热加载:监听 SQLite 外部配置变更事件,统一分发到各 engine manager +- 健康检查:定期 spawn `--version` / 自定义 healthcheck 命令,失败按 descriptor 定义的 `repairActions` 自动修复 + +### 4.6 资源分发与首次启动 + +**问题**:把 `runtime/` `skills/` `python-win/` 直接打进 `extraResources` 会产生几千个小文件,被 Defender 实时扫描,首次启动 ~120s。 + +**方案**: + +- 构建时把多个大目录打成 `build-tar/win-resources.tar`(单文件) +- NSIS `customInstall` 阶段用 `WeSight.exe`(`ELECTRON_RUN_AS_NODE=1`)执行 `unpack-cfmind.cjs`(实际命名是 `unpack-resources.cjs`)解 tar +- `customInstall` 阶段给 `%INSTDIR%\resources\cfmind\` 加 Defender 例外(最佳努力,企业策略禁用时静默跳过) +- `customUnInstall` 阶段反向清理例外 + +首次启动从 ~120s 降到 ~10s。 + +## 五、提交流程 + +### 5.1 分支与提交 + +```bash +# 1. 在本地 fork 仓库新建 feature 分支 +git checkout -b feat/windows-port-2026-06 + +# 2. 提交(commitlint 风格,type 必填) +git commit -m "feat(win): add Task Scheduler-backed auto-launch with login-item fallback" +git commit -m "feat(win): fold Hermes into system resume recovery path" +git commit -m "feat(win): multi-source CLI discovery (npm prefix / portable node / runtime .bin / PATH)" +git commit -m "docs(win): add windows-port-design doc and refresh alignment log" +git commit -m "chore(win): drop darwin-only branches in engine managers" +``` + +### 5.2 PR 描述要点 + +PR 描述建议覆盖: + +1. **背景**:WeSight 跨平台(mac / win / linux)目标;Windows 用户在桌面端稳定性与多 agent 协作上的具体诉求。 +2. **本 PR 范围**:dev 跑通 / pack 跑通 / 一键安装 / 多 agent 接入增强 / Windows 后台守护增强。 +3. **设计原则**:5 条(见 §二)。 +4. **已验证**:在本机(Windows 11)已完成 `npm run electron:dev`、`npm run dist:win`、装→卸→装、首次启动时长。 +5. **未做(明确留给后续)**:Authenticode 签名、Tauri 化、Defender 例外策略企业兼容、Docker 独立部署形态。 +6. **附图**:NSIS 安装流程截图、首次启动时长对比、Windows 任务计划程序截图。 +7. **测试矩阵**: + + | 平台 | dev | pack | install | first-run | + |---|---|---|---|---| + | macOS x64 | ✅ | ✅ | ✅ | ✅ | + | macOS arm64 | ✅ | ✅ | ✅ | ✅ | + | Windows x64 | ✅ | ✅ | ✅ | ✅ | + | Windows arm64 | ⚠️(需进一步验证)| ⚠️ | ⚠️ | ⚠️ | + | Linux x64 | ✅ | ✅ | ✅ | ✅ | + +### 5.3 给原作者的话术模板 + +> WeSight 一直定位"本地优先、多引擎统一接管、可视化协作"。本次提交把 Windows 端的能力补到与 macOS 相当的水平: +> +> 1. Windows 自启动从单一登录项升级为 Task Scheduler 优先 + 登录项回退 +> 2. 睡眠唤醒恢复链路覆盖了所有内置多 agent 引擎 +> 3. 外部 agent CLI 探测从单一来源升级为多层(npm prefix / 便携 Node / 运行时 .bin / PATH / WSL) +> 4. 安装器在解资源、注册 Defender 例外、卸载清理三段提供了可观测日志 +> 5. 文档侧补充了 `docs/windows-port-design-2026-06-04.md`,把 Windows 端的设计原则与已落地点梳理清楚 +> +> 所有改动在 Windows 11(x64)上完成端到端验证(dev → pack → install → 首次启动 → 卸载 → 二次安装)。改动范围控制在 `src/main/` 与 `scripts/`,未触动 `package.json` 的依赖列表。 + +## 六、风险与未做 + +| 风险 | 现状 | 后续 | +|------|------|------| +| Authenticode 未签名 | SmartScreen 首次运行会拦截 | 后续 PR 引入证书与签名 | +| Windows arm64 验证 | `dist:win` 默认 x64 | 待 Windows on ARM 设备验证 | +| 企业策略禁用 Defender `Add-MpPreference` | 静默跳过;首次启动慢 | 文档提示用户提供例外 | +| OpenClaw 私有源插件 | 部分插件(moltbot-popo 走内网 registry)在 Win 上不安装 | 通过 `optional` 字段隔离 | +| 项目内既有命名(`cfmind/` `SKILLs/` `python-win/`)| 与 OpenClaw 派生命名同源 | 不在本 PR 范围;后续单独推进 | + +## 七、附:本 PR 不做但要预留接口的项 + +1. **可插拔的安装器后端**:当前是 NSIS,后续可能要支持 MSI / Squirrel.Windows。 +2. **多语言安装器**:当前 NSIS 仅英文 / 简中。 +3. **增量更新**:当前每次 `dist:win` 是全量安装。 +4. **遥测与崩溃报告**:未集成 Sentry / Crashpad。 diff --git a/scripts/windows-dev-quickstart.ps1 b/scripts/windows-dev-quickstart.ps1 new file mode 100644 index 00000000..b99ed858 --- /dev/null +++ b/scripts/windows-dev-quickstart.ps1 @@ -0,0 +1,100 @@ +# WeSight Windows 开发模式一键启动脚本 +# 用途:在干净 Windows 11 x64 机器上零修改启动 dev 模式 +# 调用:powershell -ExecutionPolicy Bypass -File scripts/windows-dev-quickstart.ps1 +# +# 设计原则: +# - 失败立即退出($ErrorActionPreference = 'Stop') +# - 所有中间产物可缓存(setup:mingit / setup:python-runtime 自身已幂等) +# - 不需要 WSL;用 Git Bash / 捆绑 mingit 二选一 +# - 适配 Node 24(engines: ">=24 <25") + +[CmdletBinding()] +param( + [switch]$SkipMingit, # 跳过 mingit 安装(如果系统已装 Git Bash) + [switch]$SkipPnpm, # 跳过 pnpm 安装 + [int]$VitePort = 5175 +) + +$ErrorActionPreference = 'Stop' +$ProjectRoot = Resolve-Path (Join-Path $PSScriptRoot '..') +Set-Location $ProjectRoot + +function Write-Section($msg) { + Write-Host "" + Write-Host "==> $msg" -ForegroundColor Cyan +} + +# ============================================================ +# 前置检查 +# ============================================================ +Write-Section "检查环境" + +# 1. Node 版本(engines: >=24 <25) +$nodeVersion = (node --version) 2>$null +if (-not $nodeVersion) { + throw "未检测到 node。请先安装 Node 24 LTS:https://nodejs.org/" +} +$nodeMajor = [int]($nodeVersion -replace '^v(\d+)\..*$', '$1') +if ($nodeMajor -lt 24) { + throw "WeSight 要求 Node 24+,当前是 $nodeVersion" +} +Write-Host " Node: $nodeVersion" -ForegroundColor Green + +# 2. npm +$npmVersion = (npm --version) 2>$null +if (-not $npmVersion) { + throw "未检测到 npm。请用 Node 24 自带 npm" +} +Write-Host " npm: $npmVersion" -ForegroundColor Green + +# 3. bash(dev 模式不会调 bash,但 postinstall 的 patch-package 可能在边缘场景触发) +# 这一步只警告,不阻断 +$bashLocations = @() +try { + $bashLocations = (where.exe bash 2>$null) -split "`r?`n" | Where-Object { $_ } +} catch {} +if ($bashLocations.Count -gt 0) { + $gitBash = $bashLocations | Where-Object { $_ -notmatch 'WindowsApps' } | Select-Object -First 1 + if ($gitBash) { + Write-Host " bash: $gitBash" -ForegroundColor Green + } else { + Write-Host " bash: 仅检测到 WSL bash(不推荐;dev 模式可接受,pack 模式需要 Git Bash)" -ForegroundColor Yellow + } +} else { + Write-Host " bash: 未检测到(dev 模式不需要;pack 模式必须装 Git Bash 或跑 setup:mingit)" -ForegroundColor Yellow +} + +# ============================================================ +# 依赖安装 +# ============================================================ +Write-Section "安装 npm 依赖(含原生模块 rebuild)" +# postinstall 会自动跑: +# 1. patch-package(应用 patches/ 下的 patch) +# 2. electron-builder install-app-deps(为 better-sqlite3 等原生模块装对应 Electron ABI 版本) +# 这一步是 dev 模式最大卡点,必须在 Windows 上原生编译。 +# 编译工具链需求:Visual Studio Build Tools 2022 with "Desktop development with C++" +# (含 Windows 10/11 SDK + MSVC v143 + C++ CMake tools) +# 下载:https://visualstudio.microsoft.com/visual-cpp-build-tools/ +# 如果上一步没有 MSBuild,下面这条会失败。 + +if (Test-Path node_modules) { + Write-Host " node_modules 已存在;跳过 npm install(如果你想强制重装:Remove-Item -Recurse node_modules)" -ForegroundColor Yellow +} else { + npm install + if ($LASTEXITCODE -ne 0) { + throw "npm install 失败。如果错误信息是 node-gyp / MSB 找不到,先装 Visual Studio Build Tools 2022" + } +} + +# ============================================================ +# 启动 vite + electron +# ============================================================ +Write-Section "启动 dev 模式(vite 5175 + electron)" +Write-Host " 接下来会:先清 dist-electron,跑 tsc --project electron-tsconfig.json," -ForegroundColor Gray +Write-Host " 然后并发拉起 vite 和 electron。首次会编译主进程 + 渲染进程。" -ForegroundColor Gray +Write-Host " 关闭任一窗口即终止整个 dev 会话。" -ForegroundColor Gray +Write-Host "" + +# electron:dev = concurrently "vite --port 5175" "wait-on ... && start:electron" +# start:electron = cross-env NODE_ENV=development ELECTRON_START_URL=http://localhost:5175 electron . +npm run electron:dev -- --port $VitePort diff --git a/scripts/windows-dist-quickstart.ps1 b/scripts/windows-dist-quickstart.ps1 new file mode 100644 index 00000000..5b34fe03 --- /dev/null +++ b/scripts/windows-dist-quickstart.ps1 @@ -0,0 +1,167 @@ +# WeSight Windows 打包 + 安装一键脚本 +# 用途:在 Windows 11 x64 上产出 NSIS 安装器,并做端到端验证 +# 调用:powershell -ExecutionPolicy Bypass -File scripts/windows-dist-quickstart.ps1 +# +# 流程: +# 1. 装依赖(含 native rebuild) +# 2. 装 mingit bash(若没装 Git Bash)—— openclaw runtime 脚本要 bash +# 3. 装 openclaw runtime for win-x64(pnpm install + build + asar pack,约 5-10 分钟) +# 4. 装 python runtime 到 resources/python-win +# 5. build vite + tsc +# 6. build skills +# 7. compile electron(tsc --project electron-tsconfig.json) +# 8. electron-builder --win --x64(NSIS) +# 9. (可选)静默安装 + 启动 + 卸载 smoke test + +[CmdletBinding()] +param( + [switch]$SkipRuntime, # 跳过 openclaw runtime 构建(仅当 vendor/openclaw-runtime/win-x64 已存在时) + [switch]$SkipPython, # 跳过 python runtime 准备(仅当 resources/python-win/python.exe 已存在时) + [switch]$NoSmokeTest, # 跳过装/卸 smoke test + [string]$InstallerPath # 自定义安装器路径(默认自动从 release/ 找) +) + +$ErrorActionPreference = 'Stop' +$ProjectRoot = Resolve-Path (Join-Path $PSScriptRoot '..') +Set-Location $ProjectRoot + +function Write-Section($msg) { + Write-Host "" + Write-Host "==> $msg" -ForegroundColor Cyan +} + +# ============================================================ +# 前置检查 +# ============================================================ +Write-Section "检查环境" +$nodeVersion = (node --version) 2>$null +if (-not $nodeVersion) { throw "未检测到 node" } +Write-Host " Node: $nodeVersion" -ForegroundColor Green + +# bash 必须有(mingit 或 Git Bash 二选一) +$bashAvailable = (Test-Path "resources/mingit/bin/bash.exe") -or (Test-Path "resources/mingit/usr/bin/bash.exe") +if (-not $bashAvailable) { + $systemBash = (where.exe bash 2>$null | Where-Object { $_ -notmatch 'WindowsApps' }) | Select-Object -First 1 + if ($systemBash) { + Write-Host " bash: $systemBash" -ForegroundColor Green + $bashAvailable = $true + } +} +if (-not $bashAvailable) { + Write-Host " bash: 未检测到;将自动安装 mingit" -ForegroundColor Yellow +} + +# ============================================================ +# 1. npm install +# ============================================================ +Write-Section "1/9 装依赖" +if (-not (Test-Path node_modules)) { + npm install + if ($LASTEXITCODE -ne 0) { throw "npm install 失败" } +} else { + Write-Host " node_modules 已存在,跳过" -ForegroundColor Yellow +} + +# ============================================================ +# 2. mingit bash(OpenClaw runtime 构建脚本用) +# ============================================================ +Write-Section "2/9 装 mingit bash(若未装 Git Bash)" +if (-not $bashAvailable) { + npm run setup:mingit -- --required + if ($LASTEXITCODE -ne 0) { throw "setup:mingit 失败" } +} else { + Write-Host " bash 已就绪,跳过" -ForegroundColor Yellow +} + +# ============================================================ +# 3. openclaw runtime for win-x64 +# ============================================================ +Write-Section "3/9 装 openclaw runtime for win-x64" +$runtimeDir = "vendor/openclaw-runtime/win-x64" +if ($SkipRuntime -and (Test-Path $runtimeDir)) { + Write-Host " --SkipRuntime 且 $runtimeDir 已存在,跳过" -ForegroundColor Yellow +} elseif (Test-Path $runtimeDir) { + Write-Host " $runtimeDir 已存在(脚本会基于 fingerprint 判定是否需要重建)" -ForegroundColor Yellow + npm run openclaw:runtime:win-x64 + if ($LASTEXITCODE -ne 0) { throw "openclaw:runtime:win-x64 失败" } +} else { + Write-Host " 首次构建:pnpm install + pnpm build + pnpm ui:build + npm pack + 装 production deps + 打包 gateway.asar" -ForegroundColor Gray + Write-Host " 预计 5-10 分钟,依赖网络(corepack 拉 pnpm、npm 拉 production deps)" -ForegroundColor Gray + npm run openclaw:runtime:win-x64 + if ($LASTEXITCODE -ne 0) { throw "openclaw:runtime:win-x64 失败" } +} + +# ============================================================ +# 4. python runtime +# ============================================================ +Write-Section "4/9 准备 Python runtime 到 resources/python-win" +$pythonExe = "resources/python-win/python.exe" +if ($SkipPython -and (Test-Path $pythonExe)) { + Write-Host " --SkipPython 且 $pythonExe 已存在,跳过" -ForegroundColor Yellow +} else { + # electron-builder 钩子在 beforePack 阶段会跑 ensurePortablePythonRuntime({ required: true }) + # 这一步可以提前跑(脚本自身幂等),便于快速失败 + npm run setup:python-runtime -- --required + if ($LASTEXITCODE -ne 0) { throw "setup:python-runtime 失败" } +} + +# ============================================================ +# 5. build vite +# ============================================================ +Write-Section "5/9 vite build (渲染进程)" +npm run build +if ($LASTEXITCODE -ne 0) { throw "vite build 失败" } + +# ============================================================ +# 6. build skills +# ============================================================ +Write-Section "6/9 build skills (web-search / tech-news / email)" +npm run build:skills +if ($LASTEXITCODE -ne 0) { throw "build:skills 失败" } + +# ============================================================ +# 7. compile electron +# ============================================================ +Write-Section "7/9 tsc compile:electron (主进程)" +# precompile:electron 会触发 electron-builder install-app-deps(再次确认原生模块 ABI 匹配) +npm run compile:electron +if ($LASTEXITCODE -ne 0) { throw "compile:electron 失败" } + +# ============================================================ +# 8. electron-builder --win --x64 +# ============================================================ +Write-Section "8/9 electron-builder --win --x64 (NSIS 安装器)" +Write-Host " 产物输出:release/WeSight Setup *.exe" -ForegroundColor Gray +Write-Host " 预计 3-8 分钟" -ForegroundColor Gray +npm run dist:win +if ($LASTEXITCODE -ne 0) { throw "dist:win 失败" } + +# ============================================================ +# 9. smoke test (optional) +# ============================================================ +Write-Section "9/9 smoke test" +if ($NoSmokeTest) { + Write-Host " --NoSmokeTest,跳过" -ForegroundColor Yellow +} else { + $installer = if ($InstallerPath) { + Resolve-Path $InstallerPath + } else { + Get-ChildItem "release/WeSight Setup *.exe" -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1 + } + if (-not $installer) { + Write-Host " 未找到安装器,跳过 smoke test" -ForegroundColor Yellow + } else { + Write-Host " 安装器: $installer" -ForegroundColor Green + Write-Host " 接下来你可以:" -ForegroundColor Cyan + Write-Host " 1. 双击安装器,按提示安装" -ForegroundColor Gray + Write-Host " 2. 启动 WeSight,检查任务计划程序里 'WeSightAutostart' 是否被创建" -ForegroundColor Gray + Write-Host " 3. 触发一次系统睡眠 / 唤醒,观察 agent 是否自动恢复" -ForegroundColor Gray + Write-Host " 4. 在'设置 → 引擎'里切换 Hermes/OpenClaw/Codex,看是否可用" -ForegroundColor Gray + Write-Host " 5. 控制面板卸载 WeSight,观察安装目录是否被完整清空" -ForegroundColor Gray + Write-Host " 自动 smoke test 需要管理员权限(NSIS 提权)" -ForegroundColor Gray + Write-Host " 如需无人工介入的自动 smoke test,建议用 CI runner(GitHub Actions windows-latest)" -ForegroundColor Gray + } +} + +Write-Section "完成" +Write-Host " release/WeSight Setup *.exe 已就绪" -ForegroundColor Green