@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.en.md +66 -257
  2. package/README.ja.md +66 -257
  3. package/README.md +66 -257
  4. package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
  5. package/dist/chunk-7DSURTJ4.js.map +1 -0
  6. package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
  7. package/dist/chunk-AOQ4N657.js.map +1 -0
  8. package/dist/cli-config-templates-6AHNUGBN.js +5 -0
  9. package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
  10. package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
  11. package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
  12. package/dist/index.js +435 -247
  13. package/dist/index.js.map +1 -1
  14. package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
  15. package/dist/src-2V6VP4I5.js.map +1 -0
  16. package/package.json +8 -8
  17. package/templates/README.md +48 -145
  18. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
  19. package/templates/apps/api/.dockerignore.hbs +14 -7
  20. package/templates/apps/api/.gitignore.hbs +7 -0
  21. package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
  22. package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
  23. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
  24. package/templates/apps/api/hai.build.json.hbs +1 -1
  25. package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
  26. package/templates/apps/api/package.json.hbs +5 -0
  27. package/templates/apps/fullstack/.dockerignore.hbs +14 -9
  28. package/templates/apps/fullstack/.gitignore.hbs +7 -0
  29. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
  30. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
  31. package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
  32. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
  33. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
  34. package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
  35. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
  36. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
  37. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
  38. package/templates/apps/fullstack/hai.build.json.hbs +2 -2
  39. package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
  40. package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
  41. package/templates/apps/fullstack/package.json.hbs +2 -0
  42. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
  43. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
  44. package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
  45. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
  46. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
  47. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
  48. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
  49. package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
  50. package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
  51. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
  52. package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
  53. package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
  54. package/templates/base/.dockerignore.hbs +14 -8
  55. package/templates/base/.gitignore.hbs +7 -0
  56. package/templates/base/e2e/app.spec.ts.hbs +10 -0
  57. package/templates/base/hai.build.json.hbs +1 -1
  58. package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
  59. package/templates/base/package.json.hbs +8 -3
  60. package/templates/base/vite.config.ts.hbs +3 -1
  61. package/templates/shared/.gitattributes +6 -0
  62. package/templates/skills/AGENTS.md +3 -3
  63. package/templates/skills/bridges/admin/AGENTS.md +1 -1
  64. package/templates/skills/bridges/api/AGENTS.md +1 -1
  65. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
  66. package/templates/skills/bridges/h5/AGENTS.md +1 -1
  67. package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
  68. package/templates/skills/bridges/website/AGENTS.md +1 -1
  69. package/templates/skills/hai-api-client/SKILL.md +14 -161
  70. package/templates/skills/hai-api-client/reference.md +166 -0
  71. package/templates/skills/hai-api-contract/SKILL.md +15 -273
  72. package/templates/skills/hai-api-contract/reference.md +278 -0
  73. package/templates/skills/hai-app-create/SKILL.md +2 -2
  74. package/templates/skills/hai-app-create/development.md +4 -4
  75. package/templates/skills/hai-app-create/reference.md +5 -4
  76. package/templates/skills/hai-app-review/SKILL.md +1 -1
  77. package/templates/skills/hai-app-tests/SKILL.md +4 -3
  78. package/templates/skills/hai-app-tests/reference.md +2 -2
  79. package/templates/skills/hai-app-tests/validation.md +31 -0
  80. package/templates/skills/hai-audit/SKILL.md +17 -142
  81. package/templates/skills/hai-audit/reference.md +145 -0
  82. package/templates/skills/hai-build/SKILL.md +14 -8
  83. package/templates/skills/hai-build/reference.md +10 -9
  84. package/templates/skills/hai-cache/SKILL.md +15 -182
  85. package/templates/skills/hai-cache/reference.md +187 -0
  86. package/templates/skills/hai-capacitor/SKILL.md +4 -3
  87. package/templates/skills/hai-capacitor/reference.md +6 -4
  88. package/templates/skills/hai-ci/SKILL.md +2 -0
  89. package/templates/skills/hai-core/SKILL.md +3 -3
  90. package/templates/skills/hai-crypto/SKILL.md +16 -252
  91. package/templates/skills/hai-crypto/reference.md +256 -0
  92. package/templates/skills/hai-datapipe/SKILL.md +15 -196
  93. package/templates/skills/hai-datapipe/reference.md +202 -0
  94. package/templates/skills/hai-deploy/SKILL.md +20 -55
  95. package/templates/skills/hai-deploy/middleware.md +5 -11
  96. package/templates/skills/hai-framework-sync/SKILL.md +2 -2
  97. package/templates/skills/hai-framework-sync/sync.md +1 -1
  98. package/templates/skills/hai-iam/SKILL.md +1 -3
  99. package/templates/skills/hai-kit/SKILL.md +12 -167
  100. package/templates/skills/hai-kit/reference.md +172 -0
  101. package/templates/skills/hai-payment/SKILL.md +19 -251
  102. package/templates/skills/hai-payment/reference.md +256 -0
  103. package/templates/skills/hai-pr-review/SKILL.md +4 -4
  104. package/templates/skills/hai-reach/SKILL.md +2 -2
  105. package/templates/skills/hai-reldb/SKILL.md +1 -1
  106. package/templates/skills/hai-scheduler/SKILL.md +15 -195
  107. package/templates/skills/hai-scheduler/reference.md +202 -0
  108. package/templates/skills/hai-serv/SKILL.md +2 -2
  109. package/templates/skills/hai-spec-implement/SKILL.md +1 -1
  110. package/templates/skills/hai-spec-implement/workflow.md +3 -3
  111. package/templates/skills/hai-storage/SKILL.md +17 -238
  112. package/templates/skills/hai-storage/reference.md +243 -0
  113. package/templates/skills/hai-ui/SKILL.md +1 -1
  114. package/templates/skills/hai-ui/reference.md +9 -5
  115. package/templates/skills/hai-vecdb/SKILL.md +2 -2
  116. package/dist/chunk-FWN5B54L.js.map +0 -1
  117. package/dist/chunk-QYX6V7H2.js.map +0 -1
  118. package/dist/cli-config-templates-3XSFIXBP.js +0 -5
  119. package/dist/src-RDQ2EIBT.js.map +0 -1
  120. package/templates/apps/api/Dockerfile.hbs +0 -35
  121. package/templates/apps/fullstack/Dockerfile.hbs +0 -57
  122. /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
  123. /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
  124. /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
  125. /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-kit
