@h-ai/cli 0.1.0-alpha.65 → 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.
Files changed (69) hide show
  1. package/README.en.md +13 -7
  2. package/README.ja.md +13 -7
  3. package/README.md +14 -8
  4. package/dist/{chunk-IPG3QNYK.js → chunk-FWN5B54L.js} +36 -4
  5. package/dist/chunk-FWN5B54L.js.map +1 -0
  6. package/dist/{chunk-47V5OT22.js → chunk-QYX6V7H2.js} +17 -43
  7. package/dist/chunk-QYX6V7H2.js.map +1 -0
  8. package/dist/cli-config-templates-3XSFIXBP.js +5 -0
  9. package/dist/{cli-config-templates-SJ2MBIR7.js.map → cli-config-templates-3XSFIXBP.js.map} +1 -1
  10. package/dist/{cli-skill-templates-A5VIR5J6.js → cli-skill-templates-TSYEQTCY.js} +3 -3
  11. package/dist/{cli-skill-templates-A5VIR5J6.js.map → cli-skill-templates-TSYEQTCY.js.map} +1 -1
  12. package/dist/index.js +273 -208
  13. package/dist/index.js.map +1 -1
  14. package/dist/{src-6PPBKQ6Q.js → src-RDQ2EIBT.js} +506 -488
  15. package/dist/src-RDQ2EIBT.js.map +1 -0
  16. package/package.json +8 -8
  17. package/templates/apps/api/.dockerignore.hbs +4 -5
  18. package/templates/apps/api/.gitignore.hbs +2 -1
  19. package/templates/apps/api/Dockerfile.hbs +2 -15
  20. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +2 -2
  21. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +0 -1
  22. package/templates/apps/api/hai.build.json.hbs +1 -0
  23. package/templates/apps/api/package.json.hbs +5 -8
  24. package/templates/apps/fullstack/.dockerignore.hbs +4 -5
  25. package/templates/apps/fullstack/.gitignore.hbs +2 -1
  26. package/templates/apps/fullstack/Dockerfile.hbs +3 -15
  27. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +0 -3
  28. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +0 -2
  29. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
  30. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -6
  31. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +2 -3
  32. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +73 -2
  33. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +2 -2
  34. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +2 -2
  35. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -6
  36. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +0 -2
  37. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
  38. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +1 -0
  39. package/templates/apps/fullstack/hai.build.json.hbs +7 -0
  40. package/templates/apps/fullstack/package.json.hbs +5 -12
  41. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +2 -2
  42. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +0 -1
  43. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +1 -1
  44. package/templates/apps/mobile-app/index.html.hbs +1 -0
  45. package/templates/apps/mobile-app/public/runtime-config.js +1 -0
  46. package/templates/apps/mobile-app/src/lib/api.ts.hbs +2 -1
  47. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +3 -2
  48. package/templates/base/.dockerignore.hbs +4 -5
  49. package/templates/base/.gitignore.hbs +2 -1
  50. package/templates/base/Dockerfile.hbs +5 -22
  51. package/templates/base/docker/nginx.conf.hbs +6 -0
  52. package/templates/base/hai.build.json.hbs +1 -0
  53. package/templates/base/package.json.hbs +13 -17
  54. package/templates/base/svelte.config.js.hbs +2 -0
  55. package/templates/base/vite.config.ts.hbs +6 -2
  56. package/templates/shared/.gitattributes +5 -0
  57. package/templates/shared/container-runtime-config.sh +30 -0
  58. package/templates/shared/run-e2e.mjs +65 -0
  59. package/templates/shared/vite-runtime-config.ts +54 -0
  60. package/templates/skills/hai-app-create/development.md +3 -1
  61. package/templates/skills/hai-app-create/reference.md +2 -2
  62. package/templates/skills/hai-build/SKILL.md +16 -1
  63. package/templates/skills/hai-deploy/SKILL.md +52 -260
  64. package/templates/skills/hai-deploy/middleware.md +8 -14
  65. package/templates/skills/hai-ui/reference.md +2 -0
  66. package/dist/chunk-47V5OT22.js.map +0 -1
  67. package/dist/chunk-IPG3QNYK.js.map +0 -1
  68. package/dist/cli-config-templates-SJ2MBIR7.js +0 -5
  69. package/dist/src-6PPBKQ6Q.js.map +0 -1
@@ -1,292 +1,84 @@
1
1
  ---
2
2
  name: hai-deploy
3
- description: "使用 @h-ai/deploy 部署应用:Vercel 云平台或 docker-ssh 容器化部署到任意 Linux 主机,并按需开通/生成基础设施。"
3
+ description: "部署已有构建产物:配置环境与中间件,生成 Compose tar 并通过 SSH 或手工搬运发布;也支持 Vercel。"
4
4
  ---
5
5
 
6
6
  # hai-deploy
7
7
 
8
8
  ## 能力契约
9
9
 
