@h-ai/cli 0.1.0-alpha.67 → 0.1.0-alpha.68

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 (164) hide show
  1. package/README.en.md +40 -4
  2. package/README.ja.md +40 -4
  3. package/README.md +40 -4
  4. package/dist/{src-2V6VP4I5.js → chunk-FI56GONY.js} +32 -3103
  5. package/dist/chunk-FI56GONY.js.map +1 -0
  6. package/dist/chunk-MBPB37FR.js +1348 -0
  7. package/dist/chunk-MBPB37FR.js.map +1 -0
  8. package/dist/{chunk-7DSURTJ4.js → chunk-ZFNBUF77.js} +327 -199
  9. package/dist/chunk-ZFNBUF77.js.map +1 -0
  10. package/dist/cli-config-templates-W2LLPMB6.js +5 -0
  11. package/dist/{cli-config-templates-6AHNUGBN.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
  12. package/dist/{cli-skill-templates-FC3QRKDO.js → cli-skill-templates-US5CCAJ4.js} +3 -3
  13. package/dist/{cli-skill-templates-FC3QRKDO.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
  14. package/dist/index.js +920 -336
  15. package/dist/index.js.map +1 -1
  16. package/dist/metafile-esm.json +1 -0
  17. package/dist/src-DSJJXM2F.js +3849 -0
  18. package/dist/src-DSJJXM2F.js.map +1 -0
  19. package/package.json +9 -8
  20. package/templates/apps/admin/messages/ja-JP.json +38 -0
  21. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
  22. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
  23. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
  24. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
  25. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +5 -1
  26. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
  27. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
  28. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
  29. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
  30. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
  31. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
  32. package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
  33. package/templates/apps/api/package.json.hbs +1 -1
  34. package/templates/apps/api/playwright.config.ts.hbs +15 -7
  35. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +1 -1
  36. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
  37. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
  38. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
  39. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +1 -1
  40. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
  41. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
  42. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
  43. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +1 -1
  44. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
  45. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
  46. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +19 -8
  47. package/templates/apps/fullstack/package.json.hbs +1 -1
  48. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
  49. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
  50. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
  51. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
  52. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
  53. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
  54. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
  55. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
  56. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
  57. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
  58. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
  59. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
  60. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
  61. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
  62. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
  63. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +9 -4
  64. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
  65. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
  66. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +5 -5
  67. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
  68. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +1 -1
  69. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
  70. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +11 -12
  71. package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
  72. package/templates/apps/h5/messages/ja-JP.json +40 -0
  73. package/templates/apps/h5/src/routes/+layout.svelte.hbs +14 -3
  74. package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
  75. package/templates/apps/mobile-app/src/App.svelte.hbs +13 -3
  76. package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
  77. package/templates/apps/website/messages/ja-JP.json +32 -0
  78. package/templates/apps/website/src/routes/+layout.svelte.hbs +10 -2
  79. package/templates/base/e2e/app.spec.ts.hbs +13 -8
  80. package/templates/base/package.json.hbs +4 -1
  81. package/templates/base/playwright.config.ts +15 -4
  82. package/templates/base/src/hooks.server.ts.hbs +15 -6
  83. package/templates/base/src/lib/server/init.ts.hbs +139 -30
  84. package/templates/base/vite.config.ts.hbs +2 -2
  85. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
  86. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
  87. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
  88. package/templates/i18n/project.inlang/settings.json +1 -1
  89. package/templates/shared/e2e-env.ts +98 -0
  90. package/templates/shared/run-e2e.mjs +51 -12
  91. package/templates/skills/AGENTS.md +4 -0
  92. package/templates/skills/bridges/admin/AGENTS.md +5 -1
  93. package/templates/skills/bridges/api/AGENTS.md +2 -0
  94. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +4 -0
  95. package/templates/skills/bridges/h5/AGENTS.md +5 -1
  96. package/templates/skills/bridges/mobile-app/AGENTS.md +5 -1
  97. package/templates/skills/bridges/website/AGENTS.md +5 -1
  98. package/templates/skills/hai-ai/SKILL.md +8 -2
  99. package/templates/skills/hai-ai/guide.md +134 -60
  100. package/templates/skills/hai-ai/reference.md +51 -11
  101. package/templates/skills/hai-api-client/SKILL.md +2 -0
  102. package/templates/skills/hai-api-client/reference.md +17 -5
  103. package/templates/skills/hai-api-contract/SKILL.md +1 -0
  104. package/templates/skills/hai-api-contract/reference.md +4 -2
  105. package/templates/skills/hai-app-create/SKILL.md +4 -0
  106. package/templates/skills/hai-app-create/development.md +20 -4
  107. package/templates/skills/hai-app-create/reference.md +2 -1
  108. package/templates/skills/hai-app-review/SKILL.md +6 -2
  109. package/templates/skills/hai-app-review/differential.md +34 -0
  110. package/templates/skills/hai-app-tests/SKILL.md +3 -1
  111. package/templates/skills/hai-app-tests/reference.md +4 -4
  112. package/templates/skills/hai-app-tests/validation.md +10 -3
  113. package/templates/skills/hai-audit/SKILL.md +10 -8
  114. package/templates/skills/hai-audit/reference.md +3 -0
  115. package/templates/skills/hai-build/reference.md +6 -4
  116. package/templates/skills/hai-cache/SKILL.md +9 -3
  117. package/templates/skills/hai-cache/reference.md +53 -11
  118. package/templates/skills/hai-capacitor/SKILL.md +3 -1
  119. package/templates/skills/hai-capacitor/reference.md +9 -5
  120. package/templates/skills/hai-core/SKILL.md +4 -0
  121. package/templates/skills/hai-core/reference.md +25 -5
  122. package/templates/skills/hai-crypto/SKILL.md +10 -2
  123. package/templates/skills/hai-crypto/reference.md +64 -10
  124. package/templates/skills/hai-datapipe/SKILL.md +2 -0
  125. package/templates/skills/hai-datapipe/reference.md +3 -1
  126. package/templates/skills/hai-deploy/SKILL.md +9 -4
  127. package/templates/skills/hai-deploy/middleware.md +51 -37
  128. package/templates/skills/hai-iam/SKILL.md +24 -8
  129. package/templates/skills/hai-iam/reference.md +181 -154
  130. package/templates/skills/hai-kit/SKILL.md +23 -7
  131. package/templates/skills/hai-kit/reference.md +42 -24
  132. package/templates/skills/hai-payment/SKILL.md +13 -11
  133. package/templates/skills/hai-payment/reference.md +80 -41
  134. package/templates/skills/hai-pr-review/SKILL.md +21 -88
  135. package/templates/skills/hai-reach/SKILL.md +5 -2
  136. package/templates/skills/hai-reach/reference.md +83 -22
  137. package/templates/skills/hai-reldb/SKILL.md +5 -2
  138. package/templates/skills/hai-reldb/reference.md +68 -25
  139. package/templates/skills/hai-scheduler/SKILL.md +8 -5
  140. package/templates/skills/hai-scheduler/reference.md +38 -24
  141. package/templates/skills/hai-serv/SKILL.md +23 -8
  142. package/templates/skills/hai-serv/reference.md +123 -87
  143. package/templates/skills/hai-spec-implement/SKILL.md +4 -0
  144. package/templates/skills/hai-spec-implement/evaluation.md +18 -0
  145. package/templates/skills/hai-spec-implement/handoff.md +47 -0
  146. package/templates/skills/hai-spec-implement/templates.md +5 -0
  147. package/templates/skills/hai-spec-implement/workflow.md +2 -0
  148. package/templates/skills/hai-spec-review/SKILL.md +4 -0
  149. package/templates/skills/hai-storage/SKILL.md +8 -2
  150. package/templates/skills/hai-storage/reference.md +64 -25
  151. package/templates/skills/hai-ui/SKILL.md +16 -2
  152. package/templates/skills/hai-ui/guide.md +3 -3
  153. package/templates/skills/hai-ui/reference.md +17 -6
  154. package/templates/skills/hai-ui-design/SKILL.md +31 -0
  155. package/templates/skills/hai-ui-design/patterns.md +26 -0
  156. package/templates/skills/hai-ui-review/SKILL.md +30 -0
  157. package/templates/skills/hai-ui-review/checklist.md +22 -0
  158. package/templates/skills/hai-vecdb/SKILL.md +12 -8
  159. package/templates/skills/hai-vecdb/reference.md +33 -3
  160. package/dist/chunk-7DSURTJ4.js.map +0 -1
  161. package/dist/chunk-AOQ4N657.js +0 -640
  162. package/dist/chunk-AOQ4N657.js.map +0 -1
  163. package/dist/cli-config-templates-6AHNUGBN.js +0 -5
  164. package/dist/src-2V6VP4I5.js.map +0 -1
@@ -1,24 +1,87 @@
1
1
  /**
2
2
  * {{projectName}} — API 客户端初始化。
3
3
  * Web 预览仅使用内存 TokenStore;原生端使用 Capacitor 安全存储。
4
+ * 原生存储失败会拒绝 Promise;登录/退出调用方须处理 auth.setTokens/clear 失败并释放界面 loading。
4
5
  */
5
6
  import { apiClient } from '@h-ai/api-client'
6
7
  import { createCapacitorTokenStorage } from '@h-ai/capacitor'
8
+ {{#if features.crypto}}
9
+ import { crypto } from '@h-ai/crypto'
10
+ {{/if}}
7
11
 
8
12
  const DEFAULT_API_BASE = 'http://localhost:3000/api/v1'
9
13
 
10
14
  let initialized = false
15
+ let initializing: Promise<void> | undefined
16
+ let closing: Promise<void> | undefined
17
+ {{#if features.crypto}}
18
+ // 只关闭本客户端实际初始化的 crypto,保留外部调用方已有实例。
19
+ let ownsCrypto = false
11
20
 
21
+ /**
22
+ * 解析客户端传输选项;仅显式 off 连接匹配的明文 API,握手失败不降级。
23
+ * @returns 绑定实际 crypto 与公开协商子路径的选项,或显式明文模式的 undefined。
24
+ * @example 模块内部上下文
25
+ * ```ts
26
+ * const transport = resolveTransport()
27
+ * if (transport) {
28
+ * await initApi()
29
+ * try {
30
+ * if (!apiClient.config?.transport) throw new Error('Transport configuration missing')
31
+ * }
32
+ * finally { await closeApi() }
33
+ * }
34
+ * ```
35
+ */
36
+ function resolveTransport() {
37
+ const runtime = (globalThis as typeof globalThis & { __HAI_PUBLIC_CONFIG__?: { apiTransport?: string, apiKeyExchangePath?: string } }).__HAI_PUBLIC_CONFIG__
38
+ if ((runtime?.apiTransport || import.meta.env.PUBLIC_API_TRANSPORT) === 'off')
39
+ return undefined
40
+ return { crypto, keyExchangePath: runtime?.apiKeyExchangePath || import.meta.env.PUBLIC_API_KEY_EXCHANGE_PATH || '/_hai/key-exchange' }
41
+ }
42
+ {{/if}}
43
+
44
+ /**
45
+ * 按公开运行配置、构建配置、本地示例地址的顺序解析含 API 前缀的 baseUrl。
46
+ * @returns API baseUrl;服务端私钥和连接配置不属于该公开地址。
47
+ * @example 模块内部上下文
48
+ * ```ts
49
+ * const base = new URL(resolveApiBase())
50
+ * const apiOrigin = base.origin
51
+ * ```
52
+ */
12
53
  export function resolveApiBase(): string {
13
54
  const runtime = (globalThis as typeof globalThis & { __HAI_PUBLIC_CONFIG__?: { apiBase?: string } }).__HAI_PUBLIC_CONFIG__
14
55
  return runtime?.apiBase || import.meta.env.PUBLIC_API_BASE || DEFAULT_API_BASE
15
56
  }
16
57
 
58
+ /**
59
+ * 查询实际 Capacitor 平台,决定安全原生存储或 Web 内存存储。
60
+ * @returns 是否处于 Capacitor 原生平台;浏览器预览返回 false。
61
+ * @throws Error - 平台模块加载或检测失败时拒绝 Promise,不伪装为 Web 模式。
62
+ * @example 模块内部上下文
63
+ * ```ts
64
+ * if (await usesNativeTokenStorage()) {
65
+ * const storage = createCapacitorTokenStorage()
66
+ * const accessToken = await storage.getAccessToken() // 只用于认证,不输出令牌。
67
+ * }
68
+ * ```
69
+ */
17
70
  export async function usesNativeTokenStorage(): Promise<boolean> {
18
71
  const { Capacitor } = await import('@capacitor/core')
19
72
  return Capacitor.isNativePlatform()
20
73
  }
21
74
 
75
+ /**
76
+ * 按真实平台创建 TokenStorage;绑定 client 后通过 client.auth 变更身份。
77
+ * @returns 原生安全存储或 Web 内存存储;其方法的读写故障继续拒绝 Promise。
78
+ * @throws Error - 平台检测或存储工厂失败时拒绝,不改用较弱的持久化方式。
79
+ * @example 模块内部上下文
80
+ * ```ts
81
+ * const storage = await createTokenStorage()
82
+ * const accessToken = await storage.getAccessToken() // null 表示未认证,不记录令牌。
83
+ * ```
84
+ */
22
85
  async function createTokenStorage() {
23
86
  if (await usesNativeTokenStorage())
24
87
  return createCapacitorTokenStorage()
@@ -26,29 +89,125 @@ async function createTokenStorage() {
26
89
  return apiClient.tokenStorage.memory()
27
90
  }
28
91
 
92
+ /**
93
+ * 合并在途初始化并等待关闭;安全存储、所选 crypto 与 typed client 依次就绪。
94
+ * @returns 客户端已准备;重复调用复用同一客户端。
95
+ * @throws Error - 安全存储、crypto 或 client 初始化失败,业务 HaiResult 在此边界明确转成拒绝。
96
+ * @throws AggregateError - 初始化和本次 crypto 清理同时失败,后续 closeApi 可重试。
97
+ * @example 模块内部上下文;目标 API 已提供标准 IAM 端点。
98
+ * ```ts
99
+ * await initApi()
100
+ * try {
101
+ * const current = await apiClient.iam.auth.currentUser()
102
+ * if (!current.success) throw new Error(current.error.message)
103
+ * }
104
+ * finally { await closeApi() }
105
+ * ```
106
+ */
29
107
  export async function initApi(): Promise<void> {
108
+ if (closing)
109
+ await closing
30
110
  if (initialized)
31
111
  return
32
-
33
- const initResult = await apiClient.init({
34
- baseUrl: resolveApiBase(),
35
- auth: {
36
- storage: await createTokenStorage(),
37
- },
112
+ if (initializing)
113
+ return initializing
114
+ {{#if features.crypto}}
115
+ if (ownsCrypto)
116
+ await closeApi()
117
+ {{/if}}
118
+ initializing = initialize().finally(() => {
119
+ initializing = undefined
38
120
  })
121
+ return initializing
122
+ }
39
123
 
40
- if (!initResult.success)
41
- throw new Error(initResult.error.message)
42
-
124
+ /**
125
+ * 在初始化屏障内装配客户端;安全存储先就绪,失败不退回明文并释放本次资源。
126
+ * @returns 当前客户端已初始化;由 initApi 管理并发屏障。
127
+ * @throws Error - 平台、所选 crypto 或 client 拒绝初始化。
128
+ * @throws AggregateError - 初始化失败且本次 crypto 未确认清理,保留关闭重试义务。
129
+ * @example 模块内部上下文;仅在初始化屏障内调用。
130
+ * ```ts
131
+ * await initialize()
132
+ * try {
133
+ * const current = await apiClient.iam.auth.currentUser()
134
+ * if (!current.success) throw new Error(current.error.message)
135
+ * }
136
+ * finally { await closeApi() }
137
+ * ```
138
+ */
139
+ async function initialize(): Promise<void> {
140
+ const storage = await createTokenStorage()
141
+ {{#if features.crypto}}
142
+ const transport = resolveTransport()
143
+ try {
144
+ if (transport && !crypto.isInitialized) {
145
+ const result = await crypto.init()
146
+ if (!result.success)
147
+ throw new Error(result.error.message)
148
+ ownsCrypto = true
149
+ }
150
+ const result = await apiClient.init({
151
+ baseUrl: resolveApiBase(),
152
+ auth: { storage },
153
+ ...(transport ? { transport } : {}),
154
+ })
155
+ if (!result.success)
156
+ throw new Error(result.error.message)
157
+ initialized = true
158
+ }
159
+ catch (error) {
160
+ if (ownsCrypto) {
161
+ try {
162
+ await crypto.close()
163
+ ownsCrypto = false
164
+ }
165
+ catch (cleanupError) {
166
+ throw new AggregateError([error, cleanupError], 'Mobile API initialization and crypto cleanup failed')
167
+ }
168
+ }
169
+ throw error
170
+ }
171
+ {{else}}
172
+ const result = await apiClient.init({ baseUrl: resolveApiBase(), auth: { storage } })
173
+ if (!result.success)
174
+ throw new Error(result.error.message)
43
175
  initialized = true
176
+ {{/if}}
44
177
  }
45
178
 
179
+ /**
180
+ * 等待在途初始化,先销毁客户端 transport,再释放自身初始化的 crypto。
181
+ * @returns 所有本次资源确认关闭;无资源或并发关闭时安全复用。
182
+ * @throws Error - client 或 crypto 未确认关闭;保留状态供下一次 closeApi 重试。
183
+ * @example 模块内部上下文;目标 API 已提供标准 IAM 端点。
184
+ * ```ts
185
+ * await initApi()
186
+ * try {
187
+ * const current = await apiClient.iam.auth.currentUser()
188
+ * if (!current.success) throw new Error(current.error.message)
189
+ * }
190
+ * finally { await closeApi() } // 先 client,再本客户端初始化的 crypto。
191
+ * ```
192
+ */
46
193
  export async function closeApi(): Promise<void> {
47
- if (!initialized)
48
- return
49
-
50
- await apiClient.close()
51
- initialized = false
194
+ closing ??= (async () => {
195
+ if (initializing)
196
+ await initializing.catch(() => undefined)
197
+ if (initialized) {
198
+ await apiClient.close()
199
+ initialized = false
200
+ }
201
+ {{#if features.crypto}}
202
+ if (ownsCrypto) {
203
+ await crypto.close()
204
+ ownsCrypto = false
205
+ }
206
+ {{/if}}
207
+ })().finally(() => {
208
+ closing = undefined
209
+ })
210
+ return closing
52
211
  }
53
212
 
54
213
  export { apiClient }
@@ -0,0 +1,32 @@
1
+ {
2
+ "app_title": "企業サイト",
3
+ "app_description": "企業の公式サイト",
4
+ "meta_description": "企業の公式サイト",
5
+ "nav_home": "ホーム",
6
+ "nav_about": "会社概要",
7
+ "nav_services": "サービス",
8
+ "nav_contact": "担当者",
9
+ "page_home_title": "ようこそ",
10
+ "page_home_subtitle": "専門的で信頼できる企業向けサービス",
11
+ "page_home_cta": "お問い合わせ",
12
+ "page_home_advantages": "私たちの強み",
13
+ "page_home_team": "専門チーム",
14
+ "page_home_team_desc": "長年の業界経験を持つチームです。",
15
+ "page_home_service": "高品質なサービス",
16
+ "page_home_service_desc": "総合的で質の高いサポートを提供します。",
17
+ "page_home_tech": "革新的な技術",
18
+ "page_home_tech_desc": "業界をリードする革新的な技術ソリューション。",
19
+ "page_about_title": "会社概要",
20
+ "page_about_desc": "企業情報とこれまでの歩み。",
21
+ "page_services_title": "サービス",
22
+ "page_services_item1": "サービス1",
23
+ "page_services_item2": "サービス2",
24
+ "page_services_desc": "サービスの説明。",
25
+ "page_contact_title": "お問い合わせ",
26
+ "page_contact_desc": "ご質問はお気軽にお問い合わせください。",
27
+ "page_contact_name": "お名前",
28
+ "page_contact_email": "メールアドレス",
29
+ "page_contact_message": "メッセージ",
30
+ "page_contact_submit": "送信",
31
+ "footer_copyright": "Copyright © {year}. All rights reserved."
32
+ }
@@ -11,8 +11,8 @@
11
11
  import { page } from '$app/state'
12
12
  {{#if hasI18n}}
13
13
  import * as m from '$lib/paraglide/messages'
14
- import { getLocale } from '$lib/paraglide/runtime.js'
15
- import { setGlobalLocale } from '@h-ai/ui'
14
+ import { getLocale, setLocale } from '$lib/paraglide/runtime.js'
15
+ import { LanguageSwitch, setGlobalLocale } from '@h-ai/ui'
16
16
  {{/if}}
17
17
  import '../app.css'
18
18
 
@@ -37,6 +37,11 @@
37
37
  ] as const
38
38
  {{#if hasI18n}}
39
39
 
40
+ function handleLanguageChange(language: string) {
41
+ setGlobalLocale(language)
42
+ setLocale(language as Parameters<typeof setLocale>[0])
43
+ }
44
+
40
45
  if (browser) {
41
46
  const paraglideLocale = getLocale()
42
47
  setGlobalLocale(paraglideLocale)
@@ -64,6 +69,9 @@
64
69
  {/each}
65
70
  </ul>
66
71
  </nav>
72
+ {{#if hasI18n}}
73
+ <LanguageSwitch currentLanguage={getLocale()} onchange={handleLanguageChange} />
74
+ {{/if}}
67
75
  </header>
68
76
 
69
77
  <main class='flex-1'>
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from 'node:crypto'
1
2
  import { mkdir } from 'node:fs/promises'
2
3
  import path from 'node:path'
3
4
  import process from 'node:process'
@@ -67,7 +68,7 @@ test('configured IAM registration and password login', async ({ page, context })
67
68
  {{/if_eq}}
68
69
  const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
69
70
  await mkdir(evidence, { recursive: true })
70
- const username = `scaf${Date.now()}`
71
+ const username = `scaf${randomUUID().replaceAll('-', '').slice(0, 12)}`
71
72
  const password = 'ScaffoldPassword123'
72
73
  const authUrl = '{{#if isCapacitorApp}}/{{else}}/auth/login{{/if}}'
73
74
  await page.goto(authUrl)
@@ -76,11 +77,15 @@ test('configured IAM registration and password login', async ({ page, context })
76
77
  await expect(page.locator('footer')).toBeVisible()
77
78
  await page.screenshot({ path: path.join(evidence, '{{projectName}}-login.png'), fullPage: true })
78
79
  await page.getByRole('{{#if isCapacitorApp}}button{{else}}link{{/if}}', { name: /立即注册|Register now/i }).click()
79
- await expect(page.locator('input[name="username"]')).toBeVisible()
80
+ {{#unless isCapacitorApp}}
81
+ await expect(page).toHaveURL(/\/auth\/register(?:\?|$)/)
82
+ {{/unless}}
83
+ const registerForm = page.locator('.register-form')
84
+ await expect(registerForm.locator('#register-username')).toBeVisible()
80
85
  await page.screenshot({ path: path.join(evidence, '{{projectName}}-register.png'), fullPage: true })
81
86
  if (process.env.IAM_NOTIFICATION_URL) {
82
87
  // 协议弹窗留在认证页,关闭后继续填写注册表单。
83
- await page.getByRole('button', { name: /用户协议|User Agreement/i, exact: true }).click()
88
+ await registerForm.getByRole('button', { name: /用户协议|Terms of Service/i, exact: true }).click()
84
89
  await expect(page.getByRole('dialog')).toBeVisible()
85
90
  await expect(page.getByRole('dialog').getByRole('heading', { name: 'Configured registration agreement' })).toBeVisible()
86
91
  await expect(page.locator('iframe')).toHaveCount(0)
@@ -88,11 +93,11 @@ test('configured IAM registration and password login', async ({ page, context })
88
93
  await page.keyboard.press('Escape')
89
94
  await expect(page.getByRole('dialog')).not.toBeVisible()
90
95
  }
91
- await page.locator('input[name="username"]').fill(username)
92
- await page.locator('input[name="email"]').fill(`${username}@example.com`)
93
- await page.locator('input[type="password"]').nth(0).fill(password)
94
- await page.locator('input[type="password"]').nth(1).fill(password)
95
- await page.locator('button[type="submit"]').click()
96
+ await registerForm.locator('#register-username').fill(username)
97
+ await registerForm.locator('#register-email').fill(`${username}@example.com`)
98
+ await registerForm.locator('#register-password').fill(password)
99
+ await registerForm.locator('#register-confirm-password').fill(password)
100
+ await registerForm.locator('button[type="submit"]').click()
96
101
  await expect(page.locator('.register-form')).not.toBeVisible()
97
102
  {{#unless isCapacitorApp}}
98
103
  const me = await page.request.get('/api/auth/me')
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "engines": { "node": "^22.22.2 || ^24.15.0 || >=26.0.0" },
3
3
  "name": "{{projectName}}",
4
- "version": "0.1.0-alpha.67",
4
+ "version": "0.1.0-alpha.68",
5
5
  "private": true,
6
6
  "packageManager": "pnpm@12.11.2",
7
7
  "description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
@@ -59,6 +59,9 @@
59
59
  {{#if isSvelteOnlyApp}}
60
60
  "@h-ai/api-client": "{{haiPackageSpecifier}}",
61
61
  "@h-ai/capacitor": "{{haiPackageSpecifier}}",
62
+ {{#if features.crypto}}
63
+ "@h-ai/crypto": "{{haiPackageSpecifier}}",
64
+ {{/if}}
62
65
  "@h-ai/ui": "{{haiPackageSpecifier}}"
63
66
  {{else}}
64
67
  "@h-ai/core": "{{haiPackageSpecifier}}",
@@ -1,5 +1,6 @@
1
1
  import process from 'node:process'
2
2
  import { defineConfig } from '@playwright/test'
3
+ import { createE2eNotificationEnv, createE2eServerEnv, createE2eTransportRing } from './scripts/e2e-env.js'
3
4
 
4
5
  const DEFAULT_BASE_URL = 'http://localhost:4173'
5
6
  const baseURL = process.env.BASE_URL || DEFAULT_BASE_URL
@@ -35,9 +36,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
35
36
  */
36
37
  export default defineConfig({
37
38
  testDir: './e2e',
38
- fullyParallel: false,
39
+ fullyParallel: true,
39
40
  forbidOnly: !!process.env.CI,
40
- retries: process.env.CI ? 2 : 0,
41
+ retries: 0,
41
42
  workers: 2,
42
43
  reporter: 'list',
43
44
  timeout: 30_000,
@@ -45,13 +46,23 @@ export default defineConfig({
45
46
  use: {
46
47
  baseURL,
47
48
  channel: 'chrome',
48
- trace: 'on-first-retry',
49
+ trace: 'retain-on-failure',
49
50
  screenshot: 'only-on-failure',
50
51
  },
51
52
 
52
53
  webServer: {
53
54
  command: `pnpm build && pnpm preview --host ${previewHost} --port ${previewPort} --strictPort`,
54
- env: { HAI_E2E: '1', BASE_URL: baseURL },
55
+ env: createE2eServerEnv({
56
+ ...createE2eNotificationEnv(),
57
+ HAI_CORE_ENV: 'test',
58
+ BASE_URL: baseURL,
59
+ PUBLIC_API_BASE: process.env.PUBLIC_API_BASE || '',
60
+ HAI_DB: JSON.stringify({ type: 'sqlite', database: `${process.env.HAI_E2E_DATA_ROOT}/app.db` }),
61
+ HAI_CACHE: JSON.stringify({ type: 'memory' }),
62
+ HAI_STORAGE: JSON.stringify({ type: 'local', root: `${process.env.HAI_E2E_DATA_ROOT}/uploads` }),
63
+ HAI_VECDB: JSON.stringify({ type: 'lancedb', path: `${process.env.HAI_E2E_DATA_ROOT}/vecdb` }),
64
+ HAI_CRYPTO_TRANSPORT_SERVERKEYRING: createE2eTransportRing(),
65
+ }),
55
66
  url: baseURL,
56
67
  reuseExistingServer: false,
57
68
  timeout: 180_000,
@@ -5,10 +5,16 @@
5
5
  */
6
6
 
7
7
  import type { Handle } from '@sveltejs/kit'
8
+ {{#if features.cache}}
9
+ import process from 'node:process'
10
+ {{/if}}
8
11
  {{#if hasI18n}}
9
12
  import { paraglideMiddleware } from '$lib/paraglide/server.js'
10
13
  {{/if}}
11
14
  import { initApp } from '$lib/server/init.js'
15
+ {{#if features.cache}}
16
+ import { cache } from '@h-ai/cache'
17
+ {{/if}}
12
18
  import { core } from '@h-ai/core'
13
19
  {{#if features.iam}}
14
20
  import { iam } from '@h-ai/iam'
@@ -54,16 +60,14 @@ const i18nHandle: Handle = async ({ event, resolve }) => {
54
60
  if (event.url.pathname.startsWith('/api/')) {
55
61
  const locale = event.cookies.get('PARAGLIDE_LOCALE') ?? '{{defaultLocale}}'
56
62
  event.locals.locale = locale
57
- kit.i18n.setLocale(locale)
58
- return resolve(event)
63
+ return kit.i18n.runWithLocale(locale, () => resolve(event))
59
64
  }
60
65
 
61
66
  return paraglideMiddleware(event.request, async ({ locale }) => {
62
67
  event.locals.locale = locale
63
- kit.i18n.setLocale(locale)
64
- return resolve(event, {
68
+ return kit.i18n.runWithLocale(locale, () => resolve(event, {
65
69
  transformPageChunk: ({ html }) => html.replace('%lang%', locale),
66
- })
70
+ }))
67
71
  })
68
72
  }
69
73
 
@@ -95,7 +99,12 @@ const haiHandle = kit.createHandle({
95
99
  operations: () => iam.auth,
96
100
  },
97
101
  {{/if}}
98
- rateLimit: { windowMs: 60000, maxRequests: 200 },
102
+ {{#if features.cache}}
103
+ rateLimit: { windowMs: 60000, maxRequests: 200, store: kit.middleware.createCacheRateLimitStore(cache, { namespace: '{{projectName}}', allowMemory: process.env.NODE_ENV !== 'production' || process.env.HAI_E2E === '1' }) },
104
+ {{else}}
105
+ // 未选择cache能力时不启用限流;启用前须接入共享原子store。
106
+ rateLimit: false,
107
+ {{/if}}
99
108
  logging: { logBody: false },
100
109
  onError: (error: unknown, _event) => {
101
110
  core.logger.error('Request error:', { error })