@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,294 @@
1
+ import type { HaiResult } from '@h-ai/core'
2
+ import type { Role, User } from '@h-ai/iam'
3
+ import type { ServConfig, ServContext } from '@h-ai/serv'
4
+ import type { AdminCreateUser, AdminInviteUser, AdminRole, AdminRoleCode, AdminUpdateUser, AdminUser, AvatarUpload, ProfileUpdate } from '{{fullstack.contractPackageName}}'
5
+ import { Buffer } from 'node:buffer'
6
+ import { audit } from '@h-ai/audit'
7
+ import { cache } from '@h-ai/cache'
8
+ import { core, err, HaiCommonError, ok } from '@h-ai/core'
9
+ import { iam } from '@h-ai/iam'
10
+ import { storage } from '@h-ai/storage'
11
+ import { SUPPORTED_LOCALES } from '{{fullstack.contractPackageName}}'
12
+ import { serverM } from './messages.js'
13
+
14
+ const ROLE_CODES = new Set<AdminRoleCode>(['admin', 'audit_admin', 'config_admin', 'business_operator', 'user'])
15
+ const SUPPORTED_THEMES = [{{#each fullstack.theme.themes}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}] as readonly string[]
16
+ let adminMutationActive = false
17
+
18
+ /** 管理写操作串行化,锁内重新鉴权,防止两个管理员同时互相删除或降级。 */
19
+ export async function withAdminMutation<T>(context: ServContext, operation: string, work: () => Promise<HaiResult<T>>): Promise<HaiResult<T>> {
20
+ // mini 的管理写入仅支持单写实例;进程内保护无租期,慢请求不会在 TTL 到期后被并发穿透。
21
+ if (adminMutationActive)
22
+ return err(HaiCommonError.CONFLICT, serverM('busy', { locale: context.locale }))
23
+ adminMutationActive = true
24
+ let owner = ''
25
+ const key = 'hai:app:admin-mutation'
26
+ let acquired = false
27
+ let leaseLost = false
28
+ let renewalPending = false
29
+ let renewalTimer: ReturnType<typeof setInterval> | undefined
30
+ try {
31
+ const locked = await cache.lock.acquire(key, { ttl: 300 })
32
+ if (!locked.success)
33
+ return locked
34
+ if (!locked.data)
35
+ return err(HaiCommonError.CONFLICT, serverM('busy', { locale: context.locale }))
36
+ owner = locked.data
37
+ acquired = true
38
+ renewalTimer = setInterval(() => {
39
+ if (renewalPending || leaseLost)
40
+ return
41
+ renewalPending = true
42
+ void cache.lock.extend(key, 300, owner).then((renewed) => {
43
+ if (!renewed.success || !renewed.data) {
44
+ leaseLost = true
45
+ core.logger.error('Admin mutation lease renewal failed', { owner, error: renewed.success ? 'lease_not_owned' : renewed.error.code })
46
+ }
47
+ }).catch(() => {
48
+ leaseLost = true
49
+ core.logger.error('Admin mutation lease renewal failed', { owner, error: 'unexpected_renewal_failure' })
50
+ }).finally(() => {
51
+ renewalPending = false
52
+ })
53
+ }, 100_000)
54
+ renewalTimer.unref()
55
+ const actor = await getAdminUser(context.session?.userId ?? '')
56
+ if (!actor.success)
57
+ return actor
58
+ const permitted = await iam.authz.checkPermission(actor.data.id, operation.startsWith('iam.roles.') ? 'app.roles.manage' : 'app.users.manage')
59
+ if (!permitted.success)
60
+ return permitted
61
+ if (actor.data.accountStatus !== 'active' || (actor.data.accountType === 'temporary' && (!actor.data.accountExpiresAt || new Date(actor.data.accountExpiresAt).getTime() <= Date.now())) || !permitted.data)
62
+ return err(HaiCommonError.FORBIDDEN, serverM('selfProtection', { locale: context.locale }))
63
+ if (leaseLost)
64
+ return err(HaiCommonError.CONFLICT, serverM('busy', { locale: context.locale }))
65
+ const sensitive = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', operation, context.request.headers.get('x-hai-reauthentication') ?? undefined)
66
+ if (!sensitive.success)
67
+ return sensitive
68
+ return await work()
69
+ }
70
+ finally {
71
+ if (renewalTimer)
72
+ clearInterval(renewalTimer)
73
+ try {
74
+ if (acquired) {
75
+ const released = await cache.lock.release(key, owner)
76
+ if (!released.success || !released.data)
77
+ core.logger.error('Failed to release admin mutation lock', { owner, error: released.success ? 'lease_not_owned' : released.error.code })
78
+ }
79
+ }
80
+ finally {
81
+ adminMutationActive = false
82
+ }
83
+ }
84
+ }
85
+
86
+ /** 用户与角色读取失败直接透传,不能把角色查询失败当作普通用户。 */
87
+ export async function getAdminUser(id: string): Promise<HaiResult<AdminUser>> {
88
+ const user = await iam.user.getUser(id)
89
+ if (!user.success)
90
+ return user
91
+ if (!user.data)
92
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound'))
93
+ const roles = await iam.authz.getUserRoles(id)
94
+ if (!roles.success)
95
+ return roles
96
+ return ok(toAdminUser(user.data, roles.data))
97
+ }
98
+
99
+ export function toAdminUser(user: User, roles: Role[]): AdminUser {
100
+ return {
101
+ id: user.id,
102
+ username: user.username,
103
+ email: user.email,
104
+ displayName: user.displayName,
105
+ avatarUrl: user.avatarUrl,
106
+ accountType: user.accountType ?? 'permanent',
107
+ accountStatus: user.accountStatus,
108
+ accountExpiresAt: user.accountExpiresAt?.toISOString() ?? null,
109
+ lastLoginAt: user.lastLoginAt?.toISOString() ?? null,
110
+ roles: roles.map(role => role.code).filter(isRoleCode),
111
+ createdAt: user.createdAt.toISOString(),
112
+ updatedAt: user.updatedAt.toISOString(),
113
+ }
114
+ }
115
+
116
+ export function isRoleCode(code: string): code is AdminRoleCode {
117
+ return ROLE_CODES.has(code as AdminRoleCode)
118
+ }
119
+
120
+ export async function createAdminUser(input: AdminCreateUser, context: ServContext): Promise<HaiResult<AdminUser>> {
121
+ const role = await iam.authz.getRoleByCode(input.role)
122
+ if (!role.success)
123
+ return role
124
+ if (!role.data)
125
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
126
+ const registered = await iam.user.createUser({ ...input, accountExpiresAt: input.accountExpiresAt ? new Date(input.accountExpiresAt) : undefined })
127
+ if (!registered.success)
128
+ return registered
129
+ const userId = registered.data.user.id
130
+ const assigned = await iam.authz.syncRoles(userId, [role.data.id])
131
+ if (!assigned.success) {
132
+ await rollbackCreatedUser(userId)
133
+ return assigned
134
+ }
135
+ await recordAudit(context, 'create', 'users', userId, { role: input.role, accountStatus: input.accountStatus })
136
+ return getAdminUser(userId)
137
+ }
138
+
139
+ /** 安全通知发送后发生未知结果时保留账号,不自动重试或删除已发出的邀请。 */
140
+ export async function inviteAdminUser(input: AdminInviteUser, context: ServContext): Promise<HaiResult<AdminUser>> {
141
+ const role = await iam.authz.getRoleByCode(input.role)
142
+ if (!role.success || !role.data)
143
+ return role.success ? err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale })) : role
144
+ const created = await iam.user.inviteUser({ ...input, accountExpiresAt: input.accountExpiresAt ? new Date(input.accountExpiresAt) : undefined })
145
+ if (!created.success)
146
+ return created
147
+ const assigned = await iam.authz.syncRoles(created.data.user.id, [role.data.id])
148
+ if (!assigned.success)
149
+ return assigned
150
+ await recordAudit(context, 'invite', 'users', created.data.user.id, { role: input.role })
151
+ return getAdminUser(created.data.user.id)
152
+ }
153
+
154
+ async function rollbackCreatedUser(id: string): Promise<void> {
155
+ const removed = await iam.user.deleteUser(id)
156
+ if (!removed.success)
157
+ core.logger.error('Failed to roll back user creation', { userId: id, error: removed.error.code })
158
+ }
159
+
160
+ export async function updateAdminUser(input: AdminUpdateUser, context: ServContext): Promise<HaiResult<AdminUser>> {
161
+ if (input.id === context.session?.userId && ((input.role && input.role !== 'admin') || (input.accountStatus && input.accountStatus !== 'active') || input.accountType === 'temporary' || input.accountExpiresAt != null))
162
+ return err(HaiCommonError.FORBIDDEN, serverM('selfProtection', { locale: context.locale }))
163
+ const existing = await iam.user.getUser(input.id)
164
+ if (!existing.success)
165
+ return existing
166
+ if (!existing.data)
167
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
168
+ const role = input.role ? await iam.authz.getRoleByCode(input.role) : undefined
169
+ if (role && !role.success)
170
+ return role
171
+ if (role?.success && !role.data)
172
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
173
+ const { id, role: _role, ...profile } = input
174
+ const previousRoles = await iam.authz.getUserRoles(id)
175
+ if (!previousRoles.success)
176
+ return previousRoles
177
+ if (role?.success && role.data) {
178
+ const assigned = await iam.authz.syncRoles(id, [role.data.id])
179
+ if (!assigned.success)
180
+ return assigned
181
+ }
182
+ const updated = await iam.user.updateUser(id, { ...profile, accountExpiresAt: profile.accountExpiresAt === undefined ? undefined : profile.accountExpiresAt ? new Date(profile.accountExpiresAt) : null })
183
+ if (!updated.success) {
184
+ // 角色 ID 可精确还原,避免 undefined 资料字段被 updateUser 忽略后无法补偿。
185
+ if (role?.success && role.data) {
186
+ const restored = await iam.authz.syncRoles(id, previousRoles.data.map(item => item.id))
187
+ if (!restored.success)
188
+ core.logger.error('Failed to restore roles after user update failure', { userId: id, error: restored.error.code })
189
+ }
190
+ return updated
191
+ }
192
+ await recordAudit(context, 'update', 'users', id, { fields: Object.keys(profile), role: input.role })
193
+ return getAdminUser(id)
194
+ }
195
+
196
+ export async function getAdminRoles(): Promise<HaiResult<{ items: AdminRole[] }>> {
197
+ const roles = await iam.authz.getAllRoles({ page: 1, pageSize: 100 })
198
+ if (!roles.success)
199
+ return roles
200
+ const items = roles.data.items.filter(role => isRoleCode(role.code))
201
+ const counts = await iam.authz.getRoleUserCounts(items.map(role => role.id))
202
+ if (!counts.success)
203
+ return counts
204
+ return ok({ items: items.map(role => ({
205
+ id: role.id,
206
+ code: role.code as AdminRoleCode,
207
+ name: role.name,
208
+ description: role.description,
209
+ userCount: counts.data.get(role.id) ?? 0,
210
+ })) })
211
+ }
212
+
213
+ export async function updateProfile(input: ProfileUpdate, context: ServContext): Promise<HaiResult<AdminUser>> {
214
+ const token = context.accessToken ?? ''
215
+ const current = await iam.user.getCurrentUser(token)
216
+ if (!current.success)
217
+ return current
218
+ if ((input.locale && !(SUPPORTED_LOCALES as readonly string[]).includes(input.locale)) || (input.theme && !SUPPORTED_THEMES.includes(input.theme)))
219
+ return err(HaiCommonError.VALIDATION_ERROR, serverM('unsupportedPreference', { locale: context.locale }))
220
+ const metadata = { ...current.data.metadata }
221
+ if (input.locale)
222
+ metadata.locale = input.locale
223
+ if (input.theme)
224
+ metadata.theme = input.theme
225
+ const updated = await iam.user.updateCurrentUser(token, { displayName: input.displayName, email: input.email, metadata })
226
+ if (!updated.success)
227
+ return updated
228
+ await recordAudit(context, 'update', 'profile', current.data.id, { fields: Object.keys(input) })
229
+ return getAdminUser(current.data.id)
230
+ }
231
+
232
+ /** 上传路径由服务端生成,禁止客户端写入任意 storage key。 */
233
+ export async function uploadAvatar(userId: string, input: AvatarUpload, context: ServContext): Promise<HaiResult<{ avatarUrl: string }>> {
234
+ const current = await iam.user.getUser(userId)
235
+ if (!current.success)
236
+ return current
237
+ if (!current.data)
238
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
239
+ const bytes = Buffer.from(input.base64, 'base64')
240
+ if (bytes.length === 0 || bytes.length > 512 * 1024 || bytes.toString('base64') !== input.base64 || !matchesImage(bytes, input.contentType))
241
+ return err(HaiCommonError.VALIDATION_ERROR, serverM('invalidAvatar', { locale: context.locale }))
242
+ const extension = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp' }[input.contentType]
243
+ const key = `avatars/${userId}/${core.id.uuid()}.${extension}`
244
+ const stored = await storage.file.put(key, bytes, { contentType: input.contentType })
245
+ if (!stored.success)
246
+ return stored
247
+ const apiPrefix = core.config.getOrThrow<ServConfig>('serv').http.apiPrefix
248
+ // 只保存生成资源路径,公开 Origin 由客户端已配置的 API 地址决定,禁止信任 Host。
249
+ const avatarUrl = `${apiPrefix}/assets/${key}`
250
+ const updated = await iam.user.updateUser(userId, { avatarUrl })
251
+ if (!updated.success) {
252
+ const cleaned = await storage.file.delete(key)
253
+ if (!cleaned.success)
254
+ core.logger.warn('Failed to clean up rejected avatar', { error: cleaned.error.code })
255
+ return updated
256
+ }
257
+ // 更新成功后删除旧头像;只清理此用户且符合服务端生成规则的资源。
258
+ const oldKey = current.data.avatarUrl?.split('/assets/')[1]
259
+ if (oldKey && oldKey.startsWith(`avatars/${userId}/`) && /^avatars\/[\w-]+\/[\w-]+\.(?:png|jpg|webp)$/.test(oldKey)) {
260
+ const cleaned = await storage.file.delete(oldKey)
261
+ if (!cleaned.success)
262
+ core.logger.warn('Failed to clean up previous avatar', { error: cleaned.error.code })
263
+ }
264
+ await recordAudit(context, 'update', 'avatars', userId)
265
+ return ok({ avatarUrl })
266
+ }
267
+
268
+ function matchesImage(bytes: Buffer, contentType: AvatarUpload['contentType']): boolean {
269
+ if (contentType === 'image/png') {
270
+ if (bytes.length < 33 || bytes.subarray(0, 8).toString('hex') !== '89504e470d0a1a0a' || bytes.toString('ascii', 12, 16) !== 'IHDR')
271
+ return false
272
+ const width = bytes.readUInt32BE(16)
273
+ const height = bytes.readUInt32BE(20)
274
+ return width > 0 && height > 0 && width <= 4096 && height <= 4096
275
+ }
276
+ if (contentType === 'image/jpeg')
277
+ return bytes.length >= 4 && bytes.subarray(0, 3).toString('hex') === 'ffd8ff' && bytes.subarray(-2).toString('hex') === 'ffd9'
278
+ return bytes.length >= 20 && bytes.toString('ascii', 0, 4) === 'RIFF' && bytes.toString('ascii', 8, 12) === 'WEBP' && bytes.readUInt32LE(4) + 8 === bytes.length
279
+ }
280
+
281
+ /** 审计记录仅含操作字段与资源标识,不记录密码、token、头像内容或联系信息。 */
282
+ export async function recordAudit(context: ServContext, action: string, resource: string, resourceId?: string, details?: Record<string, unknown>): Promise<void> {
283
+ const result = await audit.log({
284
+ userId: context.session?.userId,
285
+ action,
286
+ resource,
287
+ resourceId,
288
+ details,
289
+ ipAddress: context.ip,
290
+ userAgent: context.userAgent,
291
+ })
292
+ if (!result.success)
293
+ core.logger.error('Failed to record audit event', { action, resource, error: result.error.code })
294
+ }
@@ -0,0 +1,28 @@
1
+ /** 应用自己的定时任务装配;Scheduler 先于 IAM/Audit 关闭,不修改全局回调所属关系。 */
2
+ import type { HaiResult } from '@h-ai/core'
3
+ import { audit } from '@h-ai/audit'
4
+ import { core, err, ok } from '@h-ai/core'
5
+ import { HaiSchedulerError, scheduler, SchedulerConfigSchema } from '@h-ai/scheduler'
6
+ import { serverM } from './messages.js'
7
+
8
+ /** 仅启用自动备份时启动现有调度器;手动备份不需要启动它。 */
9
+ export async function startAuditJobs(): Promise<HaiResult<void>> {
10
+ const schedule = audit.schedulerPlan
11
+ if (!schedule)
12
+ return ok(undefined)
13
+ const config = SchedulerConfigSchema.safeParse(core.config.get('scheduler') ?? {})
14
+ if (!config.success)
15
+ return err(HaiSchedulerError.CONFIG_ERROR, serverM('busy'), config.error)
16
+ const initialized = await scheduler.init({
17
+ ...config.data,
18
+ ...schedule,
19
+ })
20
+ if (!initialized.success) {
21
+ await scheduler.close()
22
+ return initialized
23
+ }
24
+ const started = scheduler.start()
25
+ if (!started.success)
26
+ await scheduler.close()
27
+ return started
28
+ }
@@ -0,0 +1,50 @@
1
+ /** 示例业务配置仅管理公开公告,不接触系统凭据或安全策略。 */
2
+ import type { HaiResult } from '@h-ai/core'
3
+ import { audit } from '@h-ai/audit'
4
+ import { err, HaiCommonError, ok } from '@h-ai/core'
5
+ import { reldb } from '@h-ai/reldb'
6
+ import { z } from 'zod'
7
+
8
+ export const BusinessSettingsSchema = z.object({ notice: z.string().trim().max(200) }).strict()
9
+
10
+ export function initBusinessSettings(): Promise<HaiResult<void>> {
11
+ return reldb.ddl.createTable('app_business_settings', { id: { type: 'TEXT', primaryKey: true }, notice: { type: 'TEXT', notNull: true } })
12
+ }
13
+
14
+ export async function readBusinessSettings(): Promise<HaiResult<{ notice: string }>> {
15
+ const row = await reldb.sql.get<{ notice: string }>('SELECT notice FROM app_business_settings WHERE id = ?', ['public'])
16
+ return row.success ? ok({ notice: row.data?.notice ?? '' }) : row
17
+ }
18
+
19
+ export async function saveBusinessSettings(input: { notice: string }): Promise<HaiResult<{ notice: string }>> {
20
+ const parsed = BusinessSettingsSchema.safeParse(input)
21
+ if (!parsed.success)
22
+ return err(HaiCommonError.VALIDATION_ERROR)
23
+ const begun = await reldb.tx.begin()
24
+ if (!begun.success)
25
+ return begun
26
+ const tx = begun.data
27
+ try {
28
+ const sql = reldb.config?.type === 'mysql'
29
+ ? 'INSERT INTO app_business_settings (id, notice) VALUES (?, ?) ON DUPLICATE KEY UPDATE notice = VALUES(notice)'
30
+ : 'INSERT INTO app_business_settings (id, notice) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET notice = excluded.notice'
31
+ const saved = await tx.execute(sql, ['public', parsed.data.notice])
32
+ if (!saved.success) {
33
+ await tx.rollback()
34
+ return saved
35
+ }
36
+ const logged = await audit.capture({ action: 'business.configuration.update', resource: 'business-settings', category: 'business', details: { fields: ['notice'] } }, { tx })
37
+ if (!logged.success) {
38
+ await tx.rollback()
39
+ return logged
40
+ }
41
+ const committed = await tx.commit()
42
+ if (!committed.success)
43
+ await tx.rollback()
44
+ return committed.success ? ok(parsed.data) : committed
45
+ }
46
+ catch (error) {
47
+ await tx.rollback()
48
+ return err(HaiCommonError.INTERNAL_ERROR, undefined, error)
49
+ }
50
+ }
@@ -0,0 +1,50 @@
1
+ import { core } from '@h-ai/core'
2
+
3
+ /** 服务端业务提示与角色种子名称同步维护中英日文。 */
4
+ export const serverM = core.i18n.createMessageGetter({
5
+ 'zh-CN': {
6
+ admin: '系统管理员',
7
+ audit_admin: '审计管理员',
8
+ config_admin: '配置管理员',
9
+ dutyProtected: '不能通过此入口修改其他职责的权限定义。',
10
+ user: '普通用户',
11
+ business_operator: '业务操作员',
12
+ notFound: '用户或角色不存在',
13
+ unauthorized: '请提供要撤销的访问令牌',
14
+ selfProtection: '不能删除当前管理员或修改其职责、有效期和账号状态',
15
+ invalidAvatar: '请选择不超过 512 KB 的有效 PNG、JPEG 或 WebP 图片',
16
+ busy: '系统繁忙,请稍后重试',
17
+ unsupportedPreference: '不支持此语言或主题',
18
+ invalidDateRange: '结束时间不能早于开始时间',
19
+ },
20
+ 'ja-JP': {
21
+ admin: 'システム管理者',
22
+ audit_admin: '監査管理者',
23
+ config_admin: '設定管理者',
24
+ dutyProtected: 'この操作では他の職務の権限定義を変更できません。',
25
+ user: '一般ユーザー',
26
+ business_operator: '業務担当者',
27
+ notFound: 'ユーザーまたはロールが存在しません',
28
+ unauthorized: '取り消すアクセストークンを指定してください',
29
+ selfProtection: '自分の管理者アカウントの削除や職務、有効期限、状態の変更はできません',
30
+ invalidAvatar: '512KB以下の有効なPNG、JPEG、WebP画像を選択してください',
31
+ busy: 'システムが混雑しています。再試行してください',
32
+ unsupportedPreference: 'この言語またはテーマには対応していません',
33
+ invalidDateRange: '終了日は開始日以降にしてください',
34
+ },
35
+ 'en-US': {
36
+ admin: 'System administrator',
37
+ audit_admin: 'Audit administrator',
38
+ config_admin: 'Configuration administrator',
39
+ dutyProtected: 'This operation cannot change permission definitions for other duties.',
40
+ user: 'User',
41
+ business_operator: 'Business operator',
42
+ notFound: 'User or role does not exist',
43
+ unauthorized: 'Provide the access token to revoke',
44
+ selfProtection: 'Ask another administrator to change your duty, expiry or account status',
45
+ invalidAvatar: 'Choose a valid PNG, JPEG or WebP image up to 512 KB',
46
+ busy: 'The system is busy. Please try again',
47
+ unsupportedPreference: 'This language or theme is not supported',
48
+ invalidDateRange: 'End date must not precede start date',
49
+ },
50
+ })
@@ -0,0 +1,106 @@
1
+ import type { ServContext } from '@h-ai/serv'
2
+ import { audit, AuditInitConfigSchema } from '@h-ai/audit'
3
+ import { core, err, HaiCommonError, ok } from '@h-ai/core'
4
+ import { iam } from '@h-ai/iam'
5
+ import { serv } from '@h-ai/serv'
6
+ import { createIamProcedures } from '@h-ai/serv/features/iam'
7
+ import { adminContract, profileContract } from '{{fullstack.contractPackageName}}'
8
+ import {
9
+ createAdminUser,
10
+ getAdminRoles,
11
+ inviteAdminUser,
12
+ isRoleCode,
13
+ recordAudit,
14
+ toAdminUser,
15
+ updateAdminUser,
16
+ updateProfile,
17
+ uploadAvatar,
18
+ withAdminMutation,
19
+ } from '../admin-service.js'
20
+ import { readBusinessSettings, saveBusinessSettings } from '../business-settings.js'
21
+ import { serverM } from '../messages.js'
22
+
23
+ /** 身份认证沿用框架公共契约与 refresh-cookie 传输。 */
24
+ export function createAuthProcedures() {
25
+ return createIamProcedures({ iam }).auth
26
+ }
27
+
28
+ export function createAdminProcedures() {
29
+ return serv.implement(adminContract).context<ServContext>().route('permissions.list').permission('app.roles.manage').handle(async ({ input }) => {
30
+ const result = await iam.authz.getAllPermissions(input)
31
+ return result.success ? ok({ total: result.data.total, items: result.data.items.map(item => ({ id: item.id, code: item.code, name: item.name, sensitiveOperations: item.sensitiveOperations ?? [] })) }) : result
32
+ }).route('permissions.update').permission('app.roles.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.permissions.sensitiveOperations', async () => {
33
+ const result = await iam.authz.setPermissionSensitiveOperations(input.id, input.sensitiveOperations)
34
+ return result.success ? ok({ saved: true as const }) : result
35
+ })).route('configuration.get').permission('app.config.manage').handle(() => readBusinessSettings()).route('configuration.update').permission('app.config.manage').handle(async ({ input, context }) => {
36
+ const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'business.settings.update', context.request.headers.get('x-hai-reauthentication') ?? undefined)
37
+ return checked.success ? saveBusinessSettings(input) : checked
38
+ }).route('users.list').permission('app.users.manage').handle(async ({ input }) => {
39
+ const users = await iam.user.listUsers(input)
40
+ if (!users.success)
41
+ return users
42
+ const roles = await iam.authz.getUserRolesForMany(users.data.items.map(user => user.id))
43
+ if (!roles.success)
44
+ return roles
45
+ return ok({ ...users.data, items: users.data.items.map(user => toAdminUser(user, roles.data.get(user.id) ?? [])) })
46
+ }).route('users.resetPassword').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.resetPassword', async () => {
47
+ const result = await iam.user.adminResetPassword(input.id, input.newPassword)
48
+ if (!result.success)
49
+ return result
50
+ await recordAudit(context, 'password_reset', 'users', input.id)
51
+ return ok({ reset: true as const })
52
+ })).route('users.invite').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.invite', () => inviteAdminUser(input, context))).route('users.create').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.create', () => createAdminUser(input, context))).route('users.update').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.update', () => updateAdminUser(input, context))).route('users.delete').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.delete', async () => {
53
+ if (context.session.userId === input.id)
54
+ return err(HaiCommonError.FORBIDDEN, serverM('selfProtection', { locale: context.locale }))
55
+ const result = await iam.user.deleteUser(input.id)
56
+ if (!result.success)
57
+ return result
58
+ await recordAudit(context, 'delete', 'users', input.id)
59
+ return ok({ deleted: true as const })
60
+ })).route('users.avatar').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.avatar', () => uploadAvatar(input.id, input, context))).route('roles.list').permission('app.roles.manage').handle(() => getAdminRoles()).route('roles.update').permission('app.roles.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.roles.update', async () => {
61
+ const role = await iam.authz.getRole(input.id)
62
+ if (!role.success)
63
+ return role
64
+ if (!role.data || !isRoleCode(role.data.code))
65
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
66
+ if (role.data.code !== 'admin' && role.data.code !== 'user')
67
+ return err(HaiCommonError.FORBIDDEN, serverM('dutyProtected', { locale: context.locale }))
68
+ const updated = await iam.authz.updateRole(input.id, { description: input.description })
69
+ if (!updated.success)
70
+ return updated
71
+ const roles = await getAdminRoles()
72
+ if (!roles.success)
73
+ return roles
74
+ const result = roles.data.items.find(item => item.id === input.id)
75
+ if (!result)
76
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
77
+ await recordAudit(context, 'update', 'roles', input.id, { fields: ['description'] })
78
+ return ok(result)
79
+ })).route('audit.backup').permission('audit:backup').handle(async ({ input, context }) => {
80
+ const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'audit.backup', context.request.headers.get('x-hai-reauthentication') ?? undefined)
81
+ if (!checked.success)
82
+ return checked
83
+ const result = await audit.backup.run({ ...input, signal: context.request.signal })
84
+ return result.success ? ok({ ...result.data, createdAt: result.data.createdAt.toISOString(), cutoff: result.data.cutoff.toISOString() }) : result
85
+ }).route('audit.list').permission('audit:read').handle(async ({ input, context }) => {
86
+ if (input.startDate && input.endDate && input.startDate > input.endDate)
87
+ return err(HaiCommonError.VALIDATION_ERROR, serverM('invalidDateRange', { locale: context.locale }))
88
+ const result = await audit.list({ ...input, startDate: input.startDate ? new Date(input.startDate) : undefined, endDate: input.endDate ? new Date(input.endDate) : undefined })
89
+ if (!result.success)
90
+ return result
91
+ const stats = await audit.getStats(7, { category: input.category, outcome: input.outcome, severity: input.severity, resource: input.resource, scopeId: input.scopeId, userId: input.userId })
92
+ if (!stats.success)
93
+ return stats
94
+ return ok({ ...result.data, stats: stats.data, backupEnabled: AuditInitConfigSchema.parse(core.config.get('audit') ?? {}).backup.enabled, items: result.data.items.map(item => ({ ...item, createdAt: new Date(item.createdAt).toISOString() })) })
95
+ }).build()
96
+ }
97
+
98
+ export function createProfileProcedures() {
99
+ return serv.implement(profileContract).context<ServContext>().route('update').auth().handle(async ({ input, context }) => {
100
+ const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'iam.profile.update', context.request.headers.get('x-hai-reauthentication') ?? undefined)
101
+ return checked.success ? updateProfile(input, context) : checked
102
+ }).route('avatar').auth().handle(async ({ input, context }) => {
103
+ const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'iam.profile.avatar', context.request.headers.get('x-hai-reauthentication') ?? undefined)
104
+ return checked.success ? uploadAvatar(context.session.userId, input, context) : checked
105
+ }).build()
106
+ }
@@ -0,0 +1,49 @@
1
+ import type { ServContext } from '@h-ai/serv'
2
+ import type { AppEchoOutputData, AppFrontendTarget, AppInfoOutputData } from '{{fullstack.contractPackageName}}'
3
+ import { ok } from '@h-ai/core'
4
+ import { serv } from '@h-ai/serv'
5
+ import { appContract } from '{{fullstack.contractPackageName}}'
6
+ import { readBusinessSettings } from '../business-settings.js'
7
+
8
+ export interface CreateAppProceduresOptions {
9
+ readonly name: string
10
+ readonly version: string
11
+ readonly env: string
12
+ readonly defaultLocale: string
13
+ readonly supportedLocales: readonly string[]
14
+ readonly startedAt: string
15
+ readonly frontends: readonly AppFrontendTarget[]
16
+ }
17
+
18
+ /** app 领域 procedures:把共享 contract 绑定到真实业务处理函数。 */
19
+ export function createAppProcedures(options: CreateAppProceduresOptions) {
20
+ return serv
21
+ .implement(appContract)
22
+ .context<ServContext>()
23
+ .route('info', async () => {
24
+ const settings = await readBusinessSettings()
25
+ return settings.success ? ok({ ...createAppInfo(options), notice: settings.data.notice }) : settings
26
+ })
27
+ .route('echo', ({ input, context }) => {
28
+ const data: AppEchoOutputData = {
29
+ message: input.message,
30
+ echoedAt: new Date().toISOString(),
31
+ requestId: context.requestId,
32
+ }
33
+ return ok(data)
34
+ })
35
+ .build()
36
+ }
37
+
38
+ function createAppInfo(options: CreateAppProceduresOptions): AppInfoOutputData {
39
+ return {
40
+ notice: '',
41
+ name: options.name,
42
+ version: options.version,
43
+ env: options.env,
44
+ defaultLocale: options.defaultLocale,
45
+ supportedLocales: [...options.supportedLocales],
46
+ startedAt: options.startedAt,
47
+ frontends: [...options.frontends],
48
+ }
49
+ }
@@ -0,0 +1,34 @@
1
+ import type { CreateAppProceduresOptions } from './app-procedures.js'
2
+ {{#if features.feedback}}
3
+ import { feedback } from '@h-ai/feedback'
4
+ import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
5
+ {{/if}}
6
+ import { createAdminProcedures, createAuthProcedures, createProfileProcedures } from './admin-procedures.js'
7
+ import { createAppProcedures } from './app-procedures.js'
8
+
9
+ // @hai-generator:procedures:imports
10
+
11
+ export interface CreateServerProceduresOptions {
12
+ readonly app: CreateAppProceduresOptions
13
+ }
14
+
15
+ /** 工程级 procedures 聚合入口;后续增加领域时在这里追加即可。 */
16
+ export function createServerProcedures(options: CreateServerProceduresOptions) {
17
+ return {
18
+ // @hai-generator:procedures:entries
19
+ app: createAppProcedures(options.app),
20
+ iam: { auth: createAuthProcedures() },
21
+ admin: createAdminProcedures(),
22
+ profile: createProfileProcedures(),
23
+ {{#if features.feedback}}
24
+ feedback: createFeedbackProcedures({
25
+ feedback,
26
+ resolveContext: ({ session }) => ({
27
+ scopeId: '{{projectName}}',
28
+ userId: session.userId,
29
+ permissions: session.permissions,
30
+ }),
31
+ }),
32
+ {{/if}}
33
+ }
34
+ }