@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.21
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 +74 -154
- package/README.ja.md +74 -154
- package/README.md +74 -154
- package/dist/{chunk-AKV2CQZV.js → chunk-2ZL5ORQ2.js} +316 -97
- package/dist/chunk-2ZL5ORQ2.js.map +1 -0
- package/dist/{chunk-EF7WOFWZ.js → chunk-L53P43B2.js} +1312 -163
- package/dist/chunk-L53P43B2.js.map +1 -0
- package/dist/cli-config-templates-LWMD2I6V.js +5 -0
- package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
- package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
- package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30118 -3253
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +22 -18
- package/templates/README.md +45 -30
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
- package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
- package/templates/apps/api/.dockerignore.hbs +10 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +8 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +6 -2
- package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +24 -2
- package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +74 -24
- package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +153 -37
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
- package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +106 -33
- package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
- package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
- package/templates/apps/api/eslint.config.js.hbs +3 -1
- package/templates/apps/api/images/serv.Dockerfile.hbs +15 -10
- package/templates/apps/api/playwright.config.ts.hbs +23 -19
- package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
- package/templates/apps/fullstack/.dockerignore.hbs +10 -0
- package/templates/apps/fullstack/.gitignore.hbs +7 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +14 -12
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +5 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +9 -9
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
- package/templates/apps/fullstack/docs/security-configuration.md.hbs +4 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +140 -28
- package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
- package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +19 -11
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +17 -8
- package/templates/apps/fullstack/package.json.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +7 -5
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +13 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -4
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +147 -33
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +146 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +142 -26
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +48 -66
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
- package/templates/apps/fullstack/playwright.config.ts.hbs +25 -3
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -14
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +74 -30
- package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
- package/templates/apps/mobile-app/capacitor.config.ts.hbs +5 -2
- package/templates/apps/mobile-app/messages/en-US.json +2 -2
- package/templates/apps/mobile-app/messages/ja-JP.json +2 -2
- package/templates/apps/mobile-app/messages/zh-CN.json +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +20 -6
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -1
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
- package/templates/apps/website/scripts/static-website.mjs.hbs +19 -6
- package/templates/apps/website/src/routes/+layout.svelte.hbs +56 -29
- package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
- package/templates/base/.dockerignore.hbs +10 -0
- package/templates/base/.gitignore.hbs +7 -2
- package/templates/base/e2e/app.spec.ts.hbs +130 -36
- package/templates/base/eslint-rules.js +44 -25
- package/templates/base/eslint.config.js.hbs +3 -1
- package/templates/base/images/web.Dockerfile.hbs +25 -14
- package/templates/base/package.json.hbs +13 -7
- package/templates/base/patches/daisyui-5.7.50.patch +8 -0
- package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
- package/templates/base/playwright.config.ts +3 -1
- package/templates/base/pnpm-workspace.yaml.hbs +33 -16
- package/templates/base/src/app.d.ts.hbs +1 -1
- package/templates/base/src/app.html.hbs +1 -0
- package/templates/base/src/hooks.server.ts.hbs +44 -33
- package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
- package/templates/base/src/lib/server/init.ts.hbs +146 -35
- package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
- package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
- package/templates/base/svelte.config.js.hbs +10 -100
- package/templates/base/tests/scaffold.test.ts.hbs +12 -3
- package/templates/base/tsconfig.json.hbs +7 -4
- package/templates/base/vite.config.ts.hbs +108 -14
- package/templates/base/vitest.config.ts.hbs +6 -2
- package/templates/features/ai/routes/api/chat/+server.ts +3 -1
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +14 -3
- package/templates/shared/browser-check/startup.js +115 -0
- package/templates/shared/browser-check/update.css +37 -0
- package/templates/shared/browser-check/update.html +19 -0
- package/templates/shared/browser-startup.spec.ts +23 -0
- package/templates/shared/build-metadata.ts +18 -4
- package/templates/shared/e2e-env.ts +22 -6
- package/templates/shared/native-name.d.mts +5 -0
- package/templates/shared/native-name.mjs +94 -0
- package/templates/shared/public-base-path.mjs +17 -4
- package/templates/shared/run-e2e.mjs +46 -12
- package/templates/shared/run-node.mjs +56 -0
- package/templates/shared/run-tauri.mjs +74 -0
- package/templates/shared/vite-runtime-config.ts +9 -3
- package/templates/skills/AGENTS.md +10 -73
- package/templates/skills/bridges/admin/AGENTS.md +9 -65
- package/templates/skills/bridges/api/AGENTS.md +8 -59
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
- package/templates/skills/bridges/h5/AGENTS.md +8 -63
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
- package/templates/skills/bridges/website/AGENTS.md +8 -63
- package/templates/skills/hai-ai/SKILL.md +6 -2
- package/templates/skills/hai-ai/acp.md +153 -0
- package/templates/skills/hai-ai/guide.md +27 -12
- package/templates/skills/hai-ai/reference.md +24 -15
- package/templates/skills/hai-ai/resources.md +69 -0
- package/templates/skills/hai-api-client/SKILL.md +1 -1
- package/templates/skills/hai-api-client/reference.md +3 -1
- package/templates/skills/hai-app-create/collaboration.md +7 -0
- package/templates/skills/hai-app-create/development.md +19 -10
- package/templates/skills/hai-app-create/project.md +33 -0
- package/templates/skills/hai-app-create/reference.md +10 -10
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-app-tests/validation.md +1 -0
- package/templates/skills/hai-build/SKILL.md +19 -9
- package/templates/skills/hai-build/paths.md +4 -2
- package/templates/skills/hai-build/reference.md +26 -8
- package/templates/skills/hai-cache/reference.md +6 -2
- package/templates/skills/hai-capacitor/SKILL.md +1 -0
- package/templates/skills/hai-capacitor/reference.md +24 -0
- package/templates/skills/hai-ci/SKILL.md +3 -0
- package/templates/skills/hai-core/SKILL.md +9 -4
- package/templates/skills/hai-core/reference.md +40 -10
- package/templates/skills/hai-core/request-access.md +2 -2
- package/templates/skills/hai-crypto/SKILL.md +2 -2
- package/templates/skills/hai-crypto/reference.md +7 -3
- package/templates/skills/hai-deploy/SKILL.md +41 -5
- package/templates/skills/hai-deploy/middleware.md +26 -14
- package/templates/skills/hai-framework-sync/SKILL.md +13 -9
- package/templates/skills/hai-framework-sync/sync.md +2 -0
- package/templates/skills/hai-iam/reference.md +2 -2
- package/templates/skills/hai-kit/SKILL.md +1 -1
- package/templates/skills/hai-kit/reference.md +19 -5
- package/templates/skills/hai-payment/reference.md +41 -17
- package/templates/skills/hai-reach/SKILL.md +3 -1
- package/templates/skills/hai-reach/reference.md +29 -9
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-reldb/reference.md +9 -2
- package/templates/skills/hai-scheduler/reference.md +1 -1
- package/templates/skills/hai-serv/SKILL.md +2 -1
- package/templates/skills/hai-serv/reference.md +26 -15
- package/templates/skills/hai-storage/reference.md +23 -6
- package/templates/skills/hai-ui/SKILL.md +11 -6
- package/templates/skills/hai-ui/charts.md +51 -0
- package/templates/skills/hai-ui/guide.md +1 -1
- package/templates/skills/hai-ui/reference.md +58 -38
- package/templates/skills/hai-vecdb/reference.md +8 -3
- package/dist/chunk-AKV2CQZV.js.map +0 -1
- package/dist/chunk-EF7WOFWZ.js.map +0 -1
- package/dist/chunk-MUHRBJSW.js +0 -29272
- package/dist/chunk-MUHRBJSW.js.map +0 -1
- package/dist/cli-config-templates-HHFHNXBN.js +0 -5
- package/dist/src-TEBHR5I5.js +0 -5
- package/dist/src-TEBHR5I5.js.map +0 -1
- package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +0 -18
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
- 每轮都在 finally/teardown 停止本轮进程、清理本轮临时工程/容器/镜像/数据;确认退出和资源释放后再运行冲突任务。日志、截图与报告保留到问题分析和交付结束,再按项目约定清理,不覆盖失败证据、不提交验收产物、不删除用户资源。
|
|
36
36
|
- 部署夹具先检查固定公开基础镜像,仅确认缺失才拉取;成功 create 先登记完整 ID,再在私有 PID namespace 内异步准备 APT/pip/sshd。所有准备 exec 转交同一取消信号,等待本地客户端 close 后按该 ID 停止全树并核验消失。总准备 deadline 早于 hook 并预留回收预算;公共缓存不删,私有 tag 回收前复核内容 ID/owner。未取得可信 create 回执或测试进程被强制终止时记录未知,不能按名称或 label 认领资源。
|
|
37
37
|
- 浏览器执行器默认将每轮、每组的证据分别放入忽略的 `test-results/run-*/group-*`;只清理本轮临时数据库/上传数据。显式 `HAI_AUTH_EVIDENCE_DIR`、`--output` 和 Playwright HTML 输出目录会保留,取消也不删除已有报告。不要复制私有环境、密钥或业务数据库到证据目录。
|
|
38
|
+
- 带浏览器前端的 fullstack 模板通过 `--next --e2e-group=session-close --config playwright.config.ts` 单独运行会话回归。执行器只把组名传给本轮 Playwright,配置在该轮启用页面离开撤销、短会话超时和首次改密。
|
|
38
39
|
- 经托管 `test:e2e` 入口为服务子进程提供绝对 `HAI_E2E_DATA_ROOT` 和显式测试后端,隔离继承的 `HAI_*` / `PUBLIC_*` / `VITE_*` 与用户 `.env.local`。仅本轮测试启用 `HAI_E2E=1`;普通开发与生产的 dotenv 加载、共享后端守卫不放宽。启用 transport 的验收使用本轮可信 ring 和 registry,不能关闭加密掩盖客户端契约问题。
|
|
39
40
|
- Fullstack验收已部署服务时,通过 `E2E_ADMIN_PASSWORD`、`E2E_AUDIT_ADMIN_PASSWORD`、`E2E_CONFIG_ADMIN_PASSWORD`、`E2E_BUSINESS_OPERATOR_PASSWORD` 分别提供同环境的初始管理员、审计管理员、配置管理员、业务操作员密码;对应 `HAI_ADMIN_DEFAULT_PASSWORD`、`HAI_AUDIT_ADMIN_PASSWORD`、`HAI_CONFIG_ADMIN_PASSWORD`、`HAI_BUSINESS_OPERATOR_PASSWORD`。测试配置、登录和再次验证共用这些值;固定回退只用于托管测试账号,不能替代随机部署凭据。只核对来源与变量名,不把值写入日志、截图或报告;保留同密码拒绝、初始凭据和再次验证断言。
|
|
40
41
|
- 最终报告各项门禁状态、执行/复用范围、修复结果和未验证原因;mock、容器、真实服务及原生结果分别说明。只陈述实际证据,不以“脚本执行完了”代表验证通过。
|
|
@@ -17,6 +17,12 @@ description: "构建或排查 hai 应用的源码、静态网站、容器镜像
|
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
|
+
- 构建批次使用 `build-<工程>-<目标>-YYYYMMDD-HHmmss-<随机串>-<版本>`,时间与 deploy 共用 Core 的构建机本地日期格式。原生目标补平台、Desktop 架构和 debug/release;归档文件如 `todo-android-debug-0.1.0.apk`、`todo-windows-x64-release-0.1.0-setup.exe`,用工程标识,显示名仍取 Core。latest 存批次相对目录,buildId 是不含工程/目标前缀的短标识;旧目录和清单保持可读,不重命名历史制品或原生工具工作文件。
|
|
21
|
+
|
|
22
|
+
- 原生名称是单独的公开构建输入:App 的 appName、Desktop 的 productName/主窗口初始标题复用 `_core.yml.name` 与 HAI_CORE_NAME,Fullstack 从工程根 dotenv 和 serv YAML 解析。正式打包默认 prod,Tauri dev 默认 dev,进程 HAI_CORE_ENV 可切换模式;只有名称写入原生配置。Desktop 使用 pnpm tauri/tauri:build 或 hai build 的入口,不绕过生成的 run-tauri.mjs;保留当前平台 JSON 的窗口数组与 null 删除,优先更新 main 标签窗口。已有 Capacitor 平台需另行同步显示名;源码/镜像的环境隔离规则保持适用。
|
|
23
|
+
|
|
24
|
+
- 父级 `HAI_CORE` JSON 替换 YAML。父 JSON 未提供 `name` 且没有生效的 `HAI_CORE_NAME` 时,直接运行原生工具与 `hai build` 都使用 Core 默认名 `hai Admin`。
|
|
25
|
+
|
|
20
26
|
- 先读当前 package.json、lockfile 与 README,识别 SvelteKit / API workspace / fullstack / Vite SPA / 原生 App,按实际 scripts 验证。
|
|
21
27
|
- 升级 hai-framework、比较版本差异或同步框架 skills 时,使用 [hai-framework-sync](../hai-framework-sync/SKILL.md) 完成版本发现、差异分析、选择/阻塞处理、迁移与验证。
|
|
22
28
|
- 功能开发先用 [development.md](../hai-app-create/development.md) 把需求匹配到当前版本的框架能力与配置;再读取所选模块 skill 和公开类型。
|
|
@@ -27,24 +33,26 @@ description: "构建或排查 hai 应用的源码、静态网站、容器镜像
|
|
|
27
33
|
|
|
28
34
|
## 构建与部署
|
|
29
35
|
|
|
30
|
-
- website
|
|
36
|
+
- 新建website只安装Core/Kit/UI,默认Node SSR,可直接切换静态发布;注释配置与环境变量参考不启用模块。后续增加的服务端业务依赖、初始化及API/actions须先移除。compile静态预渲染使用`HAI_BUILD_STATIC=1 PUBLIC_BASE_PATH=/docs-site`,docs-site替换为实际仓库名,各路径段仅ASCII字母、数字、_、.、-,无末尾/;省略/0保持SSR,根站路径留空。HAI_BUILD_STATIC仅用于compile,PUBLIC_BASE_PATH也支持image网站构建。静态拒绝服务端业务模块、自定义API/actions与未预渲染页面,不放宽生产守卫、不用SPA fallback;默认健康端点不发布。构建不读根.env,static为公开目录,拒绝.env/.env.*与符号链接。上传build使用include-hidden-files:true保留.nojekyll,验首页/深链/刷新/资产与敏感哨兵。
|
|
31
37
|
|
|
32
38
|
- 创建默认包含开发期部署工具,可用 --no-deploy 关闭。每个镜像使用 images/ 中独立 Dockerfile,Web 配置位于 images/web/,不使用镜像阶段 target 配置。
|
|
33
|
-
- 构建器生成 images/dependencies/ 安装输入;fetch、安装和生产依赖整理共用 pnpm store,安装与生产依赖整理共用 SQLite
|
|
39
|
+
- 构建器生成 images/dependencies/ 安装输入;fetch、安装和生产依赖整理共用 pnpm store,安装与生产依赖整理共用 SQLite 驱动缓存,并复用依赖层;依赖安装在源码COPY前,前后端仅复制各自需要的源码。运行镜像去掉应用自身声明、source map和README。file: 归档、补丁、workspace 元数据必须进入下载上下文;原生依赖缓存按系统/架构/ABI 区分。基础镜像、中间件及工具指定版本,禁止 latest。
|
|
34
40
|
- 默认容器面向 CPU 运行或远程 AI 服务,Dockerfile 设置 `ONNXRUNTIME_NODE_INSTALL_CUDA=skip`,跳过额外 CUDA/TensorRT 下载;使用宿主机 GPU 时定制镜像和运行参数,不修改本机 npm 或全局 AI/GPU 配置。
|
|
35
41
|
- pnpm run deploy --build 显式串联构建、打包和发布;--bundle-only 只交付完整离线包与跨平台脚本;--services 以服务器成功状态为基线只更新选中服务。完整包自动包含自建中间件镜像,服务器禁止联网拉取。
|
|
36
|
-
- `pnpm build` 默认仅编译源码(调用 compile);`pnpm build --target
|
|
37
|
-
-
|
|
38
|
-
-
|
|
42
|
+
- `pnpm build` 默认仅编译源码(调用 compile);`pnpm build --target image` 根据 hai.json.build.images 的 dockerfile 构建并导出 serv/web 镜像(可单独或组合),可用 --images web 只构建前端,默认最多两项并行,--concurrency 1可串行,auto 优先可达 Podman、其次 Docker,可用 --runtime 显式指定。
|
|
43
|
+
- 自动化直接执行 `hai build --target image --json`,成功时 stdout 只有单行 JSON,进度和失败走 stderr。读取 `artifactDir` 作为本次部署输入,清单记录 Git 提交、dirty 状态、实际镜像 OS/架构、镜像引用和归档 SHA-256。发布 Worker 从固定提交的干净检出构建。
|
|
44
|
+
- compile 交付源码编译结果,image 交付镜像归档,desktop 交付 Tauri 系统安装包,app 交付 Capacitor 原生制品;每次选择一个 target。hai.json 的 build.images[].dockerfile 指向独立镜像文件,build.desktop/build.app 声明原生工程目录,deploy 保存整个项目的部署配置;参数和平台前置以当前 CLI 类型与工程 scripts 为准。cap:sync 只同步工程,不能作为已打包或已安装的证据。
|
|
45
|
+
- `--image-source` / `--npm-source` 分别选 official/cn/custom;首次交互保存到 hai.json.build.sources,参数仅覆盖本次,--yes 复用且未配置时走官方。cn 镜像用毫秒镜像覆盖 Docker Hub/GHCR,npm 用 npmmirror;保留版本和 lockfile 校验。旧 Dockerfile 缺参数时明确提示同步模板。自定义映射见 [下载源与代理](reference.md#下载源与代理)。
|
|
46
|
+
- Docker/Podman 的构建代理用预定义参数传递;--proxy-mode 选 inherit/system/custom/none,--proxy-url 指定自定义地址,--proxy-bypass 覆盖绕过列表,--no-http-proxy 等价 none。Podman 默认网络保留原代理地址,显式 pasta 等隔离网络才改为宿主入口;Docker 非 host 网络使用宿主入口。首个下载步骤检查实际构建可达性,不能用 run 网络代替。镜像拉取端仍需 Docker daemon/Desktop 或 Podman Machine/API 自身代理,CLI 分别预检提示。不要把代理写成 Dockerfile ENV 或固化进运行镜像。
|
|
39
47
|
- --network 默认沿用引擎网络;只有实际构建网络无法访问依赖/代理时才显式选择网络,pasta 仅适用于 rootless Podman。build 与 run 的主机名解析可能不同,须验证构建网络中的可达性,不默认改成 host 网络。
|
|
40
48
|
- 从运行build的应用根package.json.version读取版本,不能使用CLI/框架版本或服务子包版本代替。根目录清单version=4,非根目录version=5并记录webBasePath;appVersion为应用版本。servContainerPort/webContainerPort仅记录images配置显式填写的端口,部分构建也保留未选镜像端口,未填写时沿用部署默认值。镜像tar名含版本、buildId区分同版本构建,Tauri也接收根版本。不要手改清单或保留无版本的交付归档。
|
|
41
|
-
-
|
|
42
|
-
- 运行产物的反馈标识为 appVersion/buildId/commit:CLI 过滤调用者 HAI_/PUBLIC_/VITE_
|
|
49
|
+
- 源码/镜像构建不区分 dev/test/prod,不加载部署环境覆盖,不注入运行期凭据;原生名称遵循上述单独输入规则。所选镜像清单含 SHA-256,全部成功才更新本机最近构建指针(不是镜像标签)。不要让 compile、Dockerfile 或 Tauri beforeBuildCommand 递归调用自身的原生/镜像构建。
|
|
50
|
+
- 运行产物的反馈标识为 appVersion/buildId/commit:CLI 过滤调用者 HAI_/PUBLIC_/VITE_ 后注入本次内部生成的 HAI_BUILD_ID/HAI_BUILD_COMMIT,原生目标额外允许名称和模式。Docker 使用镜像批次 ID 经 ARG 传入编译。每个目标的运行版本取自己的 manifest,同次 SSR 的 client/server 共用 buildId;没有所属 Git 提交时明确 unknown。模板的三字段 build-metadata.json 与代码内联共用值,验收需与 API/ZIP 的各端证据分别核对。
|
|
43
51
|
- Vite 的 envDir 与 SvelteKit 的 kit.env.dir 是独立加载入口,必须同时阻止构建阶段读取工程根的 .env 文件;开发/预览由 core loader 注入,运行期由 deploy 注入。用临时 .env.local 哨兵值检查输出,避免只过滤进程变量却仍把私有文件固化进产物。
|
|
44
52
|
- 静态客户端通过 runtime-config.js 或原生宿主初始化脚本获取公开地址与元数据;仅投影公开白名单,禁止将完整 process.env、服务配置或密钥序列化到浏览器。预览与容器启动均须验证真实登录,不能依赖构建期 API 地址让 E2E 通过。
|
|
45
53
|
- Fullstack Capacitor App 在 `public/runtime-config.js` 提供公开 API 地址,再编译并同步原生资源;Tauri 从启动进程的 PUBLIC_API_BASE 注入。不能把开发代理的 .env 配置当作已安装原生包的运行配置。
|
|
46
54
|
- `pnpm build --target desktop --platform windows|linux|macos --arch x64|arm64` 调用对应工程的 Tauri build。要求在目标系统准备 Rust target、SDK 和打包工具,不假定跨系统编译。原生安装包必须按目标系统实测。
|
|
47
|
-
- deploy 默认消费已有产物,--build
|
|
55
|
+
- deploy 默认消费已有产物,--build 显式先构建。工程已交付 `hai-deploy` 时按需读取该技能;已有工程的通用 AI 支持不假定已安装部署工具,先核对实际 scripts 与依赖。网络不通时 --bundle-only 默认生成 tar.gz(显式--output release.tar保留未压缩格式),按说明手工搬运;packaged 不代表部署已运行。
|
|
48
56
|
- 测试临时工程、镜像、容器、辅助进程和数据必须在 finally/teardown 自动清理,失败/取消也清理;日志、截图和报告保留到分析和交付结束再清理,不得提交测试产物或删除用户既有资源。
|
|
49
57
|
- 浏览器测试的数据放入本轮独立临时目录;Windows 等子进程与服务退出后再删除数据库和上传文件,不依赖下一轮测试补清理。
|
|
50
58
|
|
|
@@ -54,6 +62,8 @@ description: "构建或排查 hai 应用的源码、静态网站、容器镜像
|
|
|
54
62
|
|
|
55
63
|
## 按需参考
|
|
56
64
|
|
|
65
|
+
Windows 构建子进程使用追加式 pnpm 日志并规范 CRLF,等待输出写完再完成;保留 stdout/stderr、UTF-8 和所有警告,不靠静默输出隐藏下载错误。Dockerfile 的 pnpm fetch/install 同步使用 --reporter=append-only。
|
|
66
|
+
|
|
57
67
|
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
58
68
|
|
|
59
69
|
- 适用场景
|
|
@@ -63,4 +73,4 @@ description: "构建或排查 hai 应用的源码、静态网站、容器镜像
|
|
|
63
73
|
- 统一编码规范
|
|
64
74
|
- 标准工作流
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
`pnpm build --target app --platform android --runtime auto --debug` 容器打调试 APK,省略 `--debug` 使用发布签名;`--runtime` 可选 auto/docker/podman,省略时使用本机工具链。iOS 在 macOS/Xcode 上签名导出 IPA。原生打包拒绝同步后的 `CAPACITOR_SERVER_URL`,成功后按平台、构建类型和批次归档到 `.hai/build/app/` 或 `.hai/build/desktop/`,更新对应的 `latest`。只有工程已交付 `hai-capacitor` 时,才按需读取该技能 `reference.md` 的“Android 容器打包”小节;纯 API 工程不交付原生技能,新增原生端时先补当前版本的能力与技能。构建身份统一 128 字符,显式非法 `HAI_BUILD_ID`/`HAI_BUILD_COMMIT` 报错且不回显原值。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## SvelteKit
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
SvelteKit 3 的宿主在 `sveltekit({ paths: { base: 挂载目录, relative: false } })` 中固定绝对前缀,再通过公开 `$app/paths.resolve('/').replace(/\/$/, '')` 获取目录。启用 transport 时,将同一前缀传给服务端 `crypto.transport.basePath`、浏览器 `installBrowserTransport(config, { crypto, basePath })` 与独立 Kit client 的 `transport.basePath`。默认空字符串为域名根目录,不在 YAML 另维护一份挂载目录;SSR 相对路径不能作为协议前缀。交换/排除/容量路径仍用完整路径。目录验收须检查带前缀 API 的加密标记和缺协议拒绝,页面、登录成功不能替代协议验证。
|
|
8
8
|
|
|
9
9
|
- admin、h5和website从构建进程的PUBLIC_BASE_PATH设置kit.paths.base,默认空字符串。根目录保持空,子目录如`/yyy`无末尾斜杠;构建不读取私有dotenv中的这个控制值。
|
|
10
10
|
- 组件链接用`$app/paths`的resolve;认证动作使用带base的api/auth地址。守卫匹配实际URL,protectedPaths/publicPaths/loginUrl都应带base。新模板已接好,旧应用与自定义路由须同步核对。
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
|
|
21
21
|
## SDK容器交付
|
|
22
22
|
|
|
23
|
-
- `PUBLIC_BASE_PATH=/yyy`用于compile或
|
|
23
|
+
- `PUBLIC_BASE_PATH=/yyy`用于compile或image网站构建;HAI_BUILD_STATIC仍只用于website的compile静态预渲染。
|
|
24
24
|
- 根目录制品沿用格式4,非根目录制品使用格式5并记录webBasePath。使用支持格式5的CLI/Deploy;旧工具拒绝新格式,不能手改编号绕过。
|
|
25
25
|
- SDK生成匹配的Nginx目录、运行配置入口、健康检查和访问URL。外部网关转发到此入口时保留目录,避免再次剥离。
|
|
26
26
|
- 每次完整或部分构建保持同一PUBLIC_BASE_PATH,即使只构建serv。目录变化须全量部署;遗漏或改变目录的增量包会在替换前拒绝。失败恢复使用旧发布的健康路径;容器恢复不撤销业务数据。
|
|
27
27
|
|
|
28
|
+
全站统一目录时,复用 `PUBLIC_BASE_PATH`、Serv `http.apiPrefix`、健康/就绪和文档路径及存储 `publicEndpoint` 的显式配置。额外 SDK 网关保留此前缀,根路径业务可全部拒绝;内部探活和 HTTP-01 验证入口单独处理。配置步骤见上述手册的“所有对外业务路径必须带同一目录”。浏览器验收须覆盖根 API 不可达时仍能登录和上传下载。
|
|
29
|
+
|
|
28
30
|
## 验收与同步
|
|
29
31
|
|
|
30
32
|
核对根路径回归、带目录的加载/刷新/认证/权限、公开配置无缓存、API目标与390px布局;确认所有真实前置已成功,区分浏览器、Nginx和服务器恢复证据。修改目录能力时同步构建清单、部署读取方、健康检查、回滚、模板、用例、三语说明和使用截图。
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
应用 Node/工具链要求以当前 `package.json.engines`、lockfile 与 CI 为准。使用 Turbo 时核对影响产物的环境变量、共享配置和模式切换清理策略,并验证实际产物;不要假定应用使用框架仓库的 tsup/Turbo 私有脚本。
|
|
6
6
|
|
|
7
|
+
镜像 JSON 的 `manifest.source.dirty` 检查工程、所选 context 与 Dockerfile 中未忽略文件的 Git 状态;context 指向仓库根时包含兄弟包。跨所属仓库或检查不完整返回 null,不能解释为干净;它不是构建输入的内容证明,发布 Worker 仍须使用固定提交的受控检出。
|
|
8
|
+
|
|
7
9
|
本技能帮助识别应用架构、依赖和技能。日常开发决策、安全边界与能力选择见 [development.md](../hai-app-create/development.md)。
|
|
8
10
|
|
|
9
11
|
## 适用场景
|
|
@@ -44,9 +46,25 @@
|
|
|
44
46
|
|
|
45
47
|
pnpm 专属配置放在 `pnpm-workspace.yaml`,`.npmrc` 仅用于注册表与认证。Docker 缓存层使用 `pnpm fetch` 读取锁文件;pnpm 12 的该命令不支持 `--frozen-lockfile`。后续仍通过 `pnpm install --offline --frozen-lockfile` 校验清单与锁文件一致。容器裁剪用 `pnpm pm deploy --filter <服务包> --prod <目标目录>`,确保执行内置命令,避免项目同名 `deploy` 脚本覆盖它。保留 `.gitattributes`,让 `.dockerignore`、Dockerfile 和 POSIX 脚本统一使用 LF,保持跨平台输入一致并确保 Linux 脚本可运行。
|
|
46
48
|
|
|
49
|
+
镜像构建只应使用可信且构建期间不会被其他主体替换的 context。CLI 在各 context 的 `images/dependencies/` 生成依赖缓存及 `.managed.json` 跟踪清单;缓存祖先目录不得是符号链接或 Windows junction,缓存文件不得是链接或多硬链接文件。更新通过独占临时文件原子替换,内容未变时保留时间戳;旧清单清理仅限该缓存树。若报缓存路径错误,按提示的原因和诊断码检查该路径,处理链接或访问权限后再重建。跟踪清单最多 16MiB,超限或 JSON 损坏时会给出诊断,且不会回显原文件内容。
|
|
50
|
+
|
|
51
|
+
### 下载源与代理
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
pnpm build --target image --image-source cn --npm-source cn
|
|
55
|
+
pnpm build --target image --proxy-mode custom --proxy-url http://127.0.0.1:10808
|
|
56
|
+
pnpm build --target image --image-source custom --image-mirrors "docker.io=mirror.example.com/docker.io,ghcr.io=mirror.example.com/ghcr.io" --npm-source custom --npm-registry https://npm.example.com/registry
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
首次交互保存尚未配置的 `hai.json.build.sources.image` / `.npm` 与 `build.proxy`。命令参数优先且不回写,已保存值其次;非交互未配置时官方源与 inherit 代理。image 形如 `{ "type": "custom", "registries": { "docker.io": "mirror.example.com/docker.io", "ghcr.io": "mirror.example.com/ghcr.io" } }`,npm 形如 `{ "type": "custom", "registry": "https://npm.example.com/registry" }`。official/cn 只需 type。cn 使用毫秒镜像的 docker.1ms.run / ghcr.1ms.run 及 registry.npmmirror.com;公共服务有同步延迟和限流,需验证实际版本。打包中间件会复用本地原始镜像,缺失时从所选源拉取并恢复原标签,离线包与部署身份保持一致。
|
|
60
|
+
|
|
61
|
+
代理形如 `{ "mode": "custom", "url": "http://127.0.0.1:10808", "bypass": "localhost,127.0.0.1" }`。inherit 使用进程变量;system 读取 Windows/macOS 静态代理,PAC/自动发现或其他平台用继承/自定义;none 清空本次代理变量,--no-http-proxy 等价 none。自定义地址不含认证,认证代理仅从环境继承。Podman 默认构建网络保留原代理地址,显式 --network pasta 等隔离网络时回环地址替换为 host.containers.internal;Docker 非 host 网络使用 host.docker.internal。须验证真实构建网络,run 结果不能代替;默认不通时显式试 --network pasta 或改用可达代理地址。Docker/远程 Podman 拉取使用引擎侧代理,不能用构建参数代替配置引擎,修改与重启独立安排。
|
|
62
|
+
|
|
63
|
+
Dockerfile 在 FROM 前声明 HAI_NODE_IMAGE / 适用的 HAI_NGINX_IMAGE,构建阶段用 HAI_NPM_REGISTRY 设置 COREPACK_NPM_REGISTRY、npm_config_registry、pnpm_config_registry。首个下载步骤验证代理 TCP 可达性,代理不进入运行 ENV。旧工程同步模板后重新构建;显式 tarball/Git URL 和原生包不由 registry 覆盖。SQLite 13 原生二进制随 npm 包交付,使用同一 npm 源,无独立下载步骤。deploy 的 npm 参数须与 --build 一起使用。
|
|
64
|
+
|
|
47
65
|
### SvelteKit Adapter 模式
|
|
48
66
|
|
|
49
|
-
CLI website模板默认Node SSR
|
|
67
|
+
CLI website模板默认Node SSR,只安装Core/Kit/UI;注释配置和环境变量参考不启用模块。后续增加的服务端业务依赖、初始化和API/actions须在静态发布前移除;在compile时使用`HAI_BUILD_STATIC=1`与可选`PUBLIC_BASE_PATH`切换完整静态预渲染,边界见本skill的静态网站说明;该模板没有使用下方VITE_ADAPTER工厂。静态产物用preview本机检查,上传build到Pages/CDN;生成的start/Docker配置属于普通SSR模式,不能用于运行静态HTML。切回SSR只需清除静态开关并重建,PUBLIC_BASE_PATH仍可按实际网站目录用于SSR或Docker;SvelteKit/Vite差异、制品格式和健康检查见[网站挂载目录](paths.md)。
|
|
50
68
|
|
|
51
69
|
仅在项目实际使用 SvelteKit 和 `@h-ai/kit/adapter` 时使用;Vite SPA 不配置 SvelteKit adapter,fullstack 各前端按自身 scripts 构建。
|
|
52
70
|
|
|
@@ -60,14 +78,14 @@ CLI website模板默认Node SSR。纯展示站点通过`--features none`创建
|
|
|
60
78
|
**Adapter 切换**:
|
|
61
79
|
|
|
62
80
|
```typescript
|
|
63
|
-
//
|
|
81
|
+
// vite.config.ts — SvelteKit 3 的配置在 Vite 插件中
|
|
64
82
|
import { createAdapter } from '@h-ai/kit/adapter'
|
|
83
|
+
import { sveltekit } from '@sveltejs/kit/vite'
|
|
84
|
+
import { defineConfig } from 'vite'
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
},
|
|
70
|
-
}
|
|
86
|
+
export default defineConfig({
|
|
87
|
+
plugins: [sveltekit({ adapter: await createAdapter(), paths: { relative: false } })],
|
|
88
|
+
})
|
|
71
89
|
```
|
|
72
90
|
|
|
73
91
|
- `VITE_ADAPTER=node`(默认)→ adapter-node(SSR)
|
|
@@ -141,7 +159,7 @@ import { reldb, ReldbConfigSchema } from '@h-ai/reldb'
|
|
|
141
159
|
import { iam, IamConfigSchema } from '@h-ai/iam'
|
|
142
160
|
|
|
143
161
|
// 放入应用已有的启动函数;并发防护和关闭钩子复用当前模板。
|
|
144
|
-
core.init({ configDir: './config' })
|
|
162
|
+
core.init({ configDir: './config', configSchemas: { db: ReldbConfigSchema, cache: CacheConfigSchema, iam: IamConfigSchema } })
|
|
145
163
|
const dbConfig = core.config.validate('db', ReldbConfigSchema)
|
|
146
164
|
if (!dbConfig.success) return dbConfig
|
|
147
165
|
const cacheConfig = core.config.validate('cache', CacheConfigSchema)
|
|
@@ -38,9 +38,13 @@ type: memory # memory | redis
|
|
|
38
38
|
### 2. 初始化与关闭
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
|
-
import {
|
|
41
|
+
import { core } from '@h-ai/core'
|
|
42
|
+
import { cache, CacheConfigSchema } from '@h-ai/cache'
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
const config = core.config.validate('cache', CacheConfigSchema)
|
|
45
|
+
if (!config.success) throw new Error(config.error.message)
|
|
46
|
+
const initialized = await cache.init(config.data)
|
|
47
|
+
if (!initialized.success) throw new Error(initialized.error.message)
|
|
44
48
|
// 使用后关闭
|
|
45
49
|
const closed = await cache.close()
|
|
46
50
|
if (!closed.success) throw new Error(closed.error.message)
|
|
@@ -20,6 +20,7 @@ description: "在 Android/iOS 应用中接入 @h-ai/capacitor 设备、相机、
|
|
|
20
20
|
- 先判断平台和插件可用性,复用已有 SPA 入口;原生 UI 变化验证权限拒绝、取消和返回路径。
|
|
21
21
|
- TokenStorage 在原生端使用安全插件;Web/插件不可用或存储失败时拒绝 Promise。调用方处理 setTokens/clear 失败并释放 loading;clear 的实例内屏蔽不替代服务端撤销。
|
|
22
22
|
- 原生打包前确认公开 API 地址的运行配置入口;fullstack App 使用 `public/runtime-config.js`,构建不会读取部署环境文件。这里只放公开地址,不能放 Token 或密钥。
|
|
23
|
+
- Linux Android 打包可使用包内的 SDK 镜像定义,按 [Android 容器打包](reference.md#android-容器打包) 执行应用已有打包入口。
|
|
23
24
|
|
|
24
25
|
## 按需参考
|
|
25
26
|
|
|
@@ -8,6 +8,24 @@
|
|
|
8
8
|
|
|
9
9
|
> **浏览器端 / 原生 App 专用。** 在 Capacitor 原生环境(Android/iOS)中提供完整能力;`createCapacitorTokenStorage()` 仅在原生环境持久化 token,纯 Web 不做不安全回退。`capacitor.preferences` 仍可用于普通偏好数据。
|
|
10
10
|
|
|
11
|
+
### Android 容器打包
|
|
12
|
+
|
|
13
|
+
在已安装依赖的工程根运行 `pnpm build --target app --platform android --runtime auto --debug`,CLI 自动准备构建镜像和 workspace。省略 `--debug` 构建发布 APK,需要已有 Android 工程,并通过进程或 CI 提供 `HAI_ANDROID_KEYSTORE`、`HAI_ANDROID_STORE_PASSWORD`、`HAI_ANDROID_KEY_ALIAS`、`HAI_ANDROID_KEY_PASSWORD`。密钥库只读挂载到独立签名容器。APK 归档在 `.hai/build/app/android/<构建类型>/<buildId>/`。
|
|
14
|
+
|
|
15
|
+
使用 `@h-ai/capacitor` 随包提供的 `images/android.Dockerfile`,从项目根目录执行。`image_dir` 指向本包的实际安装目录下的 `images`,例如 `apps/my-app/node_modules/@h-ai/capacitor/images`。
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
image_dir=apps/my-app/node_modules/@h-ai/capacitor/images
|
|
19
|
+
docker build --platform linux/amd64 -t hai-capacitor-android:36 \
|
|
20
|
+
-f "$image_dir/android.Dockerfile" "$image_dir"
|
|
21
|
+
docker run --rm -it --platform linux/amd64 \
|
|
22
|
+
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
23
|
+
-e GRADLE_USER_HOME=/tmp/.gradle -e ANDROID_USER_HOME=/tmp/.android \
|
|
24
|
+
-v "$PWD:/workspace" hai-capacitor-android:36 bash
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
镜像提供 Node.js 24、pnpm 12.11.2、JDK 21、Android SDK 36 和 Build Tools 35.0.0 / 36.0.0。用干净工作副本,在容器内执行 `pnpm install --frozen-lockfile`,构建应用依赖的本地 workspace 包,再执行项目已有的 Android APK 命令。使用 rootless Podman 时,在 `--user` 之外加上 `--userns=keep-id`。发布签名沿用应用配置,密钥库从外部只读挂载,口令由构建环境注入。构建机架构是 `linux/amd64`,APK 目标 ABI 由 Gradle 工程和原生插件决定。iOS 使用 macOS/Xcode。
|
|
28
|
+
|
|
11
29
|
## 适用场景
|
|
12
30
|
|
|
13
31
|
- Android/iOS 原生应用开发(Svelte 5 + Vite + Capacitor)
|
|
@@ -66,6 +84,8 @@ await apiClient.init({
|
|
|
66
84
|
|
|
67
85
|
任一 key 写入失败也会屏蔽整对凭据,避免 access 已落盘而 refresh 保存失败后的半完成登录。登录/注册保存失败后尝试 `auth.clear()` 清理部分数据;清理失败继续显示未确认删除的错误,不能只清界面身份或宣称持久删除成功。
|
|
68
86
|
|
|
87
|
+
同一模块内全部 TokenStorage 共享原生写入/删除队列;clear 等待此前写入再删除,后续显式写入才可建立新凭据。该顺序覆盖同模块多适配器,不跨独立 WebView/JavaScript 运行环境提供进程间协调。
|
|
88
|
+
|
|
69
89
|
#### Preferences 子操作
|
|
70
90
|
|
|
71
91
|
```typescript
|
|
@@ -240,6 +260,10 @@ export async function initCapacitor() {
|
|
|
240
260
|
|
|
241
261
|
### SPA 模式配置(必需)
|
|
242
262
|
|
|
263
|
+
CLI `build --target app` 归档的 APK 使用工程标识、平台、debug/release 和版本命名,如 `todo-android-debug-0.1.0.apk`;批次目录使用可读本地时间 `build-todo-app-android-debug-20261105-171924-62e4f1-0.1.0`。底层 Gradle 原始输出仍由 Gradle 管理,归档名称与 Core 应用显示名分别承担制品识别和用户展示职责。
|
|
264
|
+
|
|
265
|
+
生成应用的 `appName` 通过 `scripts/native-name.mjs` 解析 Core `name`,支持 `HAI_CORE_NAME` 及 dotenv 覆盖。Fullstack 从工程根读取环境文件,从 serv 读取 `_core.yml`。父级 `HAI_CORE` JSON 替换 YAML,省略 `name` 且没有生效的 `HAI_CORE_NAME` 时使用 Core 默认名 `hai Admin`。正式打包默认 prod,可由进程 `HAI_CORE_ENV` 切换。已有 Android/iOS 工程需同步平台显示名称后重打包。下面的示例使用固定名称演示最小 Capacitor 配置。
|
|
266
|
+
|
|
243
267
|
Capacitor 原生壳读取 `webDir` 指定的静态产物。纯 Vite 模板默认为 `dist`;SvelteKit 项目需配置静态适配器,输出可能是 `build`。构建器、容器与 `webDir` 必须一致,以下是 Vite 示例:
|
|
244
268
|
|
|
245
269
|
```typescript
|
|
@@ -61,6 +61,8 @@ lint/typecheck 只能执行已配置的规则。新增开发规范时区分可
|
|
|
61
61
|
- 若 E2E 稳定且耗时可控,可作为 required check。
|
|
62
62
|
- 若 E2E 依赖浏览器、服务、密钥或容易 flaky,先放到 `workflow_dispatch` / `schedule` / 独立 job。
|
|
63
63
|
- 应用级 E2E 使用:`pnpm --filter <app> test:e2e`。
|
|
64
|
+
- 依赖镜像或环境开关的关键 E2E,在 CI 中必须明确准备前置并启用;准备失败应阻断,不能让缺少配置导致的 skip 冒充验收。使用 Turbo 严格环境时,将影响测试执行范围的变量放入 `env`,同时传递变量并参与缓存身份,避免命中跳过用例时的缓存。
|
|
65
|
+
- 真实付费服务验收限用户已授权的可信分支和事件。可复用 workflow 的 secret 仅在调用方按事件显式传入,并只向定向验收步骤注入;安装、构建和普通根测试剥离该凭据。PR 继续使用夹具。成功和失败均可保留脱敏 JSON/HTML/截图证据,不能将密钥或认证头写入制品。
|
|
64
66
|
|
|
65
67
|
### hai-framework 依赖策略
|
|
66
68
|
|
|
@@ -77,6 +79,7 @@ lint/typecheck 只能执行已配置的规则。新增开发规范时区分可
|
|
|
77
79
|
- [ ] 使用 `concurrency` 取消同分支旧任务
|
|
78
80
|
- [ ] 不在 CI 日志打印 token、cookie、连接串、`.env` 内容
|
|
79
81
|
- [ ] release workflow 独立提权,只在 main/tag/manual 触发
|
|
82
|
+
- [ ] 自动推进默认发布标签时核对实际来源顺序;互斥锁不保证旧 CI 先完成,迟到版本不能回退默认标签,人工回退使用显式入口
|
|
80
83
|
- [ ] 失败日志可定位,不吞错、不 `|| true` 掩盖核心质量门禁
|
|
81
84
|
|
|
82
85
|
## Secret scan 策略
|
|
@@ -17,14 +17,19 @@ description: "配置 hai 应用启动、YAML/环境变量、日志、i18n 与 Ha
|
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
|
-
- Node 启动用 core.init({ configDir })
|
|
21
|
-
- 自动加载和监听配置目录内所有 YAML
|
|
20
|
+
- Node 启动用 core.init({ configDir, configSchemas }) 加载配置;configSchemas 登记所用模块的公开 Schema,使 YAML 未声明的合法可选字段也能接收细项环境变量。勿传 logging 覆盖 _core.yml 中的日志设置,除非任务明确要求。
|
|
21
|
+
- 自动加载和监听配置目录内所有 YAML;监听支持原地保存、原子替换和删除后重建,失败通知错误并保留最近成功的缓存,取消监听同时清除待执行重载。项目根 `hai.json` 的 `build` / `deploy` 由 CLI 读取,不进入运行时配置。单项 load 返回失败时记日志并继续,必需配置须 validate 并检查 success;目录扫描、日志设置等异常仍可能传播。
|
|
22
22
|
- 应用名称、版本、环境、默认语言和日志设置统一从 `_core.yml` 经 core.config 获取,日志使用 core.logger;UI 不另维护硬编码副本。SvelteKit layout / fullstack app.info 仅下发公开字段白名单;独立 Vite mobile-app 在 Node 构建时解析并注入公开默认值,修改这些编译默认值需要重新构建。静态 Web 的公开 API 地址等通过 `runtime-config.js` 覆盖时无需重建镜像,原生包在复制静态资源前准备配置入口。浏览器不加载 YAML,不接收完整模块配置、密钥或日志脱敏上下文。
|
|
23
|
-
- `HAI_<配置名>_<路径>`
|
|
24
|
-
-
|
|
23
|
+
- `HAI_<配置名>_<路径>` 支持父 JSON 基础与细项覆盖,同源先父后子,高来源整段替换清除低来源细项。camelCase 不拆词,约定变量优先于显式插值;宿主显式提供 `configSchemas` 识别 YAML 缺少的可选路径,模块执行业务校验。
|
|
24
|
+
- 环境文件约定:`.env.example` 完整列出字段用途、默认值、枚举和范围;`.env` 为公共基础;`.env.dev`、`.env.test`、`.env.prod` 为公共环境覆盖,公共文件提交 Git。`.env.local` 为共享私有配置,`.env.<mode>.local` 为模式私有配置,均不提交 Git。优先级为进程环境 > `.env.<mode>.local` > `.env.local` > `.env.<mode>` > `.env` > YAML;Vite 在 Node 配置阶段调用 `core.config.loadEnvFiles({ mode, envDir })` 并检查 success 后注入进程,Node 入口复用 `run-node.mjs` 的结构合并,避免原生多 `--env-file` 遗留低来源细项。`core.init` 不自行读取 dotenv。
|
|
25
25
|
- 配置 getter 与 ID/logger 方法不是 HaiResult;validate/load 等须检查 success。
|
|
26
26
|
- 日志可用 `{ error }` 记录原生 Error;两端先规范化诊断再字段/URL 脱敏,配置 redact 路径仍生效。默认包含 passwordHash、passwd、secretKey 等实际凭据字段;异常文本清理显式凭据标签和 URL,不保证识别无标签秘密。原异常不变,错误子图循环/深度用固定标记;业务隐私仍由调用方清理或遮蔽整条路径。
|
|
27
27
|
- 初始化前创建的默认、模块与嵌套 child Logger 在后续输出中应用当前全局 logging;configure/setLevel 也作用于已有实例。create 的显式 level/format/context 优先,未指定项动态继承。浏览器 json/pretty 与附加 redact 路径实际生效,消息文本仍由调用方脱敏。
|
|
28
|
+
- 脱敏副本不执行自定义 toJSON,不保留类原型;普通对象和类实例仅复制自有可枚举数据属性,跳过访问器,Date/URL 转为 ISO/脱敏字符串。副本用于诊断,不作为原类实例使用;仍只选取必要字段并清理无标签秘密。
|
|
29
|
+
- `core.async.parallel` 的并发数必须为大于 0 的安全整数,非法值拒绝为 RangeError,任务不会执行;debounce/throttle 保留回调参数类型。deepMerge 只递归普通对象,数组/Date/集合/类实例以后值覆盖;groupBy 支持 constructor 等原型同名键。
|
|
30
|
+
- 多模块应用可把各模块的 start/close 函数交给 `core.module.createRuntime`,创建时不执行;调用返回对象的 start 依次初始化,close 反序释放,均须检查 success。README/入门示例按[使用文档与证据](../hai-app-create/collaboration.md#使用文档与证据)说明目的并添加关键步骤注释。
|
|
31
|
+
- Core 运行能力按 core.xx 使用;使用入门逐项覆盖初始化及全部子功能,分别给出用途和展示输入、输出的带注释示例。无返回值的调用说明执行后的变化,工厂展示返回对象的使用结果,正文不保留讨论或逐句答复旧文案的痕迹,按共享文档要求核对。ok/err 是现有跨模块共用的结果构造器,类型、Schema 和错误常量也独立导入;说明这些明确例外,不臆造 core 上的方法。
|
|
32
|
+
- 解释环境覆盖时用 app.port/HAI_APP_PORT 与 `${PORT:3000}` 的实际输入展示最终值;说明只有 YAML 中的表达式会被替换,空字符串是已设置的值,混合文本仍为字符串,字符串 Schema 可保留前导零,环境/JSON 字面值不再展开。
|
|
28
33
|
|
|
29
34
|
错误提示与诊断分离:HTTP/UI 边界用 core.error.toPublic,仅公开 i18n 安全消息及 error.traceId;重新翻译后用 core.error.formatMessage 保留关联。ToastContainer 默认只显示可读说明,自动将编号移入排查信息图标,展开后可查看、复制并提供给客户支持或开发人员。前后端日志传完整 Error 和操作上下文,使用同一 requestId/traceId 并脱敏;禁止只写通用错误标题,也不能把 cause/ext/SQL/绑定值直接返回用户。数据库日志保留安全的驱动错误码、消息和堆栈。
|
|
30
35
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# hai-core — 详细用法
|
|
2
2
|
|
|
3
|
+
HTTP/UI 边界只对明确的有效 4xx 状态公开业务提示;错误缺少 `httpStatus` 或状态非法时按 500 返回安全提示。手写业务错误须提供完整错误定义或显式状态,不能依赖缺省状态展示原文。
|
|
4
|
+
|
|
3
5
|
从 [SKILL.md](SKILL.md) 选择主题后读取对应小节。代码片段展示 API 用法;省略的业务变量、schema、依赖与 return 上下文需由应用补齐。
|
|
4
6
|
|
|
5
7
|
> `@h-ai/core` 是 hai-framework 的基础模块,提供配置管理、结构化日志、国际化、HaiResult 错误模型、Zod 校验错误本地化与模块生命周期工具。所有其他模块均依赖 core。
|
|
@@ -11,8 +13,10 @@
|
|
|
11
13
|
| 能力 | Node.js | 浏览器 |
|
|
12
14
|
|------|---------|--------|
|
|
13
15
|
| `core.logger` | ✅ pino(结构化 JSON / pretty) | ✅ loglevel(DevTools console) |
|
|
14
|
-
| `core.config` | ✅
|
|
15
|
-
| `core.i18n` | ✅ | ✅ |
|
|
16
|
+
| `core.config` | ✅ 读取 YAML、校验和监听文件 | 不能读取/监听本地文件;文件方法返回 `SERVICE_UNAVAILABLE` |
|
|
17
|
+
| `core.i18n` | ✅ 每个请求保留自己的语言,等待异步操作后不受其他请求影响 | ✅ 设置页面语言;`runWithLocale` 调用抛错 |
|
|
18
|
+
| `core.context` | ✅ 等待异步操作后仍可读取当前请求编号,其他请求不会覆盖它 | 仅同步回调中可读取,await 后不保留 |
|
|
19
|
+
| `core.access` | ✅ 可信地址与请求策略 | 不提供 |
|
|
16
20
|
| `core.zodValidation` | ✅ | ✅ |
|
|
17
21
|
| `core.id` | ✅ | ✅ |
|
|
18
22
|
| 工具函数 | ✅ | ✅ |
|
|
@@ -37,13 +41,15 @@ import { core } from '@h-ai/core'
|
|
|
37
41
|
// 自动加载配置目录内所有 .yml / .yaml
|
|
38
42
|
core.init({ configDir: './config' })
|
|
39
43
|
|
|
40
|
-
//
|
|
44
|
+
// 可选启用配置监听
|
|
41
45
|
core.init({ configDir: './config', watchConfig: true })
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
项目根 `hai.json` 的 `build` / `deploy` 由 CLI 读取;Core 自动加载和监听配置目录内所有 YAML,不设置保留文件名。单项 `config.load` 返回失败时记录错误并继续,应用须 validate 必需配置并检查 success。目录扫描、日志设置等异常仍可能传播,不能把同步 void 当作所有配置加载成功的证明。
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
应用需要同时启动多个模块时,可将各模块的初始化/关闭函数交给 `core.module.createRuntime`。创建时只记录这些函数;调用返回对象的 start 按顺序初始化,close 反序释放,失败时清理已尝试启动的模块。两者返回 HaiResult,需检查 success。带注释的数据库/缓存示例见[应用启动与关闭](https://github.com/hai-series/hai-framework/blob/main/packages/core/README.md#应用启动与关闭)。
|
|
51
|
+
|
|
52
|
+
失败消息保留模块、错误码与配置字段路径,不回显配置值或 SDK 原始异常。启动配置错误附带原因和处理建议:`ext.fields` 配合 `HaiConfigError.CONFIG_ENV_VAR_MISSING.code` 明确提示环境变量未设置,`CONFIG_VALIDATION_ERROR.code` 或 Schema 异常提示校验未通过;只有字段路径时提示配置缺失或无效,不推断缺失。自定义启动钩子缺少必需变量时可抛出带上述错误码和 `ext: { fields: ['变量名'] }` 的异常。即使只打印 `error.message`,也能定位配置项并看到处理方式。
|
|
47
53
|
|
|
48
54
|
### 2. 使用功能
|
|
49
55
|
|
|
@@ -64,15 +70,15 @@ const formErrors = core.zodValidation.mapZodErrorToFormErrors(zodError, getMessa
|
|
|
64
70
|
|
|
65
71
|
| 方法 | 签名 | 说明 |
|
|
66
72
|
| ----------- | --------------------------------- | ----------------------------------------- |
|
|
67
|
-
| `core.init` | `(options?: CoreOptions) => void` |
|
|
73
|
+
| `core.init` | `(options?: CoreOptions) => void` | 同步扫描指定目录直属的全部运行时 YAML,无保留文件名 |
|
|
68
74
|
|
|
69
|
-
> `CoreOptions`:`{ logging?: Partial<LoggingConfig>, configDir?: string, watchConfig?: boolean }`
|
|
75
|
+
> `CoreOptions`:`{ logging?: Partial<LoggingConfig>, configDir?: string, configSchemas?: Readonly<Record<string, z.ZodType>>, watchConfig?: boolean }`
|
|
70
76
|
>
|
|
71
77
|
> 注意:core 没有 `close()` 方法,初始化后持续可用。
|
|
72
78
|
|
|
73
79
|
### 配置管理 — `core.config`
|
|
74
80
|
|
|
75
|
-
>
|
|
81
|
+
> 浏览器不能读取或监听本地配置文件,也没有 Node 专属的 resolve/mergeEnv/toEnv/parseEnv/stringifyEnv。`get()` 返回 `undefined`,`has()` 返回 `false`,`keys()` 返回 `[]`;`loadEnvFiles()` / `load()` / `validate()` / `reload()` 返回 `HaiCommonError.SERVICE_UNAVAILABLE`;`getOrThrow()` 抛错,`watch()` 立即回调不可用错误并返回不执行操作的取消函数。
|
|
76
82
|
|
|
77
83
|
| 方法 | 签名 | 说明 |
|
|
78
84
|
| ------------ | -------------------------------------------------------------- | ------------------------------ |
|
|
@@ -88,8 +94,14 @@ const formErrors = core.zodValidation.mapZodErrorToFormErrors(zodError, getMessa
|
|
|
88
94
|
| `unwatch` | `(name?: string) => void` | 停止配置文件监听 |
|
|
89
95
|
| `isWatching` | `(name: string) => boolean` | 检查是否正在监听某个配置 |
|
|
90
96
|
|
|
97
|
+
Node 的 `loadEnvFiles({ mode, envDir? })` 返回合并变量的 HaiResult,不修改进程;`core.init` 不自动读取 dotenv。`resolve`/`mergeEnv`/`toEnv`/`parseEnv`/`stringifyEnv` 用于配置生成,返回值可能含秘密,不能整体记录日志。完整来源与生成规则见[基础配置手册](https://github.com/hai-series/hai-framework/blob/main/docs/usage/configuration/runtime.md)。
|
|
98
|
+
|
|
99
|
+
变量替换示例:`app.yml` 写 `port: ${PORT:3000}`,PORT 未设置时读取默认值 3000,`PORT=4000` 时读取 4000;设置 `HAI_APP_PORT=5000` 则直接覆盖为 5000,不再要求 PORT 存在。Schema 的数字/布尔字段可把单个表达式转换为对应类型,字符串字段保留前导零;混合文本仍为字符串。空串表示已设置,不使用默认值;环境值里的 `${PORT}` 保持原样,不继续替换。分步解释与可运行示例见[Core 文件与环境配置](https://github.com/hai-series/hai-framework/blob/main/packages/core/README.md#文件与环境配置coreconfig)。
|
|
100
|
+
|
|
91
101
|
**`watch()` 回调处理**:
|
|
92
102
|
|
|
103
|
+
监听支持原地写入、原子替换及删除重建;校验/读取失败时保留最近成功的缓存并通知错误,恢复有效文件后继续重载。它只响应目标文件,取消监听同时清除待执行重载,不会因此重新初始化应用模块。
|
|
104
|
+
|
|
93
105
|
```typescript
|
|
94
106
|
// watch 回调签名:(config: T | null, error?: HaiError) => void
|
|
95
107
|
// - 成功重载时:传入新配置,error 为 undefined
|
|
@@ -118,8 +130,8 @@ unwatch()
|
|
|
118
130
|
|
|
119
131
|
- YAML 根、对象、数组和叶子节点均可映射为 `HAI_<配置名>_<YAML 路径>`;配置名和 key 转大写、层级以 `_` 分隔,camelCase 不拆词
|
|
120
132
|
- 例如 `core.config.load('ai', ...)` 加载的 `llm.apiKey` 对应 `HAI_AI_LLM_APIKEY`,`llm.models[0].temperature` 对应 `HAI_AI_LLM_MODELS_0_TEMPERATURE`
|
|
121
|
-
-
|
|
122
|
-
-
|
|
133
|
+
- 父 JSON 提供节点基础,随后细项覆盖;例如 HAI_DB 提供连接基础,HAI_DB_HOST 细化主机。数组 JSON 替换完整列表,索引仅更新已有元素,缺少列表或越界报错。Schema 决定字段类型,字符串保留前导零,变量/JSON 中的内容不再次插值。
|
|
134
|
+
- 所有入口统一来源:进程 > `.env.<mode>.local` > `.env.local` > `.env.<mode>` > `.env` > YAML。同源先父后子,与行序无关;高来源整段替换清除低来源细项。约定变量优先于显式 `${VAR}`。`configSchemas` 由宿主显式传入,Core 不导入业务模块;扫描只使用结构,模块负责完整校验。
|
|
123
135
|
|
|
124
136
|
- `${VAR}` — 读取 `process.env.VAR`;缺失则返回 `HaiConfigError.CONFIG_ENV_VAR_MISSING` 错误
|
|
125
137
|
- `${VAR:default}` — 读取 `process.env.VAR`;缺失则使用默认值
|
|
@@ -130,9 +142,16 @@ unwatch()
|
|
|
130
142
|
# config/_core.yml
|
|
131
143
|
name: my-app
|
|
132
144
|
env: development
|
|
145
|
+
defaultLocale: zh-CN
|
|
133
146
|
logging:
|
|
134
147
|
level: info
|
|
135
148
|
format: pretty
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`_core.yml` 只接受应用元数据和 logging;defaultLocale 由宿主读取后调用 `core.i18n.setGlobalLocale`,配置加载本身不切换语言。业务字段放独立 YAML,再用业务 Schema 校验,例如:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
# config/app.yml
|
|
136
155
|
feature:
|
|
137
156
|
debug: false
|
|
138
157
|
maxRetries: 3
|
|
@@ -320,10 +339,14 @@ core.id.isValidNanoId('abc123', 6) // true/false
|
|
|
320
339
|
|
|
321
340
|
`isDefined` / `isObject` / `isFunction` / `isPromise` / `isString` / `isNumber` / `isBoolean` / `isArray`
|
|
322
341
|
|
|
342
|
+
`isDefined` 同时排除 null 与 undefined;`isObject` 排除 null 和数组,但也接受 Date、Map 和类实例,不是纯对象判定。
|
|
343
|
+
|
|
323
344
|
#### 对象操作 — `core.object`
|
|
324
345
|
|
|
325
346
|
`deepClone` / `deepMerge` / `pick` / `omit` / `keys` / `values` / `entries` / `fromEntries`
|
|
326
347
|
|
|
348
|
+
`deepMerge` 只递归合并普通对象(含空原型对象);数组、Date、Map、Set 和类实例由后值覆盖,不修改输入对象。
|
|
349
|
+
|
|
327
350
|
```typescript
|
|
328
351
|
core.object.deepMerge(a, b, c) // 递归合并(防原型污染)
|
|
329
352
|
core.object.pick(obj, ['a', 'b'])
|
|
@@ -344,6 +367,8 @@ core.string.constantTimeEqual(a, b) // Node 使用原生等长比较;浏览
|
|
|
344
367
|
|
|
345
368
|
`unique` / `chunk` / `groupBy` / `first` / `last` / `flatten` / `compact` / `shuffle` / `intersection` / `difference`
|
|
346
369
|
|
|
370
|
+
`groupBy` 的原型同名键(如 constructor、__proto__)也会建立独立的自有分组,数字键和分组内的输入顺序保持原有行为。
|
|
371
|
+
|
|
347
372
|
```typescript
|
|
348
373
|
core.array.unique([1, 1, 2]) // [1, 2]
|
|
349
374
|
core.array.chunk([1, 2, 3, 4], 2) // [[1,2], [3,4]]
|
|
@@ -355,6 +380,8 @@ core.array.intersection([1, 2], [2, 3]) // [2]
|
|
|
355
380
|
|
|
356
381
|
`delay` / `withTimeout` / `retry` / `parallel` / `serial` / `debounce` / `throttle`
|
|
357
382
|
|
|
383
|
+
超时不取消底层任务;重试只处理抛错/reject,不识别失败 HaiResult。当前 `maxRetries` 按总尝试次数计算,至少执行一次。`parallel` 验证并发参数为大于 0 的安全整数,非法值拒绝为 RangeError,任何任务均不执行;正常结果按输入顺序返回。debounce/throttle 保留传入回调的参数类型。
|
|
384
|
+
|
|
358
385
|
```typescript
|
|
359
386
|
await core.async.delay(1000)
|
|
360
387
|
await core.async.withTimeout(fetch('/api'), 5000)
|
|
@@ -423,6 +450,7 @@ Core 模块定义了两组标准错误码:
|
|
|
423
450
|
| `HaiCommonError.VALIDATION_ERROR` | `hai:common:200` | 校验失败 |
|
|
424
451
|
| `HaiCommonError.INVALID_REQUEST` | `hai:common:201` | 请求无效 |
|
|
425
452
|
| `HaiCommonError.PARAMETER_MISSING` | `hai:common:202` | 参数缺失 |
|
|
453
|
+
| `HaiCommonError.RATE_LIMIT_EXCEEDED` | `hai:common:203` | 请求频率超限(HTTP 429) |
|
|
426
454
|
| `HaiCommonError.NOT_FOUND` | `hai:common:300` | 资源不存在 |
|
|
427
455
|
| `HaiCommonError.ALREADY_EXISTS` | `hai:common:301` | 资源已存在 |
|
|
428
456
|
| `HaiCommonError.CONFLICT` | `hai:common:302` | 冲突 |
|
|
@@ -474,7 +502,7 @@ if (!result.success) {
|
|
|
474
502
|
|
|
475
503
|
### 服务端初始化(SvelteKit hooks.server.ts)
|
|
476
504
|
|
|
477
|
-
在应用已有的 init.ts 中执行 core.init({ configDir: './config' })
|
|
505
|
+
在应用已有的 init.ts 中执行 core.init({ configDir: './config', configSchemas }),configSchemas 登记所用模块的公开 Schema,再校验配置并初始化依赖模块;hooks.server.ts 等待该启动流程完成后接入 kit.createHandle。完整依赖顺序与失败清理见 [hai-build](../hai-build/SKILL.md)。不要在一个文件里同时 import 和声明同名 initModules。
|
|
478
506
|
|
|
479
507
|
### 配置校验最佳实践
|
|
480
508
|
|
|
@@ -547,6 +575,8 @@ logger.debug('Data fetched', { count: items.length })
|
|
|
547
575
|
|
|
548
576
|
使用 `core.sanitize.sanitizeUrl(url)` 统一处理连接地址;它会遮蔽用户名、密码、敏感 query 参数和 OAuth 键值 fragment。非法地址返回固定占位符。`sanitizeSensitiveFields` 对 URL 类字段使用同一规则;自定义 matcher 同样匹配参数名,不能只遮蔽 userinfo 后记录含 token 的 URL。
|
|
549
577
|
|
|
578
|
+
脱敏副本不执行自定义 `toJSON()`,普通对象和类实例只复制自有可枚举数据属性,跳过访问器和原型;Date 转 ISO 字符串,URL 转脱敏字符串。原对象不变,副本用于诊断,不作为原类实例调用。仍需只选取必要字段,并明确清理未标注秘密和业务隐私。
|
|
579
|
+
|
|
550
580
|
```ts
|
|
551
581
|
const safeUrl = core.sanitize.sanitizeUrl("https://example.com/a?token=value&limit=10")
|
|
552
582
|
// https://example.com/a?token=[REDACTED]&limit=10
|
|
@@ -48,9 +48,9 @@ Node listener 使用 socket 对端;Fetch 必须传可信平台 `remoteAddress`
|
|
|
48
48
|
|
|
49
49
|
## 部署与公共边界
|
|
50
50
|
|
|
51
|
-
`.env.<环境>` 或私有覆盖可设置 `HAI_SERV_HTTP_ACCESS='{"file":"./config/security/request-access.yml"}'` / `HAI_KIT_ACCESS
|
|
51
|
+
`.env.<环境>` 或私有覆盖可设置 `HAI_SERV_HTTP_ACCESS='{"file":"./config/security/request-access.yml"}'` / `HAI_KIT_ACCESS=...`。同来源 `HAI_SERV` / `HAI_KIT` JSON 提供基础,`HAI_SERV_HTTP_ACCESS_FILE` / `HAI_KIT_ACCESS_FILE` 等细项继续覆盖;更高来源整段替换会清除低来源细项。
|
|
52
52
|
|
|
53
|
-
部署校验当前选中后端的配置文件,规范化快照放入交付包并参加 SHA-256 校验,容器通过 `/run/hai/request-access.yml` 只读挂载。工程原始引用不回写成容器路径。仅更新前端时保留后端原文件;失败恢复使用旧发布的 Compose
|
|
53
|
+
部署校验当前选中后端的配置文件,规范化快照放入交付包并参加 SHA-256 校验,容器通过 `/run/hai/request-access.yml` 只读挂载。工程原始引用不回写成容器路径。仅更新前端时保留后端原文件;失败恢复使用旧发布的 Compose/文件。待上传归档和 SDK 策略快照不能手改;替换快照时重新打包。已部署 Node 后端挂载的 `.env.runtime` 可按[部署指引](../hai-deploy/SKILL.md#后端运行配置重载)修改后重启,注意上层 JSON 优先级;不会自动重写 Nginx 或策略文件。镜像/配置回退不能撤销数据库和业务效果。
|
|
54
54
|
|
|
55
55
|
完整 Kit 配置只留在服务端;layout data 只下发 transport 等公开字段,不把策略、代理网段和文件路径用 `?raw` 打入浏览器。
|
|
56
56
|
|
|
@@ -5,7 +5,7 @@ description: "实现 SM2/SM3/SM4 加解密、验签、密码哈希,或通过 s
|
|
|
5
5
|
|
|
6
6
|
# hai-crypto
|
|
7
7
|
|
|
8
|
-
transport 仅支持 v2:SM2 发送方签名、独立密钥的 Encrypt-then-HMAC、HTTP 上下文/响应状态绑定及共享原子防重放。GET/HEAD、空 POST 与空响应也认证;无降级开关。TLS
|
|
8
|
+
transport 仅支持 v2:SM2 发送方签名、独立密钥的 Encrypt-then-HMAC、HTTP 上下文/响应状态绑定及共享原子防重放。GET/HEAD、空 POST 与空响应也认证;无降级开关。TLS 信任边界、可配时间窗口、nonce 持久化与实现示例见 [参考](reference.md)。
|
|
9
9
|
|
|
10
10
|
## 能力契约
|
|
11
11
|
|
|
@@ -32,7 +32,7 @@ transport 仅支持 v2:SM2 发送方签名、独立密钥的 Encrypt-then-HMAC
|
|
|
32
32
|
|
|
33
33
|
业务容量统一配置 transport.maxPlaintextBytes:UTF-8 JSON 明文字节,默认 1 MiB,有效整数范围 1 KiB~64 MiB,请求和响应共用。客户端从握手获取服务端预算,可选本地上限取较小值;resolveTransportBodyLimits 自动推导密文预算为两倍明文加 32 KiB。握手 JSON 16 KiB、公钥 4 KiB、认证头 16 KiB、路径 8 KiB、认证及防重放保持独立。原流超限/取消立即停止、取消并释放 reader;请求 413、响应返回加密的 500 容量错误,客户端识别 TransportBodyTooLargeError / hai:crypto:070。 自定义适配器使用 manager.bodyLimits 和 readTransportBody 消费原流,不 clone/tee;握手返回 maxPlaintextBytes。
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
|
|
36
36
|
|
|
37
37
|
## 按需参考
|
|
38
38
|
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
> `@h-ai/crypto` 提供非对称加密、哈希、对称加密与密码哈希能力,支持 Node.js 与浏览器双端。
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status
|
|
6
|
+
认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status。请求默认有效期为 5 分钟,客户端时间最多可领先服务器 30 秒。服务端可用 `maxRequestAgeSeconds`(1~600)和 `maxFutureSkewSeconds`(0~300)调整,单位为整数秒。共享 registry 原子消费 (clientId, requestId),nonce 保留到 issuedAt + 11 分钟,覆盖最长可配窗口及节点时钟差,存储故障拒绝请求。Redis 必须保留有效 nonce,使用 noeviction/可靠持久化策略;各节点时钟须同步。此能力防止同一传输消息重放,业务重试仍需自己的幂等键。
|
|
7
7
|
|
|
8
8
|
空 POST 也发送空明文的认证封装;GET/HEAD 将封装放在 X-Hai-Transport 头,204/304/HEAD 等无正文响应也使用同名头承载认证封装。其它请求/响应在 JSON 正文承载封装并带 X-Encrypted: true。客户端必须关联原请求上下文和 HTTP status,缺标记、旧格式或认证失败一律 reject;明文接口由 Serv/Kit 路径规则 `encryption: disabled` 或宿主 `excludePaths` 显式允许;调用方同步选择普通请求,API Client/Kit 包装器使用自己的 `excludePaths`。CORS 须允许并暴露 X-Hai-Transport。握手公钥的真实性依赖 TLS,不替代 IAM 业务授权。
|
|
9
9
|
|
|
10
10
|
## 运行环境
|
|
11
11
|
|
|
12
|
+
传输客户端 `destroy()` 会取消并废弃在途握手;旧握手成功或失败都不能恢复状态或影响下一代握手。单请求取消仍只停止该请求的等待。
|
|
13
|
+
|
|
12
14
|
**Node.js + 浏览器双端可用。** 浏览器端通过 `crypto.init()` 初始化(无需配置参数)。
|
|
13
15
|
|
|
14
16
|
| 能力 | Node.js | 浏览器 | 说明 |
|
|
@@ -31,6 +33,8 @@
|
|
|
31
33
|
|
|
32
34
|
## 使用步骤
|
|
33
35
|
|
|
36
|
+
并发调用 `close()` 会使尚未完成的 `init()` 失效,迟到初始化不会重新打开模块;等待该次初始化返回后可再次初始化。
|
|
37
|
+
|
|
34
38
|
### 1. 初始化与关闭
|
|
35
39
|
|
|
36
40
|
```typescript
|
|
@@ -214,7 +218,7 @@ const sharedServer = crypto.transport.createServer({
|
|
|
214
218
|
|
|
215
219
|
`serverKeyRing` 为 `{ activeKeyId, keys: { [id]: { publicKey, privateKey } } }`,ID 为 1–64 个字母、数字、下划线或连字符。根入口 `parseTransportServerKeyRing(secret)` 接受服务端 Secret JSON 或配置对象,返回 HaiResult,错误不附带原始 Secret 或解析片段。工厂验证每对公私钥并捕获快照;没有隐式随机私钥或 memory registry 回退。`keyStore` 也必填,生产用共享 Redis/数据库;开发/测试可显式 `createInMemoryKeyStore(capacity)`。
|
|
216
220
|
|
|
217
|
-
示例服务端读取 `process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING`,检查解析结果后仅传给服务端工厂;禁止写入普通 YAML、PUBLIC_/VITE_、日志或响应。CLI 创建服务端项目时在 `.env.dev/.env.test/.env.prod` 分别生成独立 ring;环境文件随项目提交,克隆用户共享对应环境配置。可由进程环境、私有覆盖或 Secret Manager 替换,同一环境所有节点使用同一 ring。
|
|
221
|
+
示例服务端读取 `process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING`,检查解析结果后仅传给服务端工厂;禁止写入普通 YAML、PUBLIC_/VITE_、日志或响应。CLI 创建服务端项目时在 `.env.dev.local/.env.test.local/.env.prod.local` 分别生成独立 ring;环境文件随项目提交,克隆用户共享对应环境配置。可由进程环境、私有覆盖或 Secret Manager 替换,同一环境所有节点使用同一 ring。
|
|
218
222
|
|
|
219
223
|
请求密文 `EncryptedRequestPayload` 携带协商的 `serverKeyId`;服务端只按 ID 选择私钥,未知/撤销 ID 拒绝,不尝试其它 key。轮换先向所有节点分发包含新旧版本的 ring,再切 activeKeyId 并滚动重建 manager;过渡期后删除旧 ID 并更新所有节点。旧客户端 destroy 后重协商;禁止自动重放非幂等请求。共享 ring + registry 支持跨节点与重启,不依赖 sticky session。
|
|
220
224
|
|
|
@@ -231,7 +235,7 @@ const manager = crypto.transport.createServer({ serverKeyRing: ring.data, keySto
|
|
|
231
235
|
|
|
232
236
|
业务容量统一配置 transport.maxPlaintextBytes:UTF-8 JSON 明文字节,默认 1 MiB,有效整数范围 1 KiB~64 MiB,请求和响应共用。客户端从握手获取服务端预算,可选本地上限取较小值;resolveTransportBodyLimits(limit, { requiredPlaintextBytes }) 合并模块需求:保留显式上限,不足时报配置冲突;省略时按需要推导,无模块需要仍为 1 MiB。Serv/Kit 的 auto 规范化为 undefined。密文预算为两倍明文加 32 KiB。握手 JSON 16 KiB、公钥 4 KiB、认证头 16 KiB、路径 8 KiB、认证及防重放保持独立。原流超限/取消立即停止、取消并释放 reader;请求 413、响应返回加密的 500 容量错误,客户端识别 TransportBodyTooLargeError / hai:crypto:070。 自定义适配器使用 manager.bodyLimits 和 readTransportBody 消费原流,不 clone/tee;握手返回 maxPlaintextBytes。
|
|
233
237
|
|
|
234
|
-
|
|
238
|
+
Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
|
|
235
239
|
|
|
236
240
|
自定义 HTTP 适配器检查失败原因 `error instanceof TransportTimeError && error.context` 后,使用 `createTransportErrorResponse(error, { manager, context: error.context })` 加密拒绝响应;不能根据未认证报文的时间戳给出可信时间诊断。
|
|
237
241
|
|