@h-ai/cli 0.1.0-alpha9 → 1.0.0-beta.2

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 (438) hide show
  1. package/README.en.md +229 -0
  2. package/README.ja.md +229 -0
  3. package/README.md +175 -113
  4. package/dist/chunk-AKV2CQZV.js +1497 -0
  5. package/dist/chunk-AKV2CQZV.js.map +1 -0
  6. package/dist/chunk-EF7WOFWZ.js +7557 -0
  7. package/dist/chunk-EF7WOFWZ.js.map +1 -0
  8. package/dist/chunk-MUHRBJSW.js +29272 -0
  9. package/dist/chunk-MUHRBJSW.js.map +1 -0
  10. package/dist/chunk-ZSDEVXZ7.js +38 -0
  11. package/dist/{chunk-2H7UOFLK.js.map → chunk-ZSDEVXZ7.js.map} +1 -1
  12. package/dist/cli-config-templates-HHFHNXBN.js +5 -0
  13. package/dist/cli-config-templates-HHFHNXBN.js.map +1 -0
  14. package/dist/cli-skill-templates-PN4FLGBC.js +5 -0
  15. package/dist/cli-skill-templates-PN4FLGBC.js.map +1 -0
  16. package/dist/index.js +4444 -554
  17. package/dist/index.js.map +1 -1
  18. package/dist/metafile-esm.json +1 -0
  19. package/dist/src-TEBHR5I5.js +5 -0
  20. package/dist/src-TEBHR5I5.js.map +1 -0
  21. package/package.json +35 -15
  22. package/templates/README.md +56 -114
  23. package/templates/apps/admin/messages/en-US.json +7 -0
  24. package/templates/apps/admin/messages/ja-JP.json +40 -0
  25. package/templates/apps/admin/messages/zh-CN.json +7 -0
  26. package/templates/apps/admin/src/routes/+layout.svelte.hbs +65 -8
  27. package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -14
  28. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +54 -22
  29. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
  30. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
  31. package/templates/apps/api/.dockerignore.hbs +25 -0
  32. package/templates/apps/api/.gitignore.hbs +22 -0
  33. package/templates/apps/api/.npmrc.hbs +1 -0
  34. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +39 -0
  35. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +41 -0
  36. package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
  37. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +28 -0
  38. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +5 -0
  39. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +16 -0
  40. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
  41. package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
  42. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
  43. package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
  44. package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +60 -0
  45. package/templates/apps/api/apps/{{projectName}}-serv/config/_core.yml.hbs +17 -0
  46. package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +54 -0
  47. package/templates/apps/api/apps/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
  48. package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +66 -0
  49. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +119 -0
  50. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +7 -0
  51. package/templates/apps/api/apps/{{projectName}}-serv/src/index.ts.hbs +41 -0
  52. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +165 -0
  53. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +57 -0
  54. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +41 -0
  55. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +176 -0
  56. package/templates/apps/api/apps/{{projectName}}-serv/tsconfig.json.hbs +16 -0
  57. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +20 -0
  58. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +12 -0
  59. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +113 -0
  60. package/templates/apps/api/e2e/capture-evidence.ts.hbs +23 -0
  61. package/templates/apps/api/eslint.config.js.hbs +30 -0
  62. package/templates/apps/api/hai.json.hbs +4 -0
  63. package/templates/apps/api/images/serv.Dockerfile.hbs +37 -0
  64. package/templates/apps/api/package.json.hbs +53 -0
  65. package/templates/apps/api/playwright.config.ts.hbs +67 -0
  66. package/templates/apps/api/pnpm-workspace.yaml.hbs +40 -0
  67. package/templates/apps/api/tsconfig.base.json +14 -0
  68. package/templates/apps/fullstack/.dockerignore.hbs +25 -0
  69. package/templates/apps/fullstack/.gitignore.hbs +38 -0
  70. package/templates/apps/fullstack/.npmrc.hbs +1 -0
  71. package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
  72. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +67 -0
  73. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +20 -0
  74. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +20 -0
  75. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +22 -0
  76. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +7 -0
  77. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
  78. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +7 -0
  79. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +86 -0
  80. package/templates/apps/fullstack/apps/{{projectName}}-app/project.inlang/settings.json.hbs +17 -0
  81. package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
  82. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +59 -0
  83. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +20 -0
  84. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +8 -0
  85. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +9 -0
  86. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +19 -0
  87. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +21 -0
  88. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +16 -0
  89. package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +23 -0
  90. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +74 -0
  91. package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
  92. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +64 -0
  93. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +20 -0
  94. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +20 -0
  95. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/en-US.json.hbs +7 -0
  96. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
  97. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/zh-CN.json.hbs +7 -0
  98. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +66 -0
  99. package/templates/apps/fullstack/apps/{{projectName}}-desktop/project.inlang/settings.json.hbs +17 -0
  100. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +25 -0
  101. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.css.hbs +18 -0
  102. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +8 -0
  103. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +9 -0
  104. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +20 -0
  105. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +33 -0
  106. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
  107. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +16 -0
  108. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
  109. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
  110. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
  111. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
  112. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
  113. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +91 -0
  114. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
  115. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +46 -0
  116. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +14 -0
  117. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +22 -0
  118. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +89 -0
  119. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +25 -0
  120. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/en-US.json.hbs +12 -0
  121. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
  122. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/zh-CN.json.hbs +12 -0
  123. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/project.inlang/settings.json.hbs +17 -0
  124. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +54 -0
  125. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +20 -0
  126. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +21 -0
  127. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/en-US.json.hbs +7 -0
  128. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
  129. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/zh-CN.json.hbs +7 -0
  130. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +53 -0
  131. package/templates/apps/fullstack/apps/{{projectName}}-web/project.inlang/settings.json.hbs +17 -0
  132. package/templates/apps/fullstack/apps/{{projectName}}-web/public/runtime-config.js.hbs +1 -0
  133. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +18 -0
  134. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.css.hbs +18 -0
  135. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +8 -0
  136. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +9 -0
  137. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
  138. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +16 -0
  139. package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +23 -0
  140. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +76 -0
  141. package/templates/apps/fullstack/docs/security-configuration.md.hbs +349 -0
  142. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +988 -0
  143. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +119 -0
  144. package/templates/apps/fullstack/eslint.config.js.hbs +41 -0
  145. package/templates/apps/fullstack/hai.json.hbs +11 -0
  146. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +38 -0
  147. package/templates/apps/fullstack/images/web/19-runtime-config.envsh.hbs +5 -0
  148. package/templates/apps/fullstack/images/web/nginx.conf.hbs +24 -0
  149. package/templates/apps/fullstack/images/web/runtime-config.js.template.hbs +3 -0
  150. package/templates/apps/fullstack/images/web.Dockerfile.hbs +37 -0
  151. package/templates/apps/fullstack/package.json.hbs +61 -0
  152. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +40 -0
  153. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +30 -0
  154. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +78 -0
  155. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +134 -0
  156. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-contract.ts.hbs +20 -0
  157. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +35 -0
  158. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +7 -0
  159. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +23 -0
  160. package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +31 -0
  161. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsconfig.json.hbs +9 -0
  162. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +9 -0
  163. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +84 -0
  164. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_ai.yml.hbs +1 -0
  165. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +1 -0
  166. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +1 -0
  167. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +22 -0
  168. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +1 -0
  169. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_feedback.yml.hbs +1 -0
  170. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +1 -0
  171. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_payment.yml.hbs +1 -0
  172. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +60 -0
  173. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_scheduler.yml.hbs +1 -0
  174. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +64 -0
  175. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +1 -0
  176. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_vecdb.yml.hbs +1 -0
  177. package/templates/apps/fullstack/packages/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
  178. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +62 -0
  179. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +115 -0
  180. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +7 -0
  181. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +40 -0
  182. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +294 -0
  183. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/audit-jobs.ts.hbs +28 -0
  184. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +50 -0
  185. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +50 -0
  186. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +106 -0
  187. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +49 -0
  188. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +34 -0
  189. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +159 -0
  190. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +73 -0
  191. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +505 -0
  192. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +72 -0
  193. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +183 -0
  194. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsconfig.json.hbs +10 -0
  195. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +18 -0
  196. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +12 -0
  197. package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +10 -0
  198. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +97 -0
  199. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +164 -0
  200. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +164 -0
  201. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +164 -0
  202. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +55 -0
  203. package/templates/apps/fullstack/packages/{{projectName}}-shared/project.inlang/settings.json.hbs +11 -0
  204. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +7 -0
  205. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +94 -0
  206. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +36 -0
  207. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +16 -0
  208. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/session-recovery.svelte.ts.hbs +4 -0
  209. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +122 -0
  210. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +90 -0
  211. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +86 -0
  212. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +94 -0
  213. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/configuration.svelte.hbs +25 -0
  214. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +157 -0
  215. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +77 -0
  216. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +44 -0
  217. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +216 -0
  218. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +12 -0
  219. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +55 -0
  220. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +614 -0
  221. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +23 -0
  222. package/templates/apps/fullstack/packages/{{projectName}}-shared/svelte.config.js.hbs +1 -0
  223. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +150 -0
  224. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +253 -0
  225. package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +17 -0
  226. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -0
  227. package/templates/apps/fullstack/playwright.config.ts.hbs +114 -0
  228. package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +18 -0
  229. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +38 -0
  230. package/templates/apps/fullstack/tsconfig.base.json +14 -0
  231. package/templates/apps/h5/messages/en-US.json +3 -0
  232. package/templates/apps/h5/messages/ja-JP.json +40 -0
  233. package/templates/apps/h5/messages/zh-CN.json +3 -0
  234. package/templates/apps/h5/src/routes/+layout.svelte.hbs +85 -30
  235. package/templates/apps/h5/src/routes/+page.svelte.hbs +20 -8
  236. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
  237. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
  238. package/templates/apps/mobile-app/.env.hbs +3 -0
  239. package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
  240. package/templates/apps/mobile-app/index.html.hbs +14 -0
  241. package/templates/apps/mobile-app/messages/en-US.json +36 -0
  242. package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
  243. package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
  244. package/templates/apps/mobile-app/public/runtime-config.js +1 -0
  245. package/templates/apps/mobile-app/src/App.svelte.hbs +153 -0
  246. package/templates/apps/mobile-app/src/lib/api.ts.hbs +213 -0
  247. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +14 -0
  248. package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
  249. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +58 -0
  250. package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -0
  251. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +16 -0
  252. package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
  253. package/templates/apps/website/messages/en-US.json +4 -2
  254. package/templates/apps/website/messages/ja-JP.json +34 -0
  255. package/templates/apps/website/messages/zh-CN.json +4 -2
  256. package/templates/apps/website/scripts/static-website.mjs.hbs +67 -0
  257. package/templates/apps/website/src/routes/+layout.svelte.hbs +77 -26
  258. package/templates/apps/website/src/routes/+layout.ts +3 -0
  259. package/templates/apps/website/src/routes/+page.svelte.hbs +31 -12
  260. package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
  261. package/templates/apps/website/src/routes/api/health/+server.ts +3 -0
  262. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +44 -34
  263. package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
  264. package/templates/base/.dockerignore.hbs +27 -0
  265. package/templates/base/.gitignore.hbs +41 -0
  266. package/templates/base/.npmrc.hbs +1 -0
  267. package/templates/base/e2e/app.spec.ts.hbs +213 -0
  268. package/templates/base/e2e/helpers.ts +1 -1
  269. package/templates/base/eslint-rules.js +45 -0
  270. package/templates/base/eslint.config.js.hbs +34 -0
  271. package/templates/base/hai.json.hbs +4 -0
  272. package/templates/base/images/web/nginx.conf.hbs +24 -0
  273. package/templates/base/images/web.Dockerfile.hbs +72 -0
  274. package/templates/base/package.json.hbs +125 -49
  275. package/templates/base/playwright.config.ts +44 -6
  276. package/templates/base/pnpm-workspace.yaml.hbs +40 -0
  277. package/templates/base/src/app.css.hbs +40 -0
  278. package/templates/base/src/app.d.ts.hbs +14 -1
  279. package/templates/base/src/app.html.hbs +13 -0
  280. package/templates/base/src/hooks.server.ts.hbs +115 -43
  281. package/templates/base/src/lib/build-metadata.ts.hbs +7 -0
  282. package/templates/base/src/lib/server/init.ts.hbs +125 -152
  283. package/templates/base/src/lib/server/kit-config.ts.hbs +40 -0
  284. package/templates/base/src/routes/+layout.server.ts.hbs +35 -0
  285. package/templates/base/static/favicon.png +0 -0
  286. package/templates/base/svelte.config.js.hbs +84 -5
  287. package/templates/base/tests/scaffold.test.ts.hbs +102 -0
  288. package/templates/base/tsconfig.json.hbs +50 -0
  289. package/templates/base/vite.config.ts.hbs +117 -12
  290. package/templates/base/vitest.config.ts.hbs +19 -0
  291. package/templates/features/ai/routes/api/chat/+server.ts +6 -7
  292. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +3 -17
  293. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +10 -0
  294. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +9 -96
  295. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +9 -122
  296. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +10 -0
  297. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +10 -0
  298. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +9 -96
  299. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +9 -112
  300. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +10 -0
  301. package/templates/features/iam/routes-shared/api/auth/captcha/+server.ts.hbs +4 -0
  302. package/templates/features/iam/routes-shared/api/auth/config/+server.ts +4 -0
  303. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +1 -29
  304. package/templates/features/iam/routes-shared/api/auth/login/ldap/+server.ts +4 -0
  305. package/templates/features/iam/routes-shared/api/auth/login/otp/+server.ts +4 -0
  306. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +13 -6
  307. package/templates/features/iam/routes-shared/api/auth/otp/send/+server.ts +4 -0
  308. package/templates/features/iam/routes-shared/api/auth/password/renew/+server.ts +5 -0
  309. package/templates/features/iam/routes-shared/api/auth/password-reset/confirm/+server.ts +4 -0
  310. package/templates/features/iam/routes-shared/api/auth/password-reset/request/+server.ts +4 -0
  311. package/templates/features/iam/routes-shared/api/auth/reauthenticate/+server.ts +5 -0
  312. package/templates/features/iam/routes-shared/api/auth/register/+server.ts +1 -23
  313. package/templates/features/iam/routes-shared/api/auth/session/close/+server.ts.hbs +4 -0
  314. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +10 -0
  315. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +11 -0
  316. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +11 -0
  317. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +10 -0
  318. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +25 -23
  319. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +5 -2
  320. package/templates/features/storage/routes/api/upload/+server.ts +10 -8
  321. package/templates/i18n/project.inlang/settings.json +2 -2
  322. package/templates/shared/.gitattributes +11 -0
  323. package/templates/shared/build-metadata.ts +42 -0
  324. package/templates/shared/container-runtime-config.sh +30 -0
  325. package/templates/shared/e2e-env.ts +98 -0
  326. package/templates/shared/hash-router.svelte.ts +83 -0
  327. package/templates/shared/public-base-path.mjs +19 -0
  328. package/templates/shared/run-e2e.mjs +104 -0
  329. package/templates/shared/vite-runtime-config.ts +56 -0
  330. package/templates/skills/AGENTS.md +75 -16
  331. package/templates/skills/CLAUDE.md +0 -11
  332. package/templates/skills/bridges/admin/AGENTS.md +89 -0
  333. package/templates/skills/bridges/api/AGENTS.md +83 -0
  334. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +102 -0
  335. package/templates/skills/bridges/h5/AGENTS.md +88 -0
  336. package/templates/skills/bridges/mobile-app/AGENTS.md +100 -0
  337. package/templates/skills/bridges/website/AGENTS.md +88 -0
  338. package/templates/skills/copilot-instructions.md +3 -59
  339. package/templates/skills/hai-ai/SKILL.md +44 -715
  340. package/templates/skills/hai-ai/guide.md +457 -0
  341. package/templates/skills/hai-ai/reference.md +292 -0
  342. package/templates/skills/hai-api-client/SKILL.md +26 -339
  343. package/templates/skills/hai-api-client/reference.md +180 -0
  344. package/templates/skills/hai-api-contract/SKILL.md +36 -0
  345. package/templates/skills/hai-api-contract/reference.md +280 -0
  346. package/templates/skills/hai-app-create/SKILL.md +38 -351
  347. package/templates/skills/hai-app-create/collaboration.md +52 -0
  348. package/templates/skills/hai-app-create/development.md +243 -0
  349. package/templates/skills/hai-app-create/reference.md +376 -0
  350. package/templates/skills/hai-app-review/SKILL.md +57 -126
  351. package/templates/skills/hai-app-review/differential.md +34 -0
  352. package/templates/skills/hai-app-tests/SKILL.md +34 -454
  353. package/templates/skills/hai-app-tests/reference.md +462 -0
  354. package/templates/skills/hai-app-tests/validation.md +42 -0
  355. package/templates/skills/hai-audit/SKILL.md +37 -122
  356. package/templates/skills/hai-audit/reference.md +184 -0
  357. package/templates/skills/hai-build/SKILL.md +62 -315
  358. package/templates/skills/hai-build/paths.md +30 -0
  359. package/templates/skills/hai-build/reference.md +353 -0
  360. package/templates/skills/hai-cache/SKILL.md +26 -175
  361. package/templates/skills/hai-cache/reference.md +229 -0
  362. package/templates/skills/hai-capacitor/SKILL.md +24 -364
  363. package/templates/skills/hai-capacitor/reference.md +358 -0
  364. package/templates/skills/hai-change-plan/SKILL.md +46 -0
  365. package/templates/skills/hai-ci/SKILL.md +114 -0
  366. package/templates/skills/hai-core/SKILL.md +31 -469
  367. package/templates/skills/hai-core/reference.md +557 -0
  368. package/templates/skills/hai-core/request-access.md +65 -0
  369. package/templates/skills/hai-crypto/SKILL.md +29 -180
  370. package/templates/skills/hai-crypto/reference.md +317 -0
  371. package/templates/skills/hai-datapipe/SKILL.md +24 -207
  372. package/templates/skills/hai-datapipe/reference.md +204 -0
  373. package/templates/skills/hai-deploy/SKILL.md +53 -178
  374. package/templates/skills/hai-deploy/credentials.md +19 -0
  375. package/templates/skills/hai-deploy/middleware.md +129 -0
  376. package/templates/skills/hai-feedback/SKILL.md +85 -0
  377. package/templates/skills/hai-framework-sync/SKILL.md +105 -0
  378. package/templates/skills/hai-framework-sync/sync.md +49 -0
  379. package/templates/skills/hai-iam/SKILL.md +53 -325
  380. package/templates/skills/hai-iam/reference.md +535 -0
  381. package/templates/skills/hai-kit/SKILL.md +33 -329
  382. package/templates/skills/hai-kit/reference.md +202 -0
  383. package/templates/skills/hai-payment/SKILL.md +28 -262
  384. package/templates/skills/hai-payment/reference.md +295 -0
  385. package/templates/skills/hai-pr-review/SKILL.md +41 -0
  386. package/templates/skills/hai-reach/SKILL.md +28 -300
  387. package/templates/skills/hai-reach/reference.md +363 -0
  388. package/templates/skills/hai-reldb/SKILL.md +28 -449
  389. package/templates/skills/hai-reldb/reference.md +500 -0
  390. package/templates/skills/hai-scheduler/SKILL.md +26 -174
  391. package/templates/skills/hai-scheduler/reference.md +216 -0
  392. package/templates/skills/hai-serv/SKILL.md +60 -0
  393. package/templates/skills/hai-serv/reference.md +792 -0
  394. package/templates/skills/hai-spec-implement/SKILL.md +50 -0
  395. package/templates/skills/hai-spec-implement/changes.md +67 -0
  396. package/templates/skills/hai-spec-implement/evaluation.md +18 -0
  397. package/templates/skills/hai-spec-implement/handoff.md +47 -0
  398. package/templates/skills/hai-spec-implement/templates.md +117 -0
  399. package/templates/skills/hai-spec-implement/workflow.md +56 -0
  400. package/templates/skills/hai-spec-review/SKILL.md +57 -0
  401. package/templates/skills/hai-spec-review/checklist.md +71 -0
  402. package/templates/skills/hai-storage/SKILL.md +31 -262
  403. package/templates/skills/hai-storage/reference.md +284 -0
  404. package/templates/skills/hai-ui/SKILL.md +62 -238
  405. package/templates/skills/hai-ui/guide.md +81 -0
  406. package/templates/skills/hai-ui/reference.md +705 -0
  407. package/templates/skills/hai-ui-design/SKILL.md +31 -0
  408. package/templates/skills/hai-ui-design/patterns.md +31 -0
  409. package/templates/skills/hai-ui-review/SKILL.md +32 -0
  410. package/templates/skills/hai-ui-review/checklist.md +29 -0
  411. package/templates/skills/hai-vecdb/SKILL.md +25 -267
  412. package/templates/skills/hai-vecdb/reference.md +325 -0
  413. package/templates/skills/opencode.json +0 -5
  414. package/dist/chunk-2H7UOFLK.js +0 -10
  415. package/dist/chunk-LANIDJC7.js +0 -659
  416. package/dist/chunk-LANIDJC7.js.map +0 -1
  417. package/dist/chunk-YV7WPUI6.js +0 -206
  418. package/dist/chunk-YV7WPUI6.js.map +0 -1
  419. package/dist/config-templates-S5RI2MC6.js +0 -5
  420. package/dist/config-templates-S5RI2MC6.js.map +0 -1
  421. package/dist/skill-templates-N5DU5FNX.js +0 -5
  422. package/dist/skill-templates-N5DU5FNX.js.map +0 -1
  423. package/dist/src-XJ6YXNAF.js +0 -15030
  424. package/dist/src-XJ6YXNAF.js.map +0 -1
  425. package/templates/apps/android-app/.env.hbs +0 -3
  426. package/templates/apps/android-app/messages/en-US.json +0 -24
  427. package/templates/apps/android-app/messages/zh-CN.json +0 -24
  428. package/templates/apps/android-app/src/lib/api.ts.hbs +0 -30
  429. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
  430. package/templates/apps/android-app/src/routes/+layout.ts +0 -7
  431. package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
  432. package/templates/apps/api/src/routes/+layout.svelte +0 -14
  433. package/templates/apps/api/src/routes/+page.svelte +0 -11
  434. package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
  435. package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
  436. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
  437. package/templates/base/src/app.css +0 -2
  438. package/templates/base/tsconfig.json +0 -8
