@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
@@ -0,0 +1,145 @@
1
+ # hai-audit 参考
2
+
3
+ 统一审计日志模块,基于 `@h-ai/reldb` 实现持久化,提供审计日志的记录、分页查询、清理与统计。
4
+
5
+ ## 运行环境
6
+
7
+ > ⚠️ **服务端模块(Node.js only)。** 审计日志由服务端自动记录,浏览器端通过 API 端点查询审计数据。
8
+
9
+ ## 依赖
10
+
11
+ | 模块 | 用途 | 是否必需 | 初始化要求 |
12
+ | --- | --- | --- | --- |
13
+ | `@h-ai/reldb` | 数据库(审计日志持久化) | **必需** | 需在 `audit.init()` 前初始化 |
14
+
15
+ ## 使用步骤
16
+
17
+ ### 1. 配置
18
+
19
+ 审计模块依赖已初始化的 `@h-ai/reldb` 单例,无需独立配置文件。审计日志固定存储在 `hai_audit_logs` 表中。
20
+
21
+ ### 2. 初始化
22
+
23
+ ```ts
24
+ import { audit } from '@h-ai/audit'
25
+ import { reldb } from '@h-ai/reldb'
26
+
27
+ // 先初始化依赖
28
+ await reldb.init({ type: 'sqlite', database: './data.db' })
29
+
30
+ // 再初始化审计模块(自动使用已初始化的 reldb 单例)
31
+ const result = await audit.init()
32
+ if (!result.success) {
33
+ // 处理错误
34
+ }
35
+ ```
36
+
37
+ 自定义用户表映射:
38
+
39
+ ```ts
40
+ await audit.init({
41
+ userTable: 'hai_iam_users',
42
+ userIdColumn: 'id',
43
+ userNameColumn: 'username',
44
+ })
45
+ ```
46
+
47
+ ### 3. 关闭
48
+
49
+ ```ts
50
+ const closeResult = await audit.close()
51
+ if (!closeResult.success) {
52
+ throw new Error(closeResult.error.message)
53
+ }
54
+ ```
55
+
56
+ ## 核心 API
57
+
58
+ ### 生命周期
59
+
60
+ | 方法 | 签名 | 说明 |
61
+ | --------------------- | ---------------------------------------------------------------- | ---------- |
62
+ | `audit.init` | `(config?: AuditInitConfigInput) => Promise<HaiResult<void>>` | 初始化模块 |
63
+ | `audit.close` | `() => Promise<HaiResult<void>>` | 关闭模块 |
64
+ | `audit.isInitialized` | `boolean` | 初始化状态 |
65
+
66
+ ### 日志操作
67
+
68
+ | 方法 | 签名 | 说明 |
69
+ | --------------------- | ----------------------------------------------------------------------------------- | -------------------- |
70
+ | `audit.log` | `(input: CreateAuditLogInput) => Promise<HaiResult<AuditLog>>` | 记录审计日志 |
71
+ | `audit.list` | `(options?: ListAuditLogsOptions) => Promise<HaiResult<PaginatedResult<AuditLogWithUser>>>` | 分页查询(items/total/page/pageSize,含用户名;使用返回的规范化元数据,默认 pageSize 20) |
72
+ | `audit.getUserRecent` | `(userId: string, limit?: number) => Promise<HaiResult<AuditLog[]>>` | 用户最近活动 |
73
+ | `audit.cleanup` | `(olderThanDays?: number) => Promise<HaiResult<number>>` | 清理旧日志 |
74
+ | `audit.getStats` | `(days?: number) => Promise<HaiResult<AuditStatItem[]>>` | 操作统计 |
75
+
76
+ ### 便捷记录器 (`audit.helper`)
77
+
78
+ | 方法 | 说明 |
79
+ | ------------------------------------------------------------------------------ | ------------ |
80
+ | `audit.helper.login(userId, ip?, ua?)` | 登录 |
81
+ | `audit.helper.logout(userId, ip?, ua?)` | 登出 |
82
+ | `audit.helper.register(userId, ip?, ua?)` | 注册 |
83
+ | `audit.helper.passwordResetRequest(email, ip?, ua?)` | 密码重置请求 |
84
+ | `audit.helper.passwordResetComplete(userId, ip?, ua?)` | 密码重置完成 |
85
+ | `audit.helper.crud(input)` | CRUD 操作 |
86
+
87
+ > `audit.log()` / `audit.helper.*()` 中的 `details` 仅会自动脱敏凭证类字段(如 `password` / `token` / `secret`);邮箱、手机号默认保留原值。
88
+
89
+ ## 错误码
90
+
91
+ | 错误码 | code | 说明 |
92
+ | --------------------------------- | ---------------- | ------------ |
93
+ | `HaiAuditError.LOG_FAILED` | `hai:audit:001` | 记录失败 |
94
+ | `HaiAuditError.QUERY_FAILED` | `hai:audit:002` | 查询失败 |
95
+ | `HaiAuditError.CLEANUP_FAILED` | `hai:audit:003` | 清理失败 |
96
+ | `HaiAuditError.STATS_FAILED` | `hai:audit:004` | 统计失败 |
97
+ | `HaiAuditError.INIT_IN_PROGRESS` | `hai:audit:005` | 初始化进行中 |
98
+ | `HaiAuditError.NOT_INITIALIZED` | `hai:audit:010` | 模块未初始化 |
99
+ | `HaiAuditError.CONFIG_ERROR` | `hai:audit:012` | 配置错误 |
100
+
101
+ ## 常见模式
102
+
103
+ ### 用户认证审计
104
+
105
+ ```ts
106
+ // 登录
107
+ await audit.helper.login(userId, request.ip, request.headers['user-agent'])
108
+
109
+ // 登出
110
+ await audit.helper.logout(userId, request.ip, request.headers['user-agent'])
111
+ ```
112
+
113
+ ### CRUD 操作审计
114
+
115
+ ```ts
116
+ await audit.helper.crud({ userId, action: 'create', resource: 'users', resourceId: newUser.id, details: { name: newUser.name } })
117
+ await audit.helper.crud({ userId, action: 'update', resource: 'roles', resourceId: roleId, details: { changes } })
118
+ await audit.helper.crud({ userId, action: 'delete', resource: 'permissions', resourceId: permId })
119
+ ```
120
+
121
+ ### 仪表盘数据
122
+
123
+ ```ts
124
+ // 最近日志
125
+ const recentLogs = await audit.list({ pageSize: 10 })
126
+
127
+ // 7 天统计
128
+ const stats = await audit.getStats(7)
129
+ ```
130
+
131
+ ### 定期清理
132
+
133
+ ```ts
134
+ // 清理 90 天前的日志
135
+ const result = await audit.cleanup(90)
136
+ if (result.success) {
137
+ logger.info(`Cleaned up ${result.data} old audit logs`)
138
+ }
139
+ ```
140
+
141
+ ## 相关 Skills
142
+
143
+ - `hai-reldb` — 数据库模块(审计模块依赖)
144
+ - `hai-iam` — 身份认证与权限管理(审计的主要记录对象)
145
+ - `hai-core` — 核心工具(日志、ID 生成、i18n)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-build
3
- description: "定位 hai 应用架构、模块依赖、初始化顺序、构建模式和对应技能。"
3
+ description: "构建或排查 hai 应用的源码、容器镜像和桌面产物;识别工程架构、依赖与对应构建入口。"
4
4
  ---
