@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.21

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 (254) hide show
  1. package/README.en.md +74 -154
  2. package/README.ja.md +74 -154
  3. package/README.md +74 -154
  4. package/dist/{chunk-AKV2CQZV.js → chunk-2ZL5ORQ2.js} +316 -97
  5. package/dist/chunk-2ZL5ORQ2.js.map +1 -0
  6. package/dist/{chunk-EF7WOFWZ.js → chunk-L53P43B2.js} +1312 -163
  7. package/dist/chunk-L53P43B2.js.map +1 -0
  8. package/dist/cli-config-templates-LWMD2I6V.js +5 -0
  9. package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
  10. package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
  11. package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +30118 -3253
  14. package/dist/index.js.map +1 -1
  15. package/dist/metafile-esm.json +1 -1
  16. package/package.json +22 -18
  17. package/templates/README.md +45 -30
  18. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
  19. package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
  20. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
  21. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
  22. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
  23. package/templates/apps/api/.dockerignore.hbs +10 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +8 -0
  25. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
  27. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
  28. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  29. package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +6 -2
  30. package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +24 -2
  31. package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
  32. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +74 -24
  33. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  34. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +153 -37
  35. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
  36. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +106 -33
  37. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
  38. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  39. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
  40. package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
  41. package/templates/apps/api/eslint.config.js.hbs +3 -1
  42. package/templates/apps/api/images/serv.Dockerfile.hbs +15 -10
  43. package/templates/apps/api/playwright.config.ts.hbs +23 -19
  44. package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
  45. package/templates/apps/fullstack/.dockerignore.hbs +10 -0
  46. package/templates/apps/fullstack/.gitignore.hbs +7 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +14 -12
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +5 -3
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
  50. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
  51. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
  56. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -8
  57. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +3 -3
  58. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
  59. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
  60. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +0 -2
  61. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
  62. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
  63. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
  64. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +9 -9
  65. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
  66. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +1 -0
  67. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
  68. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
  69. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
  70. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
  71. package/templates/apps/fullstack/docs/security-configuration.md.hbs +4 -1
  72. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +140 -28
  73. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
  74. package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
  75. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +19 -11
  76. package/templates/apps/fullstack/images/web.Dockerfile.hbs +17 -8
  77. package/templates/apps/fullstack/package.json.hbs +2 -1
  78. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +7 -5
  79. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
  80. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
  81. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  82. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +17 -7
  83. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +13 -7
  84. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +17 -7
  85. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -4
  86. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
  87. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  88. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
  89. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
  90. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
  91. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
  92. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
  93. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
  94. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +147 -33
  95. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
  96. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +146 -30
  97. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
  98. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +142 -26
  99. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
  100. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  101. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +48 -66
  102. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +1 -0
  103. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +1 -0
  104. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +1 -0
  105. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
  106. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
  107. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
  108. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
  109. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
  110. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
  116. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
  117. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
  118. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
  119. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
  120. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
  121. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
  122. package/templates/apps/fullstack/playwright.config.ts.hbs +25 -3
  123. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -14
  124. package/templates/apps/h5/src/routes/+layout.svelte.hbs +74 -30
  125. package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
  126. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
  127. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
  128. package/templates/apps/mobile-app/capacitor.config.ts.hbs +5 -2
  129. package/templates/apps/mobile-app/messages/en-US.json +2 -2
  130. package/templates/apps/mobile-app/messages/ja-JP.json +2 -2
  131. package/templates/apps/mobile-app/messages/zh-CN.json +2 -2
  132. package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
  133. package/templates/apps/mobile-app/src/lib/api.ts.hbs +20 -6
  134. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
  135. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
  136. package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -1
  137. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
  138. package/templates/apps/website/scripts/static-website.mjs.hbs +19 -6
  139. package/templates/apps/website/src/routes/+layout.svelte.hbs +56 -29
  140. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
  141. package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
  142. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
  143. package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
  144. package/templates/base/.dockerignore.hbs +10 -0
  145. package/templates/base/.gitignore.hbs +7 -2
  146. package/templates/base/e2e/app.spec.ts.hbs +130 -36
  147. package/templates/base/eslint-rules.js +44 -25
  148. package/templates/base/eslint.config.js.hbs +3 -1
  149. package/templates/base/images/web.Dockerfile.hbs +25 -14
  150. package/templates/base/package.json.hbs +13 -7
  151. package/templates/base/patches/daisyui-5.7.50.patch +8 -0
  152. package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
  153. package/templates/base/playwright.config.ts +3 -1
  154. package/templates/base/pnpm-workspace.yaml.hbs +33 -16
  155. package/templates/base/src/app.d.ts.hbs +1 -1
  156. package/templates/base/src/app.html.hbs +1 -0
  157. package/templates/base/src/hooks.server.ts.hbs +44 -33
  158. package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
  159. package/templates/base/src/lib/server/init.ts.hbs +146 -35
  160. package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
  161. package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
  162. package/templates/base/svelte.config.js.hbs +10 -100
  163. package/templates/base/tests/scaffold.test.ts.hbs +12 -3
  164. package/templates/base/tsconfig.json.hbs +7 -4
  165. package/templates/base/vite.config.ts.hbs +108 -14
  166. package/templates/base/vitest.config.ts.hbs +6 -2
  167. package/templates/features/ai/routes/api/chat/+server.ts +3 -1
  168. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
  169. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
  170. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
  171. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
  172. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
  173. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
  174. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
  175. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
  176. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
  177. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
  178. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
  179. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
  180. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +14 -3
  181. package/templates/shared/browser-check/startup.js +115 -0
  182. package/templates/shared/browser-check/update.css +37 -0
  183. package/templates/shared/browser-check/update.html +19 -0
  184. package/templates/shared/browser-startup.spec.ts +23 -0
  185. package/templates/shared/build-metadata.ts +18 -4
  186. package/templates/shared/e2e-env.ts +22 -6
  187. package/templates/shared/native-name.d.mts +5 -0
  188. package/templates/shared/native-name.mjs +94 -0
  189. package/templates/shared/public-base-path.mjs +17 -4
  190. package/templates/shared/run-e2e.mjs +46 -12
  191. package/templates/shared/run-node.mjs +56 -0
  192. package/templates/shared/run-tauri.mjs +74 -0
  193. package/templates/shared/vite-runtime-config.ts +9 -3
  194. package/templates/skills/AGENTS.md +10 -73
  195. package/templates/skills/bridges/admin/AGENTS.md +9 -65
  196. package/templates/skills/bridges/api/AGENTS.md +8 -59
  197. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
  198. package/templates/skills/bridges/h5/AGENTS.md +8 -63
  199. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
  200. package/templates/skills/bridges/website/AGENTS.md +8 -63
  201. package/templates/skills/hai-ai/SKILL.md +6 -2
  202. package/templates/skills/hai-ai/acp.md +153 -0
  203. package/templates/skills/hai-ai/guide.md +27 -12
  204. package/templates/skills/hai-ai/reference.md +24 -15
  205. package/templates/skills/hai-ai/resources.md +69 -0
  206. package/templates/skills/hai-api-client/SKILL.md +1 -1
  207. package/templates/skills/hai-api-client/reference.md +3 -1
  208. package/templates/skills/hai-app-create/collaboration.md +7 -0
  209. package/templates/skills/hai-app-create/development.md +19 -10
  210. package/templates/skills/hai-app-create/project.md +33 -0
  211. package/templates/skills/hai-app-create/reference.md +10 -10
  212. package/templates/skills/hai-app-tests/reference.md +1 -1
  213. package/templates/skills/hai-app-tests/validation.md +1 -0
  214. package/templates/skills/hai-build/SKILL.md +19 -9
  215. package/templates/skills/hai-build/paths.md +4 -2
  216. package/templates/skills/hai-build/reference.md +26 -8
  217. package/templates/skills/hai-cache/reference.md +6 -2
  218. package/templates/skills/hai-capacitor/SKILL.md +1 -0
  219. package/templates/skills/hai-capacitor/reference.md +24 -0
  220. package/templates/skills/hai-ci/SKILL.md +3 -0
  221. package/templates/skills/hai-core/SKILL.md +9 -4
  222. package/templates/skills/hai-core/reference.md +40 -10
  223. package/templates/skills/hai-core/request-access.md +2 -2
  224. package/templates/skills/hai-crypto/SKILL.md +2 -2
  225. package/templates/skills/hai-crypto/reference.md +7 -3
  226. package/templates/skills/hai-deploy/SKILL.md +41 -5
  227. package/templates/skills/hai-deploy/middleware.md +26 -14
  228. package/templates/skills/hai-framework-sync/SKILL.md +13 -9
  229. package/templates/skills/hai-framework-sync/sync.md +2 -0
  230. package/templates/skills/hai-iam/reference.md +2 -2
  231. package/templates/skills/hai-kit/SKILL.md +1 -1
  232. package/templates/skills/hai-kit/reference.md +19 -5
  233. package/templates/skills/hai-payment/reference.md +41 -17
  234. package/templates/skills/hai-reach/SKILL.md +3 -1
  235. package/templates/skills/hai-reach/reference.md +29 -9
  236. package/templates/skills/hai-reldb/SKILL.md +1 -1
  237. package/templates/skills/hai-reldb/reference.md +9 -2
  238. package/templates/skills/hai-scheduler/reference.md +1 -1
  239. package/templates/skills/hai-serv/SKILL.md +2 -1
  240. package/templates/skills/hai-serv/reference.md +26 -15
  241. package/templates/skills/hai-storage/reference.md +23 -6
  242. package/templates/skills/hai-ui/SKILL.md +11 -6
  243. package/templates/skills/hai-ui/charts.md +51 -0
  244. package/templates/skills/hai-ui/guide.md +1 -1
  245. package/templates/skills/hai-ui/reference.md +58 -38
  246. package/templates/skills/hai-vecdb/reference.md +8 -3
  247. package/dist/chunk-AKV2CQZV.js.map +0 -1
  248. package/dist/chunk-EF7WOFWZ.js.map +0 -1
  249. package/dist/chunk-MUHRBJSW.js +0 -29272
  250. package/dist/chunk-MUHRBJSW.js.map +0 -1
  251. package/dist/cli-config-templates-HHFHNXBN.js +0 -5
  252. package/dist/src-TEBHR5I5.js +0 -5
  253. package/dist/src-TEBHR5I5.js.map +0 -1
  254. package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +0 -18
