@h-ai/cli 1.0.0-beta.18 → 1.0.0-beta.19
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 +4 -0
- package/README.ja.md +4 -0
- package/README.md +4 -0
- package/dist/{chunk-6YZBC5MO.js → chunk-2ZL5ORQ2.js} +85 -25
- package/dist/chunk-2ZL5ORQ2.js.map +1 -0
- package/dist/{chunk-7NVRYYHN.js → chunk-L53P43B2.js} +129 -31
- package/dist/chunk-L53P43B2.js.map +1 -0
- package/dist/cli-config-templates-LWMD2I6V.js +5 -0
- package/dist/{cli-config-templates-QG5HOYR5.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
- package/dist/{cli-skill-templates-W4I6ZKYW.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
- package/dist/{cli-skill-templates-W4I6ZKYW.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2747 -671
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +22 -22
- 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/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/src/app.ts.hbs +71 -32
- 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 +131 -54
- 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 +78 -31
- 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 +3 -4
- package/templates/apps/api/playwright.config.ts.hbs +23 -19
- package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
- 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/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/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}}-web/eslint.config.js.hbs +10 -2
- 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/e2e/fullstack.spec.ts.hbs +111 -26
- 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 +3 -4
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +0 -2
- 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/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/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 +139 -49
- 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 +139 -28
- 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 +141 -25
- 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/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 +9 -3
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -15
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -21
- 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/src/App.svelte.hbs +17 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +14 -3
- 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/tasks.svelte.hbs +5 -1
- package/templates/apps/website/scripts/static-website.mjs.hbs +16 -3
- package/templates/apps/website/src/routes/+layout.svelte.hbs +44 -23
- 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/e2e/app.spec.ts.hbs +49 -16
- package/templates/base/eslint-rules.js +44 -25
- package/templates/base/eslint.config.js.hbs +3 -1
- package/templates/base/images/web.Dockerfile.hbs +3 -4
- package/templates/base/package.json.hbs +5 -4
- 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/hooks.server.ts.hbs +36 -27
- package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
- package/templates/base/src/lib/server/init.ts.hbs +124 -51
- 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 +11 -2
- package/templates/shared/browser-check/startup.js +32 -6
- package/templates/shared/browser-startup.spec.ts +3 -1
- package/templates/shared/build-metadata.ts +18 -4
- package/templates/shared/e2e-env.ts +22 -6
- package/templates/shared/native-name.mjs +9 -2
- package/templates/shared/public-base-path.mjs +17 -4
- package/templates/shared/run-e2e.mjs +40 -9
- package/templates/shared/run-node.mjs +15 -4
- package/templates/shared/run-tauri.mjs +37 -8
- 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/acp.md +2 -0
- package/templates/skills/hai-ai/guide.md +17 -6
- package/templates/skills/hai-ai/reference.md +21 -12
- package/templates/skills/hai-api-client/reference.md +1 -1
- package/templates/skills/hai-app-create/development.md +3 -1
- package/templates/skills/hai-app-create/project.md +33 -0
- package/templates/skills/hai-app-create/reference.md +8 -8
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-build/SKILL.md +2 -2
- package/templates/skills/hai-build/paths.md +1 -1
- package/templates/skills/hai-build/reference.md +11 -7
- package/templates/skills/hai-capacitor/reference.md +2 -0
- package/templates/skills/hai-ci/SKILL.md +1 -0
- package/templates/skills/hai-core/reference.md +3 -0
- package/templates/skills/hai-crypto/reference.md +2 -0
- package/templates/skills/hai-deploy/SKILL.md +7 -1
- package/templates/skills/hai-deploy/middleware.md +3 -1
- package/templates/skills/hai-framework-sync/sync.md +2 -0
- package/templates/skills/hai-kit/reference.md +16 -4
- package/templates/skills/hai-payment/reference.md +2 -0
- package/templates/skills/hai-reach/SKILL.md +2 -1
- package/templates/skills/hai-reach/reference.md +1 -1
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-reldb/reference.md +4 -0
- package/templates/skills/hai-serv/reference.md +12 -2
- package/templates/skills/hai-storage/reference.md +12 -3
- package/templates/skills/hai-ui/SKILL.md +3 -2
- package/templates/skills/hai-ui/guide.md +1 -1
- package/templates/skills/hai-ui/reference.md +23 -35
- package/templates/skills/hai-vecdb/reference.md +1 -1
- package/dist/chunk-6YZBC5MO.js.map +0 -1
- package/dist/chunk-7NVRYYHN.js.map +0 -1
- package/dist/cli-config-templates-QG5HOYR5.js +0 -5
|
@@ -7,9 +7,13 @@ import { resolveBuildMetadata } from './scripts/build-metadata.js'
|
|
|
7
7
|
|
|
8
8
|
export default defineConfig({
|
|
9
9
|
{{#unless isSvelteOnlyApp}}
|
|
10
|
-
plugins: [
|
|
10
|
+
plugins: [
|
|
11
|
+
sveltekit(),
|
|
12
|
+
],
|
|
11
13
|
{{/unless}}
|
|
12
|
-
define: {
|
|
14
|
+
define: {
|
|
15
|
+
__HAI_BUILD_METADATA__: JSON.stringify(resolveBuildMetadata(fileURLToPath(new URL('.', import.meta.url)))),
|
|
16
|
+
},
|
|
13
17
|
test: {
|
|
14
18
|
globals: true,
|
|
15
19
|
environment: 'node',
|
|
@@ -6,7 +6,9 @@ import { ai } from '@h-ai/ai'
|
|
|
6
6
|
import { kit } from '@h-ai/kit'
|
|
7
7
|
import { z } from 'zod'
|
|
8
8
|
|
|
9
|
-
const ChatInputSchema = z.object({
|
|
9
|
+
const ChatInputSchema = z.object({
|
|
10
|
+
message: z.string().trim().min(1).max(4000),
|
|
11
|
+
}).strict()
|
|
10
12
|
|
|
11
13
|
export const POST: RequestHandler = kit.handler(async ({ request, locals }) => {
|
|
12
14
|
const { message } = await kit.validate.body(request, ChatInputSchema)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='login' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('admin'))} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='register' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('admin'))} />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='forgot' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='login' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
2
|
import { goto } from '$app/navigation'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { page } from '$app/state'
|
|
5
5
|
import { kit } from '@h-ai/kit'
|
|
6
6
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
7
7
|
|
|
8
|
-
const authActions = kit.client.createAuthActions({ basePath: `${
|
|
8
|
+
const authActions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
|
-
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('
|
|
11
|
+
<AuthPanel config={page.data.iamPublicConfig} actions={authActions} initialMode='register' loginUrl={resolve('auth/login')} registerUrl={resolve('auth/register')} onauthenticated={() => goto(resolve('/'))} />
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang='ts'>
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from '$app/paths'
|
|
3
3
|
import { page } from '$app/state'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { AuthPanel } from '@h-ai/ui/components/scenes/iam/index'
|
|
6
6
|
|
|
7
|
-
const actions = kit.client.createAuthActions({ basePath: `${
|
|
7
|
+
const actions = kit.client.createAuthActions({ basePath: `${resolve('/').replace(/\/$/, '')}/api/auth` })
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('
|
|
10
|
+
<AuthPanel config={page.data.iamPublicConfig} {actions} initialMode='reset' resetToken={page.url.searchParams.get('token') ?? ''} loginUrl={resolve('auth/login')} />
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/** 创建支付参数 API;渠道、场景与 openid 必须通过显式校验,禁止回落到其它支付模式。 */
|
|
2
2
|
import type { RequestHandler } from './$types'
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from '$app/paths'
|
|
4
4
|
import { kit } from '@h-ai/kit'
|
|
5
5
|
import { payment } from '@h-ai/payment'
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
|
|
8
|
+
// Vite 配置固定 paths.relative=false,HTTP 前缀不会随 SSR 页面深度改变。
|
|
9
|
+
const base = resolve('/').replace(/\/$/, '')
|
|
10
|
+
|
|
8
11
|
/** 本模板的业务字段;服务端仍需从可信订单重新核对金额、描述、订单号和所属用户。 */
|
|
9
12
|
const CreatePaymentSchema = z.object({
|
|
10
13
|
provider: z.enum(['wechat', 'alipay', 'stripe']),
|
|
@@ -15,7 +18,13 @@ const CreatePaymentSchema = z.object({
|
|
|
15
18
|
userId: z.string().trim().min(1).max(128).optional(),
|
|
16
19
|
metadata: z.record(z.string(), z.string()).optional(),
|
|
17
20
|
notifyUrl: z.url({ protocol: /^https?$/ }).optional(),
|
|
18
|
-
}).strict().refine(input => input.tradeType === (input.provider === 'wechat'
|
|
21
|
+
}).strict().refine(input => input.tradeType === (input.provider === 'wechat'
|
|
22
|
+
? 'jsapi'
|
|
23
|
+
: 'h5'), {
|
|
24
|
+
path: ['tradeType'],
|
|
25
|
+
}).refine(input => input.provider !== 'wechat' || input.userId !== undefined, {
|
|
26
|
+
path: ['userId'],
|
|
27
|
+
})
|
|
19
28
|
|
|
20
29
|
export const POST: RequestHandler = async ({ request, locals }) => {
|
|
21
30
|
const { provider, tradeType, outTradeNo, totalAmount, subject, notifyUrl, userId, metadata } = await kit.validate.body(request, CreatePaymentSchema)
|
|
@@ -44,12 +44,21 @@
|
|
|
44
44
|
var currentMinor = Number(parts[1] || 0)
|
|
45
45
|
if (currentMajor > major || (currentMajor === major && currentMinor >= minor))
|
|
46
46
|
return null
|
|
47
|
-
return {
|
|
47
|
+
return {
|
|
48
|
+
name: name,
|
|
49
|
+
version: version,
|
|
50
|
+
minimum: minor
|
|
51
|
+
? major + '.' + minor
|
|
52
|
+
: String(major),
|
|
53
|
+
help: help,
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
function getLanguage(value) {
|
|
51
58
|
var language = (value || '').toLowerCase().split(/[-_]/)[0]
|
|
52
|
-
return language === 'zh' || language === 'ja' || language === 'en'
|
|
59
|
+
return language === 'zh' || language === 'ja' || language === 'en'
|
|
60
|
+
? language
|
|
61
|
+
: ''
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
function openUpdatePage() {
|
|
@@ -62,13 +71,30 @@
|
|
|
62
71
|
function showUpdateInstructions() {
|
|
63
72
|
var locale = getLanguage(location.hash.slice(1)) || getLanguage(navigator.language) || 'en'
|
|
64
73
|
var messages = {
|
|
65
|
-
zh: [
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
zh: [
|
|
75
|
+
'浏览器版本过低',
|
|
76
|
+
'当前使用 {name} {version}。此站点至少需要 {name} {minimum}。',
|
|
77
|
+
'请更新到最新版本,然后重新打开此站点。',
|
|
78
|
+
'查看官方更新说明',
|
|
79
|
+
],
|
|
80
|
+
en: [
|
|
81
|
+
'Update your browser',
|
|
82
|
+
'You are using {name} {version}. This site requires {name} {minimum} or later.',
|
|
83
|
+
'Install the latest update, then reopen this site.',
|
|
84
|
+
'Official update instructions',
|
|
85
|
+
],
|
|
86
|
+
ja: [
|
|
87
|
+
'ブラウザーを更新してください',
|
|
88
|
+
'現在のバージョンは {name} {version} です。このサイトには {name} {minimum} 以降が必要です。',
|
|
89
|
+
'最新版に更新してから、このサイトを開き直してください。',
|
|
90
|
+
'公式の更新手順を見る',
|
|
91
|
+
],
|
|
68
92
|
}
|
|
69
93
|
var message = messages[locale]
|
|
70
94
|
var help = document.getElementById('browser-update-help')
|
|
71
|
-
root.lang = locale === 'zh'
|
|
95
|
+
root.lang = locale === 'zh'
|
|
96
|
+
? 'zh-CN'
|
|
97
|
+
: locale
|
|
72
98
|
document.title = message[0]
|
|
73
99
|
document.getElementById('browser-update-title').textContent = message[0]
|
|
74
100
|
document.getElementById('browser-update-version').textContent = message[1].replace(/\{(name|version|minimum)\}/g, function (_match, key) {
|
|
@@ -3,7 +3,9 @@ import { expect, test } from '@playwright/test'
|
|
|
3
3
|
test('old browser sees upgrade instructions without the application bundles', async ({ page }) => {
|
|
4
4
|
// 只改变浏览器脚本看到的版本,确保检测在客户端执行。
|
|
5
5
|
await page.addInitScript(() => {
|
|
6
|
-
Object.defineProperty(navigator, 'userAgent', {
|
|
6
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
7
|
+
get: () => 'Mozilla/5.0 Gecko/20100101 Firefox/113.0',
|
|
8
|
+
})
|
|
7
9
|
})
|
|
8
10
|
await page.route('**/*', async (route) => {
|
|
9
11
|
const { pathname } = new URL(route.request().url())
|
|
@@ -7,7 +7,9 @@ import process from 'node:process'
|
|
|
7
7
|
/** 当前编译目标的私有产物身份;同一 SSR 构建重入复用身份,各端版本来自自己的 manifest。 */
|
|
8
8
|
export function resolveBuildMetadata(directory: string): { appVersion: string, buildId: string, commit: string } {
|
|
9
9
|
const manifest: { version?: unknown } = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'))
|
|
10
|
-
const appVersion = typeof manifest.version === 'string' && manifest.version.trim()
|
|
10
|
+
const appVersion = typeof manifest.version === 'string' && manifest.version.trim()
|
|
11
|
+
? manifest.version.trim()
|
|
12
|
+
: 'unknown'
|
|
11
13
|
if (appVersion.length > 128)
|
|
12
14
|
throw new Error('Invalid package.json version: maximum length is 128')
|
|
13
15
|
const suppliedId = process.env.HAI_BUILD_ID?.trim()
|
|
@@ -27,14 +29,26 @@ export function resolveBuildMetadata(directory: string): { appVersion: string, b
|
|
|
27
29
|
/** 未跟踪的生成工程不能把临时目录的父框架提交冒充自身源码。 */
|
|
28
30
|
function sourceCommit(directory: string): string {
|
|
29
31
|
try {
|
|
30
|
-
const options = {
|
|
32
|
+
const options = {
|
|
33
|
+
cwd: directory,
|
|
34
|
+
encoding: 'utf8' as const,
|
|
35
|
+
stdio: 'pipe' as const,
|
|
36
|
+
windowsHide: true,
|
|
37
|
+
}
|
|
31
38
|
const root = execFileSync('git', ['rev-parse', '--show-toplevel'], options).trim()
|
|
32
39
|
const manifest = relative(root, join(directory, 'package.json')).replaceAll('\\', '/')
|
|
33
40
|
if (manifest.startsWith('../'))
|
|
34
41
|
return 'unknown'
|
|
35
|
-
execFileSync('git', [
|
|
42
|
+
execFileSync('git', [
|
|
43
|
+
'ls-files',
|
|
44
|
+
'--error-unmatch',
|
|
45
|
+
'--',
|
|
46
|
+
manifest,
|
|
47
|
+
], { ...options, cwd: root })
|
|
36
48
|
const commit = execFileSync('git', ['rev-parse', 'HEAD'], options).trim()
|
|
37
|
-
return /^(?:[a-f\d]{40}|[a-f\d]{64})$/i.test(commit)
|
|
49
|
+
return /^(?:[a-f\d]{40}|[a-f\d]{64})$/i.test(commit)
|
|
50
|
+
? commit
|
|
51
|
+
: 'unknown'
|
|
38
52
|
}
|
|
39
53
|
catch {
|
|
40
54
|
return 'unknown'
|
|
@@ -29,7 +29,12 @@ export function createE2eServerEnv(profile: Record<string, string>): Record<stri
|
|
|
29
29
|
const environment: Record<string, string | undefined> = Object.fromEntries(
|
|
30
30
|
Object.keys(process.env).filter(key => /^(?:HAI_|PUBLIC_|VITE_)/i.test(key)).map(key => [key, undefined]),
|
|
31
31
|
)
|
|
32
|
-
for (const name of [
|
|
32
|
+
for (const name of [
|
|
33
|
+
'HAI_E2E_DATA_ROOT',
|
|
34
|
+
'HAI_AUTH_EVIDENCE_DIR',
|
|
35
|
+
'HAI_CORE_NAME',
|
|
36
|
+
'HAI_CORE_VERSION',
|
|
37
|
+
]) {
|
|
33
38
|
if (process.env[name] !== undefined)
|
|
34
39
|
environment[name] = process.env[name]
|
|
35
40
|
}
|
|
@@ -56,10 +61,15 @@ export function createE2eServerEnv(profile: Record<string, string>): Record<stri
|
|
|
56
61
|
export function createE2eTransportRing(): string {
|
|
57
62
|
const key = createECDH('SM2')
|
|
58
63
|
key.generateKeys()
|
|
59
|
-
return JSON.stringify({
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
return JSON.stringify({
|
|
65
|
+
activeKeyId: 'e2e-v1',
|
|
66
|
+
keys: {
|
|
67
|
+
'e2e-v1': {
|
|
68
|
+
publicKey: key.getPublicKey('hex'),
|
|
69
|
+
privateKey: key.getPrivateKey('hex').padStart(64, '0'),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
})
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/**
|
|
@@ -93,6 +103,12 @@ export function createE2eNotificationEnv(): Record<string, string> {
|
|
|
93
103
|
{ name: 'password_reset', provider: 'email', body: 'Reset: {token}' },
|
|
94
104
|
{ name: 'password_reset_sms', provider: 'sms', body: 'Reset: {token}' },
|
|
95
105
|
]),
|
|
96
|
-
HAI_IAM_AGREEMENTS: JSON.stringify({
|
|
106
|
+
HAI_IAM_AGREEMENTS: JSON.stringify({
|
|
107
|
+
showOnRegister: true,
|
|
108
|
+
showOnLogin: true,
|
|
109
|
+
userAgreementMarkdown: '# Configured registration agreement\n\n**Registration terms**\n\n- Keep your account credentials secure.',
|
|
110
|
+
userAgreementUrl: new URL('/terms', url).href,
|
|
111
|
+
privacyPolicyUrl: new URL('/privacy', url).href,
|
|
112
|
+
}),
|
|
97
113
|
}
|
|
98
114
|
}
|
|
@@ -8,7 +8,12 @@ import { core, CoreConfigSchema } from '@h-ai/core'
|
|
|
8
8
|
* @returns {Record<string, unknown>} Core 环境覆盖值。
|
|
9
9
|
*/
|
|
10
10
|
function resolveCoreInputs(environment) {
|
|
11
|
-
const resolved = core.config.resolve({
|
|
11
|
+
const resolved = core.config.resolve({
|
|
12
|
+
name: 'core',
|
|
13
|
+
value: {},
|
|
14
|
+
schema: CoreConfigSchema,
|
|
15
|
+
environment,
|
|
16
|
+
})
|
|
12
17
|
if (!resolved.success)
|
|
13
18
|
throw new Error(resolved.error.message)
|
|
14
19
|
const { data } = resolved
|
|
@@ -42,7 +47,9 @@ export function nativeNameEnvironment(envDir) {
|
|
|
42
47
|
for (const key of ['name', 'env']) {
|
|
43
48
|
if (value[key] === undefined)
|
|
44
49
|
continue
|
|
45
|
-
const schema = key === 'name'
|
|
50
|
+
const schema = key === 'name'
|
|
51
|
+
? CoreConfigSchema.shape.name
|
|
52
|
+
: CoreConfigSchema.shape.env
|
|
46
53
|
const parsed = schema.safeParse(value[key])
|
|
47
54
|
if (!parsed.success)
|
|
48
55
|
throw new Error(`Invalid HAI_CORE_${key.toUpperCase()}`)
|
|
@@ -8,12 +8,25 @@ const basePathMessage = core.i18n.createMessageGetter({
|
|
|
8
8
|
'ja-JP': { invalid: 'PUBLIC_BASE_PATHは空、または/で始まるサイト内ディレクトリーです。外部URL、末尾の/、クエリー、フラグメント、パス逸脱は使えません。' },
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} [value] - 无末尾斜杠的公开目录;空字符串表示域名根目录。
|
|
13
|
+
* @returns {'' | `/${string}`} 经校验的站内挂载目录,可直接用于 SvelteKit paths.base。
|
|
14
|
+
*/
|
|
12
15
|
export function resolvePublicBasePath(value = process.env.PUBLIC_BASE_PATH ?? '') {
|
|
13
16
|
if (value && (!/^\/[\w.-]+(?:\/[\w.-]+)*$/.test(value) || value.split('/').some(segment => segment === '.' || segment === '..'))) {
|
|
14
|
-
const language = [
|
|
15
|
-
|
|
17
|
+
const language = [
|
|
18
|
+
process.env.HAI_CORE_DEFAULTLOCALE,
|
|
19
|
+
process.env.LC_ALL,
|
|
20
|
+
process.env.LC_MESSAGES,
|
|
21
|
+
process.env.LANG,
|
|
22
|
+
].find(item => item?.trim())?.split(/[_.-]/)[0]?.toLowerCase()
|
|
23
|
+
const locale = language === 'en' || language === 'c' || language === 'posix'
|
|
24
|
+
? 'en-US'
|
|
25
|
+
: language === 'ja'
|
|
26
|
+
? 'ja-JP'
|
|
27
|
+
: 'zh-CN'
|
|
16
28
|
throw new Error(basePathMessage('invalid', { locale }))
|
|
17
29
|
}
|
|
18
|
-
|
|
30
|
+
// 已排除不以 / 开头的非空字符串;正则校验无法自动收窄 TypeScript 字符串类型。
|
|
31
|
+
return /** @type {'' | `/${string}`} */ (value)
|
|
19
32
|
}
|
|
@@ -12,7 +12,9 @@ const cli = require.resolve('@playwright/test/cli')
|
|
|
12
12
|
const dataParent = resolve(process.env.HAI_E2E_DATA_ROOT || tmpdir())
|
|
13
13
|
mkdirSync(dataParent, { recursive: true })
|
|
14
14
|
const dataRoot = mkdtempSync(join(dataParent, 'hai-browser-e2e-'))
|
|
15
|
-
const groups = [
|
|
15
|
+
const groups = [
|
|
16
|
+
{ args: [], name: undefined },
|
|
17
|
+
]
|
|
16
18
|
for (const argument of process.argv.slice(2)) {
|
|
17
19
|
if (argument === '--next')
|
|
18
20
|
groups.push({ args: [], name: undefined })
|
|
@@ -39,7 +41,12 @@ function stop() {
|
|
|
39
41
|
if (process.platform === 'win32' && child?.pid && child.exitCode === null) {
|
|
40
42
|
// Windows 的 kill 不传递 SIGTERM;同时结束本次 Playwright 创建的浏览器与服务进程。
|
|
41
43
|
stopping = new Promise((resolve) => {
|
|
42
|
-
const killer = spawn('taskkill', [
|
|
44
|
+
const killer = spawn('taskkill', [
|
|
45
|
+
'/pid',
|
|
46
|
+
String(child.pid),
|
|
47
|
+
'/t',
|
|
48
|
+
'/f',
|
|
49
|
+
], { windowsHide: true, stdio: 'ignore' })
|
|
43
50
|
killer.once('error', () => {
|
|
44
51
|
child?.kill()
|
|
45
52
|
resolve()
|
|
@@ -63,16 +70,30 @@ try {
|
|
|
63
70
|
const groupDataRoot = join(dataRoot, `group-${index + 1}`)
|
|
64
71
|
mkdirSync(groupDataRoot)
|
|
65
72
|
const hasOutput = args.some(argument => argument === '--output' || argument.startsWith('--output='))
|
|
66
|
-
const outputArgs = hasOutput
|
|
73
|
+
const outputArgs = hasOutput
|
|
74
|
+
? []
|
|
75
|
+
: [
|
|
76
|
+
'--output',
|
|
77
|
+
join(groupRoot, 'results'),
|
|
78
|
+
]
|
|
67
79
|
const html = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR || process.env.PLAYWRIGHT_HTML_REPORT
|
|
68
80
|
const code = await new Promise((resolve, reject) => {
|
|
69
|
-
child = spawn(process.execPath, [
|
|
81
|
+
child = spawn(process.execPath, [
|
|
82
|
+
cli,
|
|
83
|
+
'test',
|
|
84
|
+
...args,
|
|
85
|
+
...outputArgs,
|
|
86
|
+
], {
|
|
70
87
|
cwd,
|
|
71
88
|
windowsHide: true,
|
|
72
89
|
stdio: 'inherit',
|
|
73
90
|
env: {
|
|
74
91
|
...process.env,
|
|
75
|
-
...(!html
|
|
92
|
+
...(!html
|
|
93
|
+
? {
|
|
94
|
+
PLAYWRIGHT_HTML_OUTPUT_DIR: join(groupRoot, 'html'),
|
|
95
|
+
}
|
|
96
|
+
: {}),
|
|
76
97
|
HAI_AUTH_EVIDENCE_DIR: process.env.HAI_AUTH_EVIDENCE_DIR || join(groupRoot, 'evidence'),
|
|
77
98
|
HAI_E2E_DATA_ROOT: groupDataRoot,
|
|
78
99
|
HAI_E2E_GROUP: name,
|
|
@@ -82,8 +103,11 @@ try {
|
|
|
82
103
|
child.once('exit', resolve)
|
|
83
104
|
})
|
|
84
105
|
// 配置组互相独立,普通失败继续收集;保留首个失败码,中断才停止后续组。
|
|
85
|
-
if (code !== 0)
|
|
86
|
-
process.exitCode ||= typeof code === 'number' && code !== 0
|
|
106
|
+
if (code !== 0) {
|
|
107
|
+
process.exitCode ||= typeof code === 'number' && code !== 0
|
|
108
|
+
? code
|
|
109
|
+
: 1
|
|
110
|
+
}
|
|
87
111
|
if (interrupted) {
|
|
88
112
|
process.exitCode ||= 1
|
|
89
113
|
break
|
|
@@ -96,10 +120,17 @@ finally {
|
|
|
96
120
|
await stopping
|
|
97
121
|
// Playwright 退出后服务进程已关闭,此时删除 SQLite / storage,避免 Windows 文件锁。
|
|
98
122
|
try {
|
|
99
|
-
rmSync(dataRoot, {
|
|
123
|
+
rmSync(dataRoot, {
|
|
124
|
+
recursive: true,
|
|
125
|
+
force: true,
|
|
126
|
+
maxRetries: 3,
|
|
127
|
+
retryDelay: 100,
|
|
128
|
+
})
|
|
100
129
|
}
|
|
101
130
|
catch (error) {
|
|
102
|
-
process.stderr.write(`E2E data cleanup failed: ${error instanceof Error
|
|
131
|
+
process.stderr.write(`E2E data cleanup failed: ${error instanceof Error
|
|
132
|
+
? error.message
|
|
133
|
+
: String(error)}\n`)
|
|
103
134
|
// 清理错误不能把原始失败(如 exit 7)覆盖成另一状态,也不能让成功变为假成功。
|
|
104
135
|
if (!process.exitCode)
|
|
105
136
|
process.exitCode = 1
|
|
@@ -12,13 +12,22 @@ if (!mode || !environmentDirectory || !entry)
|
|
|
12
12
|
// 从实际应用解析公开 Core 入口,脚本可以放在 workspace 根目录。
|
|
13
13
|
const require = createRequire(resolve('package.json'))
|
|
14
14
|
const { core } = await import(pathToFileURL(require.resolve('@h-ai/core')).href)
|
|
15
|
-
const loaded = core.config.loadEnvFiles({
|
|
15
|
+
const loaded = core.config.loadEnvFiles({
|
|
16
|
+
mode,
|
|
17
|
+
envDir: resolve(environmentDirectory),
|
|
18
|
+
})
|
|
16
19
|
if (!loaded.success)
|
|
17
20
|
throw new Error(loaded.error.message)
|
|
18
21
|
|
|
19
|
-
const child = spawn(process.execPath, [
|
|
22
|
+
const child = spawn(process.execPath, [
|
|
23
|
+
resolve(entry),
|
|
24
|
+
...arguments_,
|
|
25
|
+
], {
|
|
20
26
|
cwd: process.cwd(),
|
|
21
|
-
env: {
|
|
27
|
+
env: {
|
|
28
|
+
...process.env,
|
|
29
|
+
...loaded.data,
|
|
30
|
+
},
|
|
22
31
|
stdio: 'inherit',
|
|
23
32
|
windowsHide: true,
|
|
24
33
|
})
|
|
@@ -35,7 +44,9 @@ try {
|
|
|
35
44
|
child.once('error', reject)
|
|
36
45
|
child.once('close', (code, signal) => resolve({ code, signal }))
|
|
37
46
|
})
|
|
38
|
-
process.exitCode = result.code ?? (result.signal === 'SIGINT'
|
|
47
|
+
process.exitCode = result.code ?? (result.signal === 'SIGINT'
|
|
48
|
+
? 130
|
|
49
|
+
: 143)
|
|
39
50
|
}
|
|
40
51
|
finally {
|
|
41
52
|
process.off('SIGINT', interrupt)
|