5
5
 
6
6
  # hai-build
@@ -22,25 +22,31 @@ description: "定位 hai 应用架构、模块依赖、初始化顺序、构建
22
22
  - 功能开发先用 [development.md](../hai-app-create/development.md) 把需求匹配到当前版本的框架能力与配置;再读取所选模块 skill 和公开类型。
23
23
  - 有生命周期的模块按真实依赖初始化、检查结果并反序清理;core、纯函数和工厂不套用相同生命周期。
24
24
  - 构建任务检查当前 engines、适配器、缓存与目标产物,不能照搬框架仓库私有构建命令。
25
+ - 多项门禁按 [分轮验证与集中修复](../hai-app-tests/validation.md) 先收集结果再集中修复;遵守产物依赖与资源隔离,不并行清理/重建同一输出目录,不用旧产物补成通过。
25
26
 
26
27
  ## 构建与部署
27
28
 
28
- - `pnpm build` 默认仅编译源码(调用 compile);`pnpm build --target docker` 根据 hai.build.json 构建并导出 app/可选 web 镜像,auto 优先可达 Podman、其次 Docker,可用 --runtime 显式指定。
29
- - compile 交付源码编译结果,docker 交付镜像归档,desktop 交付 Tauri 系统安装包;每次只选一种。images[].target 是 Dockerfile 阶段名,fullstack 的 app 使用 runtime、web 使用 web,不能与 CLI target 混用;参数默认值和示例必须与真实入口同步。
29
+ - 创建默认包含开发期部署工具,可用 --no-deploy 关闭。每个镜像使用 images/ 中独立 Dockerfile,Web 配置位于 images/web/,不使用镜像阶段 target 配置。
30
+ - 构建器生成 images/dependencies/ 安装输入;fetch、安装和生产依赖整理共用 pnpm store,安装与生产依赖整理共用 SQLite 驱动缓存,并复用依赖层。file: 归档、补丁、workspace 元数据必须进入下载上下文;原生依赖缓存按系统/架构/ABI 区分。基础镜像、中间件及工具指定版本,禁止 latest。
31
+ - 默认容器面向 CPU 运行或远程 AI 服务,Dockerfile 设置 `ONNXRUNTIME_NODE_INSTALL_CUDA=skip`,跳过额外 CUDA/TensorRT 下载;使用宿主机 GPU 时定制镜像和运行参数,不修改本机 npm 或全局 AI/GPU 配置。
32
+ - pnpm run deploy --build 显式串联构建、打包和发布;--bundle-only 只交付完整离线包与跨平台脚本;--services 以服务器成功状态为基线只更新选中服务。完整包自动包含自建中间件镜像,服务器禁止联网拉取。
33
+ - `pnpm build` 默认仅编译源码(调用 compile);`pnpm build --target docker` 根据 hai.build.json 的 dockerfile 构建并导出 app/可选 web 镜像,可用 --images web 只构建前端,auto 优先可达 Podman、其次 Docker,可用 --runtime 显式指定。
34
+ - compile 交付源码编译结果,docker 交付镜像归档,desktop 交付 Tauri 系统安装包;每次只选一种。images[].dockerfile 指向独立镜像文件,最终阶段作为交付镜像;参数默认值和示例必须与真实入口同步。
30
35
  - Podman 的客户端代理通过预定义构建参数传递,覆盖 Windows/macOS 远程引擎的服务端默认值;代理地址必须能从构建容器访问,--no-http-proxy 禁止此传递。不要把代理写成 Dockerfile ENV 或固化进交付镜像。
