@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
@@ -1,276 +1,45 @@
1
1
  ---
2
2
  name: hai-storage
3
- description: 使用 @h-ai/storage 进行文件存储操作(本地/S3),包括 file/dir/presign 三组能力;当需求涉及文件上传下载、目录批量删除、S3 预签名 URL 或客户端直传时使用。
3
+ description: "实现本地/S3 文件与目录操作、流式读写、checksum 和预签名上传下载;处理冲突、校验与取消。"
4
4
  ---
5
5
 
6
6
  # hai-storage
7
7
 
8
- > `@h-ai/storage` 提供统一文件存储接口,支持本地文件系统(LocalProvider)和 S3 兼容存储(S3Provider),通过 `storage.file`、`storage.dir`、`storage.presign` 三组接口访问能力。
8
+ ## 能力契约
9
9
 
10
- ---
11
-
12
- ## 运行环境
13
-
14
- > ⚠️ **服务端模块**。`storage.file` / `storage.dir` / `storage.presign` 均在 Node.js 端使用。浏览器端通过预签名 URL 或 API 端点间接访问存储(见下方「浏览器端文件上传」)。
15
-
16
- ---
17
-
18
- ## 适用场景
19
-
20
- - 文件上传与下载
21
- - 预签名 URL 生成(客户端直传)
22
- - S3 兼容存储对接(AWS S3、MinIO、阿里云 OSS 等)
23
- - 本地开发文件存储
24
- - 目录前缀清理与批量文件删除
25
-
26
- ---
27
-
28
- ## 使用步骤
29
-
30
- ### 1. 配置
31
-
32
- ```yaml
33
- # config/_storage.yml
34
- type: ${HAI_STORAGE_TYPE:local} # local | s3
35
- # 本地存储
36
- root: ${HAI_STORAGE_PATH:./data/storage}
37
- # S3 存储(type=s3 时生效)
38
- # endpoint: ${HAI_STORAGE_S3_ENDPOINT:}
39
- # region: ${HAI_STORAGE_S3_REGION:us-east-1}
40
- # accessKeyId: ${HAI_STORAGE_S3_ACCESS_KEY:}
41
- # secretAccessKey: ${HAI_STORAGE_S3_SECRET_KEY:}
42
- # bucket: ${HAI_STORAGE_S3_BUCKET:my-bucket}
43
- # forcePathStyle: false
44
- # prefix: ''
45
- # publicUrl: ''
46
- ```
47
-
48
- ### 2. 初始化与关闭
49
-
50
- ```typescript
51
- import { storage } from '@h-ai/storage'
52
-
53
- await storage.init(core.config.get('storage'))
54
- // 使用后关闭
55
- await storage.close()
56
- ```
57
-
58
- ---
59
-
60
- ## 核心 API
61
-
62
- ### 文件操作
63
-
64
- `storage.file` 提供以下方法:
65
-
66
- - `put(key, data, options?)`
67
- - `get(key, options?)`
68
- - `head(key)`
69
- - `exists(key)`
70
- - `delete(key)`
71
- - `deleteMany(keys)`
72
- - `copy(sourceKey, destKey, options?)`
73
-
74
- ```typescript
75
- // 上传文件
76
- const result = await storage.file.put('avatars/user-123.png', fileBuffer, {
77
- contentType: 'image/png',
78
- metadata: { userId: '123' },
79
- })
80
-
81
- // 下载文件
82
- const file = await storage.file.get('avatars/user-123.png')
83
-
84
- // 列出文件
85
- const files = await storage.dir.list({ prefix: 'avatars/', maxKeys: 100 })
86
- ```
87
-
88
- ### 预签名 URL
89
-
90
- `storage.presign` 提供以下方法:
91
-
92
- - `getUrl(key, options?)`
93
- - `putUrl(key, options?)`
94
- - `publicUrl(key)`
95
-
96
- ```typescript
97
- // 客户端直传模式
98
- const uploadUrl = await storage.presign.putUrl('uploads/doc.pdf', {
99
- expiresIn: 3600, // 1 小时有效
100
- contentType: 'application/pdf',
101
- })
102
-
103
- // 临时下载链接
104
- const downloadUrl = await storage.presign.getUrl('uploads/doc.pdf', {
105
- expiresIn: 300, // 5 分钟有效
106
- })
107
- ```
108
-
109
- ---
110
-
111
- ## 错误码 — `HaiStorageError`
112
-
113
- | 错误码 | code | 说明 |
114
- |--------|------|------|
115
- | `HaiStorageError.CONNECTION_FAILED` | `hai:storage:001` | 连接失败 |
116
- | `HaiStorageError.OPERATION_FAILED` | `hai:storage:002` | 操作失败 |
117
- | `HaiStorageError.NOT_FOUND` | `hai:storage:003` | 文件不存在 |
118
- | `HaiStorageError.ALREADY_EXISTS` | `hai:storage:004` | 文件已存在 |
119
- | `HaiStorageError.PERMISSION_DENIED` | `hai:storage:005` | 权限不足 |
120
- | `HaiStorageError.QUOTA_EXCEEDED` | `hai:storage:006` | 配额超限 |
121
- | `HaiStorageError.INVALID_PATH` | `hai:storage:007` | 路径无效 |
122
- | `HaiStorageError.IO_ERROR` | `hai:storage:008` | IO 错误 |
123
- | `HaiStorageError.NETWORK_ERROR` | `hai:storage:009` | 网络错误 |
124
- | `HaiStorageError.NOT_INITIALIZED` | `hai:storage:010` | 未初始化 |
125
- | `HaiStorageError.UNSUPPORTED_TYPE` | `hai:storage:011` | 不支持的存储类型 |
126
- | `HaiStorageError.CONFIG_ERROR` | `hai:storage:012` | 配置错误 |
127
- | `HaiStorageError.PRESIGN_FAILED` | `hai:storage:013` | 预签名 URL 生成失败 |
128
- | `HaiStorageError.UPLOAD_FAILED` | `hai:storage:014` | 上传失败 |
129
- | `HaiStorageError.DOWNLOAD_FAILED` | `hai:storage:015` | 下载失败 |
10
+ | 项目 | 契约 |
11
+ | --- | --- |
12
+ | 能力 | 使用 @h-ai/storage 访问本地/S3 文件、目录和预签名上传下载 |
13
+ | 适用场景 | 文件接口、目录操作或浏览器直传 |
14
+ | 输入 | StorageConfigInput、文件 key、内容、presign 选项 |
15
+ | 输出 | file/dir/presign 操作的 HaiResult;deleteMany 的成功 data/失败 error.ext.deleteMany 保留逐项进度;checksum 生成字符串或校验结果 |
16
+ | 限制 | 仅服务端;预签名仅 S3,local getUrl/putUrl 返回 UNSUPPORTED_OPERATION、publicUrl 为 null;浏览器先检查 API success 再直传。Local list 使用绑定根目录/prefix/delimiter 的无状态游标,files 与 commonPrefixes 共用 maxKeys(1–1000);可跨实例续页,每次仍扫描排序且非快照,游标不代替前缀授权。putUrl 不提供 maxSize 强制约束,上传大小须服务端验证。 |
130
17
 