@@ -6,7 +6,9 @@ import { ai } from '@h-ai/ai'
6
6
  import { kit } from '@h-ai/kit'
7
7
  import { z } from 'zod'
8
8
 
9
- const ChatInputSchema = z.object({ message: z.string().trim().min(1).max(4000) }).strict()
9
+ const ChatInputSchema = z.object({
10
+ message: z.string().trim().min(1).max(4000),
11
+ }).strict()
10
12
 
11
13
  export const POST: RequestHandler = kit.handler(async ({ request, locals }) => {
12
14
  const { message } = await kit.validate.body(request, ChatInputSchema)
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/admin', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('admin'))} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/admin', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('admin'))} />
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const authActions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const authActions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const authActions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
@@ -1,11 +1,11 @@
1
1
  <script lang='ts'>
2
2
  import { goto } from '$app/navigation'
3
- import { base, resolve } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { page } from '$app/state'
5
5
  import { kit } from '@h-ai/kit'
6
6
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
7
7
 
8
- const authActions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
8
+ const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
9
9
  </script>
10
10
 
11
- <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('/auth/login', {})} registerUrl={resolve('/auth/register', {})} onauthenticated={() => goto(resolve('/', {}))} />
11
+ <AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
@@ -1,10 +1,10 @@
1
1
  <script lang='ts'>
