@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.
Files changed (198) hide show
  1. package/README.en.md +86 -241
  2. package/README.ja.md +86 -241
  3. package/README.md +86 -241
  4. package/dist/{src-RDQ2EIBT.js → chunk-FI56GONY.js} +32 -2669
  5. package/dist/chunk-FI56GONY.js.map +1 -0
  6. package/dist/chunk-MBPB37FR.js +1348 -0
  7. package/dist/chunk-MBPB37FR.js.map +1 -0
  8. package/dist/{chunk-QYX6V7H2.js → chunk-ZFNBUF77.js} +347 -276
  9. package/dist/chunk-ZFNBUF77.js.map +1 -0
  10. package/dist/cli-config-templates-W2LLPMB6.js +5 -0
  11. package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
  12. package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-US5CCAJ4.js} +3 -3
  13. package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
  14. package/dist/index.js +1304 -532
  15. package/dist/index.js.map +1 -1
  16. package/dist/metafile-esm.json +1 -0
  17. package/dist/src-DSJJXM2F.js +3849 -0
  18. package/dist/src-DSJJXM2F.js.map +1 -0
  19. package/package.json +9 -8
  20. package/templates/README.md +48 -145
  21. package/templates/apps/admin/messages/ja-JP.json +38 -0
  22. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
  23. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
  24. package/templates/apps/api/.dockerignore.hbs +14 -7
  25. package/templates/apps/api/.gitignore.hbs +7 -0
  26. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
  27. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
  28. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
  29. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +33 -8
  30. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
  31. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
  32. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
  33. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
  34. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
  35. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
  36. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
  37. package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
  38. package/templates/apps/api/hai.build.json.hbs +1 -1
  39. package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
  40. package/templates/apps/api/package.json.hbs +6 -1
  41. package/templates/apps/api/playwright.config.ts.hbs +15 -7
  42. package/templates/apps/fullstack/.dockerignore.hbs +14 -9
  43. package/templates/apps/fullstack/.gitignore.hbs +7 -0
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -6
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
  46. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
  50. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +23 -5
  51. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
  52. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
  53. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
  54. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
  55. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -6
  56. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
  57. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
  58. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +30 -9
  59. package/templates/apps/fullstack/hai.build.json.hbs +2 -2
  60. package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
  61. package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
  62. package/templates/apps/fullstack/package.json.hbs +3 -1
  63. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
  64. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
  65. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
  66. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
  67. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +32 -8
  68. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
  69. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
  70. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
  71. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
  72. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
  73. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
  74. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
  75. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
  76. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
  77. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
  78. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
  79. package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
  80. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +40 -14
  81. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
  82. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
  83. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +6 -6
  84. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
  85. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +4 -4
  86. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
  87. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +13 -14
  88. package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
  89. package/templates/apps/h5/messages/ja-JP.json +40 -0
  90. package/templates/apps/h5/src/routes/+layout.svelte.hbs +16 -5
  91. package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
  92. package/templates/apps/mobile-app/src/App.svelte.hbs +15 -5
  93. package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
  94. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
  95. package/templates/apps/website/messages/ja-JP.json +32 -0
  96. package/templates/apps/website/src/routes/+layout.svelte.hbs +13 -5
  97. package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
  98. package/templates/base/.dockerignore.hbs +14 -8
  99. package/templates/base/.gitignore.hbs +7 -0
  100. package/templates/base/e2e/app.spec.ts.hbs +23 -8
  101. package/templates/base/hai.build.json.hbs +1 -1
  102. package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
  103. package/templates/base/package.json.hbs +11 -3
  104. package/templates/base/playwright.config.ts +15 -4
  105. package/templates/base/src/hooks.server.ts.hbs +15 -6
  106. package/templates/base/src/lib/server/init.ts.hbs +139 -30
  107. package/templates/base/vite.config.ts.hbs +5 -3
  108. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
  109. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
  110. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
  111. package/templates/i18n/project.inlang/settings.json +1 -1
  112. package/templates/shared/.gitattributes +6 -0
  113. package/templates/shared/e2e-env.ts +98 -0
  114. package/templates/shared/run-e2e.mjs +51 -12
  115. package/templates/skills/AGENTS.md +7 -3
  116. package/templates/skills/bridges/admin/AGENTS.md +6 -2
  117. package/templates/skills/bridges/api/AGENTS.md +3 -1
  118. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +5 -1
  119. package/templates/skills/bridges/h5/AGENTS.md +6 -2
  120. package/templates/skills/bridges/mobile-app/AGENTS.md +6 -2
  121. package/templates/skills/bridges/website/AGENTS.md +6 -2
  122. package/templates/skills/hai-ai/SKILL.md +8 -2
  123. package/templates/skills/hai-ai/guide.md +134 -60
  124. package/templates/skills/hai-ai/reference.md +51 -11
  125. package/templates/skills/hai-api-client/SKILL.md +15 -160
  126. package/templates/skills/hai-api-client/reference.md +178 -0
  127. package/templates/skills/hai-api-contract/SKILL.md +16 -273
  128. package/templates/skills/hai-api-contract/reference.md +280 -0
  129. package/templates/skills/hai-app-create/SKILL.md +6 -2
  130. package/templates/skills/hai-app-create/development.md +24 -8
  131. package/templates/skills/hai-app-create/reference.md +7 -5
  132. package/templates/skills/hai-app-review/SKILL.md +7 -3
  133. package/templates/skills/hai-app-review/differential.md +34 -0
  134. package/templates/skills/hai-app-tests/SKILL.md +6 -3
  135. package/templates/skills/hai-app-tests/reference.md +5 -5
  136. package/templates/skills/hai-app-tests/validation.md +38 -0
  137. package/templates/skills/hai-audit/SKILL.md +24 -147
  138. package/templates/skills/hai-audit/reference.md +148 -0
  139. package/templates/skills/hai-build/SKILL.md +14 -8
  140. package/templates/skills/hai-build/reference.md +16 -13
  141. package/templates/skills/hai-cache/SKILL.md +20 -181
  142. package/templates/skills/hai-cache/reference.md +229 -0
  143. package/templates/skills/hai-capacitor/SKILL.md +7 -4
  144. package/templates/skills/hai-capacitor/reference.md +15 -9
  145. package/templates/skills/hai-ci/SKILL.md +2 -0
  146. package/templates/skills/hai-core/SKILL.md +7 -3
  147. package/templates/skills/hai-core/reference.md +25 -5
  148. package/templates/skills/hai-crypto/SKILL.md +22 -250
  149. package/templates/skills/hai-crypto/reference.md +310 -0
  150. package/templates/skills/hai-datapipe/SKILL.md +16 -195
  151. package/templates/skills/hai-datapipe/reference.md +204 -0
  152. package/templates/skills/hai-deploy/SKILL.md +24 -54
  153. package/templates/skills/hai-deploy/middleware.md +51 -43
  154. package/templates/skills/hai-framework-sync/SKILL.md +2 -2
  155. package/templates/skills/hai-framework-sync/sync.md +1 -1
  156. package/templates/skills/hai-iam/SKILL.md +25 -11
  157. package/templates/skills/hai-iam/reference.md +181 -154
  158. package/templates/skills/hai-kit/SKILL.md +35 -174
  159. package/templates/skills/hai-kit/reference.md +190 -0
  160. package/templates/skills/hai-payment/SKILL.md +33 -263
  161. package/templates/skills/hai-payment/reference.md +295 -0
  162. package/templates/skills/hai-pr-review/SKILL.md +21 -88
  163. package/templates/skills/hai-reach/SKILL.md +6 -3
  164. package/templates/skills/hai-reach/reference.md +83 -22
  165. package/templates/skills/hai-reldb/SKILL.md +6 -3
  166. package/templates/skills/hai-reldb/reference.md +68 -25
  167. package/templates/skills/hai-scheduler/SKILL.md +18 -195
  168. package/templates/skills/hai-scheduler/reference.md +216 -0
  169. package/templates/skills/hai-serv/SKILL.md +25 -10
  170. package/templates/skills/hai-serv/reference.md +123 -87
  171. package/templates/skills/hai-spec-implement/SKILL.md +5 -1
  172. package/templates/skills/hai-spec-implement/evaluation.md +18 -0
  173. package/templates/skills/hai-spec-implement/handoff.md +47 -0
  174. package/templates/skills/hai-spec-implement/templates.md +5 -0
  175. package/templates/skills/hai-spec-implement/workflow.md +5 -3
  176. package/templates/skills/hai-spec-review/SKILL.md +4 -0
  177. package/templates/skills/hai-storage/SKILL.md +23 -238
  178. package/templates/skills/hai-storage/reference.md +282 -0
  179. package/templates/skills/hai-ui/SKILL.md +17 -3
  180. package/templates/skills/hai-ui/guide.md +3 -3
  181. package/templates/skills/hai-ui/reference.md +26 -11
  182. package/templates/skills/hai-ui-design/SKILL.md +31 -0
  183. package/templates/skills/hai-ui-design/patterns.md +26 -0
  184. package/templates/skills/hai-ui-review/SKILL.md +30 -0
  185. package/templates/skills/hai-ui-review/checklist.md +22 -0
  186. package/templates/skills/hai-vecdb/SKILL.md +14 -10
  187. package/templates/skills/hai-vecdb/reference.md +33 -3
  188. package/dist/chunk-FWN5B54L.js +0 -468
  189. package/dist/chunk-FWN5B54L.js.map +0 -1
  190. package/dist/chunk-QYX6V7H2.js.map +0 -1
  191. package/dist/cli-config-templates-3XSFIXBP.js +0 -5
  192. package/dist/src-RDQ2EIBT.js.map +0 -1
  193. package/templates/apps/api/Dockerfile.hbs +0 -35
  194. package/templates/apps/fullstack/Dockerfile.hbs +0 -57
  195. /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
  196. /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
  197. /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
  198. /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
