@h-ai/cli 0.1.0-alpha.20 → 0.1.0-alpha.22

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 (182) hide show
  1. package/README.md +10 -7
  2. package/dist/{chunk-GSMXGUYF.js → chunk-5IGMJVV5.js} +56 -38
  3. package/dist/chunk-5IGMJVV5.js.map +1 -0
  4. package/dist/{chunk-3BBXDQ4Y.js → chunk-CFCXOKGV.js} +24 -2
  5. package/dist/chunk-CFCXOKGV.js.map +1 -0
  6. package/dist/cli-config-templates-5J5VKWWD.js +5 -0
  7. package/dist/{cli-config-templates-IGGO7P45.js.map → cli-config-templates-5J5VKWWD.js.map} +1 -1
  8. package/dist/cli-skill-templates-JMSV2SVW.js +5 -0
  9. package/dist/{cli-skill-templates-IMYZPQRG.js.map → cli-skill-templates-JMSV2SVW.js.map} +1 -1
  10. package/dist/index.js +567 -75
  11. package/dist/index.js.map +1 -1
  12. package/package.json +3 -2
  13. package/templates/README.md +40 -29
  14. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +16 -0
  15. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +38 -0
  16. package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
  17. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +33 -0
  18. package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +3 -0
  19. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +12 -0
  20. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
  21. package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
  22. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
  23. package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
  24. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +17 -0
  25. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +19 -0
  26. package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +26 -0
  27. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +55 -0
  28. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +98 -0
  29. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +21 -0
  30. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +184 -0
  31. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +49 -0
  32. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +18 -0
  33. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +168 -0
  34. package/templates/apps/api/apps/{{projectName}}-service/tsconfig.json.hbs +16 -0
  35. package/templates/apps/api/apps/{{projectName}}-service/tsup.config.ts.hbs +11 -0
  36. package/templates/apps/api/apps/{{projectName}}-service/vitest.config.ts.hbs +9 -0
  37. package/templates/apps/api/e2e/api-service.spec.ts.hbs +18 -0
  38. package/templates/apps/api/eslint.config.js +32 -0
  39. package/templates/apps/api/package.json.hbs +29 -0
  40. package/templates/apps/api/playwright.config.ts.hbs +58 -0
  41. package/templates/apps/api/pnpm-workspace.yaml.hbs +14 -0
  42. package/templates/apps/api/tsconfig.base.json +15 -0
  43. package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -7
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +18 -0
  46. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +1 -1
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +16 -0
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +6 -0
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +6 -0
  50. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +51 -29
  51. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +186 -0
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +2 -0
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +0 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +18 -0
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +14 -0
  56. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +1 -5
  57. package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +10 -4
  58. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +4 -3
  59. package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
  60. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +20 -6
  61. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +1 -1
  62. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +15 -0
  63. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +33 -30
  64. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +152 -0
  65. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +1 -2
  66. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +13 -0
  67. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +22 -0
  68. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
  69. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +12 -0
  70. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
  71. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
  72. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
  73. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
  74. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
  75. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +16 -0
  76. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
  77. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +41 -0
  78. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +2 -8
  79. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +9 -4
  80. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +44 -21
  81. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +15 -6
  82. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +1 -1
  83. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +15 -0
  84. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +25 -27
  85. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +153 -0
  86. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +0 -1
  87. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
  88. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +1 -5
  89. package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +10 -4
  90. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -2
  91. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +2 -1
  92. package/templates/apps/fullstack/eslint.config.js +0 -1
  93. package/templates/apps/fullstack/package.json.hbs +18 -17
  94. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +3 -3
  95. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +2 -2
  96. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +2 -2
  97. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -1
  98. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/{server-app.test.ts.hbs → app-server.test.ts.hbs} +1 -1
  99. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +9 -8
  100. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +1 -1
  101. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +5 -1
  102. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +1 -1
  103. package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +1 -1
  104. package/templates/apps/fullstack/playwright.config.ts.hbs +41 -4
  105. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +15 -0
  106. package/templates/apps/mobile-app/.env.hbs +3 -0
  107. package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
  108. package/templates/apps/mobile-app/index.html.hbs +13 -0
  109. package/templates/apps/mobile-app/messages/en-US.json +36 -0
  110. package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
  111. package/templates/apps/mobile-app/src/App.svelte.hbs +162 -0
  112. package/templates/apps/mobile-app/src/lib/api.ts.hbs +54 -0
  113. package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +5 -6
  114. package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
  115. package/templates/base/.gitignore.hbs +16 -0
  116. package/templates/base/{eslint.config.js → eslint.config.js.hbs} +4 -1
  117. package/templates/base/package.json.hbs +52 -33
  118. package/templates/base/playwright.config.ts +30 -3
  119. package/templates/base/pnpm-workspace.yaml.hbs +14 -0
  120. package/templates/base/src/app.d.ts.hbs +3 -0
  121. package/templates/base/src/app.html.hbs +3 -0
  122. package/templates/base/svelte.config.js.hbs +43 -3
  123. package/templates/base/tests/scaffold.test.ts.hbs +7 -0
  124. package/templates/base/tsconfig.json.hbs +50 -0
  125. package/templates/base/vite.config.ts.hbs +10 -0
  126. package/templates/skills/AGENTS.md +57 -24
  127. package/templates/skills/CLAUDE.md +18 -7
  128. package/templates/skills/bridges/admin/AGENTS.md +66 -0
  129. package/templates/skills/bridges/admin/CLAUDE.md +19 -0
  130. package/templates/skills/bridges/admin/copilot-instructions.md +46 -0
  131. package/templates/skills/bridges/api/AGENTS.md +65 -0
  132. package/templates/skills/bridges/api/CLAUDE.md +19 -0
  133. package/templates/skills/bridges/api/copilot-instructions.md +46 -0
  134. package/templates/skills/bridges/fullstack/AGENTS.md +84 -0
  135. package/templates/skills/bridges/fullstack/CLAUDE.md +19 -0
  136. package/templates/skills/bridges/fullstack/copilot-instructions.md +54 -0
  137. package/templates/skills/bridges/h5/AGENTS.md +65 -0
  138. package/templates/skills/bridges/h5/CLAUDE.md +19 -0
  139. package/templates/skills/bridges/h5/copilot-instructions.md +44 -0
  140. package/templates/skills/bridges/mobile-app/AGENTS.md +77 -0
  141. package/templates/skills/bridges/mobile-app/CLAUDE.md +19 -0
  142. package/templates/skills/bridges/mobile-app/copilot-instructions.md +45 -0
  143. package/templates/skills/bridges/website/AGENTS.md +65 -0
  144. package/templates/skills/bridges/website/CLAUDE.md +19 -0
  145. package/templates/skills/bridges/website/copilot-instructions.md +44 -0
  146. package/templates/skills/copilot-instructions.md +27 -64
  147. package/templates/skills/hai-app-create/SKILL.md +1 -0
  148. package/templates/skills/hai-app-review/SKILL.md +1 -1
  149. package/templates/skills/hai-app-tests/SKILL.md +28 -3
  150. package/templates/skills/hai-capacitor/SKILL.md +14 -22
  151. package/templates/skills/hai-ui/SKILL.md +8 -2
  152. package/templates/skills/opencode.json +1 -6
  153. package/dist/chunk-3BBXDQ4Y.js.map +0 -1
  154. package/dist/chunk-GSMXGUYF.js.map +0 -1
  155. package/dist/cli-config-templates-IGGO7P45.js +0 -5
  156. package/dist/cli-skill-templates-IMYZPQRG.js +0 -5
  157. package/templates/apps/android-app/.env.hbs +0 -3
  158. package/templates/apps/android-app/messages/en-US.json +0 -24
  159. package/templates/apps/android-app/messages/zh-CN.json +0 -24
  160. package/templates/apps/android-app/src/lib/api.ts.hbs +0 -32
  161. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -43
  162. package/templates/apps/android-app/src/routes/+layout.ts +0 -7
  163. package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -38
  164. package/templates/apps/api/src/routes/+layout.svelte +0 -14
  165. package/templates/apps/api/src/routes/+page.svelte +0 -11
  166. package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
  167. package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
  168. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
  169. package/templates/apps/api/tests/health.test.ts.hbs +0 -26
  170. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.html.hbs +0 -14
  171. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+layout.svelte.hbs +0 -18
  172. package/templates/apps/fullstack/apps/{{projectName}}-app/src/routes/+page.svelte.hbs +0 -162
  173. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.html.hbs +0 -14
  174. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+layout.svelte.hbs +0 -18
  175. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/routes/+page.svelte.hbs +0 -162
  176. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.html.hbs +0 -14
  177. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+layout.svelte.hbs +0 -18
  178. package/templates/apps/fullstack/apps/{{projectName}}-web/src/routes/+page.svelte.hbs +0 -162
  179. package/templates/apps/fullstack/pnpm-workspace.yaml +0 -7
  180. package/templates/base/tsconfig.json +0 -20
  181. /package/templates/apps/fullstack/packages/{{projectName}}-serv/src/{server-app.ts.hbs → app-server.ts.hbs} +0 -0
  182. /package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/{theme.svelte.ts.hbs → theme-store.svelte.ts.hbs} +0 -0
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://inlang.com/schema/inlang-message-format",
3
+ "app_title": "移动工作台",
4
+ "app_description": "基于 hai Agent Framework + Capacitor + Svelte 5 构建的移动应用",
5
+ "boot_failed_title": "启动失败",
6
+ "common_retry": "重试",
7
+ "common_refresh": "刷新",
8
+ "common_enabled": "已启用",
9
+ "common_disabled": "未启用",
10
+ "nav_home": "工作台",
11
+ "nav_tasks": "任务",
12
+ "nav_profile": "我的",
13
+ "hero_eyebrow": "Mobile Workspace",
14
+ "hero_title": "真正可用的移动端起点",
15
+ "hero_subtitle": "Capacitor 原生能力、安全 Token 存储、统一 API Client 与 @h-ai/ui 移动组件已经接好。",
16
+ "feature_native_title": "原生能力",
17
+ "feature_native_desc": "状态栏、安全区与设备信息就绪,可继续接入推送、相机等插件。",
18
+ "feature_api_title": "统一 API",
19
+ "feature_api_desc": "通过 PUBLIC_API_BASE 连接服务端,Token 在原生端走安全存储。",
20
+ "status_title": "运行状态",
21
+ "status_api_label": "API 地址",
22
+ "status_runtime_label": "运行环境",
23
+ "status_runtime_native": "原生 App",
24
+ "status_runtime_web": "浏览器预览",
25
+ "status_token_label": "Token 存储",
26
+ "status_token_native": "安全存储",
27
+ "status_token_memory": "内存存储",
28
+ "tasks_title": "今日任务",
29
+ "tasks_sync": "同步后端接口",
30
+ "tasks_polish": "打磨移动端体验",
31
+ "tasks_release": "准备原生打包",
32
+ "profile_title": "应用设置",
33
+ "profile_language": "语言跟随 Paraglide",
34
+ "profile_theme": "主题跟随 @h-ai/ui",
35
+ "profile_secure": "敏感 Token 不落 localStorage"
36
+ }
@@ -0,0 +1,162 @@
1
+ <script lang='ts'>
2
+ import { applyTheme, getSavedTheme, setGlobalLocale } from '@h-ai/ui'
3
+ import { onMount } from 'svelte'
4
+ import { closeApi, initApi, resolveApiBase, usesNativeTokenStorage } from './lib/api.js'
5
+ import { initCapacitor, isNativeApp } from './lib/capacitor.js'
6
+ import * as m from './lib/paraglide/messages.js'
7
+ import { getLocale } from './lib/paraglide/runtime.js'
8
+
9
+ type TabId = 'home' | 'tasks' | 'profile'
10
+
11
+ let booting = $state(true)
12
+ let bootError = $state<string | null>(null)
13
+ let activeTab = $state<TabId>('home')
14
+ let nativeRuntime = $state(false)
15
+ let nativeTokenStorage = $state(false)
16
+
17
+ const navItems = $derived([
18
+ { id: 'home', label: m.nav_home(), iconClass: 'icon-[tabler--layout-dashboard]' },
19
+ { id: 'tasks', label: m.nav_tasks(), iconClass: 'icon-[tabler--checkbox]' },
20
+ { id: 'profile', label: m.nav_profile(), iconClass: 'icon-[tabler--user-circle]' },
21
+ ])
22
+
23
+ async function bootstrap(): Promise<void> {
24
+ try {
25
+ applyTheme(getSavedTheme(), false)
26
+ setGlobalLocale(getLocale())
27
+ await initCapacitor()
28
+ await initApi()
29
+ nativeRuntime = await isNativeApp()
30
+ nativeTokenStorage = await usesNativeTokenStorage()
31
+ }
32
+ catch (error) {
33
+ bootError = error instanceof Error ? error.message : String(error)
34
+ }
35
+ finally {
36
+ booting = false
37
+ }
38
+ }
39
+
40
+ function handleTabChange(id: string): void {
41
+ if (id === 'home' || id === 'tasks' || id === 'profile')
42
+ activeTab = id
43
+ }
44
+
45
+ onMount(() => {
46
+ void bootstrap()
47
+
48
+ return () => {
49
+ void closeApi()
50
+ }
51
+ })
52
+ </script>
53
+
54
+ {#if booting}
55
+ <div class='hai-mobile-viewport'>
56
+ <div class='hai-mobile-shell max-w-lg'>
57
+ <div class='flex min-h-screen items-center justify-center'>
58
+ <span class='loading loading-spinner loading-lg'></span>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ {:else if bootError}
63
+ <div class='hai-mobile-viewport'>
64
+ <div class='hai-mobile-shell max-w-lg'>
65
+ <div class='flex min-h-screen flex-col items-center justify-center gap-3 p-6 text-center'>
66
+ <span class='icon-[tabler--alert-triangle] text-error text-4xl'></span>
67
+ <h1 class='text-lg font-bold'>{m.boot_failed_title()}</h1>
68
+ <p class='text-base-content/60 text-sm'>{bootError}</p>
69
+ <Button variant='primary' size='sm' onclick={() => location.reload()}>{m.common_retry()}</Button>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ {:else}
74
+ <div class='hai-mobile-viewport'>
75
+ <div class='hai-mobile-shell max-w-lg'>
76
+ <AppBar title={m.app_title()} safeArea fixed={false} class='bg-base-100/95 backdrop-blur border-base-content/8' />
77
+
78
+ <main class='hai-mobile-main'>
79
+ {#if activeTab === 'home'}
80
+ <section class='space-y-4 p-4 pb-6'>
81
+ <div class='bg-primary text-primary-content rounded-2xl p-5 shadow-sm'>
82
+ <p class='text-sm opacity-75'>{m.hero_eyebrow()}</p>
83
+ <h1 class='mt-1 text-2xl font-bold'>{m.hero_title()}</h1>
84
+ <p class='mt-2 text-sm opacity-80'>{m.hero_subtitle()}</p>
85
+ </div>
86
+
87
+ <div class='grid grid-cols-2 gap-3'>
88
+ <Card padding='sm' shadow='sm'>
89
+ <div class='flex flex-col gap-2'>
90
+ <span class='icon-[tabler--device-mobile-code] text-primary text-2xl'></span>
91
+ <span class='text-sm font-medium'>{m.feature_native_title()}</span>
92
+ <span class='text-base-content/60 text-xs'>{m.feature_native_desc()}</span>
93
+ </div>
94
+ </Card>
95
+ <Card padding='sm' shadow='sm'>
96
+ <div class='flex flex-col gap-2'>
97
+ <span class='icon-[tabler--api-app] text-primary text-2xl'></span>
98
+ <span class='text-sm font-medium'>{m.feature_api_title()}</span>
99
+ <span class='text-base-content/60 text-xs'>{m.feature_api_desc()}</span>
100
+ </div>
101
+ </Card>
102
+ </div>
103
+
104
+ <Card title={m.status_title()} shadow='sm'>
105
+ <div class='grid grid-cols-2 gap-3 text-sm'>
106
+ <div>
107
+ <p class='text-base-content/50'>{m.status_api_label()}</p>
108
+ <p class='font-medium break-all'>{resolveApiBase()}</p>
109
+ </div>
110
+ <div>
111
+ <p class='text-base-content/50'>{m.status_runtime_label()}</p>
112
+ <Badge variant={nativeRuntime ? 'success' : 'warning'} size='sm'>
113
+ {nativeRuntime ? m.status_runtime_native() : m.status_runtime_web()}
114
+ </Badge>
115
+ </div>
116
+ <div class='col-span-2'>
117
+ <p class='text-base-content/50'>{m.status_token_label()}</p>
118
+ <p class='font-medium'>{nativeTokenStorage ? m.status_token_native() : m.status_token_memory()}</p>
119
+ </div>
120
+ </div>
121
+ </Card>
122
+ </section>
123
+ {:else if activeTab === 'tasks'}
124
+ <section class='space-y-4 p-4 pb-6'>
125
+ <Card title={m.tasks_title()} shadow='sm'>
126
+ <div class='space-y-3'>
127
+ {#each [m.tasks_sync(), m.tasks_polish(), m.tasks_release()] as task (task)}
128
+ <div class='bg-base-200 rounded-xl flex items-center gap-3 p-3'>
129
+ <span class='icon-[tabler--circle-check] text-success text-xl'></span>
130
+ <span class='text-sm font-medium'>{task}</span>
131
+ </div>
132
+ {/each}
133
+ </div>
134
+ </Card>
135
+ </section>
136
+ {:else}
137
+ <section class='space-y-4 p-4 pb-6'>
138
+ <Card title={m.profile_title()} shadow='sm'>
139
+ <div class='space-y-3 text-sm'>
140
+ <div class='bg-base-200 rounded-xl p-3'>{m.profile_language()}</div>
141
+ <div class='bg-base-200 rounded-xl p-3'>{m.profile_theme()}</div>
142
+ <div class='bg-base-200 rounded-xl p-3'>{m.profile_secure()}</div>
143
+ </div>
144
+ </Card>
145
+ </section>
146
+ {/if}
147
+ </main>
148
+
149
+ <BottomNav
150
+ items={navItems}
151
+ active={activeTab}
152
+ onchange={handleTabChange}
153
+ safeArea
154
+ centered
155
+ maxWidth='lg'
156
+ class='border-base-content/8 shadow-[0_-12px_32px_rgba(15,23,42,0.08)]'
157
+ />
158
+ </div>
159
+ </div>
160
+ {/if}
161
+
162
+ <ToastContainer />
@@ -0,0 +1,54 @@
1
+ /**
2
+ * {{projectName}} — API 客户端初始化。
3
+ * Web 预览仅使用内存 TokenStore;原生端使用 Capacitor 安全存储。
4
+ */
5
+ import { apiClient } from '@h-ai/api-client'
6
+ import { createCapacitorTokenStorage } from '@h-ai/capacitor'
7
+
8
+ const DEFAULT_API_BASE = 'http://localhost:3000/api/v1'
9
+
10
+ let initialized = false
11
+
12
+ export function resolveApiBase(): string {
13
+ return import.meta.env.PUBLIC_API_BASE ?? DEFAULT_API_BASE
14
+ }
15
+
16
+ export async function usesNativeTokenStorage(): Promise<boolean> {
17
+ const { Capacitor } = await import('@capacitor/core')
18
+ return Capacitor.isNativePlatform()
19
+ }
20
+
21
+ async function createTokenStorage() {
22
+ if (await usesNativeTokenStorage())
23
+ return createCapacitorTokenStorage()
24
+
25
+ return apiClient.tokenStorage.memory()
26
+ }
27
+
28
+ export async function initApi(): Promise<void> {
29
+ if (initialized)
30
+ return
31
+
32
+ const initResult = await apiClient.init({
33
+ baseUrl: resolveApiBase(),
34
+ auth: {
35
+ storage: await createTokenStorage(),
36
+ refreshPath: '/auth/refresh',
37
+ },
38
+ })
39
+
40
+ if (!initResult.success)
41
+ throw new Error(initResult.error.message)
42
+
43
+ initialized = true
44
+ }
45
+
46
+ export async function closeApi(): Promise<void> {
47
+ if (!initialized)
48
+ return
49
+
50
+ await apiClient.close()
51
+ initialized = false
52
+ }
53
+
54
+ export { apiClient }
@@ -1,13 +1,12 @@
1
1
  /**
2
- * Capacitor 初始化
3
- *
4
- * 在应用启动时调用,配置状态栏等原生能力。
2
+ * Capacitor 初始化。
5
3
  */
6
4
  import { capacitor } from '@h-ai/capacitor'
7
5
 
8
- /**
9
- * 初始化 Capacitor 原生能力
10
- */
6
+ export async function isNativeApp(): Promise<boolean> {
7
+ return capacitor.isNative()
8
+ }
9
+
11
10
  export async function initCapacitor(): Promise<void> {
12
11
  await capacitor.init()
13
12
 
@@ -0,0 +1,11 @@
1
+ import { mount } from 'svelte'
2
+ import App from './App.svelte'
3
+ import './app.css'
4
+
5
+ const target = document.getElementById('app')
6
+ if (!target)
7
+ throw new Error('Root element #app not found')
8
+
9
+ const app = mount(App, { target })
10
+
11
+ export default app
@@ -0,0 +1,16 @@
1
+ node_modules
2
+ {{#unless isSvelteOnlyApp}}
3
+ .svelte-kit
4
+ {{/unless}}
5
+ build
6
+ dist
7
+ android
8
+ ios
9
+ .env
10
+ .env.*
11
+ !.env.example
12
+ *.log
13
+ .DS_Store
14
+ test-results/
15
+ playwright-report/
16
+ data/
@@ -9,8 +9,11 @@ import antfu from '@antfu/eslint-config'
9
9
  export default antfu({
10
10
  formatters: true,
11
11
  ignores: [
12
+ {{#unless isSvelteOnlyApp}}
12
13
  '**/.svelte-kit/**',
14
+ {{/unless}}
13
15
  '**/build/**',
16
+ '**/dist/**',
14
17
  '**/*.d.ts',
15
18
  '**/project.inlang/**',
16
19
  '**/paraglide/**',
@@ -29,4 +32,4 @@ export default antfu({
29
32
  'no-console': 'error',
30
33
  'ts/no-explicit-any': 'error',
31
34
  },
32
- })
35
+ })
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "{{projectName}}",
3
- "version": "0.1.0-alpha.20",
3
+ "version": "0.1.0-alpha.22",
4
4
  "private": true,
5
- "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 'android-app'}}Android app built with hai Agent Framework{{/if_eq}}",
6
- "keywords": ["hai", "sveltekit", "svelte5", "{{appType}}"],
5
+ "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}}",
6
+ "keywords": ["hai", {{#unless isSvelteOnlyApp}}"sveltekit", {{/unless}}"svelte5", "{{appType}}"],
7
7
  "license": "Apache-2.0",
8
8
  "type": "module",
9
9
  "scripts": {
@@ -11,54 +11,72 @@
11
11
  "paraglide:compile": "paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide",
12
12
  {{/if}}
13
13
  "dev": "vite dev",
14
- "build": "vite build",
14
+ "build": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}vite build",
15
15
  "preview": "vite preview",
16
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
17
- "typecheck": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
16
+ "check": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}{{#if isSvelteOnlyApp}}svelte-check --tsconfig ./tsconfig.json{{else}}svelte-kit sync && svelte-check --tsconfig ./tsconfig.json{{/if}}",
17
+ "typecheck": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}{{#if isSvelteOnlyApp}}svelte-check --tsconfig ./tsconfig.json{{else}}svelte-kit sync && svelte-check --tsconfig ./tsconfig.json{{/if}}",
18
18
  "lint": "eslint .",
19
- "test": "vitest run",
19
+ "test": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}vitest run",
20
20
  "test:watch": "vitest",
21
21
  "test:e2e": "playwright test",
22
- "clean": "rimraf .svelte-kit build .turbo"
22
+ "clean": "{{#if isSvelteOnlyApp}}rimraf dist build android ios .turbo src/lib/paraglide{{else}}rimraf .svelte-kit build .turbo{{/if}}"{{#if isCapacitorApp}},
23
+ "cap:add:android": "cap add android",
24
+ "cap:add:ios": "cap add ios",
25
+ "cap:sync": "pnpm build && cap sync",
26
+ "cap:sync:android": "pnpm build && cap sync android",
27
+ "cap:sync:ios": "pnpm build && cap sync ios",
28
+ "cap:android": "cap open android",
29
+ "cap:ios": "cap open ios",
30
+ "cap:run:android": "cap run android",
31
+ "cap:run:ios": "cap run ios",
32
+ "cap:build:android:debug": "pnpm build && cap sync android && cd android && gradlew.bat assembleDebug",
33
+ "cap:build:android:release": "pnpm build && cap sync android && cap build android --androidreleasetype APK",
34
+ "cap:build:ios": "pnpm build && cap sync ios && cap build ios"{{/if}}
23
35
  },
24
36
  "dependencies": {
25
- "@h-ai/core": "^0.1.0-alpha.20",
26
- "@h-ai/kit": "^0.1.0-alpha.20"{{#if hasUi}},
27
- "@h-ai/ui": "^0.1.0-alpha.20"{{/if}}{{#if features.db}},
28
- "@h-ai/reldb": "^0.1.0-alpha.20"{{/if}}{{#if features.cache}},
29
- "@h-ai/cache": "^0.1.0-alpha.20"{{/if}}{{#if features.iam}},
30
- "@h-ai/iam": "^0.1.0-alpha.20"{{/if}}{{#if features.crypto}},
31
- "@h-ai/crypto": "^0.1.0-alpha.20"{{/if}}{{#if features.storage}},
32
- "@h-ai/storage": "^0.1.0-alpha.20"{{/if}}{{#if features.ai}},
33
- "@h-ai/ai": "^0.1.0-alpha.20"{{/if}}{{#if features.audit}},
34
- "@h-ai/audit": "^0.1.0-alpha.20"{{/if}}{{#if features.reach}},
35
- "@h-ai/reach": "^0.1.0-alpha.20"{{/if}}{{#if features.payment}},
36
- "@h-ai/payment": "^0.1.0-alpha.20"{{/if}}{{#if features.vecdb}},
37
- "@h-ai/vecdb": "^0.1.0-alpha.20"{{/if}}{{#if features.datapipe}},
38
- "@h-ai/datapipe": "^0.1.0-alpha.20"{{/if}}{{#if features.scheduler}},
39
- "@h-ai/scheduler": "^0.1.0-alpha.20"{{/if}}{{#if features.deploy}},
40
- "@h-ai/deploy": "^0.1.0-alpha.20"{{/if}}{{#if features.api-client}},
41
- "@h-ai/api-client": "^0.1.0-alpha.20"{{/if}}{{#if features.capacitor}},
42
- "@h-ai/capacitor": "^0.1.0-alpha.20"{{/if}}{{#if isCapacitorApp}}{{#unless features.api-client}},
43
- "@h-ai/api-client": "^0.1.0-alpha.20"{{/unless}}{{#unless features.capacitor}},
44
- "@h-ai/capacitor": "^0.1.0-alpha.20"{{/unless}}{{/if}},
37
+ {{#if isSvelteOnlyApp}}
38
+ "@h-ai/api-client": "{{haiPackageSpecifier}}",
39
+ "@h-ai/capacitor": "{{haiPackageSpecifier}}",
40
+ "@h-ai/ui": "{{haiPackageSpecifier}}"
41
+ {{else}}
42
+ "@h-ai/core": "{{haiPackageSpecifier}}",
43
+ "@h-ai/kit": "{{haiPackageSpecifier}}"{{#if hasUi}},
44
+ "@h-ai/ui": "{{haiPackageSpecifier}}"{{/if}}{{#if features.db}},
45
+ "@h-ai/reldb": "{{haiPackageSpecifier}}"{{/if}}{{#if features.cache}},
46
+ "@h-ai/cache": "{{haiPackageSpecifier}}"{{/if}}{{#if features.iam}},
47
+ "@h-ai/iam": "{{haiPackageSpecifier}}"{{/if}}{{#if features.crypto}},
48
+ "@h-ai/crypto": "{{haiPackageSpecifier}}"{{/if}}{{#if features.storage}},
49
+ "@h-ai/storage": "{{haiPackageSpecifier}}"{{/if}}{{#if features.ai}},
50
+ "@h-ai/ai": "{{haiPackageSpecifier}}"{{/if}}{{#if features.audit}},
51
+ "@h-ai/audit": "{{haiPackageSpecifier}}"{{/if}}{{#if features.reach}},
52
+ "@h-ai/reach": "{{haiPackageSpecifier}}"{{/if}}{{#if features.payment}},
53
+ "@h-ai/payment": "{{haiPackageSpecifier}}"{{/if}}{{#if features.vecdb}},
54
+ "@h-ai/vecdb": "{{haiPackageSpecifier}}"{{/if}}{{#if features.datapipe}},
55
+ "@h-ai/datapipe": "{{haiPackageSpecifier}}"{{/if}}{{#if features.scheduler}},
56
+ "@h-ai/scheduler": "{{haiPackageSpecifier}}"{{/if}}{{#if features.deploy}},
57
+ "@h-ai/deploy": "{{haiPackageSpecifier}}"{{/if}}{{#if features.api-client}},
58
+ "@h-ai/api-client": "{{haiPackageSpecifier}}"{{/if}}{{#if features.capacitor}},
59
+ "@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/if}}{{#if isCapacitorApp}}{{#unless features.api-client}},
60
+ "@h-ai/api-client": "{{haiPackageSpecifier}}"{{/unless}}{{#unless features.capacitor}},
61
+ "@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/unless}}{{/if}},
45
62
  "zod": "^4.4.3"
63
+ {{/if}}
46
64
  },
47
65
  "devDependencies": {
48
66
  "@antfu/eslint-config": "^9.0.0",
49
67
  "@playwright/test": "^1.60.0",
50
68
  {{#if isCapacitorApp}}
51
- "@aparajita/capacitor-secure-storage": "^8.0.0",
69
+ "@aparajita/capacitor-secure-storage": "^8.0.0",
52
70
  "@capacitor/app": "^8.0.1",
53
- "@capacitor/camera": "^8.0.2",
71
+ "@capacitor/camera": "^8.0.1",
54
72
  "@capacitor/cli": "^8.3.4",
55
73
  "@capacitor/core": "^8.3.4",
56
74
  "@capacitor/android": "^8.3.4",
57
75
  "@capacitor/device": "^8.0.1",
76
+ "@capacitor/ios": "^8.3.4",
58
77
  "@capacitor/preferences": "^8.0.1",
59
- "@capacitor/push-notifications": "^8.0.2",
78
+ "@capacitor/push-notifications": "^8.0.1",
60
79
  "@capacitor/status-bar": "^8.0.1",
61
- "@sveltejs/adapter-static": "^3.0.10",
62
80
  {{else}}
63
81
  {{#if_eq appType 'api'}}
64
82
  "@sveltejs/adapter-node": "^5.5.4",
@@ -66,13 +84,14 @@
66
84
  "@sveltejs/adapter-auto": "^7.0.1",
67
85
  {{/if_eq}}
68
86
  {{/if}}
87
+ {{#unless isSvelteOnlyApp}}
69
88
  "@sveltejs/kit": "^2.61.1",
89
+ {{/unless}}
70
90
  "@sveltejs/vite-plugin-svelte": "^7.1.2",
71
91
  "@types/node": "^25.9.1",
72
92
  "eslint": "^10.4.0",
73
93
  "eslint-plugin-format": "^2.0.1",
74
94
  "eslint-plugin-svelte": "^3.17.1",
75
- "pino-pretty": "^13.1.3",
76
95
  "rimraf": "^6.1.3",
77
96
  "svelte": "^5.55.9",
78
97
  "svelte-check": "^4.4.8",
@@ -1,7 +1,34 @@
1
1
  import process from 'node:process'
2
2
  import { defineConfig } from '@playwright/test'
3
3
 
4
- const baseURL = process.env.BASE_URL || 'http://localhost:4173'
4
+ const DEFAULT_BASE_URL = 'http://localhost:4173'
5
+ const baseURL = process.env.BASE_URL || DEFAULT_BASE_URL
6
+ const previewHost = resolveHostFromBaseUrl(baseURL, 'localhost')
7
+ const previewPort = resolvePortFromBaseUrl(baseURL, '4173')
8
+
9
+ function resolveHostFromBaseUrl(urlString: string, fallbackHost: string): string {
10
+ try {
11
+ const url = new URL(urlString)
12
+ return url.hostname || fallbackHost
13
+ }
14
+ catch {
15
+ return fallbackHost
16
+ }
17
+ }
18
+
19
+ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string {
20
+ try {
21
+ const url = new URL(urlString)
22
+ if (url.port) {
23
+ return url.port
24
+ }
25
+
26
+ return url.protocol === 'https:' ? '443' : '80'
27
+ }
28
+ catch {
29
+ return fallbackPort
30
+ }
31
+ }
5
32
 
6
33
  /**
7
34
  * Playwright E2E 测试配置
@@ -23,8 +50,8 @@ export default defineConfig({
23
50
  },
24
51
 
25
52
  webServer: {
26
- command: 'pnpm build && pnpm preview --port 4173 --strictPort',
27
- env: { HAI_E2E: '1' },
53
+ command: `pnpm build && pnpm preview --host ${previewHost} --port ${previewPort} --strictPort`,
54
+ env: { HAI_E2E: '1', BASE_URL: baseURL },
28
55
  url: baseURL,
29
56
  reuseExistingServer: false,
30
57
  timeout: 180_000,
@@ -0,0 +1,14 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#if useCatalogProtocol}}
3
+ shellEmulator: true
4
+
5
+ trustPolicy: no-downgrade
6
+
7
+ packages:
8
+ - .
9
+
10
+ catalog:
11
+ {{#each haiCatalogPackages}}
12
+ '{{@key}}': {{this}}
13
+ {{/each}}
14
+ {{/if}}
@@ -1,3 +1,5 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#unless isSvelteOnlyApp}}
1
3
  /// <reference types="@sveltejs/kit" />
2
4
  {{#if hasUi}}
3
5
  import '@h-ai/ui/auto-import'
@@ -18,3 +20,4 @@ declare global {
18
20
  }
19
21
 
20
22
  export {}
23
+ {{/unless}}
@@ -1,3 +1,5 @@
1
+ {{! @skipIfEmpty }}
2
+ {{#unless isSvelteOnlyApp}}
1
3
  <!doctype html>
2
4
  {{#if hasI18n}}
3
5
  {{#if isCapacitorApp}}
@@ -24,3 +26,4 @@
24
26
  <div style="display: contents">%sveltekit.body%</div>
25
27
  </body>
26
28
  </html>
29
+ {{/unless}}
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Svelte 配置
3
3
  */
4
+ {{#unless isSvelteOnlyApp}}
4
5
  import process from 'node:process'
6
+ {{/unless}}
5
7
  {{#if hasUi}}
6
8
  import { autoImportHaiUi } from '@h-ai/ui/auto-import'
7
9
  {{/if}}
10
+ {{#unless isSvelteOnlyApp}}
8
11
  {{#if isCapacitorApp}}
9
12
  import adapter from '@sveltejs/adapter-static'
10
13
  {{else}}
@@ -14,8 +17,46 @@ import adapter from '@sveltejs/adapter-node'
14
17
  import adapter from '@sveltejs/adapter-auto'
15
18
  {{/if_eq}}
16
19
  {{/if}}
20
+ {{/unless}}
17
21
  import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
18
22
 
23
+ {{#if isSvelteOnlyApp}}
24
+ /** @type {import('@sveltejs/vite-plugin-svelte').SvelteConfig} */
25
+ const config = {
26
+ preprocess: [{{#if hasUi}}autoImportHaiUi(), {{/if}}vitePreprocess()],
27
+ compilerOptions: {
28
+ runes: true,
29
+ },
30
+ }
31
+ {{else}}
32
+ const DEFAULT_E2E_ORIGINS = ['http://localhost:4173', 'http://127.0.0.1:4173']
33
+
34
+ function resolveTrustedOrigins() {
35
+ if (process.env.HAI_E2E !== '1')
36
+ return []
37
+
38
+ const rawBaseUrl = process.env.BASE_URL ?? DEFAULT_E2E_ORIGINS[0]
39
+
40
+ try {
41
+ const url = new URL(rawBaseUrl)
42
+ const origins = new Set([url.origin])
43
+
44
+ if (url.hostname === 'localhost') {
45
+ url.hostname = '127.0.0.1'
46
+ origins.add(url.origin)
47
+ }
48
+ else if (url.hostname === '127.0.0.1') {
49
+ url.hostname = 'localhost'
50
+ origins.add(url.origin)
51
+ }
52
+
53
+ return [...origins]
54
+ }
55
+ catch {
56
+ return DEFAULT_E2E_ORIGINS
57
+ }
58
+ }
59
+
19
60
  /** @type {import('@sveltejs/kit').Config} */
20
61
  const config = {
21
62
  preprocess: [{{#if hasUi}}autoImportHaiUi(), {{/if}}vitePreprocess()],
@@ -33,9 +74,7 @@ const config = {
33
74
  adapter: adapter(),
34
75
  {{/if}}
35
76
  csrf: {
36
- trustedOrigins: process.env.HAI_E2E === '1'
37
- ? ['http://localhost:4173', 'http://127.0.0.1:4173']
38
- : [],
77
+ trustedOrigins: resolveTrustedOrigins(),
39
78
  },
40
79
  alias: {
41
80
  $components: './src/lib/components',
@@ -44,5 +83,6 @@ const config = {
44
83
  },
45
84
  },
46
85
  }
86
+ {{/if}}
47
87
 
48
88
  export default config
@@ -20,10 +20,17 @@ describe('generated scaffold', () => {
20
20
  expect(pkg.scripts['test:e2e']).toBeDefined()
21
21
  })
22
22
 
23
+ {{#if isSvelteOnlyApp}}
24
+ it('renders the Vite app shell entry', async () => {
25
+ const indexHtml = await readFile(join(rootDir, 'index.html'), 'utf-8')
26
+ expect(indexHtml).toContain('<div id="app"></div>')
27
+ })
28
+ {{else}}
23
29
  it('renders the app shell locale marker', async () => {
24
30
  const appHtml = await readFile(join(rootDir, 'src/app.html'), 'utf-8')
25
31
  expect(appHtml).toContain('lang="{{#if hasI18n}}{{#if isCapacitorApp}}{{defaultLocale}}{{else}}%lang%{{/if}}{{else}}{{defaultLocale}}{{/if}}"')
26
32
  })
33
+ {{/if}}
27
34
  {{#unless isCapacitorApp}}
28
35
 
29
36
  it('ships an application config loaded at startup', async () => {