10
- `hai deploy` 在模块/配置缺失或凭证、扫描、初始化、部署、关闭失败时返回非零退出码。自动化流程必须以退出码判断成功,不要只判断命令是否完成或是否输出日志。
11
-
12
10
  | 项目 | 契约 |
13
11
  | --- | --- |
14
- | 能力 | 使用 @h-ai/deploy 部署应用:Vercel 云平台或 docker-ssh 容器化部署到任意 Linux 主机 |
15
- | 适用场景 | 部署目标配置、云资源开通、SSH 主机容器部署或部署故障排查 |
16
- | 输入 | 项目构建产物、部署目标(vercel / docker-ssh)、环境配置与凭据引用 |
17
- | 输出 | 部署/资源操作结果和可验证的目标状态(含访问 URL 与健康检查) |
18
- | 限制 | 核对实际 provider 与应用运行时;开通收费资源、发布或删除必须在用户授权范围内,凭据不写入仓库。 |
19
-
20
- > `@h-ai/deploy` 提供两种部署方式:
21
- > 1. **vercel** — 将 SvelteKit(adapter-vercel)应用部署到 Vercel,并自动开通 Neon/Upstash/R2/Resend/阿里云等云基础设施;
22
- > 2. **docker-ssh** — 将原生 fullstack API 或 SvelteKit(adapter-node)应用容器化后经 SSH 部署到任意具备 docker/podman + compose 的 Linux 主机,按所选功能自动生成 compose sidecar。
23
-
24
- ## 运行环境
25
-
26
- > ⚠️ **Node.js CLI / 服务端模块。** 通过 `hai deploy` CLI 命令或在 Node.js 脚本中使用。
27
- > docker-ssh 方式要求本地安装 docker 或 podman(`auto` 优先 podman)。
28
-
29
- 当前 Node 要求 `^22.22.2 || ^24.15.0 || >=26.0.0`,生成工程固定 pnpm 12.11.2;Node 20 / pnpm 9.5.0 不受支持。Windows 必须有已启动、能构建目标 Linux 架构的容器引擎,不能把仅安装客户端或远程有 Docker 当作本地可构建。
30
-
31
- ## 适用场景
32
-
33
- - 一键部署 SvelteKit 应用到 Vercel
34
- - 容器化后经 SSH 部署到自有服务器 / 云主机 / 内网虚拟机(docker-ssh)
35
- - 自动开通 PostgreSQL、Redis、S3 等云服务,或按功能生成自建 sidecar
36
- - 管理部署凭证(~/.hai/credentials.yml)
37
- - 扫描应用依赖,自动检测所需服务
38
- - CLI 部署流程(`hai deploy`)
39
-
40
- ## 使用步骤
41
-
42
- 部署前按 [环境与中间件向导](middleware.md) 先选择环境并检查对应文件,再逐项选择默认配置、自定义连接或保持不变。用户确认配置已完备时,可选择直接部署或传 `--skip-middleware` 跳过全部中间件处理。SSH 地址、端口、密钥路径及平台 Token 首次填写后仅保存 `.env.local`,后续自动复用。该文档列出模块依赖、后端、连接字段、云开通边界和启动验证要求。
43
-
44
- ### 1. 配置
45
-
46
- ```yaml
47
- # config/_deploy.yml
48
- provider:
49
- type: vercel
50
- token: ${HAI_DEPLOY_PROVIDER_TOKEN}
51
-
52
- services:
53
- db:
54
- provisioner: neon
55
- apiKey: ${HAI_DEPLOY_SERVICES_DB_APIKEY}
56
- cache:
57
- provisioner: upstash
58
- email: ${HAI_DEPLOY_SERVICES_CACHE_EMAIL}
59
- apiKey: ${HAI_DEPLOY_SERVICES_CACHE_APIKEY}
60
- storage:
61
- provisioner: cloudflare-r2
62
- accountId: ${HAI_DEPLOY_SERVICES_STORAGE_ACCOUNTID}
63
- apiToken: ${HAI_DEPLOY_SERVICES_STORAGE_APITOKEN}
64
- accessKeyId: ${HAI_DEPLOY_SERVICES_STORAGE_ACCESSKEYID}
65
- secretAccessKey: ${HAI_DEPLOY_SERVICES_STORAGE_SECRETACCESSKEY}
66
- email:
67
- provisioner: resend
68
- apiKey: ${HAI_DEPLOY_SERVICES_EMAIL_APIKEY}
69
- from: ${HAI_DEPLOY_SERVICES_EMAIL_FROM}
70
- sms:
71
- provisioner: aliyun
72
- accessKeyId: ${HAI_DEPLOY_SERVICES_SMS_ACCESSKEYID}
73
- accessKeySecret: ${HAI_DEPLOY_SERVICES_SMS_ACCESSKEYSECRET}
74
- signName: ${HAI_DEPLOY_SERVICES_SMS_SIGNNAME}
12
+ | 能力 | 消费已有镜像或平台产物,配置环境、中间件并部署 |
13
+ | 适用场景 | SSH 自动部署、离线手工交付、Vercel 发布和部署故障排查 |
14
+ | 输入 | 完整构建产物、pnpm-lock.yaml、目标环境文件、部署配置和私有凭据引用 |
15
+ | 输出 | Compose tar 与手工说明,或已检查的在线部署结果 |
16
+ | 限制 | deploy 不编译/构建,不提供 skip-build 兼容路径;收费开通/发布/删除须在用户授权范围内。packaged 不代表 ready。 |
17
+
18
+ ## 操作流程
19
+
20
+ 1. 读取当前 package.json、`hai.build.json`、构建清单和 `_deploy.yml`;构建入口见 [hai-build](../hai-build/SKILL.md)。先显式运行 `pnpm build --target docker`,缺产物不得在 deploy 中隐式补构建。
21
+ 2. 选择环境,检查 `.env.<mode>`;缺失则让用户创建后再继续。六类环境文件和变量优先级见 [中间件与环境](middleware.md)。
22
+ 3. 逐项展示 lockfile 运行依赖要求的中间件、当前后端与默认用途,提供默认/自定义/完整配置时保持不变。用户表示配置齐全或传 `--skip-middleware` 时跳过全部处理,不重写连接或开通资源。
23
+ 4. 检查已有镜像清单和 SHA-256,再生成对应环境的 Compose tar。含 app/web 时一起发布,公开 Web 同源代理 API;私有 env 仅挂载 API。
24
+ 版本以清单 appVersion 为准,不从当前源码猜测;Compose 使用 docker-compose-<版本>.yml,tar 自动补版本后缀,启动与手工 ps/logs 都显式 -f 指定。保留稳定的项目/卷身份,跨版本跳过中间件时复用上次 Compose。CLI 仅使用 --skip-middleware,SDK 仅使用 skipMiddleware。
25
+ 5. 在线模式上传一个 tar,远程解包、导入、compose up,再检查 API/Web;手工模式 `--bundle-only` 不要求 SSH 配置、不连接远端,输出准确的复制/解包/启动步骤及所需镜像标签。
26
+ 6. 失败返回非零退出码;SDK 返回 HaiResult,先检查 success。保留已创建资源的恢复信息,不擅自删除。测试资源在 finally/teardown 清理。
27
+
28
+ ## CLI
29
+
30
+ 使用 `pnpm run deploy` 明确执行项目脚本,避免与 pnpm 自身的 deploy 命令混淆。
31
+
32
+ ```sh
33
+ pnpm build --target docker
34
+ pnpm run deploy --env test
35
+ pnpm run deploy --env prod --skip-middleware
36
+ pnpm run deploy --env prod --artifact-dir .hai/build/docker/<buildId>
37
+ pnpm run deploy --env prod --bundle-only --output release-prod.tar
38
+ # fullstack 的 deploy 脚本等价于
39
+ pnpm exec hai deploy packages/my-app-serv --env prod --env-dir .
75
40
  ```
76
41
 
77
- 这些凭证名由 `deploy.credentials` 管理,属于显式特殊映射;若同时设置
78
- `HAI_DEPLOY_PROVIDER_TOKEN` 等约定变量,约定变量仍拥有最高优先级。
79
-
80
- #### docker-ssh 配置
81
-
82
- 将上面的 `provider` 替换为 docker-ssh(远程主机需 SSH + docker/podman + compose):
42
+ API workspace 的服务目录为 apps/my-app-service。单包使用当前目录。Node/pnpm 版本以项目 engines/packageManager 为准,Windows 用 pnpm.cmd。
83
43
 
84
44
  ```yaml