@@ -0,0 +1,204 @@
1
+ # hai-datapipe 参考
2
+
3
+ > `@h-ai/datapipe` 提供文本清洗(clean)、多模式分块(chunk)和可组合管线(pipeline),纯函数模块,无需初始化。
4
+
5
+ ## 运行环境
6
+
7
+ > **服务端模块(Node.js only)。** 通常在 AI 知识库入库或文档预处理场景中使用,由 `@h-ai/ai` 内部调用或在服务端显式调用。
8
+
9
+ ## 适用场景
10
+
11
+ - 文本清洗:移除 HTML 标签、URL、Email,标准化空白
12
+ - 文本分块:句子、段落、Markdown 标题、字数、字符、自定义分隔符
13
+ - RAG 入库前的文档预处理管线
14
+ - 管线模式组合多步清洗 + 分块 + 自定义转换
15
+
16
+ ## 使用步骤
17
+
18
+ ### 直接调用(无需 init)
19
+
20
+ ```typescript
21
+ import { datapipe } from '@h-ai/datapipe'
22
+
23
+ // 清洗
24
+ const cleaned = datapipe.clean(htmlText, { removeHtml: true, removeUrls: true })
25
+
26
+ // 分块
27
+ if (!cleaned.success) return cleaned
28
+ const chunks = datapipe.chunk(cleaned.data, { mode: 'markdown', maxSize: 2000, overlap: 200 })
29
+ ```
30
+
31
+ ### 管线模式
32
+
33
+ ```typescript
34
+ const result = await datapipe.pipeline()
35
+ .clean({ removeHtml: true, removeUrls: true })
36
+ .transform(text => text.toLowerCase())
37
+ .chunk({ mode: 'paragraph', maxSize: 1000, overlap: 100 })
38
+ .chunkTransform(chunks => chunks.filter(c => c.content.length > 50))
39
+ .run(rawText)
40
+
41
+ if (result.success) {
42
+ // result.data => { text: string, chunks: DataChunk[] }
43
+ }
44
+ ```
45
+
46
+ ## 核心 API
47
+
48
+ ### 清洗 — `datapipe.clean`
49
+
50
+ ```text
51
+ datapipe.clean(text: string, options?: CleanOptionsInput): HaiResult<string>
52
+ ```
53
+
54
+ **CleanOptionsInput**:
55
+
56
+ | 字段 | 类型 | 默认 | 说明 |
57
+ | -------------------- | ------------------ | ----- | ------------------------ |
58
+ | `removeHtml` | `boolean` | true | 移除 HTML 标签 |
59
+ | `removeUrls` | `boolean` | false | 移除 URL |
60
+ | `removeEmails` | `boolean` | false | 移除 Email 地址 |
61
+ | `normalizeWhitespace`| `boolean` | true | 多空格→单空格、去多余空行 |
62
+ | `trim` | `boolean` | true | 去除首尾空白 |
63
+ | `customReplacements` | `{ pattern, replacement }[]` | — | 自定义正则替换规则 |
64
+
65
+ ### 分块 — `datapipe.chunk`
66
+
67
+ ```text
68
+ datapipe.chunk(text: string, options: ChunkOptionsInput): HaiResult<DataChunk[]>
69
+ ```
70
+
71
+ **ChunkOptionsInput**:
72
+
73
+ | 字段 | 类型 | 默认 | 说明 |
74
+ | ------------------ | --------- | ---- | ------------------------------ |
75
+ | `mode` | `ChunkMode` | — | 分块模式(必填) |
76
+ | `maxSize` | `number` | 1500 | 分块最大大小 |
77
+ | `overlap` | `number` | 0 | 重叠大小(上下文衔接) |
78
+ | `separator` | `string` | — | 自定义分隔符正则(mode=custom)|
79
+ | `markdownMinLevel` | `number` | 2 | Markdown 最低标题级别(1-6) |
80
+ | `markdownKeepTitle`| `boolean` | true | 分块中保留 Markdown 标题 |
81
+
82
+ **分块模式**:
83
+
84
+ | 模式 | 说明 |
85
+ | ----------- | --------------------------- |
86
+ | `sentence` | 按句子(中英文句号/问号/感叹号) |
87
+ | `paragraph` | 按段落(双换行) |
88
+ | `markdown` | 按 Markdown 标题层级 |
89
+ | `page` | 按分页符(\f) |
90
+ | `word` | 按字数 |
91
+ | `character` | 按字符数 |
92
+ | `custom` | 自定义正则分隔符 |
93
+
94
+ **DataChunk**:
95
+
96
+ ```typescript
97
+ interface DataChunk {
98
+ index: number // 分块索引(从 0 开始)
99
+ content: string // 分块内容
100
+ metadata?: Record<string, unknown> // 元数据(Markdown 模式含 title、level)
101
+ }
102
+ ```
103
+
104
+ ### 管线 — `datapipe.pipeline()`
105
+
106
+ ```typescript
107
+ datapipe.pipeline()
108
+ .clean(options) // 添加清洗步骤
109
+ .transform(fn) // 添加文本转换步骤(同步/异步)
110
+ .chunk(options) // 添加分块步骤
111
+ .chunkTransform(fn) // 添加分块后处理步骤
112
+ .run(text) // 执行管线 → Promise<HaiResult<PipelineResult>>
113
+ ```
114
+
115
+ **PipelineResult**:
116
+
117
+ ```typescript
118
+ interface PipelineResult {
119
+ text: string // 处理后的文本(分块前的最终文本)
120
+ chunks: DataChunk[] // 分块列表(如果管线包含 chunk 步骤)
121
+ }
122
+ ```
123
+
124
+ ## 错误码 — `HaiDatapipeError`
125
+
126
+ | 错误码 | code | 说明 |
127
+ |--------|------|------|
128
+ | `HaiDatapipeError.CLEAN_FAILED` | `hai:datapipe:001` | 清洗失败 |
129
+ | `HaiDatapipeError.CHUNK_FAILED` | `hai:datapipe:002` | 分块失败 |
130
+ | `HaiDatapipeError.TRANSFORM_FAILED` | `hai:datapipe:003` | 转换失败 |
131
+ | `HaiDatapipeError.PIPELINE_FAILED` | `hai:datapipe:004` | 管线执行失败 |
132
+ | `HaiDatapipeError.CONFIG_ERROR` | `hai:datapipe:005` | 配置错误 |
133
+ | `HaiDatapipeError.MISSING_SEPARATOR` | `hai:datapipe:006` | 自定义分隔符缺失 |
134
+
135
+ ## 常见模式
136
+
137
+ ### RAG 文档入库前处理
138
+
139
+ ```typescript
140
+ import { datapipe } from '@h-ai/datapipe'
141
+ import { ai } from '@h-ai/ai'
142
+ import { vecdb } from '@h-ai/vecdb'
143
+
144
+ // 清洗 + Markdown 分块
145
+ const result = await datapipe.pipeline()
146
+ .clean({ removeHtml: true, removeUrls: true })
147
+ .chunk({ mode: 'markdown', maxSize: 1000, overlap: 100 })
148
+ .run(rawDocument)
149
+
150
+ if (!result.success) return result
151
+
152
+ // 生成嵌入
153
+ const embeddings = await ai.embedding.embedBatch(
154
+ result.data.chunks.map(c => c.content),
155
+ )
156
+ if (!embeddings.success) return embeddings
157
+
158
+ // 入库
159
+ const docs = result.data.chunks.map((chunk, i) => ({
160
+ id: `chunk-${i}`,
161
+ vector: embeddings.data[i],
162
+ content: chunk.content,
163
+ metadata: chunk.metadata,
164
+ }))
165
+ await vecdb.vector.insert('knowledge', docs)
166
+ ```
167
+
168
+ ### 多步清洗管线
169
+
170
+ ```typescript
171
+ const result = await datapipe.pipeline()
172
+ .clean({ removeHtml: true, removeEmails: true })
173
+ .transform(text => text.replace(/\[.*?\]/g, '')) // 移除 [注释]
174
+ .transform(text => text.replace(/\(https?:.*?\)/g, '')) // 移除 Markdown 链接 URL
175
+ .chunk({ mode: 'paragraph', maxSize: 800 })
176
+ .chunkTransform(chunks => chunks.filter(c => c.content.length > 20)) // 过滤过短分块
177
+ .run(rawText)
178
+ ```
179
+
180
+ ### 错误分支处理
181
+
182
+ ```typescript
183
+ import { datapipe, HaiDatapipeError } from '@h-ai/datapipe'
184
+
185
+ const result = datapipe.chunk(text, { mode: 'custom' })
186
+ if (!result.success) {
187
+ switch (result.error.code) {
188
+ case HaiDatapipeError.MISSING_SEPARATOR.code:
189
+ // mode='custom' 时需提供 separator
190
+ break
191
+ case HaiDatapipeError.CONFIG_ERROR.code:
192
+ // 配置参数校验失败
193
+ break
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## 相关 Skills
199
+
200
+ - `hai-vecdb`:向量数据库存储(先生成 embedding,再入库)
201
+ - `hai-ai`:LLM 与 Embedding 能力
202
+ - `hai-core`:HaiResult 模型
203
+
204
+ 分块 `maxSize` 默认 1500,`overlap` 默认 0 且必须满足 `0 <= overlap < maxSize`。word 模式两者均按非空白词计量(连续中文可视为一个长词),其余模式均按 Unicode code point 计量,不是 UTF-16 length、字节或模型 token。上限包含重叠与分隔符;自然句/段/页/自定义边界只影响优先切点,超长段统一按 `maxSize-overlap` 滑窗。保留原始空白、标点、分页符及自定义分隔符,移除重叠后可还原全文;不再 split/join 或 trim 分块。Markdown section 之间不重叠,section 内使用相同硬上限,代码围栏内的标题保留为正文;仅 `markdownKeepTitle=false` 明确移除标题行。空/全空白的整体输入返回空数组。自定义正则须由可信配置提供,非法表达式返回 CHUNK_FAILED。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-deploy
3
- description: "部署已有构建产物:配置环境与中间件,生成 Compose tar 并通过 SSH 或手工搬运发布;也支持 Vercel。"
3
+ description: "构建并发布或交付已有镜像:完整离线包、按服务更新、SSH 与跨平台手工部署、失败恢复;也支持 Vercel prebuilt。"
4
4
  ---
5
5
 
6
6
  # hai-deploy
@@ -9,76 +9,46 @@ description: "部署已有构建产物:配置环境与中间件,生成 Compo
9
9
 
10
10
  | 项目 | 契约 |
11
11
  | --- | --- |
12
- | 能力 | 消费已有镜像或平台产物,配置环境、中间件并部署 |
13
- | 适用场景 | SSH 自动部署、离线手工交付、Vercel 发布和部署故障排查 |
14
- | 输入 | 完整构建产物、pnpm-lock.yaml、目标环境文件、部署配置和私有凭据引用 |
15
- | 输出 | Compose tar 与手工说明,或已检查的在线部署结果 |
16
- | 限制 | deploy 不编译/构建,不提供 skip-build 兼容路径;收费开通/发布/删除须在用户授权范围内。packaged 不代表 ready。 |
12
+ | 能力 | 配置环境与中间件,交付完整离线包或发布选中服务 |
13
+ | 适用场景 | 一体化构建发布、独立打包、隔离网络手工部署、增量更新与故障排查 |
14
+ | 输入 | package.json、hai.build.json、运行依赖锁文件、目标环境、已有制品或明确构建要求 |
15
+ | 输出 | 带校验和的 tar、跨平台部署入口与分步结果,或经过 HTTP 检查的部署结果 |
16
+ | 限制 | SDK 只消费产物;CLI --build 显式先构建。首次安装必须完整交付,packaged 不代表服务器 ready。云资源需要外网。 |
17
17
 
18
18
  ## 操作流程
19
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 清理。
20
+ 1. 读取项目 scripts、images/ 中的 Dockerfile、hai.build.json 与服务目录配置。创建默认带开发期部署工具;用户明确 --no-deploy 时不生成部署入口。构建细节见 [hai-build](../hai-build/SKILL.md)。
21
+ 2. 选择环境,验证 .env.<mode> 存在,再处理部署目标与必要中间件。直接部署缺少 SSH 参数时按人类可读字段说明补齐,已有完整配置直接复用;--bundle-only 不询问 SSH。私有参数写 .env.local,私钥仅引用路径。环境规则见 [middleware.md](middleware.md)。
22
+ 3. 先校验所选制品、版本、服务和 SHA-256。--build 只构建所选应用镜像;不带它时缺产物明确失败,不隐式补构建。格式版本 3 记录 appVersion、buildId、partial、hasWeb、镜像归档及显式配置的 appContainerPort/webContainerPort;部分构建也保留未选镜像端口,缺省使用部署默认值。同一应用版本可重建,镜像引用区分实际内容;禁止 latest 与无版本镜像。
23
+ 4. 完整包自动导出实际自建中间件镜像并去重;只更新 Web 不重新打包 API/中间件。打包机需容器引擎导出中间件,服务器仅 load,禁止 pull/build。外部连接及云开通不能伪称为离线自包含服务。
24
+ 5. --services 使用目标服务器最后成功状态为基线,检查端口/拓扑及未选依赖,保留未选配置、凭据、网络和卷。脚本先按项目/服务标签仅删除选中旧容器(保留卷),再 up -d --no-deps --no-build --pull never --no-recreate 创建新容器并保留其他容器,避免 Compose Provider 连带重建反向依赖,禁止整个项目 down。storage-init 是一次性创建 S3 桶,涉及存储初始化才运行。仅更新 storage 也需含 app 镜像的制品运行初始化;最近为 web-only 时,用 --artifact-dir 选择原完整制品,或先 pnpm build --target docker --images app,不会更新运行中的 app 服务。
25
+ 6. 部署缓存、tar 和 -deploy/ 操作目录默认位于工程根 .hai/deploy/<环境>/,与 .hai/build/ 使用同一工程根;--output 显式覆盖 tar 路径,相对命令执行目录解析。--bundle-only 不连接 SSH,生成带逐项说明的 deploy-target.json。执行上传脚本前先按说明修改并核对该 JSON,Windows deploy.ps1、Linux/macOS deploy.sh 均读取此文件,上传并执行包内 apply.sh。完全隔离时搬运 tar,独立目录解包执行 sh apply.sh,可传部署根目录。脚本不依赖源码、Node 或 pnpm;本机运行 deploy.sh 需 sh、awk、ssh、scp。
26
+ 7. 工具预检、基线、上传、校验、镜像导入、服务更新和 HTTP 检查分别报告。失败返回非零退出码,结果文件保留失败阶段;只有检查成功才更新服务器 current。已有部署失败尝试恢复所选服务的旧容器和配置;数据卷保留,已写业务数据不回退,恢复后仍需验证健康。禁止把本地打包记录当服务器成功状态。
27
+ 8. 部署及恢复全程用 Linux flock 持有固定 `.deploy.lock`;不得删除/替换文件或按时间抢锁。父进程强杀后,继承 FD 的活跃子命令仍持锁;所有持有进程退出或重启后自动释放。旧 `.deploy-lock` 目录保持原样,升级前结束旧版部署进程。强杀/断电不执行恢复 trap,缺结果文件须人工核对容器、镜像和 current 后用新包恢复,锁释放不代表副作用已回退。
27
28
 
28
29
  ## CLI
29
30
 
30
- 使用 `pnpm run deploy` 明确执行项目脚本,避免与 pnpm 自身的 deploy 命令混淆。
31
-
32
31
  ```sh