131
- ---
132
-
133
- ## API 契约(`@h-ai/storage/api`)
134
-
135
- `@h-ai/storage/api` 子路径导出所有存储端点的 Zod Schema 和端点契约定义,客户端与服务端共享同一份真相源,编译时保证 I/O 一致性。
136
-
137
- ### 端点定义 — `storageEndpoints`
138
-
139
- | 端点名 | method | path | input Schema | output Schema |
140
- | ------------------ | ------ | ---------------------------- | ------------------------ | ------------------------ |
141
- | `presignDownload` | POST | `/storage/presign/download` | `PresignGetInputSchema` | `PresignUrlOutputSchema` |
142
- | `presignUpload` | POST | `/storage/presign/upload` | `PresignPutInputSchema` | `PresignUrlOutputSchema` |
143
- | `fileInfo` | POST | `/storage/file/info` | `FileInfoInputSchema` | `FileMetadataSchema` |
144
- | `listFiles` | GET | `/storage/files` | inline(prefix/maxKeys) | `ListFilesOutputSchema` |
145
- | `deleteFile` | POST | `/storage/file/delete` | `DeleteFileInputSchema` | `z.void()` |
146
- | `deleteFiles` | POST | `/storage/files/delete` | `DeleteFilesInputSchema` | `z.void()` |
147
-
148
- ### 使用示例
149
-
150
- ```typescript
151
- import { storageEndpoints, PresignPutInputSchema } from '@h-ai/storage/api'
152
-
153
- // 客户端:通过 api-client 调用端点
154
- const { url } = await api.call(storageEndpoints.presignUpload, { key: 'avatar.png' })
155
-
156
- // 服务端:基于契约定义路由
157
- export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input) => {
158
- const result = await storage.presign.putUrl(input.key, input)
159
- return result.success ? result.data : kit.response.internalError(result.error.message)
160
- })
161
-
162
- // 独立 Schema 校验
163
- const parsed = PresignPutInputSchema.safeParse(requestBody)
164
- if (!parsed.success) { /* 校验失败 */ }
165
- ```
166
-
167
- ### 导出的 Schema
168
-
169
- - `FileMetadataSchema` — 文件元数据(key / size / contentType / lastModified / etag? / metadata?)
170
- - `PresignGetInputSchema` — 下载签名入参(key + expiresIn?)
171
- - `PresignPutInputSchema` — 上传签名入参(key + contentType? + contentLength? + expiresIn?)
172
- - `PresignUrlOutputSchema` — 签名 URL 出参(url + key + expiresAt?)
173
- - `ListFilesOutputSchema` — 文件列表出参(files + commonPrefixes + isTruncated + nextContinuationToken?)
174
- - `DeleteFileInputSchema` — 删除单文件入参(key)
175
- - `DeleteFilesInputSchema` — 批量删除入参(keys[])
176
- - `FileInfoInputSchema` — 文件信息入参(key)
177
-
178
- ### 导出的推导类型
179
-
180
- `PresignGetInput` / `PresignPutInput` / `PresignUrlOutput` / `ListFilesOutput` / `DeleteFileInput` / `DeleteFilesInput` / `FileInfoInput`
181
-
182
- ---
183
-
184
- ## 常见模式
18
+ ## 使用路径
185
19
 
