@lark-project/openclaw-lark-project 2026.3.131

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 (368) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/README.zh.md +80 -0
  4. package/dist/index.js +172 -0
  5. package/dist/index.js.map +7 -0
  6. package/dist/skills/feishu-bitable/SKILL.md +248 -0
  7. package/dist/skills/feishu-bitable/references/examples.md +813 -0
  8. package/dist/skills/feishu-bitable/references/field-properties.md +763 -0
  9. package/dist/skills/feishu-bitable/references/record-values.md +911 -0
  10. package/dist/skills/feishu-calendar/SKILL.md +244 -0
  11. package/dist/skills/feishu-channel-rules/SKILL.md +18 -0
  12. package/dist/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  13. package/dist/skills/feishu-create-doc/SKILL.md +719 -0
  14. package/dist/skills/feishu-fetch-doc/SKILL.md +93 -0
  15. package/dist/skills/feishu-im-read/SKILL.md +163 -0
  16. package/dist/skills/feishu-project/SKILL.md +122 -0
  17. package/dist/skills/feishu-task/SKILL.md +293 -0
  18. package/dist/skills/feishu-troubleshoot/SKILL.md +70 -0
  19. package/dist/skills/feishu-update-doc/SKILL.md +285 -0
  20. package/dist/src/card/builder.js +293 -0
  21. package/dist/src/card/builder.js.map +7 -0
  22. package/dist/src/card/cardkit.js +126 -0
  23. package/dist/src/card/cardkit.js.map +7 -0
  24. package/dist/src/card/flush-controller.js +107 -0
  25. package/dist/src/card/flush-controller.js.map +7 -0
  26. package/dist/src/card/markdown-style.js +57 -0
  27. package/dist/src/card/markdown-style.js.map +7 -0
  28. package/dist/src/card/reply-dispatcher-types.js +39 -0
  29. package/dist/src/card/reply-dispatcher-types.js.map +7 -0
  30. package/dist/src/card/reply-dispatcher.js +245 -0
  31. package/dist/src/card/reply-dispatcher.js.map +7 -0
  32. package/dist/src/card/reply-mode.js +29 -0
  33. package/dist/src/card/reply-mode.js.map +7 -0
  34. package/dist/src/card/streaming-card-controller.js +653 -0
  35. package/dist/src/card/streaming-card-controller.js.map +7 -0
  36. package/dist/src/card/unavailable-guard.js +76 -0
  37. package/dist/src/card/unavailable-guard.js.map +7 -0
  38. package/dist/src/channel/abort-detect.js +79 -0
  39. package/dist/src/channel/abort-detect.js.map +7 -0
  40. package/dist/src/channel/chat-queue.js +50 -0
  41. package/dist/src/channel/chat-queue.js.map +7 -0
  42. package/dist/src/channel/config-adapter.js +89 -0
  43. package/dist/src/channel/config-adapter.js.map +7 -0
  44. package/dist/src/channel/directory.js +133 -0
  45. package/dist/src/channel/directory.js.map +7 -0
  46. package/dist/src/channel/event-handlers.js +175 -0
  47. package/dist/src/channel/event-handlers.js.map +7 -0
  48. package/dist/src/channel/monitor.js +108 -0
  49. package/dist/src/channel/monitor.js.map +7 -0
  50. package/dist/src/channel/onboarding-config.js +76 -0
  51. package/dist/src/channel/onboarding-config.js.map +7 -0
  52. package/dist/src/channel/onboarding-migrate.js +55 -0
  53. package/dist/src/channel/onboarding-migrate.js.map +7 -0
  54. package/dist/src/channel/onboarding.js +285 -0
  55. package/dist/src/channel/onboarding.js.map +7 -0
  56. package/dist/src/channel/plugin.js +260 -0
  57. package/dist/src/channel/plugin.js.map +7 -0
  58. package/dist/src/channel/probe.js +14 -0
  59. package/dist/src/channel/probe.js.map +7 -0
  60. package/dist/src/channel/types.js +1 -0
  61. package/dist/src/channel/types.js.map +7 -0
  62. package/dist/src/commands/auth.js +73 -0
  63. package/dist/src/commands/auth.js.map +7 -0
  64. package/dist/src/commands/diagnose.js +658 -0
  65. package/dist/src/commands/diagnose.js.map +7 -0
  66. package/dist/src/commands/doctor.js +327 -0
  67. package/dist/src/commands/doctor.js.map +7 -0
  68. package/dist/src/commands/index.js +124 -0
  69. package/dist/src/commands/index.js.map +7 -0
  70. package/dist/src/core/accounts.js +129 -0
  71. package/dist/src/core/accounts.js.map +7 -0
  72. package/dist/src/core/agent-config.js +60 -0
  73. package/dist/src/core/agent-config.js.map +7 -0
  74. package/dist/src/core/api-error.js +55 -0
  75. package/dist/src/core/api-error.js.map +7 -0
  76. package/dist/src/core/app-owner-fallback.js +17 -0
  77. package/dist/src/core/app-owner-fallback.js.map +7 -0
  78. package/dist/src/core/app-scope-checker.js +95 -0
  79. package/dist/src/core/app-scope-checker.js.map +7 -0
  80. package/dist/src/core/auth-errors.js +120 -0
  81. package/dist/src/core/auth-errors.js.map +7 -0
  82. package/dist/src/core/chat-info-cache.js +102 -0
  83. package/dist/src/core/chat-info-cache.js.map +7 -0
  84. package/dist/src/core/config-schema.js +150 -0
  85. package/dist/src/core/config-schema.js.map +7 -0
  86. package/dist/src/core/device-flow.js +174 -0
  87. package/dist/src/core/device-flow.js.map +7 -0
  88. package/dist/src/core/feishu-fetch.js +12 -0
  89. package/dist/src/core/feishu-fetch.js.map +7 -0
  90. package/dist/src/core/footer-config.js +16 -0
  91. package/dist/src/core/footer-config.js.map +7 -0
  92. package/dist/src/core/lark-client.js +322 -0
  93. package/dist/src/core/lark-client.js.map +7 -0
  94. package/dist/src/core/lark-logger.js +92 -0
  95. package/dist/src/core/lark-logger.js.map +7 -0
  96. package/dist/src/core/lark-ticket.js +18 -0
  97. package/dist/src/core/lark-ticket.js.map +7 -0
  98. package/dist/src/core/message-unavailable.js +119 -0
  99. package/dist/src/core/message-unavailable.js.map +7 -0
  100. package/dist/src/core/owner-policy.js +25 -0
  101. package/dist/src/core/owner-policy.js.map +7 -0
  102. package/dist/src/core/permission-url.js +37 -0
  103. package/dist/src/core/permission-url.js.map +7 -0
  104. package/dist/src/core/project-auth.js +177 -0
  105. package/dist/src/core/project-auth.js.map +7 -0
  106. package/dist/src/core/project-oauth-flow.js +124 -0
  107. package/dist/src/core/project-oauth-flow.js.map +7 -0
  108. package/dist/src/core/project-token-store.js +172 -0
  109. package/dist/src/core/project-token-store.js.map +7 -0
  110. package/dist/src/core/raw-request.js +45 -0
  111. package/dist/src/core/raw-request.js.map +7 -0
  112. package/dist/src/core/scope-manager.js +62 -0
  113. package/dist/src/core/scope-manager.js.map +7 -0
  114. package/dist/src/core/security-check.js +118 -0
  115. package/dist/src/core/security-check.js.map +7 -0
  116. package/dist/src/core/shutdown-hooks.js +37 -0
  117. package/dist/src/core/shutdown-hooks.js.map +7 -0
  118. package/dist/src/core/targets.js +55 -0
  119. package/dist/src/core/targets.js.map +7 -0
  120. package/dist/src/core/token-store.js +215 -0
  121. package/dist/src/core/token-store.js.map +7 -0
  122. package/dist/src/core/tool-client.js +335 -0
  123. package/dist/src/core/tool-client.js.map +7 -0
  124. package/dist/src/core/tool-scopes.js +207 -0
  125. package/dist/src/core/tool-scopes.js.map +7 -0
  126. package/dist/src/core/tools-config.js +57 -0
  127. package/dist/src/core/tools-config.js.map +7 -0
  128. package/dist/src/core/types.js +1 -0
  129. package/dist/src/core/types.js.map +7 -0
  130. package/dist/src/core/uat-client.js +124 -0
  131. package/dist/src/core/uat-client.js.map +7 -0
  132. package/dist/src/core/version.js +27 -0
  133. package/dist/src/core/version.js.map +7 -0
  134. package/dist/src/messaging/converters/audio.js +19 -0
  135. package/dist/src/messaging/converters/audio.js.map +7 -0
  136. package/dist/src/messaging/converters/calendar.js +46 -0
  137. package/dist/src/messaging/converters/calendar.js.map +7 -0
  138. package/dist/src/messaging/converters/content-converter.js +61 -0
  139. package/dist/src/messaging/converters/content-converter.js.map +7 -0
  140. package/dist/src/messaging/converters/file.js +18 -0
  141. package/dist/src/messaging/converters/file.js.map +7 -0
  142. package/dist/src/messaging/converters/folder.js +18 -0
  143. package/dist/src/messaging/converters/folder.js.map +7 -0
  144. package/dist/src/messaging/converters/hongbao.js +14 -0
  145. package/dist/src/messaging/converters/hongbao.js.map +7 -0
  146. package/dist/src/messaging/converters/image.js +16 -0
  147. package/dist/src/messaging/converters/image.js.map +7 -0
  148. package/dist/src/messaging/converters/index.js +48 -0
  149. package/dist/src/messaging/converters/index.js.map +7 -0
  150. package/dist/src/messaging/converters/interactive/card-converter.js +1040 -0
  151. package/dist/src/messaging/converters/interactive/card-converter.js.map +7 -0
  152. package/dist/src/messaging/converters/interactive/card-utils.js +36 -0
  153. package/dist/src/messaging/converters/interactive/card-utils.js.map +7 -0
  154. package/dist/src/messaging/converters/interactive/index.js +19 -0
  155. package/dist/src/messaging/converters/interactive/index.js.map +7 -0
  156. package/dist/src/messaging/converters/interactive/legacy.js +53 -0
  157. package/dist/src/messaging/converters/interactive/legacy.js.map +7 -0
  158. package/dist/src/messaging/converters/interactive/types.js +23 -0
  159. package/dist/src/messaging/converters/interactive/types.js.map +7 -0
  160. package/dist/src/messaging/converters/location.js +17 -0
  161. package/dist/src/messaging/converters/location.js.map +7 -0
  162. package/dist/src/messaging/converters/merge-forward.js +143 -0
  163. package/dist/src/messaging/converters/merge-forward.js.map +7 -0
  164. package/dist/src/messaging/converters/post.js +113 -0
  165. package/dist/src/messaging/converters/post.js.map +7 -0
  166. package/dist/src/messaging/converters/share.js +22 -0
  167. package/dist/src/messaging/converters/share.js.map +7 -0
  168. package/dist/src/messaging/converters/sticker.js +16 -0
  169. package/dist/src/messaging/converters/sticker.js.map +7 -0
  170. package/dist/src/messaging/converters/system.js +25 -0
  171. package/dist/src/messaging/converters/system.js.map +7 -0
  172. package/dist/src/messaging/converters/text.js +12 -0
  173. package/dist/src/messaging/converters/text.js.map +7 -0
  174. package/dist/src/messaging/converters/todo.js +37 -0
  175. package/dist/src/messaging/converters/todo.js.map +7 -0
  176. package/dist/src/messaging/converters/types.js +1 -0
  177. package/dist/src/messaging/converters/types.js.map +7 -0
  178. package/dist/src/messaging/converters/unknown.js +13 -0
  179. package/dist/src/messaging/converters/unknown.js.map +7 -0
  180. package/dist/src/messaging/converters/utils.js +35 -0
  181. package/dist/src/messaging/converters/utils.js.map +7 -0
  182. package/dist/src/messaging/converters/video-chat.js +21 -0
  183. package/dist/src/messaging/converters/video-chat.js.map +7 -0
  184. package/dist/src/messaging/converters/video.js +30 -0
  185. package/dist/src/messaging/converters/video.js.map +7 -0
  186. package/dist/src/messaging/converters/vote.js +24 -0
  187. package/dist/src/messaging/converters/vote.js.map +7 -0
  188. package/dist/src/messaging/inbound/dedup.js +82 -0
  189. package/dist/src/messaging/inbound/dedup.js.map +7 -0
  190. package/dist/src/messaging/inbound/dispatch-builders.js +98 -0
  191. package/dist/src/messaging/inbound/dispatch-builders.js.map +7 -0
  192. package/dist/src/messaging/inbound/dispatch-commands.js +94 -0
  193. package/dist/src/messaging/inbound/dispatch-commands.js.map +7 -0
  194. package/dist/src/messaging/inbound/dispatch-context.js +96 -0
  195. package/dist/src/messaging/inbound/dispatch-context.js.map +7 -0
  196. package/dist/src/messaging/inbound/dispatch.js +150 -0
  197. package/dist/src/messaging/inbound/dispatch.js.map +7 -0
  198. package/dist/src/messaging/inbound/enrich.js +137 -0
  199. package/dist/src/messaging/inbound/enrich.js.map +7 -0
  200. package/dist/src/messaging/inbound/gate-effects.js +28 -0
  201. package/dist/src/messaging/inbound/gate-effects.js.map +7 -0
  202. package/dist/src/messaging/inbound/gate.js +163 -0
  203. package/dist/src/messaging/inbound/gate.js.map +7 -0
  204. package/dist/src/messaging/inbound/handler.js +132 -0
  205. package/dist/src/messaging/inbound/handler.js.map +7 -0
  206. package/dist/src/messaging/inbound/media-resolver.js +70 -0
  207. package/dist/src/messaging/inbound/media-resolver.js.map +7 -0
  208. package/dist/src/messaging/inbound/mention.js +50 -0
  209. package/dist/src/messaging/inbound/mention.js.map +7 -0
  210. package/dist/src/messaging/inbound/parse-io.js +41 -0
  211. package/dist/src/messaging/inbound/parse-io.js.map +7 -0
  212. package/dist/src/messaging/inbound/parse.js +79 -0
  213. package/dist/src/messaging/inbound/parse.js.map +7 -0
  214. package/dist/src/messaging/inbound/permission.js +30 -0
  215. package/dist/src/messaging/inbound/permission.js.map +7 -0
  216. package/dist/src/messaging/inbound/policy.js +83 -0
  217. package/dist/src/messaging/inbound/policy.js.map +7 -0
  218. package/dist/src/messaging/inbound/reaction-handler.js +162 -0
  219. package/dist/src/messaging/inbound/reaction-handler.js.map +7 -0
  220. package/dist/src/messaging/inbound/user-name-cache.js +172 -0
  221. package/dist/src/messaging/inbound/user-name-cache.js.map +7 -0
  222. package/dist/src/messaging/outbound/actions.js +239 -0
  223. package/dist/src/messaging/outbound/actions.js.map +7 -0
  224. package/dist/src/messaging/outbound/chat-manage.js +74 -0
  225. package/dist/src/messaging/outbound/chat-manage.js.map +7 -0
  226. package/dist/src/messaging/outbound/deliver.js +162 -0
  227. package/dist/src/messaging/outbound/deliver.js.map +7 -0
  228. package/dist/src/messaging/outbound/fetch.js +7 -0
  229. package/dist/src/messaging/outbound/fetch.js.map +7 -0
  230. package/dist/src/messaging/outbound/forward.js +31 -0
  231. package/dist/src/messaging/outbound/forward.js.map +7 -0
  232. package/dist/src/messaging/outbound/media-url-utils.js +101 -0
  233. package/dist/src/messaging/outbound/media-url-utils.js.map +7 -0
  234. package/dist/src/messaging/outbound/media.js +463 -0
  235. package/dist/src/messaging/outbound/media.js.map +7 -0
  236. package/dist/src/messaging/outbound/outbound.js +95 -0
  237. package/dist/src/messaging/outbound/outbound.js.map +7 -0
  238. package/dist/src/messaging/outbound/reactions.js +312 -0
  239. package/dist/src/messaging/outbound/reactions.js.map +7 -0
  240. package/dist/src/messaging/outbound/send.js +194 -0
  241. package/dist/src/messaging/outbound/send.js.map +7 -0
  242. package/dist/src/messaging/outbound/typing.js +77 -0
  243. package/dist/src/messaging/outbound/typing.js.map +7 -0
  244. package/dist/src/messaging/shared/message-lookup.js +84 -0
  245. package/dist/src/messaging/shared/message-lookup.js.map +7 -0
  246. package/dist/src/messaging/types.js +1 -0
  247. package/dist/src/messaging/types.js.map +7 -0
  248. package/dist/src/tools/auto-auth.js +714 -0
  249. package/dist/src/tools/auto-auth.js.map +7 -0
  250. package/dist/src/tools/helpers.js +133 -0
  251. package/dist/src/tools/helpers.js.map +7 -0
  252. package/dist/src/tools/mcp/doc/create.js +35 -0
  253. package/dist/src/tools/mcp/doc/create.js.map +7 -0
  254. package/dist/src/tools/mcp/doc/fetch.js +33 -0
  255. package/dist/src/tools/mcp/doc/fetch.js.map +7 -0
  256. package/dist/src/tools/mcp/doc/index.js +32 -0
  257. package/dist/src/tools/mcp/doc/index.js.map +7 -0
  258. package/dist/src/tools/mcp/doc/update.js +61 -0
  259. package/dist/src/tools/mcp/doc/update.js.map +7 -0
  260. package/dist/src/tools/mcp/project/endpoint.js +25 -0
  261. package/dist/src/tools/mcp/project/endpoint.js.map +7 -0
  262. package/dist/src/tools/mcp/project/index.js +27 -0
  263. package/dist/src/tools/mcp/project/index.js.map +7 -0
  264. package/dist/src/tools/mcp/project/tools.js +579 -0
  265. package/dist/src/tools/mcp/project/tools.js.map +7 -0
  266. package/dist/src/tools/mcp/shared.js +170 -0
  267. package/dist/src/tools/mcp/shared.js.map +7 -0
  268. package/dist/src/tools/oapi/bitable/app-table-field.js +244 -0
  269. package/dist/src/tools/oapi/bitable/app-table-field.js.map +7 -0
  270. package/dist/src/tools/oapi/bitable/app-table-record.js +501 -0
  271. package/dist/src/tools/oapi/bitable/app-table-record.js.map +7 -0
  272. package/dist/src/tools/oapi/bitable/app-table-view.js +226 -0
  273. package/dist/src/tools/oapi/bitable/app-table-view.js.map +7 -0
  274. package/dist/src/tools/oapi/bitable/app-table.js +278 -0
  275. package/dist/src/tools/oapi/bitable/app-table.js.map +7 -0
  276. package/dist/src/tools/oapi/bitable/app.js +200 -0
  277. package/dist/src/tools/oapi/bitable/app.js.map +7 -0
  278. package/dist/src/tools/oapi/bitable/index.js +13 -0
  279. package/dist/src/tools/oapi/bitable/index.js.map +7 -0
  280. package/dist/src/tools/oapi/calendar/calendar.js +131 -0
  281. package/dist/src/tools/oapi/calendar/calendar.js.map +7 -0
  282. package/dist/src/tools/oapi/calendar/event-attendee.js +301 -0
  283. package/dist/src/tools/oapi/calendar/event-attendee.js.map +7 -0
  284. package/dist/src/tools/oapi/calendar/event.js +834 -0
  285. package/dist/src/tools/oapi/calendar/event.js.map +7 -0
  286. package/dist/src/tools/oapi/calendar/freebusy.js +111 -0
  287. package/dist/src/tools/oapi/calendar/freebusy.js.map +7 -0
  288. package/dist/src/tools/oapi/calendar/index.js +11 -0
  289. package/dist/src/tools/oapi/calendar/index.js.map +7 -0
  290. package/dist/src/tools/oapi/chat/chat.js +132 -0
  291. package/dist/src/tools/oapi/chat/chat.js.map +7 -0
  292. package/dist/src/tools/oapi/chat/index.js +11 -0
  293. package/dist/src/tools/oapi/chat/index.js.map +7 -0
  294. package/dist/src/tools/oapi/chat/members.js +83 -0
  295. package/dist/src/tools/oapi/chat/members.js.map +7 -0
  296. package/dist/src/tools/oapi/common/get-user.js +95 -0
  297. package/dist/src/tools/oapi/common/get-user.js.map +7 -0
  298. package/dist/src/tools/oapi/common/index.js +7 -0
  299. package/dist/src/tools/oapi/common/index.js.map +7 -0
  300. package/dist/src/tools/oapi/common/search-user.js +67 -0
  301. package/dist/src/tools/oapi/common/search-user.js.map +7 -0
  302. package/dist/src/tools/oapi/drive/doc-comments.js +310 -0
  303. package/dist/src/tools/oapi/drive/doc-comments.js.map +7 -0
  304. package/dist/src/tools/oapi/drive/doc-media.js +314 -0
  305. package/dist/src/tools/oapi/drive/doc-media.js.map +7 -0
  306. package/dist/src/tools/oapi/drive/file.js +548 -0
  307. package/dist/src/tools/oapi/drive/file.js.map +7 -0
  308. package/dist/src/tools/oapi/drive/index.js +29 -0
  309. package/dist/src/tools/oapi/drive/index.js.map +7 -0
  310. package/dist/src/tools/oapi/helpers.js +199 -0
  311. package/dist/src/tools/oapi/helpers.js.map +7 -0
  312. package/dist/src/tools/oapi/im/format-messages.js +128 -0
  313. package/dist/src/tools/oapi/im/format-messages.js.map +7 -0
  314. package/dist/src/tools/oapi/im/index.js +15 -0
  315. package/dist/src/tools/oapi/im/index.js.map +7 -0
  316. package/dist/src/tools/oapi/im/message-read.js +404 -0
  317. package/dist/src/tools/oapi/im/message-read.js.map +7 -0
  318. package/dist/src/tools/oapi/im/message.js +179 -0
  319. package/dist/src/tools/oapi/im/message.js.map +7 -0
  320. package/dist/src/tools/oapi/im/resource.js +126 -0
  321. package/dist/src/tools/oapi/im/resource.js.map +7 -0
  322. package/dist/src/tools/oapi/im/time-utils.js +169 -0
  323. package/dist/src/tools/oapi/im/time-utils.js.map +7 -0
  324. package/dist/src/tools/oapi/im/user-name-uat.js +103 -0
  325. package/dist/src/tools/oapi/im/user-name-uat.js.map +7 -0
  326. package/dist/src/tools/oapi/index.js +56 -0
  327. package/dist/src/tools/oapi/index.js.map +7 -0
  328. package/dist/src/tools/oapi/sdk-types.js +1 -0
  329. package/dist/src/tools/oapi/sdk-types.js.map +7 -0
  330. package/dist/src/tools/oapi/search/doc-search.js +215 -0
  331. package/dist/src/tools/oapi/search/doc-search.js.map +7 -0
  332. package/dist/src/tools/oapi/search/index.js +25 -0
  333. package/dist/src/tools/oapi/search/index.js.map +7 -0
  334. package/dist/src/tools/oapi/sheets/index.js +25 -0
  335. package/dist/src/tools/oapi/sheets/index.js.map +7 -0
  336. package/dist/src/tools/oapi/sheets/sheet.js +652 -0
  337. package/dist/src/tools/oapi/sheets/sheet.js.map +7 -0
  338. package/dist/src/tools/oapi/task/comment.js +151 -0
  339. package/dist/src/tools/oapi/task/comment.js.map +7 -0
  340. package/dist/src/tools/oapi/task/index.js +11 -0
  341. package/dist/src/tools/oapi/task/index.js.map +7 -0
  342. package/dist/src/tools/oapi/task/subtask.js +175 -0
  343. package/dist/src/tools/oapi/task/subtask.js.map +7 -0
  344. package/dist/src/tools/oapi/task/task.js +405 -0
  345. package/dist/src/tools/oapi/task/task.js.map +7 -0
  346. package/dist/src/tools/oapi/task/tasklist.js +366 -0
  347. package/dist/src/tools/oapi/task/tasklist.js.map +7 -0
  348. package/dist/src/tools/oapi/wiki/index.js +27 -0
  349. package/dist/src/tools/oapi/wiki/index.js.map +7 -0
  350. package/dist/src/tools/oapi/wiki/space-node.js +311 -0
  351. package/dist/src/tools/oapi/wiki/space-node.js.map +7 -0
  352. package/dist/src/tools/oapi/wiki/space.js +148 -0
  353. package/dist/src/tools/oapi/wiki/space.js.map +7 -0
  354. package/dist/src/tools/oauth-batch-auth.js +125 -0
  355. package/dist/src/tools/oauth-batch-auth.js.map +7 -0
  356. package/dist/src/tools/oauth-cards.js +269 -0
  357. package/dist/src/tools/oauth-cards.js.map +7 -0
  358. package/dist/src/tools/oauth.js +538 -0
  359. package/dist/src/tools/oauth.js.map +7 -0
  360. package/dist/src/tools/onboarding-auth.js +101 -0
  361. package/dist/src/tools/onboarding-auth.js.map +7 -0
  362. package/dist/src/tools/project-oauth.js +305 -0
  363. package/dist/src/tools/project-oauth.js.map +7 -0
  364. package/dist/src/tools/tat/im/index.js +9 -0
  365. package/dist/src/tools/tat/im/index.js.map +7 -0
  366. package/dist/src/tools/tat/im/resource.js +123 -0
  367. package/dist/src/tools/tat/im/resource.js.map +7 -0
  368. package/package.json +64 -0