32
+ pnpm run deploy --env prod --build
33
33
  pnpm build --target docker
34
- pnpm run deploy --env test
35
- pnpm run deploy --env prod --skip-middleware
36
34
  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 .
40
- ```
41
-
42
- API workspace 的服务目录为 apps/my-app-service。单包使用当前目录。Node/pnpm 版本以项目 engines/packageManager 为准,Windows 用 pnpm.cmd。
43
-
44
- ```yaml
45
- # 服务包 config/_deploy.yml
46
- provider:
47
- type: docker-ssh
48
- expose:
49
- hostPort: 18080
50
- container:
51
- s3Engine: rustfs # rustfs | minio
35
+ pnpm run deploy --env prod --bundle-only
36
+ pnpm build --target docker --images web
37
+ pnpm run deploy --env prod --services web
52
38
  ```
53
39
 
54
- SSH/平台参数可在向导输入后存入 `.env.local` 的 `HAI_DEPLOY_PROVIDER` 整体 JSON;私钥只存路径,不记录内容。仅打包可省 SSH;在线部署需要 ssh/scp、主机信任和运行权限。完整字段、可选云 Provisioner 及中间件配置见 [middleware.md](middleware.md)。
40
+ 使用 pnpm run deploy,避免与 pnpm 自带 deploy 混淆。fullstack 指向 packages/<项目>-serv,API workspace 指向 apps/<项目>-service;这是扫描入口,不是镜像选择。--services 负责选择;--skip-middleware 只跳过配置及开通。--yes 复用有效配置,缺值时不盲目继续。
55
41
 
56
42
  ## SDK
57
43
 
58
- ```ts
59
- import { deploy } from '@h-ai/deploy'
44
+ deploy.init 后检查 HaiResult.success,deploy.deployApp 接收 artifactDir、bundleOnly、selectedServices、runtime 与 onProgress;finally 中 deploy.close。deploy.scan 和 deploy.credentials.* 不依赖 init。Vercel 只消费已有 .vercel/output,不执行源码构建,也不支持 Compose 离线/部分更新。
60
45
 
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。
70
- }
71
- finally {
72
- await deploy.close()
73
- }
74
- ```
46
+ 内部命令工具须显式枚举 `secrets`(包括转换后的敏感字面量),成功/失败/回调均跨块脱敏;不得向日志附加原始异常 cause。捕获默认每流末尾 1 Mi 字符,结构化消费先检查截断标志;异步输出回调按序等待并保持 pipe 背压。进度每行最多解析 512 字符,超时后确认远程执行状态。详细边界见 [middleware.md](middleware.md#命令输出)。
75
47
 
76
- `deploy.scan`、`deploy.credentials.getPath/load/save/saveAll` 不依赖 init。Vercel 使用 prebuilt 协议只上传 Build Output API v3 的 .vercel/output,不运行 build 命令;云认证和开通需要外网。
48
+ 超时或输入/输出失败会终止本地受管理 Job/进程组并等待核验;Windows 用系统 PowerShell/.NET 内嵌 Job,先挂起创建、分配成功才恢复,失败不降级裸进程;POSIX 后代须保留继承的进程组。`terminationTimeoutMs` 默认 10000、最大 60000 毫秒,核验失败返回 unknown。失败分支用 `getDeployCommandState(error)` 读取 localTermination 和 remoteExecution;CLI 分别显示。本地已退出不能证明 SSH 远端停止;仅新连接读到完整、ID 匹配的原子结果才确认 finished,给出结果路径/退出码。未知状态不自动重跑发布或回滚,先核对 current、步骤和实际服务。
77
49
 
78
50
  ## 离线交付与验证
79
51
 
80
- 部署器不需要本地容器引擎;提前从构建机复制完整 `.hai/build/docker/<buildId>`。Linux 准备 Docker/Podman、compose、sh、tar、sha256sum、所需中间件镜像的准确标签及数据目录权限。手工包含私有配置,权限受限,不提交 Git。启动脚本校验文件、加载 app/web、检查镜像存在并导出环境标识;缺镜像时停止,不联网拉取。
81
-
82
- 完全离线构建另需基础镜像、Linux 依赖缓存、pnpm 和匹配 ABI/libc 的 SQLite 预编译包;Windows node_modules 不能替代。生产 HTTPS、CORS、证书和 DNS 另行配置。实际断网后验收登录、刷新、重启与持久化,不能将联网测试当作离线证明。
52
+ Linux 预备 Docker/Podman、支持 --pull never 的 Compose(Docker Compose v2 或 podman-compose 1.6.0 及以上)、sh、tar、sha256sum、awk、curl、util-linux flock 及目录/容器权限,部署目录文件系统须支持 flock。完整 tar 包含全部自建中间件归档;目标架构须匹配。包中有私有配置,安全搬运,不提交 Git。离线构建额外准备基础镜像、Linux 依赖缓存、工具链与原生预编译包;Windows node_modules 不能替代。
83
53
 
84
- 改动后执行 typecheck、lint、build、test,模板执行 scaffold gates,部署流程执行 test:deploy-gates;真实 Tauri 安装包按系统单独验证。所有测试临时工程、镜像、容器、辅助进程、截图、日志和报告必须在 finally/teardown 自动清理,失败/取消同样清理。不得把验收产物提交 Git,也不得清理用户既有资源。
54
+ 按 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集适用的 typecheck、lint、build、test、scaffold gates 和 test:deploy-gates,汇总后集中修复、再验证。实际验证无 registry 网络的首次部署、登录刷新、同版本重发、只更新 Web/API、损坏归档、缺基线、健康失败与恢复。Windows/Linux/macOS 上传入口分别执行验收;原生安装包在对应 OS 验证。测试临时工程、容器、镜像、数据和进程在 finally/teardown 清理,只清理测试拥有的资源;日志、截图和报告保留到分析和交付结束再清理。
@@ -4,85 +4,93 @@
4
4
 
5
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
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` 猜测部署目标。
7
+ 环境文件遵循项目的六文件规范:`.env.example` 为带说明/枚举/范围的完整清单,`.env` 为公共基础,`.env.dev/test/prod` 为公共环境覆盖,以上五类提交 Git;`.env.local` 为忽略 Git 的本机私有覆盖。加载顺序为进程 > `.env.local` > 目标环境 > `.env`。真实密码、API Key、含认证信息的连接 URL 写 `.env.local`,公共后端和地址写目标环境文件;CLI 仅报告保存结果,不打印环境变量名或值。调用 deploy API 时显式传 `{ environment: 'test', envDir: '/workspace/app' }`,不要用调用者的 `NODE_ENV` 猜测部署目标。
8
8
 