186
- ### 与 kit 集成(文件上传 API)
20
+ 1. Node 端配置 Local/S3,校验并检查 init;MinIO/RustFS 使用 s3 类型。S3 `createBucket` 默认 false,需自动建桶时设 true(YAML 用 `${HAI_STORAGE_CREATEBUCKET:false}`);初始化只在 HeadBucket 明确 404 时创建,权限/网络/建桶失败会阻止就绪。默认自建 S3 部署通过环境启用此项。
21
+ 2. 从 file/dir/presign 选择操作,检查 HaiResult。流与 checksum 按真实签名处理,checksum 在 Node 无需 init。S3 内部 endpoint 与浏览器 publicEndpoint 分开:publicEndpoint 无默认,缺省仍用 endpoint;可带 /delivery/files 等安全代理目录,不含桶、凭据或查询。含目录时外部签名用桶路径,返回 URL 已加前缀,代理去前缀后须保留原始 S3 URI/Host/query;调用方不能自行改签名地址。publicUrl 仅拼接公开资源,GET 签名不能用于 HEAD;跨源 CORS 在实际 S3 配置。
22
+ 3. 客户端经受保护 API 或预签名 URL,服务端校验路径、内容、大小及资源权限;putUrl 不强制 maxSize。
23
+ 进度上传使用原生 XHR;预取消不发送,取消/错误一次结算并清理监听,完成后取消不影响旧请求。取消不回滚远端对象,先核对状态再授权删除。
24
+ 4. 不覆盖上传使用 ifNoneMatch;冲突/checksum 失败不能伪装成功。验证取消、流错误和文件清理,最后 close。
25
+ 5. 批量删除核对逐项进度,S3 每批最多 1000 项;HTTP 200 不能代替成功判断。unknown 结果先核对业务状态,避免重删并发新对象;dir.delete 不保证隔离并发写入或清除 S3 历史版本。
26
+ 6. 自建 Compose 的桶路径由 Web Nginx 或 SSR/API gateway 直达 storage,下载/Range 不经过 Serv,存储端口和控制台不公开。publicEndpoint 可含入口前缀,Nginx 用原始 request_uri 去前缀且保留编码、Host、签名查询,外层代理不能再剥离;入口可在网站目录内,拒绝 API/资源/健康冲突。新增网关、变更桶/公开地址需全量基线,增量及回滚保持原配置。外部 S3 不生成本地路由,Local 不开放目录。验证大文件、Range、过期/篡改/匿名拒绝、根/前缀路径和增量代理 DNS 刷新;签名 URL 不进入日志或截图。
27
+ 7. 同源对象可能包含不可信 HTML;默认代理返回 CSP sandbox/default-src none、nosniff、no-referrer、private/no-store,不用于执行网站脚本。外部 S3/CDN 须配置等价策略。自建 bucket/region 为空时由部署补 hai/us-east-1,不能误写成 Storage Schema 默认值。
187
28
 