85
- # config/_deploy.yml
45
+ # 服务包 config/_deploy.yml
86
46
  provider:
87
47
  type: docker-ssh
88
- ssh:
89
- host: ${HAI_DEPLOY_PROVIDER_SSH_HOST}
90
- port: 22
91
- username: deploy
92
- identityFile: ${HAI_DEPLOY_PROVIDER_SSH_IDENTITYFILE} # 私钥文件路径,省略则用 ssh-agent
93
- remote:
94
- baseDir: /opt/hai/apps
95
- runtime: auto # auto 优先 podman,其次 docker
96
48
  expose:
97
- type: port
98
- containerPort: 3000
99
- hostPort: 18080 # 访问地址 http://<host>:18080
100
-
49
+ hostPort: 18080
101
50
  container:
102
- runtime: auto # 本地构建运行时,可选
103
-
104
- services:
105
- # 启用的服务会被加入远程 compose sidecar(postgres/redis/rustfs 或 minio)
106
- # 若配置了云 Provisioner(如 neon),则优先用云资源,不再自建 sidecar
107
- db:
108
- provisioner: neon
109
- apiKey: ${HAI_DEPLOY_SERVICES_DB_APIKEY}
51
+ s3Engine: rustfs # rustfs | minio
110
52
  ```
111
53
 
112
- docker-ssh 要点:
54
+ SSH/平台参数可在向导输入后存入 `.env.local` 的 `HAI_DEPLOY_PROVIDER` 整体 JSON;私钥只存路径,不记录内容。仅打包可省 SSH;在线部署需要 ssh/scp、主机信任和运行权限。完整字段、可选云 Provisioner 及中间件配置见 [middleware.md](middleware.md)。
113
55
 
114
- - 应用镜像优先使用工程自带的 Dockerfile;SvelteKit 需要 adapter-node,原生 fullstack 服务自动生成以 `dist/index.js` 启动的镜像。
115
- - compose 按 `deploy.scan()` 识别的服务**及其后端类型**生成匹配 sidecar 并注入 `HAI_*` 覆盖变量:
116
- - `db`:`postgresql`→postgres、`mysql`→mysql、`sqlite`→数据卷(无 sidecar)→ `HAI_DB`
117
- - `cache`:`redis`→redis → `HAI_CACHE`;`storage`:`s3`→RustFS/MinIO(container.s3Engine) → `HAI_STORAGE`
118
- - `vecdb`:`qdrant`/`pgvector`/`chroma`→对应 sidecar、`lancedb`→数据卷 → `HAI_VECDB`
119
- - 若同一服务已配置云 Provisioner(如 neon 提供 `HAI_DB`),优先用云资源,不再自建 sidecar
120
- - sidecar 密码写被忽略的本机私有配置及远程 `.env.runtime`(权限 600),不入公共环境文件或 `compose.yml`
121
- - `identityFile` 是私钥**路径**(非私钥内容);禁止把私钥内容写进 `_deploy.yml`
56
+ ## SDK
122
57
 
123
- ### 2. 初始化与关闭
124
-
125
- ```typescript
58
+ ```ts
126
59
  import { deploy } from '@h-ai/deploy'
127
60
 
128
- deploy.credentials.load()
129
-
130
- await deploy.init({
131
- provider: { type: 'vercel', token: 'vel_xxx' },
132
- services: {
133
- db: { provisioner: 'neon', apiKey: 'neon_xxx' },
134
- },
135
- })
136
-
137
- // 使用后关闭
138
- await deploy.close()
139
- ```
140
-
141
- ### 3. 扫描应用
142
-
143
- ```typescript
144
- const scanResult = await deploy.scan('./apps/my-app', { environment: 'test' })
145
- if (scanResult.success) {
146
- const { appName, isSvelteKit, requiredServices } = scanResult.data
61
+ const initialized = await deploy.init({ provider: { type: 'docker-ssh', expose: { hostPort: 18080 } } })
62
+ if (!initialized.success) throw new Error(initialized.error.message)
63
+ try {
64
+ const result = await deploy.deployApp('./packages/my-app-serv', {
65
+ environment: 'prod', envDir: '.', bundleOnly: true,
66
+ artifactDir: './.hai/build/docker/<buildId>', skipMiddleware: true,
67
+ })
68
+ if (!result.success) throw new Error(result.error.message)
69
+ // bundlePath/instructions;只有在线部署才有健康检查和可访问 URL。
147
70
  }
148
- ```
149
-
150
- ### 4. 开通基础设施
151
-
152
- ```typescript
153
- const provResults = await deploy.provisionAll('my-project')
154
- if (provResults.success) {
155
- for (const prov of provResults.data) {
156
- // prov.envVars 包含需要注入的环境变量
157
- }
158
- }
159
- ```
160
-
161
- ### 5. 完整部署
162
-
163
- ```typescript
164
- const result = await deploy.deployApp('./apps/my-app', {
165
- projectName: 'my-project',
166
- skipProvision: false,
167
- skipBuild: false,
168
- })
169
- if (result.success) {
170
- // result.data.url — 部署地址
171
- // result.data.deploymentId — 部署 ID
172
- // result.data.envVarsSet — 已设置的环境变量列表
71
+ finally {
72
+ await deploy.close()
173
73
  }
174
74
  ```