9
9
  ## 识别与逐项选择
10
10
 
11
+ 数据库连接保存前复用所选后端的公开配置 Schema;MySQL `ssl` 仅 `false` 或严格对象,`{}` 默认验证证书链和 DNS 身份,`ca/cert/key` 为 PEM 内容且 `cert/key` 必须成对;私钥仅进入私有环境。PostgreSQL 保留其布尔值/字符串模式/对象契约。CLI 内联 `@h-ai/reldb/schemas` 与 Zod,无需为配置校验给目标应用额外安装数据库包或驱动;部署本身仍需已安装的 `@h-ai/deploy`,实际运行应用仍需其数据库依赖。
12
+
11
13
  先用 `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
14
 
13
15
  `ScanResult.haiModules` 列出所有实际运行依赖中的 Hai 模块;`ScanResult.middlewares` 提供服务、后端、来源、配置路径和变量前缀;连接配置可能含凭据,不能整体记入日志。`DEPLOY_MIDDLEWARE` 是后端和连接字段的公共目录,字段名称、类型与约束须与模块 Schema 同步。
14
16
 
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 → 阿里云凭据接入 |
17
+ | 来源 | 后端选项 | 默认 Compose 行为 | 云自动开通 |
18
+ | ------------------------------------------ | ------------------------------------- | ------------------------------------------------------------- | ----------------------------------- |
19
+ | reldb;IAM/audit;启用 DB 日志的 scheduler | SQLite / MySQL / PostgreSQL | SQLite 数据卷;mysql / postgres 服务 | PostgreSQL → Neon |
20
+ | cache;IAM;scheduler 可选分布式锁 | memory / Redis | memory 无服务;redis 服务 | Redis → Upstash |
21
+ | storage | local / S3 | 本地数据卷;RustFS(默认)/MinIO + bucket 初始化 | S3 → Cloudflare R2 |
22
+ | vecdb | LanceDB / pgvector / Qdrant / Chroma | 本地卷或对应容器 | 自定义已有服务 |
23
+ | IAM 可选目录认证 | disabled / LDAP | 禁用无服务;OpenLDAP | 自定义已有目录 |
24
+ | Reach Provider 数组 | console / SMTP / HTTP API;阿里云 SMS | console 无服务;SMTP 用 Mailpit 捕获测试邮件;HTTP/SMS 自定义 | SMTP → Resend;SMS → 阿里云凭据接入 |
23
25
 
24
- `serv` 声明 AI peer 不代表应用启用了 AI,不应因此安装 AI/ONNX/CUDA 或开通向量服务。LDAP 需要在应用安装 IAM 的可选 peer `ldapts`,并显式设置 `login.ldap=true`;安装 IAM 不代表启用了 LDAP。Reach 保留 Provider 的数组索引及名称,不能把 HTTP 回调当作 SMTP。默认目录没有业务账号;Mailpit 不会对外投递生产邮件。生产应配置企业 LDAP/SMTP,或使用对应云服务。
26
+ `serv` 声明 AI peer 不代表应用启用了 AI,不应因此安装 AI/ONNX/CUDA 或开通向量服务。LDAP 需要在应用安装 IAM 的可选 peer `ldapts`,并显式设置 `login.ldap=true`;安装 IAM 不代表启用了 LDAP。Reach 每个 Provider 保留独立数组索引及名称;通用 Console/HTTP API 按应用模板约定将 `name: sms` 归为短信,其他名称归为邮件服务,SMTP 和 aliyun-sms 按实际 type 分类,不能把 HTTP 回调当作 SMTP。email/sms 的 Console 均只输出日志,不实际投递。默认目录没有业务账号;Mailpit 不会对外投递生产邮件。生产应配置企业 LDAP/SMTP,或使用对应云服务。
25
27
 
26
- 每项都展示当前生效后端和用途,例如 `HAI_DB_TYPE=postgresql` 显示 PostgreSQL,再选择:
28
+ 每项都展示当前生效后端和用途,例如 PostgreSQL,再选择:
27
29
 
28
30
  1. **使用默认配置**:选择后端;Compose 生成匹配容器/卷,云平台调用已支持的 Provisioner,并保存实际连接配置。MySQL、LDAP、向量库等没有云自动开通器时,明确提示改用自定义连接;不能声称已经创建云资源。Resend 需要已验证的发件域名,R2 需要已有 S3 访问密钥,阿里云短信需要签名/模板与额度。
29
- 2. **使用自定义配置**:逐项输入所选后端的连接字段,展示变量名、字段用途与约束,校验必填项、端口、布尔和 URL;凭据用密码输入。保存配置后跳过该服务的开通与 sidecar。
31
+ 2. **使用自定义配置**:逐项输入所选后端的连接字段,展示字段用途与约束,校验必填项、端口、布尔和 URL;凭据用密码输入。保存配置后跳过该服务的开通与 sidecar。
30
32
  3. **保持不变**:仅在目标 `.env.<mode>` 显式声明当前后端及完整连接配置时出现;私有凭据可来自 `.env.local`/进程。选择后不修改该项、不认证其 Provisioner、不创建或移除已有中间件容器,继续下一项。只有 `.env` 或其他环境有配置不满足此条件。
31
33
 
32
34
  逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar 创建,保留已有服务;仍校验已有构建产物并执行部署及健康检查。命令行使用唯一的 `--skip-middleware`,SDK 使用 `skipMiddleware`。不要因为缺少某项中间件的目标文件声明而阻止用户主动跳过。
33
35
 
34
36
  `--yes` 用于已授权的自动化:目标环境已有完整配置时保持不变,否则使用当前后端默认配置;不支持的云后端或缺少必需凭据直接失败。自动化直接部署用 `hai deploy packages/my-app-serv --env prod --yes --skip-middleware`。跳过不作为配置完整性或连接成功的证明。
35
37
 
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 })` 可显式保存本机参数。禁止读取、复制或打印私钥内容。
38
+ 直接部署的目标不完整时,先引导选择 SSH/Vercel。SSH 逐项输入主机、端口、用户名、私钥文件路径、known_hosts 路径、远程目录/运行时及容器/主机端口;端口范围 1–65535。密钥路径留空使用 ssh-agent/默认密钥,known_hosts 留空使用 OpenSSH 默认文件。完整目标自动复用;修改 `.env.local` 的 `HAI_DEPLOY_PROVIDER` 可切换连接。首次填写的整个目标对象(包括地址、端口、路径、Token)只保存 `.env.local` 并添加 Git 忽略,不把机器信息写公共环境文件。`--bundle-only` 不询问 SSH,连接信息可在后续上传前填写到生成的 `deploy-target.json`。SDK 使用 `saveDeploymentEnvironment(snapshot, variables, { private: true })` 可显式保存本机参数。禁止读取、复制或打印私钥内容。
37
39
 
