@h-ai/cli 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.en.md +31 -9
- package/README.ja.md +30 -8
- package/README.md +38 -16
- package/dist/{chunk-X3RSYNBG.js → chunk-FWN5B54L.js} +94 -4
- package/dist/chunk-FWN5B54L.js.map +1 -0
- package/dist/chunk-QYX6V7H2.js +5613 -0
- package/dist/chunk-QYX6V7H2.js.map +1 -0
- package/dist/cli-config-templates-3XSFIXBP.js +5 -0
- package/dist/{cli-config-templates-QRF6B5T4.js.map → cli-config-templates-3XSFIXBP.js.map} +1 -1
- package/dist/{cli-skill-templates-EZFW7D43.js → cli-skill-templates-TSYEQTCY.js} +3 -3
- package/dist/{cli-skill-templates-EZFW7D43.js.map → cli-skill-templates-TSYEQTCY.js.map} +1 -1
- package/dist/index.js +733 -210
- package/dist/index.js.map +1 -1
- package/dist/{src-DUGLX4YB.js → src-RDQ2EIBT.js} +1054 -660
- package/dist/{src-DUGLX4YB.js.map → src-RDQ2EIBT.js.map} +1 -1
- package/package.json +9 -7
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +4 -2
- package/templates/apps/api/.dockerignore.hbs +4 -0
- package/templates/apps/api/.gitignore.hbs +6 -5
- package/templates/apps/api/Dockerfile.hbs +5 -8
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +2 -2
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +2 -2
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +4 -4
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +8 -2
- package/templates/apps/api/hai.build.json.hbs +1 -0
- package/templates/apps/api/package.json.hbs +13 -7
- package/templates/apps/fullstack/.dockerignore.hbs +4 -3
- package/templates/apps/fullstack/.gitignore.hbs +24 -10
- package/templates/apps/fullstack/Dockerfile.hbs +6 -8
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +0 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +5 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +8 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -6
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +6 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +73 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +8 -4
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +5 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +8 -4
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +24 -1
- package/templates/apps/fullstack/hai.build.json.hbs +7 -0
- package/templates/apps/fullstack/package.json.hbs +16 -11
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +8 -14
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +8 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +8 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +4 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +14 -12
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +17 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +15 -5
- package/templates/apps/fullstack/playwright.config.ts.hbs +6 -1
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +0 -1
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +13 -13
- package/templates/apps/mobile-app/index.html.hbs +1 -0
- package/templates/apps/mobile-app/public/runtime-config.js +1 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +46 -117
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +2 -1
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +3 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +58 -0
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -0
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +16 -0
- package/templates/base/.dockerignore.hbs +4 -0
- package/templates/base/.gitignore.hbs +20 -5
- package/templates/base/Dockerfile.hbs +10 -11
- package/templates/base/docker/nginx.conf.hbs +6 -0
- package/templates/base/e2e/app.spec.ts.hbs +14 -0
- package/templates/base/hai.build.json.hbs +1 -0
- package/templates/base/package.json.hbs +25 -17
- package/templates/base/svelte.config.js.hbs +2 -0
- package/templates/base/tests/scaffold.test.ts.hbs +9 -3
- package/templates/base/vite.config.ts.hbs +11 -5
- package/templates/shared/.gitattributes +5 -0
- package/templates/shared/container-runtime-config.sh +30 -0
- package/templates/shared/hash-router.svelte.ts +83 -0
- package/templates/shared/run-e2e.mjs +65 -0
- package/templates/shared/vite-runtime-config.ts +54 -0
- package/templates/skills/AGENTS.md +15 -1
- package/templates/skills/bridges/admin/AGENTS.md +17 -1
- package/templates/skills/bridges/api/AGENTS.md +17 -3
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +35 -11
- package/templates/skills/bridges/h5/AGENTS.md +17 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +19 -3
- package/templates/skills/bridges/website/AGENTS.md +17 -1
- package/templates/skills/hai-ai/reference.md +2 -2
- package/templates/skills/hai-app-create/SKILL.md +5 -3
- package/templates/skills/hai-app-create/development.md +128 -2
- package/templates/skills/hai-app-create/reference.md +4 -4
- package/templates/skills/hai-app-review/SKILL.md +2 -2
- package/templates/skills/hai-app-tests/SKILL.md +2 -1
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-build/SKILL.md +16 -1
- package/templates/skills/hai-core/SKILL.md +2 -1
- package/templates/skills/hai-deploy/SKILL.md +52 -252
- package/templates/skills/hai-deploy/middleware.md +88 -0
- package/templates/skills/hai-framework-sync/SKILL.md +1 -1
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-reach/reference.md +8 -8
- package/templates/skills/hai-spec-implement/SKILL.md +12 -9
- package/templates/skills/hai-spec-implement/changes.md +67 -0
- package/templates/skills/hai-spec-implement/templates.md +25 -3
- package/templates/skills/hai-spec-implement/workflow.md +7 -6
- package/templates/skills/hai-spec-review/SKILL.md +14 -5
- package/templates/skills/hai-spec-review/checklist.md +4 -3
- package/templates/skills/hai-storage/SKILL.md +2 -0
- package/templates/skills/hai-ui/reference.md +2 -0
- package/dist/chunk-WW2MIO6T.js +0 -992
- package/dist/chunk-WW2MIO6T.js.map +0 -1
- package/dist/chunk-X3RSYNBG.js.map +0 -1
- package/dist/cli-config-templates-QRF6B5T4.js +0 -5
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/AuditManagement.svelte.hbs → pages/audit.svelte.hbs} +0 -0
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/RoleManagement.svelte.hbs → pages/roles.svelte.hbs} +0 -0
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/ProfileSettings.svelte.hbs → pages/settings.svelte.hbs} +0 -0
- /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/{components/UserManagement.svelte.hbs → pages/users.svelte.hbs} +0 -0
|
@@ -1,284 +1,84 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-deploy
|
|
3
|
-
description: "
|
|
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
|
-
| 能力 |
|
|
15
|
-
| 适用场景 |
|
|
16
|
-
| 输入 |
|
|
17
|
-
| 输出 |
|
|
18
|
-
| 限制 |
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```yaml
|
|
43
|
-
# config/_deploy.yml
|
|
44
|
-
provider:
|
|
45
|
-
type: vercel
|
|
46
|
-
token: ${HAI_DEPLOY_VERCEL_TOKEN}
|
|
47
|
-
|
|
48
|
-
services:
|
|
49
|
-
db:
|
|
50
|
-
provisioner: neon
|
|
51
|
-
apiKey: ${HAI_DEPLOY_NEON_API_KEY}
|
|
52
|
-
cache:
|
|
53
|
-
provisioner: upstash
|
|
54
|
-
email: ${HAI_DEPLOY_UPSTASH_EMAIL}
|
|
55
|
-
apiKey: ${HAI_DEPLOY_UPSTASH_API_KEY}
|
|
56
|
-
storage:
|
|
57
|
-
provisioner: cloudflare-r2
|
|
58
|
-
accountId: ${HAI_DEPLOY_CF_ACCOUNT_ID}
|
|
59
|
-
apiToken: ${HAI_DEPLOY_CF_API_TOKEN}
|
|
60
|
-
accessKeyId: ${HAI_DEPLOY_R2_ACCESS_KEY_ID}
|
|
61
|
-
secretAccessKey: ${HAI_DEPLOY_R2_SECRET_ACCESS_KEY}
|
|
62
|
-
email:
|
|
63
|
-
provisioner: resend
|
|
64
|
-
apiKey: ${HAI_DEPLOY_RESEND_API_KEY}
|
|
65
|
-
from: ${HAI_DEPLOY_RESEND_FROM}
|
|
66
|
-
sms:
|
|
67
|
-
provisioner: aliyun
|
|
68
|
-
accessKeyId: ${HAI_DEPLOY_ALIYUN_ACCESS_KEY_ID}
|
|
69
|
-
accessKeySecret: ${HAI_DEPLOY_ALIYUN_ACCESS_KEY_SECRET}
|
|
70
|
-
signName: ${HAI_DEPLOY_ALIYUN_SIGN_NAME}
|
|
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 .
|
|
71
40
|
```
|
|
72
41
|
|
|
73
|
-
|
|
74
|
-
`HAI_DEPLOY_PROVIDER_TOKEN` 等约定变量,约定变量仍拥有最高优先级。
|
|
75
|
-
|
|
76
|
-
#### docker-ssh 配置
|
|
77
|
-
|
|
78
|
-
将上面的 `provider` 替换为 docker-ssh(远程主机需 SSH + docker/podman + compose):
|
|
42
|
+
API workspace 的服务目录为 apps/my-app-service。单包使用当前目录。Node/pnpm 版本以项目 engines/packageManager 为准,Windows 用 pnpm.cmd。
|
|
79
43
|
|
|
80
44
|
```yaml
|
|
81
|
-
# config/_deploy.yml
|
|
45
|
+
# 服务包 config/_deploy.yml
|
|
82
46
|
provider:
|
|
83
47
|
type: docker-ssh
|
|
84
|
-
ssh:
|
|
85
|
-
host: ${HAI_DEPLOY_SSH_HOST}
|
|
86
|
-
port: 22
|
|
87
|
-
username: deploy
|
|
88
|
-
identityFile: ${HAI_DEPLOY_SSH_KEY} # 私钥文件路径,省略则用 ssh-agent
|
|
89
|
-
remote:
|
|
90
|
-
baseDir: /opt/hai/apps
|
|
91
|
-
runtime: auto # auto 优先 podman,其次 docker
|
|
92
48
|
expose:
|
|
93
|
-
|
|
94
|
-
containerPort: 3000
|
|
95
|
-
hostPort: 18080 # 访问地址 http://<host>:18080
|
|
96
|
-
|
|
49
|
+
hostPort: 18080
|
|
97
50
|
container:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
services:
|
|
101
|
-
# 启用的服务会被加入远程 compose sidecar(postgres/redis/minio)
|
|
102
|
-
# 若配置了云 Provisioner(如 neon),则优先用云资源,不再自建 sidecar
|
|
103
|
-
db:
|
|
104
|
-
provisioner: neon
|
|
105
|
-
apiKey: ${HAI_DEPLOY_NEON_API_KEY}
|
|
51
|
+
s3Engine: rustfs # rustfs | minio
|
|
106
52
|
```
|
|
107
53
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- 应用镜像使用工程自带的 `Dockerfile`(`@sveltejs/adapter-node`),缺少 adapter-node 会返回 `ADAPTER_MISSING`
|
|
111
|
-
- compose 按 `deploy.scan()` 识别的服务**及其后端类型**生成匹配 sidecar 并注入 `HAI_*` 覆盖变量:
|
|
112
|
-
- `db`:`postgresql`→postgres、`mysql`→mysql、`sqlite`→数据卷(无 sidecar)→ `HAI_DB`
|
|
113
|
-
- `cache`:`redis`→redis → `HAI_CACHE`;`storage`:`s3`→minio → `HAI_STORAGE`
|
|
114
|
-
- `vecdb`:`qdrant`/`pgvector`/`chroma`→对应 sidecar、`lancedb`→数据卷 → `HAI_VECDB`
|
|
115
|
-
- 若同一服务已配置云 Provisioner(如 neon 提供 `HAI_DB`),优先用云资源,不再自建 sidecar
|
|
116
|
-
- sidecar 密码等敏感值只写入远程 `.env.runtime`(权限 600),不入 `compose.yml`
|
|
117
|
-
- `identityFile` 是私钥**路径**(非私钥内容);禁止把私钥内容写进 `_deploy.yml`
|
|
54
|
+
SSH/平台参数可在向导输入后存入 `.env.local` 的 `HAI_DEPLOY_PROVIDER` 整体 JSON;私钥只存路径,不记录内容。仅打包可省 SSH;在线部署需要 ssh/scp、主机信任和运行权限。完整字段、可选云 Provisioner 及中间件配置见 [middleware.md](middleware.md)。
|
|
118
55
|
|
|
119
|
-
|
|
56
|
+
## SDK
|
|
120
57
|
|
|
121
|
-
```
|
|
58
|
+
```ts
|
|
122
59
|
import { deploy } from '@h-ai/deploy'
|
|
123
60
|
|
|
124
|
-
deploy.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// 使用后关闭
|
|
134
|
-
await deploy.close()
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### 3. 扫描应用
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
const scanResult = await deploy.scan('./apps/my-app')
|
|
141
|
-
if (scanResult.success) {
|
|
142
|
-
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。
|
|
143
70
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
### 4. 开通基础设施
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
const provResults = await deploy.provisionAll('my-project')
|
|
150
|
-
if (provResults.success) {
|
|
151
|
-
for (const prov of provResults.data) {
|
|
152
|
-
// prov.envVars 包含需要注入的环境变量
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### 5. 完整部署
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
const result = await deploy.deployApp('./apps/my-app', {
|
|
161
|
-
projectName: 'my-project',
|
|
162
|
-
skipProvision: false,
|
|
163
|
-
skipBuild: false,
|
|
164
|
-
})
|
|
165
|
-
if (result.success) {
|
|
166
|
-
// result.data.url — 部署地址
|
|
167
|
-
// result.data.deploymentId — 部署 ID
|
|
168
|
-
// result.data.envVarsSet — 已设置的环境变量列表
|
|
71
|
+
finally {
|
|
72
|
+
await deploy.close()
|
|
169
73
|
}
|
|
170
74
|
```
|
|
171
75
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
| 方法 | 签名 | 说明 |
|
|
175
|
-
| ---------------------- | -------------------------------------------------------------------------- | ------------ |
|
|
176
|
-
| `deploy.credentials.load` | `() => HaiResult<string[]>` | 加载凭证到 `process.env` |
|
|
177
|
-
| `deploy.credentials.save` | `(key: string, value: string) => HaiResult<void>` | 保存单个凭证 |
|
|
178
|
-
| `deploy.credentials.saveAll` | `(entries: Record<string, string>) => HaiResult<void>` | 批量保存凭证 |
|
|
179
|
-
| `deploy.credentials.getPath` | `() => string` | 获取凭证文件路径 |
|
|
180
|
-
| `deploy.init` | `(config: DeployConfigInput) => Promise<HaiResult<void>>` | 初始化模块 |
|
|
181
|
-
| `deploy.close` | `() => Promise<void>` | 关闭模块 |
|
|
182
|
-
| `deploy.scan` | `(appDir: string) => Promise<HaiResult<ScanResult>>` | 扫描应用(不依赖 init) |
|
|
183
|
-
| `deploy.provisionAll` | `(projectName: string) => Promise<HaiResult<ProvisionResult[]>>` | 开通所有服务 |
|
|
184
|
-
| `deploy.deployApp` | `(appDir: string, options?) => Promise<HaiResult<DeployResult>>` | 完整部署 |
|
|
185
|
-
| `deploy.config` | `DeployConfig \| null` | 当前配置 |
|
|
186
|
-
| `deploy.isInitialized` | `boolean` | 初始化状态 |
|
|
187
|
-
|
|
188
|
-
## 凭证管理
|
|
189
|
-
|
|
190
|
-
所有凭证操作都通过 `deploy.credentials.*` 访问,模块入口不再单独导出自由函数。
|
|
191
|
-
|
|
192
|
-
> `deploy.credentials.*()` 与 `deploy.scan()` 都不依赖 `deploy.init()`,可用于 CLI 在读取 `_deploy.yml` 之前准备环境。
|
|
193
|
-
|
|
194
|
-
## 错误码 — `HaiDeployError`
|
|
195
|
-
|
|
196
|
-
| 错误码 | code | 说明 |
|
|
197
|
-
| --------------------------------------- | ------------------ | ------------------------ |
|
|
198
|
-
| `HaiDeployError.DEPLOY_FAILED` | `hai:deploy:001` | 部署失败(通用) |
|
|
199
|
-
| `HaiDeployError.PROJECT_CREATE_FAILED` | `hai:deploy:002` | 平台项目创建失败 |
|
|
200
|
-
| `HaiDeployError.BUILD_FAILED` | `hai:deploy:003` | 应用构建失败 |
|
|
201
|
-
| `HaiDeployError.UPLOAD_FAILED` | `hai:deploy:004` | 构建产物上传失败 |
|
|
202
|
-
| `HaiDeployError.AUTH_REQUIRED` | `hai:deploy:005` | 未认证 |
|
|
203
|
-
| `HaiDeployError.AUTH_FAILED` | `hai:deploy:006` | 认证失败 |
|
|
204
|
-
| `HaiDeployError.PROVISION_FAILED` | `hai:deploy:007` | 基础设施开通失败 |
|
|
205
|
-
| `HaiDeployError.ADAPTER_MISSING` | `hai:deploy:008` | SvelteKit adapter 未安装 |
|
|
206
|
-
| `HaiDeployError.SCAN_FAILED` | `hai:deploy:009` | 应用扫描失败 |
|
|
207
|
-
| `HaiDeployError.NOT_INITIALIZED` | `hai:deploy:010` | 模块未初始化 |
|
|
208
|
-
| `HaiDeployError.ENV_VAR_FAILED` | `hai:deploy:011` | 环境变量设置失败 |
|
|
209
|
-
| `HaiDeployError.UNSUPPORTED_TYPE` | `hai:deploy:012` | 不支持的类型 |
|
|
210
|
-
| `HaiDeployError.CONFIG_ERROR` | `hai:deploy:013` | 配置错误 |
|
|
211
|
-
| `HaiDeployError.CREDENTIAL_ERROR` | `hai:deploy:014` | 凭证读写失败 |
|
|
212
|
-
| `HaiDeployError.CONTAINER_RUNTIME_NOT_FOUND` | `hai:deploy:015` | 未找到容器运行时 |
|
|
213
|
-
| `HaiDeployError.CONTAINER_BUILD_FAILED` | `hai:deploy:016` | 镜像构建失败 |
|
|
214
|
-
| `HaiDeployError.REMOTE_CONNECT_FAILED` | `hai:deploy:017` | 远程连接失败 |
|
|
215
|
-
| `HaiDeployError.REMOTE_COMMAND_FAILED` | `hai:deploy:018` | 远程命令失败 |
|
|
216
|
-
| `HaiDeployError.IMAGE_TRANSFER_FAILED` | `hai:deploy:019` | 镜像传输失败 |
|
|
217
|
-
| `HaiDeployError.HEALTH_CHECK_FAILED` | `hai:deploy:020` | 健康检查未通过 |
|
|
218
|
-
|
|
219
|
-
## CLI 命令
|
|
220
|
-
|
|
221
|
-
通过 CLI 部署前,先按 [CLI 安装与命令](../hai-app-create/development.md#cli-安装与命令) 全局安装 `@h-ai/cli`,确认 `hai --version` 可运行后再执行下列命令。在 Node.js 脚本中直接使用 `@h-ai/deploy` API 时无需安装 CLI。
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
# 部署当前目录
|
|
225
|
-
hai deploy
|
|
226
|
-
|
|
227
|
-
# 部署指定应用
|
|
228
|
-
hai deploy ./apps/admin-console
|
|
229
|
-
|
|
230
|
-
# 跳过基础设施开通
|
|
231
|
-
hai deploy --skip-provision
|
|
232
|
-
|
|
233
|
-
# 跳过构建
|
|
234
|
-
hai deploy --skip-build
|
|
235
|
-
|
|
236
|
-
# 自定义项目名
|
|
237
|
-
hai deploy --project-name my-custom-name
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
> 部署方式由 `config/_deploy.yml` 的 `provider.type` 决定(vercel / docker-ssh),
|
|
241
|
-
> CLI 命令相同。docker-ssh 成功后输出的访问地址为 `http://<host>:<hostPort>`。
|
|
242
|
-
|
|
243
|
-
## 常见模式
|
|
244
|
-
|
|
245
|
-
### 仅部署(跳过基础设施)
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
await deploy.deployApp('./apps/my-app', { skipProvision: true })
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### 仅开通基础设施
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
await deploy.init(config)
|
|
255
|
-
const result = await deploy.provisionAll('my-project')
|
|
256
|
-
// 手动使用 result.data 中的环境变量
|
|
257
|
-
await deploy.close()
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### 自定义部署流程
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
import { deploy } from '@h-ai/deploy'
|
|
264
|
-
|
|
265
|
-
// Provider 通过 init 配置自动创建,不需要手动导入
|
|
266
|
-
await deploy.init({
|
|
267
|
-
provider: { type: 'vercel', token: 'vel_xxx' },
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
// 使用 deploy API 部署
|
|
271
|
-
const result = await deploy.deployApp('./apps/my-app')
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## 相关 Skills
|
|
76
|
+
`deploy.scan`、`deploy.credentials.getPath/load/save/saveAll` 不依赖 init。Vercel 使用 prebuilt 协议只上传 Build Output API v3 的 .vercel/output,不运行 build 命令;云认证和开通需要外网。
|
|
275
77
|
|
|
276
|
-
|
|
277
|
-
- **hai-reldb** — 数据库(Neon PostgreSQL)
|
|
278
|
-
- **hai-cache** — 缓存(Upstash Redis)
|
|
78
|
+
## 离线交付与验证
|
|
279
79
|
|
|
280
|
-
|
|
80
|
+
部署器不需要本地容器引擎;提前从构建机复制完整 `.hai/build/docker/<buildId>`。Linux 准备 Docker/Podman、compose、sh、tar、sha256sum、所需中间件镜像的准确标签及数据目录权限。手工包含私有配置,权限受限,不提交 Git。启动脚本校验文件、加载 app/web、检查镜像存在并导出环境标识;缺镜像时停止,不联网拉取。
|
|
281
81
|
|
|
282
|
-
|
|
82
|
+
完全离线构建另需基础镜像、Linux 依赖缓存、pnpm 和匹配 ABI/libc 的 SQLite 预编译包;Windows node_modules 不能替代。生产 HTTPS、CORS、证书和 DNS 另行配置。实际断网后验收登录、刷新、重启与持久化,不能将联网测试当作离线证明。
|
|
283
83
|
|
|
284
|
-
|
|
84
|
+
改动后执行 typecheck、lint、build、test,模板执行 scaffold gates,部署流程执行 test:deploy-gates;真实 Tauri 安装包按系统单独验证。所有测试临时工程、镜像、容器、辅助进程、截图、日志和报告必须在 finally/teardown 自动清理,失败/取消同样清理。不得把验收产物提交 Git,也不得清理用户既有资源。
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 部署环境与中间件
|
|
2
|
+
|
|
3
|
+
## 环境先行
|
|
4
|
+
|
|
5
|
+
`hai deploy` 首先输入部署环境,或用 `hai deploy --env test` 指定。`.env.test` / `.env.prod` 必须由用户先创建;缺失时停止,不代替用户创建一个空文件继续部署。指定要部署的目录,例如 `hai deploy packages/my-app-serv --env test`,该目录必须对应 lockfile 的一个 importer;不遍历 apps/packages 或猜测 fullstack 布局。环境文件默认取当前目录(有 `.env` 时)或 lockfile 根目录,必要时传 `--env-dir`。
|
|
6
|
+
|
|
7
|
+
环境文件遵循项目的六文件规范:`.env.example` 为带说明/枚举/范围的完整清单,`.env` 为公共基础,`.env.dev/test/prod` 为公共环境覆盖,以上五类提交 Git;`.env.local` 为忽略 Git 的本机私有覆盖。加载顺序为进程 > `.env.local` > 目标环境 > `.env`。真实密码、API Key、含认证信息的连接 URL 写 `.env.local`,公共后端和地址写目标环境文件;日志展示脱敏值。调用 deploy API 时显式传 `{ environment: 'test', envDir: '/workspace/app' }`,不要用调用者的 `NODE_ENV` 猜测部署目标。
|
|
8
|
+
|
|
9
|
+
## 识别与逐项选择
|
|
10
|
+
|
|
11
|
+
先用 `deploy.scan(appDir, { environment, envDir })` 核对实际依赖、配置与环境值。扫描从目标 workspace 的 `pnpm-lock.yaml` importer 出发,沿 dependencies/optionalDependencies 遍历,排除 devDependencies、peerDependencies 和其他应用未被引用的依赖;workspace link 只沿运行依赖继续。pnpm 12 的工具链 YAML 文档不能混入应用图。lockfile、目标 importer 或依赖节点缺失时先执行 `pnpm install` 修复;依赖图不读取 package.json、pnpm-workspace.yaml 或项目目录布局。配置文件决定已识别模块的后端,保留的旧配置不能自行启用未依赖的模块。
|
|
12
|
+
|
|
13
|
+
`ScanResult.haiModules` 列出所有实际运行依赖中的 Hai 模块;`ScanResult.middlewares` 提供服务、后端、来源、配置路径和变量前缀;连接配置可能含凭据,不能整体记入日志。`DEPLOY_MIDDLEWARE` 是后端和连接字段的公共目录,字段名称、类型与约束须与模块 Schema 同步。
|
|
14
|
+
|
|
15
|
+
| 来源 | 后端选项 | 默认 Compose 行为 | 云自动开通 |
|
|
16
|
+
| --- | --- | --- | --- |
|
|
17
|
+
| reldb;IAM/audit;启用 DB 日志的 scheduler | SQLite / MySQL / PostgreSQL | SQLite 数据卷;mysql / postgres 服务 | PostgreSQL → Neon |
|
|
18
|
+
| cache;IAM;scheduler 可选分布式锁 | memory / Redis | memory 无服务;redis 服务 | Redis → Upstash |
|
|
19
|
+
| storage | local / S3 | 本地数据卷;RustFS(默认)/MinIO + bucket 初始化 | S3 → Cloudflare R2 |
|
|
20
|
+
| vecdb | LanceDB / pgvector / Qdrant / Chroma | 本地卷或对应容器 | 自定义已有服务 |
|
|
21
|
+
| IAM 可选目录认证 | disabled / LDAP | 禁用无服务;OpenLDAP | 自定义已有目录 |
|
|
22
|
+
| Reach Provider 数组 | console / SMTP / HTTP API;阿里云 SMS | console 无服务;SMTP 用 Mailpit 捕获测试邮件;HTTP/SMS 自定义 | SMTP → Resend;SMS → 阿里云凭据接入 |
|
|
23
|
+
|
|
24
|
+
`serv` 声明 AI peer 不代表应用启用了 AI,不应因此安装 AI/ONNX/CUDA 或开通向量服务。LDAP 需要在应用安装 IAM 的可选 peer `ldapts`,并显式设置 `login.ldap=true`;安装 IAM 不代表启用了 LDAP。Reach 保留 Provider 的数组索引及名称,不能把 HTTP 回调当作 SMTP。默认目录没有业务账号;Mailpit 不会对外投递生产邮件。生产应配置企业 LDAP/SMTP,或使用对应云服务。
|
|
25
|
+
|
|
26
|
+
每项都展示当前生效后端和用途,例如 `HAI_DB_TYPE=postgresql` 显示 PostgreSQL,再选择:
|
|
27
|
+
|
|
28
|
+
1. **使用默认配置**:选择后端;Compose 生成匹配容器/卷,云平台调用已支持的 Provisioner,并保存实际连接配置。MySQL、LDAP、向量库等没有云自动开通器时,明确提示改用自定义连接;不能声称已经创建云资源。Resend 需要已验证的发件域名,R2 需要已有 S3 访问密钥,阿里云短信需要签名/模板与额度。
|
|
29
|
+
2. **使用自定义配置**:逐项输入所选后端的连接字段,展示变量名、字段用途与约束,校验必填项、端口、布尔和 URL;凭据用密码输入。保存配置后跳过该服务的开通与 sidecar。
|
|
30
|
+
3. **保持不变**:仅在目标 `.env.<mode>` 显式声明当前后端及完整连接配置时出现;私有凭据可来自 `.env.local`/进程。选择后不修改该项、不认证其 Provisioner、不创建或移除已有中间件容器,继续下一项。只有 `.env` 或其他环境有配置不满足此条件。
|
|
31
|
+
|
|
32
|
+
逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar 创建,保留已有服务;仍校验已有构建产物并执行部署及健康检查。命令行使用唯一的 `--skip-middleware`,SDK 使用 `skipMiddleware`。不要因为缺少某项中间件的目标文件声明而阻止用户主动跳过。
|
|
33
|
+
|
|
34
|
+
`--yes` 用于已授权的自动化:目标环境已有完整配置时保持不变,否则使用当前后端默认配置;不支持的云后端或缺少必需凭据直接失败。自动化直接部署用 `hai deploy packages/my-app-serv --env prod --yes --skip-middleware`。跳过不作为配置完整性或连接成功的证明。
|
|
35
|
+
|
|
36
|
+
部署目标不完整时,先引导选择 SSH/Vercel。SSH 逐项输入主机、端口、用户名、私钥文件路径、known_hosts 路径、远程目录/运行时及容器/主机端口;端口范围 1–65535。密钥路径留空使用 ssh-agent/默认密钥,known_hosts 留空使用 OpenSSH 默认文件。完整目标自动复用;修改 `.env.local` 的 `HAI_DEPLOY_PROVIDER` 可切换连接。首次填写的整个目标对象(包括地址、端口、路径、Token)只保存 `.env.local` 并添加 Git 忽略,不把机器信息写公共环境文件。SDK 使用 `saveDeploymentEnvironment(snapshot, variables, { private: true })` 可显式保存本机参数。禁止读取、复制或打印私钥内容。
|
|
37
|
+
|
|
38
|
+
### Hai 模块与中间件的关系
|
|
39
|
+
|
|
40
|
+
识别依据是运行图中的所有节点,不按应用直接依赖硬编码消费者名单。下面按当前公开依赖和功能说明;新增模块/后端时同步此表、连接目录与真实运行测试。
|
|
41
|
+
|
|
42
|
+
| 模块 | 中间件来源或外部能力 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| reldb / cache / storage / vecdb | 分别拥有关系库、缓存、对象存储、向量库连接契约 |
|
|
45
|
+
| iam / kit | IAM 运行依赖 audit/reldb/cache/crypto;kit 经 IAM 继承。LDAP 按 login.ldap 开关启用,Reach peer 只有另行实际依赖时进入运行图 |
|
|
46
|
+
| audit / payment | audit 经 reldb 持久化;payment 经 audit 继承数据库。微信、支付宝、Stripe 是已有商户 API,沿 payment 配置接入,不生成假云服务或容器 |
|
|
47
|
+
| scheduler / reach | 两者实际依赖 reldb/cache;Reach 另按启用的 SMTP/HTTP/短信渠道配置 |
|
|
48
|
+
| ai | 实际依赖 reldb/vecdb/datapipe。LLM、图像、音频端点及模型来自 AI 配置,不能仅凭包名选择模型镜像或 CUDA;不自动创建付费厂商账号 |
|
|
49
|
+
| crypto / serv | crypto 的可选运行依赖按 lockfile 解析,serv 经 crypto 继承;serv 的 IAM/storage/AI peer 不作为启用证据 |
|
|
50
|
+
| core / datapipe / api-contract / api-client / ui / capacitor / cli / deploy | 本身没有额外专用中间件;仍沿实际运行依赖继续遍历,例如 api-client/capacitor 的 crypto 分支。UI/设备能力、部署目标/Provisioner 凭据按各自配置处理 |
|
|
51
|
+
|
|
52
|
+
S3 是协议,不是某个容器产品。默认配置时另选 RustFS/MinIO,保存到 HAI_DEPLOY_CONTAINER_S3ENGINE(rustfs | minio,默认 rustfs),应用始终写 HAI_STORAGE_TYPE=s3 或 HAI_STORAGE。自定义 AWS S3、OSS、R2 等端点不生成 sidecar。镜像使用与 storage 集成测试一致的 RustFS 官方版本和 MinIO 社区版本;存储桶初始化复用应用镜像里的 S3 SDK,无需额外 mc 镜像,必须幂等且不能吞掉失败。
|
|
53
|
+
|
|
54
|
+
## 连接字段
|
|
55
|
+
|
|
56
|
+
以下是自定义向导的连接字段;完整可用项、枚举与范围仍以项目 `.env.example` 和公开 Schema 为准。变量前缀为 `HAI_DB`、`HAI_CACHE`、`HAI_STORAGE`、`HAI_VECDB`、`HAI_IAM_LDAP`;Reach 使用实际索引 `HAI_REACH_PROVIDERS_<index>`。camelCase 不拆词,例如 `bindPassword` → `BINDPASSWORD`。
|
|
57
|
+
|
|
58
|
+
| 后端 | 连接字段 |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| SQLite | database(文件路径或 `:memory:`) |
|
|
61
|
+
| MySQL / PostgreSQL | url(优先)、host、port(1–65535)、database、user、password、ssl |
|
|
62
|
+
| Redis | url(优先)、host、port、password、db、tls、keyPrefix、connectTimeout、commandTimeout |
|
|
63
|
+
| local / LanceDB | root / path |
|
|
64
|
+
| S3 | endpoint、bucket、region、accessKeyId、secretAccessKey、forcePathStyle |
|
|
65
|
+
| Qdrant / Chroma | url;Qdrant 可选 apiKey |
|
|
66
|
+
| pgvector | url(优先)或 host/port/user/password,以及 database |
|
|
67
|
+
| LDAP | url、bindDn、bindPassword、searchBase、searchFilter、usernameAttribute、emailAttribute、displayNameAttribute、useTls、connectTimeout |
|
|
68
|
+
| SMTP | host、port、secure、user、pass、from |
|
|
69
|
+
| HTTP API | url、method(POST / PUT)、headers(JSON)、timeout |
|
|
70
|
+
| 阿里云 SMS | accessKeyId、accessKeySecret、signName、endpoint |
|
|
71
|
+
|
|
72
|
+
## 离线准备与验收
|
|
73
|
+
|
|
74
|
+
构建与部署分开:先在构建机运行 `pnpm build --target docker`,将完整 `.hai/build/docker/<buildId>` 复制到部署电脑。deploy 只读取这些已校验的镜像 tar,不需要本地 Docker/Podman、基础镜像、pnpm store 或 SQLite 下载。Node/pnpm 版本以工程 engines/packageManager 为准。
|
|
75
|
+
|
|
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
|
+
|
|
78
|
+
Linux 需要 compose、sh、tar、sha256sum 和容器/目录/持久化磁盘权限;中间件镜像另行 docker load,名称、tag 和架构须与包内清单一致。自定义连接要求现有服务可从应用容器访问,并备好账号、数据库、S3 桶与密钥。云 Provider/Provisioner 需要网络,不能用于完全离线。
|
|
79
|
+
|
|
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`,不修改系统代理。
|
|
81
|
+
|
|
82
|
+
## 启动与验证
|
|
83
|
+
|
|
84
|
+
fullstack 的 hai.build.json 含 app/web 时,两镜像共用构建 ID,部署生成一个 Compose 和同源代理。API 前缀来自当前服务配置,缺省 CORS 填公开入口,明确 Origin 保留;Web 不挂载 .env.runtime。线上成功要求 API/Web 健康,手工模式返回 packaged 并提示用户自行验收。生产登录刷新还需 HTTPS、正确 CORS、受信证书与 DNS/hosts,模块不配置 TLS。
|
|
85
|
+
|
|
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,不把运行期凭据写入镜像。
|
|
87
|
+
|
|
88
|
+
修改后运行 deploy/CLI typecheck、lint、build、test、scaffold gates 和 test:deploy-gates。测试应分别证明构建/环境选择、SSH 上传及手工包启动、Web 登录/刷新;断网能力另做实际断网验收。测试的临时工程、容器、镜像、日志、截图、报告与辅助进程必须 finally/teardown 自动清理,失败也清理;不得删除用户已有资源。原生客户端及付费云资源单独验收并报告边界。
|
|
@@ -90,7 +90,7 @@ npm view @h-ai/cli dist-tags --json
|
|
|
90
90
|
1. 按差异清单迁移调用方、类型、配置、环境变量说明、初始化和构建入口,保留应用行为与架构。新默认行为改变须显式核对;只对生命周期模块遵循 init → use → close,不给 core/serv/纯函数套用生命周期。
|
|
91
91
|
2. 用目标版本回归核验已有补丁/本地覆盖:仍必要则针对目标重建并说明依据;只有上游已修复且回归通过时移除。npm 升级遇到本地联调映射时,明确恢复 npm 或继续本地验证的选择,不能只改版本号却继续运行旧源码。
|
|
92
92
|
3. 使用当前包管理器安装并更新 lockfile,保留失败诊断;不手改 lockfile 的解析字段。安装后从**各消费 workspace**核对真实版本、公开 exports/类型及框架传递依赖,检查残留旧版/重复解析和本地路径。
|
|
93
|
-
4. 按 [同步规则](sync.md) 合并目标 CLI 中适配本架构的完整 skill 目录及 AI
|
|
93
|
+
4. 按 [同步规则](sync.md) 合并目标 CLI 中适配本架构的完整 skill 目录及 AI 入口。对“旧发布模板、应用现有文件、新发布模板”比较,保留仍适用的私有规则/自定义技能;已明确的业务需求优先于模板默认值,仅未决冲突转为选择。没有旧模板时先辨识私有内容,不直接覆盖。同步版本必须与目标运行时一致。
|
|
94
94
|
5. 同步受影响的 README、配置样例、skill 与测试,逐项更新差异清单状态。即使已处于目标版本,也核验解析/配置/skills 漂移;一致时报告无需升级,不制造改动。
|
|
95
95
|
|
|
96
96
|
### 6. 验证与交付
|
|
@@ -22,7 +22,7 @@ hai-framework 是通用能力和 skill 模板的源头;应用仓库消费这
|
|
|
22
22
|
|
|
23
23
|
`hai init` 只补缺失内容,即使 `--force` 也不覆盖已有 AI 规则和 skills,不能用它代替旧版本规则合并。未发布模板修正从明确本地来源同步,不能假定已发布 CLI 已包含。
|
|
24
24
|
|
|
25
|
-
1. 比较来源与应用的同名完整目录,包括 SKILL、reference/guide
|
|
25
|
+
1. 比较来源与应用的同名完整目录,包括 SKILL、reference/guide、脚本和资产;升级时结合旧发布模板识别应用私有覆盖。模板业务默认值服从用户当前明确要求及其指定文档;移除或改写已被需求替代的旧模板强制规则,保留仍适用的项目私有决策,不将默认 admin/user、限额或流程重新强加给应用。无冲突部分合并,真实未决冲突列出位置、双方意图和可选方案;已有明确决策不重复询问。
|
|
26
26
|
2. 按目标架构选择需要的模块技能与桥接模板;不将 SvelteKit 指令套到 API/Vite 项目。保留自定义技能;上游重命名/删除的旧目录先检查调用与私有内容,不能整树覆盖或删除。
|
|
27
27
|
3. 项目规则合并到 AGENTS,`.github/copilot-instructions.md` 只引用共享入口;CLAUDE 如存在,通过 `@AGENTS.md` 复用共享规则,保留用户额外规则及现有 `.claude/`,不另复制技能正文。OpenCode 的 `instructions` 直接引用 `AGENTS.md`;配置有变化时合并已有字段,不整体替换。
|
|
28
28
|
4. 确保新增/修改/修复任务能到达 `hai-app-create` 与其 `development.md`,审查到达 `hai-app-review`,框架升级到达 `hai-framework-sync`。只引用目标实际交付的可选技能,不把目录缺失当成运行时能力缺失。
|
|
@@ -34,17 +34,17 @@
|
|
|
34
34
|
providers:
|
|
35
35
|
- name: email
|
|
36
36
|
type: smtp
|
|
37
|
-
host: ${
|
|
38
|
-
port: ${
|
|
37
|
+
host: ${HAI_REACH_PROVIDERS_0_HOST:smtp.example.com}
|
|
38
|
+
port: ${HAI_REACH_PROVIDERS_0_PORT:465}
|
|
39
39
|
secure: true
|
|
40
|
-
user: ${
|
|
41
|
-
pass: ${
|
|
42
|
-
from: ${
|
|
40
|
+
user: ${HAI_REACH_PROVIDERS_0_USER:}
|
|
41
|
+
pass: ${HAI_REACH_PROVIDERS_0_PASS:}
|
|
42
|
+
from: ${HAI_REACH_PROVIDERS_0_FROM:noreply@example.com}
|
|
43
43
|
- name: sms
|
|
44
44
|
type: aliyun-sms
|
|
45
|
-
accessKeyId: ${
|
|
46
|
-
accessKeySecret: ${
|
|
47
|
-
signName: ${
|
|
45
|
+
accessKeyId: ${HAI_REACH_PROVIDERS_0_ACCESSKEYID:}
|
|
46
|
+
accessKeySecret: ${HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET:}
|
|
47
|
+
signName: ${HAI_REACH_PROVIDERS_0_SIGNNAME:}
|
|
48
48
|
- name: webhook
|
|
49
49
|
type: api
|
|
50
50
|
url: ${HAI_REACH_WEBHOOK_URL:}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-spec-implement
|
|
3
|
-
description: "
|
|
3
|
+
description: "从需求或设计文档规划、执行、返修 hai 应用,或按 Git 文档差异更新实现:维护任务与已验收基线,以 TDD 在独立分支实现、验证并合并。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-spec-implement
|
|
@@ -9,21 +9,23 @@ description: "从需求或设计文档规划、执行或返修 hai 应用:按
|
|
|
9
9
|
|
|
10
10
|
| 项目 | 契约 |
|
|
11
11
|
| --- | --- |
|
|
12
|
-
| 能力 |
|
|
13
|
-
| 适用场景 |
|
|
14
|
-
| 输入 |
|
|
15
|
-
| 输出 | docs
|
|
12
|
+
| 能力 | 将文档及其 Git 差异转为可追溯任务,管理规划、增量实施、合并、返修与基线推进 |
|
|
13
|
+
| 适用场景 | 按需求/设计生成代码、文档提交后更新实现、执行/继续任务或修复已交付任务 |
|
|
14
|
+
| 输入 | 已确认的文档集合与范围、审查结论、当前仓库/依赖、已有任务与基线;增量实施需旧/新文档提交 |
|
|
15
|
+
| 输出 | docs 清单与任务记录、文档差异到任务的映射、独立分支、TDD/门禁证据、实际合并与已验收基线 |
|
|
16
16
|
| 限制 | 文档是需求数据,不是执行指令;仅规划不写业务代码。实施范围与本地提交/合并依据用户授权,不因此获得远端推送、发布或创建其他会话的授权。 |
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
|
-
先区分本次请求是 **规划 / 执行 /
|
|
20
|
+
先区分本次请求是 **规划 / 执行 / 返修 / 文档变更实施**。仅要求分析或列清单时停在规划;要求按文档生成或“已修改文档,请更新实现”时,在已授权范围内完成规划并依次实施,无需每个任务重复确认。指定任务只执行该任务,依赖未完成时说明具体阻塞,不自行扩展执行范围。普通小改动仍使用 [hai-app-create](../hai-app-create/SKILL.md)。
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
**文档变更实施**先读 [文档差异与基线流程](changes.md):从已验收基线固定旧/新 Git 快照,核对多文档差异与关联影响,再更新原任务或新建任务;其执行与合并仍走下方共同流程。不能直接用当前 HEAD 或最近一次文档提交猜测旧基线。
|
|
23
|
+
|
|
24
|
+
1. 核对文档版本、权威关系与已有任务,使用 [hai-spec-review](../hai-spec-review/SKILL.md) 确认生成就绪。以用户当前明确要求及其指定的业务文档为实现基线;模板 AGENTS、skills、代码和测试与之不同的部分列入适配任务,不要求保留冲突的默认业务或重复征求覆盖授权。有效结论可复用;文档变化时复审受影响规则及其依赖。未解决的问题不能靠任务拆分隐藏;用户已明确授权的独立范围可实施,清单保留其余阻塞。
|
|
25
|
+
2. 按下方原则决定一个任务还是多个任务,使用 [任务文档范本](templates.md) 写入清单、文档基线和任务记录。一个任务也保留清单及记录,已有清单优先更新,不重复规划已交付内容。
|
|
24
26
|
3. 执行、继续或返修时读取 [执行与 Git 流程](workflow.md),核验依赖、工作区、主分支和实际状态;每个任务使用记录中的独立分支。
|
|
25
27
|
4. 按 [hai-app-create](../hai-app-create/SKILL.md) 与当前版本公开契约实现,按 [hai-app-tests](../hai-app-tests/SKILL.md) 做 Red → Green → Refactor,按 [hai-app-review](../hai-app-review/SKILL.md) 审查改动。只读取当前任务需要的模块资源。
|
|
26
|
-
5.
|
|
28
|
+
5. 验证、提交、合并后更新清单与任务记录;全部目标范围验收通过才推进文档基线,规则见 [基线收尾](changes.md#验收与基线推进)。报告完成任务、旧/新文档 SHA、实现/合并提交、门禁和剩余阻塞;验证通过但尚未合并不能标为已完成。
|
|
27
29
|
|
|
28
30
|
## 拆分原则
|
|
29
31
|
|
|
@@ -31,6 +33,7 @@ description: "从需求或设计文档规划、执行或返修 hai 应用:按
|
|
|
31
33
|
- **按真实依赖排序**:多个功能共用的 contract、迁移或基础配置可先行,但必须有真实消费者及可验证契约。记录依赖任务编号,避免循环;共同事务/状态约束难以分离时合并任务。
|
|
32
34
|
- **小需求保持单任务**:无需以文档页数、文件数、固定工时或 token 数硬拆。一个任务应能在合理上下文中说明规则、完成改动并跑完适用门禁;过大时按业务边界细分,过碎时合并。
|
|
33
35
|
- **先复用再安排开发**:区分已有满足项、配置接入和真实功能缺口;已有能力通过验证即可,不重复生成框架或现成模块。不要为可能出现的需求安排抽象、依赖或占位功能。
|
|
36
|
+
- **模板差异纳入交付**:按目标业务调整或移除示例逻辑,成套同步 schema、授权、初始化/种子数据、迁移、界面、测试与文档。将仍适用的回归与已被新需求替代的断言区分,不能让旧角色/流程断言或过期 AGENTS/skill 继续约束实现。
|
|
34
37
|
- **每项有来源和验收**:需求章节/编号映射到任务或已有实现证据,权限、异常和状态规则纳入验收;排除/阻塞项写明理由。不用“完成模块”“完善页面”等无法判定的标题替代结果。
|
|
35
38
|
- **默认串行**:按依赖和清单顺序执行。这里的任务是 Markdown 与 Git 工作项,不自动变成额外会话、子代理、调度器或新的 SDD 工具链。
|
|
36
39
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# 文档差异与基线流程
|
|
2
|
+
|
|
3
|
+
用户提交需求/设计文档后要求同步实现时读取本文件;首次实施也按此处记录和收尾基线。任务执行、TDD 与分支合并复用 [workflow.md](workflow.md),记录结构见 [templates.md](templates.md)。
|
|
4
|
+
|
|
5
|
+
## 基线含义
|
|
6
|
+
|
|
7
|
+
在已有任务清单内维护以下信息,不新增状态系统:
|
|
8
|
+
|
|
9
|
+
| 标识 | 含义 |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| B:已验收文档提交 | 当前计划的完整文档集合已被实现并通过整体验收时对应的 Git 完整 SHA;首次未验收时为“未建立” |
|
|
12
|
+
| S:本轮目标文档提交 | 本次要实现的文档快照完整 SHA;开始分析时固定,执行期间不随 HEAD 自动变化 |
|
|
13
|
+
| I:本轮集成起点 | 规划时确认的主分支完整 SHA,用于定位当前代码和文档状态 |
|
|
14
|
+
| H:已验收实现提交 | 最后一次通过整体门禁的主分支完整 SHA,包含实际任务合并;与 B 分开记录 |
|
|
15
|
+
|
|
16
|
+
B/S 指向文档内容,H 指向经过验证的实现,三者通常不同。记录完整文档路径集合、权威关系和适用范围;多文档用同一 S 快照,不逐份取“最后修改提交”拼接。路径集合包括本轮新增/删除/重命名的旧、新路径;文档拆分、合并时保留来源映射。多个独立计划分别维护基线,不用一个计划的完成替代另一个计划。
|
|
17
|
+
|
|
18
|
+
## 固定输入与恢复
|
|
19
|
+
|
|
20
|
+
1. 先读取清单中的 B/H、未完成变更轮次与任务记录,核验 Git 对象和实际代码。旧项目没有 B/H 时,先从历史任务、合并和验收证据追溯;证据不足则标记“未建立”,对当前文档与实现做覆盖核验,形成补齐任务。不能选择 HEAD、最近文档提交或最近 merge 来伪造旧基线;完成覆盖核验及整体验收后才能首次建立。
|
|
21
|
+
2. 将用户给出的新提交解析成完整 SHA S;只说“修改了某文档”时,核对该路径的提交历史和当前集成线,默认取分析开始时包含这些已提交修改的 HEAD 快照,说明选择依据。多份文档、跨多个提交时比较 B 到 S 的净变化,不能只看 S 的单个提交。若存在多个无法推断的候选版本,仅澄清目标版本,同时继续独立的影响分析。
|
|
22
|
+
3. 用 `git status` 检查文档是否还有未提交修改,包含未跟踪文件。默认实现固定的已提交快照;工作区文档与 S 不同则明确列出,不静默混入、不替用户提交。用户改为采用未提交版本时,先明确新的输入与可追溯记录,不能声称它已由 S 表示。
|
|
23
|
+
4. 核验 B/S/H 对象可读取、B/H 的历史验收仍有依据,确认主分支实际包含此前实现。对象缺失先检查本地 refs/浅克隆边界,在允许的仓库访问范围内补齐历史;无法核验时阻塞基线判断,不以空差异表示无变化。H 不在当前集成历史或实现被 revert 时,记录实现漂移并重新核验受影响需求。
|
|
24
|
+
5. 常规演进应能证明 B 是 S 的祖先;分叉、回退或历史重写时先记录原因,确认用户目标和端点内容后比较 B/S 两棵树。不擅自改用 merge-base 或三点 diff 缩小变化,不 reset/rebase 用户历史。已存在相同 B/S 的未完成轮次时从其阶段恢复,不重复创建变更和任务。
|
|
25
|
+
|
|
26
|
+
## 差异与任务映射
|
|
27
|
+
|
|
28
|
+
1. 先查看仓库级路径变化以发现移出目录的重命名,再比较完整文档集合的内容。以下为命令形状,B/S、路径需替换为核验值,参数按当前 shell 正确引用:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
git rev-parse --verify <ref>^{commit}
|
|
32
|
+
git merge-base --is-ancestor <B> <S>
|
|
33
|
+
git diff --name-status --find-renames <B> <S>
|
|
34
|
+
git diff --find-renames <B> <S> -- <全部受管文档的旧路径与新路径>
|
|
35
|
+
git show <B>:<旧路径>
|
|
36
|
+
git show <S>:<新路径>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
祖先检查退出码 1 表示非祖先,其他错误需单独处理;新增/删除文件只读取存在的一端。重命名检测是线索,结合正文识别拆分/合并,不能只按文件名认定需求新增或删除。富文本文档需读取两端内容/附件,无法可靠提取时记录阻塞,不当作纯格式变化。
|
|
40
|
+
2. 用户指出的路径是入口,仍核对计划内全部文档在 B/S 间的变化,以及关联章节、附件和权威关系;跨文档权限、状态、字段、接口和验收一起复审。区分新增、修改、删除、移动/拆分/合并及纯说明变更。读取差异两端上下文和当前代码/测试,使用 [hai-spec-review](../hai-spec-review/SKILL.md) 复审受影响规则;不能把删掉一句话直接解释为删除功能。
|
|
41
|
+
3. 在清单追加稳定变更编号(如 C001),保存 B/S/I、文档集合、差异条目、需求位置、关联任务及处理结论。每项变化必须映射到以下结果之一:
|
|
42
|
+
|
|
43
|
+
| 情况 | 处理 |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| 原任务的规则、缺陷或验收变化 | 复用任务编号、文件和分支,更新当前来源与验收;已完成任务追加返修轮次,保留历史证据 |
|
|
46
|
+
| 已有待执行/进行中任务受影响 | 更新其范围、依赖和验收;进行中任务先保存阶段,合入新规划与主线后重新验证,旧测试结果不能证明新验收 |
|
|
47
|
+
| 新的独立可验收能力 | 新建稳定编号并关联变更与依赖;一个任务可处理多项差异,一项跨功能差异可关联多个任务 |
|
|
48
|
+
| 明确移除/替代需求 | 核对调用方、权限、数据迁移/保留与回归;复用原任务或新建有关联的清理任务,不能只删任务记录或擅自删除业务数据 |
|
|
49
|
+
| 原任务尚未交付且已失去需求 | 标为“已取消”并写明来源和替代任务;若有部分实现进入主线,仍需清理/验证任务,不能用取消掩盖残留 |
|
|
50
|
+
| 纯说明/路径调整或当前实现已满足 | 记录语义不变或已有满足的证据、必要验证及来源链接更新,不强造业务任务或 TDD 失败 |
|
|
51
|
+
| 冲突、未决、超出授权范围 | 记录阻塞/排除依据与恢复条件;不能静默忽略,也不能把尚未同步的需求宣称已验收 |
|
|
52
|
+
|
|
53
|
+
4. 检查受影响任务的上游/下游、共享契约和回归范围,调整依赖顺序并消除循环;不要重开所有历史任务。任务记录关联 C001 与本轮 B/S,清单覆盖映射更新到 S。用户只授权部分差异时可实施独立范围,但同一计划仍有未同步变化就保留 B;需要独立推进时先明确划分计划和文档范围,不能悄悄改成逐文件基线。
|
|
54
|
+
|
|
55
|
+
## 分支执行与中断
|
|
56
|
+
|
|
57
|
+
- 将固定的目标文档、差异映射、更新/新增任务记录先集成到实际主分支,再按依赖串行执行。S 在其他分支时先核对范围:只集成本次授权的文档变化,不能顺带合入无关代码;记录来源 S 及实际文档集成提交,并按 Git blob 核对主线文档集合与 S 一致,不强求来源提交 SHA 在 cherry-pick 后保持不变。
|
|
58
|
+
- 新任务从最新主线创建分支;原任务复用原分支,先合入最新主线。分支缺失时按记录和当前主线核验后恢复,不从旧代码覆盖主线。每轮按新验收走 TDD、适用门禁、本地合并、集成验证和记账,详见 [执行流程](workflow.md)。
|
|
59
|
+
- 发现 S 之后又有文档变化时,保留本轮 S 并列为后续候选;不要在执行中自动改目标或把较新需求当作已完成。若较新文档已进入当前主线并与 S 不一致,暂停依赖该差异的实施及基线推进,保存进度后按用户目标调整本轮。用户明确采用新目标时从未推进的 B 比较新 S,记录目标替换及旧 B/S,核对已合入代码后复用任务和证据,重新映射、复审与验证;不要重复开发已满足项或继续执行被新目标撤回的旧任务。
|
|
60
|
+
- 中断时保存目标、任务阶段、实际实现/合并 SHA 和下一步;B/H 保持上次整体验收结果。续跑先核对实际主线与任务分支,再补验证/合并/记账。部分完成、门禁失败、只交付分支或存在未决项时都不能推进 B/H。
|
|
61
|
+
|
|
62
|
+
## 验收与基线推进
|
|
63
|
+
|
|
64
|
+
1. 核对本轮每项差异均有处理证据,所有需要实现/清理的任务已实际合并,取消/无需修改项有依据;受影响的原有需求和完整覆盖映射仍成立。首次实施无 B 时对完整文档集合验收。B 等于 S 或 diff 为空时仍检查未完成任务与实现漂移,不能直接宣称完成。
|
|
65
|
+
2. 在主分支执行项目要求的整体门禁和需求验收,确认目标文档集合的 Git blob(含删除/新增路径)与 S 一致,工作区没有混入未提交实现。若执行期间主线代码继续推进,检查新提交的影响,对最终主线重新执行必要验证,记录通过验证的完整 SHA H。
|
|
66
|
+
3. 只有以上通过后,在主分支独立文档记账提交中将“已验收文档提交”推进为 S,将“已验收实现提交”更新为 H,关闭 C001 并保留旧 B/H、任务合并与门禁证据。H 是记账前已验证的实现提交,不填该记账提交自身的 SHA,不通过 amend/rebase 制造自引用。
|
|
67
|
+
4. 提交后核验记录已进入实际主分支,报告 B → S、H、实际合并/记账提交、验证及未完成范围。纯说明变化可以在证据与适用验证齐全后推进文档基线,无需制造代码改动;仍存在未处理需求时报告部分完成并保留旧基线。
|