@lark-base-open/base-database-cli 1.1.1 → 1.3.7

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 (222) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/LICENSE +6 -0
  3. package/README.md +45 -27
  4. package/dist/ai-functions.d.ts +42 -0
  5. package/dist/ai-functions.d.ts.map +1 -0
  6. package/dist/ai-functions.js +327 -0
  7. package/dist/ai-functions.js.map +1 -0
  8. package/dist/archive.d.ts +21 -0
  9. package/dist/archive.d.ts.map +1 -1
  10. package/dist/archive.js +65 -10
  11. package/dist/archive.js.map +1 -1
  12. package/dist/auth.d.ts +5 -0
  13. package/dist/auth.d.ts.map +1 -1
  14. package/dist/auth.js +26 -3
  15. package/dist/auth.js.map +1 -1
  16. package/dist/check.d.ts +74 -0
  17. package/dist/check.d.ts.map +1 -0
  18. package/dist/check.js +283 -0
  19. package/dist/check.js.map +1 -0
  20. package/dist/dependency-policy.d.ts +10 -0
  21. package/dist/dependency-policy.d.ts.map +1 -0
  22. package/dist/dependency-policy.js +162 -0
  23. package/dist/dependency-policy.js.map +1 -0
  24. package/dist/deploy.d.ts +1 -0
  25. package/dist/deploy.d.ts.map +1 -1
  26. package/dist/deploy.js +122 -175
  27. package/dist/deploy.js.map +1 -1
  28. package/dist/environment-variables.d.ts +52 -0
  29. package/dist/environment-variables.d.ts.map +1 -0
  30. package/dist/environment-variables.js +563 -0
  31. package/dist/environment-variables.js.map +1 -0
  32. package/dist/environment.d.ts +1 -1
  33. package/dist/environment.js +1 -1
  34. package/dist/environment.js.map +1 -1
  35. package/dist/function-input-types.d.ts +10 -0
  36. package/dist/function-input-types.d.ts.map +1 -0
  37. package/dist/function-input-types.js +160 -0
  38. package/dist/function-input-types.js.map +1 -0
  39. package/dist/function-schema.d.ts.map +1 -1
  40. package/dist/function-schema.js +89 -36
  41. package/dist/function-schema.js.map +1 -1
  42. package/dist/init-field-retry.d.ts +17 -0
  43. package/dist/init-field-retry.d.ts.map +1 -0
  44. package/dist/init-field-retry.js +127 -0
  45. package/dist/init-field-retry.js.map +1 -0
  46. package/dist/init-journal.d.ts +98 -0
  47. package/dist/init-journal.d.ts.map +1 -0
  48. package/dist/init-journal.js +492 -0
  49. package/dist/init-journal.js.map +1 -0
  50. package/dist/init-schema.d.ts +8 -0
  51. package/dist/init-schema.d.ts.map +1 -0
  52. package/dist/init-schema.js +423 -0
  53. package/dist/init-schema.js.map +1 -0
  54. package/dist/init.d.ts +14 -0
  55. package/dist/init.d.ts.map +1 -1
  56. package/dist/init.js +883 -261
  57. package/dist/init.js.map +1 -1
  58. package/dist/lark-cli-error.d.ts +46 -0
  59. package/dist/lark-cli-error.d.ts.map +1 -0
  60. package/dist/lark-cli-error.js +171 -0
  61. package/dist/lark-cli-error.js.map +1 -0
  62. package/dist/lark-cli.d.ts +1 -1
  63. package/dist/lark-cli.d.ts.map +1 -1
  64. package/dist/lark-cli.js +66 -18
  65. package/dist/lark-cli.js.map +1 -1
  66. package/dist/mutation-schema.d.ts +146 -0
  67. package/dist/mutation-schema.d.ts.map +1 -0
  68. package/dist/mutation-schema.js +153 -0
  69. package/dist/mutation-schema.js.map +1 -0
  70. package/dist/mutation.d.ts +10 -0
  71. package/dist/mutation.d.ts.map +1 -0
  72. package/dist/mutation.js +131 -0
  73. package/dist/mutation.js.map +1 -0
  74. package/dist/npm.d.ts +1 -0
  75. package/dist/npm.d.ts.map +1 -1
  76. package/dist/npm.js +2 -2
  77. package/dist/npm.js.map +1 -1
  78. package/dist/preview-receipt.d.ts.map +1 -1
  79. package/dist/preview-receipt.js +15 -5
  80. package/dist/preview-receipt.js.map +1 -1
  81. package/dist/process.d.ts +3 -1
  82. package/dist/process.d.ts.map +1 -1
  83. package/dist/process.js +16 -4
  84. package/dist/process.js.map +1 -1
  85. package/dist/program.d.ts.map +1 -1
  86. package/dist/program.js +240 -41
  87. package/dist/program.js.map +1 -1
  88. package/dist/project-build.d.ts +25 -0
  89. package/dist/project-build.d.ts.map +1 -0
  90. package/dist/project-build.js +229 -0
  91. package/dist/project-build.js.map +1 -0
  92. package/dist/project-context.d.ts +344 -0
  93. package/dist/project-context.d.ts.map +1 -0
  94. package/dist/project-context.js +485 -0
  95. package/dist/project-context.js.map +1 -0
  96. package/dist/project-layout.d.ts +5 -0
  97. package/dist/project-layout.d.ts.map +1 -0
  98. package/dist/project-layout.js +24 -0
  99. package/dist/project-layout.js.map +1 -0
  100. package/dist/public-sql-error.d.ts +25 -0
  101. package/dist/public-sql-error.d.ts.map +1 -0
  102. package/dist/public-sql-error.js +81 -0
  103. package/dist/public-sql-error.js.map +1 -0
  104. package/dist/pull.d.ts.map +1 -1
  105. package/dist/pull.js +54 -15
  106. package/dist/pull.js.map +1 -1
  107. package/dist/site-readme.d.ts +3 -0
  108. package/dist/site-readme.d.ts.map +1 -0
  109. package/dist/site-readme.js +83 -0
  110. package/dist/site-readme.js.map +1 -0
  111. package/dist/workspace-fingerprint.d.ts +21 -1
  112. package/dist/workspace-fingerprint.d.ts.map +1 -1
  113. package/dist/workspace-fingerprint.js +81 -2
  114. package/dist/workspace-fingerprint.js.map +1 -1
  115. package/package.json +2 -1
  116. package/skills/lark-base-database/SKILL.md +66 -50
  117. package/skills/lark-base-database/references/ai-function.md +471 -0
  118. package/skills/lark-base-database/references/base-capability-routing.md +3 -3
  119. package/skills/lark-base-database/references/database-function-limitations.md +46 -0
  120. package/skills/lark-base-database/references/database-function.md +349 -0
  121. package/skills/lark-base-database/references/design/components/actions-fields.md +16 -0
  122. package/skills/lark-base-database/references/design/components/code/controls.css +122 -0
  123. package/skills/lark-base-database/references/design/components/code/controls.tsx +73 -0
  124. package/skills/lark-base-database/references/design/components/code/date-picker.tsx +42 -0
  125. package/skills/lark-base-database/references/design/components/code/floating.tsx +41 -0
  126. package/skills/lark-base-database/references/design/components/code/overlays.tsx +19 -0
  127. package/skills/lark-base-database/references/design/components/code/select.tsx +34 -0
  128. package/skills/lark-base-database/references/design/components/code/styles.d.ts +1 -0
  129. package/skills/lark-base-database/references/design/components/code/theme.css +49 -0
  130. package/skills/lark-base-database/references/design/components/code/usage.tsx +38 -0
  131. package/skills/lark-base-database/references/design/components/date-time-number.md +9 -0
  132. package/skills/lark-base-database/references/design/components/feedback-navigation.md +9 -0
  133. package/skills/lark-base-database/references/design/components/index.md +18 -0
  134. package/skills/lark-base-database/references/design/components/overlays.md +9 -0
  135. package/skills/lark-base-database/references/design/components/patterns/flat-business.md +7 -0
  136. package/skills/lark-base-database/references/design/components/selection.md +24 -0
  137. package/skills/lark-base-database/references/design/components.md +91 -0
  138. package/skills/lark-base-database/references/design/composition.md +90 -0
  139. package/skills/lark-base-database/references/design/control-recipes.md +107 -0
  140. package/skills/lark-base-database/references/design/examples.md +172 -0
  141. package/skills/lark-base-database/references/design/foundations.md +126 -0
  142. package/skills/lark-base-database/references/design/review.md +82 -0
  143. package/skills/lark-base-database/references/design/scenes/analysis-report.md +66 -0
  144. package/skills/lark-base-database/references/design/scenes/brand-showcase.md +70 -0
  145. package/skills/lark-base-database/references/design/scenes/business-operations.md +85 -0
  146. package/skills/lark-base-database/references/design/scenes/campaign-report.md +66 -0
  147. package/skills/lark-base-database/references/design/scenes/dashboard.md +77 -0
  148. package/skills/lark-base-database/references/design/scenes/festive-campaign.md +69 -0
  149. package/skills/lark-base-database/references/design/theme.md +27 -0
  150. package/skills/lark-base-database/references/design/typography.md +56 -0
  151. package/skills/lark-base-database/references/development.md +165 -32
  152. package/skills/lark-base-database/references/environment-variables.md +55 -0
  153. package/skills/lark-base-database/references/faas.md +41 -48
  154. package/skills/lark-base-database/references/file-transfer.md +25 -25
  155. package/skills/lark-base-database/references/frontend-design-guide.md +33 -77
  156. package/skills/lark-base-database/references/mutations.md +295 -0
  157. package/skills/lark-base-database/references/permission-design.md +24 -24
  158. package/skills/lark-base-database/references/release.md +51 -24
  159. package/skills/lark-base-database/references/style-guide.md +10 -67
  160. package/skills/lark-base-database/references/verification.md +15 -11
  161. package/skills/lark-base-database/references/workspace.md +46 -8
  162. package/templates/database-starter/README.md +34 -4
  163. package/templates/database-starter/database-functions/README.md +8 -0
  164. package/templates/database-starter/mutations/README.md +19 -0
  165. package/templates/database-starter/page/README.md +25 -19
  166. package/templates/database-starter/page/package-lock.json +8 -16
  167. package/templates/database-starter/page/package.json +2 -3
  168. package/templates/database-starter/page/src/App.tsx +23 -3
  169. package/templates/database-starter/page/src/components/UserSelectorEditor/index.tsx +32 -4
  170. package/templates/database-starter/page/src/components/UserSelectorPanel/index.tsx +104 -34
  171. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.test.ts +55 -11
  172. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.ts +68 -25
  173. package/templates/database-starter/page/src/components/database-runtime-ready.tsx +1 -1
  174. package/templates/database-starter/page/src/components/database-user-menu/index.tsx +4 -7
  175. package/templates/database-starter/page/src/components/feedback/page-state.tsx +40 -5
  176. package/templates/database-starter/page/src/components/floating-button/assets/edit-active.svg +3 -0
  177. package/templates/database-starter/page/src/components/floating-button/assets/edit-default.svg +3 -0
  178. package/templates/database-starter/page/src/components/floating-button/index.tsx +75 -112
  179. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.test.ts +107 -37
  180. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.ts +13 -50
  181. package/templates/database-starter/page/src/components/ui-language.test.tsx +101 -0
  182. package/templates/database-starter/page/src/examples/ai-function/ImageTaskOperation.tsx +202 -0
  183. package/templates/database-starter/page/src/examples/ai-function/README.md +14 -0
  184. package/templates/database-starter/page/src/examples/ai-function/TextStreamOperation.tsx +184 -0
  185. package/templates/database-starter/page/src/examples/ai-function/ai-copy.ts +173 -0
  186. package/templates/database-starter/page/src/examples/ai-function/ai-delta.test.ts +30 -0
  187. package/templates/database-starter/page/src/examples/ai-function/ai-delta.ts +26 -0
  188. package/templates/database-starter/page/src/examples/ai-function/ai-language.test.tsx +86 -0
  189. package/templates/database-starter/page/src/examples/ai-function/ai-media-panel.test.ts +201 -0
  190. package/templates/database-starter/page/src/examples/ai-function/ai-media-panel.tsx +382 -0
  191. package/templates/database-starter/page/src/lib/attachment-image.tsx +144 -0
  192. package/templates/database-starter/page/src/lib/database-function-inputs.generated.ts +4 -0
  193. package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +180 -76
  194. package/templates/database-starter/page/src/lib/database-runtime.test.ts +130 -10
  195. package/templates/database-starter/page/src/lib/database-runtime.ts +484 -51
  196. package/templates/database-starter/page/src/lib/date-time.ts +61 -0
  197. package/templates/database-starter/page/src/lib/ui-language.ts +86 -0
  198. package/templates/database-starter/page/src/main.tsx +1 -1
  199. package/templates/database-starter/page/tsconfig.json +2 -2
  200. package/templates/database-starter/page/vite.config.ts +1 -2
  201. package/templates/database-starter/prettierignore +1 -1
  202. package/templates/database-starter/server-functions/README.md +2 -2
  203. package/templates/database-starter/server-functions/package-lock.json +4 -4
  204. package/templates/database-starter/server-functions/package.json +1 -1
  205. package/dist/agent-guide.d.ts +0 -19
  206. package/dist/agent-guide.d.ts.map +0 -1
  207. package/dist/agent-guide.js +0 -31
  208. package/dist/agent-guide.js.map +0 -1
  209. package/dist/database-skill.d.ts +0 -24
  210. package/dist/database-skill.d.ts.map +0 -1
  211. package/dist/database-skill.js +0 -148
  212. package/dist/database-skill.js.map +0 -1
  213. package/skills/lark-base-database/references/base-sql-unsupported.md +0 -42
  214. package/skills/lark-base-database/references/database-capability-skill.md +0 -40
  215. package/skills/lark-base-database/references/sql-function.md +0 -289
  216. package/templates/database-capability/SKILL.md.tmpl +0 -58
  217. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.test.ts +0 -23
  218. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.tsx +0 -873
  219. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.test.ts +0 -25
  220. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.tsx +0 -197
  221. package/templates/database-starter/page/src/components/floating-button/utils/env.ts +0 -18
  222. package/templates/database-starter/sql/README.md +0 -8