188
- ```typescript
189
- import { storage } from '$lib/server/init'
190
- // src/routes/api/files/upload/+server.ts
191
- import { kit } from '@h-ai/kit'
192
- import { z } from 'zod'
29
+ ## 按需参考
193
30
 
194
- const UploadSchema = z.object({
195
- key: z.string().min(1),
196
- })
31
+ 按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
197
32
 
198
- export const POST = kit.handler(async ({ request, locals }) => {
199
- kit.guard.requirePermission(locals.session, 'storage:write')
200
- const formData = await request.formData()
201
- const file = formData.get('file') as File
202
- if (!file)
203
- return kit.response.badRequest('Missing file')
204
-
205
- const key = `uploads/${locals.session.userId}/${file.name}`
206
- const buffer = new Uint8Array(await file.arrayBuffer())
207
- const result = await storage.file.put(key, buffer, { contentType: file.type })
208
- if (!result.success)
209
- return kit.response.internalError()
210
- return kit.response.created({ key })
211
- })
212
- ```
213
-
214
- > 说明:`@h-ai/storage` 只提供存储能力,不强制要求 `/api/storage` 路由命名。应用可按业务语义自定义 API 路径。
215
-
216
- ### 头像上传
217
-
218
- ```typescript
219
- const key = `avatars/${userId}/${Date.now()}.png`
220
- const result = await storage.file.put(key, avatarBuffer, {
221
- contentType: 'image/png',
222
- })
223
- if (result.success) {
224
- const publicUrl = storage.presign.publicUrl(key)
225
- await iam.user.updateUser(userId, { avatarUrl: publicUrl ?? '' })
226
- }
227
- ```
228
-
229
- ---
230
-
231
- ## 浏览器端文件上传(预签名 URL 模式)
232
-
233
- 浏览器端无法直接使用 `storage` 模块,应通过 API 获取预签名 URL 后直传:
234
-
235
- ```typescript
236
- import { apiFetch } from '$lib/utils/api'
237
-
238
- // 1. 向服务端请求预签名上传 URL
239
- const res = await apiFetch('/api/storage/presign/upload', {
240
- method: 'POST',
241
- headers: { 'Content-Type': 'application/json' },
242
- body: JSON.stringify({ key: `uploads/${file.name}`, contentType: file.type }),
243
- })
244
- const { url } = await res.json()
245
-
246
- // 2. 浏览器直传到存储(不经过服务端,大文件友好)
247
- await fetch(url, {
248
- method: 'PUT',
249
- body: file,
250
- headers: { 'Content-Type': file.type },
251
- })
252
- ```
253
-
254
- 也可使用契约调用(需配合 `@h-ai/api-client`):
255
-
256
- ```typescript
257
- import { api } from '@h-ai/api-client'
258
- import { storageEndpoints } from '@h-ai/storage/api'
259
-
260
- const result = await api.call(storageEndpoints.presignUpload, {
261
- key: `avatars/${userId}.png`,
262
- contentType: 'image/png',
263
- })
264
- if (result.success) {
265
- await fetch(result.data.url, { method: 'PUT', body: file })
266
- }
267
- ```
268
-
269
- ---
33
+ - [运行环境](reference.md#运行环境)
34
+ - [适用场景](reference.md#适用场景)
35
+ - [使用步骤](reference.md#使用步骤)
36
+ - [核心 API](reference.md#核心-api)
37
+ - [错误码 — `HaiStorageError`](reference.md#错误码--haistorageerror)
38
+ - [HTTP API 契约](reference.md#http-api-契约)
39
+ - [常见模式](reference.md#常见模式)
40
+ - [浏览器端文件上传(预签名 URL 模式)](reference.md#浏览器端文件上传预签名-url-模式)
41
+ - [相关 Skills](reference.md#相关-skills)
270
42
 
271
- ## 相关 Skills
43
+ ## 跨端输入与能力约束
272
44
 
273
- - `hai-build`:模块初始化顺序
274
- - `hai-core`:配置与 HaiResult 模型
275
- - `hai-kit`:SvelteKit API 端点集成(`kit.handler` + `kit.guard`)
276
- - `hai-ui`:Storage 场景组件(FileUpload/ImageUpload/AvatarUpload)
45
+ key 统一使用 `@h-ai/storage/schemas` 的 StorageKeySchema(UTF-8 最多1024字节,无绝对路径/反斜线/控制符/点路径段),prefix另允许空串与部分文件名。HTTP批量删除按原数组限制1–1000项,不能以去重绕过;Node仍按Provider分批。签名有效期整数1–604800秒,Node默认3600、HTTP默认900。HTTP上传只接受key/contentType/expiresIn,不接受contentLength/maxSize/metadata/checksum等file.put选项;未知字段返回400,typed client为VALIDATION_FAILED。Node流式读写、Range、checksum、条件写等无默认HTTP端点。FileUpload大小/类型仅为前端选择体验,服务端仍需验证实际对象与归属;不要编造上传代理端点。校验不替代session资源授权及Provider防逃逸。
@@ -0,0 +1,284 @@
1
+ # hai-storage 参考
2
+
3
+ > `@h-ai/storage` 提供统一文件存储接口,支持本地文件系统(LocalProvider)和 S3 兼容存储(S3Provider),通过 `storage.file`、`storage.dir`、`storage.presign` 访问存储,`storage.checksum` 提供 Node 端无需初始化的字节完整性工具。
4
+
5
+ ## 运行环境
6
+
7
+ > ⚠️ **服务端模块**。`storage.file` / `storage.dir` / `storage.presign` 均在 Node.js 端使用。浏览器端通过预签名 URL 或 API 端点间接访问存储(见下方「浏览器端文件上传」)。
8
+
9
+ ## 适用场景
10
+
11
+ - 文件上传与下载
12
+ - 预签名 URL 生成(客户端直传)
13
+ - S3 兼容存储对接(AWS S3、MinIO、RustFS、阿里云 OSS 等)
14
+ - 本地开发文件存储
15
+ - 目录前缀清理与批量文件删除
16
+
17
+ ## 使用步骤
18
+
19
+ ### 1. 配置
20
+
21
+ ```yaml
22
+ # config/_storage.yml
23
+ type: local # local | s3
24
+ # 本地存储
25
+ root: ./data/storage
26
+ # S3 存储(type=s3 时生效)
27
+ # endpoint: ''
28
+ # publicEndpoint: https://app.example.com/delivery/files # 可含代理目录,不含桶;网关去此前缀后直达 S3
29
+ # region: us-east-1
30
+ # accessKeyId: ''
31
+ # secretAccessKey: ''
32
+ # bucket: my-bucket
33
+ # createBucket: ${HAI_STORAGE_CREATEBUCKET:false}
34
+ # forcePathStyle: false
35
+ # prefix: ''
36
+ # publicUrl: ''
37
+ operationLog:
38
+ read: false # file.get/head/exists, dir.list, presign.getUrl/publicUrl
39
+ write: false # file.put/delete/deleteMany/copy, dir.delete, presign.putUrl
40
+ maxLength: 1000
41
+ level: debug # info | debug | trace
42
+ ```
43
+
44
+ 约定环境变量优先于 YAML,例如 `HAI_STORAGE_ROOT`、
45
+ `HAI_STORAGE_ACCESSKEYID`、`HAI_STORAGE_SECRETACCESSKEY`。
46
+
47
+ MinIO、RustFS 使用同一个 S3 Provider:配置 `type: s3`、S3 API `endpoint`(通常为 9000 端口)和 `forcePathStyle: true`。`createBucket` 默认 false,需要预先创建 `bucket`;设为 true 后,`storage.init` 用 HeadBucket 检查,明确 404 时才创建,存在的桶及其中对象保留。权限、网络或创建失败均返回失败且不发布连接;凭据须有检查及创建桶的权限。不要新增 `type: minio` 或 `type: rustfs`。
48
+
49
+ 使用 hai-deploy 的默认自建 S3 配置时,可通过 `hai.json` 的 `deploy.container.s3Engine`(`HAI_DEPLOY_CONTAINER_S3ENGINE`,`rustfs | minio`)选择实现,默认 RustFS。运行环境启用 `HAI_STORAGE_CREATEBUCKET=true` 后由应用初始化 storage 时建桶;部署不再生成独立初始化服务或脚本。自定义外部 S3 默认不建桶,可按凭据权限显式启用。
50
+
51
+ `operationLog` 在 Local/S3 Provider 的真实 file/dir/presign 操作处输出日志,不要在 `storage-main.ts` 或调用方重复包装。文件内容不会写入日志,上传数据只记录字节长度;`maxLength` 用于截断序列化后的参数,`level` 默认 `debug`。
52
+
53
+ ### 2. 初始化与关闭
54
+
55
+ ```typescript
56
+ import { storage } from '@h-ai/storage'
57
+
58
+ await storage.init(core.config.get('storage'))
59
+ // 使用后关闭
60
+ const closeResult = await storage.close()
61
+ if (!closeResult.success) {
62
+ throw new Error(closeResult.error.message)
63
+ }
64
+ ```
65
+
66
+
67
+ 初始化只在健康检查成功后发布连接;失败时释放候选资源。若清理仍失败,初始化错误的 `error.ext.cleanupPending` 为 `true`。`storage.close()` 返回 `Promise<HaiResult<void>>`,等待正在进行的初始化,并合并并发关闭。开始清理后 `isInitialized` 为 `false`,配置与业务操作不再可用;清理失败仍保留资源,后续 `close()` 或 `init()` 先重试释放,成功前不会创建替代连接。调用方必须检查关闭结果。
68
+
69
+ ```ts
70
+ const closed = await storage.close()
71
+ if (!closed.success) {
72
+ // 处理清理故障后有限重试;再次失败应向应用报告。
73
+ const retried = await storage.close()
74
+ if (!retried.success) throw new Error(retried.error.message)
75
+ }
76
+ ```
77
+
78
+
79
+ > `storage.config` 返回的是**脱敏配置快照**;S3 的 `accessKeyId` / `secretAccessKey` 等敏感字段会被替换为 `[REDACTED]`。
80
+
81
+ ## 核心 API
82
+
83
+ ### 文件操作
84
+
85
+ `storage.file` 提供以下方法:
86
+
87
+ - `put(key, data, options?)`
88
+ - `get(key, options?)`
89
+ - `getStream(key, options?)`(字节流,迭代错误可抛出)
90
+ - `head(key)`
91
+ - `exists(key)`
92
+ - `delete(key)`
93
+ - `deleteMany(keys)`
94
+ - `copy(sourceKey, destKey, options?)`
95
+
96
+ ```typescript
97
+ // 上传文件
98
+ const result = await storage.file.put('avatars/user-123.png', fileBuffer, {
99
+ contentType: 'image/png',
100
+ metadata: { userId: '123' },
101
+ })
102
+
103
+ // 下载文件
104
+ const file = await storage.file.get('avatars/user-123.png')
105
+
106
+ // 列出文件
107
+ const files = await storage.dir.list({ prefix: 'avatars/', maxKeys: 100 })
108
+ ```
109
+
110
+ `file.deleteMany(keys)` 返回 `StorageDeleteResult`:成功时 `data` 为 `{ deletedKeys, failures: [] }`;任一删除项未确认完成时返回 `OPERATION_FAILED`,进度在 `error.ext?.deleteMany`(初始化等前置错误可能不带进度)。重复输入只处理一次,输出 key 不含配置 prefix。S3 每批最多 1000 项,逐项检查 `Deleted` 与 `Errors`;HTTP 200 不代表全部成功,遗漏/重复/未知回执标为 `unknown`。本地删除最多并发 32 项。
111
+
112
+ 只对明确的 `InternalError / ServiceUnavailable / SlowDown / RequestTimeout` 项最多尝试三次;成功及权限失败项不重发。`DeleteObjects` 专用客户端禁用 SDK 请求重试;网络中断立即标为 `unknown`,未发送的后续批次标为 `notAttempted`,不盲目重放未知结果。`retryable` 只表示故障类别,调用方重试前仍须核对未知结果及并发写入。`dir.delete()` 传播删除失败;它不能阻止并发新增对象。S3 使用不带 VersionId 的删除,开启版本控制的桶可能创建删除标记,不保证清除历史版本。
113
+
114
+ `@h-ai/serv` / `@h-ai/api-client` 的 `storage.files.deleteMany` 使用同一成功结构并透传失败进度。
115
+
116
+ ```ts
117
+ const result = await storage.file.deleteMany(['a.txt', 'b.txt'])
118
+ if (!result.success) {
119
+ const progress = result.error.ext?.deleteMany
120
+ // 结合 outcome 与业务写入状态决定补偿;未知结果不能当作未执行。
121
+ const knownFailures = progress?.failures.filter(item => item.outcome === 'failed' && item.retryable) ?? []
122
+ }
123
+ ```
124
+
125
+ ### 预签名 URL
126
+
127
+ `storage.presign` 提供以下方法:
128
+
129
+ - `getUrl(key, options?)`
130
+ - `putUrl(key, options?)`
131
+ - `publicUrl(key)`
132
+
133
+ 预签名仅支持 S3;local `getUrl` / `putUrl` 返回 `HaiStorageError.UNSUPPORTED_OPERATION`(hai:storage:018),`publicUrl` 返回 null,不生成虚拟 URL。serv 与 apiClient 保留失败,须先检查 success;本地文件由应用已有的鉴权接口上传/下载,不假定框架内置签名路由。
134
+
135
+ ```typescript
136
+ // 客户端直传模式
137
+ const uploadUrl = await storage.presign.putUrl('uploads/doc.pdf', {
138
+ expiresIn: 3600, // 1 小时有效
139
+ contentType: 'application/pdf',
140
+ })
141
+ if (!uploadUrl.success) return uploadUrl
142
+
143
+ // 临时下载链接
144
+ const downloadUrl = await storage.presign.getUrl('uploads/doc.pdf', {
145
+ expiresIn: 300, // 5 分钟有效
146
+ })
147
+ if (!downloadUrl.success) return downloadUrl
148
+ ```
149
+
150
+ > `storage.presign.putUrl()` 当前不支持 `maxSize` 约束参数;如需大小限制,请在应用层上传前校验,或在服务端落库/回调处二次校验。
151
+
152
+ ## 错误码 — `HaiStorageError`
153
+
154
+ | 错误码 | code | 说明 |
155
+ |--------|------|------|
156
+ | `HaiStorageError.CONNECTION_FAILED` | `hai:storage:001` | 连接失败 |
157
+ | `HaiStorageError.OPERATION_FAILED` | `hai:storage:002` | 操作失败 |
158
+ | `HaiStorageError.NOT_FOUND` | `hai:storage:003` | 文件不存在 |
159
+ | `HaiStorageError.ALREADY_EXISTS` | `hai:storage:004` | 文件已存在 |
160
+ | `HaiStorageError.CHECKSUM_FAILED` | `hai:storage:016` | checksum 读取/计算失败 |
161
+ | `HaiStorageError.INVALID_CHECKSUM` | `hai:storage:017` | checksum 格式/算法非法 |
162
+ | `HaiStorageError.UNSUPPORTED_OPERATION` | `hai:storage:018` | 当前 Provider 不支持该操作(local 预签名) |
163
+ | `HaiStorageError.INVALID_LIST_OPTIONS` | `hai:storage:019` | 本地列表参数或游标无效、查询范围不匹配 |
164
+ | `HaiStorageError.PERMISSION_DENIED` | `hai:storage:005` | 权限不足 |
165
+ | `HaiStorageError.QUOTA_EXCEEDED` | `hai:storage:006` | 配额超限 |
166
+ | `HaiStorageError.INVALID_PATH` | `hai:storage:007` | 路径无效 |
167
+ | `HaiStorageError.IO_ERROR` | `hai:storage:008` | IO 错误 |
168
+ | `HaiStorageError.NETWORK_ERROR` | `hai:storage:009` | 网络错误 |
169
+ | `HaiStorageError.NOT_INITIALIZED` | `hai:storage:010` | 未初始化 |
170
+ | `HaiStorageError.UNSUPPORTED_TYPE` | `hai:storage:011` | 不支持的存储类型 |
171
+ | `HaiStorageError.CONFIG_ERROR` | `hai:storage:012` | 配置错误 |
172
+ | `HaiStorageError.PRESIGN_FAILED` | `hai:storage:013` | 预签名 URL 生成失败 |
173
+ | `HaiStorageError.UPLOAD_FAILED` | `hai:storage:014` | 上传失败 |
174
+ | `HaiStorageError.DOWNLOAD_FAILED` | `hai:storage:015` | 下载失败 |
175
+
176
+ ## HTTP API 契约
177
+
178
+ 公共 HTTP API 统一由 `@h-ai/api-contract` 提供,并由 `@h-ai/serv/features/storage` 绑定到本模块。
179
+
180
+ ```typescript
181
+ import { apiClient } from '@h-ai/api-client'
182
+
183
+ const result = await apiClient.storage.presignedUrls.createUpload({ key: 'avatar.png' })
184
+ if (!result.success) return result
185
+ ```
186
+
187
+ ## 常见模式
188
+
189
+ 大文件上传用 `file.put(key, readableOrAsyncByteStream, { signal, ifNoneMatch: '*' })`,内部使用有界 S3 multipart;下载用 `file.getStream(key, { signal, rangeStart, rangeEnd })`,及时消费或关闭,迭代期间 I/O/取消可能抛错。本地读取目录直接返回 `INVALID_PATH`。checksum 的 signal 取消会停止等待下一块数据并请求关闭输入迭代器。`get()` 返回完整 Buffer,只用于容量可控的数据。仅创建写入采用 S3 标准条件或本地原子发布,冲突返回 `ALREADY_EXISTS`;后端不支持时失败,不退回覆盖。
190
+
191
+ `checksum.generate(data, options?)` / `checksum.verify(data, expected, options?)` 默认 SHA-256、小写 hex,可选 sha384/sha512/sha1/md5、base64 编码与 signal;流仅消费一次,读取失败返回 HaiResult 错误,不匹配返回 false。MD5/SHA-1 不作安全认证,ETag 和 multipart 复合 checksum 不当作完整文件摘要。浏览器不导入 Node checksum;离线增量摘要需使用适用的浏览器实现,不把 SubtleCrypto.digest 当流式 API。
192
+
193
+ ### 与 kit 集成(文件上传 API)
194
+
195
+ ```typescript
196
+ import { storage } from '$lib/server/init'
197
+ // src/routes/api/files/upload/+server.ts
198
+ import { kit } from '@h-ai/kit'
199
+ import { z } from 'zod'
200
+
201
+ const UploadSchema = z.object({
202
+ key: z.string().min(1),
203
+ })
204
+
205
+ export const POST = kit.handler(async ({ request, locals }) => {
206
+ kit.guard.require(locals.session, 'storage:write')
207
+ const formData = await request.formData()
208
+ const file = formData.get('file') as File
209
+ if (!file)
210
+ return kit.response.badRequest('Missing file')
211
+
212
+ const key = `uploads/${locals.session.userId}/${file.name}`
213
+ const buffer = new Uint8Array(await file.arrayBuffer())
214
+ const result = await storage.file.put(key, buffer, { contentType: file.type })
215
+ if (!result.success)
216
+ return kit.response.internalError()
217
+ return kit.response.created({ key })
218
+ })
219
+ ```
220
+
221
+ > 说明:`@h-ai/storage` 只提供存储能力,不强制要求 `/api/storage` 路由命名。应用可按业务语义自定义 API 路径。
222
+
223
+ ### 列表分页
224
+
225
+ Local 的 `prefix` 是 key 字符串前缀,可匹配 `reports/2026-` 这类不以斜杠结尾的前缀。按 UTF-8 字节序返回,`maxKeys` 为 1–1000 的整数、默认 1000,`files` 与 `commonPrefixes` 合计占用预算。`delimiter` 按 prefix 后首次出现的位置分组,空字符串不分组;空物理目录不产生条目。分组与计数参照 [S3 ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html)。
226
+
227
+ 本地不透明游标绑定规范根目录、prefix 和 delimiter,不依赖进程缓存;同根目录的独立实例或重启后可以续页,下一页可调整 maxKeys。格式、版本或范围不匹配返回 `INVALID_LIST_OPTIONS`。只在确有下一条时返回 `isTruncated: true` 和 `nextContinuationToken`。游标不是授权凭据,HTTP 服务仍须授权 prefix;它不提供快照,并发增删可影响后续页。不同挂载路径不视为同一根目录。每次请求仍扫描排序匹配目录,大目录应缩小 prefix 或使用 S3。
228
+
229
+ ```ts
230
+ let continuationToken: string | undefined
231
+ do {
232
+ const page = await storage.dir.list({ prefix: 'reports/2026-', delimiter: '/', maxKeys: 100, continuationToken })
233
+ if (!page.success) return page
234
+ // 消费当前页的 files 和 commonPrefixes。
235
+ continuationToken = page.data.nextContinuationToken
236
+ } while (continuationToken)
237
+ ```
238
+
239
+ ### 头像上传
240
+
241
+ ```typescript
242
+ const key = `avatars/${userId}/${Date.now()}.png`
243
+ const result = await storage.file.put(key, avatarBuffer, {
244
+ contentType: 'image/png',
245
+ })
246
+ if (result.success) {
247
+ const publicUrl = storage.presign.publicUrl(key)
248
+ await iam.user.updateUser(userId, { avatarUrl: publicUrl ?? '' })
249
+ }
250
+ ```
251
+
252
+ ## 浏览器端文件上传(预签名 URL 模式)
253
+
254
+ `uploadWithPresignedUrl` 传 `onProgress` 时使用 XHR,否则使用 fetch。`abortController` 预取消不发送;准备中/进行中取消、外部 XHR abort、网络/超时和进度回调异常一次结算失败并解绑本次监听,完成后取消不会触碰旧 XHR。取消不能撤销远端已发布的对象;应用先核对业务/对象状态,再授权清理,不把本地取消当作服务端回滚。实际 Chrome 门禁设置 `HAI_STORAGE_RUN_BROWSER_GATES=1`,执行 `pnpm --filter @h-ai/storage exec vitest run tests/storage-client-browser.test.ts`;浏览器缺失直接失败。
255
+
256
+ 浏览器端无法直接使用 `storage` 模块。服务端使用 S3 时,通过 typed API 获取预签名 URL,检查失败后再直传:
257
+
258
+ ```typescript
259
+ import { apiClient } from '@h-ai/api-client'
260
+ import { uploadWithPresignedUrl } from '@h-ai/storage/client'
261
+
262
+ // apiClient 已初始化;用户和资源范围由服务端授权策略决定。
263
+ const result = await apiClient.storage.presignedUrls.createUpload({
264
+ key: `avatars/${userId}.png`,
265
+ contentType: 'image/png',
266
+ })
267
+ if (!result.success) return result
268
+ return uploadWithPresignedUrl(result.data.url, file, { contentType: 'image/png' })
269
+ ```
270
+
271
+ ## 相关 Skills
272
+
273
+ - `hai-build`:模块初始化顺序
274
+ - `hai-core`:配置与 HaiResult 模型
275
+ - `hai-kit`:SvelteKit API 端点集成(`kit.handler` + `kit.guard`)
276
+ - `hai-ui`:Storage 场景组件(FileUpload/ImageUpload/AvatarUpload)
277
+
278
+ ## 生产共享状态
279
+
280
+ 生产使用共享 S3 或兼容服务;local 只用于开发/明确测试。对象业务授权和读改写的条件提交由应用负责。 NODE_ENV 或 HAI_CORE_ENV 为 production 时在初始化成功前检查,只有 HAI_E2E=1/HAI_E2E_MOCK=1 明确测试豁免。不要将租约当作最终写入条件,外部未知效果须按稳定业务键核对。
281
+
282
+ ## 跨端输入与能力约束
283
+
284
+ key 统一使用 `@h-ai/storage/schemas` 的 StorageKeySchema(UTF-8 最多1024字节,无绝对路径/反斜线/控制符/点路径段),prefix另允许空串与部分文件名。HTTP批量删除按原数组限制1–1000项,不能以去重绕过;Node仍按Provider分批。签名有效期整数1–604800秒,Node默认3600、HTTP默认900。HTTP上传只接受key/contentType/expiresIn,不接受contentLength/maxSize/metadata/checksum等file.put选项;未知字段返回400,typed client为VALIDATION_FAILED。Node流式读写、Range、checksum、条件写等无默认HTTP端点。FileUpload大小/类型仅为前端选择体验,服务端仍需验证实际对象与归属;不要编造上传代理端点。校验不替代session资源授权及Provider防逃逸。