3
- description: "使用 @h-ai/kit 集成 SvelteKit hooks、守卫、校验、响应及同源客户端传输。"
3
+ description: "在 SvelteKit 中接入 hooks、认证守卫、输入校验、响应和同源加密客户端;不用于独立 serv 服务。"
4
4
  ---
5
5
 
6
6
  # hai-kit
@@ -15,173 +15,18 @@ description: "使用 @h-ai/kit 集成 SvelteKit hooks、守卫、校验、响应
15
15
  | 输出 | handle、Response、校验值和客户端请求结果 |
16
16
  | 限制 | 无 init/close;保留 SvelteKit 控制流异常。跨端共享 HTTP contract 用 api-contract + serv,勿复制到 kit。 |
17
17
 
18
- > `@h-ai/kit` 只负责 SvelteKit 集成:`handle` hook、guard、response、validate、client、auth、crud、A2A。公共跨端 HTTP API 契约统一使用 `@h-ai/api-contract` + `@h-ai/serv`,不要在 kit 中定义业务 API contract。
18
+ ## 使用路径
19
19
 
20
- ## 使用步骤
20
+ 1. 仅在 SvelteKit 集成;先查现有 hooks、配置、守卫与路由,不将其套用于独立 serv 应用。
21
+ 2. 用 createHandle 编排已初始化模块;认证 API 与公开路径复用 kit.auth,客户端复用框架动作。
22
+ 3. 区分返回 HaiResult 的业务操作与抛 Response/redirect/error 的框架控制流,不吞掉控制流。
23
+ 4. 对齐 Cookie/CSRF/transport 与服务端配置,验证匿名、登录、权限拒绝和表单失败。
21
24
 
22
- ### 1. 请求管道
25
+ ## 按需参考
23
26
 
