@larksuite/openclaw-lark 2025.7.15-beta.0

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 (426) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/bin/openclaw-lark.js +39 -0
  4. package/index.d.ts +36 -0
  5. package/index.js +188 -0
  6. package/openclaw.plugin.json +64 -0
  7. package/package.json +63 -0
  8. package/secret-contract-api.d.ts +15 -0
  9. package/secret-contract-api.js +78 -0
  10. package/skills/feishu-bitable/SKILL.md +248 -0
  11. package/skills/feishu-bitable/references/examples.md +813 -0
  12. package/skills/feishu-bitable/references/field-properties.md +763 -0
  13. package/skills/feishu-bitable/references/record-values.md +911 -0
  14. package/skills/feishu-calendar/SKILL.md +242 -0
  15. package/skills/feishu-channel-rules/SKILL.md +18 -0
  16. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  17. package/skills/feishu-create-doc/SKILL.md +719 -0
  18. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  19. package/skills/feishu-im-read/SKILL.md +163 -0
  20. package/skills/feishu-task/SKILL.md +340 -0
  21. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  22. package/skills/feishu-update-doc/SKILL.md +285 -0
  23. package/src/card/builder.d.ts +152 -0
  24. package/src/card/builder.js +794 -0
  25. package/src/card/card-error.d.ts +91 -0
  26. package/src/card/card-error.js +206 -0
  27. package/src/card/cardkit.d.ts +90 -0
  28. package/src/card/cardkit.js +203 -0
  29. package/src/card/flush-controller.d.ts +45 -0
  30. package/src/card/flush-controller.js +138 -0
  31. package/src/card/image-resolver.d.ts +45 -0
  32. package/src/card/image-resolver.js +116 -0
  33. package/src/card/markdown-style.d.ts +16 -0
  34. package/src/card/markdown-style.js +106 -0
  35. package/src/card/reasoning-utils.d.ts +14 -0
  36. package/src/card/reasoning-utils.js +64 -0
  37. package/src/card/reply-dispatcher-types.d.ts +132 -0
  38. package/src/card/reply-dispatcher-types.js +61 -0
  39. package/src/card/reply-dispatcher.d.ts +15 -0
  40. package/src/card/reply-dispatcher.js +447 -0
  41. package/src/card/reply-mode.d.ts +41 -0
  42. package/src/card/reply-mode.js +70 -0
  43. package/src/card/streaming-card-controller.d.ts +118 -0
  44. package/src/card/streaming-card-controller.js +1045 -0
  45. package/src/card/tool-use-config.d.ts +26 -0
  46. package/src/card/tool-use-config.js +76 -0
  47. package/src/card/tool-use-display.d.ts +37 -0
  48. package/src/card/tool-use-display.js +476 -0
  49. package/src/card/tool-use-trace-store.d.ts +51 -0
  50. package/src/card/tool-use-trace-store.js +271 -0
  51. package/src/card/unavailable-guard.d.ts +35 -0
  52. package/src/card/unavailable-guard.js +87 -0
  53. package/src/channel/abort-detect.d.ts +47 -0
  54. package/src/channel/abort-detect.js +219 -0
  55. package/src/channel/chat-queue.d.ts +41 -0
  56. package/src/channel/chat-queue.js +68 -0
  57. package/src/channel/config-adapter.d.ts +23 -0
  58. package/src/channel/config-adapter.js +107 -0
  59. package/src/channel/directory.d.ts +57 -0
  60. package/src/channel/directory.js +197 -0
  61. package/src/channel/event-handlers.d.ts +17 -0
  62. package/src/channel/event-handlers.js +380 -0
  63. package/src/channel/interactive-dispatch.d.ts +59 -0
  64. package/src/channel/interactive-dispatch.js +195 -0
  65. package/src/channel/monitor.d.ts +17 -0
  66. package/src/channel/monitor.js +140 -0
  67. package/src/channel/onboarding-config.d.ts +18 -0
  68. package/src/channel/onboarding-config.js +96 -0
  69. package/src/channel/onboarding-migrate.d.ts +25 -0
  70. package/src/channel/onboarding-migrate.js +70 -0
  71. package/src/channel/onboarding.d.ts +12 -0
  72. package/src/channel/onboarding.js +300 -0
  73. package/src/channel/plugin.d.ts +13 -0
  74. package/src/channel/plugin.js +310 -0
  75. package/src/channel/probe.d.ts +14 -0
  76. package/src/channel/probe.js +24 -0
  77. package/src/channel/types.d.ts +37 -0
  78. package/src/channel/types.js +8 -0
  79. package/src/commands/auth.d.ts +21 -0
  80. package/src/commands/auth.js +165 -0
  81. package/src/commands/diagnose.d.ts +69 -0
  82. package/src/commands/diagnose.js +848 -0
  83. package/src/commands/doctor.d.ts +27 -0
  84. package/src/commands/doctor.js +588 -0
  85. package/src/commands/index.d.ts +25 -0
  86. package/src/commands/index.js +219 -0
  87. package/src/commands/locale.d.ts +7 -0
  88. package/src/commands/locale.js +8 -0
  89. package/src/core/accounts.d.ts +51 -0
  90. package/src/core/accounts.js +219 -0
  91. package/src/core/agent-config.d.ts +100 -0
  92. package/src/core/agent-config.js +148 -0
  93. package/src/core/api-error.d.ts +48 -0
  94. package/src/core/api-error.js +117 -0
  95. package/src/core/app-owner-fallback.d.ts +22 -0
  96. package/src/core/app-owner-fallback.js +39 -0
  97. package/src/core/app-scope-checker.d.ts +87 -0
  98. package/src/core/app-scope-checker.js +198 -0
  99. package/src/core/auth-errors.d.ts +144 -0
  100. package/src/core/auth-errors.js +160 -0
  101. package/src/core/card-action-operator.d.ts +33 -0
  102. package/src/core/card-action-operator.js +30 -0
  103. package/src/core/chat-info-cache.d.ts +67 -0
  104. package/src/core/chat-info-cache.js +165 -0
  105. package/src/core/comment-target.d.ts +65 -0
  106. package/src/core/comment-target.js +100 -0
  107. package/src/core/config-schema.d.ts +490 -0
  108. package/src/core/config-schema.js +223 -0
  109. package/src/core/device-flow.d.ts +77 -0
  110. package/src/core/device-flow.js +217 -0
  111. package/src/core/domains.d.ts +18 -0
  112. package/src/core/domains.js +34 -0
  113. package/src/core/feishu-fetch.d.ts +18 -0
  114. package/src/core/feishu-fetch.js +28 -0
  115. package/src/core/footer-config.d.ts +24 -0
  116. package/src/core/footer-config.js +51 -0
  117. package/src/core/lark-client.d.ts +125 -0
  118. package/src/core/lark-client.js +468 -0
  119. package/src/core/lark-logger.d.ts +23 -0
  120. package/src/core/lark-logger.js +160 -0
  121. package/src/core/lark-ticket.d.ts +29 -0
  122. package/src/core/lark-ticket.js +40 -0
  123. package/src/core/message-unavailable.d.ts +53 -0
  124. package/src/core/message-unavailable.js +142 -0
  125. package/src/core/owner-policy.d.ts +32 -0
  126. package/src/core/owner-policy.js +55 -0
  127. package/src/core/permission-url.d.ts +22 -0
  128. package/src/core/permission-url.js +76 -0
  129. package/src/core/raw-request.d.ts +27 -0
  130. package/src/core/raw-request.js +90 -0
  131. package/src/core/runtime-store.d.ts +13 -0
  132. package/src/core/runtime-store.js +29 -0
  133. package/src/core/scope-manager.d.ts +168 -0
  134. package/src/core/scope-manager.js +224 -0
  135. package/src/core/sdk-compat.d.ts +20 -0
  136. package/src/core/sdk-compat.js +28 -0
  137. package/src/core/security-check.d.ts +72 -0
  138. package/src/core/security-check.js +184 -0
  139. package/src/core/shutdown-hooks.d.ts +22 -0
  140. package/src/core/shutdown-hooks.js +61 -0
  141. package/src/core/synthetic-target.d.ts +33 -0
  142. package/src/core/synthetic-target.js +40 -0
  143. package/src/core/targets.d.ts +60 -0
  144. package/src/core/targets.js +173 -0
  145. package/src/core/token-store.d.ts +54 -0
  146. package/src/core/token-store.js +326 -0
  147. package/src/core/tool-client.d.ts +176 -0
  148. package/src/core/tool-client.js +426 -0
  149. package/src/core/tool-scopes.d.ts +156 -0
  150. package/src/core/tool-scopes.js +344 -0
  151. package/src/core/tools-config.d.ts +55 -0
  152. package/src/core/tools-config.js +143 -0
  153. package/src/core/types.d.ts +91 -0
  154. package/src/core/types.js +12 -0
  155. package/src/core/uat-client.d.ts +46 -0
  156. package/src/core/uat-client.js +192 -0
  157. package/src/core/version.d.ts +31 -0
  158. package/src/core/version.js +69 -0
  159. package/src/messaging/converters/audio.d.ts +8 -0
  160. package/src/messaging/converters/audio.js +24 -0
  161. package/src/messaging/converters/calendar.d.ts +13 -0
  162. package/src/messaging/converters/calendar.js +56 -0
  163. package/src/messaging/converters/content-converter-helpers.d.ts +30 -0
  164. package/src/messaging/converters/content-converter-helpers.js +82 -0
  165. package/src/messaging/converters/content-converter.d.ts +24 -0
  166. package/src/messaging/converters/content-converter.js +40 -0
  167. package/src/messaging/converters/file.d.ts +8 -0
  168. package/src/messaging/converters/file.js +24 -0
  169. package/src/messaging/converters/folder.d.ts +8 -0
  170. package/src/messaging/converters/folder.js +24 -0
  171. package/src/messaging/converters/hongbao.d.ts +8 -0
  172. package/src/messaging/converters/hongbao.js +20 -0
  173. package/src/messaging/converters/image.d.ts +8 -0
  174. package/src/messaging/converters/image.js +22 -0
  175. package/src/messaging/converters/index.d.ts +8 -0
  176. package/src/messaging/converters/index.js +53 -0
  177. package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
  178. package/src/messaging/converters/interactive/card-converter.js +1177 -0
  179. package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
  180. package/src/messaging/converters/interactive/card-utils.js +47 -0
  181. package/src/messaging/converters/interactive/index.d.ts +8 -0
  182. package/src/messaging/converters/interactive/index.js +25 -0
  183. package/src/messaging/converters/interactive/legacy.d.ts +11 -0
  184. package/src/messaging/converters/interactive/legacy.js +60 -0
  185. package/src/messaging/converters/interactive/types.d.ts +23 -0
  186. package/src/messaging/converters/interactive/types.js +27 -0
  187. package/src/messaging/converters/location.d.ts +8 -0
  188. package/src/messaging/converters/location.js +23 -0
  189. package/src/messaging/converters/merge-forward.d.ts +32 -0
  190. package/src/messaging/converters/merge-forward.js +235 -0
  191. package/src/messaging/converters/post.d.ts +11 -0
  192. package/src/messaging/converters/post.js +235 -0
  193. package/src/messaging/converters/share.d.ts +9 -0
  194. package/src/messaging/converters/share.js +28 -0
  195. package/src/messaging/converters/sticker.d.ts +8 -0
  196. package/src/messaging/converters/sticker.js +22 -0
  197. package/src/messaging/converters/system.d.ts +12 -0
  198. package/src/messaging/converters/system.js +36 -0
  199. package/src/messaging/converters/text.d.ts +8 -0
  200. package/src/messaging/converters/text.js +18 -0
  201. package/src/messaging/converters/todo.d.ts +8 -0
  202. package/src/messaging/converters/todo.js +45 -0
  203. package/src/messaging/converters/types.d.ts +114 -0
  204. package/src/messaging/converters/types.js +8 -0
  205. package/src/messaging/converters/unknown.d.ts +8 -0
  206. package/src/messaging/converters/unknown.js +20 -0
  207. package/src/messaging/converters/utils.d.ts +22 -0
  208. package/src/messaging/converters/utils.js +57 -0
  209. package/src/messaging/converters/video-chat.d.ts +8 -0
  210. package/src/messaging/converters/video-chat.js +31 -0
  211. package/src/messaging/converters/video.d.ts +8 -0
  212. package/src/messaging/converters/video.js +35 -0
  213. package/src/messaging/converters/vote.d.ts +8 -0
  214. package/src/messaging/converters/vote.js +28 -0
  215. package/src/messaging/inbound/bot-content.d.ts +84 -0
  216. package/src/messaging/inbound/bot-content.js +117 -0
  217. package/src/messaging/inbound/bot-loop-guard.d.ts +48 -0
  218. package/src/messaging/inbound/bot-loop-guard.js +89 -0
  219. package/src/messaging/inbound/comment-context.d.ts +82 -0
  220. package/src/messaging/inbound/comment-context.js +353 -0
  221. package/src/messaging/inbound/comment-handler.d.ts +30 -0
  222. package/src/messaging/inbound/comment-handler.js +269 -0
  223. package/src/messaging/inbound/dedup.d.ts +59 -0
  224. package/src/messaging/inbound/dedup.js +121 -0
  225. package/src/messaging/inbound/dispatch-builders.d.ts +104 -0
  226. package/src/messaging/inbound/dispatch-builders.js +241 -0
  227. package/src/messaging/inbound/dispatch-commands.d.ts +22 -0
  228. package/src/messaging/inbound/dispatch-commands.js +131 -0
  229. package/src/messaging/inbound/dispatch-context.d.ts +67 -0
  230. package/src/messaging/inbound/dispatch-context.js +153 -0
  231. package/src/messaging/inbound/dispatch.d.ts +50 -0
  232. package/src/messaging/inbound/dispatch.js +477 -0
  233. package/src/messaging/inbound/enrich.d.ts +102 -0
  234. package/src/messaging/inbound/enrich.js +236 -0
  235. package/src/messaging/inbound/gate-effects.d.ts +23 -0
  236. package/src/messaging/inbound/gate-effects.js +46 -0
  237. package/src/messaging/inbound/gate.d.ts +91 -0
  238. package/src/messaging/inbound/gate.js +342 -0
  239. package/src/messaging/inbound/handler-registry.d.ts +25 -0
  240. package/src/messaging/inbound/handler-registry.js +23 -0
  241. package/src/messaging/inbound/handler.d.ts +37 -0
  242. package/src/messaging/inbound/handler.js +286 -0
  243. package/src/messaging/inbound/media-resolver.d.ts +32 -0
  244. package/src/messaging/inbound/media-resolver.js +91 -0
  245. package/src/messaging/inbound/mention-registry.d.ts +59 -0
  246. package/src/messaging/inbound/mention-registry.js +115 -0
  247. package/src/messaging/inbound/mention.d.ts +48 -0
  248. package/src/messaging/inbound/mention.js +102 -0
  249. package/src/messaging/inbound/parse-io.d.ts +50 -0
  250. package/src/messaging/inbound/parse-io.js +86 -0
  251. package/src/messaging/inbound/parse.d.ts +28 -0
  252. package/src/messaging/inbound/parse.js +128 -0
  253. package/src/messaging/inbound/permission.d.ts +17 -0
  254. package/src/messaging/inbound/permission.js +44 -0
  255. package/src/messaging/inbound/policy.d.ts +95 -0
  256. package/src/messaging/inbound/policy.js +168 -0
  257. package/src/messaging/inbound/reaction-handler.d.ts +62 -0
  258. package/src/messaging/inbound/reaction-handler.js +259 -0
  259. package/src/messaging/inbound/sentinel-store.d.ts +37 -0
  260. package/src/messaging/inbound/sentinel-store.js +98 -0
  261. package/src/messaging/inbound/synthetic-message.d.ts +26 -0
  262. package/src/messaging/inbound/synthetic-message.js +59 -0
  263. package/src/messaging/inbound/user-name-cache-store.d.ts +62 -0
  264. package/src/messaging/inbound/user-name-cache-store.js +236 -0
  265. package/src/messaging/inbound/user-name-cache.d.ts +78 -0
  266. package/src/messaging/inbound/user-name-cache.js +306 -0
  267. package/src/messaging/inbound/vc-meeting-invited-handler.d.ts +20 -0
  268. package/src/messaging/inbound/vc-meeting-invited-handler.js +236 -0
  269. package/src/messaging/inbound/vc-sender.d.ts +41 -0
  270. package/src/messaging/inbound/vc-sender.js +53 -0
  271. package/src/messaging/outbound/actions.d.ts +16 -0
  272. package/src/messaging/outbound/actions.js +329 -0
  273. package/src/messaging/outbound/bot-peer-context.d.ts +42 -0
  274. package/src/messaging/outbound/bot-peer-context.js +40 -0
  275. package/src/messaging/outbound/chat-manage.d.ts +64 -0
  276. package/src/messaging/outbound/chat-manage.js +117 -0
  277. package/src/messaging/outbound/deliver.d.ts +186 -0
  278. package/src/messaging/outbound/deliver.js +410 -0
  279. package/src/messaging/outbound/fetch.d.ts +12 -0
  280. package/src/messaging/outbound/fetch.js +17 -0
  281. package/src/messaging/outbound/forward.d.ts +26 -0
  282. package/src/messaging/outbound/forward.js +51 -0
  283. package/src/messaging/outbound/media-url-utils.d.ts +29 -0
  284. package/src/messaging/outbound/media-url-utils.js +172 -0
  285. package/src/messaging/outbound/media.d.ts +260 -0
  286. package/src/messaging/outbound/media.js +865 -0
  287. package/src/messaging/outbound/normalize-mentions.d.ts +50 -0
  288. package/src/messaging/outbound/normalize-mentions.js +166 -0
  289. package/src/messaging/outbound/outbound-mention.d.ts +41 -0
  290. package/src/messaging/outbound/outbound-mention.js +112 -0
  291. package/src/messaging/outbound/outbound.d.ts +79 -0
  292. package/src/messaging/outbound/outbound.js +195 -0
  293. package/src/messaging/outbound/reactions.d.ts +124 -0
  294. package/src/messaging/outbound/reactions.js +384 -0
  295. package/src/messaging/outbound/send.d.ts +153 -0
  296. package/src/messaging/outbound/send.js +417 -0
  297. package/src/messaging/outbound/typing.d.ts +60 -0
  298. package/src/messaging/outbound/typing.js +139 -0
  299. package/src/messaging/shared/message-lookup.d.ts +54 -0
  300. package/src/messaging/shared/message-lookup.js +120 -0
  301. package/src/messaging/types.d.ts +316 -0
  302. package/src/messaging/types.js +11 -0
  303. package/src/tools/ask-user-question.d.ts +32 -0
  304. package/src/tools/ask-user-question.js +922 -0
  305. package/src/tools/auto-auth.d.ts +57 -0
  306. package/src/tools/auto-auth.js +904 -0
  307. package/src/tools/helpers.d.ts +264 -0
  308. package/src/tools/helpers.js +389 -0
  309. package/src/tools/mcp/doc/create.d.ts +12 -0
  310. package/src/tools/mcp/doc/create.js +47 -0
  311. package/src/tools/mcp/doc/fetch.d.ts +12 -0
  312. package/src/tools/mcp/doc/fetch.js +39 -0
  313. package/src/tools/mcp/doc/index.d.ts +12 -0
  314. package/src/tools/mcp/doc/index.js +50 -0
  315. package/src/tools/mcp/doc/update.d.ts +12 -0
  316. package/src/tools/mcp/doc/update.js +64 -0
  317. package/src/tools/mcp/shared.d.ts +59 -0
  318. package/src/tools/mcp/shared.js +239 -0
  319. package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
  320. package/src/tools/oapi/bitable/app-table-field.js +225 -0
  321. package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
  322. package/src/tools/oapi/bitable/app-table-record.js +439 -0
  323. package/src/tools/oapi/bitable/app-table-view.d.ts +16 -0
  324. package/src/tools/oapi/bitable/app-table-view.js +172 -0
  325. package/src/tools/oapi/bitable/app-table.d.ts +17 -0
  326. package/src/tools/oapi/bitable/app-table.js +195 -0
  327. package/src/tools/oapi/bitable/app.d.ts +18 -0
  328. package/src/tools/oapi/bitable/app.js +189 -0
  329. package/src/tools/oapi/bitable/index.d.ts +9 -0
  330. package/src/tools/oapi/bitable/index.js +17 -0
  331. package/src/tools/oapi/calendar/calendar.d.ts +15 -0
  332. package/src/tools/oapi/calendar/calendar.js +125 -0
  333. package/src/tools/oapi/calendar/event-attendee.d.ts +14 -0
  334. package/src/tools/oapi/calendar/event-attendee.js +161 -0
  335. package/src/tools/oapi/calendar/event.d.ts +16 -0
  336. package/src/tools/oapi/calendar/event.js +712 -0
  337. package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
  338. package/src/tools/oapi/calendar/freebusy.js +114 -0
  339. package/src/tools/oapi/calendar/index.d.ts +8 -0
  340. package/src/tools/oapi/calendar/index.js +15 -0
  341. package/src/tools/oapi/chat/chat.d.ts +16 -0
  342. package/src/tools/oapi/chat/chat.js +127 -0
  343. package/src/tools/oapi/chat/index.d.ts +10 -0
  344. package/src/tools/oapi/chat/index.js +23 -0
  345. package/src/tools/oapi/chat/members.d.ts +11 -0
  346. package/src/tools/oapi/chat/members.js +84 -0
  347. package/src/tools/oapi/common/get-user.d.ts +12 -0
  348. package/src/tools/oapi/common/get-user.js +109 -0
  349. package/src/tools/oapi/common/index.d.ts +6 -0
  350. package/src/tools/oapi/common/index.js +11 -0
  351. package/src/tools/oapi/common/search-user.d.ts +11 -0
  352. package/src/tools/oapi/common/search-user.js +76 -0
  353. package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
  354. package/src/tools/oapi/drive/doc-comments.js +351 -0
  355. package/src/tools/oapi/drive/doc-media.d.ts +19 -0
  356. package/src/tools/oapi/drive/doc-media.js +371 -0
  357. package/src/tools/oapi/drive/file.d.ts +19 -0
  358. package/src/tools/oapi/drive/file.js +519 -0
  359. package/src/tools/oapi/drive/index.d.ts +12 -0
  360. package/src/tools/oapi/drive/index.js +45 -0
  361. package/src/tools/oapi/helpers.d.ts +183 -0
  362. package/src/tools/oapi/helpers.js +385 -0
  363. package/src/tools/oapi/im/format-messages.d.ts +50 -0
  364. package/src/tools/oapi/im/format-messages.js +169 -0
  365. package/src/tools/oapi/im/index.d.ts +10 -0
  366. package/src/tools/oapi/im/index.js +25 -0
  367. package/src/tools/oapi/im/message-read.d.ts +13 -0
  368. package/src/tools/oapi/im/message-read.js +419 -0
  369. package/src/tools/oapi/im/message.d.ts +16 -0
  370. package/src/tools/oapi/im/message.js +152 -0
  371. package/src/tools/oapi/im/resource.d.ts +13 -0
  372. package/src/tools/oapi/im/resource.js +186 -0
  373. package/src/tools/oapi/im/time-utils.d.ts +46 -0
  374. package/src/tools/oapi/im/time-utils.js +212 -0
  375. package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
  376. package/src/tools/oapi/im/user-name-uat.js +143 -0
  377. package/src/tools/oapi/index.d.ts +11 -0
  378. package/src/tools/oapi/index.js +64 -0
  379. package/src/tools/oapi/sdk-types.d.ts +96 -0
  380. package/src/tools/oapi/sdk-types.js +13 -0
  381. package/src/tools/oapi/search/doc-search.d.ts +13 -0
  382. package/src/tools/oapi/search/doc-search.js +194 -0
  383. package/src/tools/oapi/search/index.d.ts +12 -0
  384. package/src/tools/oapi/search/index.js +37 -0
  385. package/src/tools/oapi/sheets/index.d.ts +12 -0
  386. package/src/tools/oapi/sheets/index.js +35 -0
  387. package/src/tools/oapi/sheets/sheet.d.ts +16 -0
  388. package/src/tools/oapi/sheets/sheet.js +688 -0
  389. package/src/tools/oapi/task/attachment.d.ts +18 -0
  390. package/src/tools/oapi/task/attachment.js +107 -0
  391. package/src/tools/oapi/task/comment.d.ts +15 -0
  392. package/src/tools/oapi/task/comment.js +149 -0
  393. package/src/tools/oapi/task/index.d.ts +11 -0
  394. package/src/tools/oapi/task/index.js +21 -0
  395. package/src/tools/oapi/task/section.d.ts +17 -0
  396. package/src/tools/oapi/task/section.js +293 -0
  397. package/src/tools/oapi/task/subtask.d.ts +14 -0
  398. package/src/tools/oapi/task/subtask.js +171 -0
  399. package/src/tools/oapi/task/task.d.ts +19 -0
  400. package/src/tools/oapi/task/task.js +512 -0
  401. package/src/tools/oapi/task/task_agent.d.ts +14 -0
  402. package/src/tools/oapi/task/task_agent.js +108 -0
  403. package/src/tools/oapi/task/tasklist.d.ts +19 -0
  404. package/src/tools/oapi/task/tasklist.js +276 -0
  405. package/src/tools/oapi/wiki/index.d.ts +12 -0
  406. package/src/tools/oapi/wiki/index.js +42 -0
  407. package/src/tools/oapi/wiki/space-node.d.ts +17 -0
  408. package/src/tools/oapi/wiki/space-node.js +233 -0
  409. package/src/tools/oapi/wiki/space.d.ts +15 -0
  410. package/src/tools/oapi/wiki/space.js +133 -0
  411. package/src/tools/oauth-batch-auth.d.ts +11 -0
  412. package/src/tools/oauth-batch-auth.js +150 -0
  413. package/src/tools/oauth-cards.d.ts +39 -0
  414. package/src/tools/oauth-cards.js +324 -0
  415. package/src/tools/oauth.d.ts +47 -0
  416. package/src/tools/oauth.js +592 -0
  417. package/src/tools/onboarding-auth.d.ts +27 -0
  418. package/src/tools/onboarding-auth.js +133 -0
  419. package/src/tools/tat/im/index.d.ts +15 -0
  420. package/src/tools/tat/im/index.js +22 -0
  421. package/src/tools/tat/im/resource.d.ts +15 -0
  422. package/src/tools/tat/im/resource.js +192 -0
  423. package/tsdown.config.d.ts +2 -0
  424. package/tsdown.config.js +25 -0
  425. package/vitest.config.d.ts +2 -0
  426. package/vitest.config.js +12 -0