2
- import { base, resolve } from '$app/paths'
2
+ import { resolve } from '$app/paths'
3
3
  import { page } from '$app/state'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
6
6
 
7
- const actions = kit.client.createAuthActions({ basePath: `${base}/api/auth` })
7
+ const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
8
8
  </script>
9
9
 
10
- <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('/auth/login', {})} />
10
+ <AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
@@ -1,10 +1,13 @@
1
1
  /** 创建支付参数 API;渠道、场景与 openid 必须通过显式校验,禁止回落到其它支付模式。 */
2
2
  import type { RequestHandler } from './$types'
3
- import { base } from '$app/paths'
3
+ import { resolve } from '$app/paths'
4
4
  import { kit } from '@h-ai/kit'
5
5
  import { payment } from '@h-ai/payment'
6
6
  import { z } from 'zod'
7
7
 
8
+ // Vite 配置固定 paths.relative=false,HTTP 前缀不会随 SSR 页面深度改变。
9
+ const base = resolve('/').replace(/\/$/, '')
10
+
8
11
  /** 本模板的业务字段;服务端仍需从可信订单重新核对金额、描述、订单号和所属用户。 */
9
12
  const CreatePaymentSchema = z.object({
10
13
  provider: z.enum(['wechat', 'alipay', 'stripe']),
@@ -15,7 +18,13 @@ const CreatePaymentSchema = z.object({
15
18
  userId: z.string().trim().min(1).max(128).optional(),
16
19
  metadata: z.record(z.string(), z.string()).optional(),
17
20
  notifyUrl: z.url({ protocol: /^https?$/ }).optional(),
18
- }).strict().refine(input => input.tradeType === (input.provider === 'wechat' ? 'jsapi' : 'h5'), { path: ['tradeType'] }).refine(input => input.provider !== 'wechat' || input.userId !== undefined, { path: ['userId'] })
21
+ }).strict().refine(input => input.tradeType === (input.provider === 'wechat'
22
+ ? 'jsapi'
23
+ : 'h5'), {
24
+ path: ['tradeType'],
25
+ }).refine(input => input.provider !== 'wechat' || input.userId !== undefined, {
26
+ path: ['userId'],
27
+ })
19
28
 