@@ -0,0 +1,101 @@
1
+ import { getLarkAccount } from "../core/accounts";
2
+ import { LarkClient } from "../core/lark-client";
3
+ import { getAppGrantedScopes } from "../core/app-scope-checker";
4
+ import { getAppOwnerFallback } from "../core/app-owner-fallback";
5
+ import { executeAuthorize } from "./oauth";
6
+ import { larkLogger } from "../core/lark-logger";
7
+ import { filterSensitiveScopes } from "../core/tool-scopes";
8
+ const log = larkLogger("tools/onboarding-auth");
9
+ const MAX_SCOPES_PER_BATCH = 100;
10
+ async function triggerOnboarding(params) {
11
+ const { cfg, userOpenId, accountId } = params;
12
+ const acct = getLarkAccount(cfg, accountId);
13
+ if (!acct.configured) {
14
+ log.warn(`account ${accountId} not configured, skipping`);
15
+ return;
16
+ }
17
+ const sdk = LarkClient.fromAccount(acct).sdk;
18
+ const { appId } = acct;
19
+ const ownerOpenId = await getAppOwnerFallback(acct, sdk);
20
+ if (!ownerOpenId) {
21
+ log.info(`app ${appId} has no owner info, skipping`);
22
+ return;
23
+ }
24
+ if (userOpenId !== ownerOpenId) {
25
+ log.info(`user ${userOpenId} is not app owner (${ownerOpenId}), skipping`);
26
+ return;
27
+ }
28
+ log.info(`user ${userOpenId} is app owner, starting OAuth`);
29
+ let allUserScopes;
30
+ try {
31
+ allUserScopes = await getAppGrantedScopes(sdk, appId, "user");
32
+ } catch (err) {
33
+ log.warn(`failed to get app granted scopes: ${err}`);
34
+ return;
35
+ }
36
+ allUserScopes = filterSensitiveScopes(allUserScopes);
37
+ if (allUserScopes.length === 0) {
38
+ log.info("no user scopes configured, skipping");
39
+ return;
40
+ }
41
+ const batches = [];
42
+ for (let i = 0; i < allUserScopes.length; i += MAX_SCOPES_PER_BATCH) {
43
+ batches.push(allUserScopes.slice(i, i + MAX_SCOPES_PER_BATCH));
44
+ }
45
+ log.info(`${allUserScopes.length} user scopes, ${batches.length} batch(es)`);
46
+ const startBatch = async (batchIndex) => {
47
+ if (batchIndex >= batches.length) {
48
+ log.info("all batches completed");
49
+ return;
50
+ }
51
+ const batch = batches[batchIndex];
52
+ const scope = batch.join(" ");
53
+ let batchInfo = "";
54
+ if (batches.length > 1) {
55
+ batchInfo = `
56
+
57
+ \u{1F4CB} \u6388\u6743\u8FDB\u5EA6\uFF1A\u7B2C ${batchIndex + 1}/${batches.length} \u6279\uFF08\u672C\u6279 ${batch.length} \u4E2A\u6743\u9650\uFF0C\u5171 ${allUserScopes.length} \u4E2A\uFF09`;
58
+ if (batchIndex < batches.length - 1) {
59
+ batchInfo += `
60
+ \u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\u3002`;
61
+ } else {
62
+ batchInfo += `
63
+ \u8FD9\u662F\u6700\u540E\u4E00\u6279\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528\u6240\u6709\u529F\u80FD\u3002`;
64
+ }
65
+ }
66
+ const ticket = {
67
+ messageId: `onboarding:${Date.now()}`,
68
+ chatId: userOpenId,
69
+ accountId,
70
+ startTime: Date.now(),
71
+ senderOpenId: userOpenId,
72
+ chatType: "p2p"
73
+ };
74
+ log.info(`starting batch ${batchIndex + 1}/${batches.length}, scopes=${batch.length}`);
75
+ try {
76
+ await executeAuthorize({
77
+ account: acct,
78
+ senderOpenId: userOpenId,
79
+ scope,
80
+ isBatchAuth: true,
81
+ totalAppScopes: allUserScopes.length,
82
+ alreadyGranted: batchIndex * MAX_SCOPES_PER_BATCH,
83
+ batchInfo,
84
+ skipSyntheticMessage: true,
85
+ cfg,
86
+ ticket,
87
+ onAuthComplete: async () => {
88
+ log.info(`batch ${batchIndex + 1}/${batches.length} auth completed`);
89
+ await startBatch(batchIndex + 1);
90
+ }
91
+ });
92
+ } catch (err) {
93
+ log.error(`batch ${batchIndex + 1} failed: ${err}`);
94
+ }
95
+ };
96
+ await startBatch(0);
97
+ }
98
+ export {
99
+ triggerOnboarding
100
+ };
101
+ //# sourceMappingURL=onboarding-auth.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/tools/onboarding-auth.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Onboarding \u9884\u6388\u6743\u6A21\u5757\u3002\n *\n * \u914D\u5BF9\u540E\u81EA\u52A8\u53D1\u8D77 OAuth Device Flow\uFF0C\u5F15\u5BFC\u5E94\u7528 owner \u5B8C\u6210\u7528\u6237\u6388\u6743\u3002\n * \u4EC5\u5F53\u914D\u5BF9\u7528\u6237 === \u5E94\u7528 owner \u65F6\u89E6\u53D1\u3002\n *\n * \u98DE\u4E66\u9650\u5236\uFF1A\u5355\u6B21 OAuth \u6700\u591A 50 \u4E2A scope\u3002\n * \u8D85\u8FC7 50 \u4E2A\u65F6\u81EA\u52A8\u5206\u6279\u5904\u7406\uFF0C\u6BCF\u6279\u6388\u6743\u5B8C\u6210\u540E\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\uFF08\u94FE\u5F0F\u89E6\u53D1\uFF09\u3002\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { getLarkAccount } from '../core/accounts';\nimport { LarkClient } from '../core/lark-client';\nimport { getAppGrantedScopes } from '../core/app-scope-checker';\nimport { getAppOwnerFallback } from '../core/app-owner-fallback';\nimport { executeAuthorize } from './oauth';\nimport { larkLogger } from '../core/lark-logger';\nimport { filterSensitiveScopes } from '../core/tool-scopes';\n\nconst log = larkLogger('tools/onboarding-auth');\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_SCOPES_PER_BATCH = 100;\n\n// ---------------------------------------------------------------------------\n// Trigger onboarding\n// ---------------------------------------------------------------------------\n\n/**\n * \u914D\u5BF9\u540E\u89E6\u53D1 onboarding OAuth \u6388\u6743\u3002\n *\n * \u6D41\u7A0B\uFF1A\n * 1. \u68C0\u67E5 userOpenId === \u5E94\u7528 owner\uFF0C\u4E0D\u5339\u914D\u5219\u9759\u9ED8\u8DF3\u8FC7\n * 2. \u8BFB\u53D6 onboarding-scopes.json \u4E2D\u7684 user scope \u5217\u8868\n * 3. \u5206\u6279\u5904\u7406\uFF08\u6BCF\u6279\u6700\u591A 50 \u4E2A\uFF09\uFF0C\u7B2C\u4E00\u6279\u76F4\u63A5\u53D1\u8D77 OAuth Device Flow\n * 4. \u6BCF\u6279\u6388\u6743\u5B8C\u6210\u540E\u901A\u8FC7 onAuthComplete \u56DE\u8C03\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\n */\nexport async function triggerOnboarding(params: {\n cfg: ClawdbotConfig;\n userOpenId: string;\n accountId: string;\n}): Promise<void> {\n const { cfg, userOpenId, accountId } = params;\n\n const acct = getLarkAccount(cfg, accountId);\n if (!acct.configured) {\n log.warn(`account ${accountId} not configured, skipping`);\n return;\n }\n\n const sdk = LarkClient.fromAccount(acct).sdk;\n const { appId } = acct;\n\n // 1. \u68C0\u67E5 userOpenId === \u5E94\u7528 owner\uFF08\u7EDF\u4E00\u8D70 getAppOwnerFallback\uFF09\n const ownerOpenId = await getAppOwnerFallback(acct, sdk);\n if (!ownerOpenId) {\n log.info(`app ${appId} has no owner info, skipping`);\n return;\n }\n if (userOpenId !== ownerOpenId) {\n log.info(`user ${userOpenId} is not app owner (${ownerOpenId}), skipping`);\n return;\n }\n\n log.info(`user ${userOpenId} is app owner, starting OAuth`);\n\n // 3. \u52A8\u6001\u83B7\u53D6\u5E94\u7528\u5DF2\u5F00\u901A\u7684 user scope \u5217\u8868\n let allUserScopes: string[];\n try {\n allUserScopes = await getAppGrantedScopes(sdk, appId, 'user');\n } catch (err) {\n log.warn(`failed to get app granted scopes: ${err}`);\n return;\n }\n\n // \u8FC7\u6EE4\u6389\u654F\u611F scope\n allUserScopes = filterSensitiveScopes(allUserScopes);\n\n if (allUserScopes.length === 0) {\n log.info('no user scopes configured, skipping');\n return;\n }\n\n // 4. \u5206\u6279\n const batches: string[][] = [];\n for (let i = 0; i < allUserScopes.length; i += MAX_SCOPES_PER_BATCH) {\n batches.push(allUserScopes.slice(i, i + MAX_SCOPES_PER_BATCH));\n }\n\n log.info(`${allUserScopes.length} user scopes, ${batches.length} batch(es)`);\n\n // 5. \u94FE\u5F0F\u53D1\u8D77\u6388\u6743\uFF08\u7B2C\u4E00\u6279\u540C\u6B65\u53D1\u8D77\uFF0C\u540E\u7EED\u6279\u6B21\u7531 onAuthComplete \u56DE\u8C03\u89E6\u53D1\uFF09\n const startBatch = async (batchIndex: number): Promise<void> => {\n if (batchIndex >= batches.length) {\n log.info('all batches completed');\n return;\n }\n\n const batch = batches[batchIndex];\n const scope = batch.join(' ');\n\n let batchInfo = '';\n if (batches.length > 1) {\n batchInfo =\n `\\n\\n\uD83D\uDCCB \u6388\u6743\u8FDB\u5EA6\uFF1A\u7B2C ${batchIndex + 1}/${batches.length} \u6279` +\n `\uFF08\u672C\u6279 ${batch.length} \u4E2A\u6743\u9650\uFF0C\u5171 ${allUserScopes.length} \u4E2A\uFF09`;\n if (batchIndex < batches.length - 1) {\n batchInfo += `\\n\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u53D1\u8D77\u4E0B\u4E00\u6279\u3002`;\n } else {\n batchInfo += `\\n\u8FD9\u662F\u6700\u540E\u4E00\u6279\uFF0C\u6388\u6743\u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528\u6240\u6709\u529F\u80FD\u3002`;\n }\n }\n\n const ticket = {\n messageId: `onboarding:${Date.now()}`,\n chatId: userOpenId,\n accountId,\n startTime: Date.now(),\n senderOpenId: userOpenId,\n chatType: 'p2p' as const,\n };\n\n log.info(`starting batch ${batchIndex + 1}/${batches.length}, scopes=${batch.length}`);\n\n try {\n await executeAuthorize({\n account: acct,\n senderOpenId: userOpenId,\n scope,\n isBatchAuth: true,\n totalAppScopes: allUserScopes.length,\n alreadyGranted: batchIndex * MAX_SCOPES_PER_BATCH,\n batchInfo,\n skipSyntheticMessage: true,\n cfg,\n ticket,\n onAuthComplete: async () => {\n log.info(`batch ${batchIndex + 1}/${batches.length} auth completed`);\n await startBatch(batchIndex + 1);\n },\n });\n } catch (err) {\n log.error(`batch ${batchIndex + 1} failed: ${err}`);\n }\n };\n\n await startBatch(0);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,305 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getTicket } from "../core/lark-ticket";
3
+ import { larkLogger } from "../core/lark-logger";
4
+ import { formatLarkError } from "../core/api-error";
5
+ import {
6
+ startLocalAuthFlow,
7
+ prepareRemoteAuth,
8
+ completeRemoteAuth
9
+ } from "../core/project-auth";
10
+ import {
11
+ getProjectStoredToken,
12
+ setProjectStoredToken,
13
+ removeProjectStoredToken,
14
+ projectTokenStatus,
15
+ getProjectClientId
16
+ } from "../core/project-token-store";
17
+ import { getProjectMcpEndpoint } from "../tools/mcp/project/endpoint";
18
+ import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit";
19
+ import { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from "./oauth-cards";
20
+ import { json } from "./oapi/helpers";
21
+ const log = larkLogger("tools/project-oauth");
22
+ const FeishuProjectOAuthSchema = Type.Object(
23
+ {
24
+ action: Type.Union(
25
+ [
26
+ Type.Literal("authorize"),
27
+ Type.Literal("complete_auth"),
28
+ Type.Literal("status"),
29
+ Type.Literal("revoke")
30
+ ],
31
+ {
32
+ description: "authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743"
33
+ }
34
+ ),
35
+ callback_url: Type.Optional(
36
+ Type.String({
37
+ description: "\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL"
38
+ })
39
+ )
40
+ },
41
+ {
42
+ description: "\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002"
43
+ }
44
+ );
45
+ const pendingLocalFlows = /* @__PURE__ */ new Map();
46
+ const pendingRemoteSessions = /* @__PURE__ */ new Map();
47
+ function fk(userOpenId) {
48
+ return `project:${userOpenId}`;
49
+ }
50
+ function buildTokenFromOAuth(userOpenId, clientId, tokens) {
51
+ const now = Date.now();
52
+ const expiresIn = tokens.expires_in ?? 7200;
53
+ const refreshExpiresIn = 30 * 24 * 3600;
54
+ return {
55
+ userOpenId,
56
+ appId: clientId,
57
+ accessToken: tokens.access_token,
58
+ refreshToken: tokens.refresh_token ?? "",
59
+ expiresAt: now + expiresIn * 1e3,
60
+ refreshExpiresAt: now + refreshExpiresIn * 1e3,
61
+ scope: tokens.scope ?? "",
62
+ grantedAt: now
63
+ };
64
+ }
65
+ async function isAlreadyAuthorized(senderOpenId) {
66
+ const clientId = await getProjectClientId(senderOpenId);
67
+ if (!clientId) return false;
68
+ const existing = await getProjectStoredToken(clientId, senderOpenId);
69
+ return !!existing && projectTokenStatus(existing) !== "expired";
70
+ }
71
+ async function tryLocalAuth(mcpEndpoint) {
72
+ try {
73
+ return await startLocalAuthFlow(mcpEndpoint);
74
+ } catch (err) {
75
+ log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);
76
+ return null;
77
+ }
78
+ }
79
+ function registerFeishuProjectOAuthTool(api) {
80
+ if (!api.config) return;
81
+ const cfg = api.config;
82
+ api.registerTool(
83
+ {
84
+ name: "feishu_project_oauth",
85
+ label: "Feishu Project OAuth",
86
+ description: "\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002",
87
+ parameters: FeishuProjectOAuthSchema,
88
+ async execute(_toolCallId, params) {
89
+ const p = params;
90
+ const ticket = getTicket();
91
+ const senderOpenId = ticket?.senderOpenId;
92
+ if (!senderOpenId) {
93
+ return json({
94
+ error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002"
95
+ });
96
+ }
97
+ const accountId = ticket.accountId;
98
+ const mcpEndpoint = getProjectMcpEndpoint(cfg);
99
+ try {
100
+ switch (p.action) {
101
+ // ---------------------------------------------------------------
102
+ // STATUS
103
+ // ---------------------------------------------------------------
104
+ case "status": {
105
+ const clientId = await getProjectClientId(senderOpenId);
106
+ if (!clientId) {
107
+ return json({
108
+ authorized: false,
109
+ message: "\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
110
+ });
111
+ }
112
+ const existing = await getProjectStoredToken(clientId, senderOpenId);
113
+ if (!existing) {
114
+ return json({
115
+ authorized: false,
116
+ message: "\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
117
+ });
118
+ }
119
+ const status = projectTokenStatus(existing);
120
+ return json({
121
+ authorized: status !== "expired",
122
+ token_status: status,
123
+ scope: existing.scope,
124
+ granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : void 0,
125
+ expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : void 0
126
+ });
127
+ }
128
+ // ---------------------------------------------------------------
129
+ // AUTHORIZE(自动选择本地/远程)
130
+ // ---------------------------------------------------------------
131
+ case "authorize": {
132
+ if (await isAlreadyAuthorized(senderOpenId)) {
133
+ return json({
134
+ success: true,
135
+ message: "\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002",
136
+ authorized: true
137
+ });
138
+ }
139
+ const chatId = ticket.chatId;
140
+ if (!chatId) {
141
+ return json({ error: "\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807" });
142
+ }
143
+ const key = fk(senderOpenId);
144
+ const oldLocal = pendingLocalFlows.get(key);
145
+ if (oldLocal) {
146
+ oldLocal.superseded = true;
147
+ oldLocal.controller.abort();
148
+ await oldLocal.close().catch(() => {
149
+ });
150
+ pendingLocalFlows.delete(key);
151
+ }
152
+ pendingRemoteSessions.delete(key);
153
+ const local = await tryLocalAuth(mcpEndpoint);
154
+ if (local) {
155
+ const { session: session2, waitForCode, port, close } = local;
156
+ const authCard2 = buildAuthCard({
157
+ verificationUriComplete: session2.authorizationUrl,
158
+ expiresMin: 5
159
+ });
160
+ const cardId2 = await createCardEntity({ cfg, card: authCard2, accountId });
161
+ if (!cardId2) {
162
+ await close();
163
+ return json({ error: "\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25" });
164
+ }
165
+ await sendCardByCardId({
166
+ cfg,
167
+ to: chatId,
168
+ cardId: cardId2,
169
+ replyToMessageId: ticket.messageId?.startsWith("om_") ? ticket.messageId : void 0,
170
+ replyInThread: Boolean(ticket.threadId),
171
+ accountId
172
+ });
173
+ const abortController = new AbortController();
174
+ let seq = 1;
175
+ const currentFlow = {
176
+ controller: abortController,
177
+ cardId: cardId2,
178
+ sequence: seq,
179
+ superseded: false,
180
+ close
181
+ };
182
+ pendingLocalFlows.set(key, currentFlow);
183
+ waitForCode().then(async (result) => {
184
+ if (currentFlow.superseded) return;
185
+ const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);
186
+ await setProjectStoredToken(storedToken);
187
+ try {
188
+ await updateCardKitCardForAuth({
189
+ cfg,
190
+ cardId: cardId2,
191
+ card: buildAuthSuccessCard(),
192
+ sequence: ++seq,
193
+ accountId
194
+ });
195
+ } catch (e) {
196
+ log.warn(`failed to update project auth card to success: ${e}`);
197
+ }
198
+ }).catch(async (err) => {
199
+ if (currentFlow.superseded) return;
200
+ log.error(`project local auth failed: ${err}`);
201
+ try {
202
+ const msg = err instanceof Error ? err.message : String(err);
203
+ await updateCardKitCardForAuth({
204
+ cfg,
205
+ cardId: cardId2,
206
+ card: buildAuthFailedCard(msg),
207
+ sequence: ++seq,
208
+ accountId
209
+ });
210
+ } catch (e) {
211
+ log.warn(`failed to update project auth card to failure: ${e}`);
212
+ }
213
+ }).finally(async () => {
214
+ await close().catch(() => {
215
+ });
216
+ if (pendingLocalFlows.get(key) === currentFlow) {
217
+ pendingLocalFlows.delete(key);
218
+ }
219
+ });
220
+ return json({
221
+ success: true,
222
+ mode: "local",
223
+ message: "\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002",
224
+ awaiting_authorization: true,
225
+ callback_port: port
226
+ });
227
+ }
228
+ const session = await prepareRemoteAuth(mcpEndpoint);
229
+ pendingRemoteSessions.set(key, session);
230
+ const authCard = buildAuthCard({
231
+ verificationUriComplete: session.authorizationUrl,
232
+ expiresMin: 10
233
+ });
234
+ const cardId = await createCardEntity({ cfg, card: authCard, accountId });
235
+ if (cardId) {
236
+ await sendCardByCardId({
237
+ cfg,
238
+ to: chatId,
239
+ cardId,
240
+ replyToMessageId: ticket.messageId?.startsWith("om_") ? ticket.messageId : void 0,
241
+ replyInThread: Boolean(ticket.threadId),
242
+ accountId
243
+ });
244
+ }
245
+ return json({
246
+ success: true,
247
+ mode: "remote",
248
+ message: "\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002",
249
+ authorization_url: session.authorizationUrl,
250
+ awaiting_callback_url: true
251
+ });
252
+ }
253
+ // ---------------------------------------------------------------
254
+ // COMPLETE_AUTH(远程模式下用户回传 callback URL)
255
+ // ---------------------------------------------------------------
256
+ case "complete_auth": {
257
+ if (!p.callback_url) {
258
+ return json({
259
+ error: "\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002"
260
+ });
261
+ }
262
+ const key = fk(senderOpenId);
263
+ const session = pendingRemoteSessions.get(key);
264
+ if (!session) {
265
+ return json({
266
+ error: "\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002"
267
+ });
268
+ }
269
+ const result = await completeRemoteAuth(session, p.callback_url);
270
+ pendingRemoteSessions.delete(key);
271
+ const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);
272
+ await setProjectStoredToken(storedToken);
273
+ return json({
274
+ success: true,
275
+ message: "\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01",
276
+ authorized: true
277
+ });
278
+ }
279
+ // ---------------------------------------------------------------
280
+ // REVOKE
281
+ // ---------------------------------------------------------------
282
+ case "revoke": {
283
+ const clientId = await getProjectClientId(senderOpenId);
284
+ if (clientId) {
285
+ await removeProjectStoredToken(clientId, senderOpenId);
286
+ }
287
+ return json({ success: true, message: "\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002" });
288
+ }
289
+ default:
290
+ return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${p.action}` });
291
+ }
292
+ } catch (err) {
293
+ log.error(`project oauth ${p.action} failed: ${err}`);
294
+ return json({ error: formatLarkError(err) });
295
+ }
296
+ }
297
+ },
298
+ { name: "feishu_project_oauth" }
299
+ );
300
+ api.logger.info?.("feishu_project_oauth: Registered feishu_project_oauth tool");
301
+ }
302
+ export {
303
+ registerFeishuProjectOAuthTool
304
+ };
305
+ //# sourceMappingURL=project-oauth.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/tools/project-oauth.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * feishu_project_oauth \u2014 \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB OAuth \u6388\u6743\u5DE5\u5177\u3002\n *\n * \u57FA\u4E8E MCP \u6807\u51C6 OAuth\uFF08Authorization Code + PKCE + \u52A8\u6001\u5BA2\u6237\u7AEF\u6CE8\u518C\uFF09\uFF0C\n * \u4E0D\u9700\u8981 appId/appSecret\u3002\n *\n * \u5355\u4E00 authorize action \u81EA\u52A8\u5224\u65AD\u8FD0\u884C\u73AF\u5883\uFF1A\n * - \u672C\u5730\uFF08\u80FD\u7ED1\u7AEF\u53E3\uFF09\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\uFF0C\u6D4F\u89C8\u5668\u91CD\u5B9A\u5411\u81EA\u52A8\u5B8C\u6210\n * - \u8FDC\u7A0B\uFF08\u7ED1\u7AEF\u53E3\u5931\u8D25 / headless\uFF09\uFF1A\u964D\u7EA7\u4E3A\u53D1\u9001\u6388\u6743\u94FE\u63A5\uFF0C\u7B49\u7528\u6237\u56DE\u4F20 callback URL\n *\n * Actions:\n * - authorize : \u53D1\u8D77\u6388\u6743\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\u6A21\u5F0F\uFF09\n * - complete_auth : \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\uFF0C\u7528\u6237\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743\n * - status : \u68C0\u67E5\u98DE\u4E66\u9879\u76EE\u6388\u6743\u72B6\u6001\n * - revoke : \u64A4\u9500\u98DE\u4E66\u9879\u76EE\u6388\u6743\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { Type } from '@sinclair/typebox';\nimport { getTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\nimport { formatLarkError } from '../core/api-error';\nimport {\n startLocalAuthFlow,\n prepareRemoteAuth,\n completeRemoteAuth,\n type ProjectAuthSession,\n} from '../core/project-auth';\nimport {\n getProjectStoredToken,\n setProjectStoredToken,\n removeProjectStoredToken,\n projectTokenStatus,\n getProjectClientId,\n} from '../core/project-token-store';\nimport type { StoredUAToken } from '../core/token-store';\nimport { getProjectMcpEndpoint } from '../tools/mcp/project/endpoint';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard } from './oauth-cards';\nimport { json } from './oapi/helpers';\n\nconst log = larkLogger('tools/project-oauth');\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst FeishuProjectOAuthSchema = Type.Object(\n {\n action: Type.Union(\n [\n Type.Literal('authorize'),\n Type.Literal('complete_auth'),\n Type.Literal('status'),\n Type.Literal('revoke'),\n ],\n {\n description:\n 'authorize: \u53D1\u8D77\u98DE\u4E66\u9879\u76EE\u6388\u6743; ' +\n 'complete_auth: \u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u56DE\u4F20 callback URL \u5B8C\u6210\u6388\u6743; ' +\n 'status: \u68C0\u67E5\u6388\u6743\u72B6\u6001; revoke: \u64A4\u9500\u6388\u6743',\n },\n ),\n callback_url: Type.Optional(\n Type.String({\n description: '\u4EC5 complete_auth \u65F6\u9700\u8981\uFF1A\u7528\u6237\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574\u56DE\u8C03 URL',\n }),\n ),\n },\n {\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u9700\u5355\u72EC\u6388\u6743\u3002',\n },\n);\n\ninterface FeishuProjectOAuthParams {\n action: 'authorize' | 'complete_auth' | 'status' | 'revoke';\n callback_url?: string;\n}\n\n// ---------------------------------------------------------------------------\n// In-flight state\n// ---------------------------------------------------------------------------\n\ninterface PendingLocalFlow {\n controller: AbortController;\n cardId: string;\n sequence: number;\n superseded: boolean;\n close: () => Promise<void>;\n}\n\nconst pendingLocalFlows = new Map<string, PendingLocalFlow>();\nconst pendingRemoteSessions = new Map<string, ProjectAuthSession>();\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fk(userOpenId: string): string {\n return `project:${userOpenId}`;\n}\n\nfunction buildTokenFromOAuth(\n userOpenId: string,\n clientId: string,\n tokens: { access_token: string; refresh_token?: string; expires_in?: number; scope?: string },\n): StoredUAToken {\n const now = Date.now();\n const expiresIn = tokens.expires_in ?? 7200;\n const refreshExpiresIn = 30 * 24 * 3600;\n return {\n userOpenId,\n appId: clientId,\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token ?? '',\n expiresAt: now + expiresIn * 1000,\n refreshExpiresAt: now + refreshExpiresIn * 1000,\n scope: tokens.scope ?? '',\n grantedAt: now,\n };\n}\n\nasync function isAlreadyAuthorized(senderOpenId: string): Promise<boolean> {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) return false;\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n return !!existing && projectTokenStatus(existing) !== 'expired';\n}\n\n/**\n * \u5C1D\u8BD5\u542F\u52A8\u672C\u5730\u56DE\u8C03\u670D\u52A1\u5668\u3002\n * \u6210\u529F\u8FD4\u56DE flow \u5BF9\u8C61\uFF1B\u5931\u8D25\uFF08\u7AEF\u53E3\u7ED1\u5B9A\u5931\u8D25\u7B49\uFF09\u8FD4\u56DE null\uFF0C\u8C03\u7528\u65B9\u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F\u3002\n */\nasync function tryLocalAuth(mcpEndpoint: string) {\n try {\n return await startLocalAuthFlow(mcpEndpoint);\n } catch (err) {\n log.info(`local auth unavailable, falling back to remote: ${err instanceof Error ? err.message : err}`);\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerFeishuProjectOAuthTool(api: OpenClawPluginApi) {\n if (!api.config) return;\n\n const cfg = api.config;\n\n api.registerTool(\n {\n name: 'feishu_project_oauth',\n label: 'Feishu Project OAuth',\n description:\n '\u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u72EC\u7ACB\u6388\u6743\u5DE5\u5177\u3002' +\n '\u98DE\u4E66\u9879\u76EE\u7684\u6388\u6743\u4E0E\u98DE\u4E66 IM/\u6587\u6863\u7684\u6388\u6743\u4E0D\u5171\u4EAB\uFF0C\u9996\u6B21\u4F7F\u7528\u98DE\u4E66\u9879\u76EE\u529F\u80FD\u65F6\u9700\u8981\u5355\u72EC\u6388\u6743\u3002' +\n '\u8C03\u7528 authorize \u81EA\u52A8\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002\u5982\u679C\u63D0\u793A\u9700\u8981\u624B\u52A8\u56DE\u4F20 URL\uFF0C\u518D\u8C03\u7528 complete_auth\u3002',\n parameters: FeishuProjectOAuthSchema,\n\n async execute(_toolCallId: string, params: unknown) {\n const p = params as FeishuProjectOAuthParams;\n\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return json({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n const accountId = ticket.accountId;\n const mcpEndpoint = getProjectMcpEndpoint(cfg);\n\n try {\n switch (p.action) {\n // ---------------------------------------------------------------\n // STATUS\n // ---------------------------------------------------------------\n case 'status': {\n const clientId = await getProjectClientId(senderOpenId);\n if (!clientId) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const existing = await getProjectStoredToken(clientId, senderOpenId);\n if (!existing) {\n return json({\n authorized: false,\n message: '\u98DE\u4E66\u9879\u76EE\u672A\u6388\u6743\u3002\u8BF7\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n const status = projectTokenStatus(existing);\n return json({\n authorized: status !== 'expired',\n token_status: status,\n scope: existing.scope,\n granted_at: existing.grantedAt ? new Date(existing.grantedAt).toISOString() : undefined,\n expires_at: existing.expiresAt ? new Date(existing.expiresAt).toISOString() : undefined,\n });\n }\n\n // ---------------------------------------------------------------\n // AUTHORIZE\uFF08\u81EA\u52A8\u9009\u62E9\u672C\u5730/\u8FDC\u7A0B\uFF09\n // ---------------------------------------------------------------\n case 'authorize': {\n if (await isAlreadyAuthorized(senderOpenId)) {\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002',\n authorized: true,\n });\n }\n\n const chatId = ticket.chatId;\n if (!chatId) {\n return json({ error: '\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807' });\n }\n\n // Cancel any existing local flow\n const key = fk(senderOpenId);\n const oldLocal = pendingLocalFlows.get(key);\n if (oldLocal) {\n oldLocal.superseded = true;\n oldLocal.controller.abort();\n await oldLocal.close().catch(() => {});\n pendingLocalFlows.delete(key);\n }\n pendingRemoteSessions.delete(key);\n\n // --- \u5C1D\u8BD5\u672C\u5730\u6A21\u5F0F ---\n const local = await tryLocalAuth(mcpEndpoint);\n\n if (local) {\n // \u672C\u5730\u6A21\u5F0F\uFF1A\u542F\u52A8\u56DE\u8C03\u670D\u52A1\u5668\u6210\u529F\n const { session, waitForCode, port, close } = local;\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 5,\n });\n const cardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!cardId) {\n await close();\n return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n }\n\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n\n const abortController = new AbortController();\n let seq = 1;\n const currentFlow: PendingLocalFlow = {\n controller: abortController,\n cardId,\n sequence: seq,\n superseded: false,\n close,\n };\n pendingLocalFlows.set(key, currentFlow);\n\n waitForCode()\n .then(async (result) => {\n if (currentFlow.superseded) return;\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n try {\n await updateCardKitCardForAuth({\n cfg, cardId, card: buildAuthSuccessCard(), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to success: ${e}`);\n }\n })\n .catch(async (err) => {\n if (currentFlow.superseded) return;\n log.error(`project local auth failed: ${err}`);\n try {\n const msg = err instanceof Error ? err.message : String(err);\n await updateCardKitCardForAuth({\n cfg, cardId, card: buildAuthFailedCard(msg), sequence: ++seq, accountId,\n });\n } catch (e) {\n log.warn(`failed to update project auth card to failure: ${e}`);\n }\n })\n .finally(async () => {\n await close().catch(() => {});\n if (pendingLocalFlows.get(key) === currentFlow) {\n pendingLocalFlows.delete(key);\n }\n });\n\n return json({\n success: true,\n mode: 'local',\n message: '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\uFF0C\u8BF7\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6548\u3002',\n awaiting_authorization: true,\n callback_port: port,\n });\n }\n\n // --- \u964D\u7EA7\u4E3A\u8FDC\u7A0B\u6A21\u5F0F ---\n const session = await prepareRemoteAuth(mcpEndpoint);\n pendingRemoteSessions.set(key, session);\n\n const authCard = buildAuthCard({\n verificationUriComplete: session.authorizationUrl,\n expiresMin: 10,\n });\n const cardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (cardId) {\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId,\n replyToMessageId: ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket.threadId),\n accountId,\n });\n }\n\n return json({\n success: true,\n mode: 'remote',\n message:\n '\u5DF2\u53D1\u9001\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5361\u7247\u3002\u8BF7\u70B9\u51FB\u94FE\u63A5\u5728\u6D4F\u89C8\u5668\u4E2D\u5B8C\u6210\u6388\u6743\u3002' +\n '\u6388\u6743\u540E\u6D4F\u89C8\u5668\u4F1A\u8DF3\u8F6C\u5230\u4E00\u4E2A\u65E0\u6CD5\u8BBF\u95EE\u7684\u5730\u5740\uFF08http://127.0.0.1:...\uFF09\uFF0C' +\n '\u8BF7\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u5B8C\u6574 URL\uFF0C\u7136\u540E\u8C03\u7528 complete_auth \u5E76\u4F20\u5165 callback_url \u53C2\u6570\u3002',\n authorization_url: session.authorizationUrl,\n awaiting_callback_url: true,\n });\n }\n\n // ---------------------------------------------------------------\n // COMPLETE_AUTH\uFF08\u8FDC\u7A0B\u6A21\u5F0F\u4E0B\u7528\u6237\u56DE\u4F20 callback URL\uFF09\n // ---------------------------------------------------------------\n case 'complete_auth': {\n if (!p.callback_url) {\n return json({\n error: '\u8BF7\u63D0\u4F9B callback_url \u53C2\u6570\uFF08\u4ECE\u6D4F\u89C8\u5668\u5730\u5740\u680F\u590D\u5236\u7684\u5B8C\u6574 URL\uFF09\u3002',\n });\n }\n\n const key = fk(senderOpenId);\n const session = pendingRemoteSessions.get(key);\n if (!session) {\n return json({\n error: '\u6CA1\u6709\u5F85\u5B8C\u6210\u7684\u6388\u6743\u6D41\u7A0B\u3002\u8BF7\u5148\u8C03\u7528 authorize \u53D1\u8D77\u6388\u6743\u3002',\n });\n }\n\n const result = await completeRemoteAuth(session, p.callback_url);\n pendingRemoteSessions.delete(key);\n\n const storedToken = buildTokenFromOAuth(senderOpenId, result.clientId, result.tokens);\n await setProjectStoredToken(storedToken);\n\n return json({\n success: true,\n message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u6210\u529F\uFF01',\n authorized: true,\n });\n }\n\n // ---------------------------------------------------------------\n // REVOKE\n // ---------------------------------------------------------------\n case 'revoke': {\n const clientId = await getProjectClientId(senderOpenId);\n if (clientId) {\n await removeProjectStoredToken(clientId, senderOpenId);\n }\n return json({ success: true, message: '\u98DE\u4E66\u9879\u76EE\u6388\u6743\u5DF2\u64A4\u9500\u3002' });\n }\n\n default:\n return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${(p as { action: string }).action}` });\n }\n } catch (err) {\n log.error(`project oauth ${p.action} failed: ${err}`);\n return json({ error: formatLarkError(err) });\n }\n },\n },\n { name: 'feishu_project_oauth' },\n );\n\n api.logger.info?.('feishu_project_oauth: Registered feishu_project_oauth tool');\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["session", "authCard", "cardId"]
7
+ }
@@ -0,0 +1,9 @@
1
+ import { registerFeishuImBotImageTool } from "./resource";
2
+ function registerFeishuImTools(api) {
3
+ registerFeishuImBotImageTool(api);
4
+ api.logger.info?.("feishu_im: Registered feishu_im_bot_image");
5
+ }
6
+ export {
7
+ registerFeishuImTools
8
+ };
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/tools/tat/im/index.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * IM \u5DE5\u5177\u96C6\n * \u7EDF\u4E00\u5BFC\u51FA\u6240\u6709\u5373\u65F6\u901A\u8BAF\u76F8\u5173\u5DE5\u5177\u7684\u6CE8\u518C\u51FD\u6570\n */\n\nimport type { OpenClawPluginApi } from 'openclaw/plugin-sdk';\nimport { registerFeishuImBotImageTool } from './resource';\n\n/**\n * \u6CE8\u518C\u6240\u6709 IM \u5DE5\u5177\n *\n * Note: feishu_im_message_reaction \u548C feishu_im_message_recall \u5DF2\u79FB\u9664\uFF0C\n * \u5176\u529F\u80FD\u7531 ChannelMessageActionAdapter (actions.ts) \u7684 react/delete action \u7EDF\u4E00\u8986\u76D6\u3002\n */\nexport function registerFeishuImTools(api: OpenClawPluginApi) {\n registerFeishuImBotImageTool(api);\n api.logger.info?.('feishu_im: Registered feishu_im_bot_image');\n}\n"],
5
+ "mappings": "AASA,SAAS,oCAAoC;AAQtC,SAAS,sBAAsB,KAAwB;AAC5D,+BAA6B,GAAG;AAChC,MAAI,OAAO,OAAO,2CAA2C;AAC/D;",
6
+ "names": []
7
+ }
@@ -0,0 +1,123 @@
1
+ import { buildRandomTempFilePath } from "openclaw/plugin-sdk";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { json, createToolContext, formatLarkError } from "../../oapi/helpers";
4
+ import * as fsPromises from "node:fs/promises";
5
+ import * as path from "node:path";
6
+ const MIME_TO_EXT = {
7
+ "image/png": ".png",
8
+ "image/jpeg": ".jpg",
9
+ "image/jpg": ".jpg",
10
+ "image/gif": ".gif",
11
+ "image/webp": ".webp",
12
+ "image/svg+xml": ".svg",
13
+ "image/bmp": ".bmp",
14
+ "image/tiff": ".tiff",
15
+ "video/mp4": ".mp4",
16
+ "video/mpeg": ".mpeg",
17
+ "video/quicktime": ".mov",
18
+ "video/x-msvideo": ".avi",
19
+ "video/webm": ".webm",
20
+ "audio/mpeg": ".mp3",
21
+ "audio/wav": ".wav",
22
+ "audio/ogg": ".ogg",
23
+ "audio/mp4": ".m4a",
24
+ "application/pdf": ".pdf",
25
+ "application/msword": ".doc",
26
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
27
+ "application/vnd.ms-excel": ".xls",
28
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
29
+ "application/vnd.ms-powerpoint": ".ppt",
30
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
31
+ "application/zip": ".zip",
32
+ "application/x-rar-compressed": ".rar",
33
+ "text/plain": ".txt",
34
+ "application/json": ".json"
35
+ };
36
+ async function extractBuffer(res) {
37
+ let chunks;
38
+ if (typeof res.getReadableStream === "function") {
39
+ const stream = res.getReadableStream();
40
+ chunks = [];
41
+ for await (const chunk of stream) {
42
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
43
+ }
44
+ } else if (Buffer.isBuffer(res)) {
45
+ chunks = [res];
46
+ } else if (Buffer.isBuffer(res?.data)) {
47
+ chunks = [res.data];
48
+ } else {
49
+ throw new Error("\u65E0\u6CD5\u4ECE\u54CD\u5E94\u4E2D\u63D0\u53D6\u4E8C\u8FDB\u5236\u6570\u636E");
50
+ }
51
+ const buffer = Buffer.concat(chunks);
52
+ const contentType = res.headers?.["content-type"] ?? "";
53
+ return { buffer, contentType };
54
+ }
55
+ async function saveToTempFile(buffer, contentType, prefix) {
56
+ const mimeType = contentType ? contentType.split(";")[0].trim() : "";
57
+ const mimeExt = mimeType ? MIME_TO_EXT[mimeType] : void 0;
58
+ const filePath = buildRandomTempFilePath({
59
+ prefix,
60
+ extension: mimeExt
61
+ });
62
+ await fsPromises.mkdir(path.dirname(filePath), { recursive: true });
63
+ await fsPromises.writeFile(filePath, buffer);
64
+ return filePath;
65
+ }
66
+ const FeishuImBotImageSchema = Type.Object({
67
+ message_id: Type.String({
68
+ description: "\u6D88\u606F ID\uFF08om_xxx \u683C\u5F0F\uFF09\uFF0C\u5F15\u7528\u6D88\u606F\u53EF\u4ECE\u4E0A\u4E0B\u6587\u4E2D\u7684 [message_id=om_xxx] \u63D0\u53D6"
69
+ }),
70
+ file_key: Type.String({
71
+ description: "\u8D44\u6E90 Key\uFF0C\u56FE\u7247\u6D88\u606F\u7684 image_key\uFF08img_xxx\uFF09\u6216\u6587\u4EF6\u6D88\u606F\u7684 file_key\uFF08file_xxx\uFF09"
72
+ }),
73
+ type: Type.Union([Type.Literal("image"), Type.Literal("file")], {
74
+ description: "\u8D44\u6E90\u7C7B\u578B\uFF1Aimage\uFF08\u56FE\u7247\u6D88\u606F\u4E2D\u7684\u56FE\u7247\uFF09\u3001file\uFF08\u6587\u4EF6/\u97F3\u9891/\u89C6\u9891\u6D88\u606F\u4E2D\u7684\u6587\u4EF6\uFF09"
75
+ })
76
+ });
77
+ function registerFeishuImBotImageTool(api) {
78
+ if (!api.config) return;
79
+ const { getClient, log } = createToolContext(api, "feishu_im_bot_image");
80
+ api.registerTool(
81
+ {
82
+ name: "feishu_im_bot_image",
83
+ label: "Feishu: IM Bot Image Download",
84
+ description: "\u3010\u4EE5\u673A\u5668\u4EBA\u8EAB\u4EFD\u3011\u4E0B\u8F7D\u98DE\u4E66 IM \u6D88\u606F\u4E2D\u7684\u56FE\u7247\u6216\u6587\u4EF6\u8D44\u6E90\u5230\u672C\u5730\u3002\n\n\u9002\u7528\u573A\u666F\uFF1A\u7528\u6237\u76F4\u63A5\u53D1\u9001\u7ED9\u673A\u5668\u4EBA\u7684\u6D88\u606F\u3001\u7528\u6237\u5F15\u7528\u7684\u6D88\u606F\u3001\u673A\u5668\u4EBA\u6536\u5230\u7684\u7FA4\u804A\u6D88\u606F\u4E2D\u7684\u56FE\u7247/\u6587\u4EF6\u3002\u5373\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u4E2D\u51FA\u73B0\u7684 message_id \u548C image_key/file_key\uFF0C\u5E94\u4F7F\u7528\u672C\u5DE5\u5177\u4E0B\u8F7D\u3002\n\u5F15\u7528\u6D88\u606F\u7684 message_id \u53EF\u4ECE\u4E0A\u4E0B\u6587\u4E2D\u7684 [message_id=om_xxx] \u63D0\u53D6\uFF0C\u65E0\u9700\u5411\u7528\u6237\u8BE2\u95EE\u3002\n\n\u6587\u4EF6\u81EA\u52A8\u4FDD\u5B58\u5230 /tmp/openclaw/ \u4E0B\uFF0C\u8FD4\u56DE\u503C\u4E2D\u7684 saved_path \u4E3A\u5B9E\u9645\u4FDD\u5B58\u8DEF\u5F84\u3002",
85
+ parameters: FeishuImBotImageSchema,
86
+ async execute(_toolCallId, params) {
87
+ const p = params;
88
+ try {
89
+ const client = getClient();
90
+ log.info(`download: message_id="${p.message_id}", file_key="${p.file_key}", type="${p.type}"`);
91
+ const res = await client.im.messageResource.get({
92
+ path: {
93
+ message_id: p.message_id,
94
+ file_key: p.file_key
95
+ },
96
+ params: { type: p.type }
97
+ });
98
+ const { buffer, contentType } = await extractBuffer(res);
99
+ log.info(`download: ${buffer.length} bytes, content-type=${contentType}`);
100
+ const savedPath = await saveToTempFile(buffer, contentType, "bot-resource");
101
+ log.info(`download: saved to ${savedPath}`);
102
+ return json({
103
+ message_id: p.message_id,
104
+ file_key: p.file_key,
105
+ type: p.type,
106
+ size_bytes: buffer.length,
107
+ content_type: contentType,
108
+ saved_path: savedPath
109
+ });
110
+ } catch (err) {
111
+ log.error(`Error: ${formatLarkError(err)}`);
112
+ return json({ error: formatLarkError(err) });
113
+ }
114
+ }
115
+ },
116
+ { name: "feishu_im_bot_image" }
117
+ );
118
+ api.logger.info?.("feishu_im_bot_image: Registered feishu_im_bot_image tool");
119
+ }
120
+ export {
121
+ registerFeishuImBotImageTool
122
+ };
123
+ //# sourceMappingURL=resource.js.map