38
40
  ### Hai 模块与中间件的关系
39
41
 
40
42
  识别依据是运行图中的所有节点,不按应用直接依赖硬编码消费者名单。下面按当前公开依赖和功能说明;新增模块/后端时同步此表、连接目录与真实运行测试。
41
43
 
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 凭据按各自配置处理 |
44
+ | 模块 | 中间件来源或外部能力 |
45
+ | --------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
46
+ | reldb / cache / storage / vecdb | 分别拥有关系库、缓存、对象存储、向量库连接契约 |
47
+ | iam / kit | IAM 运行依赖 audit/reldb/cache/crypto;kit 经 IAM 继承。LDAP 按 login.ldap 开关启用,Reach peer 只有另行实际依赖时进入运行图 |
48
+ | audit / payment | audit 经 reldb 持久化;Stripe payment 直接依赖先初始化的共享 reldb 保存渠道映射和幂等结果,审计可选。微信、支付宝、Stripe 是已有商户 API,沿 payment 配置接入,不生成假云服务或容器 |
49
+ | scheduler / reach | 两者实际依赖 reldb/cache;Reach 另按启用的 SMTP/HTTP/短信渠道配置 |
50
+ | ai | 实际依赖 reldb/vecdb/datapipe。LLM、图像、音频端点及模型来自 AI 配置,不能仅凭包名选择模型镜像或 CUDA;不自动创建付费厂商账号 |
51
+ | crypto / serv | crypto 的可选运行依赖按 lockfile 解析,serv 经 crypto 继承;serv 的 IAM/storage/AI peer 不作为启用证据 |
52
+ | core / datapipe / api-contract / api-client / ui / capacitor / cli / deploy | 本身没有额外专用中间件;仍沿实际运行依赖继续遍历,例如 api-client/capacitor 的 crypto 分支。UI/设备能力、部署目标/Provisioner 凭据按各自配置处理 |
51
53
 