20
29
  export const POST: RequestHandler = async ({ request, locals }) => {
21
30
  const { provider, tradeType, outTradeNo, totalAmount, subject, notifyUrl, userId, metadata } = await kit.validate.body(request, CreatePaymentSchema)
@@ -28,5 +37,7 @@ export const POST: RequestHandler = async ({ request, locals }) => {
28
37
  metadata,
29
38
  notifyUrl: notifyUrl ?? `${new URL(request.url).origin}${base}/api/v1/payment/notify/${provider}`,
30
39
  })
31
- return kit.response.fromResult(result, locals.requestId)
40
+ if (!result.success)
41
+ return kit.response.fromError(result.error, locals.requestId)
42
+ return kit.response.ok(result.data, locals.requestId)
32
43
  }
@@ -0,0 +1,115 @@
1
+ /* eslint-disable no-var, object-shorthand, prefer-template, prefer-arrow-callback -- 此脚本须在低版本浏览器中直接执行,使用 ES5 语法。 */
2
+ ;(function () {
3
+ var agent = navigator.userAgent
4
+ var root = document.documentElement
5
+ var location = window.location
6
+ var edgeHelp = 'https://support.microsoft.com/edge/microsoft-edge-update-settings'
7
+ var chromeHelp = 'https://support.google.com/chrome/answer/95414'
8
+ var firefoxHelp = 'https://support.mozilla.org/kb/update-firefox-latest-release'
9
+ var safariHelp = 'https://support.apple.com/102665'
10
+ var iosHelp = 'https://support.apple.com/118575'
11
+ var browser = getOutdatedBrowser()
12
+
13
+ // 匹配 Vite 8 的默认构建基线。Edge 的 UA 同时包含 Chrome,iOS 先按系统版本判断。
14
+ function getOutdatedBrowser() {
15
+ var version = /\b(?:CPU (?:iPhone )?OS|iPhone OS) (\d+_\d+(?:_\d+)?)/.exec(agent)
16
+ if (version && /\b(?:iPhone|iPad|iPod)\b/.test(agent)) {
17
+ return outdated('iOS / iPadOS', version, 16, 4, iosHelp)
18
+ }
19
+ version = /\b(?:EdgA?|Edge)\/(\d+(?:\.\d+)*)/.exec(agent)
20
+ if (version) {
21
+ return outdated('Microsoft Edge', version, 111, 0, edgeHelp)
22
+ }
23
+ version = /\bFirefox\/(\d+(?:\.\d+)*)/.exec(agent)
24
+ if (version) {
25
+ return outdated('Firefox', version, 114, 0, firefoxHelp)
26
+ }
27
+ version = /\bChrome\/(\d+(?:\.\d+)*)/.exec(agent)
28
+ if (version) {
29
+ if (/\b(?:wv|OPR\/|SamsungBrowser\/|Vivaldi\/|YaBrowser\/|HeadlessChrome\/)/.test(agent))
30
+ return null
31
+ return outdated('Google Chrome', version, 111, 0, chromeHelp)
32
+ }
33
+ version = /\bVersion\/(\d+(?:\.\d+)*)/.exec(agent)
34
+ if (version && /\bSafari\//.test(agent)) {
35
+ return outdated('Safari', version, 16, 4, safariHelp)
36
+ }
37
+ return null
38
+ }
39
+
40
+ function outdated(name, match, major, minor, help) {
41
+ var version = match[1].replace(/_/g, '.')
42
+ var parts = version.split('.')
43
+ var currentMajor = Number(parts[0])
44
+ var currentMinor = Number(parts[1] || 0)
45
+ if (currentMajor > major || (currentMajor === major && currentMinor >= minor))
46
+ return null
47
+ return {
48
+ name: name,
49
+ version: version,
50
+ minimum: minor
51
+ ? major + '.' + minor
52
+ : String(major),
53
+ help: help,
54
+ }
55
+ }
56
+
57
+ function getLanguage(value) {
58
+ var language = (value || '').toLowerCase().split(/[-_]/)[0]
59
+ return language === 'zh' || language === 'ja' || language === 'en'
60
+ ? language
61
+ : ''
62
+ }
63
+
64
+ function openUpdatePage() {
65
+ // 以静态脚本地址为基准,保留发布目录。仅传语言,不把原页面地址写到升级页。
66
+ var script = document.getElementById('hai-browser-startup')
67
+ var language = getLanguage(root.lang) || getLanguage(navigator.language) || 'en'
68
+ location.replace(script.src.replace(/\/startup\.js$/, '/update.html') + '#' + language)
69
+ }
70
+
71
+ function showUpdateInstructions() {
72
+ var locale = getLanguage(location.hash.slice(1)) || getLanguage(navigator.language) || 'en'
73
+ var messages = {
74
+ zh: [
75
+ '浏览器版本过低',
76
+ '当前使用 {name} {version}。此站点至少需要 {name} {minimum}。',
77
+ '请更新到最新版本,然后重新打开此站点。',
78
+ '查看官方更新说明',
79
+ ],
80
+ en: [
81
+ 'Update your browser',
82
+ 'You are using {name} {version}. This site requires {name} {minimum} or later.',
83
+ 'Install the latest update, then reopen this site.',
84
+ 'Official update instructions',
85
+ ],
86
+ ja: [
87
+ 'ブラウザーを更新してください',
88
+ '現在のバージョンは {name} {version} です。このサイトには {name} {minimum} 以降が必要です。',
89
+ '最新版に更新してから、このサイトを開き直してください。',
90
+ '公式の更新手順を見る',
91
+ ],
92
+ }
93
+ var message = messages[locale]
94
+ var help = document.getElementById('browser-update-help')
95
+ root.lang = locale === 'zh'
96
+ ? 'zh-CN'
97
+ : locale
98
+ document.title = message[0]
99
+ document.getElementById('browser-update-title').textContent = message[0]
100
+ document.getElementById('browser-update-version').textContent = message[1].replace(/\{(name|version|minimum)\}/g, function (_match, key) {
101
+ return browser[key]
102
+ })
103
+ document.getElementById('browser-update-instruction').textContent = message[2]
104
+ help.textContent = message[3]
105
+ help.href = browser.help
106
+ help.hidden = false
107
+ }
108
+
109
+ if (!browser)
110
+ return
111
+ if (root.hasAttribute('data-hai-browser-update'))
112
+ showUpdateInstructions()
113
+ else
114
+ openUpdatePage()
115
+ })()
@@ -0,0 +1,37 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 12vh 20px 32px;
4
+ background: #f4f6fa;
5
+ color: #1b263b;
6
+ font-family: system-ui, sans-serif;
7
+ font-size: 1rem;
8
+ line-height: 1.6;
9
+ }
10
+
11
+ main {
12
+ max-width: 33rem;
13
+ margin: 0 auto;
14
+ padding: 1.5rem;
15
+ border: 1px solid #dce2ec;
16
+ border-radius: 0.75rem;
17
+ background: #fff;
18
+ }
19
+
20
+ h1 {
21
+ margin: 0 0 1rem;
22
+ font-size: 1.5rem;
23
+ line-height: 1.25;
24
+ }
25
+
26
+ p {
27
+ margin: 0 0 0.75rem;
28
+ }
29
+
30
+ a {
31
+ color: #1642a3;
32
+ }
33
+
34
+ a:focus {
35
+ outline: 2px solid currentColor;
36
+ outline-offset: 4px;
37
+ }
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html lang="en" data-hai-browser-update>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="robots" content="noindex" />
7
+ <title>Browser requirements</title>
8
+ <link rel="stylesheet" href="./update.css" />
9
+ </head>
10
+ <body>
11
+ <main>
12
+ <h1 id="browser-update-title">Browser requirements</h1>
13
+ <p id="browser-update-version">This site requires a current browser.</p>
14
+ <p id="browser-update-instruction">Install the latest update, then reopen this site.</p>
15
+ <a id="browser-update-help" rel="noreferrer" hidden></a>
16
+ </main>
17
+ <script src="./startup.js"></script>
18
+ </body>
19
+ </html>
@@ -0,0 +1,23 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ test('old browser sees upgrade instructions without the application bundles', async ({ page }) => {
4
+ // 只改变浏览器脚本看到的版本,确保检测在客户端执行。
5
+ await page.addInitScript(() => {
6
+ Object.defineProperty(navigator, 'userAgent', {
7
+ get: () => 'Mozilla/5.0 Gecko/20100101 Firefox/113.0',
8
+ })
9
+ })
10
+ await page.route('**/*', async (route) => {
11
+ const { pathname } = new URL(route.request().url())
12
+ if (/\.(?:js|css)$/.test(pathname) && !pathname.includes('/_hai/browser-check/'))
13
+ await route.abort()
14
+ else
15
+ await route.continue()
16
+ })
17
+ await page.goto('./', { waitUntil: 'commit' })
18
+ await expect(page).toHaveURL(/\/_hai\/browser-check\/update\.html#(?:zh|en|ja)$/)
19
+ await expect(page.getByRole('heading', { level: 1 })).toBeVisible()
20
+ await expect(page.getByRole('main')).toContainText('Firefox 113.0')
21
+ await expect(page.getByRole('main')).toContainText('Firefox 114')
22
+ await expect(page.getByRole('link')).toHaveAttribute('href', 'https://support.mozilla.org/kb/update-firefox-latest-release')
23
+ })
@@ -7,7 +7,9 @@ import process from 'node:process'
7
7
  /** 当前编译目标的私有产物身份;同一 SSR 构建重入复用身份,各端版本来自自己的 manifest。 */
8
8
  export function resolveBuildMetadata(directory: string): { appVersion: string, buildId: string, commit: string } {
9
9
  const manifest: { version?: unknown } = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'))
10
- const appVersion = typeof manifest.version === 'string' && manifest.version.trim() ? manifest.version.trim() : 'unknown'
10
+ const appVersion = typeof manifest.version === 'string' && manifest.version.trim()
11
+ ? manifest.version.trim()
12
+ : 'unknown'
11
13
  if (appVersion.length > 128)
12
14
  throw new Error('Invalid package.json version: maximum length is 128')
13
15
  const suppliedId = process.env.HAI_BUILD_ID?.trim()
@@ -27,14 +29,26 @@ export function resolveBuildMetadata(directory: string): { appVersion: string, b
27
29
  /** 未跟踪的生成工程不能把临时目录的父框架提交冒充自身源码。 */
28
30
  function sourceCommit(directory: string): string {
29
31
  try {
30
- const options = { cwd: directory, encoding: 'utf8' as const, stdio: 'pipe' as const, windowsHide: true }
32
+ const options = {
33
+ cwd: directory,
34
+ encoding: 'utf8' as const,
35
+ stdio: 'pipe' as const,
36
+ windowsHide: true,
37
+ }
31
38
  const root = execFileSync('git', ['rev-parse', '--show-toplevel'], options).trim()
32
39
  const manifest = relative(root, join(directory, 'package.json')).replaceAll('\\', '/')
33
40
  if (manifest.startsWith('../'))
34
41
  return 'unknown'
35
- execFileSync('git', ['ls-files', '--error-unmatch', '--', manifest], { ...options, cwd: root })
42
+ execFileSync('git', [
43
+ 'ls-files',
44
+ '--error-unmatch',
45
+ '--',
46
+ manifest,
47
+ ], { ...options, cwd: root })
36
48
  const commit = execFileSync('git', ['rev-parse', 'HEAD'], options).trim()
37
- return /^(?:[a-f\d]{40}|[a-f\d]{64})$/i.test(commit) ? commit : 'unknown'
49
+ return /^(?:[a-f\d]{40}|[a-f\d]{64})$/i.test(commit)
50
+ ? commit
51
+ : 'unknown'
38
52
  }
39
53
  catch {
40
54
  return 'unknown'
@@ -29,7 +29,12 @@ export function createE2eServerEnv(profile: Record<string, string>): Record<stri
29
29
  const environment: Record<string, string | undefined> = Object.fromEntries(
30
30
  Object.keys(process.env).filter(key => /^(?:HAI_|PUBLIC_|VITE_)/i.test(key)).map(key => [key, undefined]),
31
31
  )
32
- for (const name of ['HAI_E2E_DATA_ROOT', 'HAI_AUTH_EVIDENCE_DIR', 'HAI_CORE_NAME', 'HAI_CORE_VERSION']) {
32
+ for (const name of [
33
+ 'HAI_E2E_DATA_ROOT',
34
+ 'HAI_AUTH_EVIDENCE_DIR',
35
+ 'HAI_CORE_NAME',
36
+ 'HAI_CORE_VERSION',
37
+ ]) {
33
38
  if (process.env[name] !== undefined)
34
39
  environment[name] = process.env[name]
35
40
  }
@@ -56,10 +61,15 @@ export function createE2eServerEnv(profile: Record<string, string>): Record<stri
56
61
  export function createE2eTransportRing(): string {
57
62
  const key = createECDH('SM2')
58
63
  key.generateKeys()
59
- return JSON.stringify({ activeKeyId: 'e2e-v1', keys: { 'e2e-v1': {
60
- publicKey: key.getPublicKey('hex'),
61
- privateKey: key.getPrivateKey('hex').padStart(64, '0'),
62
- } } })
64
+ return JSON.stringify({
65
+ activeKeyId: 'e2e-v1',
66
+ keys: {
67
+ 'e2e-v1': {
68
+ publicKey: key.getPublicKey('hex'),
69
+ privateKey: key.getPrivateKey('hex').padStart(64, '0'),
70
+ },
71
+ },
72
+ })
63
73
  }
64
74
 
65
75
  /**
@@ -93,6 +103,12 @@ export function createE2eNotificationEnv(): Record<string, string> {
93
103
  { name: 'password_reset', provider: 'email', body: 'Reset: {token}' },
94
104
  { name: 'password_reset_sms', provider: 'sms', body: 'Reset: {token}' },
95
105
  ]),
96
- HAI_IAM_AGREEMENTS: JSON.stringify({ showOnRegister: true, showOnLogin: true, userAgreementMarkdown: '# Configured registration agreement\n\n**Registration terms**\n\n- Keep your account credentials secure.', userAgreementUrl: new URL('/terms', url).href, privacyPolicyUrl: new URL('/privacy', url).href }),
106
+ HAI_IAM_AGREEMENTS: JSON.stringify({
107
+ showOnRegister: true,
108
+ showOnLogin: true,
109
+ userAgreementMarkdown: '# Configured registration agreement\n\n**Registration terms**\n\n- Keep your account credentials secure.',
110
+ userAgreementUrl: new URL('/terms', url).href,
111
+ privacyPolicyUrl: new URL('/privacy', url).href,
112
+ }),
97
113
  }
98
114
  }
@@ -0,0 +1,5 @@
1
+ /** 原生构建只透传显式的 Core 名称与环境模式。 */
2
+ export declare function nativeNameEnvironment(envDir?: string): Record<string, string>
3
+
4
+ /** 从 Core YAML 与分层环境配置解析原生应用名称。 */
5
+ export declare function resolveNativeName(envDir: string, configFile: string, mode?: string): string
@@ -0,0 +1,94 @@
1
+ /** 原生工具在 Node 边界解析 Core 名称,不初始化服务或导出完整配置。 */
2
+ import process from 'node:process'
3
+ import { core, CoreConfigSchema } from '@h-ai/core'
4
+
5
+ /**
6
+ * 读取原生构建所需的 Core 环境覆盖,父级必须是对象。
7
+ * @param {NodeJS.ProcessEnv} [environment] 原生构建的已合并环境。
8
+ * @returns {Record<string, unknown>} Core 环境覆盖值。
9
+ */
10
+ function resolveCoreInputs(environment) {
11
+ const resolved = core.config.resolve({
12
+ name: 'core',
13
+ value: {},
14
+ schema: CoreConfigSchema,
15
+ environment,
16
+ })
17
+ if (!resolved.success)
18
+ throw new Error(resolved.error.message)
19
+ const { data } = resolved
20
+ if (data === null || typeof data !== 'object' || Array.isArray(data))
21
+ throw new Error('Invalid HAI_CORE: expected a JSON object')
22
+ return /** @type {Record<string, unknown>} */ (data)
23
+ }
24
+
25
+ /**
26
+ * 只将显式名称和环境模式传给原生构建子进程,父 JSON 中的其他字段不透传。
27
+ * @param {string} [envDir] 指定时先读取该目录的环境文件,供隔离容器的名称输入使用。
28
+ * @returns {Record<string, string>} 原生名称的公开构建输入。
29
+ */
30
+ export function nativeNameEnvironment(envDir) {
31
+ let inputs = process.env
32
+ /** @type {string | undefined} */
33
+ let selectedMode
34
+ if (envDir) {
35
+ selectedMode = /** @type {string | undefined} */ (resolveCoreInputs().env) ?? 'production'
36
+ const loaded = core.config.loadEnvFiles({ envDir, mode: selectedMode })
37
+ if (!loaded.success)
38
+ throw new Error(loaded.error.message)
39
+ inputs = loaded.data
40
+ }
41
+ const value = resolveCoreInputs(inputs)
42
+ // 父 JSON 替换 YAML。只转发细项时也要保留 name 缺省后的 Schema 默认值。
43
+ if (inputs.HAI_CORE !== undefined && value.name === undefined)
44
+ value.name = CoreConfigSchema.shape.name.parse(undefined)
45
+ /** @type {Record<string, string>} */
46
+ const environment = {}
47
+ for (const key of ['name', 'env']) {
48
+ if (value[key] === undefined)
49
+ continue
50
+ const schema = key === 'name'
51
+ ? CoreConfigSchema.shape.name
52
+ : CoreConfigSchema.shape.env
53
+ const parsed = schema.safeParse(value[key])
54
+ if (!parsed.success)
55
+ throw new Error(`Invalid HAI_CORE_${key.toUpperCase()}`)
56
+ environment[`HAI_CORE_${key.toUpperCase()}`] = parsed.data
57
+ }
58
+ // 保留本次入口选择的模式,避免 dotenv 的运行环境字段改变子进程二次加载的文件集合。
59
+ if (selectedMode)
60
+ environment.HAI_CORE_ENV = selectedMode
61
+ return environment
62
+ }
63
+
64
+ /**
65
+ * 按 Core 统一的 dotenv 优先级加载名称;环境只在同步解析期间注入并在结束后还原。
66
+ * @param {string} envDir 环境文件目录。
67
+ * @param {string} configFile Core YAML 的绝对路径。
68
+ * @param {string} [mode] 未指定时使用显式 Core 模式、NODE_ENV,最后默认为 prod。
69
+ * @returns {string} 应用显示名称。
70
+ */
71
+ export function resolveNativeName(envDir, configFile, mode) {
72
+ const overrides = nativeNameEnvironment()
73
+ const loaded = core.config.loadEnvFiles({ mode: overrides.HAI_CORE_ENV ?? mode ?? process.env.NODE_ENV ?? 'prod', envDir })
74
+ if (!loaded.success)
75
+ throw new Error(loaded.error.message)
76
+ const previous = new Map(Object.keys(loaded.data).map(key => [key, process.env[key]]))
77
+ try {
78
+ Object.assign(process.env, loaded.data)
79
+ const result = core.config.load('core', configFile, CoreConfigSchema)
80
+ if (!result.success)
81
+ throw new Error(result.error.message)
82
+ if (!result.data.name.trim())
83
+ throw new Error('Invalid HAI_CORE_NAME: expected a nonempty application name')
84
+ return result.data.name
85
+ }
86
+ finally {
87
+ for (const [key, value] of previous) {
88
+ if (value === undefined)
89
+ delete process.env[key]
90
+ else
91
+ process.env[key] = value
92
+ }
93
+ }
94
+ }