@h-ai/deploy 0.1.0-alpha.64 → 0.1.0-alpha.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,243 +1,145 @@
1
1
  # @h-ai/deploy
2
2
 
3
- [简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
3
+ [简体中文](README.md) | [English](README.en.md) | [日本語](README.ja.md)
4
4
 
5
- [框架首页](../../README.md) · 相关模块:[@h-ai/core](../core/README.md)
5
+ 部署 SDK,也是 `@h-ai/cli` 的 `hai deploy` 后端。**只部署已有产物,不编译源码、不构建镜像。**
6
6
 
7
- 自动化部署模块,支持两种部署方式:一键将 SvelteKit 应用部署到 Vercel(并自动开通云基础设施),或将应用容器化后通过 SSH 部署到任意 Linux 主机(docker-ssh)。
7
+ 完整的创建、开发、构建与部署流程见[使用指南](../../docs/usage/README.md);下文侧重部署准备、SDK 与二次开发。
8
8
 
9
- ## 支持的部署方式
9
+ ## 功能
10
10
 
11
- | Provider | 目标 | 说明 |
12
- | ------------ | --------------- | ---------------------------------------------- |
13
- | `vercel` | Vercel 平台 | SvelteKit + adapter-vercel,配合云 Provisioner |
14
- | `docker-ssh` | 任意 Linux 主机 | SvelteKit + adapter-node,容器化后经 SSH 部署 |
11
+ - `docker-ssh`:读取镜像清单,选择环境和中间件,生成一个 Compose tar;上传到 Linux 后解包、导入镜像、启动并检查健康状态。
12
+ - `--bundle-only`:只生成同样的 tar 和操作说明,不连接 SSH;支持 U 盘或其他手工搬运方式。
13
+ - fullstack 的构建清单含 app/web 两个镜像时一起部署。公开端口提供 Web 和同源 API;Web 不接收 API 的私有变量,支持 hash 页面刷新。
14
+ - `vercel`:上传已有 `.vercel/output` 并设置运行变量;需要网络,按 [Build Output API](https://vercel.com/docs/build-output-api/primitives) 的 prebuilt 协议部署,不执行本地或平台源码构建。
15
+ - 只从目标 importer 的 `pnpm-lock.yaml` 遍历运行依赖(含 workspace link),排除 dev/peer,识别数据库、cache、storage、vecdb、LDAP、邮件和短信等中间件。
16
+ - 中间件逐项选择默认、自定义或保持不变;配置完整时用 `--skip-middleware` 跳过全部处理。Compose 默认支持 PostgreSQL、MySQL、Redis、RustFS/MinIO、Qdrant、pgvector、Chroma、OpenLDAP、Mailpit;云开通支持 Neon、Upstash、R2、Resend、阿里云短信。详细字段见 [环境与中间件](../cli/templates/skills/hai-deploy/middleware.md)。
15
17
 
16
- ## 支持的云服务(Provisioner)
18
+ ## 构建与部署
17
19
 
18
- | 服务类型 | Provisioner | 说明 |
19
- | -------- | ------------- | ---------------------- |
20
- | 数据库 | Neon | PostgreSQL Serverless |
21
- | 缓存 | Upstash | Redis TCP + TLS |
22
- | 存储 | Cloudflare R2 | S3 兼容对象存储 |
23
- | 邮件 | Resend | SMTP(已有已验证域名) |
24
- | 短信 | 阿里云 | 阿里云短信服务 |
20
+ 当前工程要求 Node `^22.22.2 || ^24.15.0 || >=26.0.0`、pnpm 12.11.2。Windows 用 `pnpm.cmd`;Node 20 / pnpm 9.5.0 不受支持。
25
21
 
26
- ## 快速开始
22
+ 生成工程的 `pnpm build` 调用 `hai build`,默认只编译源码。镜像和原生安装包按目标显式构建,**不选择 dev/test/prod,不读部署文件,也不注入运行期凭据**:
27
23
 
28
- ```typescript
29
- import { deploy } from '@h-ai/deploy'
30
-
31
- // 可选:从 ~/.hai/credentials.yml 注入 HAI_DEPLOY_* 环境变量
32
- deploy.credentials.load()
33
-
34
- // 初始化
35
- await deploy.init({
36
- provider: { type: 'vercel', token: 'vel_xxx' },
37
- services: {
38
- db: { provisioner: 'neon', apiKey: 'neon_xxx' },
39
- cache: { provisioner: 'upstash', email: 'a@b.com', apiKey: 'up_xxx' },
40
- },
41
- })
42
-
43
- // 扫描应用依赖
44
- const scan = await deploy.scan('./apps/my-app')
24
+ ```powershell
25
+ pnpm.cmd build
26
+ pnpm.cmd build --target docker
27
+ # 自动选择可用的 Podman,失败后尝试 Docker;也可显式指定
28
+ pnpm.cmd build --target docker --runtime docker
29
+ # 启用 desktop 的 fullstack;需在对应系统上安装 Rust target/系统 SDK/打包工具
30
+ pnpm.cmd build --target desktop --platform windows --arch x64
31
+ ```
45
32
 
46
- // 开通基础设施
47
- const provisions = await deploy.provisionAll('my-app')
33
+ `hai.build.json` 指定 Dockerfile 阶段、context 和 Tauri 目录。compile 只编译源码,docker 导出镜像,desktop 调用 Tauri,具体默认值和示例见[构建参数](../../docs/usage/README.md#build-的可选参数)。版本取应用根 package.json.version;以 1.2.3 为例,输出 `.hai/build/docker/1.2.3-<构建编号>/`,含格式版本 2 的 manifest.json(appVersion 为应用版本)、app-1.2.3.tar、可选 web-1.2.3.tar 和 SHA-256。全部成功才更新 latest。Tauri 使用相同根版本,安装包在对应工程 src-tauri/target/<Rust target>/release/bundle/。各 OS 分别准备主机/runner 与 SDK,不承诺跨系统编译。
48
34
 
49
- // 完整部署
50
- const result = await deploy.deployApp('./apps/my-app')
35
+ fullstack 在工程根执行:
51
36
 
52
- await deploy.close()
37
+ ```powershell
38
+ pnpm.cmd run deploy --env test
39
+ pnpm.cmd run deploy --env prod --skip-middleware
40
+ # 精确复用一次构建,绝不重新构建
41
+ pnpm.cmd run deploy --env prod --artifact-dir .hai/build/docker/<buildId>
42
+ # 服务器网络不通,只交付包
43
+ pnpm.cmd run deploy --env prod --bundle-only --output release-prod.tar
53
44
  ```
54
45
 
55
- ## 配置
46
+ 等价命令为 `pnpm exec hai deploy packages/my-app-serv --env prod --env-dir .`。API workspace 的服务目录为 `apps/my-app-service`。模板已安装项目 CLI;选择 deploy 功能时安装对应 SDK。旧工程需自行安装与框架版本匹配的 `@h-ai/cli`、`@h-ai/deploy`,并提供构建配置和 `_deploy.yml`。
56
47
 
57
- 通过 `config/_deploy.yml` 管理 git 安全的配置,密钥存放在 `~/.hai/credentials.yml`:
48
+ 先检查 `.env.<mode>`;缺少则停止并提示创建。`.env` 公共基础、`.env.dev/test/prod` 环境覆盖和完整说明 `.env.example` 提交 Git;`.env.local` 保存私有值并忽略。优先级:进程 > `.env.local` > 环境文件 > `.env`,整体 JSON 节点优先于子字段。
58
49
 
59
50
  ```yaml
60
- # config/_deploy.yml
51
+ # packages/my-app-serv/config/_deploy.yml
61
52
  provider:
62
- type: vercel
63
- token: ${HAI_DEPLOY_VERCEL_TOKEN}
64
-
65
- services:
66
- db:
67
- provisioner: neon
68
- apiKey: ${HAI_DEPLOY_NEON_API_KEY}
69
- cache:
70
- provisioner: upstash
71
- email: ${HAI_DEPLOY_UPSTASH_EMAIL}
72
- apiKey: ${HAI_DEPLOY_UPSTASH_API_KEY}
53
+ type: docker-ssh
54
+ expose:
55
+ hostPort: 18080
56
+ container:
57
+ s3Engine: ${HAI_DEPLOY_CONTAINER_S3ENGINE:rustfs}
73
58
  ```
74
59
 
75
- 这里的变量由 `deploy.credentials` 管理,属于显式特殊映射;若同时设置
76
- `HAI_DEPLOY_PROVIDER_TOKEN` 等按 YAML 路径生成的约定变量,约定变量优先。
60
+ container.s3Engine 可省略,默认 rustfs;仅自动创建 Compose S3 服务时区分 RustFS/MinIO 的镜像和桶初始化,既有自定义 S3 不受它影响。向导会保存选择,完整说明见[使用指南](../../docs/usage/README.md#先准备环境与部署目标)。跳过全部中间件处理使用 --skip-middleware,SDK 对应 skipMiddleware。
77
61
 
78
- 如需在脚本或 CLI 中显式管理凭证文件,请通过 `deploy.credentials.*` 访问:
62
+ 自动 SSH 部署首次向导填写目标,写入 `.env.local` 后复用;也可直接配置:
79
63
 
80
- ```typescript
81
- deploy.credentials.load()
82
- deploy.credentials.save('HAI_DEPLOY_VERCEL_TOKEN', 'vel_xxx')
83
- deploy.credentials.saveAll({
84
- HAI_DEPLOY_NEON_API_KEY: 'neon_xxx',
85
- })
64
+ ```dotenv
65
+ HAI_DEPLOY_PROVIDER='{"type":"docker-ssh","ssh":{"host":"10.0.0.20","username":"deploy","port":22,"identityFile":"C:/Users/me/.ssh/hai_deploy"},"remote":{"baseDir":"/opt/hai/apps","runtime":"docker"},"expose":{"hostPort":18080,"containerPort":3000}}'
86
66
  ```
87
67
 
88
- > `deploy.scan()` 与 `deploy.credentials.*()` 都是本地文件系统能力,不依赖 `deploy.init()`。
89
-
90
- ## docker-ssh 部署
68
+ 仅打包不要求 SSH 参数。本地部署工具只需要已安装依赖、`tar` 和构建产物;**不需要本地 Docker/Podman**。自动部署另外需要 `ssh/scp`、服务器指纹与非交互登录权限、可写部署目录、SSH 及应用端口可达。
91
69
 
92
- 将应用容器化后通过 SSH 部署到任意 Linux 主机,无需云平台账号。
70
+ ## tar 内容与手工启动
93
71
 
94
- ### 前置条件
72
+ 部署 tar 默认名为 bundle-<项目>-<部署ID>-<应用版本>.tar,自定义 --output release-prod.tar 也会自动得到 release-prod-1.2.3.tar(示例版本 1.2.3)。包含 docker-compose-1.2.3.yml、manifest.json、.env.runtime、可选 web.conf、镜像归档、SHA256SUMS、start-<mode>.sh 和 README.txt。它含私有凭据,权限 600,不提交 Git。packaged 不代表服务器已运行;自动部署上传一个 tar,在远程解包启动,不传源码或构建镜像。
95
73
 
96
- - **本地**:安装 docker 或 podman(`auto` 优先 podman)。
97
- - **远程主机**:可 SSH 登录、已装 docker/podman 与 compose、登录用户可运行容器。
98
- - **应用**:SvelteKit 使用 `@sveltejs/adapter-node`(工程自带的 `Dockerfile` 已满足);缺少时返回 `ADAPTER_MISSING`。
74
+ 将包搬到 Linux 后,按包内 README 操作:
99
75
 
100
- ### 使用步骤
101
-
102
- 1)把 SSH 私钥路径存入凭证文件(不进仓库)。编辑 `~/.hai/credentials.yml`,或用 API:
103
-
104
- ```typescript
105
- deploy.credentials.save('HAI_DEPLOY_SSH_KEY', '~/.ssh/hai_deploy')
76
+ ```sh
77
+ mkdir -p release && chmod 700 release
78
+ tar -xf release-prod-1.2.3.tar -C release
79
+ cd release
80
+ HAI_DEPLOY_PROVIDER_REMOTE_RUNTIME=docker sh start-prod.sh
106
81
  ```
107
82
 
108
- 2)在 `config/_deploy.yml` 声明目标主机:
83
+ 启动脚本自动 -f 选择带版本的 Compose,检查归档和镜像、导入 app/web、导出环境标识后启动。缺中间件镜像时停止,不联网下载。卷按项目和环境隔离,跨版本重复部署保留数据。手工检查使用 `docker compose -f docker-compose-1.2.3.yml --env-file .env.runtime ps`(或 logs),并验证 Web、/health。生产 HTTPS、CORS、DNS 与证书须另行配置。
109
84
 
110
- ```yaml
111
- provider:
112
- type: docker-ssh
113
- ssh:
114
- host: 10.0.0.20 # 主机 IP 或 ~/.ssh/config 别名
115
- port: 22
116
- username: deploy
117
- identityFile: ${HAI_DEPLOY_SSH_KEY} # 私钥文件路径,省略则用 ssh-agent
118
- remote:
119
- baseDir: /opt/hai/apps # 远程部署根目录
120
- runtime: auto # 远程运行时;auto 优先 podman,其次 docker
121
- expose:
122
- type: port
123
- containerPort: 3000 # 容器内端口(adapter-node 默认 3000)
124
- hostPort: 18080 # 宿主机端口 → 访问地址 http://10.0.0.20:18080
85
+ ## 完全离线准备(Windows → Linux)
125
86
 
126
- container:
127
- runtime: auto # 本地构建运行时(可选)
87
+ 已有 PostgreSQL、Redis、RustFS 镜像还不够,准备以下内容:
128
88
 
129
- services: {} # 启用的服务会自动生成 compose sidecar,见下
130
- ```
89
+ 1. Windows 上受支持的 Node/pnpm、完整项目、锁文件和已安装 CLI/SDK 依赖;验证断网后命令可启动。本机无容器引擎时,从构建机复制完整 `.hai/build/docker/<buildId>`。
90
+ 2. 在构建机预先执行 `pnpm build --target docker`,确认镜像架构与服务器一致。离线部署只消费这些 tar,不需要 Node 基础镜像、pnpm store、CUDA、编译器或 SQLite 下载。无 AI 运行依赖的 fullstack 不安装 AI/CUDA。
91
+ 3. 若连构建也要离线,则构建机另需 Linux 容器引擎、所有基础镜像、pnpm/Corepack、完整 Linux 依赖缓存、SQLite 对应驱动版本/Node ABI/架构/libc 的预编译包。Windows node_modules 不能替代 Linux 原生依赖。镜像构建可用 `--sqlite-prebuild-mirror http://<可信地址>/better-sqlite3`;下载失败应修复缓存/网络,不在 deploy 安装编译环境。
92
+ 4. Linux 上安装对应架构 Docker/Podman、可执行的 Compose 命令、`sh`、`tar`、`sha256sum`;运行用户有容器/目录/卷权限。中间件镜像**名称和标签**须与生成 README 一致,先用 `docker load` 导入;SDK 不自动捆绑中间件镜像。自定义连接则要准备已运行的服务、账号、数据库/桶、容器可达地址。
93
+ 5. `.env.prod`、私有管理员密码/连接密钥、部署公开端口、持久化磁盘和必要证书。切勿把容器内 localhost 当服务器宿主机。
94
+ 6. 无任何两机网络时,用 `--bundle-only`,手工复制包;有内网但无互联网时也可 SSH 自动部署。云 Provider/Provisioner 不适用于完全离线,应选 Compose 或自定义已有服务。
131
95
 
132
- 3)部署并验证(CLI 或 API,二者等价):
96
+ 离线验收须实际断开外网后打包/搬运/启动、登录、页面刷新、重启和数据持久化;联网通过不等于断网通过。
133
97
 