24
- ```ts
25
- import type { Handle } from '@sveltejs/kit'
26
- import { kit } from '@h-ai/kit'
27
+ 按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
27
28
 
28
- const haiHandle = kit.createHandle({
29
- auth: {
30
- verifyToken: async token => token ? { userId: 'u_1', roles: ['admin'], permissions: ['user:read'] } : null,
31
- loginUrl: '/auth/login',
32
- protectedPaths: ['/admin/*'],
33
- },
34
- rateLimit: { windowMs: 60_000, maxRequests: 100 },
35
- logging: true,
36
- })
37
-
38
- export const handle: Handle = haiHandle
39
- ```
40
-
41
- ### 2. SvelteKit API endpoint
42
-
43
- ```ts
44
- import { kit } from '@h-ai/kit'
45
- import { z } from 'zod'
46
-
47
- const CreateUserSchema = z.object({ name: z.string().min(1) })
48
-
49
- export const POST = kit.handler(async ({ request, locals }) => {
50
- kit.guard.require(locals.session, 'user:create')
51
- const payload = await kit.validate.body(request, CreateUserSchema)
52
- return kit.response.created(payload)
53
- })
54
- ```
55
-
56
- ### 3. 浏览器同源请求
57
-
58
- ```ts
59
- import { kit } from '@h-ai/kit'
60
-
61
- const client = kit.client.create()
62
- export const { apiFetch } = client
63
- ```
64
-
65
- > 同源 endpoint 优先使用 httpOnly Cookie;`auth: true` 只读取 `kit.auth.setBrowserToken()` 写入的页面内存 Token,不会默认读写 localStorage。确需 Bearer Header 时,显式传入 `BrowserTokenStore`,禁止把敏感 Token 存入 localStorage。
66
-
67
- ### 4. 同源传输加密
68
-
69
- kit 不再维护本地传输加密实现;服务端和客户端都统一委托 `@h-ai/crypto` 的 `crypto.transport`。
70
-
71
- ```ts
72
- // hooks.server.ts
73
- import { cache } from '@h-ai/cache'
74
- import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
75
- import { kit } from '@h-ai/kit'
76
-
77
- await crypto.init()
78
- await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
79
-
80
- export const handle = kit.createHandle({
81
- auth: {
82
- verifyToken,
83
- protectedPaths: ['/api/*'],
84
- publicPaths: ['/api/_hai/*'],
85
- },
86
- crypto: {
87
- crypto,
88
- transport: {
89
- requireEncryption: true,
90
- keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
91
- },
92
- },
93
- })
94
- ```
95
-
96
- 需要保护会话 Cookie 时配置 `crypto.encryptedCookies`,并通过 `crypto.cookieEncryptionKey` 或 `HAI_KIT_COOKIE_KEY` 提供密钥。缺少密钥会在 `createHandle()` 时失败;加解密错误不会回退到明文 Cookie。
97
-
98
- ```ts
99
- // routes/+layout.svelte
100
- <script lang='ts'>
101
- import { browser } from '$app/environment'
102
- import { crypto } from '@h-ai/crypto'
103
- import { kit } from '@h-ai/kit'
104
- import { appKitConfig } from '$lib/config/kit-config'
105
-
106
- // 一次性安装:内部按 appKitConfig.transport 是否启用决定行为,并预热 crypto
107
- if (browser) {
108
- kit.client.installBrowserTransport(appKitConfig, { crypto })
109
- }
110
- </script>
111
- ```
112
-
113
- ```ts
114
- // lib/utils/api.ts —— 业务层只看到 apiFetch
115
- import { kit } from '@h-ai/kit'
116
-
117
- export const { apiFetch } = kit.client.create()
118
- ```
119
-
120
- 默认协商端点:`/api/_hai/key-exchange`。如 `keyExchangePath` 自定义,浏览器端同步设置 `keyExchangeUrl`。
121
-
122
- 多节点部署时,可在 `kit.createHandle({ crypto: { transport } })` 的运行时对象里直接传 `keyStore`;推荐使用 `@h-ai/crypto` 根入口导出的 `createRedisTransportKeyStore()` / `createReldbTransportKeyStore()`。这类对象依赖不要写进 `_kit.yml`。
123
-
124
- transport 默认保护同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页面数据请求;页面文档、静态资源与 `multipart/form-data` 上传请求保持明文透传。
125
-
126
- 安全策略默认 fail-closed:`requireEncryption: true` 时,受保护路径缺少 `X-Client-Id` 必须返回 400;服务端 transport 管理器不可用、响应体无法加密或超过单次加密上限时返回错误,禁止明文业务响应回退。`requireEncryption: false` 只适合迁移期灰度。
127
-
128
- ### 5. 使用 `_kit.yml` 统一 transport 配置
129
-
130
- `@h-ai/kit` 现在提供 `KitConfigSchema` / `resolveKitConfig()`,适合同一份 `_kit.yml`
131
- 同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
132
-
133
- ```yml
134
- server:
135
- host: 127.0.0.1
136
- port: 3000
137
-
138
- transport:
139
- keyExchangePath: /api/_hai/key-exchange
140
- requireEncryption: true
141
- encryptResponse: true
142
- excludePaths:
143
- - /api/storage
144
- - /api/public
145
- - /api/auth/profile/avatar
146
- maxClients: 10000
147
- ```
148
-
149
- ```ts
150
- import { resolveKitConfig } from '@h-ai/kit'
151
- import { parse } from 'yaml'
152
- import rawKitConfig from '../config/_kit.yml?raw'
153
-
154
- export const appKitConfig = resolveKitConfig(parse(rawKitConfig) ?? {})
155
- ```
156
-
157
- `resolveKitConfig()` 同时解析 `server`(host/port)与 `transport`;host/port 优先级为 `HOST` / `PORT` 环境变量 > `_kit.yml` 的 `server` > 默认 `127.0.0.1:3000`(与 `@h-ai/serv` 同构)。
158
- `_kit.yml` 只放公开的 server/transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
159
-
160
- ## 核心 API
161
-
162
- | API | 用途 |
163
- | --- | --- |
164
- | `kit.createHandle()` | 创建 SvelteKit Handle |
165
- | `kit.sequence()` | 组合多个 Handle |
166
- | `kit.handler()` | API endpoint 错误边界 |
167
- | `kit.guard.require()` | 权限守卫 |
168
- | `kit.response.*` | 统一响应工厂 |
169
- | `kit.validate.*` | Zod 请求校验 |
170
- | `kit.auth.*` | Cookie / Token 辅助 |
171
- | `kit.client.create()` | 浏览器端同源 apiFetch |
172
- | `kit.client.installBrowserTransport(config, { crypto })` | 推荐入口:按解析后的 `_kit.yml` 一键安装浏览器端传输加密 |
173
- | `kit.client.installBrowserTransportFetch()` | 底层入口:直接传 transport 配置安装浏览器全局 fetch 包装 |
174
- | `kit.crud.define()` | 声明式 CRUD 资源 |
175
-
176
- ## 常见模式
177
-
178
- - SvelteKit 应用内同源请求用 `kit.client.create().apiFetch`。
179
- - SvelteKit 应用启用 transport 时,在 `+layout.svelte` 的 `if (browser)` 分支调用一次 `kit.client.installBrowserTransport(appKitConfig, { crypto })`,无需重复连线 `keyExchangeUrl` / `excludePaths`。
180
- - Web/App/小程序跨域访问公共 API 用 `@h-ai/api-client` typed client。
181
- - 传输加密只通过 `crypto.transport` 间接装配,不在 kit 中新增本地加密工厂或鸭子类型。
182
- - 服务端业务模块直接调用模块 API,不通过 HTTP 自环。
183
- - API endpoint 必须用 Zod 校验用户输入,并设置认证/权限边界。
184
-
185
- ## 声明式 CRUD 表单字段
186
-
187
- `kit.crud.define` 的字段支持 `colSpan: 'auto' | 1 | 2`,以及 `validation.message: string | (() => string)`。布局通过 UI 的 `CrudPage.form` 配置;客户端校验用于交互提示,服务端仍使用 Zod 验证和现有权限守卫。UI 公共契约见相邻 [hai-ui skill](../hai-ui/SKILL.md)。
29
+ - [使用步骤](reference.md#使用步骤)
30
+ - [核心 API](reference.md#核心-api)
31
+ - [常见模式](reference.md#常见模式)
32
+ - [声明式 CRUD 表单字段](reference.md#声明式-crud-表单字段)
@@ -0,0 +1,172 @@
1
+ # hai-kit 参考
2
+
3
+ > `@h-ai/kit` 只负责 SvelteKit 集成:`handle` hook、guard、response、validate、client、auth、crud、A2A。公共跨端 HTTP API 契约统一使用 `@h-ai/api-contract` + `@h-ai/serv`,不要在 kit 中定义业务 API contract。
4
+
5
+ ## 使用步骤
6
+
7
+ ### 1. 请求管道
8
+
9
+ ```ts
10
+ import type { Handle } from '@sveltejs/kit'
11
+ import { kit } from '@h-ai/kit'
12
+
13
+ const haiHandle = kit.createHandle({
14
+ auth: {
15
+ verifyToken: async token => token ? { userId: 'u_1', roles: ['admin'], permissions: ['user:read'] } : null,
16
+ loginUrl: '/auth/login',
17
+ protectedPaths: ['/admin/*'],
18
+ },
19
+ rateLimit: { windowMs: 60_000, maxRequests: 100 },
20
+ logging: true,
21
+ })
22
+
23
+ export const handle: Handle = haiHandle
24
+ ```
25
+
26
+ ### 2. SvelteKit API endpoint
27
+
28
+ ```ts
29
+ import { kit } from '@h-ai/kit'
30
+ import { z } from 'zod'
31
+
32
+ const CreateUserSchema = z.object({ name: z.string().min(1) })
33
+
34
+ export const POST = kit.handler(async ({ request, locals }) => {
35
+ kit.guard.require(locals.session, 'user:create')
36
+ const payload = await kit.validate.body(request, CreateUserSchema)
37
+ return kit.response.created(payload)
38
+ })
39
+ ```
40
+
41
+ ### 3. 浏览器同源请求
42
+
43
+ ```ts
44
+ import { kit } from '@h-ai/kit'
45
+
46
+ const client = kit.client.create()
47
+ export const { apiFetch } = client
48
+ ```
49
+
50
+ > 同源 endpoint 优先使用 httpOnly Cookie;`auth: true` 只读取 `kit.auth.setBrowserToken()` 写入的页面内存 Token,不会默认读写 localStorage。确需 Bearer Header 时,显式传入 `BrowserTokenStore`,禁止把敏感 Token 存入 localStorage。
51
+
52
+ ### 4. 同源传输加密
53
+
54
+ kit 不再维护本地传输加密实现;服务端和客户端都统一委托 `@h-ai/crypto` 的 `crypto.transport`。
55
+
56
+ ```ts
57
+ // hooks.server.ts
58
+ import { cache } from '@h-ai/cache'
59
+ import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
60
+ import { kit } from '@h-ai/kit'
61
+
62
+ await crypto.init()
63
+ await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
64
+
65
+ export const handle = kit.createHandle({
66
+ auth: {
67
+ verifyToken,
68
+ protectedPaths: ['/api/*'],
69
+ publicPaths: ['/api/_hai/*'],
70
+ },
71
+ crypto: {
72
+ crypto,
73
+ transport: {
74
+ requireEncryption: true,
75
+ keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
76
+ },
77
+ },
78
+ })
79
+ ```
80
+
81
+ 需要保护会话 Cookie 时配置 `crypto.encryptedCookies`,并通过 `crypto.cookieEncryptionKey` 或 `HAI_KIT_COOKIE_KEY` 提供密钥。缺少密钥会在 `createHandle()` 时失败;加解密错误不会回退到明文 Cookie。
82
+
83
+ ```ts
84
+ // routes/+layout.svelte
85
+ <script lang='ts'>
86
+ import { browser } from '$app/environment'
87
+ import { crypto } from '@h-ai/crypto'
88
+ import { kit } from '@h-ai/kit'
89
+ import { appKitConfig } from '$lib/config/kit-config'
90
+
91
+ // 一次性安装:内部按 appKitConfig.transport 是否启用决定行为,并预热 crypto
92
+ if (browser) {
93
+ kit.client.installBrowserTransport(appKitConfig, { crypto })
94
+ }
95
+ </script>
96
+ ```
97
+
98
+ ```ts
99
+ // lib/utils/api.ts —— 业务层只看到 apiFetch
100
+ import { kit } from '@h-ai/kit'
101
+
102
+ export const { apiFetch } = kit.client.create()
103
+ ```
104
+
105
+ 默认协商端点:`/api/_hai/key-exchange`。如 `keyExchangePath` 自定义,浏览器端同步设置 `keyExchangeUrl`。
106
+
107
+ 多节点部署时,可在 `kit.createHandle({ crypto: { transport } })` 的运行时对象里直接传 `keyStore`;推荐使用 `@h-ai/crypto` 根入口导出的 `createRedisTransportKeyStore()` / `createReldbTransportKeyStore()`。这类对象依赖不要写进 `_kit.yml`。
108
+
109
+ transport 默认保护同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页面数据请求;页面文档、静态资源与 `multipart/form-data` 上传请求保持明文透传。
110
+
111
+ 安全策略默认 fail-closed:`requireEncryption: true` 时,受保护路径缺少 `X-Client-Id` 必须返回 400;服务端 transport 管理器不可用、响应体无法加密或超过单次加密上限时返回错误,禁止明文业务响应回退。`requireEncryption: false` 只适合迁移期灰度。
112
+
113
+ ### 5. 使用 `_kit.yml` 统一 transport 配置
114
+
115
+ `@h-ai/kit` 现在提供 `KitConfigSchema` / `resolveKitConfig()`,适合同一份 `_kit.yml`
116
+ 同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
117
+
118
+ ```yml
119
+ server:
120
+ host: 127.0.0.1
121
+ port: 3000
122
+
123
+ transport:
124
+ keyExchangePath: /api/_hai/key-exchange
125
+ requireEncryption: true
126
+ encryptResponse: true
127
+ excludePaths:
128
+ - /api/storage
129
+ - /api/public
130
+ - /api/auth/profile/avatar
131
+ maxClients: 10000
132
+ ```
133
+
134
+ ```ts
135
+ import { resolveKitConfig } from '@h-ai/kit'
136
+ import { parse } from 'yaml'
137
+ import rawKitConfig from '../config/_kit.yml?raw'
138
+
139
+ export const appKitConfig = resolveKitConfig(parse(rawKitConfig) ?? {})
140
+ ```
141
+
142
+ `resolveKitConfig()` 同时解析 `server`(host/port)与 `transport`;host/port 优先级为 `HOST` / `PORT` 环境变量 > `_kit.yml` 的 `server` > 默认 `127.0.0.1:3000`(与 `@h-ai/serv` 同构)。
143
+ `_kit.yml` 只放公开的 server/transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
144
+
145
+ ## 核心 API
146
+
147
+ | API | 用途 |
148
+ | --- | --- |
149
+ | `kit.createHandle()` | 创建 SvelteKit Handle |
150
+ | `kit.sequence()` | 组合多个 Handle |
151
+ | `kit.handler()` | API endpoint 错误边界 |
152
+ | `kit.guard.require()` | 权限守卫 |
153
+ | `kit.response.*` | 统一响应工厂 |
154
+ | `kit.validate.*` | Zod 请求校验 |
155
+ | `kit.auth.*` | Cookie / Token 辅助 |
156
+ | `kit.client.create()` | 浏览器端同源 apiFetch |
157
+ | `kit.client.installBrowserTransport(config, { crypto })` | 推荐入口:按解析后的 `_kit.yml` 一键安装浏览器端传输加密 |
158
+ | `kit.client.installBrowserTransportFetch()` | 底层入口:直接传 transport 配置安装浏览器全局 fetch 包装 |
159
+ | `kit.crud.define()` | 声明式 CRUD 资源 |
160
+
161
+ ## 常见模式
162
+
163
+ - SvelteKit 应用内同源请求用 `kit.client.create().apiFetch`。
164
+ - SvelteKit 应用启用 transport 时,在 `+layout.svelte` 的 `if (browser)` 分支调用一次 `kit.client.installBrowserTransport(appKitConfig, { crypto })`,无需重复连线 `keyExchangeUrl` / `excludePaths`。
165
+ - Web/App/小程序跨域访问公共 API 用 `@h-ai/api-client` typed client。
166
+ - 传输加密只通过 `crypto.transport` 间接装配,不在 kit 中新增本地加密工厂或鸭子类型。
167
+ - 服务端业务模块直接调用模块 API,不通过 HTTP 自环。
168
+ - API endpoint 必须用 Zod 校验用户输入,并设置认证/权限边界。
169
+
170
+ ## 声明式 CRUD 表单字段
171
+
172
+ `kit.crud.define` 的字段支持 `colSpan: 'auto' | 1 | 2`,以及 `validation.message: string | (() => string)`。布局通过 UI 的 `CrudPage.form` 配置;客户端校验用于交互提示,服务端仍使用 Zod 验证和现有权限守卫。UI 公共契约见相邻 [hai-ui skill](../hai-ui/SKILL.md)。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-payment
3
- description: "使用 @h-ai/payment 创建支付订单、验证回调、查单和退款,客户端调起支付。"
3
+ description: "接入微信、支付宝或 Stripe 的订单、回调验签、查单与退款,并处理客户端调起和幂等履约。"
4
4
  ---
5
5
 
6
6
  # hai-payment
@@ -15,257 +15,25 @@ description: "使用 @h-ai/payment 创建支付订单、验证回调、查单和
15
15
  | 输出 | 服务端 HaiResult;客户端 invokePayment 按 client 契约使用 |
16
16
  | 限制 | 服务端保存凭据并验签;客户端完成不代表付款成功,以可信回调/查单为准。订单履约与重试需幂等。 |
17
17
 
18
- > `@h-ai/payment` 是 hai-framework 的统一支付模块,通过 Provider 模式支持微信支付、支付宝、Stripe。服务端处理订单和回调,客户端调起支付。
18
+ ## 使用路径
19
19
 
20
- ## 运行环境
20
+ 1. 服务端校验 Provider 配置后 init,真实凭据经私有配置注入;浏览器只导入 `/client`。
21
+ 2. 订单号、金额单位、商品与用户由服务端可信数据决定,先保存业务订单再创建支付。
22
+ 3. 回调使用原始 body 与 headers 验签;核对订单、金额及状态,幂等更新。客户端调起成功不代表付款完成。
23
+ 4. 查单、退款与回调重试覆盖成功、失败和重复路径;真实支付仅在用户授权范围内,最后 close。
21
24
 
22
- > **服务端 + 浏览器分工模块。** `payment.createOrder` / `payment.handleNotify` / `payment.refund` 等操作在 Node.js 端执行。浏览器端使用 `invokePayment()`(从 `@h-ai/payment/client` 导入)调起支付。
25
+ ## 按需参考
23
26
 
24
- ## 依赖
27
+ 按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
25
28
 
26
- | 模块 | 用途 | 是否必需 | 初始化要求 |
27
- | --- | --- | --- | --- |
28
- | `@h-ai/audit` | 审计日志(支付操作自动审计) | 可选 | 已初始化时自动写入审计日志 |
29
-
30
- ## 适用场景
31
-
32
- - 接入微信支付(JSAPI / H5 / Native / App)
33
- - 接入支付宝(H5 / App / PC)
34
- - 接入 Stripe(Checkout Session)
35
- - 支付回调验签与通知处理
36
- - 订单查询与退款
37
- - 客户端调起支付(Web / H5 / App)
38
-
39
- ## 使用步骤
40
-
41
- ### 1. 配置
42
-
43
- ```yaml
44
- # config/_payment.yml(自行创建)
45
- wechat:
46
- appId: ''
47
- mchId: ''
48
- apiV3Key: ''
49
- privateKey: ''
50
- serialNo: ''
51
- platformCert: '' # 可选,回调验签需要
52
-
53
- alipay:
54
- appId: ''
55
- privateKey: ''
56
- alipayPublicKey: ''
57
- signType: RSA2 # 可选,默认 RSA2
58
- sandbox: false # 可选,默认 false
59
-
60
- stripe:
61
- secretKey: ''
62
- webhookSecret: ''
63
- webhookToleranceSeconds: 300 # 可选,默认 300 秒
64
- ```
65
-
66
- 约定环境变量优先于 YAML,例如 `HAI_PAYMENT_WECHAT_APIV3KEY`、
67
- `HAI_PAYMENT_STRIPE_WEBHOOKSECRET`。生产密钥只放环境或密钥管理器。
68
-
69
- ### 2. 初始化(服务端)
70
-
71
- ```typescript
72
- import { payment } from '@h-ai/payment'
73
-
74
- const initResult = await payment.init({
75
- wechat: {
76
- appId: 'wx1234567890',
77
- mchId: '1600000000',
78
- apiV3Key: 'your-api-key-v3',
79
- privateKey: '-----BEGIN RSA PRIVATE KEY-----...',
80
- serialNo: 'CERT_SERIAL_NO',
81
- },
82
- alipay: {
83
- appId: '2021000000000000',
84
- privateKey: '-----BEGIN RSA PRIVATE KEY-----...',
85
- alipayPublicKey: '-----BEGIN PUBLIC KEY-----...',
86
- },
87
- })
88
- if (!initResult.success) {
89
- // 处理初始化错误
90
- }
91
- ```
92
-
93
- ### 3. 创建订单
94
-
95
- ```typescript
96
- const result = await payment.createOrder('wechat', {
97
- orderNo: 'ORDER_20250101_001',
98
- amount: 9900, // 单位:分
99
- description: '商品名称',
100
- tradeType: 'jsapi',
101
- userId: 'user_openid',
102
- notifyUrl: 'https://example.com/payment/notify/wechat',
103
- metadata: { source: 'web' },
104
- })
105
-
106
- if (result.success) {
107
- // result.data: PaymentOrder { provider, tradeType, clientParams, prepayId? }
108
- // clientParams 包含调起支付所需参数
109
- }
110
-
111
- // amount / totalAmount 必须是正整数(单位:分)
112
- ```
113
-
114
- ### 4. 处理支付回调
115
-
116
- ```typescript
117
- // API 路由:POST /payment/notify/:provider
118
- const result = await payment.handleNotify('wechat', {
119
- body: await request.text(),
120
- headers: Object.fromEntries(request.headers),
121
- })
122
-
123
- if (result.success) {
124
- // result.data: { orderNo, transactionId, amount, status, paidAt? }
125
- // 更新订单状态...
126
- }
127
- ```
128
-
129
- ### 5. 查询订单
130
-
131
- ```typescript
132
- const result = await payment.queryOrder('wechat', 'ORDER_20250101_001')
133
-
134
- if (result.success) {
135
- // result.data.status: 'pending' | 'paid' | 'closed' | 'refunded' | 'failed'
136
- }
137
- ```
138
-
139
- ### 6. 退款
140
-
141
- ```typescript
142
- const result = await payment.refund('wechat', {
143
- orderNo: 'ORDER_20250101_001',
144
- refundNo: 'REFUND_20250101_001',
145
- amount: 9900,
146
- totalAmount: 9900, // 微信退款必填
147
- reason: '用户退款',
148
- })
149
- ```
150
-
151
- ### 7. 客户端调起支付(浏览器)
152
-
153
- ```typescript
154
- import { invokePayment } from '@h-ai/payment/client'
155
-
156
- // 前端拿到服务端返回的 PaymentOrder 后调起支付
157
- const payResult = await invokePayment(orderResult.data)
158
- ```
159
-
160
- ## 核心 API(服务端)
161
-
162
- | API | 用途 | 返回值 |
163
- | ------------------------------------------------ | -------- | ----------------------------- |
164
- | `payment.init(config)` | 初始化 | `HaiResult<void>` |
165
- | `payment.close()` | 关闭 | `void` |
166
- | `payment.createOrder(providerName, input)` | 创建订单 | `HaiResult<PaymentOrder>` |
167
- | `payment.handleNotify(providerName, request)` | 处理回调 | `HaiResult<PaymentNotifyResult>` |
168
- | `payment.queryOrder(providerName, orderNo)` | 查询订单 | `HaiResult<OrderStatus>` |
169
- | `payment.refund(providerName, input)` | 退款 | `HaiResult<RefundResult>` |
170
- | `payment.closeOrder(providerName, orderNo)` | 关闭订单 | `HaiResult<void>` |
171
- | `payment.getProvider(name)` | 获取 Provider | `PaymentProvider \| undefined` |
172
- | `payment.registerProvider(provider)` | 注册自定义 Provider | `void` |
173
-
174
- ## 客户端 API
175
-
176
- | API | 用途 | 返回值 |
177
- | ------------------------ | -------- | ----------------------------- |
178
- | `invokePayment(options)` | 调起支付 | `HaiResult<InvokePaymentResult>` |
179
-
180
- ## 错误码 — `HaiPaymentError`
181
-
182
- | 错误码 | code | 说明 |
183
- |--------|------|------|
184
- | `HaiPaymentError.CREATE_ORDER_FAILED` | `hai:payment:001` | 创建订单失败 |
185
- | `HaiPaymentError.QUERY_ORDER_FAILED` | `hai:payment:002` | 查询订单失败 |
186
- | `HaiPaymentError.REFUND_FAILED` | `hai:payment:003` | 退款失败 |
187
- | `HaiPaymentError.CLOSE_ORDER_FAILED` | `hai:payment:004` | 关闭订单失败 |
188
- | `HaiPaymentError.NOT_INITIALIZED` | `hai:payment:010` | 模块未初始化 |
189
- | `HaiPaymentError.SIGN_FAILED` | `hai:payment:020` | 签名失败 |
190
- | `HaiPaymentError.PROVIDER_NOT_FOUND` | `hai:payment:030` | Provider 未注册 |
191
- | `HaiPaymentError.INVALID_AMOUNT` | `hai:payment:040` | 金额无效 |
192
- | `HaiPaymentError.NOTIFY_VERIFY_FAILED` | `hai:payment:050` | 回调验签失败 |
193
- | `HaiPaymentError.NOTIFY_PARSE_FAILED` | `hai:payment:051` | 回调解析失败 |
194
- | `HaiPaymentError.INVOKE_WEB_FAILED` | `hai:payment:060` | Web 端调起支付失败 |
195
- | `HaiPaymentError.INVOKE_APP_FAILED` | `hai:payment:061` | App 端调起支付失败 |
196
- | `HaiPaymentError.CONFIG_ERROR` | `hai:payment:070` | 配置无效 |
197
-
198
- ## Provider 模式
199
-
200
- 内置 Provider:
201
-
202
- - **wechat** — 微信支付 API v3
203
- - **alipay** — 支付宝开放平台
204
- - **stripe** — Stripe Checkout Session 模式
205
-
206
- 自定义 Provider 通过 `payment.registerProvider(provider)` 注册。
207
-
208
- ### HTTP API 契约
209
-
210
- ```typescript
211
- import { apiContract } from '@h-ai/api-contract'
212
-
213
- // apiContract.payment.orders.create — POST /payment/orders
214
- // apiContract.payment.orders.get — GET /payment/orders/{orderNo}
215
- // apiContract.payment.orders.refund — POST /payment/orders/{orderNo}/refunds
216
- // apiContract.payment.notifications.wechat — POST /payment/notifications/wechat
217
- ```
218
-
219
- ## 常见模式
220
-
221
- ### 服务端路由(SvelteKit)
222
-
223
- ```typescript
224
- import { kit } from '@h-ai/kit'
225
- // src/routes/api/v1/payment/create/+server.ts
226
- import { payment } from '@h-ai/payment'
227
-
228
- export const POST = kit.handler(async ({ request, locals }) => {
229
- const body = await request.json()
230
- const result = await payment.createOrder(body.provider, {
231
- orderNo: body.orderNo,
232
- amount: body.amount,
233
- description: body.description,
234
- tradeType: body.tradeType,
235
- userId: body.userId,
236
- notifyUrl: body.notifyUrl,
237
- })
238
-
239
- if (!result.success) {
240
- return kit.response.internalError(result.error.message)
241
- }
242
-
243
- return kit.response.ok(result.data, locals.requestId)
244
- })
245
- ```
246
-
247
- ### 完整支付流程
248
-
249
- ```
250
- 1. 客户端 → 服务端:创建订单(api.payment.orders.create({...}))
251
- 2. 服务端 → 支付平台:调用 Provider 创建预支付
252
- 3. 服务端 → 客户端:返回 providerData(含调起参数)
253
- 4. 客户端:invokePayment(providerData) 调起支付
254
- 5. 支付平台 → 服务端:异步回调通知
255
- 6. 服务端:handleNotify() 验签 + 更新订单状态
256
- 7. 客户端:轮询或回调查询订单状态
257
- ```
258
-
259
- ## 审计日志
260
-
261
- 关键支付操作(创建订单、回调、退款、关闭订单)成功后自动写入审计日志(依赖 `@h-ai/audit`),无需额外配置。`queryOrder` 为只读操作,不写审计日志。
262
-
263
- 内置 Provider 的 HTTP 请求默认带 15 秒超时;Stripe 可通过 `webhookToleranceSeconds` 调整 webhook 时间戳容忍窗口。
264
-
265
- ## 相关 Skills
266
-
267
- - `hai-kit`:服务端 API 路由与契约处理
268
- - `hai-api-client`:客户端契约调用
269
- - `hai-reldb`:订单持久化存储
270
- - `hai-audit`:审计日志(payment 内部自动调用)
271
- - `hai-core`:HaiResult 类型、日志、配置
29
+ - [运行环境](reference.md#运行环境)
30
+ - [依赖](reference.md#依赖)
31
+ - [适用场景](reference.md#适用场景)
32
+ - [使用步骤](reference.md#使用步骤)
33
+ - [核心 API(服务端)](reference.md#核心-api服务端)
34
+ - [客户端 API](reference.md#客户端-api)
35
+ - [错误码 — `HaiPaymentError`](reference.md#错误码--haipaymenterror)
36
+ - [Provider 模式](reference.md#provider-模式)
37
+ - [常见模式](reference.md#常见模式)
38
+ - [审计日志](reference.md#审计日志)
39
+ - [相关 Skills](reference.md#相关-skills)