31
36
  - --network 默认沿用引擎网络;只有实际构建网络无法访问依赖/代理时才显式选择网络,pasta 仅适用于 rootless Podman。build 与 run 的主机名解析可能不同,须验证构建网络中的可达性,不默认改成 host 网络。
32
- - 从运行 build 的应用根 package.json.version 读取版本,不能使用 CLI/框架版本或服务子包版本代替。清单格式 version=2,appVersion 为应用版本;镜像 tar 名含版本、buildId 区分同版本构建,Tauri 也接收根版本。不要手改清单或保留无版本的交付归档。
33
- - 构建不区分 dev/test/prod,不加载部署环境覆盖,不注入运行期凭据;完整镜像清单含 SHA-256,全部成功才发布 latest。不要让 compile、Dockerfile 或 Tauri beforeBuildCommand 递归调用自身的原生/镜像构建。
37
+ - 从运行 build 的应用根 package.json.version 读取版本,不能使用 CLI/框架版本或服务子包版本代替。清单格式 version=3,appVersion 为应用版本;appContainerPort/webContainerPort 仅记录 images 配置显式填写的端口,部分构建也保留未选镜像端口,未填写时沿用部署默认值。镜像 tar 名含版本、buildId 区分同版本构建,Tauri 也接收根版本。不要手改清单或保留无版本的交付归档。
38
+ - 构建不区分 dev/test/prod,不加载部署环境覆盖,不注入运行期凭据;所选镜像清单含 SHA-256,全部成功才更新本机最近构建指针(不是镜像标签)。不要让 compile、Dockerfile 或 Tauri beforeBuildCommand 递归调用自身的原生/镜像构建。
34
39
  - Vite 的 envDir 与 SvelteKit 的 kit.env.dir 是独立加载入口,必须同时阻止构建阶段读取工程根的 .env 文件;开发/预览由 core loader 注入,运行期由 deploy 注入。用临时 .env.local 哨兵值检查输出,避免只过滤进程变量却仍把私有文件固化进产物。
35
40
  - 静态客户端通过 runtime-config.js 或原生宿主初始化脚本获取公开地址与元数据;仅投影公开白名单,禁止将完整 process.env、服务配置或密钥序列化到浏览器。预览与容器启动均须验证真实登录,不能依赖构建期 API 地址让 E2E 通过。
41
+ - Fullstack Capacitor App 在 `public/runtime-config.js` 提供公开 API 地址,再编译并同步原生资源;Tauri 从启动进程的 PUBLIC_API_BASE 注入。不能把开发代理的 .env 配置当作已安装原生包的运行配置。
36
42
  - `pnpm build --target desktop --platform windows|linux|macos --arch x64|arm64` 调用对应工程的 Tauri build。要求在目标系统准备 Rust target、SDK 和打包工具,不假定跨系统编译。原生安装包必须按目标系统实测。
37
- - deploy 只消费已有产物,详见 [hai-deploy](../hai-deploy/SKILL.md)。网络不通时 --bundle-only 生成 tar,按说明手工搬运;packaged 不代表部署已运行。
38
- - 测试临时工程、镜像、容器、辅助进程、日志、截图和报告必须在 finally/teardown 自动清理,失败/取消也清理;不得提交测试产物或删除用户既有资源。
43
+ - deploy 默认消费已有产物,--build 显式先构建,详见 [hai-deploy](../hai-deploy/SKILL.md)。网络不通时 --bundle-only 生成 tar,按说明手工搬运;packaged 不代表部署已运行。
44
+ - 测试临时工程、镜像、容器、辅助进程和数据必须在 finally/teardown 自动清理,失败/取消也清理;日志、截图和报告保留到分析和交付结束再清理,不得提交测试产物或删除用户既有资源。
39
45
  - 浏览器测试的数据放入本轮独立临时目录;Windows 等子进程与服务退出后再删除数据库和上传文件,不依赖下一轮测试补清理。
40
46
 
41
47
  ## 按需参考
42
48
 
43
- 详细配置、API 签名、错误码和范本在 [reference.md](reference.md)。按下列标题定位所需小节,不默认通读;用例中的业务变量需结合当前应用补齐。
49
+ 按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
44
50
 
45
51
  - 适用场景
