# Neta Backend / Windows 安装器打包说明

本文档记录当前 Windows 离线安装器的打包方式、依赖要求、产物路径，以及当前版本仍然存在的缺口。

## 当前状态

当前已经可以产出以下三个文件：

- `build/pkg-output/backend.exe`
- `build/tray-output/Neta.Tray.exe`
- `build/installer-output/neta-setup.exe`

其中：

- `backend.exe` 是前端静态资源 + 后端 Midway.js + Node.js 运行时的单文件产物
- `Neta.Tray.exe` 是 Windows 托盘控制层产物
- `neta-setup.exe` 是基于 Inno Setup 的离线安装器
- `packages/backend/skills` 会随安装器一起打包，并在首次启动时同步到数据目录的 `skills/` 下

## 打包环境要求

建议在 **Windows 10/11 x64** 上打包。

需要提前安装：

### 1. Node.js

建议使用当前项目已验证版本范围内的 Node.js（建议 20+，当前环境也已在 Node 22 下完成打包验证）。

### 2. pnpm

前端构建使用 `pnpm build`。

安装示例：

```bash
npm install -g pnpm
```

### 3. .NET 8 SDK

生成 `tray.exe` 需要安装 .NET 8 SDK。

```bash
winget install Microsoft.DotNet.SDK.8
```

### 4. Inno Setup 6

生成 `neta-setup.exe` 需要安装 Inno Setup 6。

可使用：

```bash
winget install --id JRSoftware.InnoSetup --accept-package-agreements --accept-source-agreements
```

当前脚本会自动尝试从以下位置查找 `ISCC.exe`：

- `%LOCALAPPDATA%\Programs\Inno Setup 6\ISCC.exe`
- `C:\Program Files (x86)\Inno Setup 6\ISCC.exe`
- `C:\Program Files\Inno Setup 6\ISCC.exe`
- 或系统 PATH 中的 `iscc`

## 一键打包步骤

### 方式一：先打 exe，再打安装器

在 `packages/backend` 目录执行：

```bash
npm run pkg
node scripts/build-windows-installer.js
```

### 方式二：直接打安装器

```bash
npm run build:windows-installer
```

说明：

- `build-windows-installer.js` 会先检查 `build/pkg-output/backend.exe`
- 如果不存在，会自动先执行 `scripts/pkg-build.js`
- 然后执行 `dotnet publish` 生成 `build/tray-output/Neta.Tray.exe`
- 最后由 Inno Setup 生成安装器，并把 `packages/backend/skills` 一并带入安装目录

## 当前打包脚本做了什么

### `scripts/pkg-build.js`

执行顺序如下：

1. 构建前端
2. 构建后端
3. 生成 `build/pkg-stage/` staging 目录
4. 把前端产物覆盖到后端 `public/` 中
5. 生成 staging 用的 `package.json`
6. 用 npm 在 staging 目录做扁平化生产依赖安装
7. patch `generator-function` 的 exports，规避 pkg 对 `.mjs` 的兼容问题
8. 执行 `@yao-pkg/pkg` 生成 `backend.exe`

### installer 构建时的前端特殊处理

Windows 安装器构建不是普通线上前端构建，当前脚本会额外注入：

- `VITE_API_BASE_URL=''`
- `VITE_COOL_EPS_ENABLE=true`

这样做的原因：

1. 安装器场景下前后端同源运行，前端 API 需要走本地同源地址
2. installer 场景前端仍依赖构建期生成的 EPS service 代理；若关闭，会导致 `service.base.open` 不存在，从而在登录页初始化时报错

## 产物位置

打包完成后产物位于：

```text
packages/backend/build/pkg-output/backend.exe
packages/backend/build/tray-output/Neta.Tray.exe
packages/backend/build/installer-output/neta-setup.exe
```

## 当前安装器默认行为

### 安装目录

默认安装到：

```text
C:\Program Files\Neta
```

### 当前默认配置文件

安装器会把：

```text
installer/config.default.yaml
```

复制为安装目录下的：

```text
config.yaml
```

### 当前数据目录

当前默认写死为：

```text
C:\NetaData
```

运行数据（日志、lock、runtime-info、skills）都写到这里。

### 内置 Skill 目录

安装器会把：

```text
packages/backend/skills
```

打进安装目录下的：

```text
{app}\skills
```

并在首次启动时把缺失的内置 skill 同步到：

```text
C:\NetaData\skills
```

本地开发（`npm run dev`）仍直接读取仓库内的：

```text
packages/backend/skills
```

### 桌面快捷方式

安装器会创建桌面快捷方式和开始菜单快捷方式，启动 `tray.exe`，由托盘控制层负责附着或拉起 `backend.exe`。

## 当前已知缺口 / 发布前建议补齐项

以下问题目前都已经记录，**其中部分属于发布阻塞项**。

### 1. 数据库账号密码仍然是明文配置（发布阻塞）

当前 `installer/config.default.yaml` 里包含数据库连接信息，安装后会以明文形式写到安装目录下的 `config.yaml`。

当前风险：

- 用户机器上可直接看到数据库账号密码
- 安装目录中的配置文件容易被误拷贝、误传播
- 不适合正式对外发布

