@h-ai/cli 1.0.0-beta.18 → 1.0.0-beta.19

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 (194) hide show
  1. package/README.en.md +4 -0
  2. package/README.ja.md +4 -0
  3. package/README.md +4 -0
  4. package/dist/{chunk-6YZBC5MO.js → chunk-2ZL5ORQ2.js} +85 -25
  5. package/dist/chunk-2ZL5ORQ2.js.map +1 -0
  6. package/dist/{chunk-7NVRYYHN.js → chunk-L53P43B2.js} +129 -31
  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-QG5HOYR5.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
  10. package/dist/{cli-skill-templates-W4I6ZKYW.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
  11. package/dist/{cli-skill-templates-W4I6ZKYW.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +2747 -671
  14. package/dist/index.js.map +1 -1
  15. package/dist/metafile-esm.json +1 -1
  16. package/package.json +22 -22
  17. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
  18. package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
  19. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
  20. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
  21. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
  22. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
  23. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
  24. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
  25. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  26. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +71 -32
  27. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  28. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +131 -54
  29. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
  30. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +78 -31
  31. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
  32. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  33. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
  34. package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
  35. package/templates/apps/api/eslint.config.js.hbs +3 -1
  36. package/templates/apps/api/images/serv.Dockerfile.hbs +3 -4
  37. package/templates/apps/api/playwright.config.ts.hbs +23 -19
  38. package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
  39. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -3
  40. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
  41. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
  42. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
  43. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
  46. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
  48. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
  49. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
  50. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
  51. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
  52. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
  53. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
  55. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +111 -26
  56. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
  57. package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
  58. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +3 -4
  59. package/templates/apps/fullstack/images/web.Dockerfile.hbs +0 -2
  60. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
  61. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
  62. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  63. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
  64. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  65. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
  66. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
  67. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
  68. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
  69. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
  70. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +139 -49
  71. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
  72. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +139 -28
  73. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
  74. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +141 -25
  75. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
  76. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  77. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
  78. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
  79. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
  80. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
  81. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
  82. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
  83. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
  84. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
  85. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
  86. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
  87. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
  88. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
  89. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
  90. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
  91. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
  92. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
  93. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
  94. package/templates/apps/fullstack/playwright.config.ts.hbs +9 -3
  95. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -15
  96. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -21
  97. package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
  98. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
  99. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
  100. package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
  101. package/templates/apps/mobile-app/src/lib/api.ts.hbs +14 -3
  102. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
  103. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
  104. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
  105. package/templates/apps/website/scripts/static-website.mjs.hbs +16 -3
  106. package/templates/apps/website/src/routes/+layout.svelte.hbs +44 -23
  107. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
  108. package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
  109. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
  110. package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
  111. package/templates/base/e2e/app.spec.ts.hbs +49 -16
  112. package/templates/base/eslint-rules.js +44 -25
  113. package/templates/base/eslint.config.js.hbs +3 -1
  114. package/templates/base/images/web.Dockerfile.hbs +3 -4
  115. package/templates/base/package.json.hbs +5 -4
  116. package/templates/base/patches/daisyui-5.7.50.patch +8 -0
  117. package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
  118. package/templates/base/playwright.config.ts +3 -1
  119. package/templates/base/pnpm-workspace.yaml.hbs +33 -16
  120. package/templates/base/src/app.d.ts.hbs +1 -1
  121. package/templates/base/src/hooks.server.ts.hbs +36 -27
  122. package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
  123. package/templates/base/src/lib/server/init.ts.hbs +124 -51
  124. package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
  125. package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
  126. package/templates/base/svelte.config.js.hbs +10 -100
  127. package/templates/base/tests/scaffold.test.ts.hbs +12 -3
  128. package/templates/base/tsconfig.json.hbs +7 -4
  129. package/templates/base/vite.config.ts.hbs +108 -14
  130. package/templates/base/vitest.config.ts.hbs +6 -2
  131. package/templates/features/ai/routes/api/chat/+server.ts +3 -1
  132. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
  133. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
  134. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
  135. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
  136. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
  137. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
  138. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
  139. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
  140. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
  141. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
  142. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
  143. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
  144. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +11 -2
  145. package/templates/shared/browser-check/startup.js +32 -6
  146. package/templates/shared/browser-startup.spec.ts +3 -1
  147. package/templates/shared/build-metadata.ts +18 -4
  148. package/templates/shared/e2e-env.ts +22 -6
  149. package/templates/shared/native-name.mjs +9 -2
  150. package/templates/shared/public-base-path.mjs +17 -4
  151. package/templates/shared/run-e2e.mjs +40 -9
  152. package/templates/shared/run-node.mjs +15 -4
  153. package/templates/shared/run-tauri.mjs +37 -8
  154. package/templates/shared/vite-runtime-config.ts +9 -3
  155. package/templates/skills/AGENTS.md +10 -73
  156. package/templates/skills/bridges/admin/AGENTS.md +9 -65
  157. package/templates/skills/bridges/api/AGENTS.md +8 -59
  158. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
  159. package/templates/skills/bridges/h5/AGENTS.md +8 -63
  160. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
  161. package/templates/skills/bridges/website/AGENTS.md +8 -63
  162. package/templates/skills/hai-ai/acp.md +2 -0
  163. package/templates/skills/hai-ai/guide.md +17 -6
  164. package/templates/skills/hai-ai/reference.md +21 -12
  165. package/templates/skills/hai-api-client/reference.md +1 -1
  166. package/templates/skills/hai-app-create/development.md +3 -1
  167. package/templates/skills/hai-app-create/project.md +33 -0
  168. package/templates/skills/hai-app-create/reference.md +8 -8
  169. package/templates/skills/hai-app-tests/reference.md +1 -1
  170. package/templates/skills/hai-build/SKILL.md +2 -2
  171. package/templates/skills/hai-build/paths.md +1 -1
  172. package/templates/skills/hai-build/reference.md +11 -7
  173. package/templates/skills/hai-capacitor/reference.md +2 -0
  174. package/templates/skills/hai-ci/SKILL.md +1 -0
  175. package/templates/skills/hai-core/reference.md +3 -0
  176. package/templates/skills/hai-crypto/reference.md +2 -0
  177. package/templates/skills/hai-deploy/SKILL.md +7 -1
  178. package/templates/skills/hai-deploy/middleware.md +3 -1
  179. package/templates/skills/hai-framework-sync/sync.md +2 -0
  180. package/templates/skills/hai-kit/reference.md +16 -4
  181. package/templates/skills/hai-payment/reference.md +2 -0
  182. package/templates/skills/hai-reach/SKILL.md +2 -1
  183. package/templates/skills/hai-reach/reference.md +1 -1
  184. package/templates/skills/hai-reldb/SKILL.md +1 -1
  185. package/templates/skills/hai-reldb/reference.md +4 -0
  186. package/templates/skills/hai-serv/reference.md +12 -2
  187. package/templates/skills/hai-storage/reference.md +12 -3
  188. package/templates/skills/hai-ui/SKILL.md +3 -2
  189. package/templates/skills/hai-ui/guide.md +1 -1
  190. package/templates/skills/hai-ui/reference.md +23 -35
  191. package/templates/skills/hai-vecdb/reference.md +1 -1
  192. package/dist/chunk-6YZBC5MO.js.map +0 -1
  193. package/dist/chunk-7NVRYYHN.js.map +0 -1
  194. package/dist/cli-config-templates-QG5HOYR5.js +0 -5
@@ -53,7 +53,10 @@ function startFeedback(): Promise<void> {
53
53
  feedbackClosing = false
54
54
  feedbackStarting = (async () => {
55
55
  try {
56
- const result = await feedback.init({ ...core.config.getOrThrow<import('@h-ai/feedback').FeedbackConfigInput>('feedback'), ...buildMetadata })
56
+ const result = await feedback.init({
57
+ ...core.config.getOrThrow<import('@h-ai/feedback').FeedbackConfigInput>('feedback'),
58
+ ...buildMetadata,
59
+ })
57
60
  if (!result.success) {
58
61
  if (feedbackFailure !== result.error.code)
59
62
  core.logger.warn('Optional feedback unavailable; business runtime remains ready', { module: 'feedback', code: result.error.code })
@@ -87,106 +90,180 @@ async function closeFeedback() {
87
90
  await feedbackStarting
88
91
  return feedback.close()
89
92
  }
90
-
91
93
  {{/if}}
92
94
 
93
95
  const runtime = core.module.createRuntime([
94
- { name: 'configuration', start: () => {
95
- core.init({
96
- configDir: './config',
97
- configSchemas: {
98
- serv: ServConfigSchema,
96
+ {
97
+ name: 'configuration',
98
+ start: () => {
99
+ core.init({
100
+ configDir: './config',
101
+ configSchemas: {
102
+ serv: ServConfigSchema,
99
103
  {{#if features.ai}}
100
- ai: AIConfigSchema,
104
+ ai: AIConfigSchema,
101
105
  {{/if}}
102
106
  {{#if features.audit}}
103
- audit: AuditInitConfigSchema,
107
+ audit: AuditInitConfigSchema,
104
108
  {{/if}}
105
109
  {{#if features.cache}}
106
- cache: CacheConfigSchema,
110
+ cache: CacheConfigSchema,
107
111
  {{/if}}
108
112
  {{#if features.feedback}}
109
- feedback: FeedbackConfigSchema,
113
+ feedback: FeedbackConfigSchema,
110
114
  {{/if}}
111
115
  {{#if features.iam}}
112
- iam: IamConfigSchema,
116
+ iam: IamConfigSchema,
113
117
  {{/if}}
114
118
  {{#if features.reach}}
115
- reach: ReachConfigSchema,
119
+ reach: ReachConfigSchema,
116
120
  {{/if}}
117
121
  {{#if features.db}}
118
- db: ReldbConfigSchema,
122
+ db: ReldbConfigSchema,
119
123
  {{/if}}
120
124
  {{#if features.audit}}
121
125
  {{#if features.scheduler}}
122
- scheduler: SchedulerConfigSchema,
126
+ scheduler: SchedulerConfigSchema,
123
127
  {{/if}}
124
128
  {{/if}}
125
129
  {{#if features.storage}}
126
- storage: StorageConfigSchema,
130
+ storage: StorageConfigSchema,
127
131
  {{/if}}
128
132
  {{#if features.vecdb}}
129
- vecdb: VecdbConfigSchema,
130
- {{/if}}
131
- },
132
- })
133
- }, close: () => {} },
134
- { name: 'serv-config', start: () => core.config.validate('serv', ServConfigSchema), close: () => {} },
133
+ vecdb: VecdbConfigSchema,
134
+ {{/if}}
135
+ },
136
+ })
137
+ },
138
+ close: () => { },
139
+ },
140
+ {
141
+ name: 'serv-config',
142
+ start: () => core.config.validate('serv', ServConfigSchema),
143
+ close: () => { },
144
+ },
135
145
  {{#if features.db}}
136
- { name: 'reldb', start: async () => {
137
- const config = ReldbConfigSchema.parse(core.config.getOrThrow('db'))
138
- if (config.type === 'sqlite' && config.database !== ':memory:') {
139
- const { mkdir } = await import('node:fs/promises')
140
- const { dirname } = await import('node:path')
141
- await mkdir(dirname(config.database), { recursive: true })
142
- }
143
- return reldb.init(config)
144
- }, close: () => reldb.close() },
146
+ {
147
+ name: 'reldb',
148
+ start: async () => {
149
+ const config = ReldbConfigSchema.parse(core.config.getOrThrow('db'))
150
+ if (config.type === 'sqlite' && config.database !== ':memory:') {
151
+ const { mkdir } = await import('node:fs/promises')
152
+ const { dirname } = await import('node:path')
153
+ await mkdir(dirname(config.database), { recursive: true })
154
+ }
155
+ return reldb.init(config)
156
+ },
157
+ close: () => reldb.close(),
158
+ },
145
159
  {{/if}}
146
160
  {{#if features.cache}}
147
- { name: 'cache', start: () => cache.init(CacheConfigSchema.parse(core.config.getOrThrow('cache'))), close: () => cache.close() },
161
+ {
162
+ name: 'cache',
163
+ start: () => cache.init(CacheConfigSchema.parse(core.config.getOrThrow('cache'))),
164
+ close: () => cache.close(),
165
+ },
148
166
  {{/if}}
149
167
  {{#if features.vecdb}}
150
- { name: 'vecdb', start: () => vecdb.init(VecdbConfigSchema.parse(core.config.getOrThrow('vecdb'))), close: () => vecdb.close() },
168
+ {
169
+ name: 'vecdb',
170
+ start: () => vecdb.init(VecdbConfigSchema.parse(core.config.getOrThrow('vecdb'))),
171
+ close: () => vecdb.close(),
172
+ },
151
173
  {{/if}}
152
174
  {{#if features.crypto}}
153
- { name: 'crypto', start: () => crypto.init(), close: () => crypto.close() },
175
+ {
176
+ name: 'crypto',
177
+ start: () => crypto.init(),
178
+ close: () => crypto.close(),
179
+ },
154
180
  {{/if}}
155
181
  {{#if features.storage}}
156
- { name: 'storage', start: () => storage.init(StorageConfigSchema.parse(core.config.getOrThrow('storage'))), close: () => storage.close() },
182
+ {
183
+ name: 'storage',
184
+ start: () => storage.init(StorageConfigSchema.parse(core.config.getOrThrow('storage'))),
185
+ close: () => storage.close(),
186
+ },
157
187
  {{/if}}
158
188
  {{#if features.reach}}
159
- { name: 'reach', enabled: () => Boolean(core.config.get('reach')), start: () => reach.init(ReachConfigSchema.parse(core.config.getOrThrow('reach'))), close: () => reach.close() },
189
+ {
190
+ name: 'reach',
191
+ enabled: () => Boolean(core.config.get('reach')),
192
+ start: () => reach.init(ReachConfigSchema.parse(core.config.getOrThrow('reach'))),
193
+ close: () => reach.close(),
194
+ },
160
195
  {{/if}}
161
196
  {{#if features.audit}}
162
- { name: 'audit', start: () => audit.init({ ...AuditInitConfigSchema.parse(core.config.get('audit') ?? {}){{#if features.storage}}, storage{{/if}}{{#if features.reach}}, reach{{/if}} }), close: () => audit.close() },
197
+ {
198
+ name: 'audit',
199
+ start: () => audit.init({
200
+ ...AuditInitConfigSchema.parse(core.config.get('audit') ?? {}),
201
+ {{#if features.storage}}
202
+ storage,
203
+ {{/if}}
204
+ {{#if features.reach}}
205
+ reach,
206
+ {{/if}}
207
+ }),
208
+ close: () => audit.close(),
209
+ },
163
210
  {{/if}}
164
211
  {{#if features.iam}}
165
- { name: 'iam', start: () => iam.init({ ...IamConfigSchema.parse(core.config.getOrThrow('iam')){{#if features.reach}}, reach{{/if}} }), close: () => iam.close() },
212
+ {
213
+ name: 'iam',
214
+ start: () => iam.init({
215
+ ...IamConfigSchema.parse(core.config.getOrThrow('iam')),
216
+ {{#if features.reach}}
217
+ reach,
218
+ {{/if}}
219
+ }),
220
+ close: () => iam.close(),
221
+ },
166
222
  {{/if}}
167
223
  {{#if features.audit}}
168
224
  {{#if features.scheduler}}
169
- { name: 'audit-jobs', enabled: () => Boolean(audit.schedulerPlan), start: async () => {
170
- const schedule = audit.schedulerPlan
171
- if (!schedule)
172
- return
173
- const initialized = await scheduler.init({ ...SchedulerConfigSchema.parse(core.config.get('scheduler') ?? {}), ...schedule })
174
- if (!initialized.success)
175
- return initialized
176
- const started = scheduler.start()
177
- if (!started.success)
178
- await scheduler.close()
179
- return started
180
- }, close: () => scheduler.close() },
225
+ {
226
+ name: 'audit-jobs',
227
+ enabled: () => Boolean(audit.schedulerPlan),
228
+ start: async () => {
229
+ const schedule = audit.schedulerPlan
230
+ if (!schedule)
231
+ return
232
+ const initialized = await scheduler.init({
233
+ ...SchedulerConfigSchema.parse(core.config.get('scheduler') ?? {}),
234
+ ...schedule,
235
+ })
236
+ if (!initialized.success)
237
+ return initialized
238
+ const started = scheduler.start()
239
+ if (!started.success)
240
+ await scheduler.close()
241
+ return started
242
+ },
243
+ close: () => scheduler.close(),
244
+ },
181
245
  {{/if}}
182
246
  {{/if}}
183
247
  {{#if features.feedback}}
184
- { name: 'feedback', enabled: () => Boolean(core.config.get('feedback')), start: startFeedback, close: closeFeedback },
248
+ {
249
+ name: 'feedback',
250
+ enabled: () => Boolean(core.config.get('feedback')),
251
+ start: startFeedback,
252
+ close: closeFeedback,
253
+ },
185
254
  {{/if}}
186
255
  {{#if features.ai}}
187
- { name: 'ai', start: () => ai.init(AIConfigSchema.parse(core.config.getOrThrow('ai'))), close: () => ai.close() },
188
- {{/if}}
189
- { name: 'bootstrap', start: () => { core.logger.info('API service initialized') }, close: () => {} },
256
+ {
257
+ name: 'ai',
258
+ start: () => ai.init(AIConfigSchema.parse(core.config.getOrThrow('ai'))),
259
+ close: () => ai.close(),
260
+ },
261
+ {{/if}}
262
+ {
263
+ name: 'bootstrap',
264
+ start: () => { core.logger.info('API service initialized') },
265
+ close: () => { },
266
+ },
190
267
  ])
191
268
 
192
269
  /** 启动失败只报告模块、错误码和配置路径,私密值不进入错误上下文。 */
@@ -1,13 +1,27 @@
1
1
  import type { AppProcedureDeps } from './app-procedures.js'
2
- {{#if features.ai}}import { ai } from '@h-ai/ai'
3
- {{/if}}{{#if features.feedback}}import { feedback } from '@h-ai/feedback'
4
- {{/if}}{{#if features.iam}}import { iam } from '@h-ai/iam'
5
- {{/if}}{{#if features.ai}}import { createAiProcedures } from '@h-ai/serv/features/ai'
6
- {{/if}}{{#if features.feedback}}import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
7
- {{/if}}{{#if features.iam}}import { createIamProcedures } from '@h-ai/serv/features/iam'
8
- {{/if}}{{#if features.storage}}import { createStorageProcedures } from '@h-ai/serv/features/storage'
2
+ {{#if features.ai}}
3
+ import { ai } from '@h-ai/ai'
4
+ {{/if}}
5
+ {{#if features.feedback}}
6
+ import { feedback } from '@h-ai/feedback'
7
+ {{/if}}
8
+ {{#if features.iam}}
9
+ import { iam } from '@h-ai/iam'
10
+ {{/if}}
11
+ {{#if features.ai}}
12
+ import { createAiProcedures } from '@h-ai/serv/features/ai'
13
+ {{/if}}
14
+ {{#if features.feedback}}
15
+ import { createFeedbackProcedures } from '@h-ai/serv/features/feedback'
16
+ {{/if}}
17
+ {{#if features.iam}}
18
+ import { createIamProcedures } from '@h-ai/serv/features/iam'
19
+ {{/if}}
20
+ {{#if features.storage}}
21
+ import { createStorageProcedures } from '@h-ai/serv/features/storage'
9
22
  import { storage } from '@h-ai/storage'
10
- {{/if}}import { createAppProcedures } from './app-procedures.js'
23
+ {{/if}}
24
+ import { createAppProcedures } from './app-procedures.js'
11
25
 
12
26
  // @hai-generator:procedures:imports
13
27
 
@@ -15,15 +29,21 @@ import { storage } from '@h-ai/storage'
15
29
  export function {{api.serviceProceduresFactoryName}}(deps: { app: AppProcedureDeps }) {
16
30
  return {
17
31
  // @hai-generator:procedures:entries
18
- {{#if features.iam}} iam: createIamProcedures({ iam }),
19
- {{/if}}{{#if features.storage}} storage: createStorageProcedures({
32
+ {{#if features.iam}}
33
+ iam: createIamProcedures({ iam }),
34
+ {{/if}}
35
+ {{#if features.storage}}
36
+ storage: createStorageProcedures({
20
37
  storage,
21
38
  authorize: ({ context, keys }) => keys.every(key =>
22
39
  key.startsWith(`users/${encodeURIComponent(context.session.userId)}/`)),
23
40
  }),
24
- {{/if}}{{#if features.ai}} // AI 默认绑定可信 session.userId,跨主体访问需另行提供 authorizeObject 策略。
41
+ {{/if}}
42
+ {{#if features.ai}}
43
+ // AI 默认绑定可信 session.userId,跨主体访问需另行提供 authorizeObject 策略。
25
44
  ai: createAiProcedures({ ai }),
26
- {{/if}}{{#if features.feedback}}
45
+ {{/if}}
46
+ {{#if features.feedback}}
27
47
  feedback: createFeedbackProcedures({
28
48
  feedback,
29
49
  resolveContext: ({ session }) => ({
@@ -31,7 +51,13 @@ export function {{api.serviceProceduresFactoryName}}(deps: { app: AppProcedureDe
31
51
  userId: session.userId,
32
52
  // 示例角色映射;实际产品应从可信租户与资源权限确定范围。
33
53
  permissions: session.roles.includes('admin')
34
- ? ['feedback:create', 'feedback:read', 'feedback:process', 'feedback:reopen', 'feedback:export']
54
+ ? [
55
+ 'feedback:create',
56
+ 'feedback:read',
57
+ 'feedback:process',
58
+ 'feedback:reopen',
59
+ 'feedback:export',
60
+ ]
35
61
  : ['feedback:create'],
36
62
  }),
37
63
  }),
@@ -4,16 +4,28 @@ import type { AddressInfo } from 'node:net'
4
4
  import { mkdtempSync, rmSync } from 'node:fs'
5
5
  import { tmpdir } from 'node:os'
6
6
  import { join } from 'node:path'
7
- {{#if features.ai}}import { ai } from '@h-ai/ai'
8
- {{/if}}import { apiClient } from '@h-ai/api-client'
9
- {{#if features.cache}}import { cache } from '@h-ai/cache'
10
- {{/if}}import { core } from '@h-ai/core'
11
- {{#if features.iam}}import { iam } from '@h-ai/iam'
12
- {{/if}}{{#if features.db}}import { reldb } from '@h-ai/reldb'
13
- {{/if}}import { serv } from '@h-ai/serv'
14
- {{#if features.storage}}import { storage } from '@h-ai/storage'
15
- {{/if}}{{#if features.vecdb}}import { vecdb } from '@h-ai/vecdb'
16
- {{/if}}import { {{api.contractExportName}} } from '{{api.contractPackageName}}'
7
+ {{#if features.ai}}
8
+ import { ai } from '@h-ai/ai'
9
+ {{/if}}
10
+ import { apiClient } from '@h-ai/api-client'
11
+ {{#if features.cache}}
12
+ import { cache } from '@h-ai/cache'
13
+ {{/if}}
14
+ import { core } from '@h-ai/core'
15
+ {{#if features.iam}}
16
+ import { iam } from '@h-ai/iam'
17
+ {{/if}}
18
+ {{#if features.db}}
19
+ import { reldb } from '@h-ai/reldb'
20
+ {{/if}}
21
+ import { serv } from '@h-ai/serv'
22
+ {{#if features.storage}}
23
+ import { storage } from '@h-ai/storage'
24
+ {{/if}}
25
+ {{#if features.vecdb}}
26
+ import { vecdb } from '@h-ai/vecdb'
27
+ {{/if}}
28
+ import { {{api.contractExportName}} } from '{{api.contractPackageName}}'
17
29
  import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
18
30
 
19
31
  import { {{api.serviceAppFactoryName}} } from '../src/app.js'
@@ -23,10 +35,12 @@ let baseUrl = ''
23
35
  let client: ReturnType<typeof apiClient.create<typeof {{api.contractExportName}}>>
24
36
  let server: ReturnType<typeof serv.listen>
25
37
  let servConfig: ServConfig
26
- {{#if features.storage}}let storageRoot = ''
27
- {{/if}}{{#if features.vecdb}}let vecdbRoot = ''
38
+ {{#if features.storage}}
39
+ let storageRoot = ''
40
+ {{/if}}
41
+ {{#if features.vecdb}}
42
+ let vecdbRoot = ''
28
43
  {{/if}}
29
-
30
44
  {{#if features.iam}}
31
45
  function uniqueUser(prefix = 'api') {
32
46
  const suffix = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`.slice(-8)
@@ -40,8 +54,11 @@ function uniqueUser(prefix = 'api') {
40
54
 
41
55
  beforeAll(async () => {
42
56
  tempRoot = mkdtempSync(join(tmpdir(), '{{projectName}}-service-test-'))
43
- {{#if features.storage}} storageRoot = join(tempRoot, 'storage')
44
- {{/if}}{{#if features.vecdb}} vecdbRoot = join(tempRoot, 'vecdb')
57
+ {{#if features.storage}}
58
+ storageRoot = join(tempRoot, 'storage')
59
+ {{/if}}
60
+ {{#if features.vecdb}}
61
+ vecdbRoot = join(tempRoot, 'vecdb')
45
62
  {{/if}}
46
63
 
47
64
  vi.stubEnv('HAI_SERV_CORS_NATIVEORIGINS', undefined)
@@ -55,27 +72,32 @@ beforeAll(async () => {
55
72
  const dbResult = await reldb.init({ type: 'sqlite', database: ':memory:' })
56
73
  if (!dbResult.success)
57
74
  throw new Error(`reldb: ${dbResult.error.message}`)
58
- {{/if}}{{#if features.cache}}
75
+ {{/if}}
76
+ {{#if features.cache}}
59
77
 
60
78
  const cacheResult = await cache.init({ type: 'memory' })
61
79
  if (!cacheResult.success)
62
80
  throw new Error(`cache: ${cacheResult.error.message}`)
63
- {{/if}}{{#if features.vecdb}}
81
+ {{/if}}
82
+ {{#if features.vecdb}}
64
83
 
65
84
  const vecdbResult = await vecdb.init({ type: 'lancedb', path: vecdbRoot })
66
85
  if (!vecdbResult.success)
67
86
  throw new Error(`vecdb: ${vecdbResult.error.message}`)
68
- {{/if}}{{#if features.iam}}
87
+ {{/if}}
88
+ {{#if features.iam}}
69
89
 
70
90
  const iamResult = await iam.init({})
71
91
  if (!iamResult.success)
72
92
  throw new Error(`iam: ${iamResult.error.message}`)
73
- {{/if}}{{#if features.storage}}
93
+ {{/if}}
94
+ {{#if features.storage}}
74
95
 
75
96
  const storageResult = await storage.init({ type: 'local', root: storageRoot })
76
97
  if (!storageResult.success)
77
98
  throw new Error(`storage: ${storageResult.error.message}`)
78
- {{/if}}{{#if features.ai}}
99
+ {{/if}}
100
+ {{#if features.ai}}
79
101
 
80
102
  const aiResult = await ai.init({})
81
103
  if (!aiResult.success)
@@ -100,7 +122,9 @@ beforeAll(async () => {
100
122
  baseUrl: `${baseUrl}${servConfig.http.apiPrefix}`,
101
123
  clientName: 'hai-mobile-app',
102
124
  headers: { 'origin': 'capacitor://localhost', 'x-hai-token-transport': 'body' },
103
- auth: { storage: apiClient.tokenStorage.memory() },
125
+ auth: {
126
+ storage: apiClient.tokenStorage.memory(),
127
+ },
104
128
  })
105
129
  if (!initResult.success)
106
130
  throw new Error(`client.init: ${initResult.error.message}`)
@@ -109,13 +133,25 @@ beforeAll(async () => {
109
133
  afterAll(async () => {
110
134
  await client?.close()
111
135
  await server?.close()
112
- {{#if features.ai}} await ai.close()
113
- {{/if}}{{#if features.storage}} await storage.close()
114
- {{/if}}{{#if features.iam}} await iam.close()
115
- {{/if}}{{#if features.vecdb}} await vecdb.close()
116
- {{/if}}{{#if features.cache}} await cache.close()
117
- {{/if}}{{#if features.db}} await reldb.close()
118
- {{/if}} rmSync(tempRoot, { recursive: true, force: true })
136
+ {{#if features.ai}}
137
+ await ai.close()
138
+ {{/if}}
139
+ {{#if features.storage}}
140
+ await storage.close()
141
+ {{/if}}
142
+ {{#if features.iam}}
143
+ await iam.close()
144
+ {{/if}}
145
+ {{#if features.vecdb}}
146
+ await vecdb.close()
147
+ {{/if}}
148
+ {{#if features.cache}}
149
+ await cache.close()
150
+ {{/if}}
151
+ {{#if features.db}}
152
+ await reldb.close()
153
+ {{/if}}
154
+ rmSync(tempRoot, { recursive: true, force: true })
119
155
  vi.unstubAllEnvs()
120
156
  })
121
157
 
@@ -135,7 +171,12 @@ describe('{{api.servPackageName}}', () => {
135
171
  expect(result.success).toBe(true)
136
172
  if (result.success) {
137
173
  const config = core.config.getOrThrow<CoreConfig>('core')
138
- expect(result.data).toMatchObject({ name: config.name, version: config.version, env: config.env, defaultLocale: config.defaultLocale })
174
+ expect(result.data).toMatchObject({
175
+ name: config.name,
176
+ version: config.version,
177
+ env: config.env,
178
+ defaultLocale: config.defaultLocale,
179
+ })
139
180
  expect(typeof result.data.transportEnabled).toBe('boolean')
140
181
  }
141
182
  })
@@ -145,7 +186,6 @@ describe('{{api.servPackageName}}', () => {
145
186
 
146
187
  expect(result.success).toBe(false)
147
188
  })
148
-
149
189
  {{#if features.iam}}
150
190
  it.each([
151
191
  ['capacitor://localhost', 'hai-mobile-app'],
@@ -156,7 +196,14 @@ describe('{{api.servPackageName}}', () => {
156
196
  ])('refreshes an expired access token at the default native Origin %s', async (origin, clientName) => {
157
197
  const native = apiClient.create({{api.contractExportName}})
158
198
  try {
159
- expect((await native.init({ baseUrl: `${baseUrl}${servConfig.http.apiPrefix}`, clientName, headers: { 'origin': origin, 'x-hai-token-transport': 'body' }, auth: { storage: apiClient.tokenStorage.memory() } })).success).toBe(true)
199
+ expect((await native.init({
200
+ baseUrl: `${baseUrl}${servConfig.http.apiPrefix}`,
201
+ clientName,
202
+ headers: { 'origin': origin, 'x-hai-token-transport': 'body' },
203
+ auth: {
204
+ storage: apiClient.tokenStorage.memory(),
205
+ },
206
+ })).success).toBe(true)
160
207
  const registration = await apiClient.createAuthActions(native).register(uniqueUser('refresh'))
161
208
  expect(registration.success).toBe(true)
162
209
  if (!registration.success)
@@ -6,7 +6,9 @@ import { resolveBuildMetadata } from '../../scripts/build-metadata.js'
6
6
  const buildMetadata = resolveBuildMetadata(fileURLToPath(new URL('.', import.meta.url)))
7
7
 
8
8
  export default defineConfig({
9
- define: { __HAI_BUILD_METADATA__: JSON.stringify(buildMetadata) },
9
+ define: {
10
+ __HAI_BUILD_METADATA__: JSON.stringify(buildMetadata),
11
+ },
10
12
  async onSuccess() {
11
13
  await writeFile(new URL('./dist/build-metadata.json', import.meta.url), JSON.stringify(buildMetadata))
12
14
  },
@@ -3,7 +3,9 @@ import { defineConfig } from 'vitest/config'
3
3
  import { resolveBuildMetadata } from '../../scripts/build-metadata.js'
4
4
 
5
5
  export default defineConfig({
6
- define: { __HAI_BUILD_METADATA__: JSON.stringify(resolveBuildMetadata(fileURLToPath(new URL('.', import.meta.url)))) },
6
+ define: {
7
+ __HAI_BUILD_METADATA__: JSON.stringify(resolveBuildMetadata(fileURLToPath(new URL('.', import.meta.url)))),
8
+ },
7
9
  test: {
8
10
  globals: true,
9
11
  environment: 'node',
@@ -16,7 +16,9 @@ test.beforeAll(async () => {
16
16
  const result = await client.init({
17
17
  baseUrl: `${process.env.BASE_URL || 'http://127.0.0.1:3000'}${API_PREFIX}`,
18
18
  {{#if features.iam}}
19
- auth: { storage: apiClient.tokenStorage.memory() },
19
+ auth: {
20
+ storage: apiClient.tokenStorage.memory(),
21
+ },
20
22
  {{/if}}
21
23
  {{#if features.crypto}}
22
24
  transport: { crypto },
@@ -105,9 +107,24 @@ test('feedback intake is idempotent and creator can append evidence without admi
105
107
  expect(duplicate.success).toBe(true)
106
108
  if (duplicate.success)
107
109
  expect(duplicate.data.id).toBe(created.data.id)
108
- const evidence = await client.feedback.reports.appendEvidence({ id: created.data.id, evidence: [{ id: 'event-1', source: 'api-test', sequence: 1, kind: 'business', timestamp: Date.now(), data: { action: 'api-feedback' } }] })
110
+ const evidence = await client.feedback.reports.appendEvidence({
111
+ id: created.data.id,
112
+ evidence: [
113
+ {
114
+ id: 'event-1',
115
+ source: 'api-test',
116
+ sequence: 1,
117
+ kind: 'business',
118
+ timestamp: Date.now(),
119
+ data: { action: 'api-feedback' },
120
+ },
121
+ ],
122
+ })
109
123
  expect(evidence.success).toBe(true)
110
- expect((await client.feedback.reports.completeCollection({ id: created.data.id, missing: ['browser-not-applicable'] })).success).toBe(true)
124
+ expect((await client.feedback.reports.completeCollection({
125
+ id: created.data.id,
126
+ missing: ['browser-not-applicable'],
127
+ })).success).toBe(true)
111
128
  expect((await client.feedback.reports.list({})).success).toBe(false)
112
129
  })
113
130
  {{/if}}
@@ -15,7 +15,10 @@ export default async function captureEvidence() {
15
15
  expect(response?.status()).toBe(200)
16
16
  await expect(page.locator('body')).toContainText('ok')
17
17
  await mkdir(evidence, { recursive: true })
18
- await page.screenshot({ path: path.join(evidence, '{{projectName}}-health.png'), fullPage: true })
18
+ await page.screenshot({
19
+ path: path.join(evidence, '{{projectName}}-health.png'),
20
+ fullPage: true,
21
+ })
19
22
  }
20
23
  finally {
21
24
  await browser.close()
@@ -3,7 +3,9 @@ import { haiQualityConfig } from './eslint-rules.js'
3
3
 
4
4
  export default antfu({
5
5
  // unused-imports 使用 TS 规则,JS 同样使用 TS parser 以保持 scope 契约一致。
6
- typescript: { files: [GLOB_SRC] },
6
+ typescript: {
7
+ files: [GLOB_SRC],
8
+ },
7
9
  formatters: true,
8
10
  ignores: [
9
11
  '**/dist/**',
@@ -1,7 +1,5 @@
1
1
  ARG HAI_NODE_IMAGE=node:24.22.1-bookworm-slim
2
2
  FROM ${HAI_NODE_IMAGE} AS build
3
- ARG SQLITE_PREBUILD_MIRROR=
4
- ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
5
3
 
6
4
  ENV PNPM_HOME=/pnpm
7
5
  ENV PATH=$PNPM_HOME:$PATH
@@ -22,8 +20,9 @@ COPY . .
22
20
  ARG HAI_BUILD_ID=
23
21
  ARG HAI_BUILD_COMMIT=unknown
24
22
  RUN HAI_BUILD_ID="$HAI_BUILD_ID" HAI_BUILD_COMMIT="$HAI_BUILD_COMMIT" pnpm compile
25
- RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
26
- RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{api.servPackageName}} --prod /prod/app
23
+ # SQLite 13 的原生二进制随 npm 包交付,在构建时验证实际加载。
24
+ RUN if [ -d node_modules/better-sqlite3 ]; then node -e "const Database=require('better-sqlite3');const db=new Database(':memory:');db.prepare('SELECT 1').get();db.close()"; fi
25
+ RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm pm deploy --filter {{api.servPackageName}} --prod /prod/app
27
26
  RUN rm -f /prod/app/README* && find /prod/app/dist -type f \( -name '*.map' -o -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' \) -delete
28
27
 
29
28
  FROM ${HAI_NODE_IMAGE} AS runtime-base