46
52
  - 项目架构
@@ -42,7 +42,7 @@
42
42
  | 公共 API | oRPC contract + Hono + typed client |
43
43
  | 认证 | Bearer access token + httpOnly refresh cookie(浏览器推荐) |
44
44
 
45
- pnpm 专属配置放在 `pnpm-workspace.yaml`,`.npmrc` 仅用于注册表与认证。Docker 缓存层使用 `pnpm fetch` 读取锁文件;pnpm 12 的该命令不支持 `--frozen-lockfile`。后续仍通过 `pnpm install --offline --frozen-lockfile` 校验清单与锁文件一致。容器裁剪用 `pnpm pm deploy --filter <服务包> --prod <目标目录>`,确保执行内置命令,避免项目同名 `deploy` 脚本覆盖它。
45
+ pnpm 专属配置放在 `pnpm-workspace.yaml`,`.npmrc` 仅用于注册表与认证。Docker 缓存层使用 `pnpm fetch` 读取锁文件;pnpm 12 的该命令不支持 `--frozen-lockfile`。后续仍通过 `pnpm install --offline --frozen-lockfile` 校验清单与锁文件一致。容器裁剪用 `pnpm pm deploy --filter <服务包> --prod <目标目录>`,确保执行内置命令,避免项目同名 `deploy` 脚本覆盖它。保留 `.gitattributes`,让 `.dockerignore`、Dockerfile 和 POSIX 脚本统一使用 LF,保持跨平台输入一致并确保 Linux 脚本可运行。
46
46
 
47
47
  ### SvelteKit Adapter 模式
48
48
 
@@ -277,12 +277,15 @@ pnpm --filter <app-name> test:e2e
277
277
  pnpm --filter <app-name> test
278
278
  pnpm --filter <app-name> test:e2e
279
279
 
280
- # Refactor:质量门禁
280
+ # Refactor:按下方分轮验证流程收集适用门禁
281
281
  pnpm typecheck
282
282
  pnpm lint
283
+ pnpm build # 构建/配置/模板/跨包契约变更
283
284
  pnpm test
284
285
  ```
285
286
 
287
+ 以上命令按 [分轮验证与集中修复](../hai-app-tests/validation.md) 调度,不在普通失败时立即修改并从头重跑。
288
+
286
289
  ## 统一编码规范
287
290
 
288
291
  ### 强制规则
@@ -311,7 +314,7 @@ return kit.response.ok(result.data)
311
314
 
312
315
  ### 质量门禁
313
316
 
314
- 按当前项目已定义的 scripts 和变更影响执行;PowerShell 使用 `pnpm.cmd`:
317
+ 按当前项目已定义的 scripts 和变更影响选择检查,执行 [分轮验证与集中修复](../hai-app-tests/validation.md):普通失败先记录,独立检查继续,汇总后集中修复。PowerShell 使用 `pnpm.cmd`:
315
318
 
316
319
  ```bash
317
320
  pnpm typecheck # TypeScript 类型检查
@@ -338,11 +341,9 @@ pnpm test # Vitest 单元测试
338
341
 
339
342
  ### 变更后:验证
340
343
 
341
- 1. `pnpm typecheck` 通过
342
- 2. `pnpm lint` 通过
343
- 3. 构建/配置/跨包变更 `pnpm build` 通过
344
- 4. `pnpm test` 通过(单元测试)
345
- 5. UI/路由/端到端改动执行相应 `test:e2e`;从 package.json 确认入口
346
- 6. 搜索确认所有引用点已更新,报告未验证边界
344
+ 1. 按影响范围调度一轮 typecheck/lint/build/test;UI/路由/端到端改动加相应 E2E,从 package.json 确认入口
345
+ 2. 记录全部可执行结果与日志;真实前置失败只阻塞依赖项,普通失败不边测边改
346
+ 3. 归并问题并集中修复,再验证失败、阻塞及受影响项;最终必需门禁对最终代码有效通过
347
+ 4. 搜索确认所有引用点已更新,报告未验证边界与结果来源
347
348
 
348
349
  涉及原生交付时按当前项目 Tauri/Gradle/Xcode 等实际入口打包,在目标 OS/设备验证安装启动、会话、安全 TokenStorage、API 域名及覆盖升级。Web 构建/E2E 不代替原生验收;记录平台/工具链与结果,缺平台写未验证。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-cache
3
- description: "使用 @h-ai/cache 的 Memory/Redis 缓存、TTL、集合、排行榜和锁。"
3
+ description: "实现或排查 Memory/Redis 缓存、TTL、集合与分布式锁,处理过期、回源和并发边界。"
4
4
  ---
5
5
 
6
6
  # hai-cache
@@ -15,188 +15,21 @@ description: "使用 @h-ai/cache 的 Memory/Redis 缓存、TTL、集合、排行
15
15
  | 输出 | 缓存和锁操作的 HaiResult,获锁 data 为 boolean |
16
16
  | 限制 | 仅服务端;Memory 不跨节点。release/extend 携带本次 owner,避免锁过期后误操作新持有者。 |
17
17
 