@@ -1,51 +1,94 @@
1
1
  # Page 开发与 Function 调用
2
2
 
3
- 实现或修改 Page 的运行时接入时读取。FaaS 新增或修改时,还须在 meta.json 显式声明 inputSchema,见[FaaS 开发](faas.md#faas-开发)。SQL 方言见[SQL Function](sql-function.md),服务端 handler 与记录写入见[FaaS 协议](faas.md),设计原则见[工程质量](style-guide.md)。
3
+ 实现或修改 Page 的运行时接入时读取。FaaS 新增或修改时,还须在 meta.json 显式声明 inputSchema,见[FaaS 开发](faas.md#faas-开发)。基础单表写入与批量调用见[Mutation Function](mutations.md)。查询语法见[Database Function](database-function.md),服务端 handler 与记录写入见[FaaS 协议](faas.md),设计原则见[工程质量](style-guide.md)。
4
4
 
5
5
  ## 工程结构
6
6
 
7
7
  ```text
8
8
  project/
9
9
  ├── page/
10
- ├── sql/<functionId>/{main.sql,meta.json}
10
+ ├── database-functions/<functionId>/{main.sql,meta.json}
11
+ ├── mutations/<functionId>/{spec.json,meta.json}
11
12
  └── server-functions/functions/<functionId>/{index.ts,meta.json}
12
13
  ```
13
14
 
14
- SQL 与 FaaS 的 `functionId` 在 应用 内全局唯一,匹配 `/^[A-Za-z0-9_-]+$/`;SQL ID 长度为 1~64。`functionId` 会作为 URL 路径段参与运行态调用,除该字符集外不接受其它字符。不同 FaaS Function 目录独立构建,不跨目录 import。Page 和 `server-functions/` 分别维护自己的 npm 工程与 `package-lock.json`。
15
+ Database Function、Mutation 与 FaaS 的 `functionId` 在应用内全局唯一,匹配 `/^[A-Za-z0-9_-]+$/`;Database Function ID 长度为 1~64。`functionId` 会作为 URL 路径段参与运行态调用,除该字符集外不接受其它字符。不同 FaaS Function 目录独立构建,不跨目录 import。Page 和 `server-functions/` 分别维护自己的 npm 工程与 `package-lock.json`。
16
+
17
+ ## 第三方依赖策略
18
+
19
+ 为 Page 或 FaaS 新增、升级第三方 npm 依赖前,先查询当前应用的服务端策略。路径参数 `<app-token>` 是当前应用的 appToken,从工程的 `base-database.jsonc` 读取(不是 Base token):
20
+
21
+ ```bash
22
+ base-database-cli lark-cli -- api GET /open-apis/base/v3/sites/<app-token>/get --params '{"withDependencyPolicy":true}' --json
23
+ ```
24
+
25
+ 响应中的 `data.dependencyPolicy` 是唯一策略事实源,字段形如:
26
+
27
+ ```json
28
+ {
29
+ "restrictedProtocols": ["file", "git"],
30
+ "rejectLocalPaths": true,
31
+ "rejectScpGitSyntax": true,
32
+ "rejectHostedGitShorthand": true,
33
+ "allowedValues": ["https://registry[.]example[.]com/.*[.]tgz"]
34
+ }
35
+ ```
36
+
37
+ 普通 npm registry 版本、semver、`npm:` alias 和服务端未限制的协议默认可用。四个限制字段分别控制:`restrictedProtocols` 列出禁用的 URL 协议,如 `file:../pkg`、`git+https://example.com/pkg.git`;`rejectLocalPaths` 拒绝本地路径,如 `../pkg`、`/tmp/pkg`;`rejectScpGitSyntax` 拒绝 SCP 风格 Git 地址,如 `git@example.com:team/pkg.git`;`rejectHostedGitShorthand` 拒绝 hosted-git shorthand,如 `github:team/pkg`、`team/pkg`。
38
+
39
+ `allowedValues` 的每一项都是 RE2 语法的正则表达式;受限来源只有在原始 dependency value(未 trim、未改大小写)被其中某项**完整匹配**时才可用。不能按包名、域名、前缀或子串推断放行,也不能把 `allowedValues` 解释成 npm 包名白名单。固定字符串里的 `.`、`+`、`?` 等正则元字符必须写成 `[.]`、`\+`、`[?]` 等转义形式。
40
+
41
+ 策略查询失败、字段缺失或类型错误,或 `allowedValues` 含非法正则时,停止添加依赖,保留错误信息与 LogID 并通过支持渠道反馈;不用内置列表或历史策略继续。`deploy` 会在安装依赖前再次查询策略,递归校验最终制品范围内的全部 `package.json`;本地预检通过不替代上传接口的最终校验。
15
42
 
16
43
  ## Function 调用契约
17
44
 
18
- ABI 是 Page、应用 loader 与 Function 之间固定的数据边界。Page 侧两类 Function 都通过 `invokeDatabaseFunction(functionId, params)` 调用,但 `params` 类型不同:
45
+ Page 与 Function 之间使用固定的调用契约。Page 侧三类 Function 都通过 `invokeDatabaseFunction(functionId, params)` 调用,但 `params` 类型不同:
19
46
 
20
47
  `functionId` 使用源码目录名,是工程常量。应用 token 由 `page/src/lib/database-runtime.ts` 从产物托管路径解析,不由业务代码传入;FaaS 内部的 `context.invokeFunction` 仍使用 `{projectId,functionId,params}`,两者不要混用。
21
48
 
22
- | Function | `params` | 实现 | 结果 |
23
- |---|---|---|---|
24
- | SQL | 按 `$1`、`$2` 排列的数组 | `main.sql` + `meta.json` | 一层 `{data:{rows},code,msg}` |
25
- | FaaS | 业务对象 | `index.ts` + `meta.json` | 一层 `{data,code,msg}`,`data` 是 handler 业务数据 |
49
+ | Function | `params` | 实现 | 结果 |
50
+ | ---------------------- | ------------------------------------------ | ------------------------ | ------------------------------------------------------------------------------------ |
51
+ | Database Function | 按 `$1`、`$2` 排列的数组 | `main.sql` + `meta.json` | 一层 `{data:{rows},code,msg}` |
52
+ | Mutation | 声明顺序的位置数组;多组参数用批量 helper | `spec.json` + `meta.json` | 一层 `{data:{record_ids,records?,record_not_found?,ignored_fields?},code,msg}` |
53
+ | FaaS | 业务对象 | `index.ts` + `meta.json` | 一层 `{data,code,msg}`,`data` 是 handler 业务数据 |
26
54
 
27
- SQL 示例:
55
+ Database Function 查询示例:
28
56
 
29
57
  ```ts
30
- await invokeDatabaseFunction<{ rows: unknown[] }>('listTasks', ['待处理'])
58
+ await invokeDatabaseFunction<{ rows: unknown[] }>('listTasks', ['待处理']);
31
59
  ```
32
60
 
61
+ Mutation 一组参数使用 `invokeDatabaseFunction(functionId, params)`,每组操作一条记录;多组参数使用 `invokeDatabaseMutationBatch(functionId, parameterGroups)`,包装成 `{batch:[{params:[...]}]}`,不能与顶层 params 混用。两个 helper 均从 `page/src/lib/database-runtime.ts` 导出。`code=0` 仍须检查 `data.record_not_found/ignored_fields`,见[调用与结果](mutations.md#调用与结果)。
62
+
33
63
  FaaS 创建操作示例:
34
64
 
35
65
  ```ts
36
66
  await invokeDatabaseFunction<{ created: boolean | null }>('createTask', {
37
67
  title: '准备评审',
38
68
  status: '待处理',
39
- })
69
+ });
40
70
  ```
41
71
 
72
+ `base-database-cli check --json` 与 Preview `deploy` 会先校验本地 Function metadata,再把 Database Function 的
73
+ `meta.params` 和 FaaS `inputSchema` 生成到
74
+ `page/src/lib/database-function-inputs.generated.ts`,随后才编译 Page。不要手改该文件;新增或修改
75
+ Function 后运行 `check`,让 `functionId`、查询参数顺序以及 FaaS 必填字段和枚举值在 Page
76
+ 编译时保持一致。动态字符串不能作为 `functionId` 绕过这份本地契约。
77
+
78
+ metadata 只声明输入,因此生成文件只约束调用参数。调用结果仍由 Page 按 operation 定义
79
+ `invokeDatabaseFunction<TData>` 的 `TData`,并在运行时校验真实响应;CLI 不根据 handler
80
+ 实现或查询投影猜测返回类型。生成类型覆盖标量类型、对象属性、必填项、枚举、数组元素和
81
+ `prefixItems` 位置,以及 Database Function 的固定参数 tuple;`minimum`、`maximum`、`minLength`、
82
+ `maxLength`、非固定数组的 `minItems` / `maxItems` 和对象精确性仍由 Function 运行时校验;
83
+ 数组的元素类型和 `prefixItems` 位置仍保留在生成类型中。TypeScript 类型不能替代输入解析。
84
+
42
85
  `invokeDatabaseFunction` 调用统一网关的 `POST /space/api/bitable/site/v1/:token/invoke_function/:function_id`,由 BaseApp 父页面以同源身份代发;应用 token 与环境由 `src/lib/database-runtime.ts` 统一解析,`env` 走 query,请求体固定为 `{"params": <上述 params>}`。返回统一包裹结构 `{code,msg,data}`,页面使用当前登录用户身份,不保存也不传递任何凭证。
43
86
 
44
87
  ## Page 调用
45
88
 
46
89
  默认 Page 的 `page/index.html` 需要把 `<title>` 设置为本次生成的 应用 名称,不留空。
47
90
 
48
- 默认 Page 必须引用并渲染 `page/src/components/floating-button/index.tsx` 中的 `FloatingButton` 组件,并显式传入 `language`。该组件自行解析应用 token,不接受 `projectId`。悬浮控件提供「编辑应用」和 Chatbot 入口。
91
+ 默认 Page 必须引用并渲染 `page/src/components/floating-button/index.tsx` 中的 `FloatingButton` 组件。该组件自行解析应用 token,不接受 `projectId`。悬浮控件仅在正式环境向服务端判定有管理权限的访问者提供「编辑应用」入口。
49
92
 
50
93
  当前账号信息(头像、姓名、组织、退出登录)由**应用自己的导航**承载,用 `page/src/components/database-user-menu/index.tsx` 的 `DatabaseUserMenu`。账号菜单位于应用导航区域,例如顶栏右上角或侧边栏底部:
51
94
 
@@ -54,13 +97,13 @@ await invokeDatabaseFunction<{ created: boolean | null }>('createTask', {
54
97
  <header className="flex items-center justify-between ...">
55
98
  <h1>设备台账</h1>
56
99
  {/* 判据:员工 / 部门 / 领用归还属于组织内多角色协作 */}
57
- <DatabaseUserMenu language="zh" audience="enterprise" />
100
+ <DatabaseUserMenu audience="enterprise" />
58
101
  </header>
59
102
 
60
103
  // 侧边栏左下角
61
104
  <aside className="flex flex-col ...">
62
105
  <nav className="flex-1">…</nav>
63
- <DatabaseUserMenu language="zh" audience="enterprise" align="left" side="up" />
106
+ <DatabaseUserMenu audience="enterprise" align="left" side="up" />
64
107
  </aside>
65
108
  ```
66
109
 
@@ -71,36 +114,126 @@ await invokeDatabaseFunction<{ created: boolean | null }>('createTask', {
71
114
  `page/src/main.tsx` 已在首屏挂载后通过 `DatabaseRuntimeReady` 上报 `siteRuntime.ready()`,并在启动失败时上报 `reportLoadFailed`。宿主从握手完成起 10 秒内等不到 ready 会展示加载失败态,因此不要把 ready 上报移到数据加载完成之后,也不要删除该组件。
72
115
 
73
116
  涉及人员搜索或人员选择的页面,优先引用 `page/src/components/UserSelectorEditor` 和 `page/src/components/UserSelectorPanel` 等 `page/src/components/` 下的人员组件;只有现有组件无法满足交互契约时才做局部扩展。
74
- 选人组件返回的 `User.user_id` 是 Base 人员单元格使用的内部用户 ID;写入人员字段时固定传 `[{"id": user.user_id}]`。`User.open_id` 只是可选的开放平台标识,不得填入 应用记录 写入 payload。
117
+ 这些一方组件以及 `DatabaseUserMenu`、`FloatingButton`、`PageState`、`AttachmentImage` 内置中文、英文、日文固定文案,不依赖 Starling。组件统一按宿主 Runtime Context 的 locale → 浏览器语言 → 英语选择文案,无需 Page 传入或写死语言;宿主切换语言后,组件随重新加载的页面重新解析。Page 自己编写的业务文案仍由应用负责。
118
+ 选人组件返回的 `User.user_id` 是 Base 人员单元格使用的内部用户 ID。Mutation 的 `USER_ID` 参数传该 ID 字符串,`list:true` 时传 ID 数组;FaaS 下游人员单元格传 `[{"id": user.user_id}]`。`User.open_id` 只是可选的开放平台标识,不得填入应用记录写入 payload。
75
119
 
76
120
  业务请求集中在 operation/runtime client,不散落在组件中:
77
121
 
78
122
  ```ts
79
- import { invokeDatabaseFunction } from '../lib/database-runtime'
123
+ import { invokeDatabaseFunction } from '../lib/database-runtime';
80
124
 
81
- interface SqlRows<TRow> {
82
- rows: TRow[]
83
- columns?: unknown[]
84
- row_count?: number
85
- log_id?: string
125
+ interface DatabaseRows<TRow> {
126
+ rows: TRow[];
127
+ columns?: unknown[];
128
+ row_count?: number;
129
+ log_id?: string;
86
130
  }
87
131
  ```
88
132
 
89
- 浏览器 SDK 对 SQL 和 FaaS 都只返回一层 `{data,code,msg}`。`invokeDatabaseFunction<TData>` 的 `TData` 只描述当前 operation 约定的顶层 `data`:SQL 通常是 ResultJSON,FaaS 则以各 handler 的业务结果为准,不能统一假设存在 `recordId`。页面只检查一次顶层 `code`,再按 operation 契约校验 `data`;不得写成 `invokeDatabaseFunction<FaasHandlerResult<TData>>`,也不得对 `result.data` 二次解包。否则后端已经写入成功时,页面仍可能误报失败。
133
+ 浏览器 SDK 对 Database Function、Mutation 和 FaaS 都只返回一层 `{data,code,msg}`。`invokeDatabaseFunction<TData>` 的 `TData` 只描述当前 operation 约定的顶层 `data`:Database Function 通常是 ResultJSON,FaaS 则以各 handler 的业务结果为准,不能统一假设存在 `recordId`。页面只检查一次顶层 `code`,再按 operation 契约校验 `data`;不得写成 `invokeDatabaseFunction<FaasHandlerResult<TData>>`,也不得对 `result.data` 二次解包。否则后端已经写入成功时,页面仍可能误报失败。
90
134
 
91
135
  运行时业务数据在边界处校验,不能只依赖 TypeScript 泛型。`functionId` 和所有路由是工程常量;用户输入只进入已声明的业务参数。
92
136
 
93
- Page 的所有业务 HTTP 都经父页面代理,使用 `page/src/lib/database-runtime.ts` 导出的 `fetch` 或 `invokeHttp`,URL 必须是以单个 `/` 开头的同源相对路径;绝对 URL、跨域 URL、路径穿越以及 Cookie、Authorization、Host、Origin 等受限 Header 都会被父页面拒绝。请求体只支持字符串,4xx/5xx 会正常返回 `Response`,业务必须自行判断 `response.ok`。禁止直连业务域名,也不得把直连作为代理失败时的兜底。Page 发起的 Base 业务读取和写入仍分别走 SQL Function 与 FaaS。
137
+ Page 的所有业务 HTTP 都经父页面代理,使用 `page/src/lib/database-runtime.ts` 导出的 `fetch` 或 `invokeHttp`,URL 必须是以单个 `/` 开头的同源相对路径;绝对 URL、跨域 URL、路径穿越以及 Cookie、Authorization、Host、Origin 等受限 Header 都会被父页面拒绝。请求体只支持字符串,4xx/5xx 会正常返回 `Response`,业务必须自行判断 `response.ok`。禁止直连业务域名,也不得把直连作为代理失败时的兜底。Page 发起的 Base 业务读取和写入分别走 Database Function 与 Mutation/FaaS。
138
+
139
+ ## 日期与时区
140
+
141
+ Database Function 返回类型与 FaaS 写入格式分别以 [查询类型](database-function.md#查询类型与结果值) 和 [CellValue 协议](faas.md#base-读写协议)为准;Mutation 日期参数见 [Mutation 声明与参数](mutations.md#声明与参数)。转换时先区分字段语义:
142
+
143
+ | 业务语义 | 应用内表示 | 转换边界 |
144
+ | ------------------------------ | ---------------------- | ---------------------------------------------------------------------------- |
145
+ | 入库日、生日、到期日等日历日期 | `YYYY-MM-DD` 字符串 | 从 Base TIMESTAMP 回填时按该字段约定的业务时区取日期;表单编辑期间保持字符串 |
146
+ | 下单、上传等事件时刻 | Unix epoch 毫秒 number | 显示时按目标时区格式化;写入无偏移文本时按写入端解析时区格式化 |
147
+
148
+ 已有 Base 先通过 `lark-cli base +base-get --base-token <base-token>` 核对 `time_zone`;新建使用本次明确设置的 Base 时区。记录业务日期时区、写入解析时区及显示时区是否一致。`init` 的默认值、浏览器时区和 FaaS 进程时区都不能代替已有 Base 的真实配置;不要给所有应用固定加八小时。多 Base 应用分别确认。
149
+
150
+ `toISOString()` 输出 UTC。`toISOString().slice(0, 10)` 只适用于明确要求 UTC 日期的场景,不能用于其他业务时区的日期回填;同样,截掉 `Z` 再将 UTC 文本作为本地时间写入,会改变事件时刻。不要对已正确的 epoch 再加偏移后交给带 `timeZone` 的格式化器,否则会重复转换。
151
+
152
+ ### 转换与编辑示例
153
+
154
+ 工程 `page/src/lib/date-time.ts` 提供不依赖浏览器或 SDK 的纯函数:
155
+
156
+ - `formatDateInput(epochMs, timeZone)`:时间戳到该业务时区的 `YYYY-MM-DD`。
157
+ - `formatBaseDateTime(epochMs, timeZone)`:时间戳到该时区的 `YYYY-MM-DD HH:mm:ss`。
158
+ - `dateInputToBaseDateTime(value)`:校验日历日期并补 `00:00:00`,不先转成 UTC。
159
+
160
+ 旧工程没有 helper 时,从当前 CLI 的 starter 复制此文件并保持原工程其余接入。FaaS 需要使用时,将该无依赖文件复制到目标 `server-functions/functions/<functionId>/`,在同一 Function 内导入;不要跨 Function 或从 Page 目录运行时导入。
161
+
162
+ 以下 `BASE_TIME_ZONE` 是应用根据 Base 信息确定的常量,示例值不是默认值。不要让任意请求参数改变写入解释时区。
163
+
164
+ ```ts
165
+ // Page:时间戳只在回填边界转换,用户输入仍保持日历日期。
166
+ import { formatDateInput } from './lib/date-time';
167
+
168
+ const BASE_TIME_ZONE = 'Asia/Shanghai'; // 示例:已核对该 Base 的 time_zone
169
+
170
+ export function initialDate(stored: number | null): string {
171
+ return stored === null ? '' : formatDateInput(stored, BASE_TIME_ZONE);
172
+ }
173
+
174
+ export function editPayload(
175
+ publisher: string,
176
+ dateValue: string,
177
+ originalDate: string,
178
+ ) {
179
+ return {
180
+ publisher,
181
+ ...(dateValue !== originalDate ? { addedAt: dateValue } : {}),
182
+ };
183
+ }
184
+ ```
185
+
186
+ ```ts
187
+ // FaaS:同一 Function 目录内使用 helper,产生符合现有 CellValue 的文本。
188
+ import { dateInputToBaseDateTime, formatBaseDateTime } from './date-time';
189
+
190
+ const BASE_TIME_ZONE = 'Asia/Shanghai'; // 示例:已核对无偏移写入按此时区解释
191
+
192
+ export function updateFields(input: { publisher: string; addedAt?: string }) {
193
+ return {
194
+ 出版社: input.publisher,
195
+ ...(input.addedAt !== undefined
196
+ ? { 入库日期: dateInputToBaseDateTime(input.addedAt) }
197
+ : {}),
198
+ };
199
+ }
200
+
201
+ export function eventFields(now: number) {
202
+ return { 上传时间: formatBaseDateTime(now, BASE_TIME_ZONE) };
203
+ }
204
+ ```
205
+
206
+ 示例采用日期字段的增量更新,因此更新 Schema 中 `addedAt` 为可选项,创建 Schema 可继续要求必填。handler 按 [FaaS 输入契约](faas.md#faas-开发)校验并处理 helper 抛出的无效日期错误。清空日期需明确提交清空操作,不把空字符串解释成清空或今天。
207
+
208
+ 无偏移文本不能唯一表示夏令时回拨时的重复小时。业务需要这类精确时刻往返时,先确认写入端支持的无歧义表示,不靠固定偏移或 helper 宣称已保证。显示时区与写入时区不同可以成立,但界面必须清楚表达,不能将显示文本直接回写。
209
+
210
+ ### 日期与时间验证
211
+
212
+ - 纯日期:午夜前后、月末/年末、闰日;在不同浏览器/进程时区下回填相同业务日期。
213
+ - 编辑:只修改其他字段,写入 payload 不包含日期,底表原日期不变;主动修改日期后回读与选择一致;空值和 epoch `0` 分别处理。
214
+ - 事件:用已知 epoch 验证写入文本与目标时区,Database Function 回读后对应同一时刻(按接口精度比较),再分别验证页面和底表显示。覆盖实际使用时区的偏移和夏令时边界。
215
+
216
+ 历史数据偏移先核对原始值、字段语义与时区配置,再判断是否需要修复;不要因发现一处转换错误而批量平移历史时间。
217
+
218
+ ## AI 对话与调用
219
+
220
+ AI 调用使用 AI Function,不创建 FaaS 模型代理,也不直连模型域名。
221
+ Preview 使用草稿版本;Prod 使用正式发布的版本。Function 配置为 `disabled: true` 时不得调用。
222
+ 能力归属按运行场景判断:面向当前页面业务流程的生成、分类、总结和推荐使用 AI Function;Base 查询与写入仍分别使用 Database Function 和 FaaS。
223
+
224
+ Page 需要 AI 对话、生成、分类、总结、文生图或文生视频时,默认按 `functionKey` 使用 `page/src/lib/database-runtime.ts` 导出的 `callSiteAIFunction` / `invokeSiteAIFunctionStream`,不创建 FaaS 模型代理,也不直连模型域名。AI 能力被收口成一类版本化托管的 AI Function 配置;四类能力配置契约、CLI 建模/调试、计费与单流约束见 [AI Function](ai-function.md)。starter 的 `page/src/examples/ai-function/TextStreamOperation.tsx` 提供最小组件示例。搭建时可复用这些示例的界面及 `ai-copy.ts`,保留 `useUiLanguage()` 接入;内置标题、提示、按钮、任务状态和本地兜底错误自动跟随宿主 locale → 浏览器语言 → 英语。Page 显式传入的 `title` 由 Page 自行处理,服务端错误与 AI 生成内容保留原文。
225
+
226
+ AI 与 Base 数据组合时保持既有边界:先用 Database Function 读取,再由 Page 裁剪必要数据并拼入 `messages` 或 `variables`;需要把结果写回 Base 时另行调用 FaaS。AI 调用本身不经过 FaaS。
94
227
 
95
228
  ## 开发期与运行态输出
96
229
 
97
- | 调用层 | 判定方式 |
98
- | --- | --- |
99
- | lark-cli 默认 JSON 输出 | 先检查进程退出与外层 `ok`,再按该命令的 `data` 契约取值;错误诊断见 `error` |
100
- | Page `invokeDatabaseFunction` | 检查一次顶层 `code`,再校验 operation 的 `data` |
101
- | SQL Function | `data` 为 ResultJSON,包含 rows 等查询信息 |
102
- | FaaS handler | 返回 `{data,code,msg}`,data 为业务结果,不再由 Page 二次解包 |
103
- | FaaS 下游记录写入 | 检查下游 envelope 和各 batch 的结果语义,见[FaaS 协议](faas.md#应用记录-写入协议) |
230
+ | 调用层 | 判定方式 |
231
+ | ----------------------------- | --------------------------------------------------------------------------------- |
232
+ | lark-cli 默认 JSON 输出 | 先检查进程退出与外层 `ok`,再按该命令的 `data` 契约取值;错误诊断见 `error` |
233
+ | Page `invokeDatabaseFunction` | 检查一次顶层 `code`,再校验 operation 的 `data` |
234
+ | Database Function | `data` 为 ResultJSON,包含 rows 等查询信息 |
235
+ | FaaS handler | 返回 `{data,code,msg}`,data 为业务结果,不再由 Page 二次解包 |
236
+ | FaaS 下游记录写入 | 检查下游 envelope 和各 batch 的结果语义,见[FaaS 协议](faas.md#应用记录-写入协议) |
104
237
 
105
238
  不要把 SDK 的 `code===0` 套在 CLI 外层,也不要将 CLI 外层的 data 当作 handler 业务数据。raw API 的命令封装与业务响应按当前 lark-cli reference 分别解析。
106
239
 
@@ -108,8 +241,8 @@ Page 的所有业务 HTTP 都经父页面代理,使用 `page/src/lib/database-
108
241
 
109
242
  实现前确定 operation 固定输入输出,按[工程质量](style-guide.md)检查远程调用依赖。新建示例数据在[发布阶段](release.md#从零建站示例数据验收)通过 CLI 写入真实 Base;不创建仅用于播种的 FaaS,也不在 Page 加载时自动写入。
110
243
 
111
- 保留 starter 的 `createArgusCsrfPlugin()` 配置;不手写 CSRF token。Page 的读取与写入分别通过 SQL/FaaS,自动化使用[原生能力](base-capability-routing.md),不从页面模拟后台定时任务。附件上传使用 `siteRuntime.uploadAttachment()`,缩略图与下载复用 starter 的 `buildAttachmentCoverUrl`、`fetchAttachmentDownloadUrl`;附件单元格追加/移除经 FaaS 调用增量路由,能力、并发和重试边界见[文件传输](file-transfer.md)。不能以 mock、未约定网关或旧列表整格覆盖代替。
244
+ Page 的读取与写入分别通过 Database Function 与 Mutation/FaaS,自动化使用[原生能力](base-capability-routing.md),不从页面模拟后台定时任务。附件上传使用 `siteRuntime.uploadAttachment()`,缩略图与下载复用 starter 的 `fetchAttachmentCover`、`fetchAttachmentDownloadUrl`;附件的写入选型、上传、展示与重试边界见[文件传输](file-transfer.md)。不能以 mock、未约定网关或旧列表整格覆盖代替。
112
245
 
113
- 修改或恢复旧 应用 时,不能假定工程已接入该插件。写请求出现 CSRF 相关失败时,按[旧工程与 CSRF 排查](verification.md#旧工程与-csrf-排查)核对插件、依赖和构建产物;缺失时按当前 starter 补齐,必要时升级 JSSDK,再重建发布。不得复制插件实现或手写 `x-secsdk-csrf-token`。
246
+ 修改或恢复旧应用时,按[旧工程与 CSRF 排查](verification.md#旧工程与-csrf-排查)迁移页面通信和构建配置。业务请求的身份与 CSRF 处理由宿主负责;Page 不注入 CSRF 脚本或手写 CSRF Header。
114
247
 
115
248
  实现完成后按[发布与交付](release.md)继续;用户明确要求运行态检查时按[诊断与验收](verification.md)执行。
@@ -0,0 +1,55 @@
1
+ # 第三方 Agent 环境变量
2
+
3
+ 本协议仅适用于外部 Agent 通过公开 CLI 配置 FaaS 环境变量。满足下述进入条件后,按[工程与环境](workspace.md)确认 CLI 入口和同一账号的 profile,再通过 `environment-variables --help` 核对子命令能力。旧版本缺少能力时报告版本限制,不调用底层 API 绕过。
4
+
5
+ ## 进入条件
6
+
7
+ 先检查 FaaS 源码与用户需求。仅在存在自定义环境变量引用,或用户明确要求查询、配置环境变量时进入本协议。两者都没有时,跳过环境变量命令与配置询问,按[发布与交付](release.md)继续;不要先调用 `list` 来判断是否需要环境变量。宿主、构建工具的环境变量和注释、字符串中的示例不构成 FaaS 配置需求。
8
+
9
+ 存在引用时,即使使用了代码回退值,也仍按本协议核对;接口 404 或查询失败不能当作“没有变量”而跳过。
10
+
11
+ ## 运行配置与边界
12
+
13
+ 环境变量是 Base Database FaaS 的运行配置,不是应用的业务数据或页面功能。值只注入发布后的 FaaS runtime,不注入 Agent 进程、Page 或构建阶段的环境。FaaS 仅用静态 `process.env.NAME` 或 `process.env['NAME']` 读取;CLI 按语法扫描 `server-functions/functions/`,忽略注释和字符串示例,动态下标、解构或无法静态确定名称的访问会阻止发布。
14
+
15
+ - `config`:非敏感配置,查询时明文返回。
16
+ - `secret`:API Key、Token 等敏感值,保存后只返回脱敏值。
17
+
18
+ 定义由 Preview 和 Prod 共享,两套环境的值独立配置。保存不发布;发布冻结目标环境的值并注入 runtime,旧运行版本保留原快照。不得将值写入源码、`base-database.jsonc`、`.env`、日志或其他工程文件,也不得用于 Page build。
19
+
20
+ Page 不得实现环境变量管理页、配置清单、变量调试页,或展示变量名、类型、Config 原值、Secret 原文或脱敏值。配置交互由第三方 Agent 的普通聊天和 CLI 承载。业务需要反馈第三方服务是否可用时,仅展示业务语义的派生状态;FaaS 只返回业务结果或最小派生状态,不返回任何 Secret 值。
21
+
22
+ ## 收集、配置与首次 Preview
23
+
24
+ 环境变量的 Value 属于用户配置,不属于 Agent 可生成的工程内容。只有用户在当前或此前聊天消息中明确给出的具体值,才允许保存;变量名、描述、页面期望文案、示例值和占位符不代表真实 Value。禁止生成、推断或代填任何测试值、默认值、随机值或看似可用的 API Key。
25
+
26
+ 满足上述进入条件的首次 Preview 使用以下流程;等待期间保留当前工程与会话,收到回复后从配置步骤继续,不重新 init 或 pull:
27
+
28
+ 1. 首次执行 `deploy` 前,一次性收集 FaaS 静态引用和用户明确要求的全部定义,确定名称、类型、客观描述及必填性。不得把 `deploy` 作为环境变量发现机制;尚不明确的信息只询问缺失项。
29
+ 2. 执行一次 `environment-variables list --target preview`,同名同类型时复用现有定义,保留当前描述和必填性,只声明缺失定义;不因 Agent 新生成的描述而覆盖已有元数据,不自动删除未引用变量。
30
+ 3. 保存用户已明确提供、且变量名与 Preview 目标对应关系明确的值。仍有缺失时一次性询问全部缺失必填变量的名称、类型、描述与对应值,并结束当前 Turn;不执行 `deploy`,不声称已经配置。
31
+ 4. 收到回复后保存明确对应的值,并执行一次 Preview list 核对。仍缺少值时只询问剩余项;全部必填值完整时自动继续首次 Preview 发布,不再要求用户回复“配置完成”或“继续”。
32
+ 5. 环境变量校验已通过但部署失败时,报告脱敏错误与可用 LogID,不重复询问值,不绕过 CLI 调用 `source_code`、`artifact` 或 `publish`。已取得 publish ID 的部署按[发布任务查询](verification.md#发布任务查询)恢复观察,不重复发起发布。
33
+ 6. Preview 成功后按[发布与交付](release.md)完成示例数据和通知收尾,再交付顶层 `baseAppUrl`、配置状态与业务摘要。普通交付不展示环境变量命令或内部排障过程。
34
+
35
+ ## CLI 写入与核对
36
+
37
+ 沿用已核实的本机 `base-database-cli`,无可用入口时按[工程与环境](workspace.md#公开-cli-版本预检)使用全限定包名回退;仅替换命令前缀,实际业务参数保持一致:
38
+
39
+ ```bash
40
+ base-database-cli environment-variables declare MAP_API_KEY \
41
+ --target preview \
42
+ --type secret \
43
+ --description '地图服务 API Key' \
44
+ --required
45
+
46
+ base-database-cli environment-variables set MAP_API_KEY \
47
+ --target preview \
48
+ --value-stdin
49
+ ```
50
+
51
+ 调用 `declare` 显式指定 `--target`、`--type`、`--description`,并在 `--required` 与 `--optional` 中选择一个;`set`、`clear` 显式指定目标环境。用户可以在普通聊天中提供 Config 或 Secret,必须通过 `--value-stdin` 的标准输入传入;使用执行工具的独立 stdin 输入通道,不把值嵌入命令文本、进程参数、临时文件或 shell 历史。CLI 在内存中通过 `lark-cli api --data -` 发送 JSON。当前执行工具没有独立 stdin 通道时说明限制,等待可用的安全输入通道,不用含值的 here-document 或 `echo` 命令替代。不得在回复、命令结果或后续消息中复述完整 Secret。
52
+
53
+ `declare` 只管理共享定义。同名元数据不同默认拒绝覆盖;只有用户明确要求修改已有描述或必填性时才更新,并先确认授权覆盖 Preview 与 Prod 的共同影响,再使用 `--force`。删除、重命名或类型变更前,说明对两套环境的定义与值的影响,并取得明确确认;类型变化会清空两套值,删除定义也会删除两套值,重命名不得擅自删除旧定义或代填新值。当前 CLI 不提供删除或重命名命令,缺少可用入口时报告限制。`set` 只更新目标环境已有定义的值;`clear` 清空该值但保留定义,两者都不触发发布。保存后用同一入口的 `environment-variables list --target <preview|prod>` 核对,Config 返回明文、Secret 返回脱敏值。
54
+
55
+ 普通更新保存后说明需要重新发布,按用户已有 Preview 发布授权执行;未授权时等待。Prod 值独立配置,Prod 仍发布本次已验证 Preview 制品,要求用户体验并明确授权,见[主流程授权约定](../SKILL.md#外部操作与授权)。不删除引用、硬编码值或将 Secret 改为 Config 绕过校验。
@@ -1,6 +1,6 @@
1
1
  # FaaS 与记录协议
2
2
 
3
- 实现服务端业务写入或诊断下游调用时读取。Page 调用方式见[Page 开发](development.md),查询类型与参数见[SQL Function](sql-function.md),权限判定见[权限设计](permission-design.md)。
3
+ 实现、维护或排查 FaaS 时读取;新写入的选型见[写入方式选择](mutations.md#写入方式选择)。Page 调用方式见[Page 开发](development.md),查询类型与参数见[Database Function](database-function.md),权限判定见[权限设计](permission-design.md)。
4
4
 
5
5
  ## 阅读路径
6
6
 
@@ -13,7 +13,9 @@
13
13
 
14
14
  ## FaaS 开发
15
15
 
16
- 每个由 Page 主动发起的写入 operation 的目录:
16
+ 第三方 Agent 配置 FaaS 的凭证或环境相关配置时,先读取[环境变量协议](environment-variables.md)。值仅由 FaaS 静态 `process.env.NAME` 或 `process.env['NAME']` 读取,不进入 Page、源码、配置、日志或函数返回值。动态 `process.env[key]`、解构和其他无法静态确定变量名的访问会阻止部署。
17
+
18
+ 每个 FaaS 业务动作的目录:
17
19
 
18
20
  ```text
19
21
  server-functions/functions/<functionId>/
@@ -38,13 +40,13 @@ server-functions/functions/<functionId>/
38
40
  }
39
41
  ```
40
42
 
41
- `inputSchema` 是 Agent 调用该 Function 时 `params` 业务值的结构,不包含外层 `{"params": ...}`;FaaS Function 必须显式声明。CLI 会校验并上传该 Schema,发布后的能力发现接口据此返回可调用参数。只使用 JSON Schema 2020-12 的受限子集:`type`、`description`、`properties`、`required`、`additionalProperties`、`items`、`prefixItems`、`minItems`、`maxItems`、`enum`、`minimum`、`maximum`、`minLength`、`maxLength`;不使用 `$ref`、组合关键字、`pattern`、`format` 或未知关键字。Schema 最大 32 KiB、嵌套不超过 16 层、节点不超过 256 个。
43
+ `inputSchema` 是调用该 Function 时 `params` 业务值的结构,不包含外层 `{"params": ...}`;FaaS Function 必须显式声明。CLI 会校验并上传该 Schema。只使用 JSON Schema 2020-12 的受限子集:`type`、`description`、`properties`、`required`、`additionalProperties`、`items`、`prefixItems`、`minItems`、`maxItems`、`enum`、`minimum`、`maximum`、`minLength`、`maxLength`;不使用 `$ref`、组合关键字、`pattern`、`format` 或未知关键字。Schema 最大 32 KiB、嵌套不超过 16 层、节点不超过 256 个。
42
44
 
43
- SQL Function 的 `inputSchema` 不手写,由 CLI 根据 `meta.params` 的顺序和 `kind` 确定性生成固定长度数组 Schema;`TIMESTAMP` 与 `DURATION` 均明确为毫秒整数。
45
+ Database Function 的 `inputSchema` 不手写,由 CLI 根据 `meta.params` 的顺序和 `kind` 确定性生成固定长度数组 Schema;参数类型见 [查询参数协议](database-function.md#function-文件与参数)。
44
46
 
45
- 上传时 CLI 把 Schema 对象放入 `sourceCode` JSON 字符串:SQL 为 `{sql,meta,inputSchema}`,FaaS 为 `{inputSchema}`,不发送独立的 Function `inputSchema` 字段。FaaS 的可执行代码仍在源码包,`meta.json` 的写法不变;发现接口仍独立返回 `inputSchema` JSON 字符串。
47
+ FaaS 在 `meta.json` 中声明 `inputSchema`,在 `index.ts` 中实现 handler。部署时,CLI 自动校验并上传 Schema 和应用源码。
46
48
 
47
- loader 读取 HTTP `req.body.params`,把这个业务对象作为 handler 第一个参数 `rawParams`;第二个参数是 runtime context:
49
+ handler 第一个参数 `rawParams` 是调用方的 `params` 业务对象;第二个参数是 runtime context:
48
50
 
49
51
  ```ts
50
52
  interface FunctionContext {
@@ -90,27 +92,15 @@ function parseCreateTask(value: unknown): CreateTaskInput {
90
92
 
91
93
  ### 远程调用与写入链路设计
92
94
 
93
- 实现前先按设计结果确认一致性、部分成功、重试语义和远程调用依赖。以一次 Page/FaaS operation 为边界列出 SQL Function、`context.fetch` 和写入 OpenAPI;标记哪些输入在 operation 开始时已经确定、哪些必须等待前一步响应。默认使用满足业务语义的最少远程等待阶段:
95
+ 每个写入 operation 先确认成功条件、一致性要求、是否允许部分成功和重试边界,再选择调用方式:
94
96
 
97
+ - 需要事务、唯一约束、条件更新或原子 compare-and-set 才能保证正确性的 operation,在当前 Base 能力下标记为不支持,不使用多次远程请求模拟。只有用户明确同意改变后端架构时才采用其他存储;交付按[完成条件](../SKILL.md#完成条件)报告。
98
+ - 在满足权限、业务条件及一致性要求后,并发执行输入已确定且互不依赖的调用;后一步依赖前一步响应时串行。
95
99
  - 同表多条创建、更新或删除合并到一个 batch 请求,单批不超过 200 条。
96
- - 同一 operation 调用多个 SQL Function 或 FaaS Function 时,输入已确定、互不依赖,且并发不改变一致性或部分成功语义的调用尽量并发;只有真实响应依赖才串行。
97
- - 多个无副作用读取或请求的参数已经确定且互不依赖时,先启动全部 Promise,再在共同消费结果处等待;存在真实响应依赖时才串行。
98
100
  - 原始 Promise 仍可能 reject 且需要知道每一路结果时使用 `Promise.allSettled`;helper 已把异常归一化为结构化结果时可以使用 `Promise.all`。
99
- - 写前 SQL 只用于服务端必须确认的权限、关键状态或其他可信数据;写后 SQL 回读用于验收或明确要求的业务校验,不作为每次生产写入的默认步骤。
100
- - 页面状态仅可用于允许弱一致性的非安全敏感计算。用户身份、权限、租户隔离、支付、库存、额度等约束不得信任页面传值。
101
- - 多路写入返回值区分全部成功、部分成功和全部失败,并携带已取得的 record ID 与安全错误信息。非幂等创建结果未知时不自动整体重试。
102
- - 需要事务、唯一约束、条件更新或原子 compare-and-set 才能保证正确性的 operation,在当前 Base 能力下标记为不支持,不使用多次远程请求模拟。
103
-
104
- 无依赖的多路写入可以采用:
105
-
106
- ```ts
107
- const writes = await Promise.allSettled([
108
- updatePrimaryRecord(context, input),
109
- createAuditRecord(context, input),
110
- ])
111
-
112
- return summarizeWrites(writes)
113
- ```
101
+ - 写前调用 Database Function 只用于服务端必须确认的权限、关键状态或其他可信数据;写后 Database Function 回读用于验收或明确要求的业务校验,不作为每次生产写入的默认步骤。
102
+ - 用户身份、权限、租户隔离、支付、库存、额度等关键约束必须由服务端可信数据确认,不得信任页面传值。
103
+ - 多路写入返回值区分全部成功、部分成功和全部失败,并携带已取得的 record ID 与安全错误信息。非幂等创建结果未知时不自动整体重试;只有底层存在可靠幂等原语时才承诺严格幂等。
114
104
 
115
105
  性能敏感的 operation 记录总耗时和各远程调用耗时;只使用成功业务请求评价写入性能,鉴权失败、参数失败和平台凭证缺失只用于诊断。日志和返回值不得包含 token、cookie 或请求凭证。
116
106
 
@@ -183,8 +173,7 @@ POST /open-apis/bitable/v1/apps/:base_token/tables/:table_id/remove_attachments
183
173
  ```
184
174
 
185
175
  后两条是附件字段专用的增量路由(注意不在 `records/` 段下):请求只提交本次增删的
186
- `file_token`,服务端读取、合并后整格提交,保留读到的其它附件及宽高;不保证原子性或去重。目标环境必须已部署对应 FaaS runtime 和服务端能力,不能仅凭 Skill 存在就判定可用。什么时候必须用它们、什么时候仍可整格写,
187
- 见[文件传输](file-transfer.md)的「写单元格」。
176
+ `file_token`,服务端读取、合并后整格提交,保留读到的其它附件及宽高;不保证原子性或去重。目标环境必须已部署对应 FaaS runtime 和服务端能力,不能仅凭 Skill 存在就判定可用。何时使用增量路由或整格写入,见[文件传输](file-transfer.md#写单元格)。Mutation 的声明、参数和返回结构见[附件写入](mutations.md#附件写入);这里的原始路由响应不能直接套用于 Page 调用 Mutation。
188
177
 
189
178
  路径首个参数是 **Base token,不是 应用 appToken**:取 `catalog.json` 中该表的
190
179
  `tables[].baseToken`,与 table ID 一起作为 Function 内的工程常量。应用 appToken 只用于
@@ -202,8 +191,9 @@ POST /open-apis/bitable/v1/apps/:base_token/tables/:table_id/remove_attachments
202
191
 
203
192
  五个请求体都是 strict JSON Schema:不接受表格中未列出的额外顶层字段。「每批 1~200 条
204
193
  记录」只适用于前三条 batch 路由;附件增量路由的限制是**一次操作的 `file_token` 总数不超过
205
- 50**;附件映射及每个 record-field 数组没有非空 Schema 约束。record ID 必须匹配
206
- `^rec[A-Za-z0-9]{1,11}$`;删除列表不允许重复,
194
+ 50**;附件映射及每个 record-field 数组没有非空 Schema 约束。record ID 必须以 `rec` 开头、
195
+ 后续为大小写字母和数字,用 `^rec[A-Za-z0-9]{1,32}$` 校验即可(实际返回如
196
+ `reczz2893mUCkwnr` 长度会超过旧的 `{1,11}` 上限,不要在业务层依赖固定短长度);删除列表不允许重复,
207
197
  更新映射的 key 自然唯一。`batch_update` 使用 `update_records` 为不同记录分别提交
208
198
  patch;不要混用 MCP 工具层 `patch_records_batch` 的 `record_id_list + patch` 请求结构。
209
199
 
@@ -225,7 +215,7 @@ patch;不要混用 MCP 工具层 `patch_records_batch` 的 `record_id_list + p
225
215
  }
226
216
  ```
227
217
 
228
- Node MCP 直接响应中的 `meta` 不会经过当前 FaaS Entry 的合成响应返回,FaaS 不得依赖该字段。
218
+ 这些路由的响应不包含 `meta` 字段,FaaS 不得依赖它。
229
219
 
230
220
  `batch_create` 和 `batch_update` 的 `data` 还可能包含被忽略的只读或不支持字段;未忽略任何字段时
231
221
  `ignored_fields` 不存在:
@@ -243,13 +233,15 @@ Node MCP 直接响应中的 `meta` 不会经过当前 FaaS Entry 的合成响应
243
233
  }
244
234
  ```
245
235
 
246
- 附件增量路由的成功响应没有 `record_id_list`:`data.attachments` 按记录、字段回显
236
+ 附件增量路由的成功响应没有 `record_id_list`:`data.attachments` 按记录 ID、字段 ID 回显
247
237
  **本次操作合并出的附件列表**(`file_token` / `name` / `size`),页面用它更新本地状态;
248
- 它不是包含全部并发写入的最终快照,不要立即用 SQL 回读替换它(从库可能尚未同步)。超时或结果未知时保留已上传 token,按[文件传输](file-transfer.md#超时与结果未知)有界核对;append 不幂等,不能自动重提或重新上传。字段设置了「仅限手机拍摄」时,
238
+ 即使请求使用字段名,响应也用字段 ID,固定按 `data.attachments[recordId][fieldId]` 读取,
239
+ 其中 `fieldId` 来自 Catalog。不要按请求中的字段名取值,否则写入成功后可能被误判为结果未知。
240
+ 它不是包含全部并发写入的最终快照,不要立即用 Database Function 回读替换它(查询结果可能尚未同步)。超时或结果未知时保留已上传 token,按[文件传输](file-transfer.md#超时与结果未知)有界核对;append 不幂等,不能自动重提或重新上传。字段设置了「仅限手机拍摄」时,
249
241
  `append_attachments` 对该字段的追加被忽略并进入 `ignored_fields`;`remove_attachments`
250
242
  不受该限制,仍可移除。校验纪律同 batch:只认约定字段,不递归找。
251
243
 
252
- runtime 识别后返回的是合成响应,因此这条链路上:
244
+ 这些路由的响应规则:
253
245
 
254
246
  - HTTP 状态恒为 `200`,业务成败只看 body 的 `code`,不能只判 `response.ok`;
255
247
  - 响应头没有 `x-tt-logid`,`DownstreamError.logId` 必然为空。排查改用 invoke 响应
@@ -265,7 +257,7 @@ runtime 识别后返回的是合成响应,因此这条链路上:
265
257
  - `batch_update`:`record_id_list` 是实际存在的记录,允许为空;它与 `record_not_found` 必须是请求 key 的不重叠、无遗漏分组。
266
258
  - `batch_delete`:`record_id_list` 必须与请求列表一致,`record_not_found` 必须是其子集;实际删除的 ID 是两者的差集。
267
259
 
268
- `code === 0` 且 `record_not_found` 非空是 Node MCP batch 的合法部分成功,不是 `invalid_response`。FaaS handler 必须在
260
+ `code === 0` 且 `record_not_found` 非空是 批量写入的合法部分成功,不是 `invalid_response`。FaaS handler 必须在
269
261
  业务结果中保留已完成和未找到的 ID;需要向 Page 表达部分成功时,使用当前 operation 的稳定业务码,
270
262
  但不得伪装成下游失败或整体重试。只有响应缺失、类型不符或不满足上述 operation 约束时才按
271
263
  `invalid_response` 处理;非幂等创建不得自动重试。
@@ -301,7 +293,7 @@ function parseSiteRecordsWriteData(
301
293
  return undefined
302
294
  }
303
295
  const data = envelope.data as Record<string, unknown>
304
- const recordIdPattern = /^rec[A-Za-z0-9]{1,11}$/
296
+ const recordIdPattern = /^rec[A-Za-z0-9]{1,32}$/
305
297
  const parseRecordIDs = (input: unknown): string[] | undefined => {
306
298
  if (
307
299
  !Array.isArray(input) ||
@@ -484,7 +476,7 @@ FaaS 写入 Base 时,必须调用目标环境的
484
476
  `.../append_attachments`、`.../remove_attachments`(附件增量)路由;
485
477
  `:base_token` 取 `catalog.json` 的 `tables[].baseToken`,并检查 HTTP 状态和业务码。
486
478
 
487
- 需要读 Base 时调用固定 SQL Function:
479
+ 需要读 Base 时调用固定 Database Function:
488
480
 
489
481
  ```ts
490
482
  const result: unknown = await context.invokeFunction({
@@ -494,28 +486,29 @@ const result: unknown = await context.invokeFunction({
494
486
  })
495
487
  ```
496
488
 
497
- 确需写前查重、权限范围、状态检查或写后查询时复用固定 SQL Function;可以在一次查询中完成的检查不拆成多个串行调用。前一个 SQL 返回的关联 ID 仅用于定位后一个 SQL 的记录时,优先把关联查询下推为一个 SQL Function。
489
+ 确需写前查重、权限范围、状态检查或写后查询时复用固定 Database Function;可以在一次查询中完成的检查不拆成多个串行调用。前一次查询返回的关联 ID 仅用于定位后一次查询的记录时,优先把关联查询下推为一个 Database Function。
498
490
 
499
491
  ## Base 读写协议
500
492
 
501
- 同一个字段在创建 schema、SQL 读取和 FaaS 应用记录 写入时使用三套不同但相关的数据结构。下表只适用于 FaaS 固定 batch 与附件增量路由;开发期 lark-cli 示例数据使用[发布手册中的独立输入契约](release.md#从零建站示例数据验收)。
493
+ 同一个字段在创建 schema、Database Function 读取和 FaaS 应用记录 写入时使用三套不同但相关的数据结构。下表只适用于 FaaS 固定 batch 与附件增量路由;开发期 lark-cli 示例数据使用[发布手册中的独立输入契约](release.md#从零建站示例数据验收)。
502
494
 
503
- | Field JSON | SQL 读取 | FaaS 应用记录 CellValue |
495
+ | Field JSON | Database Function 读取 | FaaS 应用记录 CellValue |
504
496
  |---|---|---|
505
497
  | `text` | `TEXT` string | string |
506
498
  | `number` | `DOUBLE` number | finite number |
507
- | 单选 `select` | `OPTION` string | 合法选项名 string |
508
- | 多选 `select` | `COLLECTION<OPTION>` | 合法选项名 string[] |
509
- | `datetime` | `TIMESTAMP` 毫秒 number | `YYYY-MM-DD HH:mm:ss` |
499
+ | 单选 `select` | `OPTION` `{id,name}` | 合法选项名 string |
500
+ | 多选 `select` | `ARRAY<OPTION>`,元素为 `{id,name}` | 合法选项名 string[] |
501
+ | `datetime` | `TIMESTAMP` 字符串 `YYYY-MM-DD HH:mm:ss.SSS` | `YYYY-MM-DD HH:mm:ss` 或带毫秒格式 |
510
502
  | `checkbox` | `BOOL` boolean | boolean |
511
- | 单/多 `user` | `USER` / `COLLECTION<USER>` | `[{"id":"6882577547121393684"}]`;`id` 是内部 `user_id` |
512
- | 单/多 `group_chat` | `GROUP` / `COLLECTION<GROUP>` | `[{"id":"oc_xxx"}]` |
513
- | `link` | `COLLECTION<RECORD_ID>` | `[{"id":"rec_xxx"}]` |
514
- | `attachment` | `COLLECTION<FILE>` | `[{"file_token":"...","image_width":number,"image_height":number}]`;追加/移除走附件增量路由,`batch_update` 写该字段是整格覆盖、会丢已有图片宽高,仅用于置空或明确重设。见[文件传输](file-transfer.md) |
503
+ | 单/多 `user` | `USER` / `ARRAY<USER>` | `[{"id":"<User.user_id>"}]`;从 starter 人员选择器取得 `User.user_id` |
504
+ | 单/多 `group_chat` | `CHAT` / `ARRAY<CHAT>` | `[{"id":"oc_xxx"}]` |
505
+ | 单/多 `link` | `TEXT` / `ARRAY<TEXT>`,以 Catalog `sqlType` 为准 | 都使用 record 对象数组:单值最多一个元素,多值可多个,例如 `[{"id":"rec_xxx"}]` |
506
+ | `attachment` | `ARRAY<FILE>` | `[{"file_token":"...","image_width":number,"image_height":number}]`;追加/移除走附件增量路由,`batch_update` 写该字段是整格覆盖、会丢已有图片宽高,仅用于置空或明确重设。见[文件传输](file-transfer.md) |
515
507
  | `location` | `UNSUPPORTED` | `{"lng":number,"lat":number}` |
508
+ | `auto_number` | `AUTONUMBER` `{sequence,number}` | 只读 |
516
509
  | 公式、lookup、系统字段 | 推导类型或 `UNSUPPORTED` | 只读 |
517
510
 
518
- 写入时 `null` 清空字段,`[]` 表示空集合,省略字段表示不修改。选项名、用户/群 ID 和关联 record ID 必须来自真实数据。
511
+ 写入时 `null` 清空字段,`[]` 表示空集合,省略字段表示不修改。选项名、用户/群 ID 和关联 record ID 必须来自真实数据。日期与时间的转换见[日期与时区](development.md#日期与时区)。
519
512
 
520
513
  应用 行写入只使用 [应用记录 写入协议](#应用记录-写入协议)中的固定路由。
521
514
 
@@ -556,8 +549,8 @@ const result: unknown = await context.invokeFunction({
556
549
  `data.record_id_list`,附件增量路由校验 `data.attachments`;需要后续写入、关联或回读时,
557
550
  只使用约定字段中的记录 ID。
558
551
 
559
- batch 路由单批最多 200 条。多选、多人员、关联这些集合字段是**整格覆盖**:追加或移除必须先读现有值、合并出完整列表再整格写回,直接写会丢掉单元格里已有的项。**附件字段是唯一的例外**:追加/移除走上面的附件增量路由,禁止用读-合并-整格写回实现——服务端不会补齐省略的宽高,而 SQL 读不回旧图宽高,基于 SQL 列表的重写无法重建宽高;并发时整格写回还会互相覆盖。见[文件传输](file-transfer.md)的「写单元格」。
552
+ batch 路由单批最多 200 条。多选、多人员和多值关联这些集合字段是**整格覆盖**:追加或移除必须先读现有值、合并出完整列表再整格写回,直接写会丢掉单元格里已有的项。单值关联同样用对象数组写入,但最多一个元素,替换时提交新的单元素数组,清空时提交 `null`。**附件字段是唯一的例外**:追加/移除走上面的附件增量路由,禁止用读-合并-整格写回实现——服务端不会补齐省略的宽高,而 Database Function 读不回旧图宽高,基于 Database Function 返回列表的重写无法重建宽高;并发时整格写回还会互相覆盖。见[文件传输](file-transfer.md)的「写单元格」。
560
553
 
561
- **整格覆盖的合并基准必须是可信的完整列表。** SQL 回读走从库,刚写完就回读可能拿到写入前的旧值;拿缺项的列表做基准整格写回,会静默删掉其它项,而写入仍返回成功。读取失败、从未读到过、或回读缺少本次刚写入的项时,禁掉入口让用户稍后重试,不要写。
554
+ **整格覆盖的合并基准必须是可信的完整列表。** Database Function 回读走从库,刚写完就回读可能拿到写入前的旧值;拿缺项的列表做基准整格写回,会静默删掉其它项,而写入仍返回成功。读取失败、从未读到过、或回读缺少本次刚写入的项时,禁掉入口让用户稍后重试,不要写。
562
555
 
563
556
  页面内可串行处理同一记录字段的更新,但不能因此承诺跨访问者原子性。文件字节的上传与展示是另外的路,见[文件传输](file-transfer.md)。