52
54
  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
55
 
56
+ AI 使用 MySQL 的首次建表需 CREATE 与 INDEX 权限,节点可并发初始化;仅明确重复索引且重查真实结构一致时继续,错误同名索引、权限和其他 DDL 故障必须阻止应用启动。DDL 不回滚,失败后检查初始化结果、逆序清理连接,再修复结构/权限重试,不能删库或吞错视为成功。
57
+
54
58
  ## 连接字段
55
59
 
56
60
  以下是自定义向导的连接字段;完整可用项、枚举与范围仍以项目 `.env.example` 和公开 Schema 为准。变量前缀为 `HAI_DB`、`HAI_CACHE`、`HAI_STORAGE`、`HAI_VECDB`、`HAI_IAM_LDAP`;Reach 使用实际索引 `HAI_REACH_PROVIDERS_<index>`。camelCase 不拆词,例如 `bindPassword` → `BINDPASSWORD`。
57
61
 
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 |
62
+ | 后端 | 连接字段 |
63
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
64
+ | SQLite | database(文件路径或 `:memory:`) |
65
+ | MySQL / PostgreSQL | url(优先)、host、port(1–65535)、database、user、password、ssl |
66
+ | Redis | url(优先)、host、port、password、db、tls、keyPrefix、connectTimeout、commandTimeout |
67
+ | local / LanceDB | root / path |
68
+ | S3 | endpoint、bucket、region、accessKeyId、secretAccessKey、forcePathStyle |
69
+ | Qdrant / Chroma | url;Qdrant 可选 apiKey;Chroma apiKey 用于 Bearer 自托管网关,URL 仅支持 HTTP(S) 根端点,不提供 Cloud 契约 |
70
+ | pgvector | url(优先)或 host/port/user/password,以及 database |
71
+ | LDAP | url、bindDn、bindPassword、searchBase、searchFilter、usernameAttribute、emailAttribute、displayNameAttribute、useTls、connectTimeout |
72
+ | SMTP | host、port、secure、user、pass、from |
73
+ | HTTP API | url、method(POST / PUT)、headers(JSON)、timeout |
74
+ | 阿里云 SMS | accessKeyId、accessKeySecret、signName、endpoint |
71
75
 
72
76
  ## 离线准备与验收
73
77
 
74
- 构建与部署分开:先在构建机运行 `pnpm build --target docker`,将完整 `.hai/build/docker/<buildId>` 复制到部署电脑。deploy 只读取这些已校验的镜像 tar,不需要本地 Docker/Podman、基础镜像、pnpm store 或 SQLite 下载。Node/pnpm 版本以工程 engines/packageManager 为准。
78
+ 构建与部署可分开,也可通过 pnpm run deploy --build 明确串联。构建保持环境无关;部署选择环境并只消费所选镜像。完整包自动包含 Compose 自建中间件镜像,因此导出阶段需要本地 Podman/Docker,缺镜像在打包阶段准备,不能留给隔离服务器下载。
79
+
80
+ 部署缓存、带版本 tar 与 -deploy/ 目录默认位于工程根 .hai/deploy/<环境>/;--output 可显式指定其他 tar 路径,相对命令执行目录解析。--bundle-only 只生成交付物,不连接服务器。操作目录含 Windows deploy.ps1、Linux/macOS deploy.sh 和带逐项配置说明的 deploy-target.json;运行上传脚本前先修改并核对该 JSON,两个脚本均读取此文件。本机运行 deploy.sh 需 sh、awk、ssh、scp;sh deploy.sh 主机 [端口] [用户] 可覆盖连接地址,HAI_DEPLOY_IDENTITY_FILE 和 HAI_DEPLOY_KNOWN_HOSTS_FILE 可覆盖两脚本的私钥及指纹文件路径。隔离服务器搬运并解包后执行 sh apply.sh。每步失败返回非零,结果写 result-<部署ID>.json;只有 HTTP 检查通过才更新服务器 current。
81
+
82
+ 部分更新通过 --services 指定,使用服务器成功基线保留未选服务和中间件凭据。首次安装不能使用部分包。storage-init 复用应用镜像及 S3 SDK 检查/创建桶,不清空数据;只有涉及存储初始化时执行,并检查退出码。仅更新 storage 仍需含 app 镜像的制品;最近为 web-only 时,用 --artifact-dir 选择原完整制品,或先 pnpm build --target docker --images app。app 镜像仅用于初始化,不会更新 app 服务。失败恢复尝试还原容器和配置,保留数据卷,不回退已写业务数据。
75
83
 
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 选择运行时,镜像缺少时停止,不尝试联网。
84
+ 服务器需要 Linux、Docker/Podman、Compose、sh、tar、sha256sum、awk、curl、util-linux flock、支持 flock 的持久化文件系统和容器/目录权限。部署和恢复使用固定 `.deploy.lock` 内核锁,不可删除/替换或按时间抢锁;活跃子命令继承 FD,全部持有进程退出或重启后自动释放。强杀/断电后的执行状态需核对结果文件、current 和容器,不能将锁释放当作恢复成功。完整交付包含自建服务镜像;自定义外部连接仍需准备服务、账号和网络可达性。云 Provider/Provisioner 需要外网,不能用于完全离线。
77
85
 
