@h-ai/cli 0.1.0-alpha.60 → 0.1.0-alpha.61

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 (35) hide show
  1. package/README.en.md +10 -0
  2. package/README.ja.md +10 -0
  3. package/README.md +10 -0
  4. package/dist/{chunk-P6X63D3I.js → chunk-WW2MIO6T.js} +377 -124
  5. package/dist/chunk-WW2MIO6T.js.map +1 -0
  6. package/dist/cli-config-templates-QRF6B5T4.js +5 -0
  7. package/dist/{cli-config-templates-EFABFSCY.js.map → cli-config-templates-QRF6B5T4.js.map} +1 -1
  8. package/dist/index.js +24 -5
  9. package/dist/index.js.map +1 -1
  10. package/package.json +7 -7
  11. package/templates/README.md +6 -0
  12. package/templates/apps/api/.gitignore.hbs +1 -0
  13. package/templates/apps/fullstack/.gitignore.hbs +1 -0
  14. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +8 -3
  15. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_ai.yml.hbs +1 -0
  16. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +1 -3
  17. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +1 -2
  18. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +7 -0
  19. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +1 -3
  20. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_deploy.yml.hbs +1 -0
  21. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +1 -67
  22. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_payment.yml.hbs +1 -0
  23. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +31 -0
  24. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_scheduler.yml.hbs +1 -0
  25. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +8 -0
  26. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +1 -3
  27. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_vecdb.yml.hbs +1 -0
  28. package/templates/apps/h5/src/routes/+layout.svelte.hbs +35 -23
  29. package/templates/base/.gitignore.hbs +1 -2
  30. package/templates/base/e2e/app.spec.ts.hbs +43 -1
  31. package/templates/base/package.json.hbs +1 -1
  32. package/templates/skills/hai-app-create/reference.md +10 -0
  33. package/templates/skills/hai-ui/reference.md +2 -0
  34. package/dist/chunk-P6X63D3I.js.map +0 -1
  35. package/dist/cli-config-templates-EFABFSCY.js +0 -5
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/cli",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.60",
4
+ "version": "0.1.0-alpha.61",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -30,7 +30,7 @@
30
30
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@h-ai/core": "0.1.0-alpha.60",
33
+ "@h-ai/core": "0.1.0-alpha.61",
34
34
  "cac": "^7.0.1",
35
35
  "chalk": "^6.0.1",
36
36
  "fast-wrap-ansi": "^0.2.2",
@@ -41,11 +41,11 @@
41
41
  "yaml": "^2.9.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@h-ai/cache": "0.1.0-alpha.60",
45
- "@h-ai/deploy": "0.1.0-alpha.60",
46
- "@h-ai/reach": "0.1.0-alpha.60",
47
- "@h-ai/reldb": "0.1.0-alpha.60",
48
- "@h-ai/storage": "0.1.0-alpha.60",
44
+ "@h-ai/cache": "0.1.0-alpha.61",
45
+ "@h-ai/deploy": "0.1.0-alpha.61",
46
+ "@h-ai/reach": "0.1.0-alpha.61",
47
+ "@h-ai/reldb": "0.1.0-alpha.61",
48
+ "@h-ai/storage": "0.1.0-alpha.61",
49
49
  "@types/fs-extra": "^11.0.4",
50
50
  "@types/node": "^26.6.5",
51
51
  "@types/prompts": "^2.4.9",
@@ -104,8 +104,14 @@ templates/
104
104
 
105
105
  `appType` 支持 `admin` / `website` / `h5` / `api` / `mobile-app` / `fullstack`。
106
106
 
107
+ H5 业务页面复用 `@h-ai/ui` 的 `BottomNav` 和移动端壳样式,桌面预览保持居中限宽,认证路由独立显示。
108
+
107
109
  `fullstack.*` 包含包名、前端选择、依赖版本、`contractExportName` 和原生壳 `nativeAppIdSegment`,用于渲染 contract / serv / 多端 UI / Capacitor / Tauri 模板。
108
110
 