175
75
 
176
- ## 核心 API
76
+ `deploy.scan`、`deploy.credentials.getPath/load/save/saveAll` 不依赖 init。Vercel 使用 prebuilt 协议只上传 Build Output API v3 的 .vercel/output,不运行 build 命令;云认证和开通需要外网。
177
77
 
178
- | 方法 | 签名 | 说明 |
179
- | ---------------------- | -------------------------------------------------------------------------- | ------------ |
180
- | `deploy.credentials.load` | `() => HaiResult<string[]>` | 加载凭证到 `process.env` |
181
- | `deploy.credentials.save` | `(key: string, value: string) => HaiResult<void>` | 保存单个凭证 |
182
- | `deploy.credentials.saveAll` | `(entries: Record<string, string>) => HaiResult<void>` | 批量保存凭证 |
183
- | `deploy.credentials.getPath` | `() => string` | 获取凭证文件路径 |
184
- | `deploy.init` | `(config: DeployConfigInput) => Promise<HaiResult<void>>` | 初始化模块 |
185
- | `deploy.close` | `() => Promise<void>` | 关闭模块 |
186
- | `deploy.scan` | `(appDir: string, options?: DeployScanOptions) => Promise<HaiResult<ScanResult>>` | 按环境扫描(不依赖 init) |
187
- | `deploy.provisionAll` | `(projectName: string, services?: ServiceType[]) => Promise<HaiResult<ProvisionResult[]>>` | 开通指定或全部已配置服务 |
188
- | `deploy.deployApp` | `(appDir: string, options?) => Promise<HaiResult<DeployResult>>` | 完整部署 |
189
- | `deploy.config` | `DeployConfig \| null` | 当前配置 |
190
- | `deploy.isInitialized` | `boolean` | 初始化状态 |
78
+ ## 离线交付与验证
191
79
 