建议后续方案：

- 安装器安装时让用户输入数据库连接信息
- 或首次启动时通过配置向导填写
- 或改为读取更安全的部署侧配置（如企业内部分发配置）
- 至少不要把正式生产数据库凭证直接内置到默认配置模板中

### 2. 当前没有自动更新机制

当前安装器只支持重新打包、重新安装，不支持：

- 在线检查更新
- 增量更新
- 回滚
- 版本迁移提示

建议后续补齐：

- 版本检查
- 升级包下载
- 升级失败回滚
- 配置/数据迁移策略

### 3. Windows 托盘模式

当前安装器已切换为 `tray.exe` 作为用户入口：

- 桌面快捷方式与开始菜单快捷方式指向 `tray.exe`
- 托盘菜单提供“打开系统 / 重启服务 / 停止服务 / 打开日志目录 / 打开配置目录 / 退出程序”
- 托盘优先通过 `runtime-info.json` + 本机 `status/stop` 控制面附着已运行后端
- “打开系统”始终使用 runtime 状态接口返回的真实 URL，因此当 8003 被占用、后端回退到 8004/8005 等端口时，托盘会打开实际运行端口
- 停止服务 / 退出程序 / 卸载都会先尝试优雅停机，再按安装目录路径兜底清理残留 `backend.exe`

### 4. 当前关闭路径

当前停机 / 卸载路径为：

```text
停止服务 / 退出程序
  └─ 优先调用 /app/base/runtime/stop
     └─ 等待 backend 退出
        └─ 若仍存活，则按安装目录路径强杀所有 backend.exe

卸载
  └─ tray.exe --shutdown
     └─ taskkill /IM tray.exe /F
     └─ taskkill /IM backend.exe /F
```

也就是说：

- 优先走托盘触发的本机优雅停机
- 如果 `runtime-info.json` 丢失、stop 接口失败、或 pkg stub / wrapper 残留，仍会按安装目录路径兜底清理 backend 进程
- “退出程序”不只是退出托盘，也会先结束后台服务

### 5. 安装器还没有“数据目录选择页”

设计文档中原本计划支持用户选择数据目录，但当前实现仍然使用默认值：

```text
C:\NetaData
```

也就是说：

- 目前用户可以选程序安装目录
- 但不能在安装器 UI 中选择数据目录
- `config.yaml` 中的数据目录还没有在安装时动态写入

这也是当前版本与设计目标之间的重要差距之一。

### 6. 缺少首次启动配置向导 / 数据库连通性检测

当前安装后直接启动，如果配置不对，只能从日志或错误提示中排查。

建议后续补齐：

- 首次启动向导
- 数据库连接测试
- 数据目录可写性测试
- 端口占用检查提示

### 7. 升级迁移策略还不完整

当前虽然已经做到“程序目录 / 数据目录分离”，但还缺少：

- `config.yaml` schema 版本迁移
- 数据目录结构升级迁移
- 不同版本安装器对旧配置的兼容检查

### 8. 当前更偏向“内测版安装器”，还不适合作为正式商用发布版

当前版本已经可用于：

- 内部验证
- 本地演示
- 离线安装链路测试

但如果要进入正式对外发布，还建议至少补齐：

- 配置安全
- 托盘/服务控制
- 数据目录选择
- 升级更新
- 更友好的故障提示和日志查看入口

## 推荐的下一阶段改造优先级

建议按下面顺序推进：

1. **去掉明文数据库凭证**
2. **实现安装器数据目录选择 + 写回 config.yaml**
3. ~~增加 Windows 托盘控制入口~~ ✅ 已完成
4. ~~实现优雅停止服务~~ ✅ 已完成
5. **补自动更新机制**

## 验证命令

重新打包后，可用以下命令验证：

```bash
cd packages/backend
npm run build:windows-installer
```

验证产物：

```bash
ls -lh build/pkg-output/backend.exe
ls -lh build/tray-output/Neta.Tray.exe
ls -lh build/installer-output/neta-setup.exe
```

### Windows 托盘模式验证

验证点：

- `tray.exe` 能读取 `config.yaml` 与 `runtime-info.json`
- 托盘能附着或拉起 `backend.exe`
- “打开系统”使用 runtime 状态接口返回的真实 URL（包括 8003 被占用时的回退端口）
- 内置 skill 会从安装目录 `skills/` 同步到数据目录 `skills/`
- Skill 管理页能读到内置 skill，Tool 管理页能读到工具 catalog
- “停止服务”能让 backend 退出；再次“打开系统”后托盘状态会恢复为“运行中”
- “退出程序”会先结束 backend，再退出 tray
- 卸载时优先调用 `tray.exe --shutdown`
- 若优雅停机失败，仍会兜底清理安装目录对应的 `backend.exe`

如果只验证 exe：

```bash
cp installer/config.default.yaml build/pkg-output/config.yaml
build/pkg-output/backend.exe
```

## 备注

当前 README 记录的是 **截至本次 Windows 安装器实现后的真实状态**，不是目标态说明。后续若补齐托盘、自动更新、安装器数据目录选择等能力，需要同步更新本文件。