@h-ai/cli 0.1.0-alpha8 → 0.1.0-alpha9

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/cli",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha8",
4
+ "version": "0.1.0-alpha9",
5
5
  "description": "Hai Framework CLI for project scaffolding and code generation.",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.1.0",
35
35
  "prompts": "^2.4.2",
36
36
  "yaml": "^2.7.0",
37
- "@h-ai/core": "0.1.0-alpha8"
37
+ "@h-ai/core": "0.1.0-alpha9"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/fs-extra": "^11.0.4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{projectName}}",
3
- "version": "0.1.0-alpha8",
3
+ "version": "0.1.0-alpha9",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -14,26 +14,26 @@
14
14
  "test:e2e": "playwright test"
15
15
  },
16
16
  "dependencies": {
17
- "@h-ai/core": "^0.1.0-alpha8",
18
- "@h-ai/kit": "^0.1.0-alpha8"{{#if hasUi}},
19
- "@h-ai/ui": "^0.1.0-alpha8"{{/if}}{{#if features.db}},
20
- "@h-ai/reldb": "^0.1.0-alpha8"{{/if}}{{#if features.cache}},
21
- "@h-ai/cache": "^0.1.0-alpha8"{{/if}}{{#if features.iam}},
22
- "@h-ai/iam": "^0.1.0-alpha8"{{/if}}{{#if features.crypto}},
23
- "@h-ai/crypto": "^0.1.0-alpha8"{{/if}}{{#if features.storage}},
24
- "@h-ai/storage": "^0.1.0-alpha8"{{/if}}{{#if features.ai}},
25
- "@h-ai/ai": "^0.1.0-alpha8"{{/if}}{{#if features.audit}},
26
- "@h-ai/audit": "^0.1.0-alpha8"{{/if}}{{#if features.reach}},
27
- "@h-ai/reach": "^0.1.0-alpha8"{{/if}}{{#if features.payment}},
28
- "@h-ai/payment": "^0.1.0-alpha8"{{/if}}{{#if features.vecdb}},
29
- "@h-ai/vecdb": "^0.1.0-alpha8"{{/if}}{{#if features.datapipe}},
30
- "@h-ai/datapipe": "^0.1.0-alpha8"{{/if}}{{#if features.scheduler}},
31
- "@h-ai/scheduler": "^0.1.0-alpha8"{{/if}}{{#if features.deploy}},
32
- "@h-ai/deploy": "^0.1.0-alpha8"{{/if}}{{#if features.api-client}},
33
- "@h-ai/api-client": "^0.1.0-alpha8"{{/if}}{{#if features.capacitor}},
34
- "@h-ai/capacitor": "^0.1.0-alpha8"{{/if}}{{#if isCapacitorApp}},
35
- "@h-ai/api-client": "^0.1.0-alpha8",
36
- "@h-ai/capacitor": "^0.1.0-alpha8"{{/if}},
17
+ "@h-ai/core": "^0.1.0-alpha9",
18
+ "@h-ai/kit": "^0.1.0-alpha9"{{#if hasUi}},
19
+ "@h-ai/ui": "^0.1.0-alpha9"{{/if}}{{#if features.db}},
20
+ "@h-ai/reldb": "^0.1.0-alpha9"{{/if}}{{#if features.cache}},
21
+ "@h-ai/cache": "^0.1.0-alpha9"{{/if}}{{#if features.iam}},
22
+ "@h-ai/iam": "^0.1.0-alpha9"{{/if}}{{#if features.crypto}},
23
+ "@h-ai/crypto": "^0.1.0-alpha9"{{/if}}{{#if features.storage}},
24
+ "@h-ai/storage": "^0.1.0-alpha9"{{/if}}{{#if features.ai}},
25
+ "@h-ai/ai": "^0.1.0-alpha9"{{/if}}{{#if features.audit}},
26
+ "@h-ai/audit": "^0.1.0-alpha9"{{/if}}{{#if features.reach}},
27
+ "@h-ai/reach": "^0.1.0-alpha9"{{/if}}{{#if features.payment}},
28
+ "@h-ai/payment": "^0.1.0-alpha9"{{/if}}{{#if features.vecdb}},
29
+ "@h-ai/vecdb": "^0.1.0-alpha9"{{/if}}{{#if features.datapipe}},
30
+ "@h-ai/datapipe": "^0.1.0-alpha9"{{/if}}{{#if features.scheduler}},
31
+ "@h-ai/scheduler": "^0.1.0-alpha9"{{/if}}{{#if features.deploy}},
32
+ "@h-ai/deploy": "^0.1.0-alpha9"{{/if}}{{#if features.api-client}},
33
+ "@h-ai/api-client": "^0.1.0-alpha9"{{/if}}{{#if features.capacitor}},
34
+ "@h-ai/capacitor": "^0.1.0-alpha9"{{/if}}{{#if isCapacitorApp}},
35
+ "@h-ai/api-client": "^0.1.0-alpha9",
36
+ "@h-ai/capacitor": "^0.1.0-alpha9"{{/if}},
37
37
  "zod": "^3.23.0"
38
38
  },
39
39
  "devDependencies": {
@@ -9,6 +9,21 @@ description: 使用 @h-ai/ai 进行 LLM 调用(OpenAI 兼容)、MCP 服务
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ | 能力 | Node.js | 浏览器 |
15
+ |------|---------|--------|
16
+ | `ai.llm` LLM 调用 | ✅ | ❌(通过 API 端点代理) |
17
+ | `ai.mcp` MCP 服务器 | ✅ | ❌ |
18
+ | `ai.tools` 工具定义 | ✅ | ✅(纯函数,无需 init) |
19
+ | `ai.stream` 流处理 | ✅ | ✅(纯函数,无需 init) |
20
+ | `ai.memory` / `ai.conversation` | ✅ | ❌(通过 API 查询) |
21
+ | `ai.knowledge` 知识库 | ✅ | ❌(通过 API 查询) |
22
+
23
+ 浏览器端消费 AI 能力的标准模式:通过 `api.stream()` 或 `apiFetch` 调用服务端 API 端点。
24
+
25
+ ---
26
+
12
27
  ## 依赖
13
28
 
14
29
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -368,16 +383,6 @@ if (result.success) {
368
383
  | `renameSession` | `(sessionId, title) => Promise<HaiResult<void>>` | 重命名会话 |
369
384
  | `removeSession` | `(sessionId) => Promise<HaiResult<void>>` | 删除会话 |
370
385
 
371
- **ContextManagerOptions**(嵌套子模块配置):
372
- - `scope?: InteractionScope` — 交互作用域
373
- - `systemPrompt?: string` — 系统提示词
374
- - `model?: string` / `temperature?: number` — LLM 参数覆盖
375
- - `compress?: CompressOptions & { auto?: boolean }` — 压缩配置(maxTokens/strategy/preserveLastN/auto 等)
376
- - `memory?: { enable?, enableExtract?, topK?, maxTokens?, position? }` — 记忆配置
377
- - `rag?: { enable?, sources?, topK?, minScore?, enableRerank? }` — RAG 配置
378
- - `reasoning?: { enable?, strategy?, maxRounds? }` — 推理配置
379
- - `tools?: ToolRegistryOperations` — 工具注册表
380
-
381
386
  **ContextManager 方法**:
382
387
  - `addMessage(msg)` — 追加消息(自动压缩)
383
388
  - `getMessages()` / `getTokenUsage()` / `getSummaries()` — 查询状态
@@ -649,6 +654,63 @@ async function manualChat(userInput: string) {
649
654
 
650
655
  ---
651
656
 
657
+ ## 浏览器端使用
658
+
659
+ ### 流式对话(SSE)
660
+
661
+ 浏览器通过 `api.stream()` 消费服务端流式 API:
662
+
663
+ ```typescript
664
+ import { api } from '@h-ai/api-client'
665
+
666
+ const controller = new AbortController()
667
+
668
+ for await (const chunk of api.stream('/api/v1/ai/chat', {
669
+ conversationId: 'conv-1',
670
+ content: '你好',
671
+ }, { signal: controller.signal })) {
672
+ // 每个 chunk 是 SSE data: 行的文本内容
673
+ appendToUI(chunk)
674
+ }
675
+
676
+ // 主动停止
677
+ controller.abort()
678
+ ```
679
+
680
+ 或使用 `apiFetch`(SSR SvelteKit 同源场景):
681
+
682
+ ```typescript
683
+ import { apiFetch } from '$lib/utils/api'
684
+
685
+ const res = await apiFetch('/api/v1/ai/chat', {
686
+ method: 'POST',
687
+ headers: { 'Content-Type': 'application/json' },
688
+ body: JSON.stringify({ conversationId, content: userInput }),
689
+ })
690
+
691
+ // 手动消费 SSE 流
692
+ const reader = res.body!.getReader()
693
+ const decoder = new TextDecoder()
694
+ while (true) {
695
+ const { done, value } = await reader.read()
696
+ if (done) break
697
+ appendToUI(decoder.decode(value))
698
+ }
699
+ ```
700
+
701
+ ### 浏览器端使用 ai.stream(处理流式 chunk)
702
+
703
+ `ai.stream` 是纯函数,浏览器端可直接使用:
704
+
705
+ ```typescript
706
+ import { ai } from '@h-ai/ai'
707
+
708
+ const processor = ai.stream.createProcessor()
709
+ // 将服务端返回的 SSE chunk 逐条 process,提取 delta
710
+ ```
711
+
712
+ ---
713
+
652
714
  ## 相关 Skills
653
715
 
654
716
  - `hai-build`:模块初始化顺序
@@ -9,6 +9,20 @@ description: 使用 @h-ai/api-client 单例模式构建多端共用的纯 TypeSc
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ **浏览器端专用。** 服务端 `+page.server.ts` / `+server.ts` 应直接调用模块 API(如 `iam.auth.login()`),不经过 HTTP 自环。
15
+
16
+ ### 与 kit.client 的分工
17
+
18
+ | 场景 | 推荐 | 原因 |
19
+ |------|------|------|
20
+ | SSR SvelteKit 应用(同源请求) | `kit.client.create().apiFetch`(见 hai-kit SKILL) | 自动 CSRF、传输加密、同源 Cookie 透传 |
21
+ | SPA / 原生 App(跨域请求) | **本模块** `api.init()` + `api.call()` | 完整 Token 管理、401 自动刷新、独立 baseUrl |
22
+ | 契约调用(类型安全,跨域) | **本模块** `api.call(endpoint, input)` | EndpointDef 双向 Zod 校验 |
23
+
24
+ ---
25
+
12
26
  ## 适用场景
13
27
 
14
28
  - 浏览器端/App 端统一 API 请求
@@ -247,11 +261,6 @@ const chat = await api.call(aiEndpoints.sendMessage, {
247
261
  | `ai` | `@h-ai/ai/api` | `aiEndpoints` | chat, chatStream, sendMessage |
248
262
  | `payment` | `@h-ai/payment/api` | `paymentEndpoints` | createOrder, queryOrder, … |
249
263
 
250
- ### 新模块接入契约
251
-
252
- 在模块 `src/api/` 下创建 Schema + Contract,并在 `package.json` 中声明 `"./api"` 子路径导出。
253
-
254
- ---
255
264
 
256
265
  ## 常见模式
257
266
 
@@ -7,6 +7,10 @@ description: 使用 @h-ai/audit 进行审计日志的记录、查询、清理与
7
7
 
8
8
  统一审计日志模块,基于 `@h-ai/reldb` 实现持久化,提供审计日志的记录、分页查询、清理与统计。
9
9
 
10
+ ## 运行环境
11
+
12
+ > ⚠️ **服务端模块(Node.js only)。** 审计日志由服务端自动记录,浏览器端通过 API 端点查询审计数据。
13
+
10
14
  ## 依赖
11
15
 
12
16
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -9,6 +9,12 @@ description: 使用 @h-ai/cache 进行内存或 Redis 缓存操作(kv/hash/lis
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端无需直接操作缓存,由服务端模块(如 IAM、Scheduler)内部使用。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 缓存热点数据减少数据库查询
@@ -9,6 +9,12 @@ description: 使用 @h-ai/capacitor 桥接 Capacitor 原生能力(Token 安全
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > **浏览器端 / 原生 App 专用。** 在 Capacitor 原生环境(Android/iOS)中提供完整能力,纯 Web 环境下部分功能退化(如 Preferences 退化为 localStorage)。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - Android/iOS 原生应用开发(SvelteKit + Capacitor)
@@ -20,24 +26,6 @@ description: 使用 @h-ai/capacitor 桥接 Capacitor 原生能力(Token 安全
20
26
 
21
27
  ---
22
28
 
23
- ## 模块结构
24
-
25
- ```
26
- packages/capacitor/src/
27
- index.ts # export * 聚合
28
- capacitor-main.ts # 服务对象(export const capacitor)
29
- capacitor-types.ts # 公共类型(CapacitorFunctions / CapacitorError / 业务类型)
30
- capacitor-config.ts # 错误码(HaiCapacitorError as const)
31
- capacitor-i18n.ts # i18n 消息获取器
32
- capacitor-token-storage.ts # Token 存储(TokenStorage 实现 + safe* 工具)
33
- capacitor-device.ts # 设备信息
34
- capacitor-camera.ts # 相机/相册
35
- capacitor-push.ts # 推送通知
36
- capacitor-status-bar.ts # 状态栏
37
- ```
38
-
39
- ---
40
-
41
29
  ## 使用步骤
42
30
 
43
31
  ### 1. 初始化与关闭
@@ -9,6 +9,20 @@ description: 使用 @h-ai/core 进行配置加载、日志记录、i18n 国际
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ **Node.js + 浏览器双端可用。** 构建工具自动选择正确入口(`package.json` exports 条件导出)。
15
+
16
+ | 能力 | Node.js | 浏览器 |
17
+ |------|---------|--------|
18
+ | `core.logger` | ✅ pino(结构化 JSON / pretty) | ✅ loglevel(DevTools console) |
19
+ | `core.config` | ✅ 完整(YAML 加载、watch、validate) | ⚠️ 仅支持 `core.init({ ... })` 传入配置 |
20
+ | `core.i18n` | ✅ | ✅ |
21
+ | `core.id` | ✅ | ✅ |
22
+ | 工具函数 | ✅ | ✅ |
23
+
24
+ ---
25
+
12
26
  ## 适用场景
13
27
 
14
28
  - 项目初始化与配置加载
@@ -98,24 +112,8 @@ const unwatch = core.config.watch('db', (cfg, error) => {
98
112
 
99
113
  // 调用 unwatch() 停止监听
100
114
  unwatch()
101
-
102
- // 配置未加载时立即返回 NOT_LOADED 错误
103
- core.config.watch('nonexistent', (_cfg, error) => {
104
- // error.code === HaiConfigError.CONFIG_NOT_LOADED.code
105
- })
106
-
107
- // 多个监听回调可在同一配置文件变更时同时触发
108
- core.config.watch('app', cb1)
109
- core.config.watch('app', cb2) // cb1 和 cb2 都会被调用
110
115
  ```
111
116
 
112
- **文件监听特性**:
113
-
114
- - 使用 `fs.watch()` 实现,自动防抖(100ms 防抖避免快速重复保存触发多次回调)
115
- - 同一配置名只会创建一个 watcher(多次 `watch()` 共享同一 watcher 实例)
116
- - 当最后一个回调被移除时,watcher 自动关闭
117
- - `unwatch()` 会关闭watcher,但缓存的配置数据保留
118
-
119
117
  配置文件格式(YAML,支持环境变量插值):
120
118
 
121
119
  - `${VAR}` — 读取 `process.env.VAR`;缺失则返回 `HaiConfigError.CONFIG_ENV_VAR_MISSING` 错误
@@ -301,62 +299,13 @@ core.time.addDays(date, 7)
301
299
  所有 hai 模块操作返回 `HaiResult<T>` 类型,强制处理成功/失败分支:
302
300
 
303
301
  ```typescript
304
- import type { HaiResult, HaiErrorDef, HaiError } from '@h-ai/core'
305
- import { err, ok } from '@h-ai/core'
302
+ import { ok, err, HaiCommonError } from '@h-ai/core'
306
303
 
307
304
  // 成功
308
305
  return ok(data)
309
306
 
310
- // 失败:使用预定义错误定义创建错误实例
307
+ // 失败
311
308
  return err(HaiCommonError.NOT_FOUND, 'Resource not found')
312
-
313
- // 带原始错误和建议的完整失败
314
- return err(
315
- HaiCommonError.INTERNAL_ERROR,
316
- 'Failed to process request',
317
- originalError,
318
- 'Check database connection'
319
- )
320
- ```
321
-
322
- **错误定义 vs 错误实例**:
323
-
324
- ```typescript
325
- // HaiErrorDef 是错误定义(包含错误码、HTTP 状态码、模块信息)
326
- const errorDef: HaiErrorDef = {
327
- code: 'hai:common:001', // system:module:code
328
- httpStatus: 500, // HTTP 响应状态码
329
- system: 'hai', // 系统标识
330
- module: 'common', // 所属模块
331
- }
332
-
333
- // HaiError 是具体错误实例(包含定义数据 + 运行时信息)
334
- const errorInst: HaiError = {
335
- ...errorDef,
336
- message: '具体错误消息', // i18n 翻译后的消息
337
- cause?: originalError, // 原始错误
338
- suggestion?: '用户可采取的行动', // 建议
339
- }
340
-
341
- // err() 函数接受 HaiErrorDef,自动创建 HaiError 实例
342
- err(errorDef, '消息文本', cause, '建议')
343
- ```
344
-
345
- **HaiResult 类型定义**:
346
-
347
- ```typescript
348
- type HaiResult<T>
349
- = | { success: true, data: T }
350
- | { success: false, error: HaiError }
351
- code: string
352
- message: string
353
- httpStatus?: number
354
- system?: string
355
- module?: string
356
- cause?: unknown
357
- suggestion?: string
358
- ext?: Record<string, unknown>
359
- }
360
309
  ```
361
310
 
362
311
  **使用模式**:
@@ -377,35 +326,6 @@ if (!result.success)
377
326
  // 后续代码可安全使用 result.data
378
327
  ```
379
328
 
380
- ### 模块工具 — `core.module`
381
-
382
- | 方法 | 签名 | 说明 |
383
- | ------------------------- | -------------------------------------------------------------------- | ---------------------------------------- |
384
- | `createNotInitializedKit` | `<E>(code: E['code'], messageFn: () => string) => NotInitializedKit` | 创建未初始化代理(访问任何方法返回错误) |
385
-
386
- ```typescript
387
- // 在模块 main.ts 中使用
388
- const notInitialized = core.module.createNotInitializedKit(
389
- HaiReldbError.NOT_INITIALIZED,
390
- () => reldbM('reldb_notInitialized'),
391
- )
392
-
393
- // 创建占位 Proxy(异步接口,默认)
394
- let currentDdl: DdlOperations = notInitialized.proxy<DdlOperations>()
395
- // 创建占位 Proxy(同步接口)
396
- const currentHash: HashOperations = notInitialized.proxy<HashOperations>('sync')
397
-
398
- export const myModule = {
399
- async init(config) {
400
- currentDdl = createDdlOperations(config)
401
- return ok(undefined)
402
- },
403
- get ddl() {
404
- return currentDdl
405
- },
406
- }
407
- ```
408
-
409
329
  ## 错误码
410
330
 
411
331
  Core 模块定义了两组标准错误码:
@@ -471,27 +391,6 @@ if (!result.success) {
471
391
  }
472
392
  ```
473
393
 
474
- ### 自定义错误码(模块级)
475
-
476
- 各模块应定义自己的错误码常量,遵循格式 `hai:<module>:<code>`:
477
-
478
- ```typescript
479
- // 示例:db 模块定义错误码
480
- const DbErrorInfo = {
481
- NOT_INITIALIZED: '001:500',
482
- CONNECTION_FAILED: '010:500',
483
- QUERY_FAILED: '011:500',
484
- INVALID_SCHEMA: '012:400',
485
- } as const satisfies ErrorInfo
486
-
487
- export const DbError = error.buildHaiErrorsDef('db', DbErrorInfo)
488
-
489
- // 使用
490
- import { DbError, err } from '@h-ai/db'
491
-
492
- err(DbError.CONNECTION_FAILED, 'Failed to connect to PostgreSQL')
493
- ```
494
-
495
394
  ## 常见模式
496
395
 
497
396
  ### 服务端初始化(SvelteKit hooks.server.ts)
@@ -542,3 +441,44 @@ const config = result.data // 类型安全
542
441
  - `hai-build`:项目整体架构与技能导航
543
442
  - `hai-kit`:SvelteKit 集成层(Handle/Guard/Middleware)
544
443
  - `hai-reldb` / `hai-cache` / `hai-iam`:使用 core.config 读取配置的下游模块
444
+
445
+ ---
446
+
447
+ ## 浏览器侧使用
448
+
449
+ > **禁止在应用中直接引入 `loglevel`。** `core.logger` 在浏览器端自动使用 loglevel 作为后端,API 与 Node.js 完全一致。
450
+
451
+ ### 初始化
452
+
453
+ 浏览器端无文件系统,不需要 `configDir`。在 `+layout.svelte` 或应用入口调用:
454
+
455
+ ```typescript
456
+ import { core } from '@h-ai/core'
457
+
458
+ // 浏览器端:仅传入 logging 配置即可
459
+ core.init({ logging: { level: 'info' } })
460
+ ```
461
+
462
+ ### 日志记录
463
+
464
+ ```typescript
465
+ // 与 Node.js 完全一致的 API
466
+ core.logger.info('Page loaded', { route: '/home' })
467
+ core.logger.error('API call failed', { url, status })
468
+
469
+ // 创建模块级 logger
470
+ const logger = core.logger.create({ name: 'my-page' })
471
+ logger.debug('Data fetched', { count: items.length })
472
+
473
+ // ❌ 禁止直接引用底层日志库
474
+ // import log from 'loglevel' ← 不要这样做
475
+ ```
476
+
477
+ ### 浏览器侧限制
478
+
479
+ | 功能 | 行为 |
480
+ |------|------|
481
+ | `format: 'json' \| 'pretty'` | 被忽略(始终输出到 DevTools console) |
482
+ | `redact` 字段脱敏 | 不生效(需手动脱敏) |
483
+ | `fatal` 级别 | 映射为 `console.error`(带 `[FATAL]` 前缀) |
484
+ | context 输出 | JSON 字符串追加到消息末尾(非结构化对象) |
@@ -9,6 +9,21 @@ description: 使用 @h-ai/crypto 进行加密(非对称/哈希/对称)与密
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ **Node.js + 浏览器双端可用。** 浏览器端通过 `crypto.init()` 初始化(无需配置参数)。
15
+
16
+ | 能力 | Node.js | 浏览器 | 说明 |
17
+ |------|---------|--------|------|
18
+ | `crypto.asymmetric` 非对称加密 | ✅ | ✅ | SM2 加解密、签名验签 |
19
+ | `crypto.hash` 哈希 | ✅ | ✅ | SM3 哈希、HMAC |
20
+ | `crypto.symmetric` 对称加密 | ✅ | ✅ | SM4 ECB/CBC |
21
+ | `crypto.password` 密码哈希 | ✅ | ❌ | 依赖 Node.js crypto(服务端专用) |
22
+
23
+ 浏览器端主要用于 kit 传输加密场景(`kit.client.create({ transport: { crypto } })`),一般不需要直接调用 crypto API。
24
+
25
+ ---
26
+
12
27
  ## 适用场景
13
28
 
14
29
  - 非对称加密/解密、签名/验签、密钥生成
@@ -9,6 +9,12 @@ description: 使用 @h-ai/datapipe 进行文本清洗(HTML/URL/空白移除)
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > **服务端模块(Node.js only)。** 通常在 AI 知识库入库或文档预处理场景中使用,由 `@h-ai/ai` 内部调用或在服务端显式调用。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 文本清洗:移除 HTML 标签、URL、Email,标准化空白
@@ -9,6 +9,12 @@ description: 使用 @h-ai/deploy 进行自动化部署:Vercel 部署 + 基础
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **Node.js CLI / 服务端模块。** 通过 `hai deploy` CLI 命令或在 Node.js 脚本中使用。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 一键部署 SvelteKit 应用到 Vercel
@@ -9,6 +9,12 @@ description: 使用 @h-ai/iam 进行身份认证(密码/OTP/LDAP/API Key)、
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端通过 `api.call(iamEndpoints.xxx)` 或 `apiFetch` 调用 IAM API 端点(参见 hai-api-client / hai-kit SKILL)。`@h-ai/iam/api` 子路径导出的 Zod Schema 和 EndpointDef 可在浏览器端使用。
15
+
16
+ ---
17
+
12
18
  ## 依赖
13
19
 
14
20
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -243,7 +249,7 @@ const result = await api.call(iamEndpoints.login, { username, password })
243
249
  | `HaiIamError.PASSWORD_EXPIRED` | `hai:iam:007` | 密码已过期 |
244
250
  | `HaiIamError.PASSWORD_POLICY_VIOLATION` | `hai:iam:008` | 密码不符合策略 |
245
251
  | `HaiIamError.OTP_INVALID` | `hai:iam:009` | 验证码无效 |
246
- | `HaiIamError.OTP_EXPIRED` | `hai:iam:010` | 验证码已过期 |
252
+ | `HaiIamError.NOT_INITIALIZED` | `hai:iam:010` | 未初始化 |
247
253
  | `HaiIamError.OTP_RESEND_TOO_FAST` | `hai:iam:011` | 发送过于频繁 |
248
254
  | `HaiIamError.LOGIN_DISABLED` | `hai:iam:012` | 登录方式已禁用 |
249
255
  | `HaiIamError.REGISTER_DISABLED` | `hai:iam:013` | 注册已禁用 |
@@ -255,6 +261,7 @@ const result = await api.call(iamEndpoints.login, { username, password })
255
261
  | `HaiIamError.RESET_TOKEN_INVALID` | `hai:iam:019` | 重置令牌无效 |
256
262
  | `HaiIamError.RESET_TOKEN_EXPIRED` | `hai:iam:020` | 重置令牌已过期 |
257
263
  | `HaiIamError.RESET_TOKEN_MAX_ATTEMPTS` | `hai:iam:021` | 重置验证次数超限 |
264
+ | `HaiIamError.OTP_EXPIRED` | `hai:iam:022` | 验证码已过期 |
258
265
  | `HaiIamError.SESSION_NOT_FOUND` | `hai:iam:101` | 会话不存在 |
259
266
  | `HaiIamError.SESSION_EXPIRED` | `hai:iam:102` | 会话已过期 |
260
267
  | `HaiIamError.SESSION_INVALID` | `hai:iam:103` | 会话无效 |
@@ -276,7 +283,6 @@ const result = await api.call(iamEndpoints.login, { username, password })
276
283
  | `HaiIamError.FORBIDDEN` | `hai:iam:501` | 禁止访问 |
277
284
  | `HaiIamError.INVALID_ARGUMENT` | `hai:iam:502` | 参数无效 |
278
285
  | `HaiIamError.CONFIG_ERROR` | `hai:iam:901` | 配置错误 |
279
- | `HaiIamError.NOT_INITIALIZED` | `hai:iam:910` | 未初始化 |
280
286
  | `HaiIamError.INTERNAL_ERROR` | `hai:iam:999` | 内部错误 |
281
287
 
282
288
  ---
@@ -23,6 +23,9 @@ description: 使用 @h-ai/kit 构建 SvelteKit 请求管道与 API 安全边界
23
23
  - `kit.guard.*`(`require` / `check`)
24
24
  - `kit.handler` / `kit.response` / `kit.validate`
25
25
  - `kit.fromContract(endpoint, fn)` — 契约到 SvelteKit handler
26
+ - `kit.client.create()` — 浏览器端统一 API 客户端(`apiFetch`)
27
+ - `kit.auth.createTokenStore()` — 浏览器端 Token 存储
28
+ - `kit.auth.createHandleFetch()` — `hooks.client.ts` 自动附加 Token
26
29
  - Bearer Token / `Authorization` header
27
30
  - CORS 配置(含 Capacitor Origin 预设)
28
31
  - 传输加密(key exchange / `X-Client-Id`)
@@ -61,12 +64,6 @@ export const handle = kit.createHandle({
61
64
  })
62
65
  ```
63
66
 
64
- Bearer Token 解析流程:
65
-
66
- 1. 检查 `Authorization: Bearer <token>` header
67
- 2. 若无 header,回退到 `event.cookies.get('hai_access_token')`(SSR 透传场景)
68
- 3. Token 经 `auth.verifyToken` 校验后挂载到 `event.locals.session`
69
-
70
67
  ### 2) API 端点使用 `kit.handler`
71
68
 
72
69
  ```typescript
@@ -169,38 +166,10 @@ const myEndpoint = defineEndpoint({
169
166
  | `VALIDATION_ERROR` | 422 | 数据校验失败 |
170
167
  | `INTERNAL_ERROR` | 500 | 未处理异常 |
171
168
 
172
- ### 传输加密常见错误(i18n key)
173
-
174
- | key | 含义 |
175
- | -------------------------------- | ---------------------------------- |
176
- | `kit_transportClientIdRequired` | 缺少 `X-Client-Id`(强制加密模式) |
177
- | `kit_transportClientKeyNotFound` | 客户端密钥不存在或失效 |
178
- | `kit_transportInvalidPayload` | 加密载荷格式非法 |
179
- | `kit_transportDecryptFailed` | 请求体解密失败 |
180
- | `kit_transportKeyExchangeFailed` | 密钥交换失败 |
181
-
182
169
  ## API 契约范式(端到端类型安全)
183
170
 
184
171
  > `kit.fromContract` 和 `api.call` 共同消费模块 `api/` 下的 `EndpointDef`,实现服务端↔客户端的全链路类型安全 + 运行时 Zod 校验。
185
172
 
186
- ### 契约架构
187
-
188
- ```
189
- ┌─────────────────────────────────────────────────────────────┐
190
- │ @h-ai/xx/api │
191
- │ xx-api-schemas.ts ← Zod Schema(唯一真相源) │
192
- │ xx-api-contract.ts ← xxEndpoints(method + path + schema)│
193
- └─────────────┬──────────────────────────┬────────────────────┘
194
- │ │
195
- ┌────────▼────────┐ ┌─────────▼─────────┐
196
- │ 客户端(浏览器)│ │ 服务端(SvelteKit)│
197
- │ api.call(ep, i) │ │ kit.fromContract │
198
- │ @h-ai/api-client│ │ @h-ai/kit │
199
- └────────┬────────┘ └─────────┬─────────┘
200
- │ HTTP(类型安全) │
201
- └──────────────────────────┘
202
- ```
203
-
204
173
  ### 服务端流程(本模块责任)
205
174
 
206
175
  1. 从 `@h-ai/xx/api` 导入 `xxEndpoints`
@@ -249,33 +218,8 @@ export const POST = kit.fromContract(paymentEndpoints.createOrder, async (input,
249
218
  })
250
219
  ```
251
220
 
252
- ### 已有契约模块一览
253
-
254
- | 模块 | 导入路径 | 端点对象 | 典型端点 |
255
- | ---------- | -------------------- | -------------------- | ------------------------------- |
256
- | `storage` | `@h-ai/storage/api` | `storageEndpoints` | presignUpload, listFiles, … |
257
- | `iam` | `@h-ai/iam/api` | `iamEndpoints` | login, logout, currentUser, … |
258
- | `ai` | `@h-ai/ai/api` | `aiEndpoints` | chat, chatStream, sendMessage |
259
- | `payment` | `@h-ai/payment/api` | `paymentEndpoints` | createOrder, queryOrder, … |
260
-
261
- ### 新模块接入契约
262
-
263
- 在模块 `src/api/` 下创建 Schema + Contract,并在 `package.json` 中声明 `"./api"` 子路径导出。
264
-
265
221
  ## 常见模式
266
222
 
267
- ### Bearer Token 认证流程
268
-
269
- ```
270
- 客户端 → Authorization: Bearer <accessToken> → kit.createHandle 解析
271
- ↓
272
- auth.verifyToken(token) → locals.session
273
- ↓
274
- protectedPaths 守卫检查 → 通过则继续
275
- ```
276
-
277
- SSR 场景下,浏览器通过 `hai_access_token` httpOnly Cookie 透传 Token,Handle 自动回退读取。
278
-
279
223
  ### 契约模式(推荐新项目使用)
280
224
 
281
225
  1. 在模块的 `api/` 目录定义 `EndpointDef`(Zod schema)
@@ -314,6 +258,97 @@ const config = {
314
258
 
315
259
  - `hai-core`:全局配置、日志、HaiResult 基础能力
316
260
  - `hai-iam`:Token 认证、角色权限模型
317
- - `hai-api-client`:客户端契约调用
261
+ - `hai-api-client`:客户端契约调用(SPA / 原生 App 场景)
318
262
  - `hai-crypto`:传输加密与密钥管理能力
319
263
  - `hai-capacitor`:原生 App CORS 场景
264
+
265
+ ---
266
+
267
+ ## 浏览器侧使用
268
+
269
+ ### 创建统一 API 客户端 — `kit.client`
270
+
271
+ > **所有浏览器端 API 请求必须通过 `apiFetch`,禁止直接 `fetch()`。**
272
+
273
+ 在 `src/lib/utils/api.ts` 中一次性创建,全应用共享:
274
+
275
+ ```typescript
276
+ // src/lib/utils/api.ts
277
+ import { kit } from '@h-ai/kit'
278
+
279
+ const client = kit.client.create({
280
+ auth: true, // 自动从 localStorage 注入 Bearer Token
281
+ })
282
+
283
+ export const { apiFetch } = client
284
+ ```
285
+
286
+ 如需传输加密,传入 crypto:
287
+
288
+ ```typescript
289
+ import { crypto } from '@h-ai/crypto'
290
+ import { kit } from '@h-ai/kit'
291
+
292
+ if (typeof window !== 'undefined') {
293
+ crypto.init() // 浏览器端初始化加密模块
294
+ }
295
+
296
+ const client = kit.client.create({
297
+ transport: { crypto }, // 透明完成密钥交换 + 请求/响应体加解密
298
+ auth: true,
299
+ })
300
+
301
+ export const { apiFetch } = client
302
+ ```
303
+
304
+ `apiFetch` 自动处理:
305
+ - **CSRF**:写方法(POST/PUT/DELETE)自动读取 `hai_csrf` Cookie 并设置 `X-CSRF-Token`
306
+ - **Bearer Token**:自动从 localStorage 读取并注入 `Authorization` header
307
+ - **传输加密**:可选,首次写请求时自动密钥交换
308
+
309
+ 使用示例:
310
+
311
+ ```typescript
312
+ // ✅ 正确方式
313
+ import { apiFetch } from '$lib/utils/api'
314
+
315
+ const res = await apiFetch('/api/users', {
316
+ method: 'POST',
317
+ headers: { 'Content-Type': 'application/json' },
318
+ body: JSON.stringify(data),
319
+ })
320
+
321
+ // ❌ 禁止在组件中直接 fetch
322
+ // const res = await fetch('/api/users', { ... })
323
+ ```
324
+
325
+ ### Token 存储 — `kit.auth.createTokenStore`
326
+
327
+ ```typescript
328
+ import { kit } from '@h-ai/kit'
329
+
330
+ // 创建 localStorage 存储(Web 端默认)
331
+ const tokenStore = kit.auth.createTokenStore('hai_access_token')
332
+ tokenStore.set(tokens.accessToken) // 登录后保存
333
+ tokenStore.get() // string | null
334
+ tokenStore.clear() // 登出时清除
335
+ ```
336
+
337
+ ### hooks.client.ts 标准配置 — `kit.auth.createHandleFetch`
338
+
339
+ ```typescript
340
+ // src/hooks.client.ts
341
+ import { kit } from '@h-ai/kit'
342
+
343
+ // 自动为同源请求附加 Authorization: Bearer <token>
344
+ export const handleFetch = kit.auth.createHandleFetch()
345
+ ```
346
+
347
+ ### 选择指南:kit.client vs api-client
348
+
349
+ | 场景 | 推荐 | 原因 |
350
+ |------|------|------|
351
+ | SSR SvelteKit 应用(同源请求) | `kit.client.create().apiFetch` | 自动 CSRF、同源 Cookie 透传、传输加密 |
352
+ | SPA / 原生 App(跨域请求) | `api.init()` + `api.call()` | 完整 Token 管理、401 自动刷新 |
353
+ | 服务端 `+page.server.ts` | 直接调用模块 API | 无需 HTTP 自环 |
354
+ | 契约调用(类型安全) | `api.call(endpoint, input)` | EndpointDef 双向 Zod 校验 |
@@ -9,6 +9,12 @@ description: 使用 @h-ai/payment 接入微信支付、支付宝、Stripe 统一
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > **服务端 + 浏览器分工模块。** `payment.createOrder` / `payment.handleNotify` / `payment.refund` 等操作在 Node.js 端执行。浏览器端使用 `invokePayment()`(从 `@h-ai/payment/client` 导入)调起支付。
15
+
16
+ ---
17
+
12
18
  ## 依赖
13
19
 
14
20
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -189,25 +195,14 @@ const payResult = await invokePayment(orderResult.data)
189
195
 
190
196
  ## Provider 模式
191
197
 
192
- ### PaymentProvider 接口
193
-
194
- ```typescript
195
- interface PaymentProvider {
196
- readonly name: string
197
- createOrder: (input: CreateOrderInput) => Promise<HaiResult<PaymentOrder>>
198
- handleNotify: (request: PaymentNotifyRequest) => Promise<HaiResult<PaymentNotifyResult>>
199
- queryOrder: (outTradeNo: string) => Promise<HaiResult<OrderStatus>>
200
- refund: (input: RefundInput) => Promise<HaiResult<RefundResult>>
201
- closeOrder: (outTradeNo: string) => Promise<HaiResult<void>>
202
- }
203
- ```
204
-
205
198
  内置 Provider:
206
199
 
207
- - **wechat** — 微信支付 API v3(RSA-SHA256 签名、AES-256-GCM 回调解密)
208
- - **alipay** — 支付宝开放平台(RSA2 签名验签)
200
+ - **wechat** — 微信支付 API v3
201
+ - **alipay** — 支付宝开放平台
209
202
  - **stripe** — Stripe Checkout Session 模式
210
203
 
204
+ 自定义 Provider 通过 `payment.registerProvider(provider)` 注册。
205
+
211
206
  ### API 契约
212
207
 
213
208
  ```typescript
@@ -267,16 +262,7 @@ export const POST = kit.handler(async ({ request, locals }) => {
267
262
 
268
263
  ## 审计日志
269
264
 
270
- 关键支付操作成功后自动写入审计日志(依赖 `@h-ai/audit`),无需额外配置。失败仅 warn,不影响支付流程。
271
-
272
- | 操作 | `action` | `resource` | `resourceId` | `details` |
273
- | --- | --- | --- | --- | --- |
274
- | 创建订单 | `create_order` | `payment` | `orderNo` | `{ provider, amount, tradeType }` |
275
- | 支付回调 | `payment_notify` | `payment` | `orderNo` | `{ provider, transactionId, status, amount }` |
276
- | 退款 | `refund` | `payment` | `orderNo` | `{ provider, refundNo, amount }` |
277
- | 关闭订单 | `close_order` | `payment` | `orderNo` | `{ provider }` |
278
-
279
- > `queryOrder` 为只读操作,不写审计日志。
265
+ 关键支付操作(创建订单、回调、退款、关闭订单)成功后自动写入审计日志(依赖 `@h-ai/audit`),无需额外配置。`queryOrder` 为只读操作,不写审计日志。
280
266
 
281
267
  ---
282
268
 
@@ -9,6 +9,12 @@ description: 使用 @h-ai/reach 进行邮件、短信和 API 回调发送;当
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端不直接发送邮件/短信,而是通过 API 端点触发服务端 `reach.send()`。
15
+
16
+ ---
17
+
12
18
  ## 依赖
13
19
 
14
20
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -300,22 +306,6 @@ if (!result.success) {
300
306
  }
301
307
  ```
302
308
 
303
- ### 分布式锁(DND flush 保护)
304
-
305
- > 多节点部署时,DND(delay 策略)的 pending 消息 flush 通过 `@h-ai/cache` 分布式锁保护,确保同一时刻只有一个节点执行 flush。
306
-
307
- ```typescript
308
- import { cache } from '@h-ai/cache'
309
-
310
- // 初始化 cache 后,reach 自动使用分布式锁保护 flush 操作
311
- await cache.init({ type: 'redis', host: 'localhost', port: 6379 })
312
- await reach.init({ /* ... */ })
313
- ```
314
-
315
- - 锁键:`reach:flush-pending`,TTL 60 秒
316
- - 若 cache 未初始化,分布式锁自动禁用,不影响单节点运行
317
- - 使用稳定的进程级 owner 标识,防止误释放他人锁
318
-
319
309
  ---
320
310
 
321
311
  ## 相关 Skills
@@ -9,6 +9,12 @@ description: 使用 @h-ai/reldb 进行 SQLite/PostgreSQL/MySQL 的初始化、SQ
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端请通过 API 端点间接访问数据库(参见 hai-kit 契约模式 `kit.fromContract`)。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 新增或修改数据库访问逻辑(SQL/DDL/CRUD/事务)
@@ -310,7 +316,7 @@ if (txResult.success) {
310
316
  }
311
317
  ```
312
318
 
313
- **事务内可用操作**(`DmlWithTxOperations` 继承 `DmlOperations`):
319
+ **事务内可用操作**:
314
320
 
315
321
  - `tx.query / tx.get / tx.execute / tx.batch / tx.queryPage`
316
322
  - `tx.crud.table(config)` — 事务内的 CRUD 仓库
@@ -392,11 +398,11 @@ await reldb.sql.execute(`UPDATE users SET profile = ${sql} WHERE id = ?`, [...pa
392
398
  | `HaiReldbError.DUPLICATE_ENTRY` | `hai:reldb:007` | 重复条目 |
393
399
  | `HaiReldbError.DEADLOCK` | `hai:reldb:008` | 死锁 |
394
400
  | `HaiReldbError.TIMEOUT` | `hai:reldb:009` | 超时 |
395
- | `HaiReldbError.POOL_EXHAUSTED` | `hai:reldb:010` | 连接池耗尽 |
396
- | `HaiReldbError.NOT_INITIALIZED` | `hai:reldb:011` | 数据库未初始化 |
397
- | `HaiReldbError.DDL_FAILED` | `hai:reldb:012` | DDL 操作失败 |
398
- | `HaiReldbError.UNSUPPORTED_TYPE` | `hai:reldb:013` | 不支持的数据库类型 |
399
- | `HaiReldbError.CONFIG_ERROR` | `hai:reldb:014` | 配置错误 |
401
+ | `HaiReldbError.NOT_INITIALIZED` | `hai:reldb:010` | 数据库未初始化 |
402
+ | `HaiReldbError.POOL_EXHAUSTED` | `hai:reldb:011` | 连接池耗尽 |
403
+ | `HaiReldbError.UNSUPPORTED_TYPE` | `hai:reldb:012` | 不支持的数据库类型 |
404
+ | `HaiReldbError.CONFIG_ERROR` | `hai:reldb:013` | 配置错误 |
405
+ | `HaiReldbError.DDL_FAILED` | `hai:reldb:020` | DDL 操作失败 |
400
406
 
401
407
  ---
402
408
 
@@ -7,6 +7,10 @@ description: 使用 @h-ai/scheduler 进行统一定时任务管理、任务持
7
7
 
8
8
  > `@h-ai/scheduler` 使用统一任务模型管理 API / JS / Hook 三类执行路径,支持持久化、手工触发来源记录、分布式锁与全局生命周期回调。
9
9
 
10
+ ## 运行环境
11
+
12
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端通过 API 端点触发 `scheduler.trigger()` 或查询任务日志。
13
+
10
14
  ## 依赖
11
15
 
12
16
  | 模块 | 用途 | 是否必需 | 初始化要求 |
@@ -20,6 +24,7 @@ description: 使用 @h-ai/scheduler 进行统一定时任务管理、任务持
20
24
 
21
25
  ```ts
22
26
  import { cache } from '@h-ai/cache'
27
+ import { core } from '@h-ai/core'
23
28
  import { reldb } from '@h-ai/reldb'
24
29
  import { scheduler } from '@h-ai/scheduler'
25
30
 
@@ -32,13 +37,13 @@ await scheduler.init({
32
37
  retentionDays: 30,
33
38
  hooks: {
34
39
  onTaskStart(event) {
35
- console.info('start', event.task.id, event.trigger)
40
+ core.logger.info('Task started', { taskId: event.task.id, trigger: event.trigger })
36
41
  },
37
42
  onTaskInterrupted(event) {
38
- console.warn('interrupted', event.task.id, event.reason)
43
+ core.logger.warn('Task interrupted', { taskId: event.task.id, reason: event.reason })
39
44
  },
40
45
  onTaskFinish(event) {
41
- console.info('finish', event.log.status)
46
+ core.logger.info('Task finished', { status: event.log.status })
42
47
  },
43
48
  },
44
49
  })
@@ -106,65 +111,6 @@ const logs = await scheduler.getLogs({
106
111
  })
107
112
  ```
108
113
 
109
- ## 核心类型
110
-
111
- ### TaskDefinition
112
-
113
- ```ts
114
- interface TaskDefinition {
115
- id: string
116
- name: string
117
- description?: string
118
- cron: string
119
- enabled?: boolean
120
- deleteAfterRun?: boolean
121
- retry?: {
122
- maxAttempts: number
123
- backoffMs?: number[]
124
- }
125
- params?: Record<string, unknown>
126
- handler?: ApiTaskConfig | JsTaskConfig
127
- }
128
- ```
129
-
130
- ### Handler 配置
131
-
132
- ```ts
133
- interface ApiTaskConfig {
134
- kind: 'api'
135
- url: string
136
- method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
137
- headers?: Record<string, string>
138
- body?: unknown
139
- timeout?: number
140
- }
141
-
142
- interface JsTaskConfig {
143
- kind: 'js'
144
- code: string
145
- timeout?: number
146
- }
147
- ```
148
-
149
- ### TaskExecutionLog
150
-
151
- ```ts
152
- interface TaskExecutionLog {
153
- id: number
154
- taskId: string
155
- taskName: string
156
- taskType: 'api' | 'js' | 'hook'
157
- triggerType: 'scheduled' | 'manual'
158
- triggerSource: string | null
159
- status: 'success' | 'failed' | 'interrupted'
160
- result: string | null
161
- error: string | null
162
- startedAt: number
163
- finishedAt: number
164
- duration: number
165
- }
166
- ```
167
-
168
114
  ## 核心 API
169
115
 
170
116
  | 方法 / 属性 | 签名 | 说明 |
@@ -9,6 +9,12 @@ description: 使用 @h-ai/storage 进行文件存储操作(本地/S3),包
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块**。`storage.file` / `storage.dir` / `storage.presign` 均在 Node.js 端使用。浏览器端通过预签名 URL 或 API 端点间接访问存储(见下方「浏览器端文件上传」)。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 文件上传与下载
@@ -222,6 +228,46 @@ if (result.success) {
222
228
 
223
229
  ---
224
230
 
231
+ ## 浏览器端文件上传(预签名 URL 模式)
232
+
233
+ 浏览器端无法直接使用 `storage` 模块,应通过 API 获取预签名 URL 后直传:
234
+
235
+ ```typescript
236
+ import { apiFetch } from '$lib/utils/api'
237
+
238
+ // 1. 向服务端请求预签名上传 URL
239
+ const res = await apiFetch('/api/storage/presign/upload', {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({ key: `uploads/${file.name}`, contentType: file.type }),
243
+ })
244
+ const { url } = await res.json()
245
+
246
+ // 2. 浏览器直传到存储(不经过服务端,大文件友好)
247
+ await fetch(url, {
248
+ method: 'PUT',
249
+ body: file,
250
+ headers: { 'Content-Type': file.type },
251
+ })
252
+ ```
253
+
254
+ 也可使用契约调用(需配合 `@h-ai/api-client`):
255
+
256
+ ```typescript
257
+ import { api } from '@h-ai/api-client'
258
+ import { storageEndpoints } from '@h-ai/storage/api'
259
+
260
+ const result = await api.call(storageEndpoints.presignUpload, {
261
+ key: `avatars/${userId}.png`,
262
+ contentType: 'image/png',
263
+ })
264
+ if (result.success) {
265
+ await fetch(result.data.url, { method: 'PUT', body: file })
266
+ }
267
+ ```
268
+
269
+ ---
270
+
225
271
  ## 相关 Skills
226
272
 
227
273
  - `hai-build`:模块初始化顺序
@@ -9,6 +9,12 @@ description: 使用 @h-ai/ui 构建多端应用界面,包含三层组件架构
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > **浏览器端专用。** Svelte 组件在浏览器中渲染,服务端无需引用。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 构建管理后台页面(表单、表格、弹窗、导航等)
@@ -257,10 +263,9 @@ const theme = getCurrentTheme()
257
263
 
258
264
  1. **Svelte 5 Runes**:使用 `$state`、`$derived`、`$effect`
259
265
  2. **Snippet 插槽**:使用 `{#snippet name()}...{/snippet}` 语法
260
- 3. **三层依赖红线**:primitives 不依赖上层,compounds 只依赖 primitives + Bits UI,scenes 可依赖两者
261
- 4. **自动导入例外**:`toast`、类型导入、`Range`/`FileList` 必须显式 `import`
262
- 5. **Combobox 统一单选/多选**:`MultiSelect` 已删除
263
- 6. **移动端样式**:务必引入 `design-tokens.css` + `mobile.css`,使用 SafeArea 包裹原生 App 页面
266
+ 3. **自动导入例外**:`toast`、类型导入、`Range`/`FileList` 必须显式 `import`
267
+ 4. **Combobox 统一单选/多选**:`MultiSelect` 已删除
268
+ 5. **移动端样式**:务必引入 `design-tokens.css` + `mobile.css`,使用 SafeArea 包裹原生 App 页面
264
269
 
265
270
  ---
266
271
 
@@ -271,5 +276,3 @@ const theme = getCurrentTheme()
271
276
  - `hai-iam`:IAM 模块 API(与 LoginForm/RegisterForm 配合)
272
277
  - `hai-capacitor`:原生 App 开发(与 SafeArea/AppBar 配合)
273
278
  - `hai-api-client`:客户端数据获取
274
-
275
- - `hai-capacitor`:原生 App 开发(与 SafeArea/AppBar 配合)
@@ -9,6 +9,12 @@ description: 使用 @h-ai/vecdb 进行向量数据库操作(LanceDB/pgvector/Q
9
9
 
10
10
  ---
11
11
 
12
+ ## 运行环境
13
+
14
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端不直接操作向量数据库,通过 `@h-ai/ai` 的 API 端点间接使用(如 RAG 检索、知识库查询)。
15
+
16
+ ---
17
+
12
18
  ## 适用场景
13
19
 
14
20
  - 新增或修改向量数据库访问逻辑(集合管理/向量插入/搜索)