111
+ `moduleConfigs` 保留创建时指定的模块值。Fullstack 的模块 YAML 用 `{{{moduleConfig 'db' true}}}` 等 helper 复用 `cli-config-templates.ts`;可选模块传 `features.ai` 等开关,未选择时整份注释。创建流程保留已渲染的配置,不再用精简内容覆盖;core、serv、Reach 保留应用专用默认值及完整的可选字段。新增 Schema 字段同步此生成器和相应模板。
112
+
113
+ 所有根 `.gitignore`(base / api / fullstack)保留 `**/data/`,包括 `.gitkeep` 在内的运行数据不跟踪。Fullstack 服务端 config 始终含 core、serv、db、cache、iam、storage、audit、reach、ai、vecdb、payment、scheduler、deploy 共 13 份 YAML;不为无配置入口的 crypto/datapipe 生成 YAML。未启用块的后续接入步骤见 [CLI 配置说明](../README.md#应用配置与显示)。
114
+
109
115
  ### 常用条件写法
110
116
 
111
117
  ```handlebars
@@ -1,4 +1,5 @@
1
1
  node_modules
2
+ **/data/
2
3
  .turbo
3
4
  dist
4
5
  build
@@ -1,4 +1,5 @@
1
1
  node_modules
2
+ **/data/
2
3
  .turbo
3
4
  dist
4
5
  build
@@ -6,12 +6,13 @@
6
6
 
7
7
  - `config/_core.yml`:应用元信息(名称 / 版本 / 环境 / 语言),启动时由 `core.config` 加载。
8
8
  - `config/_serv.yml`:服务端运行时设置(CORS 来源等)。
9
- - `config/_db.yml` / `_cache.yml` / `_iam.yml` / `_storage.yml` / `_audit.yml`:mini 管理所需模块配置,初始化前使用模块公开 Schema 验证。
10
- - `src/index.ts`:进程入口,先 `core.init({ configDir: './config' })`,再初始化 reldb → cache → crypto → storage → iam → audit;启动失败及退出反序释放。
9
+ - `config/_db.yml` / `_cache.yml` / `_iam.yml` / `_storage.yml` / `_audit.yml` / `_reach.yml`:管理及开发通知配置,初始化前使用模块公开 Schema 验证。Reach 默认控制台 Provider,生产按注释替换为 SMTP/短信/API。
10
+ - `config/_ai.yml` / `_vecdb.yml` / `_payment.yml` / `_scheduler.yml` / `_deploy.yml`:保留未启用模块的完整注释示例;配置文件存在不代表模块已接入。先在本 workspace 安装对应依赖,再在 `src/server/runtime.ts` 按公开 Schema 校验、初始化并反序关闭,接入实际业务后按需取消注释。整份注释先去掉一层外层注释,可选字段继续按需开启。
11
+ - `src/index.ts`:进程入口,先 `core.init({ configDir: './config' })`,再初始化 reldb → cache → crypto → storage → reach(有配置时)→ iam → audit;启动失败及退出反序释放。
11
12
  - `src/app-server.ts`:服务装配入口,挂载健康检查、OpenAPI、docs 与 procedures;应用名称 / 版本与 CORS 来源均取自上述配置。
12
13
  - `src/server/procedures/app-procedures.ts`:app 领域 procedures,对应 contract 中的 `app.info` / `app.echo` 路由。
13
14
  - `src/server/procedures/admin-procedures.ts`:认证、管理员用户/角色/审计管理和个人设置,使用 `.auth()` / `.role('admin')` 守卫。每个请求读取用户最新启用状态、角色和权限。
14
- - 当前用户与刷新 procedures 直接选用 `@h-ai/serv/features/iam` 的默认实现;仅覆写登录、退出、修改密码以追加业务审计。未选的注册/OTP/通用管理接口不挂载,HTTP App 类型直接复用公开 `ServHttpApp`。
15
+ - 标准认证 procedures 复用 `@h-ai/serv/features/iam`,注册、登录方式、重置与协议由 `_iam.yml` 控制;登录、退出、修改密码追加业务审计。HTTP App 类型复用公开 `ServHttpApp`。
15
16
  - `src/server/admin-service.ts`:角色分配、头像上传和审计编排,全部底层操作复用 hai 模块。固定 admin/user 两种角色;锁内重新鉴权,禁止管理员自删、自禁用、自降级,并补偿用户更新失败后的角色变更。
16
17
  - `tests/app-server.test.ts`:真实加密 HTTP 测试,覆盖 Cookie/受信原生刷新、RBAC、旧会话权限、并发管理操作、补偿、头像安全、偏好、审计及会话失效。
17
18
 
@@ -23,6 +24,10 @@ CLI 已生成根目录 `.env`,`HAI_ADMIN_DEFAULT_PASSWORD` 为每个项目独
23
24
 
24
25
  ## 开发
25
26
 
27
+ 所有配置字段以模块 Schema 为准;未使用字段和替代 Provider 用注释保留。叶子环境变量只覆盖已有 YAML 路径,先取消相应键的注释,或用 `HAI_DB` / `HAI_CACHE` 等整体对象覆盖;`connectTimeout` 对应 `HAI_CACHE_CONNECTTIMEOUT`。凭据示例在根 `.env.example`,实际密钥保存在忽略的 `.env` 或部署环境。`.gitignore` 的 `**/data/` 忽略所有运行数据及 `.gitkeep`;运行时会创建目录。`crypto.init()` 无配置参数,datapipe 为纯函数,不提供虚构配置。
28
+
29
+ 自动生成的 `.env` 只启用初始管理员密码和 `PUBLIC_API_BASE`;模块环境覆盖保留注释,以 YAML 为默认值来源。有明确环境覆盖时编辑 YAML 要同步核对。
30
+
26
31
  - `pnpm --filter {{fullstack.servPackageName}} dev`
27
32
  - `pnpm --filter {{fullstack.servPackageName}} test`
28
33
 
@@ -0,0 +1 @@
1
+ {{{moduleConfig 'ai' features.ai}}}
@@ -1,3 +1 @@
1
- userTable: hai_iam_users
2
- userIdColumn: id
3
- userNameColumn: username
1
+ {{{moduleConfig 'audit' true}}}
@@ -1,2 +1 @@
1
- # 多实例部署请使用共享 Redis,保障会话、管理员变更锁一致。
2
- type: memory
1
+ {{{moduleConfig 'cache' true}}}
@@ -15,7 +15,14 @@ defaultLocale: {{fullstack.i18n.baseLocale}}
15
15
  logging:
16
16
  level: info
17
17
  format: json
18
+ # context:
19
+ # service: api
18
20
  redact:
19
21
  - password
20
22
  - token
21
23
  - secret
24
+
25
+ # 短 ID 生成配置;未设置时采用框架默认值。
26
+ # id:
27
+ # prefix: app_
28
+ # length: 21
@@ -1,3 +1 @@
1
- # 默认单实例 SQLite;HAI_DB 可整体替换为 PostgreSQL/MySQL 配置。
2
- type: sqlite
3
- database: ./data/app.db
1
+ {{{moduleConfig 'db' true}}}
@@ -0,0 +1 @@
1
+ {{{moduleConfig 'deploy' features.deploy}}}
@@ -1,67 +1 @@
1
- login:
2
- password: true
3
- otp: false
4
- ldap: false
5
- apikey: false
6
- password:
7
- minLength: 8
8
- maxLength: 128
9
- requireUppercase: false
10
- requireLowercase: false
11
- requireNumber: false
12
- requireSpecialChar: false
13
- session:
14
- maxAge: 86400
15
- sliding: true
16
- singleDevice: false
17
- refreshTokenMaxAge: 604800
18
- rbac:
19
- defaultRole: user
20
- # 仅由应用创建 admin/user,避免生成无业务用途的默认角色与权限。
21
- seedDefaultData: false
22
-
23
- # 注册与登录安全
24
- register:
25
- enabled: true
26
- defaultEnabled: true
27
- security:
28
- maxLoginAttempts: 5
29
- lockoutDuration: 900
30
-
31
- # LDAP 可选:安装 ldapts 后开启 login.ldap,并声明 ldap 连接配置或注入工厂。
32
- # OTP 需启用 login.otp,并选择 Reach 标准模板或自定义发送回调。
33
- otp:
34
- email: true
35
- sms: true
36
- length: 6
37
- expiresIn: 300
38
- maxAttempts: 3
39
- resendInterval: 60
40
- passwordReset:
41
- enabled: true
42
- mode: email # phone 使用 password_reset_sms 模板
43
- tokenExpiresIn: 3600
44
- maxAttempts: 3
45
- # resetUrl: https://example.com/auth/reset-password
46
- agreements:
47
- showOnRegister: true
48
- showOnLogin: false
49
- # Markdown 正文由公开配置传给所有客户端,在弹窗内安全渲染。
50
- userAgreementMarkdown: |
51
- # 用户协议示例
52
-
53
- 请妥善保管账户凭据,并按应用提供的功能使用服务。
54
- privacyPolicyMarkdown: |
55
- # 隐私政策示例
56
-
57
- 注册资料用于账户管理,会话信息用于身份认证。
58
- # userAgreementUrl: https://example.com/terms
59
- # privacyPolicyUrl: https://example.com/privacy
60
- ui:
61
- variant: card
62
- # title: Account center
63
- # subtitle: Welcome back
64
- # brandTitle: Your application
65
- # brandText: Sign in securely
66
- # logoUrl: /logo.svg
67
- # illustrationUrl: /auth-illustration.svg
1
+ {{{moduleConfig 'iam' true}}}
@@ -0,0 +1 @@
1
+ {{{moduleConfig 'payment' features.payment}}}
@@ -4,6 +4,30 @@ providers:
4
4
  type: console
5
5
  - name: sms
6
6
  type: console
7
+ # SMTP 替换 email 控制台 Provider;同名 Provider 只保留一个。
8
+ # - name: email
9
+ # type: smtp
10
+ # host: ${HAI_REACH_SMTP_HOST:smtp.example.com}
11
+ # port: ${HAI_REACH_SMTP_PORT:465}
12
+ # secure: true
13
+ # user: ${HAI_REACH_SMTP_USER:}
14
+ # pass: ${HAI_REACH_SMTP_PASS:}
15
+ # from: ${HAI_REACH_SMTP_FROM:noreply@example.com}
16
+ # 阿里云短信替换 sms 控制台 Provider。
17
+ # - name: sms
18
+ # type: aliyun-sms
19
+ # accessKeyId: ${HAI_REACH_SMS_ACCESS_KEY}
20
+ # accessKeySecret: ${HAI_REACH_SMS_SECRET_KEY}
21
+ # signName: MyApp
22
+ # endpoint: dysmsapi.aliyuncs.com
23
+ # 自有发送服务:按需将 template.provider 改为 custom。
24
+ # - name: custom
25
+ # type: api
26
+ # url: https://notify.example.com/send
27
+ # method: POST # POST | PUT
28
+ # headers:
29
+ # Authorization: 'Bearer ${HAI_REACH_API_TOKEN}'
30
+ # timeout: 10000
7
31
  templates:
8
32
  - name: otp_email
9
33
  provider: email
@@ -19,3 +43,10 @@ templates:
19
43
  - name: password_reset_sms
20
44
  provider: sms
21
45
  body: 'Reset your password: {resetUrl}. Expires at {expiresAt}.'
46
+
47
+ # 免打扰可选;验证码应避免 delay,以免延迟到失效后投递。
48
+ # dnd:
49
+ # enabled: false
50
+ # strategy: discard # discard | delay
51
+ # start: '22:00'
52
+ # end: '08:00'
@@ -0,0 +1 @@
1
+ {{{moduleConfig 'scheduler' features.scheduler}}}
@@ -15,10 +15,17 @@ http:
15
15
  path: /openapi.json
16
16
  docs:
17
17
  path: /docs
18
+ # requireAuth: true
18
19
  health:
19
20
  path: /health
20
21
  readyPath: /ready
21
22
  rpc: false
23
+ # 内部 RPC 可选;启用时用对象替换 false,gateway-only 必须配置网关密钥。
24
+ # rpc:
25
+ # prefix: /api/rpc
26
+ # access: gateway-only # loopback | private-network | gateway-only
27
+ # gatewayHeader: X-Gateway-Key
28
+ # gatewaySecret: ${HAI_SERV_HTTP_RPC_GATEWAYSECRET}
22
29
 
23
30
  cors:
24
31
  origin: ${HAI_CORS_ORIGIN:*}
@@ -40,3 +47,4 @@ cors:
40
47
  transport:
41
48
  keyExchangePath: /_hai/key-exchange
42
49
  maxClients: 10000
50
+ # excludePaths: [/health, /ready]
@@ -1,3 +1 @@
1
- # HAI_STORAGE 可整体替换为 S3/MinIO/RustFS 配置。
2
- type: local
3
- root: ./data/uploads
1
+ {{{moduleConfig 'storage' true}}}
@@ -0,0 +1 @@
1
+ {{{moduleConfig 'vecdb' features.vecdb}}}
@@ -3,16 +3,20 @@
3
3
  /**
4
4
  * H5 应用根布局
5
5
  */
6
+ import type { BottomNavItem } from '@h-ai/ui'
6
7
  import type { Snippet } from 'svelte'
7
8
  {{#if hasI18n}}
8
9
  import { browser } from '$app/environment'
9
10
  {{/if}}
11
+ import { goto } from '$app/navigation'
10
12
  import { resolve } from '$app/paths'
11
13
  import { page } from '$app/state'
12
14
  {{#if hasI18n}}
13
15
  import * as m from '$lib/paraglide/messages'
14
16
  import { getLocale } from '$lib/paraglide/runtime.js'
15
- import { setGlobalLocale } from '@h-ai/ui'
17
+ import { BottomNav, setGlobalLocale } from '@h-ai/ui'
18
+ {{else}}
19
+ import { BottomNav } from '@h-ai/ui'
16
20
  {{/if}}
17
21
  import '../app.css'
18
22
 
@@ -22,17 +26,27 @@
22
26
 
23
27
  const { children }: Props = $props()
24
28
 
25
- const navItems = [
29
+ const navItems: (BottomNavItem & { path: '/' | '/discover' | '/profile' })[] = [
26
30
  {{#if hasI18n}}
27
- { path: '/', label: m.nav_home(), icon: 'icon-[tabler--home]' },
28
- { path: '/discover', label: m.nav_discover(), icon: 'icon-[tabler--compass]' },
29
- { path: '/profile', label: m.nav_profile(), icon: 'icon-[tabler--user]' },
31
+ { id: 'home', path: '/', label: m.nav_home(), iconClass: 'icon-[tabler--home]' },
32
+ { id: 'discover', path: '/discover', label: m.nav_discover(), iconClass: 'icon-[tabler--compass]' },
33
+ { id: 'profile', path: '/profile', label: m.nav_profile(), iconClass: 'icon-[tabler--user]' },
30
34
  {{else}}
31
- { path: '/', label: '首页', icon: 'icon-[tabler--home]' },
32
- { path: '/discover', label: '发现', icon: 'icon-[tabler--compass]' },
33
- { path: '/profile', label: '我的', icon: 'icon-[tabler--user]' },
35
+ { id: 'home', path: '/', label: '首页', iconClass: 'icon-[tabler--home]' },
36
+ { id: 'discover', path: '/discover', label: '发现', iconClass: 'icon-[tabler--compass]' },
37
+ { id: 'profile', path: '/profile', label: '我的', iconClass: 'icon-[tabler--user]' },
34
38
  {{/if}}
35
- ] as const
39
+ ]
40
+
41
+ const activeNav = $derived(navItems.find(item => item.path === page.url.pathname)?.id)
42
+ // 认证页面独立显示;业务页面复用框架导航和移动端安全区域样式。
43
+ const isAuthPage = $derived(page.url.pathname.startsWith('/auth'))
44
+
45
+ function handleNavigation(id: string) {
46
+ const item = navItems.find(item => item.id === id)
47
+ if (item)
48
+ void goto(resolve(item.path, {}))
49
+ }
36
50
  {{#if hasI18n}}
37
51
 
38
52
  if (browser) {
@@ -46,17 +60,15 @@
46
60
  <title>{page.data.appConfig.name}</title>
47
61
  </svelte:head>
48
62
 
49
- <div class='min-h-screen flex flex-col bg-base-200'>
50
- <main class='flex-1 pb-16'>
51
- {@render children()}
52
- </main>
53
-
54
- <nav class='btm-nav btm-nav-sm'>
55
- {#each navItems as item (item.path)}
56
- <a href={resolve(item.path, {})} class:active={page.url.pathname === item.path} class:text-primary={page.url.pathname === item.path}>
57
- <span class='{item.icon} size-5'></span>
58
- <span class='btm-nav-label'>{item.label}</span>
59
- </a>
60
- {/each}
61
- </nav>
62
- </div>
63
+ {#if isAuthPage}
64
+ {@render children()}
65
+ {:else}
66
+ <div class='hai-mobile-viewport'>
67
+ <div class='hai-mobile-shell max-w-lg'>
68
+ <main class='hai-mobile-main'>
69
+ {@render children()}
70
+ </main>
71
+ <BottomNav items={navItems} active={activeNav} onchange={handleNavigation} centered maxWidth='lg' safeArea />
72
+ </div>
73
+ </div>
74
+ {/if}
@@ -13,8 +13,7 @@ ios
13
13
  .DS_Store
14
14
  test-results/
15
15
  playwright-report/
16
- data/*
17
- !**/data/.gitkeep
16
+ **/data/
18
17
 
19
18
  # hai deploy 产物(compose / .env.runtime / 镜像 tar / sidecar 凭据)
20
19
  .hai/
@@ -7,18 +7,38 @@ import { expect, test } from '@playwright/test'
7
7
 
8
8
  /** 首页从真实浏览器页面发起,覆盖基础渲染链路。 */
9
9
  test('home page renders from browser', async ({ page }) => {
10
+ {{#if_eq appType 'h5'}}
11
+ await page.setViewportSize({ width: 390, height: 844 })
12
+ {{/if_eq}}
10
13
  const pageErrors: string[] = []
11
14
  page.on('pageerror', error => pageErrors.push(error.message))
12
15
  await page.goto('/')
13
16
  await expect(page).toHaveTitle(process.env.HAI_CORE_NAME ?? '{{projectName}}')
14
17
  await expect(pageErrors).toEqual([])
15
18
  await expect(page.locator('main, section').first()).toBeVisible()
19
+ {{#if_eq appType 'h5'}}
20
+ // 在手机宽度验证导航真正切换路由,避免样式失效但首页渲染测试仍通过。
21
+ const nav = page.locator('nav')
22
+ await nav.getByRole('button', { name: /发现|Discover/i, exact: true }).click()
23
+ await expect(page).toHaveURL(/\/discover$/)
24
+ await nav.getByRole('button', { name: /首页|Home/i, exact: true }).click()
25
+ await expect(page).toHaveURL(/\/$/)
26
+ expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
27
+ {{#if features.iam}}
28
+ const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
29
+ await mkdir(evidence, { recursive: true })
30
+ await page.screenshot({ path: path.join(evidence, '{{projectName}}-home.png'), fullPage: true })
31
+ {{/if}}
32
+ {{/if_eq}}
16
33
  })
17
34
  {{#if features.iam}}
18
35
 
19
36
  /** 实际注册和密码登录;截图只保存空白表单。 */
20
37
  test('configured IAM registration and password login', async ({ page, context }) => {
21
38
  test.setTimeout(90_000)
39
+ {{#if_eq appType 'h5'}}
40
+ await page.setViewportSize({ width: 390, height: 844 })
41
+ {{/if_eq}}
22
42
  const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
23
43
  await mkdir(evidence, { recursive: true })
24
44
  const username = `scaf${Date.now()}`
@@ -93,7 +113,29 @@ test('configured IAM registration and password login', async ({ page, context })
93
113
  expect(Boolean(reset?.token)).toBe(true)
94
114
  await page.getByRole('button', { name: /已有重置令牌|I have a reset token/i }).click()
95
115
  await page.screenshot({ path: path.join(evidence, '{{projectName}}-reset.png'), fullPage: true })
96
- await page.goto(`{{#if isCapacitorApp}}/{{else}}/auth/reset-password{{/if}}?token=${encodeURIComponent(reset?.token ?? '')}`)
116
+ const resetUrl = `{{#if isCapacitorApp}}/{{else}}/auth/reset-password{{/if}}?token=${encodeURIComponent(reset?.token ?? '')}`
117
+ {{#if_eq appType 'h5'}}
118
+ // 延迟客户端脚本,确定性覆盖 SSR 页面已显示、事件绑定尚未完成的窗口。
119
+ let resumeClient = () => {}
120
+ const clientBoot = new Promise<void>((resolve) => {
121
+ resumeClient = resolve
122
+ })
123
+ await page.route('**/*.js', async (route) => {
124
+ await clientBoot
125
+ await route.continue()
126
+ })
127
+ try {
128
+ await page.goto(resetUrl, { waitUntil: 'commit' })
129
+ await expect(page.locator('input[type="password"]').nth(0)).toBeDisabled()
130
+ }
131
+ finally {
132
+ resumeClient()
133
+ // 等待正在恢复的路由完成,避免提前移除拦截导致 continue 被重复处理。
134
+ await page.unrouteAll({ behavior: 'wait' })
135
+ }
136
+ {{else}}
137
+ await page.goto(resetUrl)
138
+ {{/if_eq}}
97
139
  await expect(page.locator('input[type="password"]').nth(0)).toBeVisible()
98
140
  await page.locator('input[type="password"]').nth(0).fill('ScaffoldReset456')
99
141
  await page.locator('input[type="password"]').nth(1).fill('ScaffoldReset456')
@@ -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.60",
4
+ "version": "0.1.0-alpha.61",
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}}",
@@ -312,6 +312,16 @@ interface CreateArticleInput {
312
312
 
313
313
  ## 初始化顺序
314
314
 
315
+ CLI 项目统一忽略 `**/data/`,含根目录、workspace 子目录及 `.gitkeep`;数据库/上传文件不入 Git。Fullstack 的服务端 config 始终保留 core、serv、db、cache、iam、storage、audit、reach、ai、vecdb、payment、scheduler、deploy 共 13 份 YAML,未启用模块整份注释,可选字段及替代 Provider 按块注释,`.env.example` 也保留未启用模块的注释凭据。
316
+
317
+ H5 认证路由独立显示;业务页面使用 `@h-ai/ui` 的 `BottomNav` 与移动端壳样式,导航启用 centered/maxWidth/safeArea,与页面同宽。
318
+
319
+ 扩展这些配置前先核对当前安装版本及模块公开 Schema。在服务端 workspace 安装需要的依赖,在 `src/server/runtime.ts` 按依赖顺序校验/初始化、失败清理并反序关闭,再接入 contract/procedure。整份注释文件先去掉一层外层注释,可选块只开启需要的字段;切换 Provider 时替换旧连接块并保持名称唯一。配置文件存在不代表模块已启用。crypto.init() 无配置参数,datapipe 无生命周期,不增加虚构的 YAML。
320
+
321
+ 环境变量叶子覆盖只遍历 YAML 已有路径,注释键须先取消注释,或用 HAI_DB/HAI_CACHE 等整体对象覆盖。camelCase 不拆词,例如 connectTimeout → HAI_CACHE_CONNECTTIMEOUT;密钥仅写入被忽略的环境文件或部署环境。语言列表在前端/shared 的 project.inlang/settings.json 中同步,core 只配置 defaultLocale。
322
+
323
+ Fullstack 自动生成的 .env 默认只启用管理员初始密码和 PUBLIC_API_BASE,其余环境覆盖保留注释,YAML 修改可直接生效。显式开启环境覆盖后,修改 YAML 时须核对该覆盖是否仍适用。
324
+
315
325
  复用当前应用服务端启动/关闭入口;SvelteKit 通常为 `$lib/server/init.ts`,API/fullstack 使用已有 service/serv 初始化文件。下列是启用 IAM 时的常见依赖关系,不要求无关应用安装这些模块:
316
326
 
317
327
  ```
@@ -595,6 +595,8 @@ agreements:
595
595
 
596
596
  AuthPanel 直接接收 IAM 白名单配置和 actions,内置登录(密码/LDAP/邮件/短信)、注册、找回、重置及协议展示。使用 `config.ui` 配置卡片/分栏、标题、品牌、Logo 和配图。`LoginForm` 提供 methods、onsendotp、otpLength、resendInterval;密码表单接受 passwordPolicy。组件只渲染与交互,网络和 Token 存储通过 actions 注入。认证场景按公开 IAM 错误码显示当前 UI 语言的提示,应用自定义错误保留原文。
597
597
 
598
+ AuthPanel 在 SSR 阶段禁用原生表单控件,客户端挂载并绑定事件后自动开放输入,避免接管时丢失早到的输入。应用无需额外的就绪适配;E2E 等待控件可编辑,并覆盖客户端脚本延迟加载的场景。
599
+
598
600
 
599
601
  协议在 Modal 内显示并保留表单输入;使用 URL 时,协议页面须允许 iframe 嵌入,组件禁用其脚本,另提供新窗口查看入口。登录/注册表单使用 `header/extraFields/footer` snippet,AuthPanel 透传 `loginHeader/loginFields/loginFooter` 和 `registerHeader/registerFields/registerFooter`,底部保留内置导航。自定义字段由应用绑定状态和提交,不会隐式写入 IAM 用户模型。
600
602
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/cli-config-templates.ts"],"names":[],"mappings":"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","file":"chunk-P6X63D3I.js","sourcesContent":["/**\n * @h-ai/cli — 配置文件模板\n *\n * 为各模块生成默认配置文件内容。\n * @module cli-config-templates\n */\n\nimport type { AiModuleConfig, CacheModuleConfig, CoreModuleConfig, DbModuleConfig, FeatureId, IamModuleConfig, ModuleConfigs, StorageModuleConfig } from '../cli-types.js'\nimport { stringify } from 'yaml'\n\n/**\n * 生成模块配置文件内容\n *\n * @param moduleKey - 模块标识(core/serv/db/cache/iam/storage/ai)\n * @param configs - 用户自定义配置值\n * @returns YAML 格式的配置内容\n */\nexport function generateConfigFile(moduleKey: string, configs?: ModuleConfigs): string {\n switch (moduleKey) {\n case 'core':\n return generateCoreConfig(configs?.core)\n case 'serv':\n return generateServConfig()\n case 'db':\n return generateDbConfig(configs?.db)\n case 'cache':\n return generateCacheConfig(configs?.cache)\n case 'iam':\n return generateIamConfig(configs?.iam)\n case 'storage':\n return generateStorageConfig(configs?.storage)\n case 'ai':\n return generateAiConfig(configs?.ai)\n case 'deploy':\n return generateDeployConfig()\n case 'vecdb':\n return generateVecdbConfig()\n case 'reach':\n return generateReachConfig()\n case 'scheduler':\n return generateSchedulerConfig()\n case 'audit':\n return generateAuditConfig()\n case 'payment':\n return generatePaymentConfig()\n default:\n return `# ${moduleKey} 配置\\n`\n }\n}\n\nfunction generateCoreConfig(cfg?: CoreModuleConfig): string {\n const name = cfg?.name ?? 'my-app'\n const locale = cfg?.defaultLocale ?? 'zh-CN'\n const supportedLocales = locale === 'en-US'\n ? ` - en-US\\n - zh-CN`\n : ` - zh-CN\\n - en-US`\n\n return `# =============================================================================\n# 应用配置\n# =============================================================================\n\n${stringify({ name }).trim()}\nversion: 0.1.0\nenv: development\ndebug: false\n\nlogging:\n level: info\n format: json\n redact:\n - password\n - token\n - secret\n\ndefaultLocale: ${locale}\nsupportedLocales:\n${supportedLocales}\n`\n}\n\nfunction generateServConfig(): string {\n return `# =============================================================================\n# Service / HTTP 入口配置\n# =============================================================================\n\nhttp:\n apiPrefix: /api/v1\n openapi:\n path: /openapi.json\n docs:\n path: /docs\n health:\n path: /health\n readyPath: /ready\n rpc: false\n\n# 开发期默认关闭 transport;启用 @h-ai/crypto 后可按需改为对象配置\ntransport: false\n`\n}\n\nfunction generateDbConfig(cfg?: DbModuleConfig): string {\n const dbType = cfg?.type ?? 'sqlite'\n const database = cfg?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? (dbType === 'postgresql' ? 5432 : 3306)\n\n if (dbType === 'sqlite') {\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: sqlite\n\n# SQLite 数据库路径\ndatabase: ${database}\n\n# PostgreSQL/MySQL 配置(可选)\n# host: localhost\n# port: 5432\n# user: postgres\n# password: ''\n`\n }\n\n const user = dbType === 'postgresql' ? 'postgres' : 'root'\n\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: ${dbType}\n\n# 数据库名称\ndatabase: ${database}\n\n# 连接配置\nhost: ${host}\nport: ${port}\nuser: ${user}\npassword: ''\n`\n}\n\nfunction generateCacheConfig(cfg?: CacheModuleConfig): string {\n const cacheType = cfg?.type ?? 'memory'\n\n if (cacheType === 'memory') {\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: memory\n\n# Redis 配置(当 type 为 redis 时使用)\n# url: redis://localhost:6379/0\n# host: localhost\n# port: 6379\n# password: ''\n# keyPrefix: 'hai:'\n`\n }\n\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? 6379\n\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: redis\n\n# Redis 配置\nhost: ${host}\nport: ${port}\npassword: ''\nkeyPrefix: 'hai:'\n`\n}\n\nfunction generateIamConfig(cfg?: IamModuleConfig): string {\n const loginPassword = cfg?.loginPassword ?? true\n const loginOtp = cfg?.loginOtp ?? false\n\n return `# =============================================================================\n# IAM 配置\n# =============================================================================\n\n# 登录配置\nlogin:\n password: ${loginPassword}\n otp: ${loginOtp}\n ldap: false\n\n# 注册与登录安全\nregister:\n enabled: true\n defaultEnabled: true\nsecurity:\n maxLoginAttempts: 5\n lockoutDuration: 900\n\n# LDAP 可选:安装 ldapts 后开启 login.ldap,并声明 ldap 连接配置;也可注入工厂。\n# OTP 需启用 login.otp,并选择 Reach 标准模板或自定义发送回调。\notp:\n email: true\n sms: true\n length: 6\n expiresIn: 300\n maxAttempts: 3\n resendInterval: 60\npasswordReset:\n enabled: true\n mode: email # phone 使用 password_reset_sms 模板\n tokenExpiresIn: 3600\n maxAttempts: 3\n # resetUrl: https://example.com/auth/reset-password\nagreements:\n showOnRegister: true\n showOnLogin: false\n # 用 | 配置 Markdown 正文;同主题同时提供 URL 时优先显示正文。\n userAgreementMarkdown: |\n # 用户协议示例\n\n 请妥善保管账户凭据,并按应用提供的功能使用服务。\n privacyPolicyMarkdown: |\n # 隐私政策示例\n\n 注册资料用于账户管理,会话信息用于身份认证。\n # userAgreementUrl: https://example.com/terms\n # privacyPolicyUrl: https://example.com/privacy\nui:\n variant: card\n # title: Account center\n # subtitle: Welcome back\n # brandTitle: Your application\n # brandText: Sign in securely\n # logoUrl: /logo.svg\n # illustrationUrl: /auth-illustration.svg\n\n# 密码策略\npassword:\n minLength: 8\n maxLength: 128\n requireUppercase: false\n requireLowercase: false\n requireNumber: false\n requireSpecialChar: false\n\n# 会话配置\nsession:\n maxAge: 86400 # 会话超时(秒),默认 24 小时\n sliding: true # 滑动窗口(每次请求刷新过期时间)\n singleDevice: false # 单设备登录\n refreshTokenMaxAge: 604800 # refreshToken 过期(秒),默认 7 天\n\n# RBAC 配置\nrbac:\n defaultRole: user\n\n# 是否自动创建默认数据\nseedDefaultData: true\n`\n}\n\nfunction generateStorageConfig(cfg?: StorageModuleConfig): string {\n if ((cfg?.type ?? 'local') === 'local') {\n return `# 存储配置;HAI_STORAGE 可整体替换为 S3 配置。\ntype: local\nroot: ${cfg?.localPath ?? './data/uploads'}\n`\n }\n return `# S3 存储配置\ntype: s3\nbucket: ''\nregion: us-east-1\naccessKeyId: ''\nsecretAccessKey: ''\nendpoint: https://s3.amazonaws.com\nforcePathStyle: false\n`\n}\n\nfunction generateAiConfig(cfg?: AiModuleConfig): string {\n const model = cfg?.model ?? 'gpt-4o-mini'\n\n // AIConfigSchema 结构:llm(必填)+ 可选子模块\n // LLMConfigSchema 支持顶层 apiKey/model,以及多模型 models 数组\n return `# =============================================================================\n# AI 配置\n# =============================================================================\n\n# LLM 配置(必填)\nllm:\n # API Key(约定环境变量 HAI_AI_LLM_APIKEY 优先)\n apiKey: ''\n # Base URL(约定环境变量 HAI_AI_LLM_BASEURL 优先)\n # baseUrl: https://api.openai.com/v1\n # 默认模型\n model: ${model}\n # 全局最大 Token 数(默认 4096)\n # maxTokens: 4096\n # 采样温度(0-2,默认 0.7)\n # temperature: 0.7\n # 请求超时(毫秒,默认 60000)\n # timeout: 60000\n\n # 多模型配置(可选,用于场景路由)\n # models:\n # - id: fast\n # model: gpt-4o-mini\n # - id: smart\n # model: gpt-4o\n # apiKey: ''\n\n # 场景模型映射(可选)\n # scenarios:\n # default: fast\n # analysis: smart\n`\n}\n\n/**\n * 根据选中的功能模块生成 .env.example 内容\n *\n * @param features - 选中的功能模块列表\n * @param configs - 用户自定义配置值\n * @returns .env.example 文件内容\n */\nexport function generateEnvExample(features: FeatureId[], configs?: ModuleConfigs): string {\n const sections: string[] = []\n\n // 文件头\n sections.push(`# =============================================================================\n# Environment Variables\n# =============================================================================\n#\n# Copy this file to .env and fill in actual values.\n# Config files use literal defaults; matching HAI variables override YAML leaves.\n#\n# Naming convention: HAI_<config name>_<YAML path>\n# =============================================================================`)\n\n // 应用基础(始终生成)\n sections.push(`\n# =============================================================================\n# Application\n# =============================================================================\n# Environment: development | production | test\nHAI_CORE_ENV=development\n# Enable debug mode\nHAI_CORE_DEBUG=false`)\n\n // 数据库\n if (features.includes('db')) {\n const dbType = configs?.db?.type ?? 'sqlite'\n const dbDatabase = configs?.db?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const isSqlite = dbType === 'sqlite'\n sections.push(`\n# =============================================================================\n# Database (@h-ai/reldb)\n# =============================================================================\n# Database type: sqlite | postgresql | mysql\nHAI_DB_TYPE=${dbType}\n# SQLite: file path; PostgreSQL/MySQL: database name\nHAI_DB_DATABASE=${dbDatabase}${isSqlite\n ? `\n# PostgreSQL/MySQL connection (uncomment when not using sqlite)\n# HAI_DB_HOST=localhost\n# HAI_DB_PORT=5432\n# HAI_DB_USER=postgres\n# HAI_DB_PASSWORD=`\n : `\n# Connection\nHAI_DB_HOST=${configs?.db?.host ?? 'localhost'}\nHAI_DB_PORT=${configs?.db?.port ?? (dbType === 'postgresql' ? 5432 : 3306)}\nHAI_DB_USER=${dbType === 'postgresql' ? 'postgres' : 'root'}\nHAI_DB_PASSWORD=`}`)\n }\n\n // IAM / Session\n if (features.includes('iam')) {\n sections.push(`\n# =============================================================================\n# IAM (@h-ai/iam)\n# =============================================================================\n# HAI_IAM_PASSWORD_MINLENGTH=8\n# HAI_IAM_SESSION_MAXAGE=86400\n# HAI_IAM_SESSION_REFRESHTOKENMAXAGE=604800\n# HAI_KIT_COOKIE_KEY= # 32-char hex for SM4-CBC cookie encryption\n`)\n }\n\n // 缓存\n if (features.includes('cache')) {\n const cacheType = configs?.cache?.type ?? 'memory'\n if (cacheType === 'redis') {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=redis\n# Redis connection\nHAI_CACHE_HOST=${configs?.cache?.host ?? 'localhost'}\nHAI_CACHE_PORT=${configs?.cache?.port ?? 6379}\nHAI_CACHE_PASSWORD=\n# HAI_CACHE_URL=redis://localhost:6379/0\n# HAI_CACHE_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=memory\n# Redis connection (uncomment when type=redis)\n# HAI_CACHE_URL=redis://localhost:6379/0\n# HAI_CACHE_HOST=localhost\n# HAI_CACHE_PORT=6379\n# HAI_CACHE_PASSWORD=\n# HAI_CACHE_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n }\n\n // 存储\n if (features.includes('storage')) {\n const storageType = configs?.storage?.type ?? 'local'\n if (storageType === 's3') {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Default storage provider: local | s3\nHAI_STORAGE_TYPE=s3\n# S3 / S3-compatible\nHAI_STORAGE_BUCKET=\nHAI_STORAGE_REGION=us-east-1\nHAI_STORAGE_ACCESSKEYID=\nHAI_STORAGE_SECRETACCESSKEY=\n# HAI_STORAGE_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_FORCEPATHSTYLE=false\n# Local storage (uncomment when type=local)\n# HAI_STORAGE_ROOT=./data/uploads`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Default storage provider: local | s3\nHAI_STORAGE_TYPE=local\n# Local storage root path\nHAI_STORAGE_ROOT=${configs?.storage?.localPath ?? './data/uploads'}\n# S3 / S3-compatible (uncomment when type=s3)\n# HAI_STORAGE_BUCKET=\n# HAI_STORAGE_REGION=us-east-1\n# HAI_STORAGE_ACCESSKEYID=\n# HAI_STORAGE_SECRETACCESSKEY=\n# HAI_STORAGE_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_FORCEPATHSTYLE=false`)\n }\n }\n\n // AI\n if (features.includes('ai')) {\n const provider = configs?.ai?.defaultProvider ?? 'openai'\n if (provider === 'anthropic') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible API Key (Anthropic models via OpenAI-compatible proxy)\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else if (provider === 'openai') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI / OpenAI-compatible\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL (e.g. local proxy, Azure)\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible provider\nHAI_AI_LLM_APIKEY=\n# HAI_AI_LLM_BASEURL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n }\n\n // VecDB\n if (features.includes('vecdb')) {\n sections.push(`\n# =============================================================================\n# Vector Database (@h-ai/vecdb)\n# =============================================================================\n# Backend type: lancedb | pgvector | qdrant\nHAI_VECDB_TYPE=lancedb\n# LanceDB data path\nHAI_VECDB_PATH=./data/vecdb\n# Qdrant (uncomment when type=qdrant)\n# HAI_VECDB_URL=http://localhost:6333\n# HAI_VECDB_APIKEY=\n`)\n }\n\n // Reach\n if (features.includes('reach')) {\n sections.push(`\n# =============================================================================\n# Reach (@h-ai/reach)\n# =============================================================================\n# SMTP (uncomment when enabling email provider)\n# HAI_REACH_SMTP_HOST=smtp.example.com\n# HAI_REACH_SMTP_PORT=465\n# HAI_REACH_SMTP_USER=\n# HAI_REACH_SMTP_PASS=\n# HAI_REACH_SMTP_FROM=noreply@example.com\n`)\n }\n\n // Payment\n if (features.includes('payment')) {\n sections.push(`\n# =============================================================================\n# Payment (@h-ai/payment)\n# =============================================================================\n# WeChat Pay\n# HAI_PAYMENT_WECHAT_MCHID=\n# HAI_PAYMENT_WECHAT_APIV3KEY=\n# HAI_PAYMENT_WECHAT_SERIALNO=\n# HAI_PAYMENT_WECHAT_PRIVATEKEY=\n# HAI_PAYMENT_WECHAT_APPID=\n# Alipay\n# HAI_PAYMENT_ALIPAY_APPID=\n# HAI_PAYMENT_ALIPAY_PRIVATEKEY=\n# HAI_PAYMENT_ALIPAY_ALIPAYPUBLICKEY=\n# Stripe\n# HAI_PAYMENT_STRIPE_SECRETKEY=\n# HAI_PAYMENT_STRIPE_WEBHOOKSECRET=\n`)\n }\n\n // 测试(始终生成)\n sections.push(`\n# =============================================================================\n# Testing\n# =============================================================================\n# E2E test mode (relaxes rate limiting etc.)\n# HAI_E2E=1\n`)\n\n return sections.join('\\n')\n}\n\nfunction generateDeployConfig(): string {\n return `# =============================================================================\n# 部署配置 (@h-ai/deploy)\n# =============================================================================\n# 部署平台凭证通过 ~/.hai/credentials.yml 管理\n# 环境变量引用格式: \\${VAR_NAME:default_value}\n\n# 部署平台\nprovider:\n type: vercel\n token: \\${HAI_DEPLOY_VERCEL_TOKEN}\n\n# ---------------------------------------------------------------------------\n# 方式二:docker-ssh — 将应用容器化后通过 SSH 部署到任意 Linux 主机\n# 远程主机只需具备 SSH + docker/podman + compose,本地需 docker 或 podman\n# 切换时用下方 provider 替换上面的 vercel provider\n# ---------------------------------------------------------------------------\n# provider:\n# type: docker-ssh\n# ssh:\n# host: \\${HAI_DEPLOY_SSH_HOST}\n# port: 22\n# username: deploy\n# identityFile: \\${HAI_DEPLOY_SSH_KEY} # 私钥文件路径(省略则用 ssh-agent)\n# remote:\n# baseDir: /opt/hai/apps\n# runtime: auto # auto 优先 podman,其次 docker\n# expose:\n# type: port\n# containerPort: 3000\n# hostPort: 18080 # 访问地址 http://<host>:18080\n\n# 本地构建运行时(docker-ssh 使用;auto 优先 podman)\n# container:\n# runtime: auto\n\n# 基础设施服务(按需开启)\nservices:\n # PostgreSQL 数据库 (Neon)\n # db:\n # provisioner: neon\n # apiKey: \\${HAI_DEPLOY_NEON_API_KEY}\n\n # Redis 缓存 (Upstash)\n # cache:\n # provisioner: upstash\n # email: \\${HAI_DEPLOY_UPSTASH_EMAIL}\n # apiKey: \\${HAI_DEPLOY_UPSTASH_API_KEY}\n\n # S3 存储 (Cloudflare R2)\n # storage:\n # provisioner: cloudflare-r2\n # accountId: \\${HAI_DEPLOY_CF_ACCOUNT_ID}\n # apiToken: \\${HAI_DEPLOY_CF_API_TOKEN}\n # accessKeyId: \\${HAI_DEPLOY_R2_ACCESS_KEY_ID}\n # secretAccessKey: \\${HAI_DEPLOY_R2_SECRET_ACCESS_KEY}\n\n # 邮件 (Resend)\n # email:\n # provisioner: resend\n # apiKey: \\${HAI_DEPLOY_RESEND_API_KEY}\n # from: \\${HAI_DEPLOY_RESEND_FROM}\n\n # 短信 (阿里云)\n # sms:\n # provisioner: aliyun\n # accessKeyId: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_ID}\n # accessKeySecret: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_SECRET}\n # signName: \\${HAI_DEPLOY_ALIYUN_SIGN_NAME}\n\n# docker-ssh 说明:\n# - 应用镜像使用工程自带的 Dockerfile(adapter-node),本地构建后经 SSH 传输并 load\n# - compose 会按上方启用的 db/cache/storage 自动追加 postgres/redis/minio sidecar\n# - 若同时配置了对应云 Provisioner,则优先使用云资源,不再自建 sidecar\n`\n}\n\nfunction generateVecdbConfig(): string {\n return `# =============================================================================\n# 向量数据库配置 (@h-ai/vecdb)\n# =============================================================================\n\n# 数据库类型: lancedb | pgvector | qdrant\ntype: lancedb\n\n# LanceDB 数据目录\npath: ./data/vecdb\n\n# 距离度量: cosine | euclidean | dot\n# metric: cosine\n\n# pgvector 配置(当 type 为 pgvector 时使用)\n# type: pgvector\n# host: localhost\n# port: 5432\n# database: hai\n# user: postgres\n# password: ''\n# indexType: hnsw\n# tablePrefix: vec_\n\n# Qdrant 配置(当 type 为 qdrant 时使用)\n# type: qdrant\n# url: http://localhost:6333\n# apiKey: ''\n`\n}\n\nfunction generateReachConfig(): string {\n return `# =============================================================================\n# 消息推送配置 (@h-ai/reach)\n# =============================================================================\n\nproviders:\n # 控制台输出(开发/测试用)\n - name: console_dev\n type: console\n\n # SMTP 邮件(按需开启)\n # - name: email\n # type: smtp\n # host: \\${HAI_REACH_SMTP_HOST:smtp.example.com}\n # port: \\${HAI_REACH_SMTP_PORT:465}\n # secure: true\n # user: \\${HAI_REACH_SMTP_USER:}\n # pass: \\${HAI_REACH_SMTP_PASS:}\n # from: \\${HAI_REACH_SMTP_FROM:noreply@example.com}\n\n # 阿里云短信(按需开启)\n # - name: sms\n # type: aliyun-sms\n # accessKeyId: \\${HAI_REACH_SMS_ACCESS_KEY}\n # accessKeySecret: \\${HAI_REACH_SMS_SECRET_KEY}\n # signName: MyApp\n\n# IAM 标准模板:保留空数组以支持 HAI_REACH_TEMPLATES 整体覆盖。\ntemplates: []\n# 开启对应 Provider 后,将空数组替换为需要的模板列表,例如:\n# templates:\n# - name: otp_email\n# provider: email\n# subject: \"Verification code\"\n# body: \"Your verification code is {code}.\"\n# - name: otp_sms\n# provider: sms\n# body: \"Your verification code is {code}.\"\n# - name: password_reset\n# provider: email\n# subject: \"Password reset\"\n# body: \"Reset your password: {resetUrl}. Expires at {expiresAt}.\"\n# - name: password_reset_sms\n# provider: sms\n# body: \"Reset your password: {resetUrl}. Expires at {expiresAt}.\"\n\n# 免打扰(可选)\n# dnd:\n# enabled: true\n# strategy: delay\n# start: \"22:00\"\n# end: \"08:00\"\n`\n}\n\nfunction generateSchedulerConfig(): string {\n return `# =============================================================================\n# 任务调度配置 (@h-ai/scheduler)\n# =============================================================================\n\n# 是否启用数据库日志(需要 @h-ai/reldb)\nenableDb: true\n\n# 执行日志表名\ntableName: hai_scheduler_logs\n\n# 任务定义表名\ntaskTableName: hai_scheduler_tasks\n\n# 分布式锁表名\nlockTableName: hai_scheduler_locks\n\n# 检查间隔(毫秒)\ntickInterval: 1000\n\n# 锁过期时间(毫秒)\nlockExpireMs: 300000\n`\n}\n\nfunction generateAuditConfig(): string {\n return `# =============================================================================\n# 审计日志配置 (@h-ai/audit)\n# =============================================================================\n\n# 用户表名(用于 JOIN 查询)\nuserTable: hai_iam_users\n\n# 用户表主键列名\nuserIdColumn: id\n\n# 用户名列名\nuserNameColumn: username\n`\n}\n\nfunction generatePaymentConfig(): string {\n return `# =============================================================================\n# 支付配置 (@h-ai/payment)\n# =============================================================================\n\n# 微信支付(按需开启)\n# wechat:\n# mchId: ''\n# apiV3Key: ''\n# serialNo: ''\n# privateKey: ''\n# appId: ''\n\n# 支付宝(按需开启)\n# alipay:\n# appId: ''\n# privateKey: ''\n# alipayPublicKey: ''\n# signType: RSA2\n# sandbox: false\n\n# Stripe(按需开启)\n# stripe:\n# secretKey: ''\n# webhookSecret: ''\n`\n}\n"]}
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- export { generateConfigFile, generateEnvExample } from './chunk-P6X63D3I.js';
3
- import './chunk-2H7UOFLK.js';
4
- //# sourceMappingURL=cli-config-templates-EFABFSCY.js.map
5
- //# sourceMappingURL=cli-config-templates-EFABFSCY.js.map