@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +86 -241
- package/README.ja.md +86 -241
- package/README.md +86 -241
- package/dist/{src-RDQ2EIBT.js → chunk-FI56GONY.js} +32 -2669
- package/dist/chunk-FI56GONY.js.map +1 -0
- package/dist/chunk-MBPB37FR.js +1348 -0
- package/dist/chunk-MBPB37FR.js.map +1 -0
- package/dist/{chunk-QYX6V7H2.js → chunk-ZFNBUF77.js} +347 -276
- package/dist/chunk-ZFNBUF77.js.map +1 -0
- package/dist/cli-config-templates-W2LLPMB6.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-W2LLPMB6.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-US5CCAJ4.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-US5CCAJ4.js.map} +1 -1
- package/dist/index.js +1304 -532
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-DSJJXM2F.js +3849 -0
- package/dist/src-DSJJXM2F.js.map +1 -0
- package/package.json +9 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/messages/ja-JP.json +38 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -6
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +3 -3
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +33 -8
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +0 -10
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +2 -1
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +24 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +8 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +68 -11
- package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/index.ts.hbs +10 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +2 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +6 -1
- package/templates/apps/api/playwright.config.ts.hbs +15 -7
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +7 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +30 -9
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +1 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +32 -8
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +0 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +8 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +6 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +9 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +3 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +16 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +7 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +58 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +13 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +40 -14
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +105 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +6 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +7 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +4 -4
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +8 -8
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +13 -14
- package/templates/apps/fullstack/playwright.config.ts.hbs +15 -6
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +16 -5
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +15 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +173 -14
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/messages/ja-JP.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +13 -5
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +23 -8
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +11 -3
- package/templates/base/playwright.config.ts +15 -4
- package/templates/base/src/hooks.server.ts.hbs +15 -6
- package/templates/base/src/lib/server/init.ts.hbs +139 -30
- package/templates/base/vite.config.ts.hbs +5 -3
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +11 -6
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +18 -25
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +4 -0
- package/templates/i18n/project.inlang/settings.json +1 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/run-e2e.mjs +51 -12
- package/templates/skills/AGENTS.md +7 -3
- package/templates/skills/bridges/admin/AGENTS.md +6 -2
- package/templates/skills/bridges/api/AGENTS.md +3 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +5 -1
- package/templates/skills/bridges/h5/AGENTS.md +6 -2
- package/templates/skills/bridges/mobile-app/AGENTS.md +6 -2
- package/templates/skills/bridges/website/AGENTS.md +6 -2
- package/templates/skills/hai-ai/SKILL.md +8 -2
- package/templates/skills/hai-ai/guide.md +134 -60
- package/templates/skills/hai-ai/reference.md +51 -11
- package/templates/skills/hai-api-client/SKILL.md +15 -160
- package/templates/skills/hai-api-client/reference.md +178 -0
- package/templates/skills/hai-api-contract/SKILL.md +16 -273
- package/templates/skills/hai-api-contract/reference.md +280 -0
- package/templates/skills/hai-app-create/SKILL.md +6 -2
- package/templates/skills/hai-app-create/development.md +24 -8
- package/templates/skills/hai-app-create/reference.md +7 -5
- package/templates/skills/hai-app-review/SKILL.md +7 -3
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +6 -3
- package/templates/skills/hai-app-tests/reference.md +5 -5
- package/templates/skills/hai-app-tests/validation.md +38 -0
- package/templates/skills/hai-audit/SKILL.md +24 -147
- package/templates/skills/hai-audit/reference.md +148 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +16 -13
- package/templates/skills/hai-cache/SKILL.md +20 -181
- package/templates/skills/hai-cache/reference.md +229 -0
- package/templates/skills/hai-capacitor/SKILL.md +7 -4
- package/templates/skills/hai-capacitor/reference.md +15 -9
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +7 -3
- package/templates/skills/hai-core/reference.md +25 -5
- package/templates/skills/hai-crypto/SKILL.md +22 -250
- package/templates/skills/hai-crypto/reference.md +310 -0
- package/templates/skills/hai-datapipe/SKILL.md +16 -195
- package/templates/skills/hai-datapipe/reference.md +204 -0
- package/templates/skills/hai-deploy/SKILL.md +24 -54
- package/templates/skills/hai-deploy/middleware.md +51 -43
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +25 -11
- package/templates/skills/hai-iam/reference.md +181 -154
- package/templates/skills/hai-kit/SKILL.md +35 -174
- package/templates/skills/hai-kit/reference.md +190 -0
- package/templates/skills/hai-payment/SKILL.md +33 -263
- package/templates/skills/hai-payment/reference.md +295 -0
- package/templates/skills/hai-pr-review/SKILL.md +21 -88
- package/templates/skills/hai-reach/SKILL.md +6 -3
- package/templates/skills/hai-reach/reference.md +83 -22
- package/templates/skills/hai-reldb/SKILL.md +6 -3
- package/templates/skills/hai-reldb/reference.md +68 -25
- package/templates/skills/hai-scheduler/SKILL.md +18 -195
- package/templates/skills/hai-scheduler/reference.md +216 -0
- package/templates/skills/hai-serv/SKILL.md +25 -10
- package/templates/skills/hai-serv/reference.md +123 -87
- package/templates/skills/hai-spec-implement/SKILL.md +5 -1
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +5 -0
- package/templates/skills/hai-spec-implement/workflow.md +5 -3
- package/templates/skills/hai-spec-review/SKILL.md +4 -0
- package/templates/skills/hai-storage/SKILL.md +23 -238
- package/templates/skills/hai-storage/reference.md +282 -0
- package/templates/skills/hai-ui/SKILL.md +17 -3
- package/templates/skills/hai-ui/guide.md +3 -3
- package/templates/skills/hai-ui/reference.md +26 -11
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +26 -0
- package/templates/skills/hai-ui-review/SKILL.md +30 -0
- package/templates/skills/hai-ui-review/checklist.md +22 -0
- package/templates/skills/hai-vecdb/SKILL.md +14 -10
- package/templates/skills/hai-vecdb/reference.md +33 -3
- package/dist/chunk-FWN5B54L.js +0 -468
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
**/.git
|
|
2
|
+
**/node_modules
|
|
3
|
+
**/.pnpm-store
|
|
4
|
+
**/.cache
|
|
5
|
+
**/.svelte-kit
|
|
6
|
+
**/.turbo
|
|
7
|
+
**/build
|
|
8
|
+
**/dist
|
|
9
|
+
**/coverage
|
|
10
|
+
**/playwright-report
|
|
11
|
+
**/test-results
|
|
12
|
+
**/src-tauri/target
|
|
13
|
+
**/hash-core/target
|
|
14
|
+
tmp
|
|
2
15
|
**/*.tar
|
|
3
16
|
.github
|
|
4
17
|
.agents
|
|
5
18
|
.claude
|
|
6
19
|
.codex
|
|
7
20
|
.opencode
|
|
8
|
-
.svelte-kit
|
|
9
|
-
.turbo
|
|
10
21
|
android
|
|
11
|
-
build
|
|
12
|
-
dist
|
|
13
22
|
ios
|
|
14
|
-
node_modules
|
|
15
|
-
playwright-report
|
|
16
|
-
test-results
|
|
17
23
|
**/data
|
|
18
24
|
*.log
|
|
19
25
|
# 仅保留公共基础 .env;分环境配置、私有覆盖及已有交付包不进入构建上下文。
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
1
2
|
import { mkdir } from 'node:fs/promises'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import process from 'node:process'
|
|
@@ -23,6 +24,16 @@ test('home page renders from browser', async ({ page }) => {
|
|
|
23
24
|
await expect(page.locator('main, section').first()).toBeVisible()
|
|
24
25
|
await expect(page.locator('header').first()).toBeVisible()
|
|
25
26
|
await expect(page.locator('footer').first()).toBeAttached()
|
|
27
|
+
// 大屏使用可用空间,窄屏由组件内部滚动,不能撑宽整个页面。
|
|
28
|
+
for (const width of [390, 1440, 1920, 2560, 3840]) {
|
|
29
|
+
await page.setViewportSize({ width, height: 960 })
|
|
30
|
+
const availableWidth = await page.evaluate(() => document.documentElement.clientWidth)
|
|
31
|
+
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
32
|
+
const shell = page.locator('.hai-mobile-shell')
|
|
33
|
+
if (await shell.count())
|
|
34
|
+
expect((await shell.boundingBox())!.width).toBeGreaterThan(availableWidth - 2)
|
|
35
|
+
}
|
|
36
|
+
await page.setViewportSize({ width: 390, height: 844 })
|
|
26
37
|
{{#if_eq appType 'h5'}}
|
|
27
38
|
// 在手机宽度验证导航真正切换路由,避免样式失效但首页渲染测试仍通过。
|
|
28
39
|
const nav = page.locator('nav')
|
|
@@ -57,7 +68,7 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
57
68
|
{{/if_eq}}
|
|
58
69
|
const evidence = process.env.HAI_AUTH_EVIDENCE_DIR || path.resolve('test-results/auth-screenshots')
|
|
59
70
|
await mkdir(evidence, { recursive: true })
|
|
60
|
-
const username = `scaf${
|
|
71
|
+
const username = `scaf${randomUUID().replaceAll('-', '').slice(0, 12)}`
|
|
61
72
|
const password = 'ScaffoldPassword123'
|
|
62
73
|
const authUrl = '{{#if isCapacitorApp}}/{{else}}/auth/login{{/if}}'
|
|
63
74
|
await page.goto(authUrl)
|
|
@@ -66,11 +77,15 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
66
77
|
await expect(page.locator('footer')).toBeVisible()
|
|
67
78
|
await page.screenshot({ path: path.join(evidence, '{{projectName}}-login.png'), fullPage: true })
|
|
68
79
|
await page.getByRole('{{#if isCapacitorApp}}button{{else}}link{{/if}}', { name: /立即注册|Register now/i }).click()
|
|
69
|
-
|
|
80
|
+
{{#unless isCapacitorApp}}
|
|
81
|
+
await expect(page).toHaveURL(/\/auth\/register(?:\?|$)/)
|
|
82
|
+
{{/unless}}
|
|
83
|
+
const registerForm = page.locator('.register-form')
|
|
84
|
+
await expect(registerForm.locator('#register-username')).toBeVisible()
|
|
70
85
|
await page.screenshot({ path: path.join(evidence, '{{projectName}}-register.png'), fullPage: true })
|
|
71
86
|
if (process.env.IAM_NOTIFICATION_URL) {
|
|
72
87
|
// 协议弹窗留在认证页,关闭后继续填写注册表单。
|
|
73
|
-
await
|
|
88
|
+
await registerForm.getByRole('button', { name: /用户协议|Terms of Service/i, exact: true }).click()
|
|
74
89
|
await expect(page.getByRole('dialog')).toBeVisible()
|
|
75
90
|
await expect(page.getByRole('dialog').getByRole('heading', { name: 'Configured registration agreement' })).toBeVisible()
|
|
76
91
|
await expect(page.locator('iframe')).toHaveCount(0)
|
|
@@ -78,11 +93,11 @@ test('configured IAM registration and password login', async ({ page, context })
|
|
|
78
93
|
await page.keyboard.press('Escape')
|
|
79
94
|
await expect(page.getByRole('dialog')).not.toBeVisible()
|
|
80
95
|
}
|
|
81
|
-
await
|
|
82
|
-
await
|
|
83
|
-
await
|
|
84
|
-
await
|
|
85
|
-
await
|
|
96
|
+
await registerForm.locator('#register-username').fill(username)
|
|
97
|
+
await registerForm.locator('#register-email').fill(`${username}@example.com`)
|
|
98
|
+
await registerForm.locator('#register-password').fill(password)
|
|
99
|
+
await registerForm.locator('#register-confirm-password').fill(password)
|
|
100
|
+
await registerForm.locator('button[type="submit"]').click()
|
|
86
101
|
await expect(page.locator('.register-form')).not.toBeVisible()
|
|
87
102
|
{{#unless isCapacitorApp}}
|
|
88
103
|
const me = await page.request.get('/api/auth/me')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "images": [{ "name": "app", "
|
|
1
|
+
{ "images": [{ "name": "app", "dockerfile": "images/app.Dockerfile", "containerPort": {{#if isSvelteOnlyApp}}8080{{else}}3000{{/if}} }] }
|
|
@@ -1,48 +1,51 @@
|
|
|
1
|
-
# syntax=docker/dockerfile:1.7
|
|
2
1
|
{{#if isSvelteOnlyApp}}
|
|
3
|
-
FROM node:24-bookworm-slim AS build
|
|
4
|
-
ARG SQLITE_PREBUILD_MIRROR
|
|
5
|
-
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
2
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
6
3
|
|
|
7
4
|
ENV PNPM_HOME=/pnpm
|
|
8
5
|
ENV PATH=$PNPM_HOME:$PATH
|
|
9
6
|
ENV CI=true
|
|
7
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
10
8
|
WORKDIR /workspace
|
|
11
9
|
|
|
12
|
-
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate
|
|
10
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
11
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
12
|
+
COPY images/dependencies/ ./
|
|
13
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
13
14
|
COPY . .
|
|
14
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch
|
|
15
15
|
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
16
16
|
RUN pnpm compile
|
|
17
17
|
|
|
18
|
-
FROM nginxinc/nginx-unprivileged:1.29-alpine AS runtime
|
|
18
|
+
FROM nginxinc/nginx-unprivileged:1.29.8-alpine AS runtime
|
|
19
19
|
|
|
20
|
-
COPY --chmod=755
|
|
21
|
-
COPY
|
|
20
|
+
COPY --chmod=755 images/web/19-runtime-config.sh /docker-entrypoint.d/19-hai-runtime-config.sh
|
|
21
|
+
COPY images/web/nginx.conf /etc/nginx/conf.d/default.conf
|
|
22
22
|
COPY --from=build /workspace/dist /usr/share/nginx/html
|
|
23
23
|
|
|
24
24
|
EXPOSE 8080
|
|
25
25
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
26
26
|
CMD wget -q -O /dev/null http://127.0.0.1:8080/health || exit 1
|
|
27
27
|
{{else}}
|
|
28
|
-
FROM node:24-bookworm-slim AS build
|
|
29
|
-
ARG SQLITE_PREBUILD_MIRROR
|
|
28
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
29
|
+
ARG SQLITE_PREBUILD_MIRROR=
|
|
30
30
|
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
31
31
|
|
|
32
32
|
ENV PNPM_HOME=/pnpm
|
|
33
33
|
ENV PATH=$PNPM_HOME:$PATH
|
|
34
34
|
ENV CI=true
|
|
35
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
35
36
|
WORKDIR /workspace
|
|
36
37
|
|
|
37
|
-
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate
|
|
38
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
39
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
40
|
+
COPY images/dependencies/ ./
|
|
41
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
38
42
|
COPY . .
|
|
39
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch
|
|
40
43
|
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
41
44
|
RUN pnpm compile
|
|
42
|
-
RUN if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
43
|
-
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm pm deploy --filter {{projectName}} --prod /prod/app
|
|
45
|
+
RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
46
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{projectName}} --prod /prod/app
|
|
44
47
|
|
|
45
|
-
FROM node:24-bookworm-slim AS runtime-base
|
|
48
|
+
FROM node:24.22.1-bookworm-slim AS runtime-base
|
|
46
49
|
|
|
47
50
|
ENV NODE_ENV=production
|
|
48
51
|
ENV HOST=0.0.0.0
|
|
@@ -58,5 +61,4 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
|
58
61
|
CMD node -e "fetch('http://127.0.0.1:'+process.env.PORT+'/').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
59
62
|
CMD ["node", "build"]
|
|
60
63
|
|
|
61
|
-
FROM runtime-base AS runtime
|
|
62
64
|
{{/if}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"engines": { "node": "^22.22.2 || ^24.15.0 || >=26.0.0" },
|
|
3
3
|
"name": "{{projectName}}",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.68",
|
|
5
5
|
"private": true,
|
|
6
6
|
"packageManager": "pnpm@12.11.2",
|
|
7
7
|
"description": "{{#if_eq appType 'admin'}}Enterprise management system built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'website'}}Enterprise website built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'h5'}}Mobile H5 app built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'api'}}REST API service built with hai Agent Framework{{/if_eq}}{{#if_eq appType 'mobile-app'}}Mobile app built with hai Agent Framework{{/if_eq}}",
|
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
"dev:prod": "vite dev --mode prod",
|
|
26
26
|
"compile": "{{#if hasI18n}}pnpm paraglide:compile && {{/if}}vite build",
|
|
27
27
|
"build": "hai build",
|
|
28
|
+
{{#if features.deploy}}
|
|
28
29
|
"deploy": "hai deploy",
|
|
30
|
+
{{/if}}
|
|
29
31
|
{{#unless isSvelteOnlyApp}}
|
|
30
32
|
"start": "node --env-file=.env --env-file=.env.prod --env-file-if-exists=.env.local build/index.js",
|
|
31
33
|
"start:test": "node --env-file=.env --env-file=.env.test --env-file-if-exists=.env.local build/index.js",
|
|
@@ -57,6 +59,9 @@
|
|
|
57
59
|
{{#if isSvelteOnlyApp}}
|
|
58
60
|
"@h-ai/api-client": "{{haiPackageSpecifier}}",
|
|
59
61
|
"@h-ai/capacitor": "{{haiPackageSpecifier}}",
|
|
62
|
+
{{#if features.crypto}}
|
|
63
|
+
"@h-ai/crypto": "{{haiPackageSpecifier}}",
|
|
64
|
+
{{/if}}
|
|
60
65
|
"@h-ai/ui": "{{haiPackageSpecifier}}"
|
|
61
66
|
{{else}}
|
|
62
67
|
"@h-ai/core": "{{haiPackageSpecifier}}",
|
|
@@ -73,16 +78,19 @@
|
|
|
73
78
|
"@h-ai/payment": "{{haiPackageSpecifier}}"{{/if}}{{#if features.vecdb}},
|
|
74
79
|
"@h-ai/vecdb": "{{haiPackageSpecifier}}"{{/if}}{{#if features.datapipe}},
|
|
75
80
|
"@h-ai/datapipe": "{{haiPackageSpecifier}}"{{/if}}{{#if features.scheduler}},
|
|
76
|
-
"@h-ai/scheduler": "{{haiPackageSpecifier}}"{{/if}}{{#if features.
|
|
77
|
-
"@h-ai/deploy": "{{haiPackageSpecifier}}"{{/if}}{{#if features.api-client}},
|
|
81
|
+
"@h-ai/scheduler": "{{haiPackageSpecifier}}"{{/if}}{{#if features.api-client}},
|
|
78
82
|
"@h-ai/api-client": "{{haiPackageSpecifier}}"{{/if}}{{#if features.capacitor}},
|
|
79
83
|
"@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/if}}{{#if isCapacitorApp}}{{#unless features.api-client}},
|
|
80
84
|
"@h-ai/api-client": "{{haiPackageSpecifier}}"{{/unless}}{{#unless features.capacitor}},
|
|
81
85
|
"@h-ai/capacitor": "{{haiPackageSpecifier}}"{{/unless}}{{/if}},
|
|
86
|
+
"sm-crypto": "0.5.7",
|
|
82
87
|
"zod": "{{versions.zod}}"
|
|
83
88
|
{{/if}}
|
|
84
89
|
},
|
|
85
90
|
"devDependencies": {
|
|
91
|
+
{{#if features.deploy}}
|
|
92
|
+
"@h-ai/deploy": "{{haiPackageSpecifier}}",
|
|
93
|
+
{{/if}}
|
|
86
94
|
"@h-ai/cli": "{{haiPackageSpecifier}}",
|
|
87
95
|
{{#if isSvelteOnlyApp}}
|
|
88
96
|
"@h-ai/core": "{{haiPackageSpecifier}}",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from 'node:process'
|
|
2
2
|
import { defineConfig } from '@playwright/test'
|
|
3
|
+
import { createE2eNotificationEnv, createE2eServerEnv, createE2eTransportRing } from './scripts/e2e-env.js'
|
|
3
4
|
|
|
4
5
|
const DEFAULT_BASE_URL = 'http://localhost:4173'
|
|
5
6
|
const baseURL = process.env.BASE_URL || DEFAULT_BASE_URL
|
|
@@ -35,9 +36,9 @@ function resolvePortFromBaseUrl(urlString: string, fallbackPort: string): string
|
|
|
35
36
|
*/
|
|
36
37
|
export default defineConfig({
|
|
37
38
|
testDir: './e2e',
|
|
38
|
-
fullyParallel:
|
|
39
|
+
fullyParallel: true,
|
|
39
40
|
forbidOnly: !!process.env.CI,
|
|
40
|
-
retries:
|
|
41
|
+
retries: 0,
|
|
41
42
|
workers: 2,
|
|
42
43
|
reporter: 'list',
|
|
43
44
|
timeout: 30_000,
|
|
@@ -45,13 +46,23 @@ export default defineConfig({
|
|
|
45
46
|
use: {
|
|
46
47
|
baseURL,
|
|
47
48
|
channel: 'chrome',
|
|
48
|
-
trace: 'on-
|
|
49
|
+
trace: 'retain-on-failure',
|
|
49
50
|
screenshot: 'only-on-failure',
|
|
50
51
|
},
|
|
51
52
|
|
|
52
53
|
webServer: {
|
|
53
54
|
command: `pnpm build && pnpm preview --host ${previewHost} --port ${previewPort} --strictPort`,
|
|
54
|
-
env: {
|
|
55
|
+
env: createE2eServerEnv({
|
|
56
|
+
...createE2eNotificationEnv(),
|
|
57
|
+
HAI_CORE_ENV: 'test',
|
|
58
|
+
BASE_URL: baseURL,
|
|
59
|
+
PUBLIC_API_BASE: process.env.PUBLIC_API_BASE || '',
|
|
60
|
+
HAI_DB: JSON.stringify({ type: 'sqlite', database: `${process.env.HAI_E2E_DATA_ROOT}/app.db` }),
|
|
61
|
+
HAI_CACHE: JSON.stringify({ type: 'memory' }),
|
|
62
|
+
HAI_STORAGE: JSON.stringify({ type: 'local', root: `${process.env.HAI_E2E_DATA_ROOT}/uploads` }),
|
|
63
|
+
HAI_VECDB: JSON.stringify({ type: 'lancedb', path: `${process.env.HAI_E2E_DATA_ROOT}/vecdb` }),
|
|
64
|
+
HAI_CRYPTO_TRANSPORT_SERVERKEYRING: createE2eTransportRing(),
|
|
65
|
+
}),
|
|
55
66
|
url: baseURL,
|
|
56
67
|
reuseExistingServer: false,
|
|
57
68
|
timeout: 180_000,
|
|
@@ -5,10 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { Handle } from '@sveltejs/kit'
|
|
8
|
+
{{#if features.cache}}
|
|
9
|
+
import process from 'node:process'
|
|
10
|
+
{{/if}}
|
|
8
11
|
{{#if hasI18n}}
|
|
9
12
|
import { paraglideMiddleware } from '$lib/paraglide/server.js'
|
|
10
13
|
{{/if}}
|
|
11
14
|
import { initApp } from '$lib/server/init.js'
|
|
15
|
+
{{#if features.cache}}
|
|
16
|
+
import { cache } from '@h-ai/cache'
|
|
17
|
+
{{/if}}
|
|
12
18
|
import { core } from '@h-ai/core'
|
|
13
19
|
{{#if features.iam}}
|
|
14
20
|
import { iam } from '@h-ai/iam'
|
|
@@ -54,16 +60,14 @@ const i18nHandle: Handle = async ({ event, resolve }) => {
|
|
|
54
60
|
if (event.url.pathname.startsWith('/api/')) {
|
|
55
61
|
const locale = event.cookies.get('PARAGLIDE_LOCALE') ?? '{{defaultLocale}}'
|
|
56
62
|
event.locals.locale = locale
|
|
57
|
-
kit.i18n.
|
|
58
|
-
return resolve(event)
|
|
63
|
+
return kit.i18n.runWithLocale(locale, () => resolve(event))
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
return paraglideMiddleware(event.request, async ({ locale }) => {
|
|
62
67
|
event.locals.locale = locale
|
|
63
|
-
kit.i18n.
|
|
64
|
-
return resolve(event, {
|
|
68
|
+
return kit.i18n.runWithLocale(locale, () => resolve(event, {
|
|
65
69
|
transformPageChunk: ({ html }) => html.replace('%lang%', locale),
|
|
66
|
-
})
|
|
70
|
+
}))
|
|
67
71
|
})
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -95,7 +99,12 @@ const haiHandle = kit.createHandle({
|
|
|
95
99
|
operations: () => iam.auth,
|
|
96
100
|
},
|
|
97
101
|
{{/if}}
|
|
98
|
-
|
|
102
|
+
{{#if features.cache}}
|
|
103
|
+
rateLimit: { windowMs: 60000, maxRequests: 200, store: kit.middleware.createCacheRateLimitStore(cache, { namespace: '{{projectName}}', allowMemory: process.env.NODE_ENV !== 'production' || process.env.HAI_E2E === '1' }) },
|
|
104
|
+
{{else}}
|
|
105
|
+
// 未选择cache能力时不启用限流;启用前须接入共享原子store。
|
|
106
|
+
rateLimit: false,
|
|
107
|
+
{{/if}}
|
|
99
108
|
logging: { logBody: false },
|
|
100
109
|
onError: (error: unknown, _event) => {
|
|
101
110
|
core.logger.error('Request error:', { error })
|
|
@@ -33,7 +33,7 @@ import type { AIConfigInput } from '@h-ai/ai'
|
|
|
33
33
|
{{#if features.audit}}
|
|
34
34
|
import type { AuditInitConfigInput } from '@h-ai/audit'
|
|
35
35
|
{{/if}}
|
|
36
|
-
import type { CoreConfig } from '@h-ai/core'
|
|
36
|
+
import type { CoreConfig, HaiResult } from '@h-ai/core'
|
|
37
37
|
{{#if features.iam}}
|
|
38
38
|
import type { IamConfigSettingsInput } from '@h-ai/iam'
|
|
39
39
|
{{/if}}
|
|
@@ -44,7 +44,7 @@ import type { ReachConfigInput } from '@h-ai/reach'
|
|
|
44
44
|
import type { StorageConfigInput } from '@h-ai/storage'
|
|
45
45
|
{{/if}}
|
|
46
46
|
{{#if features.ai}}
|
|
47
|
-
import { ai } from '@h-ai/ai'
|
|
47
|
+
import { ai, AIConfigSchema } from '@h-ai/ai'
|
|
48
48
|
{{/if}}
|
|
49
49
|
{{#if features.audit}}
|
|
50
50
|
import { audit, AuditInitConfigSchema } from '@h-ai/audit'
|
|
@@ -68,6 +68,9 @@ import { reldb, ReldbConfigSchema } from '@h-ai/reldb'
|
|
|
68
68
|
{{#if features.storage}}
|
|
69
69
|
import { storage, StorageConfigSchema } from '@h-ai/storage'
|
|
70
70
|
{{/if}}
|
|
71
|
+
{{#if features.vecdb}}
|
|
72
|
+
import { vecdb, VecdbConfigSchema } from '@h-ai/vecdb'
|
|
73
|
+
{{/if}}
|
|
71
74
|
|
|
72
75
|
{{#if features.db}}
|
|
73
76
|
type DbConfigInput = Parameters<typeof reldb.init>[0]
|
|
@@ -77,14 +80,46 @@ type CacheConfigInput = Parameters<typeof cache.init>[0]
|
|
|
77
80
|
{{/if}}
|
|
78
81
|
|
|
79
82
|
let initialized = false
|
|
83
|
+
let initializing: Promise<void> | undefined
|
|
84
|
+
let closing: Promise<void> | undefined
|
|
85
|
+
let cleanupPending = false
|
|
80
86
|
|
|
81
87
|
/**
|
|
82
88
|
* 初始化应用
|
|
83
89
|
*/
|
|
84
90
|
export async function initApp(): Promise<void> {
|
|
91
|
+
if (closing)
|
|
92
|
+
await closing
|
|
85
93
|
if (initialized)
|
|
86
94
|
return
|
|
95
|
+
if (!initializing) {
|
|
96
|
+
initializing = (async () => {
|
|
97
|
+
if (cleanupPending)
|
|
98
|
+
await cleanupModules()
|
|
99
|
+
cleanupPending = true
|
|
100
|
+
try {
|
|
101
|
+
await doInit()
|
|
102
|
+
initialized = true
|
|
103
|
+
cleanupPending = false
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
try {
|
|
107
|
+
await cleanupModules()
|
|
108
|
+
}
|
|
109
|
+
catch (cleanupError) {
|
|
110
|
+
throw new AggregateError([error, cleanupError], 'Application startup and cleanup failed')
|
|
111
|
+
}
|
|
112
|
+
throw error
|
|
113
|
+
}
|
|
114
|
+
})().finally(() => {
|
|
115
|
+
initializing = undefined
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
return initializing
|
|
119
|
+
}
|
|
87
120
|
|
|
121
|
+
/** 实际启动只有一个调用者执行,失败由外层检查清理结果后再抛出。 */
|
|
122
|
+
async function doInit(): Promise<void> {
|
|
88
123
|
// 1. 加载配置
|
|
89
124
|
core.init({
|
|
90
125
|
configDir: './config',
|
|
@@ -115,6 +150,16 @@ export async function initApp(): Promise<void> {
|
|
|
115
150
|
if (!auditValidation.success)
|
|
116
151
|
throw new Error(`Audit config invalid: ${auditValidation.error.message}`)
|
|
117
152
|
{{/if}}
|
|
153
|
+
{{#if features.vecdb}}
|
|
154
|
+
const vectorValidation = core.config.validate('vecdb', VecdbConfigSchema)
|
|
155
|
+
if (!vectorValidation.success)
|
|
156
|
+
throw new Error(`VecDB config invalid: ${vectorValidation.error.message}`)
|
|
157
|
+
{{/if}}
|
|
158
|
+
{{#if features.ai}}
|
|
159
|
+
const aiValidation = core.config.validate('ai', AIConfigSchema)
|
|
160
|
+
if (!aiValidation.success)
|
|
161
|
+
throw new Error(`AI config invalid: ${aiValidation.error.message}`)
|
|
162
|
+
{{/if}}
|
|
118
163
|
|
|
119
164
|
{{#if features.db}}
|
|
120
165
|
// ─── 获取配置 ───
|
|
@@ -130,13 +175,16 @@ export async function initApp(): Promise<void> {
|
|
|
130
175
|
const auditConfig = core.config.getOrThrow<AuditInitConfigInput>('audit')
|
|
131
176
|
{{/if}}
|
|
132
177
|
{{#if features.storage}}
|
|
133
|
-
const storageConfig = core.config.
|
|
178
|
+
const storageConfig = core.config.getOrThrow<StorageConfigInput>('storage')
|
|
134
179
|
{{/if}}
|
|
135
180
|
{{#if features.ai}}
|
|
136
|
-
const aiConfig = core.config.
|
|
181
|
+
const aiConfig = core.config.getOrThrow<AIConfigInput>('ai')
|
|
182
|
+
{{/if}}
|
|
183
|
+
{{#if features.vecdb}}
|
|
184
|
+
const vectorConfig = core.config.getOrThrow<Parameters<typeof vecdb.init>[0]>('vecdb')
|
|
137
185
|
{{/if}}
|
|
138
186
|
{{#if features.reach}}
|
|
139
|
-
const reachConfig = core.config.
|
|
187
|
+
const reachConfig = core.config.getOrThrow<ReachConfigInput>('reach')
|
|
140
188
|
{{/if}}
|
|
141
189
|
|
|
142
190
|
{{#if features.db}}
|
|
@@ -166,26 +214,25 @@ export async function initApp(): Promise<void> {
|
|
|
166
214
|
{{/if}}
|
|
167
215
|
|
|
168
216
|
{{#if features.storage}}
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
217
|
+
const storageValidation = core.config.validate('storage', StorageConfigSchema)
|
|
218
|
+
if (!storageValidation.success)
|
|
219
|
+
throw new Error(`Storage config invalid: ${storageValidation.error.message}`)
|
|
220
|
+
const storageResult = await storage.init(storageConfig)
|
|
221
|
+
if (!storageResult.success)
|
|
222
|
+
throw new Error(`Storage initialization failed: ${storageResult.error.message}`)
|
|
223
|
+
{{/if}}
|
|
224
|
+
|
|
225
|
+
{{#if features.vecdb}}
|
|
226
|
+
// 共享向量存储必须在 AI 选择默认 Store 之前就绪。
|
|
227
|
+
const vectorResult = await vecdb.init(vectorConfig)
|
|
228
|
+
if (!vectorResult.success)
|
|
229
|
+
throw new Error(`VecDB initialization failed: ${vectorResult.error.message}`)
|
|
179
230
|
{{/if}}
|
|
180
231
|
|
|
181
232
|
{{#if features.reach}}
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
if (!reachResult.success) {
|
|
186
|
-
core.logger.warn('Reach initialization failed', { error: reachResult.error.message })
|
|
187
|
-
}
|
|
188
|
-
}
|
|
233
|
+
const reachResult = await reach.init(reachConfig)
|
|
234
|
+
if (!reachResult.success)
|
|
235
|
+
throw new Error(`Reach initialization failed: ${reachResult.error.message}`)
|
|
189
236
|
{{/if}}
|
|
190
237
|
|
|
191
238
|
{{#if features.iam}}
|
|
@@ -205,15 +252,77 @@ export async function initApp(): Promise<void> {
|
|
|
205
252
|
{{/if}}
|
|
206
253
|
|
|
207
254
|
{{#if features.ai}}
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
core.logger.warn('AI initialization failed', { error: aiResult.error.message })
|
|
213
|
-
}
|
|
214
|
-
}
|
|
255
|
+
// 显式启用的 AI 不得在持久 Store 不可用时降级为启动成功。
|
|
256
|
+
const aiResult = await ai.init(aiConfig)
|
|
257
|
+
if (!aiResult.success)
|
|
258
|
+
throw new Error(`AI initialization failed: ${aiResult.error.message}`)
|
|
215
259
|
{{/if}}
|
|
216
260
|
|
|
217
|
-
initialized = true
|
|
218
261
|
core.logger.info('Application initialized', { app: core.config.getOrThrow<CoreConfig>('core').name })
|
|
219
262
|
}
|
|
263
|
+
|
|
264
|
+
/** 逆序关闭所有应用拥有的模块;保留失败义务以便下一次重试。 */
|
|
265
|
+
async function cleanupModules(): Promise<void> {
|
|
266
|
+
const failures: unknown[] = []
|
|
267
|
+
const closers: Array<() => Promise<HaiResult<void> | void>> = [
|
|
268
|
+
{{#if features.ai}}
|
|
269
|
+
() => ai.close(),
|
|
270
|
+
{{/if}}
|
|
271
|
+
{{#if features.audit}}
|
|
272
|
+
() => audit.close(),
|
|
273
|
+
{{/if}}
|
|
274
|
+
{{#if features.iam}}
|
|
275
|
+
() => iam.close(),
|
|
276
|
+
{{/if}}
|
|
277
|
+
{{#if features.reach}}
|
|
278
|
+
() => reach.close(),
|
|
279
|
+
{{/if}}
|
|
280
|
+
{{#if features.vecdb}}
|
|
281
|
+
() => vecdb.close(),
|
|
282
|
+
{{/if}}
|
|
283
|
+
{{#if features.storage}}
|
|
284
|
+
() => storage.close(),
|
|
285
|
+
{{/if}}
|
|
286
|
+
{{#if features.cache}}
|
|
287
|
+
() => cache.close(),
|
|
288
|
+
{{/if}}
|
|
289
|
+
{{#if features.db}}
|
|
290
|
+
() => reldb.close(),
|
|
291
|
+
{{/if}}
|
|
292
|
+
{{#if features.crypto}}
|
|
293
|
+
() => crypto.close(),
|
|
294
|
+
{{/if}}
|
|
295
|
+
]
|
|
296
|
+
for (const close of closers) {
|
|
297
|
+
try {
|
|
298
|
+
const result = await close()
|
|
299
|
+
if (result && !result.success)
|
|
300
|
+
failures.push(result.error)
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
failures.push(error)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
initialized = false
|
|
307
|
+
cleanupPending = failures.length > 0
|
|
308
|
+
if (cleanupPending)
|
|
309
|
+
throw new AggregateError(failures, 'Application cleanup failed; retry closeApp()')
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* 关闭应用模块并与并发启动/关闭合并;清理失败抛错并允许重试。
|
|
314
|
+
* @example `await closeApp()`
|
|
315
|
+
*/
|
|
316
|
+
export async function closeApp(): Promise<void> {
|
|
317
|
+
if (!closing) {
|
|
318
|
+
const pending = initializing
|
|
319
|
+
closing = (async () => {
|
|
320
|
+
if (pending)
|
|
321
|
+
await pending.catch(() => undefined)
|
|
322
|
+
await cleanupModules()
|
|
323
|
+
})().finally(() => {
|
|
324
|
+
closing = undefined
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
return closing
|
|
328
|
+
}
|
|
@@ -27,7 +27,7 @@ import { publicRuntimeConfig } from './scripts/vite-runtime-config.js'
|
|
|
27
27
|
{{/if}}
|
|
28
28
|
|
|
29
29
|
export default defineConfig(({ mode, command }) => {
|
|
30
|
-
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir: process.cwd() })
|
|
30
|
+
const envResult = command === 'build' || process.env.HAI_E2E === '1' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir: process.cwd() })
|
|
31
31
|
if (!envResult.success)
|
|
32
32
|
throw new Error(envResult.error.message)
|
|
33
33
|
const env = envResult.data
|
|
@@ -53,7 +53,7 @@ export default defineConfig(({ mode, command }) => {
|
|
|
53
53
|
{{/if}}
|
|
54
54
|
plugins: [
|
|
55
55
|
{{#if isSvelteOnlyApp}}
|
|
56
|
-
publicRuntimeConfig({ apiBase: process.env.PUBLIC_API_BASE, core: appConfig }),
|
|
56
|
+
publicRuntimeConfig({ apiBase: process.env.PUBLIC_API_BASE, {{#if features.crypto}}apiTransport: process.env.PUBLIC_API_TRANSPORT, apiKeyExchangePath: process.env.PUBLIC_API_KEY_EXCHANGE_PATH, {{/if}}core: appConfig }),
|
|
57
57
|
svelte(),
|
|
58
58
|
{{else}}
|
|
59
59
|
sveltekit(),
|
|
@@ -80,7 +80,9 @@ export default defineConfig(({ mode, command }) => {
|
|
|
80
80
|
{{/if}}
|
|
81
81
|
{{#unless isSvelteOnlyApp}}
|
|
82
82
|
ssr: {
|
|
83
|
-
noExternal: [
|
|
83
|
+
noExternal: ['@h-ai/ui', '@h-ai/kit'],
|
|
84
|
+
// sm-crypto 同时列入生产依赖,避免 adapter-node 再次打包其动态 require。
|
|
85
|
+
external: ['@h-ai/core', 'sm-crypto'],
|
|
84
86
|
},
|
|
85
87
|
{{/unless}}
|
|
86
88
|
server: {
|
|
@@ -7,11 +7,16 @@ import { kit } from '@h-ai/kit'
|
|
|
7
7
|
|
|
8
8
|
export const POST: RequestHandler = kit.handler(async ({ cookies, locals }) => {
|
|
9
9
|
const token = cookies.get('session_token')
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
try {
|
|
11
|
+
if (token) {
|
|
12
|
+
const result = await iam.auth.logout(token)
|
|
13
|
+
if (!result.success)
|
|
14
|
+
return kit.response.fromError(result.error, locals.requestId)
|
|
15
|
+
}
|
|
16
|
+
return kit.response.ok({ authenticated: false }, locals.requestId)
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
// 本地Cookie清理不代表服务端撤销成功,失败响应必须保留。
|
|
20
|
+
cookies.delete('session_token', { path: '/' })
|
|
14
21
|
}
|
|
15
|
-
cookies.delete('session_token', { path: '/' })
|
|
16
|
-
return kit.response.ok({ authenticated: false }, locals.requestId)
|
|
17
22
|
})
|