18
- > `@h-ai/cache` 提供统一缓存接口,支持 Memory 与 Redis 后端,包含 KV / Hash / List / Set / ZSet / 分布式锁 六类操作。
18
+ ## 使用路径
19
19
 
20
- 内存 KV 在到期时间点即失效。整数计数器拒绝 null/boolean/array、非整数增量和安全整数溢出,失败不修改原值;Hash 的 `__proto__` / `constructor` 名称按普通字段保留。
20
+ 1. 先确定单进程 Memory 还是共享 Redis;配置校验与 init 成功后才使用。cache 不依赖 reldb。
21
+ 2. 根据 KV/Hash/List/Set/ZSet 选择公开操作,明确 TTL、空值和回源失败的业务含义。
22
+ 3. 每次获锁生成唯一 owner,检查 success 与 data;续期/释放复用该 owner,丢锁后停止受保护工作。
23
+ 4. 验证过期、连接失败、并发与锁过期后的旧任务;最后 close。
21
24
 
22
- ## 运行环境
25
+ ## 按需参考
23
26
 
24
- > ⚠️ **服务端模块(Node.js only)。** 浏览器端无需直接操作缓存,由服务端模块(如 IAM、Scheduler)内部使用。
27
+ 按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
25
28
 
26
- ## 适用场景
27
-
28
- - 缓存热点数据减少数据库查询
29
- - 会话存储(配合 IAM)
30
- - 分布式缓存(Redis)与本地测试缓存(Memory)
31
- - 集合操作(权限缓存、标签集合)
32
- - 排行榜(ZSet)与队列(List)
33
- - 分布式锁(多节点部署互斥控制)
34
-
35
- ## 使用步骤
36
-
37
- ### 1. 配置
38
-
39
- ```yaml
40
- # config/_cache.yml
41
- type: memory # memory | redis
42
- # Redis 配置(type=redis 时生效):
43
- # host: localhost
44
- # port: 6379
45
- # password: ''
46
- # db: 0
47
- # keyPrefix: 'hai:'
48
- ```
49
-
50
- 约定环境变量优先于 YAML,例如 `HAI_CACHE_TYPE`、`HAI_CACHE_HOST`、
51
- `HAI_CACHE_KEYPREFIX`。
52
-
53
- ### 2. 初始化与关闭
54
-
55
- ```typescript
56
- import { cache } from '@h-ai/cache'
57
-
58
- await cache.init(core.config.get('cache'))
59
- // 使用后关闭
60
- await cache.close()
61
- ```
62
-
63
- `cache.config` 返回的是脱敏后的配置快照;Redis `password` / `url` 等敏感值不会原样暴露给日志或调试输出。
64
-
65
- ## 核心 API
66
-
67
- ### KV 操作(`cache.kv`)
68
-
69
- | 方法 | 说明 |
70
- | ----------------------------------- | -------------------- |
71
- | `get / set / del / exists` | 基础读写与存在性判断 |
72
- | `expire / expireAt / ttl / persist` | TTL 管理 |
73
- | `incr / incrBy / decr / decrBy` | 计数器操作 |
74
- | `mget / mset` | 批量读写 |
75
- | `scan / keys / type` | Key 检索与类型判断 |
76
-
77
- ```typescript
78
- await cache.kv.set('user:123', { name: '张三' }, { ex: 3600 })
79
- const result = await cache.kv.get<{ name: string }>('user:123')
80
- if (result.success && result.data) {
81
- // result.data.name === '张三'
82
- }
83
-
84
- await cache.kv.del('user:123')
85
- ```
86
-
87
- ### Hash/List/Set/ZSet(`cache.hash/list/set_/zset`)
88
-
89
- - `cache.hash`:对象字段读写(如用户 profile 局部更新)
90
- - `cache.list`:队列/消息顺序处理
91
- - `cache.set_`:去重集合(成员关系、权限集合)
92
- - `cache.zset`:分数排序(排行榜、权重调度)
93
-
94
- ```typescript
95
- await cache.hash.hset('profile:1', { nickname: 'alice' })
96
- await cache.list.lpush('queue:jobs', 'job-1', 'job-2')
97
- await cache.set_.sadd('role:admin:perms', 'user.read', 'user.write')
98
- await cache.zset.zadd('rank:daily', { member: 'u1', score: 100 })
99
- ```
100
-
101
- ### 分布式锁(`cache.lock`)
102
-
103
- | 方法 | 说明 |
104
- | ---------- | ------------------------------------------ |
105
- | `acquire` | 尝试获锁(SET NX EX),返回 true/false |
106
- | `release` | 释放锁(支持 owner 验证,防止误释放) |
107
- | `isLocked` | 检查锁是否被持有 |
108
- | `extend` | 续期锁 TTL(支持 owner 验证) |
109
-
110
- ```typescript
111
- // 每次尝试生成唯一 owner,后续释放/续期复用同一个值
112
- const owner = core.id.generate()
113
- const acquired = await cache.lock.acquire('my-lock', { ttl: 30, owner })
114
- if (acquired.success && acquired.data) {
115
- try {
116
- // 长任务仅在仍持有锁时续期;失败或 data=false 时停止依赖锁的后续操作
117
- const extended = await cache.lock.extend('my-lock', 60, owner)
118
- if (!extended.success || !extended.data) {
119
- logger.warn('Lock extension failed; protected work cancelled')
120
- }
121
- else {
122
- // 受保护的操作
123
- }
124
- }
125
- finally {
126
- await cache.lock.release('my-lock', owner)
127
- }
128
- }
129
-
130
- // 检查
131
- const locked = await cache.lock.isLocked('my-lock')
132
- ```
133
-
134
- **最佳实践:**
135
-
136
- - `owner` 每次获锁尝试唯一,释放/续期复用该值;仅用 nodeId 会使同节点旧任务误释放过期后重新获得的锁
137
- - 释放锁时传入 `owner` 防止误释放他人锁
138
- - Memory 后端适合开发/测试;Redis 后端用 Lua 脚本保证 release/extend 原子性
139
-
140
- ## 错误码 — `HaiCacheError`
141
-
142
- | 错误码 | code | 说明 |
143
- |--------|------|------|
144
- | `HaiCacheError.CONNECTION_FAILED` | `hai:cache:001` | 连接失败 |
145
- | `HaiCacheError.OPERATION_FAILED` | `hai:cache:002` | 操作失败 |
146
- | `HaiCacheError.SERIALIZATION_FAILED` | `hai:cache:003` | 序列化失败 |
147
- | `HaiCacheError.DESERIALIZATION_FAILED` | `hai:cache:004` | 反序列化失败 |
148
- | `HaiCacheError.KEY_NOT_FOUND` | `hai:cache:005` | 键不存在 |
149
- | `HaiCacheError.TIMEOUT` | `hai:cache:006` | 超时 |
150
- | `HaiCacheError.NOT_INITIALIZED` | `hai:cache:010` | 未初始化 |
151
- | `HaiCacheError.UNSUPPORTED_TYPE` | `hai:cache:011` | 不支持的缓存类型 |
152
- | `HaiCacheError.CONFIG_ERROR` | `hai:cache:012` | 配置错误 |
153
-
154
- ## 常见模式
155
-
156
- ### 缓存命中与回源(未缓存空值,不防穿透)
157
-
158
- ```typescript
159
- async function getUserCached(userId: string) {
160
- const cached = await cache.kv.get<User>(`user:${userId}`)
161
- if (cached.success && cached.data)
162
- return cached.data
163
-
164
- const user = await userRepo.findById(userId)
165
- if (user.success && user.data) {
166
- await cache.kv.set(`user:${userId}`, user.data, { ex: 3600 })
167
- return user.data
168
- }
169
- return null
170
- }
171
- ```
172
-
173
- ### 分布式锁保护共享资源
174
-
175
- ```typescript
176
- const lockKey = 'batch:import'
177
- const owner = core.id.generate()
178
- const acquired = await cache.lock.acquire(lockKey, { ttl: 60, owner })
179
- if (acquired.success && acquired.data) {
180
- try {
181
- await runBatchImport()
182
- }
183
- finally {
184
- await cache.lock.release(lockKey, owner)
185
- }
186
- }
187
- else if (acquired.success) {
188
- logger.info('Another node is running the import')
189
- }
190
- else {
191
- logger.error('Lock acquisition failed', { code: acquired.error.code })
192
- }
193
- ```
194
-
195
- ## 相关 Skills
196
-
197
- - `hai-build`:模块初始化顺序(cache 在 db 之后、iam 之前)
198
- - `hai-core`:配置与 HaiResult 模型
199
- - `hai-iam`:会话存储与权限缓存(底层使用 cache)
200
- - `hai-scheduler`:定时任务分布式锁(底层使用 cache.lock)
201
- - `hai-reach`:消息发送互斥锁(底层使用 cache.lock)
202
- - `hai-kit`:SvelteKit 集成层
29
+ - [运行环境](reference.md#运行环境)
30
+ - [适用场景](reference.md#适用场景)
31
+ - [使用步骤](reference.md#使用步骤)
32
+ - [核心 API](reference.md#核心-api)
33
+ - [错误码 — `HaiCacheError`](reference.md#错误码--haicacheerror)
34
+ - [常见模式](reference.md#常见模式)
35
+ - [相关 Skills](reference.md#相关-skills)
@@ -0,0 +1,187 @@
1
+ # hai-cache 参考
2
+
3
+ > `@h-ai/cache` 提供统一缓存接口,支持 Memory 与 Redis 后端,包含 KV / Hash / List / Set / ZSet / 分布式锁 六类操作。
4
+
5
+ 内存 KV 在到期时间点即失效。整数计数器拒绝 null/boolean/array、非整数增量和安全整数溢出,失败不修改原值;Hash 的 `__proto__` / `constructor` 名称按普通字段保留。
6
+
7
+ ## 运行环境
8
+
9
+ > ⚠️ **服务端模块(Node.js only)。** 浏览器端无需直接操作缓存,由服务端模块(如 IAM、Scheduler)内部使用。
10
+
11
+ ## 适用场景
12
+
13
+ - 缓存热点数据减少数据库查询
14
+ - 会话存储(配合 IAM)
15
+ - 分布式缓存(Redis)与本地测试缓存(Memory)
16
+ - 集合操作(权限缓存、标签集合)
17
+ - 排行榜(ZSet)与队列(List)
18
+ - 分布式锁(多节点部署互斥控制)
19
+
20
+ ## 使用步骤
21
+
22
+ ### 1. 配置
23
+
24
+ ```yaml
25
+ # config/_cache.yml
26
+ type: memory # memory | redis
27
+ # Redis 配置(type=redis 时生效):
28
+ # host: localhost
29
+ # port: 6379
30
+ # password: ''
31
+ # db: 0
32
+ # keyPrefix: 'hai:'
33
+ ```
34
+
35
+ 约定环境变量优先于 YAML,例如 `HAI_CACHE_TYPE`、`HAI_CACHE_HOST`、
36
+ `HAI_CACHE_KEYPREFIX`。
37
+
38
+ ### 2. 初始化与关闭
39
+
40
+ ```typescript
41
+ import { cache } from '@h-ai/cache'
42
+
43
+ await cache.init(core.config.get('cache'))
44
+ // 使用后关闭
45
+ await cache.close()
46
+ ```
47
+
48
+ `cache.config` 返回的是脱敏后的配置快照;Redis `password` / `url` 等敏感值不会原样暴露给日志或调试输出。
49
+
50
+ ## 核心 API
51
+
52
+ ### KV 操作(`cache.kv`)
53
+
54
+ | 方法 | 说明 |
55
+ | ----------------------------------- | -------------------- |
56
+ | `get / set / del / exists` | 基础读写与存在性判断 |
57
+ | `expire / expireAt / ttl / persist` | TTL 管理 |
58
+ | `incr / incrBy / decr / decrBy` | 计数器操作 |
59
+ | `mget / mset` | 批量读写 |
60
+ | `scan / keys / type` | Key 检索与类型判断 |
61
+
62
+ ```typescript
63
+ await cache.kv.set('user:123', { name: '张三' }, { ex: 3600 })
64
+ const result = await cache.kv.get<{ name: string }>('user:123')
65
+ if (result.success && result.data) {
66
+ // result.data.name === '张三'
67
+ }
68
+
69
+ await cache.kv.del('user:123')
70
+ ```
71
+
72
+ ### Hash/List/Set/ZSet(`cache.hash/list/set_/zset`)
73
+
74
+ - `cache.hash`:对象字段读写(如用户 profile 局部更新)
75
+ - `cache.list`:队列/消息顺序处理
76
+ - `cache.set_`:去重集合(成员关系、权限集合)
77
+ - `cache.zset`:分数排序(排行榜、权重调度)
78
+
79
+ ```typescript
80
+ await cache.hash.hset('profile:1', { nickname: 'alice' })
81
+ await cache.list.lpush('queue:jobs', 'job-1', 'job-2')
82
+ await cache.set_.sadd('role:admin:perms', 'user.read', 'user.write')
83
+ await cache.zset.zadd('rank:daily', { member: 'u1', score: 100 })
84
+ ```
85
+
86
+ ### 分布式锁(`cache.lock`)
87
+
88
+ | 方法 | 说明 |
89
+ | ---------- | ------------------------------------------ |
90
+ | `acquire` | 尝试获锁(SET NX EX),返回 true/false |
91
+ | `release` | 释放锁(支持 owner 验证,防止误释放) |
92
+ | `isLocked` | 检查锁是否被持有 |
93
+ | `extend` | 续期锁 TTL(支持 owner 验证) |
94
+
95
+ ```typescript
96
+ // 每次尝试生成唯一 owner,后续释放/续期复用同一个值
97
+ const owner = core.id.generate()
98
+ const acquired = await cache.lock.acquire('my-lock', { ttl: 30, owner })
99
+ if (acquired.success && acquired.data) {
100
+ try {
101
+ // 长任务仅在仍持有锁时续期;失败或 data=false 时停止依赖锁的后续操作
102
+ const extended = await cache.lock.extend('my-lock', 60, owner)
103
+ if (!extended.success || !extended.data) {
104
+ logger.warn('Lock extension failed; protected work cancelled')
105
+ }
106
+ else {
107
+ // 受保护的操作
108
+ }
109
+ }
110
+ finally {
111
+ await cache.lock.release('my-lock', owner)
112
+ }
113
+ }
114
+
115
+ // 检查
116
+ const locked = await cache.lock.isLocked('my-lock')
117
+ ```
118
+
119
+ **最佳实践:**
120
+
121
+ - `owner` 每次获锁尝试唯一,释放/续期复用该值;仅用 nodeId 会使同节点旧任务误释放过期后重新获得的锁
122
+ - 释放锁时传入 `owner` 防止误释放他人锁
123
+ - Memory 后端适合开发/测试;Redis 后端用 Lua 脚本保证 release/extend 原子性
124
+
125
+ ## 错误码 — `HaiCacheError`
126
+
127
+ | 错误码 | code | 说明 |
128
+ |--------|------|------|
129
+ | `HaiCacheError.CONNECTION_FAILED` | `hai:cache:001` | 连接失败 |
130
+ | `HaiCacheError.OPERATION_FAILED` | `hai:cache:002` | 操作失败 |
131
+ | `HaiCacheError.SERIALIZATION_FAILED` | `hai:cache:003` | 序列化失败 |
132
+ | `HaiCacheError.DESERIALIZATION_FAILED` | `hai:cache:004` | 反序列化失败 |
133
+ | `HaiCacheError.KEY_NOT_FOUND` | `hai:cache:005` | 键不存在 |
134
+ | `HaiCacheError.TIMEOUT` | `hai:cache:006` | 超时 |
135
+ | `HaiCacheError.NOT_INITIALIZED` | `hai:cache:010` | 未初始化 |
136
+ | `HaiCacheError.UNSUPPORTED_TYPE` | `hai:cache:011` | 不支持的缓存类型 |
137
+ | `HaiCacheError.CONFIG_ERROR` | `hai:cache:012` | 配置错误 |
138
+
139
+ ## 常见模式
140
+
141
+ ### 缓存命中与回源(未缓存空值,不防穿透)
142
+
143
+ ```typescript
144
+ async function getUserCached(userId: string) {
145
+ const cached = await cache.kv.get<User>(`user:${userId}`)
146
+ if (cached.success && cached.data)
147
+ return cached.data
148
+
149
+ const user = await userRepo.findById(userId)
150
+ if (user.success && user.data) {
151
+ await cache.kv.set(`user:${userId}`, user.data, { ex: 3600 })
152
+ return user.data
153
+ }
154
+ return null
155
+ }
156
+ ```
157
+
158
+ ### 分布式锁保护共享资源
159
+
160
+ ```typescript
161
+ const lockKey = 'batch:import'
162
+ const owner = core.id.generate()
163
+ const acquired = await cache.lock.acquire(lockKey, { ttl: 60, owner })
164
+ if (acquired.success && acquired.data) {
165
+ try {
166
+ await runBatchImport()
167
+ }
168
+ finally {
169
+ await cache.lock.release(lockKey, owner)
170
+ }
171
+ }
172
+ else if (acquired.success) {
173
+ logger.info('Another node is running the import')
174
+ }
175
+ else {
176
+ logger.error('Lock acquisition failed', { code: acquired.error.code })
177
+ }
178
+ ```
179
+
180
+ ## 相关 Skills
181
+
182
+ - `hai-build`:模块初始化顺序(cache 在 iam 之前;cache 自身不依赖 reldb)
183
+ - `hai-core`:配置与 HaiResult 模型
184
+ - `hai-iam`:会话存储与权限缓存(底层使用 cache)
185
+ - `hai-scheduler`:定时任务分布式锁(底层使用 cache.lock)
186
+ - `hai-reach`:消息发送互斥锁(底层使用 cache.lock)
187
+ - `hai-kit`:SvelteKit 集成层
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hai-capacitor
3
- description: "使用 @h-ai/capacitor 接入原生设备、相机、推送、状态栏和安全 Token 存储。"
3
+ description: "在 Android/iOS 应用中接入 @h-ai/capacitor 设备、相机、推送与安全 Token 存储,处理 Web/原生差异。"
4
4
  ---
5
5
 
6
6
  # hai-capacitor
@@ -13,16 +13,17 @@ description: "使用 @h-ai/capacitor 接入原生设备、相机、推送、状
13
13
  | 适用场景 | Android/iOS 原生壳集成与平台差异处理 |
14
14
  | 输入 | Capacitor 配置、原生插件、设备权限和平台状态 |
15
15
  | 输出 | 原生操作结果及 TokenStorage 适配器 |
16
- | 限制 | 区分浏览器与 Android/iOS;安全存储需原生插件,Web 不降级到 localStorage。H5 验证不能代替真机验证。 |
16
+ | 限制 | 区分浏览器与 Android/iOS;安全存储需原生插件,Web 使用 httpOnly Cookie/内存认证,不降级到 localStorage。H5 验证不能代替真机验证。 |
17
17
 
18
18
  ## 使用路径
19
19
 
20
20
  - 先判断平台和插件可用性,复用已有 SPA 入口;原生 UI 变化验证权限拒绝、取消和返回路径。
21
21
  - TokenStorage 在原生端使用安全插件;Web 下不伪造持久化成功。
22
+ - 原生打包前确认公开 API 地址的运行配置入口;fullstack App 使用 `public/runtime-config.js`,构建不会读取部署环境文件。这里只放公开地址,不能放 Token 或密钥。
22
23
 
23
24
  ## 按需参考
24
25
 
25
- 详细配置、API 签名、错误码和范本在 [reference.md](reference.md)。按下列标题定位所需小节,不默认通读;用例中的业务变量需结合当前应用补齐。
26
+ 按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
26
27
 
27
28
  - 运行环境
28
29
  - 适用场景