134
- ```bash
135
- hai deploy # 读取 config/_deploy.yml,输出访问地址
136
- ```
98
+ ## Node API
137
99
 
138
- ```typescript
100
+ ```ts
139
101
  import { deploy } from '@h-ai/deploy'
140
102
 
141
- deploy.credentials.load() // 注入 HAI_DEPLOY_* 环境变量
142
- await deploy.init(dockerSshConfig)
143
- const result = await deploy.deployApp('./apps/my-app')
144
- if (result.success) {
145
- result.data.url // http://<host>:<hostPort>
146
- result.data.healthCheck // 'passed'(HTTP 探测成功)
103
+ const initialized = await deploy.init({
104
+ provider: { type: 'docker-ssh', expose: { hostPort: 18080 } },
105
+ })
106
+ if (!initialized.success)
107
+ throw new Error(initialized.error.message)
108
+ try {
109
+ const result = await deploy.deployApp('./packages/my-app-serv', {
110
+ environment: 'prod',
111
+ envDir: '.',
112
+ bundleOnly: true,
113
+ artifactDir: './.hai/build/docker/<buildId>',
114
+ skipMiddleware: true,
115
+ })
116
+ if (!result.success)
117
+ throw new Error(result.error.message)
118
+ // result.data.bundlePath / instructions;packaged 不代表服务器 ready。
147
119
  }
148
- await deploy.close()
149
- ```
150
-
151
- ### 实现逻辑
152
-
153
- `deployApp()` 对 docker-ssh 依次执行:
154
-
155
- 1. **扫描**:读取 `package.json` 与 `config/*.yml`,识别 `@sveltejs/adapter-node` 与所需服务及其后端类型。
156
- 2. **本地构建**:用工程自带的 `Dockerfile` 构建镜像 `hai-<app>:<部署ID>`(podman 追加 `--format docker --jobs=1`)。
157
- 3. **导出**:`docker/podman save` 导出为 tar。
158
- 4. **预检**:SSH 连通性 + 远程 `docker/podman version` 与 `compose version`。
159
- 5. **传输加载**:`scp` 上传 tar → 远程 `load` → 重打为 `localhost/` 本地标签(避免误从 registry 拉取)。
160
- 6. **生成运行描述**:按扫描结果生成 `compose.yml`(app + sidecar)与 `.env.runtime`(含 `HAI_*` 覆盖变量与凭据),上传到 `<baseDir>/<app>/`,`.env.runtime` 设为 `600`。
161
- 7. **启动**:`compose up -d`(Compose 项目名固定 `hai-<app>`,重复部署幂等)。
162
- 8. **健康检查**:轮询 `http://<host>:<hostPort>/`,返回 200 才判定 `ready`。
163
-
164
- > 镜像、`compose.yml`、`.env.runtime` 落在应用 `.hai/deploy/`(已在模板 `.gitignore` 忽略)。
165
-
166
- ### compose 按功能生成 sidecar
167
-
168
- `deploy.scan()` 会识别所需服务及其**实际后端类型**(读取 `_db.yml` / `_vecdb.yml` 等的 `type`),生成匹配的 sidecar 并注入对应 `HAI_*` 覆盖变量:
169
-
170
- | 扫描到的服务 | 后端 (`type`) | 追加的 sidecar | 注入变量 |
171
- | ------------ | ------------- | ----------------------------- | ------------- |
172
- | `db` | `postgresql` | `postgres:16-alpine` | `HAI_DB` |
173
- | `db` | `mysql` | `mysql:8.4` | `HAI_DB` |
174
- | `db` | `sqlite` | 无(挂载 `/app/data` 数据卷) | — |
175
- | `cache` | `redis` | `redis:7-alpine` | `HAI_CACHE` |
176
- | `storage` | `s3` | `minio` + 初始化 bucket | `HAI_STORAGE` |
177
- | `vecdb` | `qdrant` | `qdrant/qdrant` | `HAI_VECDB` |
178
- | `vecdb` | `pgvector` | `pgvector/pgvector` | `HAI_VECDB` |
179
- | `vecdb` | `chroma` | `chromadb/chroma` | `HAI_VECDB` |
180
- | `vecdb` | `lancedb` | 无(挂载 `/app/data` 数据卷) | — |
181
-
182
- - `cache` 为 `memory`、`reach`(邮件/短信)为外部 API,均不生成 sidecar。
183
- - 若同一服务已配置云 Provisioner(如 Neon 提供 `HAI_DB`),优先用云资源,不再自建 sidecar。
184
- - sidecar 的密码等敏感值仅写入远程 `.env.runtime`(`600`),不会进入 `compose.yml`。
185
-
186
- > 安全:`identityFile` 为私钥**路径**(非私钥内容);`strictHostKeyChecking: true`(默认)
187
- > 采用 accept-new(首次信任、拒绝变更)。首次连接未知主机可先手动 `ssh` 一次。
188
-
189
- ## 错误处理
190
-
191
- ```typescript
192
- import { HaiDeployError } from '@h-ai/deploy'
193
-
194
- const result = await deploy.deployApp('./apps/my-app')
195
- if (!result.success) {
196
- switch (result.error.code) {
197
- case HaiDeployError.AUTH_FAILED.code:
198
- // Vercel Token 无效
199
- break
200
- case HaiDeployError.BUILD_FAILED.code:
201
- // 构建失败
202
- break
203
- }
120
+ finally {
121
+ await deploy.close()
204
122
  }
205
123
  ```
206
124
 
207
- 常用错误码:
208
-
209
- | 错误码 | code | 说明 |
210
- | --------------------------------- | ---------------- | ------------ |
211
- | `HaiDeployError.DEPLOY_FAILED` | `hai:deploy:001` | 部署失败 |
212
- | `HaiDeployError.BUILD_FAILED` | `hai:deploy:003` | 构建失败 |
213
- | `HaiDeployError.AUTH_FAILED` | `hai:deploy:006` | 认证失败 |
214
- | `HaiDeployError.PROVISION_FAILED` | `hai:deploy:007` | 资源开通失败 |
215
- | `HaiDeployError.NOT_INITIALIZED` | `hai:deploy:010` | 未初始化 |
216
- | `HaiDeployError.ENV_VAR_FAILED` | `hai:deploy:011` | 环境变量失败 |
125
+ `deploy.scan` 与 `deploy.credentials` 不依赖 init;业务 API 返回 HaiResult,先检查 success。资源开通失败保留恢复信息,不自动删除已创建资源。
217
126
 
218
- docker-ssh 专属错误码:
127
+ ## 二次开发与验证
219
128
 
220
- | 错误码 | code | 说明 |
221
- | -------------------------------------------- | ---------------- | ---------------- |
222
- | `HaiDeployError.CONTAINER_RUNTIME_NOT_FOUND` | `hai:deploy:015` | 未找到容器运行时 |
223
- | `HaiDeployError.CONTAINER_BUILD_FAILED` | `hai:deploy:016` | 镜像构建失败 |
224
- | `HaiDeployError.REMOTE_CONNECT_FAILED` | `hai:deploy:017` | 远程连接失败 |
225
- | `HaiDeployError.REMOTE_COMMAND_FAILED` | `hai:deploy:018` | 远程命令失败 |
226
- | `HaiDeployError.IMAGE_TRANSFER_FAILED` | `hai:deploy:019` | 镜像传输失败 |
227
- | `HaiDeployError.HEALTH_CHECK_FAILED` | `hai:deploy:020` | 健康检查未通过 |
228
-
229
- ## 测试
230
-
231
- ```bash
232
- pnpm --filter @h-ai/deploy test
129
+ ```powershell
130
+ pnpm.cmd --filter @h-ai/deploy typecheck
131
+ pnpm.cmd --filter @h-ai/deploy lint
132
+ pnpm.cmd --filter @h-ai/deploy build
133
+ pnpm.cmd --filter @h-ai/deploy test
134
+ pnpm.cmd --filter @h-ai/cli test
135
+ pnpm.cmd --filter @h-ai/cli test:scaffold-gates
136
+ pnpm.cmd --filter @h-ai/cli test:deploy-gates
233
137
  ```
234
138
 
235
- ## License
236
-
237
- Apache-2.0
139
+ 最后一项使用当前源码打包生成 fullstack,显式构建一次 API/Web,再验证 SSH 上传和手工 tar 启动、真实 Web 登录/刷新。需要运行的 Podman/Docker 和 OpenSSH。测试用 finally/teardown 清理临时工程、镜像、容器、报告、截图和辅助进程,失败同样清理;验收材料不提交 Git。原生安装包需在相应系统另做真实构建及安装验收。
238
140
 
239
- 云资源配置消费:Neon 输出 HAI_DB(PostgreSQL JSON),Upstash 输出 HAI_CACHE(Redis rediss URL JSON),R2 输出 HAI_STORAGE(扁平 S3 JSON)。邮件/短信输出 HAI_REACH_PROVIDERS 数组,deployApp 合并两个渠道并保留 YAML 中的消息模板;此覆盖替换原 providers 数组。Core 支持整体对象/数组覆盖,父节点优先于叶子变量,仍执行 Schema 校验;构建子进程与目标运行环境使用相同开通结果。R2 必须提供已有 S3 accessKeyId/secretAccessKey;Resend 必须提供已验证域名的 from;阿里云必须提供已审核 signName/模板。应用须已有相应\_db/\_cache/\_storage/\_reach.yml 并在启动时加载,网络客户端需要 Node 运行时。
141
+ 部署门禁默认不传递本机代理。需要代理下载构建依赖时,设置测试专用 `HAI_DEPLOY_E2E_CONTAINER_PROXY`(默认不设置),例如 Windows Podman 在 PowerShell 中设置 `$env:HAI_DEPLOY_E2E_CONTAINER_PROXY='http://host.containers.internal:10808'`;地址必须能从构建容器访问,不写入应用配置或交付镜像。
240
142
 
241
- 配置依据:[Upstash TLS](https://upstash.com/docs/redis/features/security)、[Resend SMTP](https://resend.com/docs/send-with-smtp)。确定性验收真实生成应用并通过 Core 和各模块 Schema;未创建云资源、未发送邮件/短信,不能据此声称云端写入读取已验收。
143
+ 测试专用 `HAI_DEPLOY_E2E_CONTAINER_NETWORK` 默认不设置,指定后传给 build 的 `--network`;例如 rootless Podman 可设置 `$env:HAI_DEPLOY_E2E_CONTAINER_NETWORK='pasta'`。构建与运行的网络可能不同,应同时校对代理在构建网络中的地址。
242
144
 
243
- 部署失败恢复:`ProvisionResult.resourceStatus` 标记 created/reused;`getDeployRecovery(result.error)` 读取 projectName、stage、platformProjectId 和资源清单,unknown 表示请求结果不确定,必须先在服务商控制台核对。失败资源已返回 ID 时保留该 ID。恢复结果不含 envVars、原始异常或连接凭证,CLI 会展示清单和建议。同名重试复用现有资源;不自动删除复用资源,新建资源仅在人工确认未被使用后清理。
145
+ Windows runner 可设置测试开关 HAI_CLI_NATIVE_BUILD=1(默认不设置),再执行 `pnpm --filter @h-ai/cli test:scaffold-gates -t "fullstack scaffold"`,额外构建生成项目的 Tauri 安装包,检查根版本 2.4.6 进入 exe 文件名。需已安装 Rust/MSVC/Windows SDK;测试的原生输出也随临时工程自动清理。