@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.68
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 +86 -241
- package/README.ja.md +86 -241
- package/README.md +86 -241
- package/dist/{src-RDQ2EIBT.js → chunk-FI56GONY.js} +32 -2669
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-QYX6V7H2.js → chunk-ZFNBUF77.js} +347 -276
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +1304 -532
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- 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}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +33 -8
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -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 +6 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- 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 +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +30 -9
- 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 +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +32 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +40 -14
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +6 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +13 -14
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +16 -5
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +15 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +13 -5
- 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 +23 -8
- 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 +11 -3
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +5 -3
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +7 -3
- package/templates/skills/bridges/admin/AGENTS.md +6 -2
- package/templates/skills/bridges/api/AGENTS.md +3 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +5 -1
- package/templates/skills/bridges/h5/AGENTS.md +6 -2
- package/templates/skills/bridges/mobile-app/AGENTS.md +6 -2
- package/templates/skills/bridges/website/AGENTS.md +6 -2
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +15 -160
- package/templates/skills/hai-api-client/reference.md +178 -0
- package/templates/skills/hai-api-contract/SKILL.md +16 -273
- package/templates/skills/hai-api-contract/reference.md +280 -0
- package/templates/skills/hai-app-create/SKILL.md +6 -2
- package/templates/skills/hai-app-create/development.md +24 -8
- package/templates/skills/hai-app-create/reference.md +7 -5
- package/templates/skills/hai-app-review/SKILL.md +7 -3
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +6 -3
- package/templates/skills/hai-app-tests/reference.md +5 -5
- package/templates/skills/hai-app-tests/validation.md +38 -0
- package/templates/skills/hai-audit/SKILL.md +24 -147
- package/templates/skills/hai-audit/reference.md +148 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +16 -13
- package/templates/skills/hai-cache/SKILL.md +20 -181
- package/templates/skills/hai-cache/reference.md +229 -0
- package/templates/skills/hai-capacitor/SKILL.md +7 -4
- package/templates/skills/hai-capacitor/reference.md +15 -9
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +7 -3
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +22 -250
- package/templates/skills/hai-crypto/reference.md +310 -0
- package/templates/skills/hai-datapipe/SKILL.md +16 -195
- package/templates/skills/hai-datapipe/reference.md +204 -0
- package/templates/skills/hai-deploy/SKILL.md +24 -54
- package/templates/skills/hai-deploy/middleware.md +51 -43
- 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 +25 -11
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +35 -174
- package/templates/skills/hai-kit/reference.md +190 -0
- package/templates/skills/hai-payment/SKILL.md +33 -263
- package/templates/skills/hai-payment/reference.md +295 -0
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +6 -3
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +6 -3
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +18 -195
- package/templates/skills/hai-scheduler/reference.md +216 -0
- package/templates/skills/hai-serv/SKILL.md +25 -10
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +5 -1
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +5 -3
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +23 -238
- package/templates/skills/hai-storage/reference.md +282 -0
- package/templates/skills/hai-ui/SKILL.md +17 -3
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +26 -11
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +14 -10
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-FWN5B54L.js +0 -468
- 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
|
@@ -34,13 +34,15 @@
|
|
|
34
34
|
```typescript
|
|
35
35
|
import { core } from '@h-ai/core'
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// 自动加载运行时 .yml / .yaml,跳过 _deploy.yml / _deploy.yaml
|
|
38
38
|
core.init({ configDir: './config' })
|
|
39
39
|
|
|
40
40
|
// 也可指定日志级别和配置监听
|
|
41
41
|
core.init({ configDir: './config', watchConfig: true })
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
`_deploy.yml` / `_deploy.yaml` 由部署 CLI 在加载目标环境与凭据后独立解析,不参加 Core 的自动加载和监听;业务 `deploy.yml`、手动 `core.config.load` 不受影响。单项 `config.load` 返回失败时记录错误并继续,应用须 validate 必需配置并检查 success。目录扫描、日志设置等异常仍可能传播,不能把同步 void 当作所有配置加载成功的证明。
|
|
45
|
+
|
|
44
46
|
### 2. 使用功能
|
|
45
47
|
|
|
46
48
|
```typescript
|
|
@@ -60,7 +62,7 @@ const formErrors = core.zodValidation.mapZodErrorToFormErrors(zodError, getMessa
|
|
|
60
62
|
|
|
61
63
|
| 方法 | 签名 | 说明 |
|
|
62
64
|
| ----------- | --------------------------------- | ----------------------------------------- |
|
|
63
|
-
| `core.init` | `(options?: CoreOptions) => void` |
|
|
65
|
+
| `core.init` | `(options?: CoreOptions) => void` | 同步加载运行时 YAML,排除两个 `_deploy` 保留文件 |
|
|
64
66
|
|
|
65
67
|
> `CoreOptions`:`{ logging?: Partial<LoggingConfig>, configDir?: string, watchConfig?: boolean }`
|
|
66
68
|
>
|
|
@@ -150,6 +152,8 @@ core.logger.error('Failed to connect', { error })
|
|
|
150
152
|
core.logger.debug('Processing item', { id, step: 'validation' })
|
|
151
153
|
```
|
|
152
154
|
|
|
155
|
+
原生 `Error` 的 `name/message/stack/cause`、`AggregateError.errors` 与可枚举自有上下文先转成副本,再走字段/URL 脱敏与附加 `redact`;两端原异常均不变。Error 子图循环用 `[Circular]`,达到 32 层用 `[MaxDepth]`,自定义替换值也经过同一规范化;普通对象的循环与替换语义不变。自由文本不会自动解析秘密,可配置 `redact: ['error.message', 'error.stack']` 遮蔽整条诊断路径。
|
|
156
|
+
|
|
153
157
|
**管理方法**:
|
|
154
158
|
|
|
155
159
|
| 方法 | 签名 | 说明 |
|
|
@@ -195,18 +199,21 @@ const level = core.logger.getLevel() // 'debug'
|
|
|
195
199
|
| 方法 | 签名 | 说明 |
|
|
196
200
|
| --------------------- | --------------------------------------------------------------------- | -------------- |
|
|
197
201
|
| `getGlobalLocale` | `() => string` | 获取当前语言 |
|
|
198
|
-
| `setGlobalLocale` | `(locale: string) => void` |
|
|
202
|
+
| `setGlobalLocale` | `(locale: string) => void` | 浏览器语言/启动默认值;请求内只改当前上下文 |
|
|
203
|
+
| `runWithLocale` | `<T>(locale: string, callback: () => T) => T` | Node 请求/SSR 异步语言隔离 |
|
|
199
204
|
| `createMessageGetter` | `(messages: Record<string, Record<string, string>>) => MessageGetter` | 创建消息获取器 |
|
|
200
205
|
|
|
201
206
|
创建模块 i18n:
|
|
202
207
|
|
|
203
208
|
```typescript
|
|
204
209
|
import enUS from '../messages/en-US.json'
|
|
210
|
+
import jaJP from '../messages/ja-JP.json'
|
|
205
211
|
import zhCN from '../messages/zh-CN.json'
|
|
206
212
|
|
|
207
213
|
const m = core.i18n.createMessageGetter({
|
|
208
214
|
'zh-CN': zhCN,
|
|
209
215
|
'en-US': enUS,
|
|
216
|
+
'ja-JP': jaJP,
|
|
210
217
|
})
|
|
211
218
|
|
|
212
219
|
// 使用
|
|
@@ -301,7 +308,7 @@ core.id.isValidNanoId('abc123', 6) // true/false
|
|
|
301
308
|
|
|
302
309
|
- 生成 UUID **一律用 `core.id.uuid()`**,禁止裸调 `crypto.randomUUID()`。
|
|
303
310
|
- 原因:`crypto.randomUUID` 仅在**安全上下文(HTTPS / localhost)**下可用,生产 http 环境会抛
|
|
304
|
-
`crypto.randomUUID is not a function`;`core.id.uuid()`
|
|
311
|
+
`crypto.randomUUID is not a function`;`core.id.uuid()` 仅使用 randomUUID 或密码学安全的 getRandomValues;两者不可用时明确抛错,绝不降级到 Math.random。
|
|
305
312
|
- 需要不带横线的 compact 形式:`core.id.uuid().replaceAll('-', '')`。
|
|
306
313
|
- 代码评审与新建代码时以此为准,发现裸调 `crypto.randomUUID()` 一律改走 `core.id.uuid()`。
|
|
307
314
|
|
|
@@ -328,7 +335,7 @@ core.object.omit(obj, ['c', 'd'])
|
|
|
328
335
|
```typescript
|
|
329
336
|
core.string.capitalize('hello') // 'Hello'
|
|
330
337
|
core.string.kebabCase('helloWorld') // 'hello-world'
|
|
331
|
-
core.string.constantTimeEqual(a, b) //
|
|
338
|
+
core.string.constantTimeEqual(a, b) // Node 使用原生等长比较;浏览器不保证恒时
|
|
332
339
|
```
|
|
333
340
|
|
|
334
341
|
#### 数组操作 — `core.array`
|
|
@@ -533,3 +540,16 @@ logger.debug('Data fetched', { count: items.length })
|
|
|
533
540
|
| context 输出 | pretty 追加 JSON 上下文,json 合并上下文到结构化 JSON 字符串 |
|
|
534
541
|
|
|
535
542
|
云资源输出格式与部署配置消费见 [hai-deploy](../hai-deploy/SKILL.md);这里的父节点覆盖规则同样适用。
|
|
543
|
+
|
|
544
|
+
### URL 日志脱敏
|
|
545
|
+
|
|
546
|
+
使用 `core.sanitize.sanitizeUrl(url)` 统一处理连接地址;它会遮蔽用户名、密码、敏感 query 参数和 OAuth 键值 fragment。非法地址返回固定占位符。`sanitizeSensitiveFields` 对 URL 类字段使用同一规则;自定义 matcher 同样匹配参数名,不能只遮蔽 userinfo 后记录含 token 的 URL。
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
const safeUrl = core.sanitize.sanitizeUrl("https://example.com/a?token=value&limit=10")
|
|
550
|
+
// https://example.com/a?token=[REDACTED]&limit=10
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Node 请求/SSR 必须用 `core.i18n.runWithLocale(locale, callback)`(kit 同名方法)包裹实际异步 resolve;serv 自动建立请求语言上下文。浏览器 setGlobalLocale/createLocaleStore 继续管理应用语言,SSR 的 UI setter/store 不写全局值。显式 MessageOptions.locale 优先;进程默认语言仅在启动时设置。
|
|
554
|
+
|
|
555
|
+
Node 入口的 core.string.constantTimeEqual 将原始 UTF-16 单元编码后使用 node:crypto.timingSafeEqual 比较等长字节,长度不相同时直接 false,长度本身不受保护。浏览器实现仅避免按内容提前返回,不提供 JavaScript 引擎级恒时保证,不用于服务端凭证校验。外围分支、编码和请求处理不因此自动成为恒时。
|
|
@@ -1,271 +1,43 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-crypto
|
|
3
|
-
description: "
|
|
3
|
+
description: "实现 SM2/SM3/SM4 加解密、验签、密码哈希,或通过 serv/kit/api-client 接入传输加密。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-crypto
|
|
7
7
|
|
|
8
|
+
transport 仅支持 v2:SM2 发送方签名、独立密钥的 Encrypt-then-HMAC、HTTP 上下文/响应状态绑定及共享原子防重放。GET/HEAD、空 POST 与空响应也认证;无降级开关。TLS 信任边界、5 分钟窗口、nonce 持久化与实现示例见 [参考](reference.md)。
|
|
9
|
+
|
|
8
10
|
## 能力契约
|
|
9
11
|
|
|
10
12
|
| 项目 | 契约 |
|
|
11
13
|
| --- | --- |
|
|
12
14
|
| 能力 | 使用 @h-ai/crypto 进行 SM2/SM3/SM4、密码哈希与传输加密 |
|
|
13
15
|
| 适用场景 | 应用加密、验签、密码保存或传输保护 |
|
|
14
|
-
| 输入 |
|
|
16
|
+
| 输入 | 无参 init、字节/文本、密钥和 transport 装配选项 |
|
|
15
17
|
| 输出 | 加解密/验签的 HaiResult;transport 工厂按公开类型使用 |
|
|
16
|
-
| 限制 |
|
|
17
|
-
|
|
18
|
-
> `@h-ai/crypto` 提供非对称加密、哈希、对称加密与密码哈希能力,支持 Node.js 与浏览器双端。
|
|
19
|
-
|
|
20
|
-
## 运行环境
|
|
21
|
-
|
|
22
|
-
**Node.js + 浏览器双端可用。** 浏览器端通过 `crypto.init()` 初始化(无需配置参数)。
|
|
23
|
-
|
|
24
|
-
| 能力 | Node.js | 浏览器 | 说明 |
|
|
25
|
-
|------|---------|--------|------|
|
|
26
|
-
| `crypto.asymmetric` 非对称加密 | ✅ | ✅ | SM2 加解密、签名验签 |
|
|
27
|
-
| `crypto.hash` 哈希 | ✅ | ✅ | SM3 哈希、HMAC |
|
|
28
|
-
| `crypto.symmetric` 对称加密 | ✅ | ✅ | SM4 ECB/CBC |
|
|
29
|
-
| `crypto.password` 密码哈希 | ✅ | ❌ | 依赖 Node.js crypto(服务端专用) |
|
|
30
|
-
| `crypto.transport` 传输加密 | ✅ | ✅ | 服务端管理器 + 客户端 encryptedFetch |
|
|
31
|
-
|
|
32
|
-
浏览器端主要用于 kit 传输加密场景(`kit.client.create({ transport: { crypto } })`),一般不需要直接调用 crypto API。
|
|
33
|
-
|
|
34
|
-
## 适用场景
|
|
35
|
-
|
|
36
|
-
- 非对称加密/解密、签名/验签、密钥生成
|
|
37
|
-
- 哈希计算与 HMAC
|
|
38
|
-
- 对称加密/解密(ECB/CBC 模式)
|
|
39
|
-
- 密码存储与验证(加盐迭代哈希)
|
|
40
|
-
- 传输加密:为 serv / kit / api-client 提供统一协议、密钥协商和请求响应加解密
|
|
41
|
-
|
|
42
|
-
## 使用步骤
|
|
43
|
-
|
|
44
|
-
### 1. 初始化与关闭
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
import { crypto } from '@h-ai/crypto'
|
|
48
|
-
|
|
49
|
-
await crypto.init()
|
|
50
|
-
|
|
51
|
-
// 使用后关闭
|
|
52
|
-
await crypto.close()
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## 核心 API
|
|
56
|
-
|
|
57
|
-
### 非对称加密 — `crypto.asymmetric`
|
|
58
|
-
|
|
59
|
-
| 方法 | 签名 | 说明 |
|
|
60
|
-
| ------------------- | ----------------------------------------------------------- | ------------ |
|
|
61
|
-
| `generateKeyPair` | `() => HaiResult<KeyPair>` | 生成密钥对 |
|
|
62
|
-
| `encrypt` | `(data, publicKey, options?) => HaiResult<string>` | 公钥加密 |
|
|
63
|
-
| `decrypt` | `(ciphertext, privateKey, options?) => HaiResult<string>` | 私钥解密 |
|
|
64
|
-
| `sign` | `(data, privateKey, options?) => HaiResult<string>` | 私钥签名 |
|
|
65
|
-
| `verify` | `(data, signature, publicKey, options?) => HaiResult<boolean>` | 公钥验签 |
|
|
66
|
-
| `isValidPublicKey` | `(key: string) => boolean` | 校验公钥格式 |
|
|
67
|
-
| `isValidPrivateKey` | `(key: string) => boolean` | 校验私钥格式 |
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
const keyPair = crypto.asymmetric.generateKeyPair()
|
|
71
|
-
if (keyPair.success) {
|
|
72
|
-
const { publicKey, privateKey } = keyPair.data
|
|
73
|
-
|
|
74
|
-
const encrypted = crypto.asymmetric.encrypt('敏感数据', publicKey)
|
|
75
|
-
if (encrypted.success) {
|
|
76
|
-
const decrypted = crypto.asymmetric.decrypt(encrypted.data, privateKey)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const signature = crypto.asymmetric.sign('待签名数据', privateKey)
|
|
80
|
-
if (signature.success) {
|
|
81
|
-
const valid = crypto.asymmetric.verify('待签名数据', signature.data, publicKey)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**AsymmetricEncryptOptions**:`{ cipherMode?: 0 | 1, outputFormat?: 'hex' | 'base64' }`
|
|
87
|
-
**SignOptions**:`{ hash?: boolean, userId?: string }`
|
|
88
|
-
|
|
89
|
-
### 哈希 — `crypto.hash`
|
|
90
|
-
|
|
91
|
-
| 方法 | 签名 | 说明 |
|
|
92
|
-
| -------- | ---------------------------------------------------------- | -------- |
|
|
93
|
-
| `hash` | `(data: string \| Uint8Array, options?) => HaiResult<string>` | 哈希 |
|
|
94
|
-
| `hmac` | `(data, key) => HaiResult<string>` | HMAC |
|
|
95
|
-
| `verify` | `(data, expectedHash) => HaiResult<boolean>` | 验证哈希 |
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
const hash = crypto.hash.hash('hello')
|
|
99
|
-
const hmac = crypto.hash.hmac('hello', 'secret-key')
|
|
100
|
-
const valid = crypto.hash.verify('hello', hash.data!)
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**HashOptions**:`{ inputEncoding?: 'utf8' | 'hex' }`
|
|
104
|
-
|
|
105
|
-
### 对称加密 — `crypto.symmetric`
|
|
106
|
-
|
|
107
|
-
| 方法 | 签名 | 说明 |
|
|
108
|
-
| --------------- | ----------------------------------------------- | --------------------------- |
|
|
109
|
-
| `generateKey` | `() => string` | 生成 128-bit 密钥(32 hex) |
|
|
110
|
-
| `generateIV` | `() => string` | 生成随机 IV(32 hex) |
|
|
111
|
-
| `encrypt` | `(data, key, options?) => HaiResult<SymmetricEncryptedPayload>` | 加密(返回结构化密文) |
|
|
112
|
-
| `decrypt` | `(payload, key) => HaiResult<string>` | 解密结构化密文 |
|
|
113
|
-
| `encryptWithIV` | `(data, key) => HaiResult<EncryptWithIVResult>` | CBC 模式加密(自动生成 IV) |
|
|
114
|
-
| `decryptWithIV` | `(ciphertext, key, iv) => HaiResult<string>` | CBC 模式解密 |
|
|
115
|
-
| `deriveKey` | `(password, salt) => string` | 从密码和盐值派生密钥 |
|
|
116
|
-
| `isValidKey` | `(key: string) => boolean` | 校验密钥格式 |
|
|
117
|
-
| `isValidIV` | `(iv: string) => boolean` | 校验 IV 格式 |
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
const key = crypto.symmetric.generateKey()
|
|
121
|
-
|
|
122
|
-
// 默认安全用法:CBC + 自动 IV,返回 { mode, ciphertext, iv, encoding }
|
|
123
|
-
const encrypted = crypto.symmetric.encrypt('明文数据', key)
|
|
124
|
-
if (encrypted.success) {
|
|
125
|
-
const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 需要单独存储 IV 时使用结构化结果
|
|
129
|
-
const withIV = crypto.symmetric.encryptWithIV('明文数据', key)
|
|
130
|
-
if (withIV.success) {
|
|
131
|
-
const decrypted = crypto.symmetric.decryptWithIV(withIV.data.ciphertext, key, withIV.data.iv)
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**SymmetricEncryptOptions**:`{ mode?: 'ecb' | 'cbc', iv?: string, outputFormat?: 'hex' | 'base64' }`
|
|
136
|
-
**SymmetricEncryptedPayload**:`{ mode: 'ecb' | 'cbc', ciphertext: string, iv?: string, encoding: 'hex' | 'base64' }`
|
|
137
|
-
|
|
138
|
-
> `encrypt()` 默认使用 CBC 并自动生成 IV。ECB 会泄漏明文结构,只有底层协议明确要求时才显式 `{ mode: 'ecb' }`。
|
|
139
|
-
|
|
140
|
-
### 密码哈希 — `crypto.password`
|
|
141
|
-
|
|
142
|
-
| 方法 | 签名 | 说明 |
|
|
143
|
-
| -------- | --------------------------------------- | -------- |
|
|
144
|
-
| `hash` | `(password, config?) => HaiResult<string>` | 密码哈希 |
|
|
145
|
-
| `verify` | `(password, hash) => HaiResult<boolean>` | 验证密码 |
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
const hashed = crypto.password.hash('MyPassword123')
|
|
149
|
-
if (hashed.success) {
|
|
150
|
-
const valid = crypto.password.verify('MyPassword123', hashed.data)
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
哈希格式:`$hai$<iterations>$<salt>$<hash>`
|
|
155
|
-
**PasswordConfig**:`{ saltLength?: number, iterations?: number }`
|
|
156
|
-
|
|
157
|
-
### 传输加密 — `crypto.transport`
|
|
158
|
-
|
|
159
|
-
传输加密主工厂必须从 `crypto.transport` 访问;共享 `keyStore` provider 请从 `@h-ai/crypto` 根入口导入,不从子目录内部路径导入。
|
|
160
|
-
|
|
161
|
-
| 方法 | 签名 | 说明 |
|
|
162
|
-
| --- | --- | --- |
|
|
163
|
-
| `createServer` | `(options?) => HaiResult<TransportEncryptionManager>` | 创建服务端传输加密管理器 |
|
|
164
|
-
| `createClient` | `({ keyExchangeUrl, fetch? }) => TransportClient` | 创建客户端 encryptedFetch 会话 |
|
|
165
|
-
| `protocol` | `TRANSPORT_PROTOCOL` | 协议常量:`X-Client-Id`、`X-Encrypted`、默认协商路径 |
|
|
166
|
-
|
|
167
|
-
共享 `keyStore` provider(从 `@h-ai/crypto` 根入口导入):
|
|
168
|
-
|
|
169
|
-
| 工厂 | 签名 | 说明 |
|
|
170
|
-
| --- | --- | --- |
|
|
171
|
-
| `createInMemoryKeyStore` | `(maxClients?) => TransportKeyStore` | 默认 FIFO 内存实现 |
|
|
172
|
-
| `createRedisTransportKeyStore` | `({ cache, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/cache` 的共享实现,通常配 Redis |
|
|
173
|
-
| `createReldbTransportKeyStore` | `({ reldb, ttlSeconds? }) => TransportKeyStore` | 基于 `@h-ai/reldb` 的共享实现,自动建表 |
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
const server = crypto.transport.createServer({ maxClients: 10000 })
|
|
177
|
-
if (!server.success)
|
|
178
|
-
return server
|
|
179
|
-
|
|
180
|
-
const client = crypto.transport.createClient({
|
|
181
|
-
keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
const resp = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
|
|
185
|
-
method: 'POST',
|
|
186
|
-
body: JSON.stringify({ hello: 'world' }),
|
|
187
|
-
})
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
192
|
-
import { cache } from '@h-ai/cache'
|
|
193
|
-
|
|
194
|
-
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
195
|
-
|
|
196
|
-
const sharedServer = crypto.transport.createServer({
|
|
197
|
-
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
198
|
-
})
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
使用流程:
|
|
202
|
-
1. 先 `await crypto.init()`。
|
|
203
|
-
2. 服务端 `createServer()` 后暴露一个 POST 密钥协商端点:接收 `clientPublicKey`,返回 `serverPublicKey + clientId`。
|
|
204
|
-
3. 客户端 `createClient({ keyExchangeUrl })` 后,首次 `init()` / `encryptedFetch()` 会自动做密钥协商。
|
|
205
|
-
4. 协商完成后,请求附带 `X-Client-Id`;若请求有 body,则自动包装成 `{ encryptedKey, ciphertext, iv }`。
|
|
206
|
-
5. 服务端先 `decryptRequest()`,业务处理后再 `encryptResponse(clientId, data)`。
|
|
207
|
-
6. 客户端收到 `X-Encrypted: true` 的响应后自动解密;`destroy()` 可清空当前会话。
|
|
208
|
-
|
|
209
|
-
`encryptedFetch()` 与原生 fetch 一样会 reject:网络错误、密钥协商失败、请求加密失败或响应解密失败都应在调用方按 fetch 错误处理策略捕获。
|
|
210
|
-
|
|
211
|
-
常规应用优先使用上层封装:
|
|
212
|
-
- serv:`serv.createApp({ transport: { crypto } })`
|
|
213
|
-
- kit:`kit.createHandle({ crypto: { crypto, transport: true } })` + `kit.client.create({ transport: { crypto } })`
|
|
214
|
-
- api-client:`apiClient.init({ transport: { crypto } })`
|
|
215
|
-
|
|
216
|
-
## 错误码 — `HaiCryptoError`
|
|
217
|
-
|
|
218
|
-
| 错误码 | code | 说明 |
|
|
219
|
-
|--------|------|------|
|
|
220
|
-
| `HaiCryptoError.INVALID_INPUT` | `hai:crypto:002` | 无效输入 |
|
|
221
|
-
| `HaiCryptoError.INVALID_KEY` | `hai:crypto:003` | 无效密钥 |
|
|
222
|
-
| `HaiCryptoError.NOT_INITIALIZED` | `hai:crypto:010` | 未初始化 |
|
|
223
|
-
| `HaiCryptoError.INIT_FAILED` | `hai:crypto:011` | 初始化失败 |
|
|
224
|
-
| `HaiCryptoError.KEY_GENERATION_FAILED` | `hai:crypto:020` | 密钥生成失败 |
|
|
225
|
-
| `HaiCryptoError.ENCRYPTION_FAILED` | `hai:crypto:021` | 加密失败 |
|
|
226
|
-
| `HaiCryptoError.DECRYPTION_FAILED` | `hai:crypto:022` | 解密失败 |
|
|
227
|
-
| `HaiCryptoError.SIGN_FAILED` | `hai:crypto:023` | 签名失败 |
|
|
228
|
-
| `HaiCryptoError.VERIFY_FAILED` | `hai:crypto:024` | 验签失败 |
|
|
229
|
-
| `HaiCryptoError.HASH_FAILED` | `hai:crypto:040` | 哈希计算失败 |
|
|
230
|
-
| `HaiCryptoError.HMAC_FAILED` | `hai:crypto:041` | HMAC 计算失败 |
|
|
231
|
-
| `HaiCryptoError.INVALID_IV` | `hai:crypto:060` | 无效 IV |
|
|
232
|
-
|
|
233
|
-
## 常见模式
|
|
234
|
-
|
|
235
|
-
### 数据加密存储
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
const key = process.env.HAI_CRYPTO_DATA_KEY!
|
|
239
|
-
const encrypted = crypto.symmetric.encrypt(sensitiveData, key)
|
|
240
|
-
// 存入数据库
|
|
18
|
+
| 限制 | crypto 操作先 init;readTransportBody/parseTransportServerKeyRing 为无需 init 的纯工具。password 仅服务端,浏览器仅支持部分能力。传输通过 serv/kit/api-client 顶层装配,不导入内部工厂。 |
|
|
241
19
|
|
|
242
|
-
|
|
243
|
-
const decrypted = crypto.symmetric.decrypt(storedPayload, key)
|
|
244
|
-
```
|
|
20
|
+
## 使用路径
|
|
245
21
|
|
|
246
|
-
|
|
22
|
+
1. `crypto.init()` 无配置参数,检查结果;确认目标端是否支持所需能力,password 仅 Node。
|
|
23
|
+
2. 选用公开 asymmetric/hash/symmetric/password 操作,检查 HaiResult;SM3 HMAC 不等同于 SHA-256 HMAC。SM4 使用 `symmetric.generateKey()` 生成随机密钥,不提供密码 KDF;密码存储使用服务端 `password.hash()`。
|
|
24
|
+
3. 传输加密经 serv/kit/api-client 顶层配置接入,统一协商与刷新路径;不导入内部工厂。
|
|
247
25
|
|
|
248
|
-
|
|
26
|
+
握手公钥先用无需 init 的 `isValidSM2PublicKey` 验证编码和 SM2 曲线点;内置 manager/registry 也校验,serv/kit 在写存储前拒绝。DB registry 用事务共享容量(maxClients 默认 10000、各节点一致),满容量拒绝、不驱逐有效 nonce;配置有限 ttlSeconds,并由应用周期调用 DB 工厂返回的 `pruneExpired()`(每次公钥/nonce 各最多 100 条)。Redis 使用原生 TTL,容量由部署管理。TTL 显式值只能为正整数秒,详见参考中的范围和迁移边界。
|
|
249
27
|
|
|
250
|
-
|
|
251
|
-
// 注册时哈希存储
|
|
252
|
-
const hashResult = crypto.password.hash(userPassword)
|
|
253
|
-
if (hashResult.success) {
|
|
254
|
-
// 存储 hashResult.data 到数据库
|
|
255
|
-
}
|
|
28
|
+
服务端必须显式注入受管 `serverKeyRing` 与共享 `keyStore`,没有进程随机私钥或内存 registry 默认值。请求携带协商 serverKeyId;各节点同 ring + registry 支持跨节点、重启和新旧版本轮换。密钥只从 Secret/私有环境注入,不进入公开配置、客户端或日志。开发/测试的内存 registry 须显式选择。
|
|
256
29
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (verifyResult.success && verifyResult.data) {
|
|
260
|
-
// 密码匹配
|
|
261
|
-
}
|
|
262
|
-
```
|
|
30
|
+
传输字节预算统一使用根入口 `TRANSPORT_BODY_LIMITS`:握手 JSON 16 KiB、公钥 4 KiB、明文 1 MiB、密文 JSON 2 MiB + 32 KiB。自定义适配器用纯函数 `readTransportBody` 消费原流并有界读取;不要 clone/tee。客户端握手超时 30 秒;取消和超限释放 reader,异常密文响应 reject。
|
|
31
|
+
4. 验证加解密往返、错误密钥/签名和关闭,密钥与明文不写日志或浏览器公开配置。
|
|
263
32
|
|
|
264
|
-
##
|
|
33
|
+
## 按需参考
|
|
265
34
|
|
|
266
|
-
|
|
267
|
-
- `hai-iam`:密码哈希(内部自动调用 crypto.password)
|
|
268
|
-
- `hai-kit`:SvelteKit 集成(传输加密)
|
|
269
|
-
- `hai-serv` / `hai-api-client`:跨域 HTTP API 的透明传输加密
|
|
35
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
270
36
|
|
|
271
|
-
|
|
37
|
+
- [运行环境](reference.md#运行环境)
|
|
38
|
+
- [适用场景](reference.md#适用场景)
|
|
39
|
+
- [使用步骤](reference.md#使用步骤)
|
|
40
|
+
- [核心 API](reference.md#核心-api)
|
|
41
|
+
- [错误码 — `HaiCryptoError`](reference.md#错误码--haicryptoerror)
|
|
42
|
+
- [常见模式](reference.md#常见模式)
|
|
43
|
+
- [相关 Skills](reference.md#相关-skills)
|