78
- Linux 需要 compose、sh、tar、sha256sum 和容器/目录/持久化磁盘权限;中间件镜像另行 docker load,名称、tag 和架构须与包内清单一致。自定义连接要求现有服务可从应用容器访问,并备好账号、数据库、S3 桶与密钥。云 Provider/Provisioner 需要网络,不能用于完全离线。
86
+ 生成的 `web.conf` 在 API location 设 `client_max_body_size 2129920`(2 MiB + 32 KiB),用 large_client_header_buffers 4 32k / proxy_buffer_size 32k 容纳 X-Hai-Transport 认证头,并配套 proxy_buffers 8 32k,保证关闭 buffering 时也能通过 Nginx 缓冲区配置校验。请求/响应 proxy buffering 保持关闭;这与 transport 密文预算一致,避免 Nginx 默认上限拦截合法的 1 MiB 明文编码结果。应用仍执行握手 16 KiB、公钥 4 KiB、明文 1 MiB 的有界读取。自有反向代理须允许相同密文预算并传播取消,不替代应用字节限制;大文件上传使用独立路径和明确预算。
79
87
 
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`,不修改系统代理。
88
+ 完全离线构建另需基础镜像、Linux 依赖缓存、pnpm 和匹配驱动/Node ABI/架构/libc 的原生预编译包。构建可用 --sqlite-prebuild-mirror,Podman 代理不可达可用 --no-http-proxy;不要改系统代理或让 deploy 下载编译环境。生产 HTTPS、CORS、证书、DNS 另行准备。实际断网验收登录、刷新、重启和数据持久化。
81
89
 
82
- ## 启动与验证
90
+ ## 命令输出
83
91
 
84
- fullstack 的 hai.build.json 含 app/web 时,两镜像共用构建 ID,部署生成一个 Compose 和同源代理。API 前缀来自当前服务配置,缺省 CORS 填公开入口,明确 Origin 保留;Web 不挂载 .env.runtime。线上成功要求 API/Web 健康,手工模式返回 packaged 并提示用户自行验收。生产登录刷新还需 HTTPS、正确 CORS、受信证书与 DNS/hosts,模块不配置 TLS。
92
+ 内部 `runCommand` / `SshExecutor.exec` 先 UTF-8 解码,再对每条逻辑流跨块精确脱敏;成功 stdout/stderr、错误、onOutput 均为安全结果。`secrets` 不推断全部进程环境,调用方列出秘密及可能输出的编码/转换值;总长不超过 1 Mi UTF-16 单元,脱敏异常失败关闭且不暴露原 cause。`maxCaptureChars` 默认 1048576、最大 16777216,捕获尾部并返回 stdoutTruncated/stderrTruncated,结构化解析先拒绝截断。onOutput 支持 Promise,顺序等待并保持 pipe 背压;异常回调使执行失败。异常终止不释放未完成的秘密尾部。部署进度仅解析每行前 512 字符。超时不能证明远端工作已终止。
85
93
 
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,不把运行期凭据写入镜像。
94
+ deadline 包括 helper 启动和输出处理。异常后 `terminationTimeoutMs` 另等待本地终止核验,默认 10000、上限 60000 毫秒。POSIX 用独立继承进程组;Windows 用隐藏且不加载 profile 的 PowerShell/.NET Job,挂起创建 → 分配成功 → 恢复,禁止 breakaway,helper/编译/分配失败拒绝执行。已脱离组的系统 daemon、现有容器和远端服务不在本地确认范围。只有受管理 Job/组无活动成员才是 confirmed;未证明为 unknown,启动前为 not-started。输入、回调和流异常同样经过此路径,已经进入的回调不能撤销,但不再交付新块。
87
95
 
88
- 修改后运行 deploy/CLI typecheck、lint、build、test、scaffold gates 和 test:deploy-gates。测试应分别证明构建/环境选择、SSH 上传及手工包启动、Web 登录/刷新;断网能力另做实际断网验收。测试的临时工程、容器、镜像、日志、截图、报告与辅助进程必须 finally/teardown 自动清理,失败也清理;不得删除用户已有资源。原生客户端及付费云资源单独验收并报告边界。
96
+ 公开 `getDeployCommandState(result.error)` 在失败分支读取原因、本地状态、远端状态、deploymentId 和 resultFile。SSH 中断默认为远端 unknown;docker-ssh 新连接只读解析原子 `result-<ID>.json`,完整/未截断/ID 匹配才为 finished,并返回实际阶段、退出码。finished 指脚本及恢复结束,容器可以继续运行;即使终态为 0,原连接/输出失败仍保留。缺失、旧记录、损坏或重连失败保持 unknown,不自动重试写入。按 ID 核对步骤、current 和服务,再恢复。
@@ -75,7 +75,7 @@ npm view @h-ai/cli dist-tags --json
75
75
  | --- | --- |
76
76
  | 自动处理 | 目标明确、可保持现有行为的依赖更新、兼容性修正、无冲突的 skills 合并和验证,直接执行 |
77
77
  | 用户选择 | 存在多个有意义的方案且影响产品行为、架构、成本、Provider 或运维方式,展示证据、选项、推荐与取舍,等待回答 |
78
- | 阻塞 | 无法取得必要产物、依赖不兼容、无法无损合并、必要工具/服务/授权缺失或关键验证失败,先诊断并修复可自行解决的原因;仍无法推进时说明具体处理步骤 |
78
+ | 阻塞 | 无法取得必要产物、依赖不兼容、无法无损合并、必要工具/服务/授权缺失等导致后续无法执行,先处理可自行解决的原因;仍无法推进时说明具体步骤。普通门禁失败先记录,继续独立检查,本轮汇总后集中修复;必要门禁未通过仍不能交付完成 |
79
79
  | 不适用 | 当前应用未使用且不影响其依赖/运行的变化,记录依据;不要求用户逐项确认 |
80
80
 
81
81
  - 尽量合并相互独立的选择,明确每个回答控制哪项改动;已给出的目标、授权和选择持续有效,不重复询问。
@@ -95,7 +95,7 @@ npm view @h-ai/cli dist-tags --json
95
95
 
96
96
  ### 6. 验证与交付
97
97
 
98
- 按应用实际 scripts 和 [hai-ci](../hai-ci/SKILL.md) 执行 `typecheck → lint → build → test`,优先受影响 workspace;跨包契约验证所有消费者。按 [hai-app-tests](../hai-app-tests/SKILL.md) 选择受影响的集成/E2E,覆盖启动/关闭、变更 API、认证、持久化等实际路径。
98
+ 按应用实际 scripts 和 [分轮验证与集中修复](../hai-app-tests/validation.md) 先收集一轮适用门禁,汇总根因后集中修复、再验证;优先受影响 workspace,跨包契约验证所有消费者。按 [hai-app-tests](../hai-app-tests/SKILL.md) 选择集成/E2E,覆盖启动/关闭、变更 API、认证、持久化等实际路径;CI/发布按 [hai-ci](../hai-ci/SKILL.md) 保留失败阻断。
99
99
 
100
100
  - 依赖升级后另核对 lockfile 与安装版本、公开入口、配置加载和关键业务流程;build 通过不能替代 typecheck 或运行验收。
101
101
  - 检查同步后的全部相邻资源、相对引用和各工具入口;目标存在 `check:skills` 时运行,不假定应用具有框架仓库私有脚本。
@@ -45,5 +45,5 @@ PowerShell 使用 `pnpm.cmd`;占位包名替换后执行。本地来源先验
45
45
 
46
46
  - 仅 skills/入口变更:检查 diff、完整目录、目标实际存在的入口及相邻资源、相对引用;排除框架仓库私有绝对路径/命令。
47
47
  - 框架模板维护:运行已有 `check:skills` 和 CLI `test:scaffold-gates`,确认资源与路由在生成项目中可达。
48
- - 同步应用依赖或运行时:按入口升级流程和 [hai-ci](../hai-ci/SKILL.md) 选择应用门禁,不以模板已同步代替运行验收。
48
+ - 同步应用依赖或运行时:按入口升级流程和 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集适用门禁、集中修复并复测;CI/发布遵守 [hai-ci](../hai-ci/SKILL.md),不以模板已同步代替运行验收。
49
49
  - 双仓改动时说明来源版本/提交、改动顺序、应用私有覆盖和双方验证;涉及 PR 时按 [hai-pr-review](../hai-pr-review/SKILL.md) 关联同步说明。仅授权一个仓库时记录另一方待同步,不擅自修改。
@@ -7,16 +7,18 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
7
7
 
8
8
  ## 能力契约
9
9
 
10
- | 项目 | 契约 |
11
- | --- | --- |
12
- | 能力 | 使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key 和 RBAC |
13
- | 适用场景 | 应用身份认证、会话失效或角色权限管理 |
14
- | 输入 | IamConfigInput、已初始化依赖、凭据、用户/角色/权限 ID |
15
- | 输出 | 认证、会话与授权操作的 HaiResult |
16
- | 限制 | 仅服务端,先初始化 reldb/cache(crypto 未初始化时由 IAM 初始化);公开认证入口是 iam.auth。客户端经 contract/API 调用,凭证只用于认证和安全存储,不进入日志或公开配置。 |
10
+ | 项目 | 契约 |
11
+ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12
+ | 能力 | 使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key 和 RBAC |
13
+ | 适用场景 | 应用身份认证、会话失效或角色权限管理 |
14
+ | 输入 | IamConfigInput、已初始化依赖、凭据、用户/角色/权限 ID |
15
+ | 输出 | 认证、会话与授权操作的 HaiResult |
16
+ | 限制 | 仅服务端,先初始化 reldb/cache(crypto 未初始化时由 IAM 初始化);公开认证入口是 iam.auth。客户端经 contract/API 调用,凭证只用于认证和安全存储,不进入日志或公开配置。 |
17
17
 
18
18
  ## 使用路径
19
19
 
20
+ - 密码登录只接收 identifier/password;AuthPanel 将 LoginForm 的 username 映射为 identifier。会话期限来自服务端 session.maxAge/refreshTokenMaxAge,不增加未实现的登录偏好字段或持久会话选择框。
21
+
20
22
  - `_iam.yml` 只存 IamConfigSettingsInput,运行时回调单独传给 iam.init;不把 reldb/cache 句柄混入配置。
21
23
  - 角色资料与 permissionIds 一次保存:未传保持、空数组清空、未知 ID 失败;人数查询失败不能显示为 0。
22
24
  - IAM 服务端是 iam.auth;typed HTTP client 的路径是 apiClient.iam.auth。
@@ -28,14 +30,12 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
28
30
  - LDAP/Reach 均可选:基础认证无需安装;默认 LDAP 关闭,选用默认客户端才安装 ldapts 并设置 login.ldap + ldap。OTP 邮件/短信由 otp.email/sms 控制,选择 Reach 标准模板或自定义发送回调;具体签名与失败处理见参考中的“可选 LDAP 与通知”。
29
31
  - 多节点共享 DB + Redis,Memory 只用于单进程。重置的策略失败计入 maxAttempts;OTP 消费不解除 resendInterval。管理员 createUser 不受自助注册开关限制。
30
32
 
31
- - 协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
32
-
33
- - 受保护请求使用 `iam.auth.verifyToken`:当前用户状态 + 角色 JOIN + 批量权限读取,失败即拒绝;`iam.session.verifyToken` 仅验证会话快照。serv 内置 IAM 注入默认使用当前授权入口,无需手动补查。
33
+ - 受保护请求使用 `iam.auth.verifyToken`:先核验会话绑定的持久凭据代,再复核当前用户启用状态与凭据代并读取当前角色/权限;任一持久读取失败即拒绝。`iam.session.get/verifyToken` 也读取持久用户并校验启用状态与 `credentialVersion`,但返回的角色/权限仍是会话快照,不能替代当前授权。serv 内置 IAM 注入默认使用当前授权入口,无需手动补查。
34
34
  - 用户批量读取用 `iam.user.getUsers(ids, { include: ['roles'] })`(最多 1000,去重保留输入顺序,缺失省略);角色筛选用 `listUsers({ roleCode })`,在分页前执行,避免 N+1 和分页后过滤。
35
35
 
36
36
  ## 按需参考
37
37
 
38
- 详细配置、API 签名、错误码和范本在 [reference.md](reference.md)。按下列标题定位所需小节,不默认通读;用例中的业务变量需结合当前应用补齐。
38
+ 按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
39
39
 
40
40
  - 运行环境
41
41
  - 依赖
@@ -47,3 +47,17 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
47
47
  - 错误码 — `HaiIamError`
48
48
  - 常见模式
49
49
  - 相关 Skills
50
+
51
+ 会话与 refresh 消费记录统一保存在 `hai:iam:sessions:{SHA256(userId)}` 版本化文档,不再使用独立 token 映射/索引。Bearer token 携带不可作为凭据的分区提示和 256 位随机值;客户端必须视为不透明字符串。`Session.version` 用于轮换提交校验。Memory 仅用于单进程测试,生产所有节点共享 Redis(含 Cluster)。并发冲突从最新文档重试,耗尽返回 CONFLICT;旧 refresh 不会产生第二个后继。若响应丢失后结果不明,调用方按失败关闭并重新登录,不假定旧凭据仍有效;全部会话仍受同一用户状态索引管理。
52
+
53
+ `iam.session.update(token, patch, expectedVersion)` 必须传入读取到的 Session.version。旧版本返回 CONFLICT,已撤销 token 返回 SESSION_NOT_FOUND;不会将旧内容重新写回或覆盖新会话。仅滑动时间戳更新不改变内容版本,允许正常并行请求。批量权限更新绑定首次读取的 token/版本集合,不将迟到数据应用到后续登录;遇到较新内容版本失败,调用方重新查询授权后再提交。
54
+
55
+ `create({ maxAge })` 可覆盖配置默认时长(正安全整数秒),该值持久化为 `Session.maxAge`;签发和刷新返回的 `expiresIn` 均使用它。滑动续期按该时长延长 access,返回已成功提交的 `lastActiveAt/expiresAt`;迟到请求只延长时间,不缩短较新续期。显式 `update` 的有效日期补丁完整生效并产生新内容版本。客户端根据本次返回的 `expiresIn` 安排刷新,不能硬编码配置默认值。
56
+
57
+ access 与 refresh 独立到期:用户分区保留至所有 access/refresh 的最晚有效期,但 access 认证只检查自己的 `expiresAt`。access 过期仍可凭未过期、未撤销的 refresh 轮换;每次轮换从新签发时刻重新计算 `refreshTokenMaxAge`,旧 access/refresh 同时失效。删除 access、logoutAll、单设备替换均撤销待刷新记录;仅 revokeRefresh 保留仍有效的 access。刷新路由只校验 refresh,不能先要求旧 access 认证成功。
58
+
59
+ OTP 发布/消费用单身份 CAS;密码重置最终 SQL 同时核验 resetVersion、credentialVersion、有效期、用户状态并改密/消费令牌。认证与刷新绑定数据库凭据代际;通知第三参数提供 expiresAt/idempotencyKey,接收端持久化幂等,异常是投递未知且不自动重发。
60
+
61
+ `iam.auth.logout(accessToken)` 直接按完整不透明令牌在用户分区执行一次 CAS,原子撤销 access 与关联 refresh;access 已到期也不能跳过撤销。非法/不存在的令牌幂等成功,缓存读取或提交故障返回失败,恢复后可用同一令牌重试。HTTP 登出只允许撤销所持令牌,不以 userId 定位他人会话,也不要求该 access 仍通过认证。调用方必须检查 `success`;本地 Cookie/安全存储清理不代表服务端撤销已经确认,失败应显示提示。
62
+
63
+ 配置示例用 `IamConfigInput` 表达可省略默认值的输入;需要完整策略快照时用对应 Schema.parse 得到输出类型。应用仅从 `@h-ai/iam` 取公开函数/类型,不深导内部策略与仓储。源码内部示例明确标注上下文,文档示例类型检查不替代真实共享存储和通知集成验证。