@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67
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 +66 -257
- package/README.ja.md +66 -257
- package/README.md +66 -257
- package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +435 -247
- package/dist/index.js.map +1 -1
- package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +5 -0
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +8 -3
- package/templates/base/vite.config.ts.hbs +3 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +4 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +20 -55
- package/templates/skills/hai-deploy/middleware.md +5 -11
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +9 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# hai-storage 参考
|
|
2
|
+
|
|
3
|
+
> `@h-ai/storage` 提供统一文件存储接口,支持本地文件系统(LocalProvider)和 S3 兼容存储(S3Provider),通过 `storage.file`、`storage.dir`、`storage.presign` 访问存储,`storage.checksum` 提供 Node 端无需初始化的字节完整性工具。
|
|
4
|
+
|
|
5
|
+
## 运行环境
|
|
6
|
+
|
|
7
|
+
> ⚠️ **服务端模块**。`storage.file` / `storage.dir` / `storage.presign` 均在 Node.js 端使用。浏览器端通过预签名 URL 或 API 端点间接访问存储(见下方「浏览器端文件上传」)。
|
|
8
|
+
|
|
9
|
+
## 适用场景
|
|
10
|
+
|
|
11
|
+
- 文件上传与下载
|
|
12
|
+
- 预签名 URL 生成(客户端直传)
|
|
13
|
+
- S3 兼容存储对接(AWS S3、MinIO、RustFS、阿里云 OSS 等)
|
|
14
|
+
- 本地开发文件存储
|
|
15
|
+
- 目录前缀清理与批量文件删除
|
|
16
|
+
|
|
17
|
+
## 使用步骤
|
|
18
|
+
|
|
19
|
+
### 1. 配置
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
# config/_storage.yml
|
|
23
|
+
type: local # local | s3
|
|
24
|
+
# 本地存储
|
|
25
|
+
root: ./data/storage
|
|
26
|
+
# S3 存储(type=s3 时生效)
|
|
27
|
+
# endpoint: ''
|
|
28
|
+
# region: us-east-1
|
|
29
|
+
# accessKeyId: ''
|
|
30
|
+
# secretAccessKey: ''
|
|
31
|
+
# bucket: my-bucket
|
|
32
|
+
# forcePathStyle: false
|
|
33
|
+
# prefix: ''
|
|
34
|
+
# publicUrl: ''
|
|
35
|
+
operationLog:
|
|
36
|
+
read: false # file.get/head/exists, dir.list, presign.getUrl/publicUrl
|
|
37
|
+
write: false # file.put/delete/deleteMany/copy, dir.delete, presign.putUrl
|
|
38
|
+
maxLength: 1000
|
|
39
|
+
level: debug # info | debug | trace
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
约定环境变量优先于 YAML,例如 `HAI_STORAGE_ROOT`、
|
|
43
|
+
`HAI_STORAGE_ACCESSKEYID`、`HAI_STORAGE_SECRETACCESSKEY`。
|
|
44
|
+
|
|
45
|
+
MinIO、RustFS 使用同一个 S3 Provider:配置 `type: s3`、S3 API `endpoint`(通常为 9000 端口)和 `forcePathStyle: true`,并预先创建 `bucket`;不要新增 `type: minio` 或 `type: rustfs`。
|
|
46
|
+
|
|
47
|
+
使用 hai-deploy 的默认自建 S3 配置时,可通过 `container.s3Engine`(`HAI_DEPLOY_CONTAINER_S3ENGINE`,`rustfs | minio`)选择实现,默认 RustFS。部署流程复用应用镜像中的 S3 SDK 幂等初始化 bucket,不依赖单独的 mc 镜像;自定义外部 S3 服务仍需确认 bucket 已存在且凭据有相应权限。
|
|
48
|
+
|
|
49
|
+
`operationLog` 在 Local/S3 Provider 的真实 file/dir/presign 操作处输出日志,不要在 `storage-main.ts` 或调用方重复包装。文件内容不会写入日志,上传数据只记录字节长度;`maxLength` 用于截断序列化后的参数,`level` 默认 `debug`。
|
|
50
|
+
|
|
51
|
+
### 2. 初始化与关闭
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { storage } from '@h-ai/storage'
|
|
55
|
+
|
|
56
|
+
await storage.init(core.config.get('storage'))
|
|
57
|
+
// 使用后关闭
|
|
58
|
+
const closeResult = await storage.close()
|
|
59
|
+
if (!closeResult.success) {
|
|
60
|
+
throw new Error(closeResult.error.message)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
> `storage.config` 返回的是**脱敏配置快照**;S3 的 `accessKeyId` / `secretAccessKey` 等敏感字段会被替换为 `[REDACTED]`。
|
|
65
|
+
|
|
66
|
+
## 核心 API
|
|
67
|
+
|
|
68
|
+
### 文件操作
|
|
69
|
+
|
|
70
|
+
`storage.file` 提供以下方法:
|
|
71
|
+
|
|
72
|
+
- `put(key, data, options?)`
|
|
73
|
+
- `get(key, options?)`
|
|
74
|
+
- `getStream(key, options?)`(字节流,迭代错误可抛出)
|
|
75
|
+
- `head(key)`
|
|
76
|
+
- `exists(key)`
|
|
77
|
+
- `delete(key)`
|
|
78
|
+
- `deleteMany(keys)`
|
|
79
|
+
- `copy(sourceKey, destKey, options?)`
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// 上传文件
|
|
83
|
+
const result = await storage.file.put('avatars/user-123.png', fileBuffer, {
|
|
84
|
+
contentType: 'image/png',
|
|
85
|
+
metadata: { userId: '123' },
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// 下载文件
|
|
89
|
+
const file = await storage.file.get('avatars/user-123.png')
|
|
90
|
+
|
|
91
|
+
// 列出文件
|
|
92
|
+
const files = await storage.dir.list({ prefix: 'avatars/', maxKeys: 100 })
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 预签名 URL
|
|
96
|
+
|
|
97
|
+
`storage.presign` 提供以下方法:
|
|
98
|
+
|
|
99
|
+
- `getUrl(key, options?)`
|
|
100
|
+
- `putUrl(key, options?)`
|
|
101
|
+
- `publicUrl(key)`
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// 客户端直传模式
|
|
105
|
+
const uploadUrl = await storage.presign.putUrl('uploads/doc.pdf', {
|
|
106
|
+
expiresIn: 3600, // 1 小时有效
|
|
107
|
+
contentType: 'application/pdf',
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// 临时下载链接
|
|
111
|
+
const downloadUrl = await storage.presign.getUrl('uploads/doc.pdf', {
|
|
112
|
+
expiresIn: 300, // 5 分钟有效
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
> `storage.presign.putUrl()` 当前不支持 `maxSize` 约束参数;如需大小限制,请在应用层上传前校验,或在服务端落库/回调处二次校验。
|
|
117
|
+
|
|
118
|
+
## 错误码 — `HaiStorageError`
|
|
119
|
+
|
|
120
|
+
| 错误码 | code | 说明 |
|
|
121
|
+
|--------|------|------|
|
|
122
|
+
| `HaiStorageError.CONNECTION_FAILED` | `hai:storage:001` | 连接失败 |
|
|
123
|
+
| `HaiStorageError.OPERATION_FAILED` | `hai:storage:002` | 操作失败 |
|
|
124
|
+
| `HaiStorageError.NOT_FOUND` | `hai:storage:003` | 文件不存在 |
|
|
125
|
+
| `HaiStorageError.ALREADY_EXISTS` | `hai:storage:004` | 文件已存在 |
|
|
126
|
+
| `HaiStorageError.CHECKSUM_FAILED` | `hai:storage:016` | checksum 读取/计算失败 |
|
|
127
|
+
| `HaiStorageError.INVALID_CHECKSUM` | `hai:storage:017` | checksum 格式/算法非法 |
|
|
128
|
+
| `HaiStorageError.PERMISSION_DENIED` | `hai:storage:005` | 权限不足 |
|
|
129
|
+
| `HaiStorageError.QUOTA_EXCEEDED` | `hai:storage:006` | 配额超限 |
|
|
130
|
+
| `HaiStorageError.INVALID_PATH` | `hai:storage:007` | 路径无效 |
|
|
131
|
+
| `HaiStorageError.IO_ERROR` | `hai:storage:008` | IO 错误 |
|
|
132
|
+
| `HaiStorageError.NETWORK_ERROR` | `hai:storage:009` | 网络错误 |
|
|
133
|
+
| `HaiStorageError.NOT_INITIALIZED` | `hai:storage:010` | 未初始化 |
|
|
134
|
+
| `HaiStorageError.UNSUPPORTED_TYPE` | `hai:storage:011` | 不支持的存储类型 |
|
|
135
|
+
| `HaiStorageError.CONFIG_ERROR` | `hai:storage:012` | 配置错误 |
|
|
136
|
+
| `HaiStorageError.PRESIGN_FAILED` | `hai:storage:013` | 预签名 URL 生成失败 |
|
|
137
|
+
| `HaiStorageError.UPLOAD_FAILED` | `hai:storage:014` | 上传失败 |
|
|
138
|
+
| `HaiStorageError.DOWNLOAD_FAILED` | `hai:storage:015` | 下载失败 |
|
|
139
|
+
|
|
140
|
+
## HTTP API 契约
|
|
141
|
+
|
|
142
|
+
公共 HTTP API 统一由 `@h-ai/api-contract` 提供,并由 `@h-ai/serv/features/storage` 绑定到本模块。
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { apiClient } from '@h-ai/api-client'
|
|
146
|
+
|
|
147
|
+
const result = await apiClient.storage.presignedUrls.createUpload({ key: 'avatar.png' })
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 常见模式
|
|
151
|
+
|
|
152
|
+
大文件上传用 `file.put(key, readableOrAsyncByteStream, { signal, ifNoneMatch: '*' })`,内部使用有界 S3 multipart;下载用 `file.getStream(key, { signal, rangeStart, rangeEnd })`,及时消费或关闭,迭代期间 I/O/取消可能抛错。本地读取目录直接返回 `INVALID_PATH`。checksum 的 signal 取消会停止等待下一块数据并请求关闭输入迭代器。`get()` 返回完整 Buffer,只用于容量可控的数据。仅创建写入采用 S3 标准条件或本地原子发布,冲突返回 `ALREADY_EXISTS`;后端不支持时失败,不退回覆盖。
|
|
153
|
+
|
|
154
|
+
`checksum.generate(data, options?)` / `checksum.verify(data, expected, options?)` 默认 SHA-256、小写 hex,可选 sha384/sha512/sha1/md5、base64 编码与 signal;流仅消费一次,读取失败返回 HaiResult 错误,不匹配返回 false。MD5/SHA-1 不作安全认证,ETag 和 multipart 复合 checksum 不当作完整文件摘要。浏览器不导入 Node checksum;离线增量摘要需使用适用的浏览器实现,不把 SubtleCrypto.digest 当流式 API。
|
|
155
|
+
|
|
156
|
+
### 与 kit 集成(文件上传 API)
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { storage } from '$lib/server/init'
|
|
160
|
+
// src/routes/api/files/upload/+server.ts
|
|
161
|
+
import { kit } from '@h-ai/kit'
|
|
162
|
+
import { z } from 'zod'
|
|
163
|
+
|
|
164
|
+
const UploadSchema = z.object({
|
|
165
|
+
key: z.string().min(1),
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
export const POST = kit.handler(async ({ request, locals }) => {
|
|
169
|
+
kit.guard.require(locals.session, 'storage:write')
|
|
170
|
+
const formData = await request.formData()
|
|
171
|
+
const file = formData.get('file') as File
|
|
172
|
+
if (!file)
|
|
173
|
+
return kit.response.badRequest('Missing file')
|
|
174
|
+
|
|
175
|
+
const key = `uploads/${locals.session.userId}/${file.name}`
|
|
176
|
+
const buffer = new Uint8Array(await file.arrayBuffer())
|
|
177
|
+
const result = await storage.file.put(key, buffer, { contentType: file.type })
|
|
178
|
+
if (!result.success)
|
|
179
|
+
return kit.response.internalError()
|
|
180
|
+
return kit.response.created({ key })
|
|
181
|
+
})
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
> 说明:`@h-ai/storage` 只提供存储能力,不强制要求 `/api/storage` 路由命名。应用可按业务语义自定义 API 路径。
|
|
185
|
+
|
|
186
|
+
> Local Provider 的 `storage.dir.list()` 会递归扫描匹配前缀下的文件,仅支持 `maxKeys` 截断,不支持 `continuationToken` 真分页;大目录建议缩小 `prefix` 或改用 S3 Provider。
|
|
187
|
+
|
|
188
|
+
### 头像上传
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const key = `avatars/${userId}/${Date.now()}.png`
|
|
192
|
+
const result = await storage.file.put(key, avatarBuffer, {
|
|
193
|
+
contentType: 'image/png',
|
|
194
|
+
})
|
|
195
|
+
if (result.success) {
|
|
196
|
+
const publicUrl = storage.presign.publicUrl(key)
|
|
197
|
+
await iam.user.updateUser(userId, { avatarUrl: publicUrl ?? '' })
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 浏览器端文件上传(预签名 URL 模式)
|
|
202
|
+
|
|
203
|
+
浏览器端无法直接使用 `storage` 模块,应通过 API 获取预签名 URL 后直传:
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { apiFetch } from '$lib/utils/api'
|
|
207
|
+
|
|
208
|
+
// 1. 向服务端请求预签名上传 URL
|
|
209
|
+
const res = await apiFetch('/api/storage/presign/upload', {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: { 'Content-Type': 'application/json' },
|
|
212
|
+
body: JSON.stringify({ key: `uploads/${file.name}`, contentType: file.type }),
|
|
213
|
+
})
|
|
214
|
+
const { url } = await res.json()
|
|
215
|
+
|
|
216
|
+
// 2. 浏览器直传到存储(不经过服务端,大文件友好)
|
|
217
|
+
await fetch(url, {
|
|
218
|
+
method: 'PUT',
|
|
219
|
+
body: file,
|
|
220
|
+
headers: { 'Content-Type': file.type },
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
也可使用 typed client(需配合 `@h-ai/api-client`):
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { apiClient } from '@h-ai/api-client'
|
|
228
|
+
|
|
229
|
+
const result = await apiClient.storage.presignedUrls.createUpload({
|
|
230
|
+
key: `avatars/${userId}.png`,
|
|
231
|
+
contentType: 'image/png',
|
|
232
|
+
})
|
|
233
|
+
if (result.success) {
|
|
234
|
+
await fetch(result.data.url, { method: 'PUT', body: file })
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## 相关 Skills
|
|
239
|
+
|
|
240
|
+
- `hai-build`:模块初始化顺序
|
|
241
|
+
- `hai-core`:配置与 HaiResult 模型
|
|
242
|
+
- `hai-kit`:SvelteKit API 端点集成(`kit.handler` + `kit.guard`)
|
|
243
|
+
- `hai-ui`:Storage 场景组件(FileUpload/ImageUpload/AvatarUpload)
|
|
@@ -31,7 +31,7 @@ description: "使用 @h-ai/ui 构建 Svelte 5 表单、表格、弹窗、新手
|
|
|
31
31
|
|
|
32
32
|
新手引导、首次 Cookie 提示、操作导览和帮助中心见 [guide.md](guide.md)。fullstack 默认在共享管理界面接入,新增业务只补内容与 data-guide 锚点。
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
35
35
|
|
|
36
36
|
- 运行环境
|
|
37
37
|
- 适用场景
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
至少检查以下四项:
|
|
40
40
|
|
|
41
41
|
- `svelte.config.*`:`autoImportHaiUi()` 在 `vitePreprocess()` 前。
|
|
42
|
-
- `vite.config.*`:`ssr.noExternal` 包含 `@h-ai
|
|
42
|
+
- `vite.config.*`:`ssr.noExternal` 包含 `@h-ai/ui` 和 `@h-ai/kit`,`optimizeDeps.exclude` 包含 `bits-ui`。
|
|
43
43
|
- `app.css`:已导入 `global.css` / `theme.css`;移动端还要导入 `design-tokens.css` / `mobile.css`。
|
|
44
44
|
- Tailwind:已配置 `@source` 扫描 `@h-ai/ui/dist/**/*.{svelte,js,ts}`。
|
|
45
45
|
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
# 核心包
|
|
92
|
-
npm install @h-ai/ui
|
|
92
|
+
npm install @h-ai/ui @h-ai/core
|
|
93
93
|
|
|
94
94
|
# 必须的对等依赖与构建工具
|
|
95
95
|
npm install -D svelte @sveltejs/kit @sveltejs/vite-plugin-svelte
|
|
@@ -99,6 +99,8 @@ npm install -D tailwindcss @tailwindcss/vite daisyui
|
|
|
99
99
|
npm install -D @iconify/tailwind4 @iconify-json/tabler
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
Node adapter 使用 Crypto、Kit 或 IAM 时,还需将 `sm-crypto@0.5.7` 作为直接生产依赖安装,保留 Node 原生模块解析。
|
|
103
|
+
|
|
102
104
|
### 2. svelte.config.js
|
|
103
105
|
|
|
104
106
|
```js
|
|
@@ -143,7 +145,8 @@ export default defineConfig({
|
|
|
143
145
|
},
|
|
144
146
|
ssr: {
|
|
145
147
|
// @h-ai 包为 ESM + Svelte 组件,需 Vite 在 SSR 时处理
|
|
146
|
-
noExternal: [
|
|
148
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
149
|
+
external: ['@h-ai/core', 'sm-crypto'],
|
|
147
150
|
},
|
|
148
151
|
})
|
|
149
152
|
```
|
|
@@ -634,8 +637,9 @@ AuthPanel 在 SSR 阶段禁用原生表单控件,客户端挂载并绑定事
|
|
|
634
637
|
4. **Combobox 统一单选/多选**:`MultiSelect` 已删除
|
|
635
638
|
5. **移动端样式**:务必引入 `design-tokens.css` + `mobile.css`,使用 SafeArea 包裹原生 App 页面
|
|
636
639
|
6. **`@source` 必须配置**:未配置则 TailwindCSS 无法扫描 `@h-ai/ui` 组件中的 class,样式会丢失
|
|
637
|
-
7.
|
|
638
|
-
8.
|
|
640
|
+
7. **响应式布局**:后台工作区和数据列表使用可用宽度,避免外壳固定 max-width 造成宽屏两侧留白。flex/grid 子项设 min-width: 0,表格在自身容器滚动;正文和登录框保留阅读宽度。至少验证 390、1440、1920、2560、3840px 的内容占宽、导航与全页溢出。
|
|
641
|
+
8. **`ssr.noExternal`**:Svelte 组件及 `@h-ai/kit/client` 的 `$app/*` 路由别名需要 Vite 编译,UI 和 Kit 保持 noExternal,core 及其他纯服务端包保留原生加载;本地链接的框架包也要显式 external,避免日志、数据库等原生依赖被重复打包。sm-crypto 使用动态 CommonJS require,应放入 `ssr.external`,并以明确版本列入应用 dependencies;adapter-node 只根据直接生产依赖排除二次打包,不能仅依赖它作为框架的传递依赖。
|
|
642
|
+
9. **data-* 属性透传**:公开 Svelte 组件会把调用方传入的 `data-*` 属性透传到根节点或主交互节点;测试和自动化优先直接使用 `data-testid`、`data-analytics-id`,不要为测试 ID 重复封装组件
|
|
639
643
|
|
|
640
644
|
## 导出路径
|
|
641
645
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-vecdb
|
|
3
|
-
description: "使用
|
|
3
|
+
description: "使用 LanceDB/pgvector/Qdrant/Chroma 管理向量集合、写入和检索;排查维度、连接与过滤问题。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-vecdb
|
|
@@ -22,7 +22,7 @@ description: "使用 @h-ai/vecdb 管理 LanceDB/pgvector/Qdrant/Chroma 集合与
|
|
|
22
22
|
|
|
23
23
|
## 按需参考
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
26
26
|
|
|
27
27
|
- 运行环境
|
|
28
28
|
- 适用场景
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../messages/en-US.json","../messages/zh-CN.json","../src/cli-i18n.ts","../src/commands/cli-skill-templates.ts"],"names":[],"mappings":"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","file":"chunk-FWN5B54L.js","sourcesContent":["{\n \"cli_createFailed\": \"Failed to create project:\",\n \"cli_installFailed\": \"Dependency installation failed ({pm}).\",\n \"cli_commandLabel\": \"Command: {command}\",\n \"cli_exitCode\": \"Exit code: {code}\",\n \"cli_signal\": \"Signal: {signal}\",\n \"cli_selectHint\": \"- ↑/↓ select, Enter confirm\",\n \"cli_multiselectHint\": \"- Space select, Enter confirm\",\n \"cli_featuresPrompt\": \"Select features:\",\n \"cli_frontendsPrompt\": \"Select frontend targets:\",\n \"cli_invalidGenerateName\": \"Use a name starting with a letter, with only letters, digits, hyphens or underscores\",\n \"cli_generateOutsideWorkspace\": \"Generated files must remain inside the workspace, including symbolic link targets\",\n \"cli_generateExists\": \"File already exists: {path}. Use --force to overwrite it\",\n \"cli_deploy_module_missing\": \"Deploy module not found. Install @h-ai/deploy first:\",\n \"cli_deploy_load_credentials\": \"Loading credentials...\",\n \"cli_deploy_credentials_failed\": \"Failed to load credentials: {message}\",\n \"cli_deploy_credentials_loaded\": \"Loaded {count} credentials\",\n \"cli_deploy_load_config\": \"Loading deploy config...\",\n \"cli_deploy_config_missing\": \"Deploy config not found: {path}\",\n \"cli_deploy_config_hint\": \"Run: hai add deploy to generate config template\",\n \"cli_deploy_config_loaded\": \"Deploy config loaded\",\n \"cli_deploy_scanning\": \"Scanning application...\",\n \"cli_deploy_scan_failed\": \"Scan failed: {message}\",\n \"cli_deploy_scanned\": \"Scanned: {name} (SvelteKit: {sveltekit}, Services: {services})\",\n \"cli_deploy_none\": \"none\",\n \"cli_deploy_initializing\": \"Initializing deploy module...\",\n \"cli_deploy_init_failed\": \"Init failed: {message}\",\n \"cli_deploy_initialized\": \"Deploy module initialized\",\n \"cli_deploy_deploying\": \"Deploying application...\",\n \"cli_deploy_failed\": \"Deploy failed: {message}\",\n \"cli_deploy_success\": \"Deployment successful!\",\n \"cli_deploy_url\": \" URL: {url}\",\n \"cli_deploy_id\": \" ID: {id}\",\n \"cli_deploy_env\": \" Env: {names}\",\n \"cli_deploy_command_failed\": \"Deploy command failed\",\n \"cli_adminPasswordSaved\": \" Administrator: admin; the random initial password is saved as HAI_ADMIN_DEFAULT_PASSWORD in .env.local\",\n \"cli_deploy_environment\": \"Deployment environment (test / prod / other)\",\n \"cli_deploy_cancelled\": \"Deployment cancelled.\",\n \"cli_deploy_environment_hint\": \"Specify --env <environment>; non-interactive calls also require --yes, or use a terminal to select middleware.\",\n \"cli_deploy_middleware\": \"{service}: current default {backend}; {purpose}. Source: {source}\",\n \"cli_deploy_action\": \"How should {service} be configured?\",\n \"cli_deploy_default\": \"Use default configuration\",\n \"cli_deploy_custom\": \"Use custom configuration\",\n \"cli_deploy_keep\": \"Keep unchanged\",\n \"cli_deploy_backend\": \"Select the backend for {service}\",\n \"cli_deploy_field\": \"{name} ({description}; {constraint})\",\n \"cli_deploy_required\": \"This value is required\",\n \"cli_deploy_invalid_value\": \"The value does not satisfy the field constraints\",\n \"cli_deploy_cloud_unsupported\": \"No cloud provisioner is available for {backend}; use a custom connection or select a supported backend.\",\n \"cli_deploy_saved\": \"Configured {name}={value} (public values in the target environment file; credentials in .env.local)\",\n \"cli_deploy_secret\": \"[redacted]\",\n \"cli_deploy_missing_keep\": \"The target environment has no complete {service} configuration to keep.\",\n \"cli_deploy_s3_engine\": \"Select the S3 service implementation (application type remains s3)\",\n \"cli_deploy_target_reused\": \"Reusing deployment target {target}. Update connection settings in HAI_DEPLOY_PROVIDER in .env.local.\",\n \"cli_deploy_target_missing\": \"Deployment target is incomplete: {fields}. Set HAI_DEPLOY_PROVIDER in .env.local, or run interactively to complete setup.\",\n \"cli_deploy_target_platform\": \"Select the deployment platform\",\n \"cli_deploy_target_ssh\": \"SSH server (Docker / Podman Compose)\",\n \"cli_deploy_target_field\": \"{name} (saved only in .env.local)\",\n \"cli_deploy_target_ssh_hint\": \"Enter connection and port settings. Enter a private key file path, or leave blank for ssh-agent/default keys. Leave known_hosts blank for the OpenSSH default. New host fingerprints are trusted on first connection; changed fingerprints are rejected.\",\n \"cli_deploy_target_runtime\": \"Remote container runtime (auto prefers Podman)\",\n \"cli_deploy_target_saved\": \"Deployment target saved to Git-ignored .env.local and reused on subsequent runs.\",\n \"cli_deploy_middleware_plan\": \"Do you need to configure middleware for this deployment?\",\n \"cli_deploy_middleware_configure\": \"Review and configure each middleware service\",\n \"cli_deploy_middleware_skip\": \"Environment is complete; deploy directly\",\n \"cli_deploy_middleware_skipped\": \"Skipped all middleware configuration and provisioning. Deploying with current environment settings and existing services.\",\n \"cli_build_failed\": \"Build command {command} failed (exit {code}).\",\n \"cli_build_use_pnpm\": \"Run via pnpm build to use the project pnpm executable.\",\n \"cli_build_target_invalid\": \"target must be compile, docker or desktop.\",\n \"cli_build_options_invalid\": \"runtime, network, sqlite-prebuild-mirror and no-http-proxy require docker; platform and arch require desktop.\",\n \"cli_build_version_invalid\": \"Set a valid three-part version in the application root package.json (for example 1.2.3 or 1.2.3-beta.1; at most 80 characters).\",\n \"cli_build_native_host\": \"Build Tauri on the matching OS (windows/linux/macos), with x64 or arm64 architecture. Install the Rust target, system SDK and packaging tools first.\",\n \"cli_build_desktop_missing\": \"No desktop directories in hai.build.json. Generate a desktop frontend or add an existing Tauri project.\",\n \"cli_build_images_missing\": \"hai.build.json requires one app image and at most one web image.\",\n \"cli_build_runtime_missing\": \"Install and start Podman or Docker. auto prefers a reachable Podman engine.\",\n \"cli_build_mirror_invalid\": \"SQLite mirror must use HTTP(S) without credentials, query or fragment.\",\n \"cli_build_ready\": \"Build ready ({runtime}): {path}. Reuse these images across deployment environments.\",\n \"cli_build_native_ready\": \"Tauri build ready: {target}. Installers are under src-tauri/target/{target}/release/bundle in each project.\",\n \"cli_deploy_bundle_provider\": \"--bundle-only requires the docker-ssh Compose provider.\",\n \"cli_deploy_bundle_local\": \"Generate bundle only; no server connection\",\n \"cli_deploy_bundle_ready\": \"Deployment bundle ready; the server has not been deployed.\",\n \"cli_deploy_bundle_path\": \"Deployment bundle: {path}\"\n}\n","{\n \"cli_createFailed\": \"创建项目失败:\",\n \"cli_installFailed\": \"安装依赖失败({pm})。\",\n \"cli_commandLabel\": \"命令: {command}\",\n \"cli_exitCode\": \"退出码: {code}\",\n \"cli_signal\": \"信号: {signal}\",\n \"cli_selectHint\": \"- ↑/↓ 选择,回车确认\",\n \"cli_multiselectHint\": \"- 空格选择,回车确认\",\n \"cli_featuresPrompt\": \"选择功能:\",\n \"cli_frontendsPrompt\": \"选择前端目标:\",\n \"cli_invalidGenerateName\": \"名称必须以字母开头,且仅包含字母、数字、短横线或下划线\",\n \"cli_generateOutsideWorkspace\": \"生成文件必须位于工作目录内,符号链接目标也不得越界\",\n \"cli_generateExists\": \"文件已存在:{path}。使用 --force 才可覆盖\",\n \"cli_deploy_module_missing\": \"未找到部署模块,请先安装 @h-ai/deploy:\",\n \"cli_deploy_load_credentials\": \"正在加载凭证…\",\n \"cli_deploy_credentials_failed\": \"加载凭证失败:{message}\",\n \"cli_deploy_credentials_loaded\": \"已加载 {count} 项凭证\",\n \"cli_deploy_load_config\": \"正在加载部署配置…\",\n \"cli_deploy_config_missing\": \"未找到部署配置:{path}\",\n \"cli_deploy_config_hint\": \"运行 hai add deploy 生成部署配置模板\",\n \"cli_deploy_config_loaded\": \"部署配置已加载\",\n \"cli_deploy_scanning\": \"正在扫描应用…\",\n \"cli_deploy_scan_failed\": \"扫描失败:{message}\",\n \"cli_deploy_scanned\": \"已扫描:{name}(SvelteKit:{sveltekit},服务:{services})\",\n \"cli_deploy_none\": \"无\",\n \"cli_deploy_initializing\": \"正在初始化部署模块…\",\n \"cli_deploy_init_failed\": \"初始化失败:{message}\",\n \"cli_deploy_initialized\": \"部署模块已初始化\",\n \"cli_deploy_deploying\": \"正在部署应用…\",\n \"cli_deploy_failed\": \"部署失败:{message}\",\n \"cli_deploy_success\": \"部署成功!\",\n \"cli_deploy_url\": \" 地址:{url}\",\n \"cli_deploy_id\": \" 标识:{id}\",\n \"cli_deploy_env\": \" 环境变量:{names}\",\n \"cli_deploy_command_failed\": \"部署命令失败\",\n \"cli_adminPasswordSaved\": \" 管理员:admin;随机初始密码已保存到 .env.local 的 HAI_ADMIN_DEFAULT_PASSWORD\",\n \"cli_deploy_environment\": \"请输入部署环境(test / prod 等)\",\n \"cli_deploy_cancelled\": \"部署已取消。\",\n \"cli_deploy_environment_hint\": \"请指定 --env <环境>;非交互调用还需 --yes,或在交互终端逐项选择中间件。\",\n \"cli_deploy_middleware\": \"{service}:当前默认 {backend};{purpose}。来源:{source}\",\n \"cli_deploy_action\": \"如何处理 {service}?\",\n \"cli_deploy_default\": \"使用默认配置\",\n \"cli_deploy_custom\": \"使用自定义配置\",\n \"cli_deploy_keep\": \"保持不变\",\n \"cli_deploy_backend\": \"请选择 {service} 的后端\",\n \"cli_deploy_field\": \"{name}({description};{constraint})\",\n \"cli_deploy_required\": \"此项必填\",\n \"cli_deploy_invalid_value\": \"取值不满足字段约束\",\n \"cli_deploy_cloud_unsupported\": \"{backend} 没有可用的云自动开通器,请选择自定义连接或切换受支持的后端。\",\n \"cli_deploy_saved\": \"已配置 {name}={value}(公共值写目标环境文件,凭据写 .env.local)\",\n \"cli_deploy_secret\": \"[已隐藏]\",\n \"cli_deploy_missing_keep\": \"目标环境尚无 {service} 的完整配置,不能保持不变。\",\n \"cli_deploy_s3_engine\": \"选择自建 S3 服务实现(应用仍使用 type: s3)\",\n \"cli_deploy_target_reused\": \"复用部署目标 {target}。可在 .env.local 的 HAI_DEPLOY_PROVIDER 中更新连接参数。\",\n \"cli_deploy_target_missing\": \"部署目标配置不完整:{fields}。请在 .env.local 中配置 HAI_DEPLOY_PROVIDER,或使用交互终端完成引导。\",\n \"cli_deploy_target_platform\": \"选择部署平台\",\n \"cli_deploy_target_ssh\": \"SSH 服务器(Docker / Podman Compose)\",\n \"cli_deploy_target_field\": \"{name}(仅保存在 .env.local)\",\n \"cli_deploy_target_ssh_hint\": \"输入服务器连接及端口参数。私钥填写文件路径;留空使用 ssh-agent 或默认密钥。known_hosts 路径留空使用 OpenSSH 默认值。首次连接信任主机指纹,后续拒绝指纹变更。\",\n \"cli_deploy_target_runtime\": \"远程容器运行时(auto 优先使用 Podman)\",\n \"cli_deploy_target_saved\": \"部署目标已保存到 .env.local(已加入 Git 忽略),下次自动复用。\",\n \"cli_deploy_middleware_plan\": \"是否需要配置本次部署的中间件?\",\n \"cli_deploy_middleware_configure\": \"逐项检查并配置中间件\",\n \"cli_deploy_middleware_skip\": \"环境配置已完备,直接部署\",\n \"cli_deploy_middleware_skipped\": \"已跳过全部中间件配置与资源开通,使用当前环境配置及已有服务直接部署。\",\n \"cli_build_failed\": \"构建命令 {command} 失败(退出码 {code})。\",\n \"cli_build_use_pnpm\": \"请通过 pnpm build 运行构建,确保使用当前项目的 pnpm。\",\n \"cli_build_target_invalid\": \"target 必须为 compile、docker 或 desktop。\",\n \"cli_build_options_invalid\": \"runtime、network、sqlite-prebuild-mirror、no-http-proxy 仅用于 docker;platform、arch 仅用于 desktop。\",\n \"cli_build_version_invalid\": \"请在应用根 package.json 中设置有效的三段式版本(如 1.2.3 或 1.2.3-beta.1,最长 80 字符)。\",\n \"cli_build_native_host\": \"Tauri 需要在对应系统上构建(windows/linux/macos),架构为 x64 或 arm64;请先安装对应 Rust target、系统 SDK 和打包工具。\",\n \"cli_build_desktop_missing\": \"hai.build.json 未配置 desktop 目录。请生成 desktop 前端或添加已有的 Tauri 工程。\",\n \"cli_build_images_missing\": \"hai.build.json 必须包含一个 app 镜像和至多一个 web 镜像。\",\n \"cli_build_runtime_missing\": \"请安装并启动 Podman 或 Docker。auto 优先选择可用的 Podman。\",\n \"cli_build_mirror_invalid\": \"SQLite 镜像地址必须为 HTTP(S),不能包含凭据、查询或片段。\",\n \"cli_build_ready\": \"构建完成({runtime}):{path}。此镜像可用于不同部署环境。\",\n \"cli_build_native_ready\": \"Tauri 构建完成:{target},安装包位于对应工程 src-tauri/target/{target}/release/bundle。\",\n \"cli_deploy_bundle_provider\": \"--bundle-only 仅适用于 docker-ssh,请选择 Compose 部署目标。\",\n \"cli_deploy_bundle_local\": \"仅生成部署包,不连接服务器\",\n \"cli_deploy_bundle_ready\": \"部署包已生成;服务器尚未部署。\",\n \"cli_deploy_bundle_path\": \"部署包:{path}\"\n}\n","/** CLI 用户可见消息;通过 core 的全局 locale 选择语言。 */\nimport { core } from '@h-ai/core'\nimport enUS from '../messages/en-US.json'\nimport zhCN from '../messages/zh-CN.json'\n\nexport const cliM = core.i18n.createMessageGetter<keyof typeof zhCN>({ 'en-US': enUS, 'zh-CN': zhCN })\n","/**\n * @h-ai/cli — Skill 模板生成\n *\n * 将 Skill 模板文件复制到用户项目中,供 AI 编程助手使用。\n * @module cli-skill-templates\n */\n\nimport type { AppType } from '../cli-types.js'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport fse from 'fs-extra'\nimport Handlebars from 'handlebars'\nimport { cliM } from '../cli-i18n.js'\n\n/**\n * 模块名到 Skill 目录名的映射\n */\nconst MODULE_SKILL_MAP: Record<string, string> = {\n 'db': 'hai-reldb',\n 'reldb': 'hai-reldb',\n 'cache': 'hai-cache',\n 'iam': 'hai-iam',\n 'storage': 'hai-storage',\n 'ai': 'hai-ai',\n 'crypto': 'hai-crypto',\n 'payment': 'hai-payment',\n 'vecdb': 'hai-vecdb',\n 'datapipe': 'hai-datapipe',\n 'reach': 'hai-reach',\n 'scheduler': 'hai-scheduler',\n 'audit': 'hai-audit',\n 'deploy': 'hai-deploy',\n 'kit': 'hai-kit',\n 'ui': 'hai-ui',\n 'api-client': 'hai-api-client',\n 'capacitor': 'hai-capacitor',\n}\n\n/**\n * 各 appType 互斥的 Skill。\n *\n * 生成样板工程时,默认复制 templates/skills 下所有 hai-* Skill,\n * 仅排除与当前样板技术栈明显冲突的少数 Skill。\n */\nconst SKILL_EXCLUSIONS_BY_APP_TYPE: Partial<Record<AppType, string[]>> = {\n 'admin': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'website': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'h5': ['hai-serv', 'hai-api-contract', 'hai-api-client', 'hai-capacitor'],\n 'api': ['hai-ui', 'hai-kit', 'hai-capacitor'],\n 'mobile-app': ['hai-core', 'hai-kit', 'hai-serv', 'hai-api-contract'],\n 'fullstack': ['hai-kit'],\n}\n\nconst WORKFLOW_SKILLS = [\n 'hai-build',\n 'hai-spec-review',\n 'hai-spec-implement',\n 'hai-app-create',\n 'hai-app-review',\n 'hai-app-tests',\n 'hai-framework-sync',\n 'hai-ci',\n 'hai-pr-review',\n]\n\nconst PACKAGE_SKILL_MAP: Record<string, string> = {\n '@h-ai/api-contract': 'hai-api-contract',\n '@h-ai/api-client': 'hai-api-client',\n '@h-ai/core': 'hai-core',\n '@h-ai/serv': 'hai-serv',\n '@h-ai/kit': 'hai-kit',\n '@h-ai/ui': 'hai-ui',\n '@h-ai/reldb': 'hai-reldb',\n '@h-ai/cache': 'hai-cache',\n '@h-ai/iam': 'hai-iam',\n '@h-ai/storage': 'hai-storage',\n '@h-ai/ai': 'hai-ai',\n '@h-ai/crypto': 'hai-crypto',\n '@h-ai/payment': 'hai-payment',\n '@h-ai/vecdb': 'hai-vecdb',\n '@h-ai/datapipe': 'hai-datapipe',\n '@h-ai/reach': 'hai-reach',\n '@h-ai/scheduler': 'hai-scheduler',\n '@h-ai/audit': 'hai-audit',\n '@h-ai/deploy': 'hai-deploy',\n '@h-ai/capacitor': 'hai-capacitor',\n}\n\nconst SKILL_TARGET_DIRS = ['.agents/skills'] as const\n\nconst BRIDGE_FILES = [\n {\n source: 'copilot-instructions.md',\n destination: '.github/copilot-instructions.md',\n },\n {\n source: 'CLAUDE.md',\n destination: 'CLAUDE.md',\n },\n {\n source: 'AGENTS.md',\n destination: 'AGENTS.md',\n },\n {\n source: 'opencode.json',\n destination: 'opencode.json',\n },\n] as const\n\nconst BRIDGE_PROFILE_REGEX = /^[a-z0-9-]+$/\n\n/** 从已有模板标识与实际包布局确认桥接类型,不按项目名称猜测技术栈。 */\nasync function detectBridgeContext(projectPath: string): Promise<{ appType?: AppType, projectName?: string }> {\n const packagePath = path.join(projectPath, 'package.json')\n if (!(await fse.pathExists(packagePath))) {\n return {}\n }\n\n let metadata: unknown\n try {\n metadata = await fse.readJson(packagePath)\n }\n catch {\n return {}\n }\n if (!metadata || typeof metadata !== 'object' || !('name' in metadata)\n || typeof metadata.name !== 'string' || !BRIDGE_PROFILE_REGEX.test(metadata.name)) {\n return {}\n }\n\n const projectName = metadata.name\n for (const [appType, directory, suffixes] of [\n ['fullstack', 'packages', ['contract', 'serv', 'shared']],\n ['api', 'apps', ['contract', 'service']],\n ] as const) {\n const manifests = await Promise.all(suffixes.map(suffix => fse.pathExists(\n path.join(projectPath, directory, `${projectName}-${suffix}`, 'package.json'),\n )))\n if (manifests.every(Boolean)) {\n return { appType, projectName }\n }\n }\n\n if ('keywords' in metadata && Array.isArray(metadata.keywords) && metadata.keywords.includes('hai')) {\n for (const appType of ['admin', 'website', 'h5', 'mobile-app'] as const) {\n if (metadata.keywords.includes(appType)) {\n return { appType, projectName }\n }\n }\n }\n return { projectName }\n}\n\nasync function listTemplateSkillNames(templatesDir: string): Promise<string[]> {\n const entries = await fse.readdir(templatesDir, { withFileTypes: true })\n const skillNames: string[] = []\n\n for (const entry of entries) {\n if (!entry.isDirectory() || !entry.name.startsWith('hai-')) {\n continue\n }\n\n const skillFilePath = path.join(templatesDir, entry.name, 'SKILL.md')\n if (await fse.pathExists(skillFilePath)) {\n skillNames.push(entry.name)\n }\n }\n\n return skillNames.sort((a, b) => a.localeCompare(b))\n}\n\nexport function resolveCompatibleSkillNames(\n skillNames: string[],\n appType?: AppType,\n): string[] {\n const excludedSkills = new Set(appType ? SKILL_EXCLUSIONS_BY_APP_TYPE[appType] ?? [] : [])\n return skillNames.filter(skillName => !excludedSkills.has(skillName))\n}\n\n/**\n * 获取 templates/skills/ 目录的绝对路径\n *\n * 兼容两种运行环境:\n * - 构建后:dist/cli-skill-templates.js → ../templates/skills\n * - 测试时:src/commands/cli-skill-templates.ts → ../../templates/skills\n */\nfunction getSkillTemplatesDir(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url))\n const distPath = path.resolve(currentDir, '..', 'templates', 'skills')\n if (fse.pathExistsSync(distPath)) {\n return distPath\n }\n return path.resolve(currentDir, '..', '..', 'templates', 'skills')\n}\n\n/**\n * 复制单个 Skill 目录到项目\n *\n * @param templatesDir - Skill 模板根目录\n * @param skillName - Skill 目录名(如 'hai-reldb')\n * @param projectPath - 用户项目根目录\n */\nasync function copySkill(\n templatesDir: string,\n skillName: string,\n projectPath: string,\n targetDirs: readonly string[] = SKILL_TARGET_DIRS,\n overwrite = true,\n): Promise<string[]> {\n const src = path.join(templatesDir, skillName)\n\n if (!(await fse.pathExists(src))) {\n return []\n }\n\n const copiedFiles: string[] = []\n const resources = await fse.readdir(src, { recursive: true, withFileTypes: true })\n const relativeFiles = resources.filter(resource => resource.isFile())\n .map(resource => path.relative(src, path.join(resource.parentPath, resource.name)))\n\n for (const targetDir of targetDirs) {\n const dest = path.join(projectPath, targetDir, skillName)\n await assertProjectOutputPath(projectPath, dest)\n if (!overwrite) {\n // 已有入口不能使新参考资源被跳过;逐文件补缺,同时保留用户修改。\n const missingFiles: string[] = []\n for (const relativeFile of relativeFiles) {\n if (!(await fse.pathExists(path.join(dest, relativeFile)))) {\n missingFiles.push(relativeFile)\n }\n }\n if (missingFiles.length === 0) {\n continue\n }\n for (const relativeFile of missingFiles)\n await assertProjectOutputPath(projectPath, path.join(dest, relativeFile))\n await fse.copy(src, dest, { overwrite: false })\n copiedFiles.push(...missingFiles.map(relativeFile => `${targetDir}/${skillName}/${relativeFile.replaceAll(path.sep, '/')}`))\n }\n else {\n for (const relativeFile of relativeFiles)\n await assertProjectOutputPath(projectPath, path.join(dest, relativeFile))\n await fse.copy(src, dest, { overwrite: true })\n copiedFiles.push(`${targetDir}/${skillName}/SKILL.md`)\n }\n }\n\n return copiedFiles\n}\n\n/** 写入前解析已有目录/文件链接,所有 AI 输出均限制在当前项目内。 */\nasync function assertProjectOutputPath(projectPath: string, destination: string): Promise<void> {\n let ancestor = destination\n while (!(await fse.pathExists(ancestor))) {\n const parent = path.dirname(ancestor)\n if (parent === ancestor)\n throw new Error(cliM('cli_generateOutsideWorkspace'))\n ancestor = parent\n }\n const relativeAncestor = path.relative(await fse.realpath(projectPath), await fse.realpath(ancestor))\n if (relativeAncestor === '..' || relativeAncestor.startsWith(`..${path.sep}`) || path.isAbsolute(relativeAncestor))\n throw new Error(cliM('cli_generateOutsideWorkspace'))\n}\n\nasync function copySkills(\n templatesDir: string,\n skillNames: string[],\n projectPath: string,\n targetDirs: readonly string[] = SKILL_TARGET_DIRS,\n overwrite = true,\n): Promise<string[]> {\n const copiedFiles: string[] = []\n\n for (const skillName of new Set(skillNames)) {\n copiedFiles.push(...await copySkill(templatesDir, skillName, projectPath, targetDirs, overwrite))\n }\n\n return copiedFiles\n}\n\n/**\n * 复制桥接文件到项目\n *\n * @param templatesDir - Skill 模板根目录\n * @param projectPath - 用户项目根目录\n */\nasync function copyBridgeFiles(\n templatesDir: string,\n projectPath: string,\n overwrite = true,\n appType?: string,\n projectName?: string,\n): Promise<string[]> {\n const copiedFiles: string[] = []\n\n for (const file of BRIDGE_FILES) {\n const src = await resolveBridgeSource(templatesDir, file.source, appType, projectName !== undefined)\n const dest = path.join(projectPath, file.destination)\n\n if (!src) {\n continue\n }\n\n if (!overwrite && await fse.pathExists(dest)) {\n continue\n }\n\n await assertProjectOutputPath(projectPath, dest)\n await fse.ensureDir(path.dirname(dest))\n if (src.endsWith('.hbs')) {\n const template = await fse.readFile(src, 'utf8')\n const rendered = Handlebars.compile(template, { noEscape: true })({ projectName })\n await fse.writeFile(dest, rendered, 'utf8')\n }\n else {\n await fse.copy(src, dest, { overwrite })\n }\n copiedFiles.push(file.destination)\n }\n\n return copiedFiles\n}\n\nasync function resolveBridgeSource(\n templatesDir: string,\n source: string,\n appType?: string,\n allowTemplate = false,\n): Promise<string | null> {\n const profile = appType && BRIDGE_PROFILE_REGEX.test(appType) ? appType : 'generic'\n const candidates = [\n ...(allowTemplate ? [path.join(templatesDir, 'bridges', profile, `${source}.hbs`)] : []),\n path.join(templatesDir, 'bridges', profile, source),\n path.join(templatesDir, 'bridges', 'generic', source),\n path.join(templatesDir, source),\n ]\n\n for (const candidate of candidates) {\n if (await fse.pathExists(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * 为新项目生成完整的 Skill 文件。\n *\n * 默认复制 templates/skills 下所有 hai-* Skill,再按 appType 排除互斥项。\n *\n * @param projectPath - 用户项目根目录\n * @param _features - 预留参数;保留签名兼容 createProject 调用方\n * @param appType - 应用类型(api 类型不复制 UI Skill)\n */\nexport async function generateSkillFiles(\n projectPath: string,\n _features: string[],\n appType?: string,\n projectName?: string,\n): Promise<string[]> {\n const templatesDir = getSkillTemplatesDir()\n\n if (!(await fse.pathExists(templatesDir))) {\n return []\n }\n\n const copiedFiles: string[] = []\n\n const templateSkillNames = await listTemplateSkillNames(templatesDir)\n const compatibleSkillNames = resolveCompatibleSkillNames(templateSkillNames, appType as AppType | undefined)\n\n copiedFiles.push(...await copySkills(templatesDir, compatibleSkillNames, projectPath))\n\n // 复制桥接文件\n copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, true, appType, projectName))\n\n return copiedFiles\n}\n\n/**\n * 为已有项目添加单个模块的 Skill 文件\n *\n * 同时补全普通开发所需工作流及缺失参考资源,保留已有文件。\n *\n * @param projectPath - 用户项目根目录\n * @param moduleId - 模块标识(如 'db'、'iam')\n * @returns 复制的文件路径,若模块无对应 Skill 则返回 null\n */\nexport async function generateModuleSkillFile(\n projectPath: string,\n moduleId: string,\n): Promise<string[] | null> {\n const skillName = MODULE_SKILL_MAP[moduleId]\n if (!skillName) {\n return null\n }\n\n const copiedFiles = await generateProjectAiSupport(projectPath, [skillName], {\n overwriteSkills: false,\n overwriteBridgeFiles: false,\n })\n return copiedFiles.length > 0 ? copiedFiles : null\n}\n\nexport function resolveSkillNamesForPackages(packageNames: string[]): string[] {\n const skillNames = [...WORKFLOW_SKILLS]\n\n for (const packageName of packageNames) {\n const skillName = PACKAGE_SKILL_MAP[packageName]\n if (skillName) {\n skillNames.push(skillName)\n }\n }\n\n return [...new Set(skillNames)]\n}\n\nexport async function generateProjectAiSupport(\n projectPath: string,\n skillNames: string[],\n options: {\n overwriteSkills?: boolean\n overwriteBridgeFiles?: boolean\n appType?: string\n projectName?: string\n } = {},\n): Promise<string[]> {\n const templatesDir = getSkillTemplatesDir()\n\n if (!(await fse.pathExists(templatesDir))) {\n return []\n }\n\n const {\n overwriteSkills = true,\n overwriteBridgeFiles = true,\n } = options\n\n const detected = await detectBridgeContext(projectPath)\n const appType = options.appType ?? detected.appType\n const projectName = options.projectName ?? detected.projectName\n // 已确认的样板补全整个兼容目录;其它项目只补工作流与明确请求的模块。\n const selectedSkillNames = appType\n ? [...resolveCompatibleSkillNames(await listTemplateSkillNames(templatesDir), appType as AppType), ...WORKFLOW_SKILLS, ...skillNames]\n : [...WORKFLOW_SKILLS, ...skillNames]\n const copiedFiles: string[] = []\n copiedFiles.push(...await copySkills(templatesDir, selectedSkillNames, projectPath, SKILL_TARGET_DIRS, overwriteSkills))\n copiedFiles.push(...await copyBridgeFiles(templatesDir, projectPath, overwriteBridgeFiles, appType, projectName))\n\n return copiedFiles\n}\n"]}
|