@@ -0,0 +1,535 @@
1
+ # hai-iam — 详细用法
2
+
3
+ 从 [SKILL.md](SKILL.md) 选择主题后读取对应小节。代码片段展示 API 用法;省略的业务变量、schema、依赖与 return 上下文需由应用补齐。
4
+
5
+ 用户的 `checkPermission(userId, code)` / `getUserAuthorization` 仅查询主体授权,不含请求的 API Key scope;受保护请求使用 `iam.auth.verifyToken` 的已收敛会话权限。API Key 的完整配置、交集示例和撤销流程见对应小节。
6
+
7
+ 角色成员数使用 `getRoleUserCounts(roleIds)`,一次聚合返回 `HaiResult<Map<string, number>>`;成功时无关联为 0,失败必须展示错误,不能伪造零值。
8
+
9
+ 角色资料和权限必须通过 `createRole({ ..., permissionIds })` / `updateRole(id, { ..., permissionIds })` 一次事务保存,禁止在应用层并行增删权限模拟替换。空数组清空、未传保持、未知权限失败、重复 ID 去重;并发以最后提交的完整集合为准。自管事务提交后同步会话,外部事务由调用方提交后同步。
10
+
11
+ 管理界面自动创建角色时使用 `role_${core.id.generate()}` 生成 code,显示名称独立保存;不要通过移除中文、表情或折叠大小写生成标识。重命名不重建 code。
12
+
13
+ > `@h-ai/iam` 是统一的身份与访问管理模块,支持多种认证策略(密码/OTP/LDAP/API Key)、Bearer Token 管理和 RBAC 授权。依赖 `@h-ai/reldb`(持久化)、`@h-ai/cache`(会话 / OTP / 一次性票据)、`@h-ai/crypto`(密码哈希)、`@h-ai/audit`(审计日志)。
14
+
15
+ ## 运行环境
16
+
17
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端通过 `@h-ai/api-client` 的 typed client 调用 IAM API,例如 `apiClient.iam.auth.login()`。HTTP 契约统一来自 `@h-ai/api-contract`。
18
+
19
+ ## 依赖
20
+
21
+ | 模块 | 用途 | 是否必需 | 初始化要求 |
22
+ | -------------- | ------------------------------ | -------- | -------------------------- |
23
+ | `@h-ai/reldb` | 数据库(用户/角色/权限持久化) | **必需** | 需在 `iam.init()` 前初始化 |
24
+ | `@h-ai/cache` | 缓存(会话/OTP/一次性票据) | **必需** | 需在 `iam.init()` 前初始化 |
25
+ | `@h-ai/crypto` | 密码哈希 | 内部使用 | 自动初始化 |
26
+ | `@h-ai/audit` | 审计日志(账号、鉴别、密码、授权、API Key、会话关键事件) | 内部使用 | 可选;宿主初始化且 capture.enabled 开启时自动写入;未选用不采集,启用后的失败可观察 |
27
+
28
+ ## 适用场景
29
+
30
+ - 用户注册与登录(密码/OTP/LDAP/API Key)
31
+ - Bearer Token 认证与管理(TokenPair: accessToken + refreshToken)
32
+ - Token 刷新(rotation 策略)与吊销
33
+ - RBAC 角色/权限的创建、分配与检查
34
+ - API Key 管理(创建、吊销、验证)
35
+ - 密码重置流程
36
+ - 与 kit 集成的认证守卫
37
+ - 通过 `@h-ai/api-contract` 暴露 IAM HTTP contract
38
+
39
+ ## 使用步骤
40
+
41
+ ### 1. 配置
42
+
43
+ ```yaml
44
+ # config/_iam.yml
45
+ password:
46
+ minLength: 8
47
+ requireUppercase: true
48
+ requireNumber: true
49
+ session:
50
+ maxAge: 86400
51
+ refreshTokenMaxAge: 604800 # 7 天
52
+ sliding: true
53
+ singleDevice: false
54
+ login:
55
+ password: true
56
+ otp: false
57
+ register:
58
+ enabled: true
59
+ security:
60
+ maxLoginAttempts: 5
61
+ lockoutDuration: 900
62
+ rbac:
63
+ defaultRole: user
64
+ superAdminRole: super_admin
65
+ ```
66
+
67
+ 约定环境变量优先于 YAML,例如 `HAI_IAM_PASSWORD_MINLENGTH`、
68
+ `HAI_IAM_SESSION_MAXAGE`、`HAI_IAM_SECURITY_MAXLOGINATTEMPTS`。
69
+
70
+ ### 2. 初始化
71
+
72
+ ```typescript
73
+ import type { IamConfigSettingsInput } from '@h-ai/iam'
74
+ import { cache } from '@h-ai/cache'
75
+ import { core } from '@h-ai/core'
76
+ import { iam } from '@h-ai/iam'
77
+ import { reldb } from '@h-ai/reldb'
78
+
79
+ // 位于应用启动函数内;依赖初始化失败时立即返回,不能继续启动 IAM。
80
+ core.init({ configDir: './config' })
81
+ const db = await reldb.init({ type: 'sqlite', database: './data.db' })
82
+ if (!db.success)
83
+ return db
84
+ const cached = await cache.init({ type: 'memory' })
85
+ if (!cached.success)
86
+ return cached
87
+ const initialized = await iam.init(core.config.getOrThrow<IamConfigSettingsInput>('iam'))
88
+ if (!initialized.success)
89
+ return initialized
90
+ // 关闭时按 iam → cache → reldb 的反向依赖顺序执行。
91
+ ```
92
+
93
+ `iam.config` 返回的是脱敏后的配置快照;LDAP `url` / `bindPassword` 等敏感值会自动隐藏。
94
+
95
+ 基础初始化创建用户/RBAC 五张表;启用 API Key 登录时另创建 `hai_iam_api_keys`。
96
+
97
+ Token 和 OTP 存储在 cache 中(不落库)。session/refresh/index 位于同一用户分区版本文档,通过单键 CAS 提交。
98
+
99
+ ### 可选 LDAP 与通知
100
+
101
+ LDAP 默认关闭,Reach 不属于 IAM 的启动前置依赖。`ldapts` 和 `@h-ai/reach` 是可选 peer dependency,基础密码/RBAC 无需安装这两个包。
102
+
103
+ - 默认 LDAP 客户端:在服务端 workspace 执行 `pnpm add ldapts`,配置 `login.ldap: true` 与 `ldap.url/bindDn/bindPassword/searchBase`。`searchFilter` 默认 `(uid={{username}})`;AD 可使用 `(sAMAccountName={{username}})` 与对应 `usernameAttribute`。`ldaps://` 使用 TLS,`ldap://` + `useTls: true` 使用 StartTLS。`ldapClientFactory` 可注入返回 `bind/search/unbind` 和 HaiResult 的客户端,此时无需安装 ldapts。
104
+ - Reach 自动发送:应用安装、初始化 `@h-ai/reach`,在 `iam.init({ ...settings, reach })` 中注入一次。`otp_email/otp_sms` 模板使用 `{code}`;`password_reset/password_reset_sms` 使用 `{token}/{expiresAt}/{resetUrl}`。模板需在 IAM 初始化前配置,Provider 绑定实际邮件/短信通道。`passwordReset.mode` 选择 `email/phone`。
105
+ - 自定义发送:直接传 `onOtpSendEmail(email, code, context)`、`onOtpSendSms(phone, code, context)`、`onPasswordResetRequest(user, token, context)`,均返回 `Promise<void>`;context 包含 expiresAt/idempotencyKey,发送服务按该稳定键持久化去重,异常表示结果未知,不自动重发。可以只实现选用通道或与 Reach 混用,显式回调优先。检查发送服务结果,投递失败在回调内抛错;IAM 捕获并返回业务错误,不要吞掉发送失败。
106
+ - `otp` 必须有配置并开启所需通道;缺少回调或标准模板时 `iam.publicConfig` 不公布该入口。Reach 由应用管理生命周期,IAM 不负责初始化或关闭。Console 仅记录元数据。
107
+
108
+ 协议以 Modal 显示,关闭后保留输入。URL 方式必须为 HTTP(S) 且页面允许 iframe 嵌入;脚本被隔离禁用。`LoginForm/RegisterForm` 提供 `header/extraFields/footer`;`AuthPanel` 提供 `loginHeader/loginFields/loginFooter`、`registerHeader/registerFields/registerFooter`。额外字段由应用绑定状态,在自己的动作中处理,IAM 不接收未经定义的字段。
109
+
110
+ 密码登录接收 identifier/password,以及启用时的单用 captcha 证明;AuthPanel 将 LoginForm 的 username 映射为 identifier。会话期限来自服务端 session.maxAge/refreshTokenMaxAge,不增加未实现的登录偏好字段或持久会话选择框。
111
+
112
+
113
+ `captcha.enabled` 默认关闭;开启后共享 Cache 保存自托管 ALTCHA 的签名秘密/用途/相对TTL,iam.auth.createCaptcha({operation}) 只返回 {id,challenge}。标准 AuthPanel/createAuthActions 自动求解并通过 captcha 字段提交;自定义表单用 altcha/lib 求解。login/register/sendOtp/passwordReset/renewPassword 严格用途绑定,验证后原子单用,失败也需新挑战;API Key 机器鉴别和管理员创建/邀请不额外加人机交互。sendOtp(identifier,{captcha})、requestPasswordReset(identifier,{captcha}) 第二参数不能在审计/HTTP包装层丢失。TTL默认120秒、cost5000、maxCounter100,可按设备设置;计算工作量不保证是真人。Cache 故障拒绝,重初始化不延期,证明不持久化在浏览器或进入日志。标准 Kit 须导出 POST /api/auth/captcha,Serv 已提供;公开配置只含 captcha.enabled。错误030/031/032/033分别需验证/无效已用过期/关闭/不可用。
114
+
115
+ ## 核心 API
116
+
117
+ ### 认证 — `iam.auth`
118
+
119
+ | 方法 | 签名 | 说明 |
120
+ | ------------------ | -------------------------------------------------------------- | ------------------------------------------------ |
121
+ | `login` | `({ identifier, password }) => Promise<HaiResult<AuthResult>>` | 密码登录(用户名/邮箱/手机号) |
122
+ | `loginWithOtp` | `({ identifier, code }) => Promise<HaiResult<AuthResult>>` | OTP 验证码登录 |
123
+ | `loginWithLdap` | `({ username, password }) => Promise<HaiResult<AuthResult>>` | LDAP 登录 |
124
+ | `loginWithApiKey` | `({ key }) => Promise<HaiResult<AuthResult>>` | API Key 登录 |
125
+ | `sendOtp` | `(identifier) => Promise<HaiResult<{ expiresAt }>>` | 发送 OTP 验证码 |
126
+ | `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证会话并读取当前用户状态及授权;必要状态读取失败拒绝 |
127
+ | `logout` | `(token) => Promise<HaiResult<void>>` | 登出 |
128
+ | `registerAndLogin` | `(options: RegisterOptions) => Promise<HaiResult<AuthResult>>` | 注册并登录(一站式) |
129
+
130
+ **AuthResult**(已改造为 TokenPair):
131
+
132
+ ```typescript
133
+ interface AuthResult {
134
+ user: User
135
+ tokens: TokenPair
136
+ roles: string[]
137
+ permissions: string[]
138
+ agreements?: AgreementDisplay
139
+ }
140
+
141
+ interface TokenPair {
142
+ accessToken: string
143
+ refreshToken: string
144
+ expiresIn: number
145
+ tokenType: 'Bearer'
146
+ }
147
+ ```
148
+
149
+ ### 会话管理 — `iam.session`
150
+
151
+ | 方法 | 签名 | 说明 |
152
+ | ------------------- | ----------------------------------------------------------------------- | ---------------------------------------- |
153
+ | `create` | `({ userId, username, roles, source? }) => Promise<HaiResult<Session>>` | 创建会话(返回含 TokenPair 的 session) |
154
+ | `get` | `(token) => Promise<HaiResult<Session \| null>>` | 获取会话(滑动续期自动延长) |
155
+ | `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证令牌 |
156
+ | `update` | `(token, data, expectedVersion) => Promise<HaiResult<void>>` | 更新会话字段(浅合并 data) |
157
+ | `delete` | `(token) => Promise<HaiResult<void>>` | 删除会话 |
158
+ | `deleteByUserId` | `(userId) => Promise<HaiResult<number>>` | 删除用户所有会话(返回删除数量) |
159
+ | `refresh` | `(refreshToken) => Promise<HaiResult<TokenPair>>` | 刷新 Token(rotation:旧 Token 失效) |
160
+ | `revokeRefresh` | `(refreshToken) => Promise<HaiResult<void>>` | 吊销 refreshToken |
161
+ | `patchUserSessions` | `(userId, updates: SessionFieldUpdates) => Promise<HaiResult<void>>` | 批量更新用户所有会话的 roles/permissions |
162
+
163
+ **Token 刷新(rotation 策略)**:
164
+
165
+ - `refresh()` 会删除旧的 accessToken + refreshToken
166
+ - 创建全新的 session,返回新的 TokenPair
167
+ - 旧 refreshToken 立即失效,防止重放攻击
168
+
169
+ ### 一次性票据 — `iam.ticket`
170
+
171
+ 短期、一次性能力票据,用于无法携带 Bearer Token 的通道(如 WebSocket URL)。密码学安全随机、TTL、原子单次消费、用途绑定。
172
+
173
+ | 方法 | 签名 | 说明 |
174
+ | --------- | --------------------------------------------------------------------------------------- | ---------------------------------- |
175
+ | `issue` | `({ subjectId, purpose, grant?, ttlMs? }) => Promise<HaiResult<{ ticket, expiresAt }>>` | 签发(默认 30s) |
176
+ | `consume` | `(ticket, { purpose? }?) => Promise<HaiResult<{ subjectId, purpose, grant }>>` | 原子消费(单次有效,用途不符拒绝) |
177
+
178
+ ```ts
179
+ const issued = await iam.ticket.issue({ subjectId, purpose: 'ai-audio', grant: { operation: 'transcribe' } })
180
+ const consumed = await iam.ticket.consume(ticket, { purpose: 'ai-audio' }) // 并发消费仅一个成功
181
+ ```
182
+
183
+ 错误码:`TICKET_INVALID`(hai:iam:110) / `TICKET_EXPIRED`(hai:iam:111) / `TICKET_ISSUE_FAILED`(hai:iam:112)。
184
+
185
+ ### 用户管理 — `iam.user`
186
+
187
+ | 方法 | 签名 | 说明 |
188
+ | --------------------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------------- |
189
+ | `register` | `(input) => Promise<HaiResult<RegisterResult>>` | 用户注册 |
190
+ | `getUser` | `(id, options?) => Promise<HaiResult<User \| null>>` | 获取用户(可选 include: ['roles']) |
191
+ | `getCurrentUser` | `(token) => Promise<HaiResult<User>>` | 通过令牌获取当前用户 |
192
+ | `updateCurrentUser` | `(token, data: UpdateCurrentUserInput) => Promise<HaiResult<User>>` | 当前用户修改个人资料(白名单字段) |
193
+ | `getUsers` | `(ids, options?) => Promise<HaiResult<User[]>>` | 批量公开用户,上限 1000;去重输入顺序、缺失省略、可 include roles |
194
+ | `listUsers` | `(options?: ListUsersOptions) => Promise<HaiResult<PaginatedResult<User>>>` | 用户列表(分页 + 搜索 + roleCode 分页前筛选 + 白名单排序) |
195
+ | `updateUser` | `(id, data) => Promise<HaiResult<User>>` | 更新用户信息 |
196
+ | `deleteUser` | `(id) => Promise<HaiResult<void>>` | 删除用户(自动清除会话) |
197
+ | `changePassword` | `(id, old, new) => Promise<HaiResult<void>>` | 修改密码(自动清除会话) |
198
+ | `changeCurrentUserPassword` | `(token, old, new) => Promise<HaiResult<void>>` | 当前用户修改密码(通过令牌识别) |
199
+ | `requestPasswordReset` | `(email) => Promise<HaiResult<void>>` | 请求密码重置(防枚举) |
200
+ | `confirmPasswordReset` | `(token, newPassword) => Promise<HaiResult<void>>` | 确认密码重置(自动清除会话) |
201
+ | `adminResetPassword` | `(userId, newPassword) => Promise<HaiResult<void>>` | 管理员直接重置密码 |
202
+ | `validatePassword` | `(password) => HaiResult<void>` | 验证密码强度(**同步方法**) |
203
+
204
+ ### RBAC 授权 — `iam.authz`
205
+
206
+ | 方法 | 签名 | 说明 |
207
+ | --------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
208
+ | `checkPermission` | `(userId, permission) => Promise<HaiResult<boolean>>` | 检查权限(超管自动通过,支持通配符) |
209
+ | `getUserAuthorization` | `(userId) => Promise<HaiResult<{roles: string[], permissions: string[]}>>` | 当前角色及权限代码,单条 JOIN 冷读与共享代际缓存;失效不确定时安全回源 |
210
+ | `getUserPermissions` | `(userId) => Promise<HaiResult<Permission[]>>` | 获取用户权限列表(通过角色聚合去重) |
211
+ | `getUserRoles` | `(userId) => Promise<HaiResult<Role[]>>` | 获取用户角色列表 |
212
+ | `getUserRolesForMany` | `(userIds) => Promise<HaiResult<Map<string, Role[]>>>` | 批量获取多用户角色(避免 N+1) |
213
+ | `assignRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 分配角色给用户 |
214
+ | `removeRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 移除用户角色 |
215
+ | `syncRoles` | `(userId, roleIds, tx?) => Promise<HaiResult<void>>` | 同步用户角色(替换为目标列表) |
216
+ | `createRole` | `({ code, name, description? }, tx?) => Promise<HaiResult<Role>>` | 创建角色 |
217
+ | `getRole` | `(roleId) => Promise<HaiResult<Role \| null>>` | 获取角色 |
218
+ | `getRoleByCode` | `(code) => Promise<HaiResult<Role \| null>>` | 根据代码获取角色 |
219
+ | `getAllRoles` | `(options?) => Promise<HaiResult<PaginatedResult<Role>>>` | 获取所有角色(分页) |
220
+ | `updateRole` | `(roleId, data, tx?) => Promise<HaiResult<Role>>` | 更新角色 |
221
+ | `deleteRole` | `(roleId, tx?) => Promise<HaiResult<void>>` | 删除角色(级联清理关联 + 会话同步) |
222
+ | `createPermission` | `({ code, name, type?, resource?, action? }, tx?) => Promise<HaiResult<Permission>>` | 创建权限 |
223
+ | `getPermission` | `(permissionId) => Promise<HaiResult<Permission \| null>>` | 获取权限 |
224
+ | `getPermissionByCode` | `(code) => Promise<HaiResult<Permission \| null>>` | 根据代码获取权限 |
225
+ | `getAllPermissions` | `(options?: PermissionQueryOptions) => Promise<HaiResult<PaginatedResult<Permission>>>` | 获取所有权限(分页 + 类型/关键字筛选) |
226
+ | `deletePermission` | `(permissionId, tx?) => Promise<HaiResult<void>>` | 删除权限(级联清理关联 + 会话同步) |
227
+ | `assignPermissionToRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 分配权限给角色 |
228
+ | `removePermissionFromRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 移除角色权限 |
229
+ | `getRolePermissions` | `(roleId) => Promise<HaiResult<Permission[]>>` | 获取角色的权限列表 |
230
+ | `getRolePermissionsForMany` | `(roleIds) => Promise<HaiResult<Map<string, Permission[]>>>` | 批量获取多角色权限(避免 N+1) |
231
+
232
+ **通配符规则**:`admin:*` 匹配 `admin:read`、`admin:write` 等。超管角色自动拥有所有权限。
233
+
234
+ ### API Key 管理 — `iam.apiKey`
235
+
236
+ 先在初始化配置中启用 API Key。它是用户的受限凭据,不能独立授予用户未拥有的权限:**有效权限 = 当前主体权限 ∩ scopes**。scopes 是与权限 code 相同的字符串,不是角色名、菜单名称或资源 ID;不会创建权限,也不会替代租户/记录归属校验。
237
+
238
+ ```yaml
239
+ # config/_iam.yml
240
+ login:
241
+ apikey: true
242
+ apikey:
243
+ maxKeysPerUser: 10
244
+ defaultExpirationDays: 30
245
+ ```
246
+
247
+ 假设主体拥有 `record:read`、`record:write`、`user:read`,不同 scopes 的结果如下(主体授权改变后重新计算):
248
+
249
+ | scopes | 有效权限 | roles |
250
+ | ------------------------------ | ----------------------------- | ------------ |
251
+ | 未传或 `[]` | 无操作权限,仅可识别身份 | `[]` |
252
+ | `['record:read']` | `record:read` | `[]` |
253
+ | `['record:*']` | `record:read`、`record:write` | `[]` |
254
+ | `['record:read', 'user:read']` | 两个指定权限 | `[]` |
255
+ | `['missing:read']` | 主体未拥有,因此无权限 | `[]` |
256
+ | `['*']` | 主体当前拥有的全部权限 | 保留主体角色 |
257
+
258
+ `record:*` 匹配冒号后的全部层级,不支持任意正则或 `record:r*` 等中间通配符。主体仅拥有 `record:read` 时,`['record:*']` 仍只得到读取权限;主体持有通配符时,交集保留较窄范围。受限 Key 没有角色入口,使用 permission 守卫;显式 `*` 是宽授权,按实际需要选择。
259
+
260
+ 以下为创建 → Key 登录 → Bearer 会话验证与操作授权 → 列表 → 撤销的服务端示例。先检查所有 HaiResult;每个管理入口还要校验本人归属或管理员权限,IAM 不知道业务的调用者身份。
261
+
262
+ ```ts
263
+ import { err, HaiCommonError, ok } from '@h-ai/core'
264
+ import { iam } from '@h-ai/iam'
265
+
266
+ async function createReadKey(userId: string) {
267
+ // 调用前校验管理权限或本人归属;scopes 使用权限 code。
268
+ const created = await iam.apiKey.createApiKey(userId, {
269
+ name: 'Read client',
270
+ expirationDays: 30,
271
+ scopes: ['record:read'],
272
+ })
273
+ if (!created.success)
274
+ return created
275
+ // rawKey 仅本次返回;通过安全渠道展示一次,不写日志。
276
+ return created
277
+ }
278
+
279
+ async function apiKeyLogin(rawKey: string) {
280
+ // 通常由客户端取得 Key 后调用;服务器内部验证并绑定 Key。
281
+ return iam.auth.loginWithApiKey({ key: rawKey })
282
+ }
283
+
284
+ async function requireRecordRead(accessToken: string, deniedMessage: string) {
285
+ // deniedMessage 由应用 i18n 提供;先读取当前且已收敛 scope 的会话。
286
+ const verified = await iam.auth.verifyToken(accessToken)
287
+ if (!verified.success)
288
+ return verified
289
+ const allowed = verified.data.permissions.some(code =>
290
+ code === '*' || code === 'record:read'
291
+ || (code.endsWith(':*') && 'record:read'.startsWith(code.slice(0, -1))),
292
+ )
293
+ if (!allowed)
294
+ return err(HaiCommonError.FORBIDDEN, deniedMessage)
295
+ // 这里只确认操作权限;业务层还须检查具体记录的数据归属。
296
+ return ok({ userId: verified.data.userId })
297
+ }
298
+
299
+ async function listMyKeys(userId: string) {
300
+ return iam.apiKey.listApiKeys(userId, {
301
+ page: 1,
302
+ pageSize: 20,
303
+ search: 'Read',
304
+ enabled: true,
305
+ })
306
+ }
307
+
308
+ async function revokeKey(keyId: string) {
309
+ // 调用前校验本人归属或管理权限。
310
+ return iam.apiKey.revokeApiKey(keyId)
311
+ }
312
+ ```
313
+
314
+ 创建后只有 `rawKey` 能用于 Key 登录;`apiKey.id` 是管理 ID,`keyPrefix` 仅用于显示/检索,均不能代替 rawKey。`verifyApiKey(rawKey)` 仅验证 Key 并返回元数据,不能代替用户状态、scopes 与操作授权。
315
+
316
+ Key 登录会话绑定 Key ID。刷新使用普通 `iam.session.refresh`,不会丢失该绑定或扩大 scopes。撤销成功后,已有 accessToken 的 get/verify 以及 refreshToken 的 refresh 都会失败;过期、禁用及必要状态读取失败同样拒绝。角色撤权由 `iam.auth.verifyToken` 读取当前授权生效。改 scope 使用创建新 Key 后撤销旧 Key,当前 API 不提供 scope 更新操作。
317
+
318
+ `expirationDays: 0` 表示不设过期时间,省略时使用 `apikey.defaultExpirationDays`;需要限时访问时显式设置。明文 Key 和 Token 不进入 URL、日志、公开配置或普通 localStorage。
319
+
320
+ | 方法 | 签名 | 说明 |
321
+ | -------------- | ---------------------------------------------------------------------------------- | ------------------------------------ |
322
+ | `createApiKey` | `(userId, options: CreateApiKeyOptions) => Promise<HaiResult<CreateApiKeyResult>>` | 创建 API Key(明文密钥仅返回一次) |
323
+ | `listApiKeys` | `(userId, options?) => Promise<HaiResult<PaginatedResult<ApiKey>>>` | 分页列出用户的 API Key |
324
+ | `getApiKey` | `(keyId) => Promise<HaiResult<ApiKey \| null>>` | 获取 API Key 详情 |
325
+ | `revokeApiKey` | `(keyId) => Promise<HaiResult<void>>` | 吊销/删除 API Key |
326
+ | `verifyApiKey` | `(rawKey) => Promise<HaiResult<ApiKey>>` | 验证 API Key 并返回实体(含用户 ID) |
327
+
328
+ ## HTTP API 契约
329
+
330
+ IAM HTTP API 统一由 `@h-ai/api-contract` 的 `apiContract.iam` 定义,由 `@h-ai/serv/features/iam` 绑定到本模块。
331
+
332
+ ```typescript
333
+ import { apiClient } from '@h-ai/api-client'
334
+
335
+ const result = await apiClient.iam.auth.login({ identifier: username, password })
336
+ ```
337
+
338
+ ## 错误码 — `HaiIamError`
339
+
340
+ | 错误码 | code | 说明 |
341
+ | --------------------------------------- | ------------- | ---------------- |
342
+ | `HaiIamError.AUTH_FAILED` | `hai:iam:001` | 认证失败 |
343
+ | `HaiIamError.INVALID_CREDENTIALS` | `hai:iam:002` | 凭证无效 |
344
+ | `HaiIamError.USER_NOT_FOUND` | `hai:iam:003` | 用户不存在 |
345
+ | `HaiIamError.ACCOUNT_UNAVAILABLE` | `hai:iam:004` | 账号状态或有效期不允许访问 |
346
+ | `HaiIamError.USER_LOCKED` | `hai:iam:005` | 用户已锁定 |
347
+ | `HaiIamError.USER_ALREADY_EXISTS` | `hai:iam:006` | 用户已存在 |
348
+ | `HaiIamError.PASSWORD_EXPIRED` | `hai:iam:007` | 密码已过期 |
349
+ | `HaiIamError.PASSWORD_POLICY_VIOLATION` | `hai:iam:008` | 密码不符合策略 |
350
+ | `HaiIamError.PASSWORD_REUSE_NOT_ALLOWED` | `hai:iam:034` | 新旧密码相同 |
351
+ | `HaiIamError.OTP_INVALID` | `hai:iam:009` | 验证码无效 |
352
+ | `HaiIamError.NOT_INITIALIZED` | `hai:iam:010` | 未初始化 |
353
+ | `HaiIamError.OTP_RESEND_TOO_FAST` | `hai:iam:011` | 发送过于频繁 |
354
+ | `HaiIamError.LOGIN_DISABLED` | `hai:iam:012` | 登录方式已禁用 |
355
+ | `HaiIamError.REGISTER_DISABLED` | `hai:iam:013` | 注册已禁用 |
356
+ | `HaiIamError.STRATEGY_NOT_SUPPORTED` | `hai:iam:014` | 认证策略不支持 |
357
+ | `HaiIamError.APIKEY_INVALID` | `hai:iam:015` | API Key 无效 |
358
+ | `HaiIamError.APIKEY_EXPIRED` | `hai:iam:016` | API Key 已过期 |
359
+ | `HaiIamError.APIKEY_DISABLED` | `hai:iam:017` | API Key 已禁用 |
360
+ | `HaiIamError.APIKEY_NOT_FOUND` | `hai:iam:018` | API Key 不存在 |
361
+ | `HaiIamError.RESET_TOKEN_INVALID` | `hai:iam:019` | 重置令牌无效 |
362
+ | `HaiIamError.RESET_TOKEN_EXPIRED` | `hai:iam:020` | 重置令牌已过期 |
363
+ | `HaiIamError.RESET_TOKEN_MAX_ATTEMPTS` | `hai:iam:021` | 重置验证次数超限 |
364
+ | `HaiIamError.OTP_EXPIRED` | `hai:iam:022` | 验证码已过期 |
365
+ | `HaiIamError.SESSION_NOT_FOUND` | `hai:iam:101` | 会话不存在 |
366
+ | `HaiIamError.SESSION_EXPIRED` | `hai:iam:102` | 会话已过期 |
367
+ | `HaiIamError.SESSION_INVALID` | `hai:iam:103` | 会话无效 |
368
+ | `HaiIamError.SESSION_CREATE_FAILED` | `hai:iam:104` | 会话创建失败 |
369
+ | `HaiIamError.TOKEN_EXPIRED` | `hai:iam:105` | 令牌已过期 |
370
+ | `HaiIamError.TOKEN_INVALID` | `hai:iam:106` | 令牌无效 |
371
+ | `HaiIamError.TOKEN_REFRESH_FAILED` | `hai:iam:107` | 令牌刷新失败 |
372
+ | `HaiIamError.PERMISSION_DENIED` | `hai:iam:201` | 权限不足 |
373
+ | `HaiIamError.ROLE_NOT_FOUND` | `hai:iam:202` | 角色不存在 |
374
+ | `HaiIamError.PERMISSION_NOT_FOUND` | `hai:iam:203` | 权限不存在 |
375
+ | `HaiIamError.ROLE_ALREADY_EXISTS` | `hai:iam:204` | 角色已存在 |
376
+ | `HaiIamError.PERMISSION_ALREADY_EXISTS` | `hai:iam:205` | 权限已存在 |
377
+ | `HaiIamError.LDAP_CONNECTION_FAILED` | `hai:iam:301` | LDAP 连接失败 |
378
+ | `HaiIamError.LDAP_BIND_FAILED` | `hai:iam:302` | LDAP 绑定失败 |
379
+ | `HaiIamError.LDAP_SEARCH_FAILED` | `hai:iam:303` | LDAP 搜索失败 |
380
+ | `HaiIamError.REPOSITORY_ERROR` | `hai:iam:401` | 存储层操作错误 |
381
+ | `HaiIamError.NOT_FOUND` | `hai:iam:402` | 资源不存在 |
382
+ | `HaiIamError.CONFLICT` | `hai:iam:403` | 资源冲突 |
383
+ | `HaiIamError.FORBIDDEN` | `hai:iam:501` | 禁止访问 |
384
+ | `HaiIamError.INVALID_ARGUMENT` | `hai:iam:502` | 参数无效 |
385
+ | `HaiIamError.CONFIG_ERROR` | `hai:iam:901` | 配置错误 |
386
+ | `HaiIamError.INTERNAL_ERROR` | `hai:iam:999` | 内部错误 |
387
+
388
+ ## 常见模式
389
+
390
+ ### 与 kit 集成(hooks.server.ts)
391
+
392
+ ```typescript
393
+ import { iam } from '@h-ai/iam'
394
+ import { kit } from '@h-ai/kit'
395
+
396
+ // 应用启动时已成功初始化 IAM。
397
+ export const handle = kit.createHandle({
398
+ auth: {
399
+ verifyToken: async (token) => {
400
+ const result = await iam.auth.verifyToken(token)
401
+ return result.success ? result.data : null
402
+ },
403
+ operations: () => iam.auth,
404
+ loginUrl: '/auth/login',
405
+ protectedPaths: ['/admin/*', '/api/*'],
406
+ publicPaths: ['/api/auth/*', '/api/public/*'],
407
+ },
408
+ })
409
+ ```
410
+
411
+ ### 客户端认证流程
412
+
413
+ ```typescript
414
+ // 登录 → 保存 Token
415
+ const login = await apiClient.iam.auth.login({ identifier: username, password })
416
+ if (login.success) {
417
+ await apiClient.auth.setTokens(login.data.tokens)
418
+ }
419
+
420
+ // 获取当前用户
421
+ const me = await apiClient.iam.auth.currentUser()
422
+ if (me.success) {
423
+ // 返回 User + 服务端当前会话的 roles/permissions。
424
+ // 身份资料与访问范围可在状态层拆开,但不要持久化权限快照。
425
+ const { roles, permissions, ...user } = me.data
426
+ }
427
+
428
+ // Token 过期时,api-client 自动调用 refreshPath 刷新
429
+ // 刷新失败时,清除 Token 并跳转登录页
430
+
431
+ // 登出:本地清理与服务端撤销确认分别处理,调用方继续检查返回结果。
432
+ async function signOut() {
433
+ const result = await apiClient.iam.auth.logout({})
434
+ await apiClient.auth.clear()
435
+ return result
436
+ }
437
+ ```
438
+
439
+ ### 自动会话失效
440
+
441
+ 以下操作自动清除受影响用户的所有活跃会话:
442
+
443
+ - `deleteUser` / `updateUser({ accountStatus: 'dormant' })` / `changePassword` / `confirmPasswordReset`
444
+ - `assignRole` / `removeRole` / `deleteRole` 会同步更新活跃会话中的角色列表
445
+
446
+ ## 配置驱动认证页面
447
+
448
+ ### 配置 Markdown 协议
449
+
450
+ 协议支持 `agreements.userAgreementMarkdown` 与 `privacyPolicyMarkdown` 直接从配置加载;YAML 的 `|` 保留 Markdown 段落和换行。弹窗复用安全 MarkdownRenderer,关闭原始 HTML、脚本和代码复制工具。同主题同时提供正文与 URL 时优先正文,URL 可作为额外查看入口;只有 URL 时使用隔离 iframe。
451
+
452
+ ```yaml
453
+ agreements:
454
+ showOnRegister: true
455
+ showOnLogin: false
456
+ userAgreementMarkdown: |
457
+ # 用户协议
458
+
459
+ **账户使用条款**
460
+
461
+ - 请妥善保管账户凭据。
462
+ privacyPolicyMarkdown: |
463
+ # 隐私政策
464
+
465
+ 注册资料用于账户管理,会话信息用于身份认证。
466
+ # 可选 URL;同主题同时配置 Markdown 和 URL 时,弹窗优先显示 Markdown。
467
+ # userAgreementUrl: https://example.com/terms
468
+ # privacyPolicyUrl: https://example.com/privacy
469
+ ```
470
+
471
+ 服务端通过 `await iam.getPublicConfig()` 提供包含数据库规则的白名单配置,先检查 `success`;失败不提供伪完整策略,同步 `iam.publicConfig` 只含文件快照,或通过客户端的 `client.iam.auth.publicConfig()` 标准 HTTP contract 获取。只展示已开启且具备依赖的登录方式;LDAP 为可选集成,见上方配置与安装说明。
472
+
473
+ | 配置 | 用途 |
474
+ | ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
475
+ | `login.password / otp / ldap` | 独立启用密码、验证码、企业账号 |
476
+ | `otp.email / sms` | 独立启用邮件、短信 |
477
+ | `register.enabled / defaultAccountStatus` | 自助注册及新账户状态 |
478
+ | `passwordReset.enabled / mode / resetUrl` | 启用重置、选择 email/phone 渠道、配置重置页面绝对地址 |
479
+ | `agreements.showOnRegister / showOnLogin` | 注册/登录协议弹窗显示;链接只接受 HTTP(S) |
480
+ | `ui.variant / title / subtitle / brandTitle / brandText / logoUrl / illustrationUrl` | 卡片/分栏布局、标题、品牌、Logo 和配图 |
481
+
482
+ 选择 Reach 时,在 reldb/cache/Reach 初始化成功后使用 `iam.init({ ...settings, reach })` 自动接入 Reach 的 `otp_email`、`otp_sms`、`password_reset`、`password_reset_sms` 模板。模板绑定对应邮件/短信 Provider;显式发送回调可覆盖默认行为。Console Provider 只记录元数据,实际收信需配置 SMTP、短信或 HTTP 通知服务。发送失败返回错误,不会伪报已发送。
483
+
484
+ SvelteKit 使用 `kit.auth.createApi(iam)` 导出标准 handler,匿名端点白名单使用 `kit.auth.publicPaths`。客户端通过 `kit.client.createAuthActions()` 连接 Cookie 端点;独立 HTTP 客户端通过 `apiClient.createAuthActions(client)` 连接 IAM contract 并自动保存 Token 到已配置安全存储。将配置及动作直接传入 `AuthPanel`,完成登录、注册、找回及重置;单独使用 `LoginForm` 可传 `methods`、`onsendotp`、`resendInterval`,密码表单可传完整 `passwordPolicy`。
485
+
486
+ 管理员使用 `iam.user.createUser()` 建用户,调用端必须校验管理员权限;关闭自助注册不影响管理端。注册与密码登录开关独立,`registerAndLogin` 在注册完成后创建会话;默认休眠账号不会创建会话;`defaultAccountStatus` 默认 active,可设为 dormant 等待管理员激活。
487
+
488
+ ### 分布式安全与边界
489
+
490
+ 所有节点共享同一数据库和 Redis;Memory 仅用于单进程测试。OTP 的验证码、尝试次数和冷却期保存在同一身份 key,通过 CAS 发布和消费;冲突拒绝旧代,消费/耗尽保留冷却期。密码重置的摘要、有效期、尝试次数和 resetVersion 保存在用户数据库行,策略失败计次,最终一条条件 SQL 同时改密、消费令牌并推进 credentialVersion;新令牌、后续尝试、停用或到期都会阻止迟到提交。发送重置请求只推进 resetVersion,不撤销现有会话;成功改密才推进 credentialVersion。认证策略保存已验证的凭据代际,建立会话/认证/刷新均校验共享代际缓存或安全回源的当前状态;缓存批量清理失败也不能使旧会话有效。通知只在成功发布后调用,第三参数为 { expiresAt, idempotencyKey },应用及 Reach Provider 必须用该稳定键持久化去重。异常/超时可能已送达,返回结果未知,不自动重发或回滚已发布凭据;没有接收端幂等保障时不能承诺外部副作用恰好一次。
491
+
492
+ 可选字段(如 `ldap`)若尚未写入 YAML,请先声明该字段,或通过 `HAI_IAM` 的 JSON 值整体覆盖模块配置;已有字段支持 `HAI_IAM_LOGIN_OTP` 等环境变量覆盖。
493
+
494
+ 标准 HTTP 入口:`GET /auth/config`、`POST /auth/login/ldap`、`POST /auth/password-reset/request`、`POST /auth/password-reset/confirm`;验证码继续使用 `/auth/otp/send` 与 `/auth/login/otp`。安全错误:`SECURITY_BUSY (hai:iam:023)`、`PASSWORD_RESET_DISABLED (hai:iam:024)`。
495
+
496
+ ## 相关 Skills
497
+
498
+ - `hai-build`:模块初始化顺序(db → cache → iam)
499
+ - `hai-kit`:SvelteKit 集成(Bearer Token + 契约处理)
500
+ - `hai-api-client`:客户端契约调用
501
+ - `hai-reldb`:底层数据存储
502
+ - `hai-cache`:会话、OTP 状态和一次性票据
503
+ - `hai-crypto`:密码哈希(内部依赖)
504
+ - `hai-ui`:IAM 场景组件(LoginForm/RegisterForm 等)
505
+
506
+ 会话与 refresh 消费记录统一保存在 `hai:iam:sessions:{SHA256(userId)}` 版本化文档,不再使用独立 token 映射/索引。Bearer token 携带不可作为凭据的分区提示和 256 位随机值;客户端必须视为不透明字符串。`Session.version` 用于轮换提交校验。Memory 仅用于单进程测试,生产所有节点共享 Redis(含 Cluster)。并发冲突从最新文档重试,耗尽返回 CONFLICT;旧 refresh 不会产生第二个后继。若响应丢失后结果不明,调用方按失败关闭并重新登录,不假定旧凭据仍有效;全部会话仍受同一用户状态索引管理。
507
+
508
+ `iam.session.update(token, patch, expectedVersion)` 必须传入读取到的 Session.version。旧版本返回 CONFLICT,已撤销 token 返回 SESSION_NOT_FOUND;不会将旧内容重新写回或覆盖新会话。仅滑动时间戳更新不改变内容版本,允许正常并行请求。批量权限更新绑定首次读取的 token/版本集合,不将迟到数据应用到后续登录;遇到较新内容版本失败,调用方重新查询授权后再提交。
509
+
510
+ `create({ maxAge })` 可覆盖配置默认时长(正安全整数秒),该值持久化为 `Session.maxAge`;签发和刷新返回的 `expiresIn` 均使用它。滑动续期按该时长延长 access,返回已成功提交的 `lastActiveAt/expiresAt`;迟到请求只延长时间,不缩短较新续期。显式 `update` 的有效日期补丁完整生效并产生新内容版本。客户端根据本次返回的 `expiresIn` 安排刷新,不能硬编码配置默认值。
511
+
512
+ 未启用额外期限时,access 与 refresh 独立到期:access 过期仍可凭有效 refresh 轮换,刷新不先要求旧 access 认证。`session.idleTimeout`、`absoluteTimeout` 以秒配置,0关闭;启用后 access 与 refresh 均检查,刷新保留首次登录时间。成功的 access 认证请求算活动;自动 refresh 仅换票,不延长闲置期限。后台业务轮询仍需按项目设计。`maxSessions: 0` 不限,否则在用户分区 CAS 原子限制有效会话数;`singleDevice: true` 默认顶旧保新,`singleDeviceMode: 'reject'` 保留旧会话并拒绝新登录。删除 access、logoutAll、单设备替换均撤销对应 refresh;`revokeRefresh` 保留仍有效的 access。
513
+
514
+ 账号生命周期仅使用必填 `accountStatus`(active/dormant/closed),无 User.enabled;列表过滤/排序同名,注册 defaultAccountStatus 默认 active、可设 dormant。beta.1 迁移前停旧写入并备份数据库;v6 将原停用 active 账号转 dormant 后删除旧列,镜像回滚不能恢复数据库。账号默认 `permanent/active`;管理端可配置 `temporary` 与 `accountExpiresAt`,或设 `accountStatus` 为 `dormant/closed`。状态/有效期变化推进持久凭据代际,重新激活不恢复旧会话。`accounts.dormantAfterDays: 0` 关闭自动休眠;启用后按 `scanIntervalSeconds` 扫描最后成功登录/创建时间,也可从可信后台调用 `iam.user.scanInactiveAccounts()`。公开注册不能接受管理字段;`closed` 保留记录,个人数据注销由项目处理。
515
+
516
+ `password.minCharacterClasses` 支持任意 N 类字符;创建/改密/重置共用服务端强度校验。`expirationDays: 0` 关闭期限;`preventSamePassword` 可独立启用,期限或首次强改时也拒绝旧密码。`requireInitialChange` 只约束管理端初始/重置密码,关闭后不受历史标记约束。首次/过期用户用 `iam.user.renewPassword({identifier,password,newPassword})` 完成原密码验证与换密后重新登录,不能先授予普通会话。AuthPanel 与标准 Kit/Serv 端点提供该流程。
517
+
518
+ `security.lockoutEnabled` 可关闭失败锁定;`failureWindow` 为首次失败起的固定窗口秒数(默认86400),并用 `maxLoginAttempts/lockoutDuration` 配置阈值/锁定时间。共享 DB 原子计次。`initialCredentials.enabled` 开启 `iam.user.inviteUser()`,只发送现有 passwordReset 短期单次设密邀请,随机初始秘密不外发;需要重置通知就绪。投递未知保留账号/已发布凭据并返回失败,先核对后恢复,不自动重发或删除账号。
519
+
520
+ `loginAccess.enabled` 配置 `allowIps/denyIps`(IP/CIDR,拒绝优先)、IANA `timezone` 和 `windows[{days,start,end}]`(周一1至周日7,HH:mm,含起不含止,跨午夜)。登录第二参 `AuthRequestContext` 必须来自可信宿主;不能使用任意转发头/JSON IP。地址规则启用但缺少可信地址时拒绝。`reauthentication.enabled/operations/ttlSeconds` 控制敏感操作证明;`reauthenticate` 验证原密码,`checkSensitiveOperation` 在原有权限/资源检查后原子消费短期单次票据,绑定用户/会话/操作。Serv 标准 IAM 写操作和 admin 守卫用 `x-hai-reauthentication`,Kit 业务用 `kit.guard.requireSensitive`;证明不提升权限且只短暂保存在内存。
521
+
522
+
523
+ 会话仓库将缓存读取/提交失败返回为失败 HaiResult,不解释为会话不存在或过期,也不执行清理补偿。用户会话及索引共用一个文档,批量授权更新不再逐 token 调用 get/ttl 或维护第二份索引;只按已确认的绝对期限清理。故障恢复后可重新读取并重试更新/退出,logoutAll 仍覆盖原有全部记录。结构损坏同样失败关闭并保留原值,交由运维处理,禁止用空文档覆盖未知授权状态。
524
+
525
+ LDAP 的 ldapSyncUser: false 要求已有且处于激活状态的本地用户以绑定数据库 credentialVersion;没有本地记录返回 USER_NOT_FOUND,不再创建虚拟 DN 身份。
526
+
527
+ Session.credentialVersion 绑定创建时的数据库凭据代际;CreateSessionOptions.expectedCredentialVersion 仅供已验证策略传入,旧代拒绝。省略它的受信低层 create 仍读取真实、激活且未到期的本地用户并绑定当前代,不能伪造缺少数据库记录的身份。
528
+
529
+ `iam.auth.logout(accessToken)` 直接按完整不透明令牌在用户分区执行一次 CAS,原子撤销 access 与关联 refresh;access 已到期也不能跳过撤销。非法/不存在的令牌幂等成功,缓存读取或提交故障返回失败,恢复后可用同一令牌重试。HTTP 登出只允许撤销所持令牌,不以 userId 定位他人会话,也不要求该 access 仍通过认证。调用方必须检查 `success`;本地 Cookie/安全存储清理不代表服务端撤销已经确认,失败应显示提示。
530
+
531
+
532
+ ### 页面离开会话
533
+
534
+ `session.endOnPageHide` 默认关闭。Kit/typed client 的 `createAuthActions().bindPageExit({ enabled })` 返回监听清理函数,宿主卸载时调用;typed client.close 也清理本实例监听。请求走现有 CSRF/加密管线,POST `/auth/session/close` 仅撤销所持旧会话,不清 Cookie,避免迟到响应影响新登录。刷新/整页导航也触发,BFCache 跳过;浏览器关闭属于最善努力,需配合服务端闲置/绝对期限,显式 logout 始终可靠撤销。
535
+