@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.
- package/README.en.md +40 -4
- package/README.ja.md +40 -4
- package/README.md +40 -4
- package/dist/{src-2V6VP4I5.js → chunk-FI56GONY.js} +32 -3103
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-7DSURTJ4.js → chunk-ZFNBUF77.js} +327 -199
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-6AHNUGBN.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-FC3QRKDO.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-FC3QRKDO.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +920 -336
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +5 -1
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
- package/templates/apps/api/package.json.hbs +1 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +19 -8
- package/templates/apps/fullstack/package.json.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +5 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +9 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +5 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +11 -12
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +14 -3
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +13 -3
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +10 -2
- package/templates/base/e2e/app.spec.ts.hbs +13 -8
- package/templates/base/package.json.hbs +4 -1
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +2 -2
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +4 -0
- package/templates/skills/bridges/admin/AGENTS.md +5 -1
- package/templates/skills/bridges/api/AGENTS.md +2 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +4 -0
- package/templates/skills/bridges/h5/AGENTS.md +5 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +5 -1
- package/templates/skills/bridges/website/AGENTS.md +5 -1
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +2 -0
- package/templates/skills/hai-api-client/reference.md +17 -5
- package/templates/skills/hai-api-contract/SKILL.md +1 -0
- package/templates/skills/hai-api-contract/reference.md +4 -2
- package/templates/skills/hai-app-create/SKILL.md +4 -0
- package/templates/skills/hai-app-create/development.md +20 -4
- package/templates/skills/hai-app-create/reference.md +2 -1
- package/templates/skills/hai-app-review/SKILL.md +6 -2
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +3 -1
- package/templates/skills/hai-app-tests/reference.md +4 -4
- package/templates/skills/hai-app-tests/validation.md +10 -3
- package/templates/skills/hai-audit/SKILL.md +10 -8
- package/templates/skills/hai-audit/reference.md +3 -0
- package/templates/skills/hai-build/reference.md +6 -4
- package/templates/skills/hai-cache/SKILL.md +9 -3
- package/templates/skills/hai-cache/reference.md +53 -11
- package/templates/skills/hai-capacitor/SKILL.md +3 -1
- package/templates/skills/hai-capacitor/reference.md +9 -5
- package/templates/skills/hai-core/SKILL.md +4 -0
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +10 -2
- package/templates/skills/hai-crypto/reference.md +64 -10
- package/templates/skills/hai-datapipe/SKILL.md +2 -0
- package/templates/skills/hai-datapipe/reference.md +3 -1
- package/templates/skills/hai-deploy/SKILL.md +9 -4
- package/templates/skills/hai-deploy/middleware.md +51 -37
- package/templates/skills/hai-iam/SKILL.md +24 -8
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +23 -7
- package/templates/skills/hai-kit/reference.md +42 -24
- package/templates/skills/hai-payment/SKILL.md +13 -11
- package/templates/skills/hai-payment/reference.md +80 -41
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +5 -2
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +5 -2
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +8 -5
- package/templates/skills/hai-scheduler/reference.md +38 -24
- package/templates/skills/hai-serv/SKILL.md +23 -8
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +4 -0
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +2 -0
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +8 -2
- package/templates/skills/hai-storage/reference.md +64 -25
- package/templates/skills/hai-ui/SKILL.md +16 -2
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +17 -6
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +12 -8
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-7DSURTJ4.js.map +0 -1
- package/dist/chunk-AOQ4N657.js +0 -640
- package/dist/chunk-AOQ4N657.js.map +0 -1
- package/dist/cli-config-templates-6AHNUGBN.js +0 -5
- 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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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${
|
|
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
|
-
|
|
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
|
|
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
|
|
92
|
-
await
|
|
93
|
-
await
|
|
94
|
-
await
|
|
95
|
-
await
|
|
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.
|
|
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:
|
|
39
|
+
fullyParallel: true,
|
|
39
40
|
forbidOnly: !!process.env.CI,
|
|
40
|
-
retries:
|
|
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-
|
|
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: {
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 })
|