@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,38 +1,31 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 创建支付订单 API
|
|
3
|
-
*/
|
|
1
|
+
/** 创建支付参数 API;渠道、场景与 openid 必须通过显式校验,禁止回落到其它支付模式。 */
|
|
4
2
|
import type { RequestHandler } from './$types'
|
|
5
3
|
import { kit } from '@h-ai/kit'
|
|
6
4
|
import { payment } from '@h-ai/payment'
|
|
5
|
+
import { z } from 'zod'
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|| tradeType === 'mini_program'
|
|
20
|
-
? tradeType
|
|
21
|
-
: 'jsapi'
|
|
7
|
+
/** 本模板的业务字段;服务端仍需从可信订单重新核对金额、描述、订单号和所属用户。 */
|
|
8
|
+
const CreatePaymentSchema = z.object({
|
|
9
|
+
provider: z.enum(['wechat', 'alipay', 'stripe']),
|
|
10
|
+
tradeType: z.enum(['jsapi', 'h5']),
|
|
11
|
+
outTradeNo: z.string().min(1),
|
|
12
|
+
totalAmount: z.number().int().positive(),
|
|
13
|
+
subject: z.string().min(1),
|
|
14
|
+
userId: z.string().trim().min(1).max(128).optional(),
|
|
15
|
+
metadata: z.record(z.string(), z.string()).optional(),
|
|
16
|
+
notifyUrl: z.url({ protocol: /^https?$/ }).optional(),
|
|
17
|
+
}).strict().refine(input => input.tradeType === (input.provider === 'wechat' ? 'jsapi' : 'h5'), { path: ['tradeType'] }).refine(input => input.provider !== 'wechat' || input.userId !== undefined, { path: ['userId'] })
|
|
22
18
|
|
|
19
|
+
export const POST: RequestHandler = async ({ request, locals }) => {
|
|
20
|
+
const { provider, tradeType, outTradeNo, totalAmount, subject, notifyUrl, userId, metadata } = await kit.validate.body(request, CreatePaymentSchema)
|
|
23
21
|
const result = await payment.createOrder(provider, {
|
|
24
22
|
orderNo: outTradeNo,
|
|
25
|
-
amount:
|
|
23
|
+
amount: totalAmount,
|
|
26
24
|
description: subject,
|
|
27
|
-
tradeType
|
|
25
|
+
tradeType,
|
|
28
26
|
userId,
|
|
29
27
|
metadata,
|
|
30
28
|
notifyUrl: notifyUrl ?? `${new URL(request.url).origin}/api/v1/payment/notify/${provider}`,
|
|
31
29
|
})
|
|
32
|
-
|
|
33
|
-
if (!result.success) {
|
|
34
|
-
return kit.response.internalError(result.error.message)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return kit.response.ok(result.data, locals.requestId)
|
|
30
|
+
return kit.response.fromResult(result, locals.requestId)
|
|
38
31
|
}
|
|
@@ -30,6 +30,10 @@ export const POST: RequestHandler = async ({ request, params }) => {
|
|
|
30
30
|
return new Response('FAIL', { status: 500 })
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// 此模板仅确认已验签解析;success 不代表已付款,Stripe 延迟付款会返回 pending。
|
|
34
|
+
// 业务接入时须在响应成功前,用共享数据库核对订单/金额/币种,按 eventId 去重并防止状态倒退;
|
|
35
|
+
// 仅 status=paid 可触发履约,不在进程内 Set/Map 中维护通知去重。
|
|
36
|
+
|
|
33
37
|
// 微信支付要求返回 JSON,支付宝要求返回 "success"
|
|
34
38
|
if (provider === 'wechat') {
|
|
35
39
|
return new Response(JSON.stringify({ code: 'SUCCESS', message: '' }), {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createECDH } from 'node:crypto'
|
|
2
|
+
import { isAbsolute } from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 仅给 Playwright 的服务子进程装配环境;不修改测试进程和 pnpm/Turbo 的环境。
|
|
7
|
+
* @param profile - 本轮测试明确选择的模块配置和可信控制值。
|
|
8
|
+
* @returns Playwright webServer.env;undefined 项由 Node spawn 从子进程环境中移除。
|
|
9
|
+
* @throws 缺少绝对数据目录,或 API 指向非本轮 loopback HTTP 夹具时抛错。
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* // 测试内部上下文;数据目录由 run-e2e 为本轮分配。
|
|
13
|
+
* const env = createE2eServerEnv({
|
|
14
|
+
* HAI_E2E_DATA_ROOT: process.cwd(),
|
|
15
|
+
* HAI_CACHE: JSON.stringify({ type: 'memory' }),
|
|
16
|
+
* })
|
|
17
|
+
* if (env.HAI_E2E !== '1') throw new Error('E2E profile was not applied')
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function createE2eServerEnv(profile: Record<string, string>): Record<string, string> {
|
|
21
|
+
const dataRoot = profile.HAI_E2E_DATA_ROOT || process.env.HAI_E2E_DATA_ROOT
|
|
22
|
+
if (!dataRoot || !isAbsolute(dataRoot))
|
|
23
|
+
throw new Error('Run E2E through the managed entrypoint with its own absolute HAI_E2E_DATA_ROOT')
|
|
24
|
+
if (profile.PUBLIC_API_BASE) {
|
|
25
|
+
const api = new URL(profile.PUBLIC_API_BASE)
|
|
26
|
+
if (api.protocol !== 'http:' || !['127.0.0.1', 'localhost', '[::1]'].includes(api.hostname) || api.username || api.password)
|
|
27
|
+
throw new Error('E2E API must use an unauthenticated loopback HTTP fixture')
|
|
28
|
+
}
|
|
29
|
+
const environment: Record<string, string | undefined> = Object.fromEntries(
|
|
30
|
+
Object.keys(process.env).filter(key => /^(?:HAI_|PUBLIC_|VITE_)/i.test(key)).map(key => [key, undefined]),
|
|
31
|
+
)
|
|
32
|
+
for (const name of ['HAI_E2E_DATA_ROOT', 'HAI_AUTH_EVIDENCE_DIR', 'HAI_CORE_NAME', 'HAI_CORE_VERSION']) {
|
|
33
|
+
if (process.env[name] !== undefined)
|
|
34
|
+
environment[name] = process.env[name]
|
|
35
|
+
}
|
|
36
|
+
Object.assign(environment, { HAI_E2E: '1', NODE_ENV: 'test', HAI_E2E_DATA_ROOT: dataRoot }, profile)
|
|
37
|
+
// Playwright 声明仅接受 string,但其 WebServer 合并 env 后直接交 Node spawn;
|
|
38
|
+
// 已用安装版真实 WebServer 验证 undefined 会删除继承项,因此仅在第三方类型边界使用窄断言。
|
|
39
|
+
return environment as Record<string, string>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 每轮生成独立可信 SM2 ring;私钥只传服务子进程,不进入公开配置或报告。
|
|
44
|
+
* @returns 符合 transport serverKeyRing 配置的 JSON 字符串。
|
|
45
|
+
* @throws 当前 Node/OpenSSL 不支持 SM2 时抛错,不降级为不可信握手。
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* // 测试内部上下文;仅交给本轮服务子进程。
|
|
49
|
+
* const env = createE2eServerEnv({
|
|
50
|
+
* HAI_E2E_DATA_ROOT: process.cwd(),
|
|
51
|
+
* HAI_CRYPTO_TRANSPORT_SERVERKEYRING: createE2eTransportRing(),
|
|
52
|
+
* })
|
|
53
|
+
* if (!env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING) throw new Error('Trusted ring is required')
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function createE2eTransportRing(): string {
|
|
57
|
+
const key = createECDH('SM2')
|
|
58
|
+
key.generateKeys()
|
|
59
|
+
return JSON.stringify({ activeKeyId: 'e2e-v1', keys: { 'e2e-v1': {
|
|
60
|
+
publicKey: key.getPublicKey('hex'),
|
|
61
|
+
privateKey: key.getPrivateKey('hex').padStart(64, '0'),
|
|
62
|
+
} } })
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 只把既有的本轮 loopback 通知夹具控制地址转成明确 Reach 配置。
|
|
67
|
+
* @returns 未启用通知夹具时为空对象;启用时包含 OTP、模板及协议配置。
|
|
68
|
+
* @throws IAM_NOTIFICATION_URL 不是无凭据的 loopback HTTP 地址时抛错。
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // 测试内部上下文;IAM_NOTIFICATION_URL 由样板门禁启动的通知夹具提供。
|
|
72
|
+
* const env = createE2eServerEnv({
|
|
73
|
+
* ...createE2eNotificationEnv(),
|
|
74
|
+
* HAI_E2E_DATA_ROOT: process.cwd(),
|
|
75
|
+
* })
|
|
76
|
+
* if (env.HAI_E2E !== '1') throw new Error('Notification profile was not isolated')
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function createE2eNotificationEnv(): Record<string, string> {
|
|
80
|
+
const address = process.env.IAM_NOTIFICATION_URL
|
|
81
|
+
if (!address)
|
|
82
|
+
return {}
|
|
83
|
+
const url = new URL(address)
|
|
84
|
+
if (url.protocol !== 'http:' || !['127.0.0.1', 'localhost', '[::1]'].includes(url.hostname) || url.username || url.password)
|
|
85
|
+
throw new Error('E2E notification fixture must use an unauthenticated loopback HTTP address')
|
|
86
|
+
return {
|
|
87
|
+
HAI_IAM_LOGIN_OTP: 'true',
|
|
88
|
+
HAI_IAM_PASSWORDRESET_ENABLED: 'true',
|
|
89
|
+
HAI_REACH_PROVIDERS: JSON.stringify(['email', 'sms'].map(name => ({ name, type: 'api', url: new URL('/send', url).href }))),
|
|
90
|
+
HAI_REACH_TEMPLATES: JSON.stringify([
|
|
91
|
+
{ name: 'otp_email', provider: 'email', body: 'Code: {code}' },
|
|
92
|
+
{ name: 'otp_sms', provider: 'sms', body: 'Code: {code}' },
|
|
93
|
+
{ name: 'password_reset', provider: 'email', body: 'Reset: {token}' },
|
|
94
|
+
{ name: 'password_reset_sms', provider: 'sms', body: 'Reset: {token}' },
|
|
95
|
+
]),
|
|
96
|
+
HAI_IAM_AGREEMENTS: JSON.stringify({ showOnRegister: true, showOnLogin: true, userAgreementMarkdown: '# Configured registration agreement\n\n**Registration terms**\n\n- Keep your account credentials secure.', userAgreementUrl: new URL('/terms', url).href, privacyPolicyUrl: new URL('/privacy', url).href }),
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** 运行浏览器验收,保留本轮证据;退出后仅回收本轮服务数据,失败和中断同样处理。 */
|
|
2
2
|
import { spawn } from 'node:child_process'
|
|
3
3
|
import { mkdirSync, mkdtempSync, rmSync } from 'node:fs'
|
|
4
4
|
import { createRequire } from 'node:module'
|
|
@@ -21,14 +21,29 @@ for (const argument of process.argv.slice(2)) {
|
|
|
21
21
|
}
|
|
22
22
|
let child
|
|
23
23
|
let interrupted = false
|
|
24
|
+
let stopping
|
|
25
|
+
/**
|
|
26
|
+
* 结束本轮 Playwright;Windows 同时结束其进程树,finally 等待该命令退出后才清理数据。
|
|
27
|
+
* @returns {void} 终止请求由 child 退出事件和 stopping Promise 确认。
|
|
28
|
+
* @example 模块内部信号处理
|
|
29
|
+
* ```js
|
|
30
|
+
* process.once('SIGTERM', stop)
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
24
33
|
function stop() {
|
|
25
34
|
if (interrupted)
|
|
26
35
|
return
|
|
27
36
|
interrupted = true
|
|
28
37
|
if (process.platform === 'win32' && child?.pid && child.exitCode === null) {
|
|
29
38
|
// Windows 的 kill 不传递 SIGTERM;同时结束本次 Playwright 创建的浏览器与服务进程。
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
stopping = new Promise((resolve) => {
|
|
40
|
+
const killer = spawn('taskkill', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' })
|
|
41
|
+
killer.once('error', () => {
|
|
42
|
+
child?.kill()
|
|
43
|
+
resolve()
|
|
44
|
+
})
|
|
45
|
+
killer.once('exit', resolve)
|
|
46
|
+
})
|
|
32
47
|
}
|
|
33
48
|
else {
|
|
34
49
|
child?.kill()
|
|
@@ -37,19 +52,37 @@ function stop() {
|
|
|
37
52
|
process.once('SIGINT', stop)
|
|
38
53
|
process.once('SIGTERM', stop)
|
|
39
54
|
try {
|
|
40
|
-
|
|
55
|
+
// 证据与临时业务数据分开;每组使用独立输出,后续 Playwright 不会清空前组证据。
|
|
56
|
+
const evidenceParent = resolve(process.env.HAI_AUTH_EVIDENCE_DIR || join(cwd, 'test-results'))
|
|
57
|
+
mkdirSync(evidenceParent, { recursive: true })
|
|
58
|
+
const runRoot = mkdtempSync(join(evidenceParent, 'run-'))
|
|
59
|
+
for (const [index, args] of groups.entries()) {
|
|
60
|
+
const groupRoot = join(runRoot, `group-${index + 1}`)
|
|
61
|
+
const groupDataRoot = join(dataRoot, `group-${index + 1}`)
|
|
62
|
+
mkdirSync(groupDataRoot)
|
|
63
|
+
const hasOutput = args.some(argument => argument === '--output' || argument.startsWith('--output='))
|
|
64
|
+
const outputArgs = hasOutput ? [] : ['--output', join(groupRoot, 'results')]
|
|
65
|
+
const html = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR || process.env.PLAYWRIGHT_HTML_REPORT
|
|
41
66
|
const code = await new Promise((resolve, reject) => {
|
|
42
|
-
child = spawn(process.execPath, [cli, 'test', ...args], {
|
|
67
|
+
child = spawn(process.execPath, [cli, 'test', ...args, ...outputArgs], {
|
|
43
68
|
cwd,
|
|
44
69
|
windowsHide: true,
|
|
45
70
|
stdio: 'inherit',
|
|
46
|
-
env: {
|
|
71
|
+
env: {
|
|
72
|
+
...process.env,
|
|
73
|
+
...(!html ? { PLAYWRIGHT_HTML_OUTPUT_DIR: join(groupRoot, 'html') } : {}),
|
|
74
|
+
HAI_AUTH_EVIDENCE_DIR: process.env.HAI_AUTH_EVIDENCE_DIR || join(groupRoot, 'evidence'),
|
|
75
|
+
HAI_E2E_DATA_ROOT: groupDataRoot,
|
|
76
|
+
},
|
|
47
77
|
})
|
|
48
78
|
child.once('error', reject)
|
|
49
79
|
child.once('exit', resolve)
|
|
50
80
|
})
|
|
51
|
-
|
|
52
|
-
|
|
81
|
+
// 配置组互相独立,普通失败继续收集;保留首个失败码,中断才停止后续组。
|
|
82
|
+
if (code !== 0)
|
|
83
|
+
process.exitCode ||= typeof code === 'number' && code !== 0 ? code : 1
|
|
84
|
+
if (interrupted) {
|
|
85
|
+
process.exitCode ||= 1
|
|
53
86
|
break
|
|
54
87
|
}
|
|
55
88
|
}
|
|
@@ -57,9 +90,15 @@ try {
|
|
|
57
90
|
finally {
|
|
58
91
|
process.removeListener('SIGINT', stop)
|
|
59
92
|
process.removeListener('SIGTERM', stop)
|
|
60
|
-
|
|
61
|
-
for (const name of ['test-results', 'playwright-report'])
|
|
62
|
-
rmSync(join(cwd, name), { recursive: true, force: true, maxRetries: 3, retryDelay: 100 })
|
|
93
|
+
await stopping
|
|
63
94
|
// Playwright 退出后服务进程已关闭,此时删除 SQLite / storage,避免 Windows 文件锁。
|
|
64
|
-
|
|
95
|
+
try {
|
|
96
|
+
rmSync(dataRoot, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 })
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
process.stderr.write(`E2E data cleanup failed: ${error instanceof Error ? error.message : String(error)}\n`)
|
|
100
|
+
// 清理错误不能把原始失败(如 exit 7)覆盖成另一状态,也不能让成功变为假成功。
|
|
101
|
+
if (!process.exitCode)
|
|
102
|
+
process.exitCode = 1
|
|
103
|
+
}
|
|
65
104
|
}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
1. 先搜索现有实现、引用点、测试和 README。
|
|
42
42
|
2. 小步修改,优先保持目录结构、公开 API 和脚本稳定。
|
|
43
43
|
3. 改动公共类型、配置、模板或共享逻辑时,同步更新所有调用方与文档。
|
|
44
|
-
4.
|
|
44
|
+
4. 一轮修改完成后按影响范围收集全部可执行门禁结果,汇总问题再集中修复、复测;普通失败不立即从头重跑。
|
|
45
45
|
|
|
46
46
|
## 代码注释与示例
|
|
47
47
|
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
## 质量门禁
|
|
54
54
|
|
|
55
|
-
先核对 package.json
|
|
55
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
58
|
pnpm typecheck
|
|
@@ -64,7 +64,7 @@ pnpm test:e2e
|
|
|
64
64
|
|
|
65
65
|
## 完成条件
|
|
66
66
|
|
|
67
|
-
- typecheck / lint / build / test / test:e2e
|
|
67
|
+
- typecheck / lint / build / test / test:e2e 状态明确;整轮收集后集中修复,最终必需门禁对最终代码有效通过,未执行或阻塞明确原因,不能宣称完成。
|
|
68
68
|
- README、i18n、测试、代码注释与实现保持一致。
|
|
69
69
|
- 最终回复说明执行过的门禁、已同步的文档 / 依赖方,以及未完成项。
|
|
70
70
|
|
|
@@ -75,3 +75,7 @@ pnpm test:e2e
|
|
|
75
75
|
- `.agents/skills/hai-app-review/SKILL.md`:应用代码审查。
|
|
76
76
|
- `.agents/skills/hai-app-tests/SKILL.md`:Vitest / Playwright 测试策略。
|
|
77
77
|
- 具体模块按需读取对应 `hai-*` Skill。
|
|
78
|
+
|
|
79
|
+
前端项目已交付对应技能时,先按 hai-ui-design 确定任务、布局、状态与组件映射,使用 hai-ui 实现,再由 hai-ui-review 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
80
|
+
|
|
81
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
- 页面组件、layout、load 只负责渲染、权限态拼装和请求编排;业务逻辑放在 `src/lib/server/services` 或模块 API。
|
|
40
40
|
- 认证授权使用 `@h-ai/iam` / `@h-ai/kit`;Web token 优先 httpOnly cookie。
|
|
41
|
-
- 所有用户可见文本走 `$lib/paraglide/messages.js`,同步 `messages/zh-CN.json
|
|
41
|
+
- 所有用户可见文本走 `$lib/paraglide/messages.js`,同步 `messages/zh-CN.json`、`messages/en-US.json`、`messages/ja-JP.json`。
|
|
42
42
|
- API 边界必须做 Zod 校验,HaiResult 错误用 kit response 转换。
|
|
43
43
|
- `+server.ts` 只能导出 handler 和 SvelteKit 允许的配置项;helper 放到 `src/lib/server/**`。
|
|
44
44
|
- 本样板默认不使用 `@h-ai/serv`、`@h-ai/api-contract`、`@h-ai/api-client`、`@h-ai/capacitor` 作为主架构。
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -81,3 +81,7 @@ pnpm test:e2e
|
|
|
81
81
|
- `hai-build`、`hai-app-create`、`hai-app-review`、`hai-app-tests`
|
|
82
82
|
- `hai-kit`、`hai-ui`、`hai-iam`、`hai-reldb`、`hai-cache`、`hai-crypto`
|
|
83
83
|
- 其它 `hai-*` 模块按需读取
|
|
84
|
+
|
|
85
|
+
前端页面先按 [hai-ui-design](.agents/skills/hai-ui-design/SKILL.md) 确定任务、布局、状态与组件映射,使用 [hai-ui](.agents/skills/hai-ui/SKILL.md) 实现,再由 [hai-ui-review](.agents/skills/hai-ui-review/SKILL.md) 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
86
|
+
|
|
87
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
## 质量门禁
|
|
59
59
|
|
|
60
|
-
先核对 package.json
|
|
60
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
63
|
pnpm typecheck
|
|
@@ -77,3 +77,5 @@ pnpm test:e2e
|
|
|
77
77
|
- `hai-build`、`hai-app-create`、`hai-app-review`、`hai-app-tests`
|
|
78
78
|
- `hai-serv`、`hai-api-contract`、`hai-api-client`
|
|
79
79
|
- `hai-core`、`hai-reldb`、`hai-cache`、`hai-iam`、`hai-crypto`
|
|
80
|
+
|
|
81
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -74,7 +74,7 @@ Fullstack 多包工程入口。先读 README、根 workspace 脚本与相关 pac
|
|
|
74
74
|
|
|
75
75
|
## 质量门禁
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集一轮适用门禁及日志,汇总后集中修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。跨包契约从根工作区验证:
|
|
78
78
|
|
|
79
79
|
```bash
|
|
80
80
|
pnpm typecheck
|
|
@@ -94,3 +94,7 @@ pnpm test:e2e
|
|
|
94
94
|
## 优先 Skills
|
|
95
95
|
|
|
96
96
|
构建与验收:`hai-build`、`hai-app-review`、`hai-app-tests`。管理业务按需读取 `hai-iam`、`hai-reldb`、`hai-cache`、`hai-audit`、`hai-storage`、`hai-crypto`;配置与跨端读取 `hai-core`、`hai-ui`、`hai-serv`、`hai-api-contract`、`hai-api-client`、`hai-capacitor`。
|
|
97
|
+
|
|
98
|
+
前端页面先按 [hai-ui-design](.agents/skills/hai-ui-design/SKILL.md) 确定任务、布局、状态与组件映射,使用 [hai-ui](.agents/skills/hai-ui/SKILL.md) 实现,再由 [hai-ui-review](.agents/skills/hai-ui-review/SKILL.md) 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
99
|
+
|
|
100
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
## 架构边界
|
|
38
38
|
|
|
39
39
|
- 使用移动端友好的布局和 `@h-ai/ui` 移动组件。
|
|
40
|
-
- 页面文本走 i18n
|
|
40
|
+
- 页面文本走 i18n,同步中英日文 messages。
|
|
41
41
|
- API 输入必须校验,业务错误按 HaiResult 传递。
|
|
42
42
|
- `+server.ts` 只能导出 handler 和 SvelteKit 允许的配置项;helper 放到 `src/lib/**`。
|
|
43
43
|
- 敏感 token 不要写入不安全存储;优先复用模板已有的鉴权与请求模式。
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -80,3 +80,7 @@ pnpm test:e2e
|
|
|
80
80
|
- `hai-build`、`hai-app-create`、`hai-app-review`、`hai-app-tests`
|
|
81
81
|
- `hai-kit`、`hai-ui`、`hai-reldb`、`hai-cache`
|
|
82
82
|
- 其它 `hai-*` 模块按需读取
|
|
83
|
+
|
|
84
|
+
前端页面先按 [hai-ui-design](.agents/skills/hai-ui-design/SKILL.md) 确定任务、布局、状态与组件映射,使用 [hai-ui](.agents/skills/hai-ui/SKILL.md) 实现,再由 [hai-ui-review](.agents/skills/hai-ui-review/SKILL.md) 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
85
|
+
|
|
86
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
- 需求迁移到 SvelteKit 时同步运行环境、路由、初始化、构建与原生壳集成;不能仅添加 `+page.svelte` 等文件就视为完成迁移。
|
|
43
43
|
- 原生能力通过 `@h-ai/capacitor` 使用;token 原生端使用 Capacitor 安全存储,Web 预览仅使用内存存储。
|
|
44
44
|
- API 调用使用 `@h-ai/api-client`,业务错误按 HaiResult 处理。
|
|
45
|
-
- 页面文本走 i18n
|
|
45
|
+
- 页面文本走 i18n,同步中英日文 messages。
|
|
46
46
|
- 原生桥接与设备调用放在 `src/lib/capacitor.ts` 或 service,避免散落在页面模板中。
|
|
47
47
|
- 本样板默认不使用 `@h-ai/core`、`@h-ai/kit`、`@h-ai/serv`、`@h-ai/api-contract` 作为主架构。
|
|
48
48
|
|
|
@@ -71,7 +71,7 @@ pnpm cap:build:android:release
|
|
|
71
71
|
|
|
72
72
|
## 质量门禁
|
|
73
73
|
|
|
74
|
-
先核对 package.json
|
|
74
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
pnpm typecheck
|
|
@@ -92,3 +92,7 @@ pnpm test:e2e
|
|
|
92
92
|
- `hai-build`、`hai-app-review`、`hai-app-tests`
|
|
93
93
|
- `hai-ui`、`hai-api-client`、`hai-capacitor`
|
|
94
94
|
- 其它 `hai-*` 模块按需读取
|
|
95
|
+
|
|
96
|
+
前端页面先按 [hai-ui-design](.agents/skills/hai-ui-design/SKILL.md) 确定任务、布局、状态与组件映射,使用 [hai-ui](.agents/skills/hai-ui/SKILL.md) 实现,再由 [hai-ui-review](.agents/skills/hai-ui-review/SKILL.md) 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
97
|
+
|
|
98
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
## 架构边界
|
|
38
38
|
|
|
39
|
-
- 页面内容与导航文案全部走 i18n
|
|
39
|
+
- 页面内容与导航文案全部走 i18n,同步中英日文 messages。
|
|
40
40
|
- 表单和 API 输入必须 Zod 校验。
|
|
41
41
|
- UI 组件优先使用 `@h-ai/ui`,不要重复实现已有组件。
|
|
42
42
|
- `+server.ts` 只能导出 handler 和 SvelteKit 允许的配置项;helper 放到 `src/lib/**`。
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
## 质量门禁
|
|
62
62
|
|
|
63
|
-
先核对 package.json
|
|
63
|
+
先核对 package.json,按 [分轮验证与集中修复](.agents/skills/hai-app-tests/validation.md) 收集适用检查与日志,再归并修复、复测失败及受影响项;普通失败不中断独立检查,真实前置阻塞才暂停依赖项。文档检查引用,UI/路由变更加 E2E,未运行项说明原因。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
pnpm typecheck
|
|
@@ -80,3 +80,7 @@ pnpm test:e2e
|
|
|
80
80
|
- `hai-build`、`hai-app-create`、`hai-app-review`、`hai-app-tests`
|
|
81
81
|
- `hai-kit`、`hai-ui`、`hai-reldb`、`hai-cache`
|
|
82
82
|
- 其它 `hai-*` 模块按需读取
|
|
83
|
+
|
|
84
|
+
前端页面先按 [hai-ui-design](.agents/skills/hai-ui-design/SKILL.md) 确定任务、布局、状态与组件映射,使用 [hai-ui](.agents/skills/hai-ui/SKILL.md) 实现,再由 [hai-ui-review](.agents/skills/hai-ui-review/SKILL.md) 检查真实页面。窄范围修改复用已有设计;按实际语言/主题/尺寸验证。
|
|
85
|
+
|
|
86
|
+
工作流技能按 hai-领域-动作 命名,能力使用类沿用领域名。需求、验收、发现与当前版本证据按 [工作交接契约](.agents/skills/hai-spec-implement/handoff.md) 传递,沿既有清单恢复任务。
|
|
@@ -13,12 +13,14 @@ description: "接入 @h-ai/ai 的 LLM、工具/MCP、RAG、会话记忆、图像
|
|
|
13
13
|
| 适用场景 | AI 应用服务端集成或浏览器代理接入 |
|
|
14
14
|
| 输入 | AIConfigInput、消息/音频/图片、scope、AbortSignal |
|
|
15
15
|
| 输出 | 领域操作为 HaiResult;流为 AsyncIterable,迭代可抛错 |
|
|
16
|
-
| 限制 | tools/stream 无需 init
|
|
16
|
+
| 限制 | tools/stream 无需 init;其他领域先初始化。生产须先初始化共享 PostgreSQL/MySQL + 网络 vecdb 或传 stateScope=shared 的 Provider;临时 Store 仅开发/测试,关闭后丢失。工具调用仍需业务授权。 |
|
|
17
17
|
|
|
18
18
|
## 使用路径
|
|
19
19
|
|
|
20
20
|
- 先选择完整结果还是流式 API;不要对 AsyncIterable 取 `.success`。流取消/错误须结束播放器和连接。
|
|
21
|
-
- Memory/Context 用 objectId/sessionId
|
|
21
|
+
- Memory/Context 用 objectId/sessionId 隔离主体;Context scope 必填,跨节点读先 reload/restore,manual pending 可跨节点按 turnId 幂等提交实际文本。共享 Store 必须实现版本 CAS/存储端期限;过期生成不自动重放外部副作用。
|
|
22
|
+
- Mem0 生产须实际映射持久 Qdrant;内存回退开关不能绕过。列表/条件清理最多 1000 条主体原始候选,超限检查 `MEMORY_LIST_INCOMPLETE`;禁止把截断结果当完整 total。
|
|
23
|
+
- HTTP AI 使用 `@h-ai/api-client` 的 typed 领域方法;A2A 由应用定义并绑定自己的 contract;`@h-ai/ai/client` 只提供独立 `createAudioClient`,不再封装字符串路径 HTTP 或重复 SSE helper。
|
|
22
24
|
- 语音客户端用一次性 ticket,不能把 IAM access token 放 URL;按 segment_started 的真实格式解码。
|
|
23
25
|
- 压缩超过硬预算须停止模型请求;不能失败后发送原始超长消息。
|
|
24
26
|
|
|
@@ -42,3 +44,7 @@ description: "接入 @h-ai/ai 的 LLM、工具/MCP、RAG、会话记忆、图像
|
|
|
42
44
|
- 质量与安全检查
|
|
43
45
|
|
|
44
46
|
补充类型/错误码与范本见 [reference.md](reference.md),仅在上述用法不足时读取。
|
|
47
|
+
|
|
48
|
+
公开 API 示例以安装包的实际入口为准;框架源码中的内部工厂例子仅供模块维护。HaiResult 调用先检查 success,流与浏览器 AudioClient 按显式抛错契约处理;Context flush 失败和 A2A 未知外部效果不得盲目重放。
|
|
49
|
+
|
|
50
|
+
Node 自定义 SSE 使用 ai.stream 的统一编解码,不用 split/trim 手拆上游事件;encoder 的非法元数据会显式抛错。Anthropic/MiMo 的真实 HTTP 流在退出和取消时释放正文,EOF 不伪造未完成事件,不自动重连或重放业务。
|