@@ -0,0 +1,688 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * feishu_sheet — 飞书电子表格统一工具。
7
+ *
8
+ * Actions: info, read, write, append, find, create, export
9
+ *
10
+ * 设计原则:
11
+ * - 接受 URL 或 spreadsheet_token(工具层自动解析)
12
+ * - read 不指定 range 时自动读取第一个工作表全部数据
13
+ * - create 支持带表头和初始数据一步创建
14
+ * - info 一次返回表格信息 + 全部工作表列表
15
+ */
16
+ /* eslint-disable @typescript-eslint/no-explicit-any */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.registerFeishuSheetTool = registerFeishuSheetTool;
52
+ const fs = __importStar(require("node:fs/promises"));
53
+ const path = __importStar(require("node:path"));
54
+ const typebox_1 = require("@sinclair/typebox");
55
+ const helpers_1 = require("../helpers.js");
56
+ const domains_1 = require("../../../core/domains.js");
57
+ // ---------------------------------------------------------------------------
58
+ // Constants
59
+ // ---------------------------------------------------------------------------
60
+ const MAX_READ_ROWS = 200;
61
+ const MAX_WRITE_ROWS = 5000;
62
+ const MAX_WRITE_COLS = 100;
63
+ const EXPORT_POLL_INTERVAL_MS = 1000;
64
+ const EXPORT_POLL_MAX_RETRIES = 30;
65
+ // ---------------------------------------------------------------------------
66
+ // Helpers
67
+ // ---------------------------------------------------------------------------
68
+ function sleep(ms) {
69
+ return new Promise((resolve) => setTimeout(resolve, ms));
70
+ }
71
+ /**
72
+ * 从飞书电子表格 URL 中解析 token 和可选的 sheet_id。
73
+ *
74
+ * 支持格式:
75
+ * https://www.feishu.cn/sheets/TOKEN
76
+ * https://xxx.feishu.cn/sheets/TOKEN?sheet=SHEET_ID
77
+ * https://xxx.feishu.cn/wiki/TOKEN(知识库中的电子表格)
78
+ */
79
+ function parseSheetUrl(url) {
80
+ try {
81
+ const u = new URL(url);
82
+ const match = u.pathname.match(/\/(?:sheets|wiki)\/([^/?#]+)/);
83
+ if (!match)
84
+ return null;
85
+ return {
86
+ token: match[1],
87
+ sheetId: u.searchParams.get('sheet') || undefined,
88
+ };
89
+ }
90
+ catch {
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * 飞书已知的 token 类型前缀。
96
+ * 新版 token:第 5/10/15 位字符(1-indexed)组成前缀。
97
+ * 旧版 token:前 3 个字符即为前缀。
98
+ *
99
+ * 常见类型:dox=云文档, sht=电子表格, bas=多维表格, wik=知识库
100
+ */
101
+ const KNOWN_TOKEN_TYPES = new Set([
102
+ 'dox',
103
+ 'doc',
104
+ 'sht',
105
+ 'bas',
106
+ 'app',
107
+ 'sld',
108
+ 'bmn',
109
+ 'fld',
110
+ 'nod',
111
+ 'box',
112
+ 'jsn',
113
+ 'img',
114
+ 'isv',
115
+ 'wik',
116
+ 'wia',
117
+ 'wib',
118
+ 'wic',
119
+ 'wid',
120
+ 'wie',
121
+ 'dsb',
122
+ ]);
123
+ /**
124
+ * 从 token 中提取类型前缀(如 "sht"、"wik"、"doc" 等)。
125
+ * 先检测新版格式(第 5/10/15 位),再回退旧版格式(前 3 位)。
126
+ */
127
+ function getTokenType(token) {
128
+ if (token.length >= 15) {
129
+ const prefix = token[4] + token[9] + token[14];
130
+ if (KNOWN_TOKEN_TYPES.has(prefix))
131
+ return prefix;
132
+ }
133
+ if (token.length >= 3) {
134
+ const prefix = token.substring(0, 3);
135
+ if (KNOWN_TOKEN_TYPES.has(prefix))
136
+ return prefix;
137
+ }
138
+ return null;
139
+ }
140
+ /**
141
+ * 从参数中解析 spreadsheet_token(支持 url 和直接 token 两种方式)。
142
+ * 如果检测到 wiki token,自动通过 wiki API 获取真实的 spreadsheet_token。
143
+ */
144
+ async function resolveToken(p, client, log) {
145
+ let token;
146
+ let urlSheetId;
147
+ if (p.spreadsheet_token) {
148
+ token = p.spreadsheet_token;
149
+ }
150
+ else if (p.url) {
151
+ const parsed = parseSheetUrl(p.url);
152
+ if (!parsed) {
153
+ throw new Error(`Failed to parse spreadsheet_token from URL: ${p.url}`);
154
+ }
155
+ token = parsed.token;
156
+ urlSheetId = parsed.sheetId;
157
+ }
158
+ else {
159
+ throw new Error('url or spreadsheet_token is required');
160
+ }
161
+ // 检测 wiki token 并解析为真实的 spreadsheet_token
162
+ const tokenType = getTokenType(token);
163
+ if (tokenType === 'wik') {
164
+ log.info(`resolveToken: detected wiki token, resolving obj_token...`);
165
+ const wikiNodeRes = await client.invoke('feishu_sheet.info', (sdk, opts) => sdk.wiki.space.getNode({
166
+ params: {
167
+ token,
168
+ obj_type: 'wiki',
169
+ },
170
+ }, opts), { as: 'user' });
171
+ (0, helpers_1.assertLarkOk)(wikiNodeRes);
172
+ const objToken = wikiNodeRes.data?.node?.obj_token;
173
+ if (!objToken) {
174
+ throw new Error(`Failed to resolve spreadsheet token from wiki token: ${token}`);
175
+ }
176
+ log.info(`resolveToken: wiki resolved ${token} -> ${objToken}`);
177
+ token = objToken;
178
+ }
179
+ return { token, urlSheetId };
180
+ }
181
+ /**
182
+ * Resolve the target range for read/write/append operations.
183
+ *
184
+ * Priority: explicit range > sheet_id param / URL sheet > first sheet via API.
185
+ * Throws if the spreadsheet has no worksheets.
186
+ */
187
+ async function resolveRange(token, range, sheetId, client, apiName) {
188
+ if (range)
189
+ return range;
190
+ if (sheetId)
191
+ return sheetId;
192
+ const sheetsRes = await client.invoke(apiName, (sdk, opts) => sdk.sheets.spreadsheetSheet.query({ path: { spreadsheet_token: token } }, opts), { as: 'user' });
193
+ (0, helpers_1.assertLarkOk)(sheetsRes);
194
+ const firstSheet = (sheetsRes.data?.sheets ?? [])[0];
195
+ if (!firstSheet?.sheet_id) {
196
+ throw new Error('spreadsheet has no worksheets');
197
+ }
198
+ return firstSheet.sheet_id;
199
+ }
200
+ /**
201
+ * 将列号(1-based)转换为 Excel 列字母(A, B, ..., Z, AA, AB, ...)。
202
+ */
203
+ function colLetter(n) {
204
+ let result = '';
205
+ while (n > 0) {
206
+ n--;
207
+ result = String.fromCharCode(65 + (n % 26)) + result;
208
+ n = Math.floor(n / 26);
209
+ }
210
+ return result;
211
+ }
212
+ /**
213
+ * 将单元格值中的富文本 segment 数组拍平为纯文本字符串。
214
+ *
215
+ * 飞书 Sheets API 对带样式的单元格返回 [{type:"text", text:"...", segmentStyle:{...}}, ...] 格式,
216
+ * 极其冗余。此函数将其拼接为单个字符串,大幅减少 token 消耗。
217
+ */
218
+ function flattenCellValue(cell) {
219
+ if (!Array.isArray(cell))
220
+ return cell;
221
+ // 检测是否为富文本 segment 数组:每个元素都是 {text: string, ...} 对象
222
+ if (cell.length > 0 && cell.every((seg) => seg != null && typeof seg === 'object' && 'text' in seg)) {
223
+ return cell.map((seg) => seg.text).join('');
224
+ }
225
+ return cell;
226
+ }
227
+ function flattenValues(values) {
228
+ if (!values)
229
+ return values;
230
+ return values.map((row) => row.map(flattenCellValue));
231
+ }
232
+ function truncateRows(values, maxRows) {
233
+ if (!values)
234
+ return { values, truncated: false, total_rows: 0 };
235
+ const total = values.length;
236
+ if (total <= maxRows)
237
+ return { values, truncated: false, total_rows: total };
238
+ return { values: values.slice(0, maxRows), truncated: true, total_rows: total };
239
+ }
240
+ // ---------------------------------------------------------------------------
241
+ // Schema
242
+ // ---------------------------------------------------------------------------
243
+ const UrlOrToken = [
244
+ typebox_1.Type.Optional(typebox_1.Type.String({
245
+ description: '电子表格 URL,例如 https://xxx.feishu.cn/sheets/TOKEN 或 https://xxx.feishu.cn/wiki/TOKEN(与 spreadsheet_token 二选一)',
246
+ })),
247
+ typebox_1.Type.Optional(typebox_1.Type.String({
248
+ description: '电子表格 token(与 url 二选一)',
249
+ })),
250
+ ];
251
+ const ValueRenderOption = typebox_1.Type.Optional(typebox_1.Type.Union([
252
+ typebox_1.Type.Literal('ToString'),
253
+ typebox_1.Type.Literal('FormattedValue'),
254
+ typebox_1.Type.Literal('Formula'),
255
+ typebox_1.Type.Literal('UnformattedValue'),
256
+ ], {
257
+ description: '值渲染方式:ToString(默认)、FormattedValue(按格式)、Formula(公式)、UnformattedValue(原始值)',
258
+ }));
259
+ const FeishuSheetSchema = typebox_1.Type.Union([
260
+ // INFO
261
+ typebox_1.Type.Object({
262
+ action: typebox_1.Type.Literal('info'),
263
+ url: UrlOrToken[0],
264
+ spreadsheet_token: UrlOrToken[1],
265
+ }),
266
+ // READ
267
+ typebox_1.Type.Object({
268
+ action: typebox_1.Type.Literal('read'),
269
+ url: UrlOrToken[0],
270
+ spreadsheet_token: UrlOrToken[1],
271
+ range: typebox_1.Type.Optional(typebox_1.Type.String({
272
+ description: '读取范围(可选)。格式:<sheetId>!A1:D10 或 <sheetId>(sheetId 通过 info 获取)。不填则自动读取第一个工作表全部数据',
273
+ })),
274
+ sheet_id: typebox_1.Type.Optional(typebox_1.Type.String({
275
+ description: '工作表 ID(可选)。仅当不提供 range 时生效,指定要读取的工作表。不填则读取第一个工作表',
276
+ })),
277
+ value_render_option: ValueRenderOption,
278
+ }),
279
+ // WRITE
280
+ typebox_1.Type.Object({
281
+ action: typebox_1.Type.Literal('write'),
282
+ url: UrlOrToken[0],
283
+ spreadsheet_token: UrlOrToken[1],
284
+ range: typebox_1.Type.Optional(typebox_1.Type.String({
285
+ description: '写入范围(可选)。格式:<sheetId>!A1:D10(sheetId 通过 info 获取)。不填则写入第一个工作表(从 A1 开始)',
286
+ })),
287
+ sheet_id: typebox_1.Type.Optional(typebox_1.Type.String({
288
+ description: '工作表 ID(可选)。仅当不提供 range 时生效。不填则使用第一个工作表',
289
+ })),
290
+ values: typebox_1.Type.Array(typebox_1.Type.Array(typebox_1.Type.Any()), {
291
+ description: '二维数组,每个元素是一行。例如 [["姓名","年龄"],["张三",25]]',
292
+ }),
293
+ }),
294
+ // APPEND
295
+ typebox_1.Type.Object({
296
+ action: typebox_1.Type.Literal('append'),
297
+ url: UrlOrToken[0],
298
+ spreadsheet_token: UrlOrToken[1],
299
+ range: typebox_1.Type.Optional(typebox_1.Type.String({
300
+ description: '追加范围(可选)。格式同 write。不填则追加到第一个工作表末尾',
301
+ })),
302
+ sheet_id: typebox_1.Type.Optional(typebox_1.Type.String({
303
+ description: '工作表 ID(可选)。仅当不提供 range 时生效',
304
+ })),
305
+ values: typebox_1.Type.Array(typebox_1.Type.Array(typebox_1.Type.Any()), {
306
+ description: '要追加的二维数组数据',
307
+ }),
308
+ }),
309
+ // FIND
310
+ typebox_1.Type.Object({
311
+ action: typebox_1.Type.Literal('find'),
312
+ url: UrlOrToken[0],
313
+ spreadsheet_token: UrlOrToken[1],
314
+ sheet_id: typebox_1.Type.String({
315
+ description: '工作表 ID(必填,可通过 info action 获取)',
316
+ }),
317
+ find: typebox_1.Type.String({
318
+ description: '查找内容(字符串或正则表达式)',
319
+ }),
320
+ range: typebox_1.Type.Optional(typebox_1.Type.String({
321
+ description: '查找范围。格式:A1:D10(不含 sheetId 前缀)。不填则搜索整个工作表',
322
+ })),
323
+ match_case: typebox_1.Type.Optional(typebox_1.Type.Boolean({ description: '是否区分大小写(默认 true)' })),
324
+ match_entire_cell: typebox_1.Type.Optional(typebox_1.Type.Boolean({ description: '是否完全匹配整个单元格(默认 false)' })),
325
+ search_by_regex: typebox_1.Type.Optional(typebox_1.Type.Boolean({ description: '是否使用正则表达式(默认 false)' })),
326
+ include_formulas: typebox_1.Type.Optional(typebox_1.Type.Boolean({ description: '是否搜索公式(默认 false)' })),
327
+ }),
328
+ // CREATE
329
+ typebox_1.Type.Object({
330
+ action: typebox_1.Type.Literal('create'),
331
+ title: typebox_1.Type.String({
332
+ description: '电子表格标题',
333
+ }),
334
+ folder_token: typebox_1.Type.Optional(typebox_1.Type.String({
335
+ description: '文件夹 token(可选)。不填时创建到「我的空间」根目录',
336
+ })),
337
+ headers: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String(), {
338
+ description: '表头列名(可选)。例如 ["姓名", "部门", "入职日期"]。提供后会写入第一行',
339
+ })),
340
+ data: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.Array(typebox_1.Type.Any()), {
341
+ description: '初始数据(可选)。二维数组,写在表头之后。例如 [["张三", "工程", "2026-01-01"]]',
342
+ })),
343
+ }),
344
+ // EXPORT
345
+ typebox_1.Type.Object({
346
+ action: typebox_1.Type.Literal('export'),
347
+ url: UrlOrToken[0],
348
+ spreadsheet_token: UrlOrToken[1],
349
+ file_extension: (0, helpers_1.StringEnum)(['xlsx', 'csv'], {
350
+ description: '导出格式:xlsx 或 csv',
351
+ }),
352
+ output_path: typebox_1.Type.Optional(typebox_1.Type.String({
353
+ description: '本地保存路径(含文件名)。不填则只返回文件信息',
354
+ })),
355
+ sheet_id: typebox_1.Type.Optional(typebox_1.Type.String({
356
+ description: '工作表 ID。导出 CSV 时必填(CSV 一次只能导出一个工作表),导出 xlsx 时可选',
357
+ })),
358
+ }),
359
+ ]);
360
+ // ---------------------------------------------------------------------------
361
+ // Registration
362
+ // ---------------------------------------------------------------------------
363
+ function registerFeishuSheetTool(api) {
364
+ if (!api.config)
365
+ return false;
366
+ const cfg = api.config;
367
+ const { toolClient, log } = (0, helpers_1.createToolContext)(api, 'feishu_sheet');
368
+ return (0, helpers_1.registerTool)(api, {
369
+ name: 'feishu_sheet',
370
+ label: 'Feishu Spreadsheet',
371
+ description: '【以用户身份】飞书电子表格工具。支持创建、读写、查找、导出电子表格。' +
372
+ '\n\n电子表格(Sheets)类似 Excel/Google Sheets,与多维表格(Bitable/Airtable)是不同产品。' +
373
+ '\n\n所有 action(除 create 外)均支持传入 url 或 spreadsheet_token,工具会自动解析。支持知识库 wiki URL,自动解析为电子表格 token。' +
374
+ '\n\nActions:' +
375
+ '\n- info:获取表格信息 + 全部工作表列表(一次调用替代 get_info + list_sheets)' +
376
+ '\n- read:读取数据。不填 range 自动读取第一个工作表全部数据' +
377
+ '\n- write:覆盖写入,高危,请谨慎使用该操作。不填 range 自动写入第一个工作表(从 A1 开始)' +
378
+ '\n- append:在已有数据末尾追加行' +
379
+ '\n- find:在工作表中查找单元格' +
380
+ '\n- create:创建电子表格。支持带 headers + data 一步创建含数据的表格' +
381
+ '\n- export:导出为 xlsx 或 csv(csv 必须指定 sheet_id)',
382
+ parameters: FeishuSheetSchema,
383
+ async execute(_toolCallId, params) {
384
+ const p = params;
385
+ try {
386
+ const client = toolClient();
387
+ const brand = client.account.brand;
388
+ switch (p.action) {
389
+ // -----------------------------------------------------------------
390
+ // INFO — 表格信息 + 全部工作表列表
391
+ // -----------------------------------------------------------------
392
+ case 'info': {
393
+ const { token } = await resolveToken(p, client, log);
394
+ log.info(`info: token=${token}`);
395
+ // 并行请求表格信息和工作表列表
396
+ const [spreadsheetRes, sheetsRes] = await Promise.all([
397
+ client.invoke('feishu_sheet.info', (sdk, opts) => sdk.sheets.spreadsheet.get({ path: { spreadsheet_token: token } }, opts), { as: 'user' }),
398
+ client.invoke('feishu_sheet.info', (sdk, opts) => sdk.sheets.spreadsheetSheet.query({ path: { spreadsheet_token: token } }, opts), { as: 'user' }),
399
+ ]);
400
+ (0, helpers_1.assertLarkOk)(spreadsheetRes);
401
+ (0, helpers_1.assertLarkOk)(sheetsRes);
402
+ const spreadsheet = spreadsheetRes.data?.spreadsheet;
403
+ const sheets = (sheetsRes.data?.sheets ?? []).map((s) => ({
404
+ sheet_id: s.sheet_id,
405
+ title: s.title,
406
+ index: s.index,
407
+ row_count: s.grid_properties?.row_count,
408
+ column_count: s.grid_properties?.column_count,
409
+ frozen_row_count: s.grid_properties?.frozen_row_count,
410
+ frozen_column_count: s.grid_properties?.frozen_column_count,
411
+ }));
412
+ log.info(`info: title="${spreadsheet?.title}", ${sheets.length} sheets`);
413
+ return (0, helpers_1.json)({
414
+ title: spreadsheet?.title,
415
+ spreadsheet_token: token,
416
+ url: `${(0, domains_1.wwwDomain)(brand)}/sheets/${token}`,
417
+ sheets,
418
+ });
419
+ }
420
+ // -----------------------------------------------------------------
421
+ // READ — 读取数据(支持自动探测范围)
422
+ // -----------------------------------------------------------------
423
+ case 'read': {
424
+ const { token, urlSheetId } = await resolveToken(p, client, log);
425
+ const range = await resolveRange(token, p.range, p.sheet_id ?? urlSheetId, client, 'feishu_sheet.read');
426
+ log.info(`read: token=${token}, range=${range}`);
427
+ const query = {
428
+ // 默认返回计算后的值(而非公式原文),日期转为可读字符串
429
+ valueRenderOption: p.value_render_option ?? 'ToString',
430
+ dateTimeRenderOption: 'FormattedString',
431
+ };
432
+ const res = await client.invokeByPath('feishu_sheet.read', `/open-apis/sheets/v2/spreadsheets/${token}/values/${encodeURIComponent(range)}`, { method: 'GET', query, as: 'user' });
433
+ if (res.code && res.code !== 0) {
434
+ return (0, helpers_1.json)({ error: res.msg || `API error code: ${res.code}` });
435
+ }
436
+ const valueRange = res.data?.valueRange;
437
+ const { values, truncated, total_rows } = truncateRows(flattenValues(valueRange?.values), MAX_READ_ROWS);
438
+ log.info(`read: ${total_rows} rows${truncated ? ` (truncated to ${MAX_READ_ROWS})` : ''}`);
439
+ return (0, helpers_1.json)({
440
+ range: valueRange?.range,
441
+ values,
442
+ ...(truncated
443
+ ? {
444
+ truncated: true,
445
+ total_rows,
446
+ hint: `Data exceeds ${MAX_READ_ROWS} rows, truncated. Please narrow the range and read again.`,
447
+ }
448
+ : {}),
449
+ });
450
+ }
451
+ // -----------------------------------------------------------------
452
+ // WRITE — 覆盖写入(支持自动 range)
453
+ // -----------------------------------------------------------------
454
+ case 'write': {
455
+ const { token, urlSheetId } = await resolveToken(p, client, log);
456
+ if (p.values && p.values.length > MAX_WRITE_ROWS) {
457
+ return (0, helpers_1.json)({ error: `write row count ${p.values.length} exceeds limit ${MAX_WRITE_ROWS}` });
458
+ }
459
+ if (p.values && p.values.some((row) => Array.isArray(row) && row.length > MAX_WRITE_COLS)) {
460
+ return (0, helpers_1.json)({ error: `write column count exceeds limit ${MAX_WRITE_COLS}` });
461
+ }
462
+ const range = await resolveRange(token, p.range, p.sheet_id ?? urlSheetId, client, 'feishu_sheet.write');
463
+ log.info(`write: token=${token}, range=${range}, rows=${p.values?.length}`);
464
+ const res = await client.invokeByPath('feishu_sheet.write', `/open-apis/sheets/v2/spreadsheets/${token}/values`, {
465
+ method: 'PUT',
466
+ body: { valueRange: { range, values: p.values } },
467
+ as: 'user',
468
+ });
469
+ if (res.code && res.code !== 0) {
470
+ return (0, helpers_1.json)({ error: res.msg || `API error code: ${res.code}` });
471
+ }
472
+ log.info(`write: updated ${res.data?.updatedCells ?? 0} cells`);
473
+ return (0, helpers_1.json)({
474
+ updated_range: res.data?.updatedRange,
475
+ updated_rows: res.data?.updatedRows,
476
+ updated_columns: res.data?.updatedColumns,
477
+ updated_cells: res.data?.updatedCells,
478
+ revision: res.data?.revision,
479
+ });
480
+ }
481
+ // -----------------------------------------------------------------
482
+ // APPEND — 追加行
483
+ // -----------------------------------------------------------------
484
+ case 'append': {
485
+ const { token, urlSheetId } = await resolveToken(p, client, log);
486
+ if (p.values && p.values.length > MAX_WRITE_ROWS) {
487
+ return (0, helpers_1.json)({ error: `append row count ${p.values.length} exceeds limit ${MAX_WRITE_ROWS}` });
488
+ }
489
+ const range = await resolveRange(token, p.range, p.sheet_id ?? urlSheetId, client, 'feishu_sheet.append');
490
+ log.info(`append: token=${token}, range=${range}, rows=${p.values?.length}`);
491
+ const res = await client.invokeByPath('feishu_sheet.append', `/open-apis/sheets/v2/spreadsheets/${token}/values_append`, {
492
+ method: 'POST',
493
+ body: { valueRange: { range, values: p.values } },
494
+ as: 'user',
495
+ });
496
+ if (res.code && res.code !== 0) {
497
+ return (0, helpers_1.json)({ error: res.msg || `API error code: ${res.code}` });
498
+ }
499
+ const updates = res.data?.updates;
500
+ log.info(`append: updated ${updates?.updatedCells ?? 0} cells`);
501
+ return (0, helpers_1.json)({
502
+ table_range: res.data?.tableRange,
503
+ updated_range: updates?.updatedRange,
504
+ updated_rows: updates?.updatedRows,
505
+ updated_columns: updates?.updatedColumns,
506
+ updated_cells: updates?.updatedCells,
507
+ revision: updates?.revision,
508
+ });
509
+ }
510
+ // -----------------------------------------------------------------
511
+ // FIND — 查找单元格
512
+ // -----------------------------------------------------------------
513
+ case 'find': {
514
+ const { token } = await resolveToken(p, client, log);
515
+ log.info(`find: token=${token}, sheet_id=${p.sheet_id}, find="${p.find}"`);
516
+ const findCondition = {
517
+ range: p.range ? `${p.sheet_id}!${p.range}` : p.sheet_id,
518
+ };
519
+ if (p.match_case !== undefined)
520
+ findCondition.match_case = !p.match_case; // oapi问题, 实际true表示不用区分, false表示需要区分,所以要取反
521
+ if (p.match_entire_cell !== undefined)
522
+ findCondition.match_entire_cell = p.match_entire_cell;
523
+ if (p.search_by_regex !== undefined)
524
+ findCondition.search_by_regex = p.search_by_regex;
525
+ if (p.include_formulas !== undefined)
526
+ findCondition.include_formulas = p.include_formulas;
527
+ const res = await client.invoke('feishu_sheet.find', (sdk, opts) => sdk.sheets.spreadsheetSheet.find({
528
+ path: {
529
+ spreadsheet_token: token,
530
+ sheet_id: p.sheet_id,
531
+ },
532
+ data: {
533
+ find_condition: findCondition,
534
+ find: p.find,
535
+ },
536
+ }, opts), { as: 'user' });
537
+ (0, helpers_1.assertLarkOk)(res);
538
+ const findResult = res.data?.find_result;
539
+ log.info(`find: matched ${findResult?.matched_cells?.length ?? 0} cells`);
540
+ return (0, helpers_1.json)({
541
+ matched_cells: findResult?.matched_cells,
542
+ matched_formula_cells: findResult?.matched_formula_cells,
543
+ rows_count: findResult?.rows_count,
544
+ });
545
+ }
546
+ // -----------------------------------------------------------------
547
+ // CREATE — 创建电子表格(支持带初始数据)
548
+ // -----------------------------------------------------------------
549
+ case 'create': {
550
+ log.info(`create: title="${p.title}", folder=${p.folder_token ?? '(root)'}, headers=${!!p.headers}, data=${p.data?.length ?? 0} rows`);
551
+ // Step 1: 创建电子表格
552
+ const createRes = await client.invoke('feishu_sheet.create', (sdk, opts) => sdk.sheets.spreadsheet.create({
553
+ data: {
554
+ title: p.title,
555
+ folder_token: p.folder_token,
556
+ },
557
+ }, opts), { as: 'user' });
558
+ (0, helpers_1.assertLarkOk)(createRes);
559
+ const spreadsheet = createRes.data?.spreadsheet;
560
+ const token = spreadsheet?.spreadsheet_token;
561
+ if (!token) {
562
+ return (0, helpers_1.json)({ error: 'failed to create spreadsheet: no token returned' });
563
+ }
564
+ const url = `${(0, domains_1.wwwDomain)(brand)}/sheets/${token}`;
565
+ log.info(`create: token=${token}`);
566
+ // Step 2: 如果有 headers 或 data,写入初始数据
567
+ if (p.headers || p.data) {
568
+ const allRows = [];
569
+ if (p.headers)
570
+ allRows.push(p.headers);
571
+ if (p.data)
572
+ allRows.push(...p.data);
573
+ if (allRows.length > 0) {
574
+ // 查询默认工作表的 sheet_id
575
+ const sheetsRes = await client.invoke('feishu_sheet.create', (sdk, opts) => sdk.sheets.spreadsheetSheet.query({ path: { spreadsheet_token: token } }, opts), { as: 'user' });
576
+ (0, helpers_1.assertLarkOk)(sheetsRes);
577
+ const firstSheet = (sheetsRes.data?.sheets ?? [])[0];
578
+ if (firstSheet?.sheet_id) {
579
+ const sheetId = firstSheet.sheet_id;
580
+ const numRows = allRows.length;
581
+ const numCols = Math.max(...allRows.map((r) => r.length));
582
+ const range = `${sheetId}!A1:${colLetter(numCols)}${numRows}`;
583
+ log.info(`create: writing ${numRows} rows to ${range}`);
584
+ const writeRes = await client.invokeByPath('feishu_sheet.create', `/open-apis/sheets/v2/spreadsheets/${token}/values`, {
585
+ method: 'PUT',
586
+ body: { valueRange: { range, values: allRows } },
587
+ as: 'user',
588
+ });
589
+ if (writeRes.code && writeRes.code !== 0) {
590
+ log.info(`create: initial data write failed: ${writeRes.msg}`);
591
+ return (0, helpers_1.json)({
592
+ spreadsheet_token: token,
593
+ url,
594
+ warning: `spreadsheet created but failed to write initial data: ${writeRes.msg}`,
595
+ });
596
+ }
597
+ }
598
+ }
599
+ }
600
+ return (0, helpers_1.json)({
601
+ spreadsheet_token: token,
602
+ title: p.title,
603
+ url,
604
+ });
605
+ }
606
+ // -----------------------------------------------------------------
607
+ // EXPORT — 导出为 xlsx/csv
608
+ // -----------------------------------------------------------------
609
+ case 'export': {
610
+ const { token } = await resolveToken(p, client, log);
611
+ if (p.file_extension === 'csv' && !p.sheet_id) {
612
+ return (0, helpers_1.json)({
613
+ error: 'sheet_id is required for CSV export (CSV can only export one worksheet at a time). Use info action to get the worksheet list.',
614
+ });
615
+ }
616
+ log.info(`export: token=${token}, format=${p.file_extension}, output=${p.output_path ?? '(info only)'}`);
617
+ // Step 1: 创建导出任务
618
+ const createRes = await client.invoke('feishu_sheet.export', (sdk, opts) => sdk.drive.exportTask.create({
619
+ data: {
620
+ file_extension: p.file_extension,
621
+ token,
622
+ type: 'sheet',
623
+ sub_id: p.sheet_id,
624
+ },
625
+ }, opts), { as: 'user' });
626
+ (0, helpers_1.assertLarkOk)(createRes);
627
+ const ticket = createRes.data?.ticket;
628
+ if (!ticket) {
629
+ return (0, helpers_1.json)({ error: 'failed to create export task: no ticket returned' });
630
+ }
631
+ log.info(`export: ticket=${ticket}`);
632
+ // Step 2: 轮询等待完成
633
+ let fileToken;
634
+ let fileName;
635
+ let fileSize;
636
+ for (let i = 0; i < EXPORT_POLL_MAX_RETRIES; i++) {
637
+ await sleep(EXPORT_POLL_INTERVAL_MS);
638
+ const pollRes = await client.invoke('feishu_sheet.export', (sdk, opts) => sdk.drive.exportTask.get({ path: { ticket }, params: { token } }, opts), { as: 'user' });
639
+ (0, helpers_1.assertLarkOk)(pollRes);
640
+ const result = pollRes.data?.result;
641
+ const jobStatus = result?.job_status;
642
+ if (jobStatus === 0) {
643
+ fileToken = result?.file_token;
644
+ fileName = result?.file_name;
645
+ fileSize = result?.file_size;
646
+ log.info(`export: done, file_token=${fileToken}, size=${fileSize}`);
647
+ break;
648
+ }
649
+ if (jobStatus !== undefined && jobStatus >= 3) {
650
+ return (0, helpers_1.json)({ error: result?.job_error_msg || `export failed (status=${jobStatus})` });
651
+ }
652
+ log.info(`export: polling ${i + 1}/${EXPORT_POLL_MAX_RETRIES}, status=${jobStatus}`);
653
+ }
654
+ if (!fileToken) {
655
+ return (0, helpers_1.json)({ error: 'export timeout: task did not complete within 30 seconds' });
656
+ }
657
+ // Step 3: 下载(如果指定了 output_path)
658
+ if (p.output_path) {
659
+ const dlRes = await client.invoke('feishu_sheet.export', (sdk, opts) => sdk.drive.exportTask.download({ path: { file_token: fileToken } }, opts), { as: 'user' });
660
+ const stream = dlRes.getReadableStream();
661
+ const chunks = [];
662
+ for await (const chunk of stream) {
663
+ chunks.push(chunk);
664
+ }
665
+ await fs.mkdir(path.dirname(p.output_path), { recursive: true });
666
+ await fs.writeFile(p.output_path, Buffer.concat(chunks));
667
+ log.info(`export: saved to ${p.output_path}`);
668
+ return (0, helpers_1.json)({
669
+ file_path: p.output_path,
670
+ file_name: fileName,
671
+ file_size: fileSize,
672
+ });
673
+ }
674
+ return (0, helpers_1.json)({
675
+ file_token: fileToken,
676
+ file_name: fileName,
677
+ file_size: fileSize,
678
+ hint: 'File exported. Provide output_path parameter to download locally.',
679
+ });
680
+ }
681
+ }
682
+ }
683
+ catch (err) {
684
+ return await (0, helpers_1.handleInvokeErrorWithAutoAuth)(err, cfg);
685
+ }
686
+ },
687
+ }, { name: 'feishu_sheet' });
688
+ }