192
- ## 凭证管理
193
-
194
- 所有凭证操作都通过 `deploy.credentials.*` 访问,模块入口不再单独导出自由函数。
195
-
196
- > `deploy.credentials.*()` 与 `deploy.scan()` 都不依赖 `deploy.init()`,可用于 CLI 在读取 `_deploy.yml` 之前准备环境。
197
-
198
- ## 配置消费与失败恢复
199
-
200
- 云资源配置消费: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 运行时。
201
-
202
- 部署失败恢复:`ProvisionResult.resourceStatus` 标记 created/reused;`getDeployRecovery(result.error)` 读取 projectName、stage、platformProjectId 和资源清单,unknown 表示请求结果不确定,必须先在服务商控制台核对。失败资源已返回 ID 时保留该 ID。恢复结果不含 envVars、原始异常或连接凭证,CLI 会展示清单和建议。同名重试复用现有资源;不自动删除复用资源,新建资源仅在人工确认未被使用后清理。
203
-
204
- ## 错误码 — `HaiDeployError`
205
-
206
- | 错误码 | code | 说明 |
207
- | --------------------------------------- | ------------------ | ------------------------ |
208
- | `HaiDeployError.DEPLOY_FAILED` | `hai:deploy:001` | 部署失败(通用) |
209
- | `HaiDeployError.PROJECT_CREATE_FAILED` | `hai:deploy:002` | 平台项目创建失败 |
210
- | `HaiDeployError.BUILD_FAILED` | `hai:deploy:003` | 应用构建失败 |
211
- | `HaiDeployError.UPLOAD_FAILED` | `hai:deploy:004` | 构建产物上传失败 |
212
- | `HaiDeployError.AUTH_REQUIRED` | `hai:deploy:005` | 未认证 |
213
- | `HaiDeployError.AUTH_FAILED` | `hai:deploy:006` | 认证失败 |
214
- | `HaiDeployError.PROVISION_FAILED` | `hai:deploy:007` | 基础设施开通失败 |
215
- | `HaiDeployError.ADAPTER_MISSING` | `hai:deploy:008` | SvelteKit adapter 未安装 |
216
- | `HaiDeployError.SCAN_FAILED` | `hai:deploy:009` | 应用扫描失败 |
217
- | `HaiDeployError.NOT_INITIALIZED` | `hai:deploy:010` | 模块未初始化 |
218
- | `HaiDeployError.ENV_VAR_FAILED` | `hai:deploy:011` | 环境变量设置失败 |
219
- | `HaiDeployError.UNSUPPORTED_TYPE` | `hai:deploy:012` | 不支持的类型 |
220
- | `HaiDeployError.CONFIG_ERROR` | `hai:deploy:013` | 配置错误 |
221
- | `HaiDeployError.CREDENTIAL_ERROR` | `hai:deploy:014` | 凭证读写失败 |
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
- ## CLI 命令
230
-
231
- 通过 CLI 部署前,先按 [CLI 安装与命令](../hai-app-create/development.md#cli-安装与命令) 全局安装 `@h-ai/cli`,确认 `hai --version` 可运行后再执行下列命令。在 Node.js 脚本中直接使用 `@h-ai/deploy` API 时无需安装 CLI。
232
-
233
- ```bash
234
- # 部署当前目录
235
- hai deploy
236
-
237
- # 部署指定应用
238
- hai deploy ./apps/admin-console --env test
239
-
240
- # 跳过基础设施开通
241
- hai deploy --env prod --skip-provision
242
-
243
- # 仅 Vercel:跳过应用构建;docker-ssh 仍会构建镜像
244
- hai deploy --env prod --skip-build
245
-
246
- # 自定义项目名
247
- hai deploy --project-name my-custom-name
248
- ```
249
-
250
- > 部署方式由 `config/_deploy.yml` 的 `provider.type` 决定(vercel / docker-ssh),
251
- > CLI 命令相同。docker-ssh 成功后输出的访问地址为 `http://<host>:<hostPort>`。
252
-
253
- fullstack 需明确部署服务包;`hai deploy packages/my-app-serv --env prod` 不会发布 Web。根 Dockerfile 的 `runtime` / `web` target 分别构建 API / Web;Web 单独转移、运行,设置浏览器可访问的 `PUBLIC_API_BASE` 和 API 的 CORS。模板 `pnpm run deploy` 是打包脚本,不等于 CLI 发布。
254
-
255
- 离线部署先按 [离线准备与验收](middleware.md#离线准备与验收) 检查材料。docker-ssh 在本地构建并导出镜像 tar,再分别上传镜像、Compose、运行环境和启动脚本,不在远程构建源码。当前无直接指定预构建镜像的参数;不得声称 `--skip-build` 能离线发布容器,也不得将联网部署测试作为断网证明。
256
-
257
- ## 常见模式
258
-
259
- ### 仅部署(跳过基础设施)
260
-
261
- ```typescript
262
- await deploy.deployApp('./apps/my-app', { skipProvision: true })
263
- ```
264
-
265
- ### 仅开通基础设施
266
-
267
- ```typescript
268
- await deploy.init(config)
269
- const result = await deploy.provisionAll('my-project')
270
- // 手动使用 result.data 中的环境变量
271
- await deploy.close()
272
- ```
273
-
274
- ### 自定义部署流程
275
-
276
- ```typescript
277
- import { deploy } from '@h-ai/deploy'
278
-
279
- // Provider 通过 init 配置自动创建,不需要手动导入
280
- await deploy.init({
281
- provider: { type: 'vercel', token: 'vel_xxx' },
282
- })
283
-
284
- // 使用 deploy API 部署
285
- const result = await deploy.deployApp('./apps/my-app')
286
- ```
80
+ 部署器不需要本地容器引擎;提前从构建机复制完整 `.hai/build/docker/<buildId>`。Linux 准备 Docker/Podman、compose、sh、tar、sha256sum、所需中间件镜像的准确标签及数据目录权限。手工包含私有配置,权限受限,不提交 Git。启动脚本校验文件、加载 app/web、检查镜像存在并导出环境标识;缺镜像时停止,不联网拉取。
287
81
 
288
- ## 相关 Skills
82
+ 完全离线构建另需基础镜像、Linux 依赖缓存、pnpm 和匹配 ABI/libc 的 SQLite 预编译包;Windows node_modules 不能替代。生产 HTTPS、CORS、证书和 DNS 另行配置。实际断网后验收登录、刷新、重启与持久化,不能将联网测试当作离线证明。
289
83
 
290
- - **hai-core** — 配置加载、日志、HaiResult 类型
291
- - **hai-reldb** — 数据库(Neon PostgreSQL)
292
- - **hai-cache** — 缓存(Upstash Redis)
84
+ 改动后执行 typecheck、lint、build、test,模板执行 scaffold gates,部署流程执行 test:deploy-gates;真实 Tauri 安装包按系统单独验证。所有测试临时工程、镜像、容器、辅助进程、截图、日志和报告必须在 finally/teardown 自动清理,失败/取消同样清理。不得把验收产物提交 Git,也不得清理用户既有资源。
@@ -29,7 +29,7 @@
29
29
  2. **使用自定义配置**:逐项输入所选后端的连接字段,展示变量名、字段用途与约束,校验必填项、端口、布尔和 URL;凭据用密码输入。保存配置后跳过该服务的开通与 sidecar。
30
30
  3. **保持不变**:仅在目标 `.env.<mode>` 显式声明当前后端及完整连接配置时出现;私有凭据可来自 `.env.local`/进程。选择后不修改该项、不认证其 Provisioner、不创建或移除已有中间件容器,继续下一项。只有 `.env` 或其他环境有配置不满足此条件。
31
31
 
32
- 逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar 创建,保留已有服务;仍执行应用构建、部署及健康检查。命令行可用 `--skip-middleware`(或 `--skip-provision`)明确选择。不要因为缺少某项中间件的目标文件声明而阻止用户主动跳过。
32
+ 逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar 创建,保留已有服务;仍校验已有构建产物并执行部署及健康检查。命令行使用唯一的 `--skip-middleware`,SDK 使用 `skipMiddleware`。不要因为缺少某项中间件的目标文件声明而阻止用户主动跳过。
33
33
 
34
34
  `--yes` 用于已授权的自动化:目标环境已有完整配置时保持不变,否则使用当前后端默认配置;不支持的云后端或缺少必需凭据直接失败。自动化直接部署用 `hai deploy packages/my-app-serv --env prod --yes --skip-middleware`。跳过不作为配置完整性或连接成功的证明。
35
35
 
@@ -71,24 +71,18 @@ S3 是协议,不是某个容器产品。默认配置时另选 RustFS/MinIO,
71
71
 
72
72
  ## 离线准备与验收
73
73
 
74
- 先核对 Node、pnpm 与目标 Linux 架构。当前要求 Node `^22.22.2 || ^24.15.0 || >=26.0.0`,生成工程固定 pnpm 12.11.2。Windows 本地需已初始化的 Linux 容器引擎、OpenSSH、完整项目及已安装的 CLI/SDK 依赖;Linux 需容器引擎和 Compose、SSH 授权与权限。无互联网仍需 Windows 到 Linux 的 SSH 和应用端口可达。
74
+ 构建与部署分开:先在构建机运行 `pnpm build --target docker`,将完整 `.hai/build/docker/<buildId>` 复制到部署电脑。deploy 只读取这些已校验的镜像 tar,不需要本地 Docker/Podman、基础镜像、pnpm store 或 SQLite 下载。Node/pnpm 版本以工程 engines/packageManager 为准。
75
75
 
76
- 有 PostgreSQL/Redis/RustFS 镜像不代表有可用服务。默认配置要求 Compose 中镜像名称、tag、架构准确匹配,SDK 不传输中间件镜像;自定义连接要求服务已启动、地址从应用容器可达并提供数据库/账号、密码、S3 密钥/桶。不要用容器内 localhost 指代宿主机服务。
76
+ `pnpm run deploy --env prod --bundle-only --output release-prod.tar` 按清单 appVersion 自动生成 release-prod-1.2.3.tar(示例版本 1.2.3),包含 app/web、docker-compose-1.2.3.yml、manifest.json、私有 env、校验和、README 与 start-prod.sh,不连接 SSH。搬到 Linux 后,创建权限 700 的目录,`tar -xf release-prod-1.2.3.tar -C release`,`cd release && sh start-prod.sh`。脚本自动指定带版本的 Compose;手工状态检查使用 `docker compose -f docker-compose-1.2.3.yml --env-file .env.runtime ps`。启动脚本可用 HAI_DEPLOY_PROVIDER_REMOTE_RUNTIME=docker/podman 选择运行时,镜像缺少时停止,不尝试联网。
77
77
 
78
- 默认 Dockerfile 会下载 Node 基础镜像、pnpm、依赖和 SQLite 预编译包,不能直接称为离线可用。源码离线构建要准备 Linux 构建环境的完整缓存和构建 frontend,调整下载步骤并断开外网演练;Windows node_modules/pnpm store 不代表 Linux 的原生依赖可用。云 Provider/Provisioner 需要外网,不用于完全离线部署。
78
+ Linux 需要 compose、sh、tar、sha256sum 和容器/目录/持久化磁盘权限;中间件镜像另行 docker load,名称、tag 和架构须与包内清单一致。自定义连接要求现有服务可从应用容器访问,并备好账号、数据库、S3 桶与密钥。云 Provider/Provisioner 需要网络,不能用于完全离线。
79
79
 
80
- docker-ssh 始终执行本地镜像构建,`--skip-build` 仅对 Vercel 生效。预构建离线交付可在准备环境导出 API/Web 镜像,在 Windows 引擎加载 API 镜像后,为目标服务包提供仅含 `FROM <已加载的API镜像>` 的 Dockerfile 和内容 `*` 的 `.dockerignore`,让 SDK 复用成品并上传。业务代码变更后必须重制成品镜像;无匹配本地镜像时不能继续假定构建无网络。
81
-
82
- fullstack 的 Web 不在服务包 `hai deploy` 中发布,需单独加载/运行 Web 镜像并设置 PUBLIC_API_BASE、API CORS。生产禁止通配 CORS,refresh Cookie 启用 Secure;须准备 HTTPS 入口、内网证书/信任与 DNS/hosts,SDK 不自动配置 TLS/反向代理。离线验收须包含断外网构建/上传/启动、API 健康、真实 Web 登录、页面与会话刷新、重启和持久化;分别报告 API 部署、Web 发布和断网证据,不能互相替代。
80
+ 如连构建也需离线,构建机另准备 Linux 容器引擎、Node/Nginx 基础镜像、pnpm/Corepack、完整 Linux 依赖缓存和匹配 SQLite 驱动/Node ABI/架构/libc 的预编译包。可用构建参数 `--sqlite-prebuild-mirror <可信HTTP(S)地址>` 指定二进制根目录;失败应修复缓存/网络,不能让 deploy 下载编译环境。构建无需选择环境;Podman 代理不可达时用 `pnpm build --target docker --no-http-proxy`,不修改系统代理。
83
81
 
84
82
  ## 启动与验证
85
83
 
86
- fullstack 创建时可通过 `--features deploy` 安装对应版本的本地部署工具。SQLite 在模板及兜底 Dockerfile 中先按驱动版本、Node ABI、架构和 libc 下载官方预编译包,最多尝试三次,加载验证后由生产依赖安装复用缓存;不预装 Python/make/C++,无匹配包或下载失败时停止构建。受限网络可预置 prebuild-install 缓存,或配置 container.sqlitePrebuildMirror(HAI_DEPLOY_CONTAINER_SQLITEPREBUILDMIRROR;自带 Dockerfile 使用 SQLITE_PREBUILD_MIRROR build arg)。镜像根地址后附 /v<驱动版本>/<匹配 ABI 的包名>,不得包含认证信息。不能把更换下载命令当成解决网络不可达,也不能用 CUDA 开关掩盖无关 AI 依赖。
87
-
88
- Podman 构建默认继承运行时代理。代理地址在容器内不可达时,在 `_deploy.yml` 配置 `container.httpProxy: false`(已声明该节点时也可使用 `HAI_DEPLOY_CONTAINER_HTTPPROXY=false`),不要修改用户的系统代理。`loadDeploymentEnvironment`、`loadDeploymentConfig`、`saveDeploymentEnvironment` 可用于脚本显式读取、解析和保存同一份部署配置,均返回 HaiResult。
89
-
90
- 构建上下文必须排除 `.env.local`、`.env.*.local`、`.env.runtime` 和 `.hai/deploy`(含镜像归档与凭据);公共环境文件仍可进入上下文。兜底 Dockerfile 使用伴随忽略文件,Podman 通过 `--ignorefile` 显式指定,避免使用 workspace 根规则后把上次部署的归档或运行配置重新打进构建层。
84
+ fullstack 的 hai.build.json 含 app/web 时,两镜像共用构建 ID,部署生成一个 Compose 和同源代理。API 前缀来自当前服务配置,缺省 CORS 填公开入口,明确 Origin 保留;Web 不挂载 .env.runtime。线上成功要求 API/Web 健康,手工模式返回 packaged 并提示用户自行验收。生产登录刷新还需 HTTPS、正确 CORS、受信证书与 DNS/hosts,模块不配置 TLS。
91
85
 
92
- 容器运行期设置 `NODE_ENV=production`,`HAI_DEPLOY_ENVIRONMENT` 保存文件模式,`HAI_CORE_ENV` 映射为 development/test/production/staging。显式环境部署的资源名追加环境后缀,避免 test/prod 复用同一数据卷。启动脚本位于服务包 `.hai/deploy/<mode>/start-<mode>.sh`,显式导出环境并执行 compose;私有 `.env.runtime` 权限为 600。自带 Dockerfile 可读取同名 build args;不得把私有环境文件或运行凭据复制进镜像。
86
+ 容器运行期 NODE_ENV=production;HAI_DEPLOY_ENVIRONMENT 保存文件模式,HAI_CORE_ENV 映射 development/test/production/staging。资源名附环境后缀,卷按环境隔离;私有 env 权限 600。构建上下文排除 .env.local、环境覆盖、.env.runtime、.hai/build、.hai/deploy 和 data,不把运行期凭据写入镜像。
93
87
 
94
- 修改此流程后执行 deploy/CLI 的 typecheck、lint、build、test,以及 `pnpm --filter @h-ai/cli test:deploy-gates`(当前源码 tarball → fullstack → Podman/SSH → 健康检查、环境标识、登录和重复部署)。模板变动另执行受影响的 scaffold gates。日志、截图、验收报告保存在 Git 忽略的临时目录,不提交仓库;付费云资源、原生客户端与远程生产部署须单独验证并说明边界。
88
+ 修改后运行 deploy/CLI typecheck、lint、build、test、scaffold gates 和 test:deploy-gates。测试应分别证明构建/环境选择、SSH 上传及手工包启动、Web 登录/刷新;断网能力另做实际断网验收。测试的临时工程、容器、镜像、日志、截图、报告与辅助进程必须 finally/teardown 自动清理,失败也清理;不得删除用户已有资源。原生客户端及付费云资源单独验收并报告边界。
@@ -606,6 +606,8 @@ AuthPanel 直接接收 IAM 白名单配置和 actions,内置登录(密码/LD
606
606
 
607
607
  AuthPanel 在 SSR 阶段禁用原生表单控件,客户端挂载并绑定事件后自动开放输入,避免接管时丢失早到的输入。应用无需额外的就绪适配;E2E 等待控件可编辑,并覆盖客户端脚本延迟加载的场景。
608
608
 
609
+ 认证链接指向当前完整 URL 时,AuthPanel 只切换场景并阻止重复导航,返回登录后可立即输入。路径、查询串或 hash 不同时交给应用路由处理;Ctrl/Cmd 等修改键点击保留浏览器行为。不要为切换场景额外调用 goto 或 location.reload。
610
+
609
611
 
610
612
  协议在 Modal 内显示并保留表单输入;使用 URL 时,协议页面须允许 iframe 嵌入,组件禁用其脚本,另提供新窗口查看入口。登录/注册表单使用 `header/extraFields/footer` snippet,AuthPanel 透传 `loginHeader/loginFields/loginFooter` 和 `registerHeader/registerFields/registerFooter`,底部保留内置导航。自定义字段由应用绑定状态和提交,不会隐式写入 IAM 用户模型。
611
613