@colinlu50/openclaw-lark-stream 2026.3.17

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 (361) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/README.zh.md +70 -0
  4. package/bin/openclaw-lark.js +48 -0
  5. package/index.d.ts +36 -0
  6. package/index.js +118 -0
  7. package/openclaw.plugin.json +10 -0
  8. package/package.json +66 -0
  9. package/skills/feishu-bitable/SKILL.md +248 -0
  10. package/skills/feishu-bitable/references/examples.md +813 -0
  11. package/skills/feishu-bitable/references/field-properties.md +763 -0
  12. package/skills/feishu-bitable/references/record-values.md +911 -0
  13. package/skills/feishu-calendar/SKILL.md +244 -0
  14. package/skills/feishu-channel-rules/SKILL.md +24 -0
  15. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  16. package/skills/feishu-create-doc/SKILL.md +719 -0
  17. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  18. package/skills/feishu-im-read/SKILL.md +163 -0
  19. package/skills/feishu-task/SKILL.md +293 -0
  20. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  21. package/skills/feishu-update-doc/SKILL.md +285 -0
  22. package/src/card/builder.d.ts +106 -0
  23. package/src/card/builder.js +443 -0
  24. package/src/card/cardkit.d.ts +90 -0
  25. package/src/card/cardkit.js +181 -0
  26. package/src/card/flush-controller.d.ts +45 -0
  27. package/src/card/flush-controller.js +134 -0
  28. package/src/card/image-resolver.d.ts +45 -0
  29. package/src/card/image-resolver.js +112 -0
  30. package/src/card/markdown-style.d.ts +16 -0
  31. package/src/card/markdown-style.js +97 -0
  32. package/src/card/reply-dispatcher-types.d.ts +120 -0
  33. package/src/card/reply-dispatcher-types.js +57 -0
  34. package/src/card/reply-dispatcher.d.ts +15 -0
  35. package/src/card/reply-dispatcher.js +299 -0
  36. package/src/card/reply-mode.d.ts +38 -0
  37. package/src/card/reply-mode.js +65 -0
  38. package/src/card/streaming-card-controller.d.ts +101 -0
  39. package/src/card/streaming-card-controller.js +810 -0
  40. package/src/card/unavailable-guard.d.ts +35 -0
  41. package/src/card/unavailable-guard.js +83 -0
  42. package/src/channel/abort-detect.d.ts +34 -0
  43. package/src/channel/abort-detect.js +124 -0
  44. package/src/channel/chat-queue.d.ts +41 -0
  45. package/src/channel/chat-queue.js +58 -0
  46. package/src/channel/config-adapter.d.ts +23 -0
  47. package/src/channel/config-adapter.js +101 -0
  48. package/src/channel/directory.d.ts +57 -0
  49. package/src/channel/directory.js +191 -0
  50. package/src/channel/event-handlers.d.ts +15 -0
  51. package/src/channel/event-handlers.js +221 -0
  52. package/src/channel/monitor.d.ts +17 -0
  53. package/src/channel/monitor.js +129 -0
  54. package/src/channel/onboarding-config.d.ts +17 -0
  55. package/src/channel/onboarding-config.js +88 -0
  56. package/src/channel/onboarding-migrate.d.ts +25 -0
  57. package/src/channel/onboarding-migrate.js +67 -0
  58. package/src/channel/onboarding.d.ts +12 -0
  59. package/src/channel/onboarding.js +296 -0
  60. package/src/channel/plugin.d.ts +13 -0
  61. package/src/channel/plugin.js +278 -0
  62. package/src/channel/probe.d.ts +14 -0
  63. package/src/channel/probe.js +21 -0
  64. package/src/channel/types.d.ts +36 -0
  65. package/src/channel/types.js +7 -0
  66. package/src/commands/auth.d.ts +21 -0
  67. package/src/commands/auth.js +161 -0
  68. package/src/commands/diagnose.d.ts +69 -0
  69. package/src/commands/diagnose.js +807 -0
  70. package/src/commands/doctor.d.ts +26 -0
  71. package/src/commands/doctor.js +584 -0
  72. package/src/commands/index.d.ts +25 -0
  73. package/src/commands/index.js +212 -0
  74. package/src/commands/locale.d.ts +7 -0
  75. package/src/commands/locale.js +7 -0
  76. package/src/core/accounts.d.ts +37 -0
  77. package/src/core/accounts.js +163 -0
  78. package/src/core/agent-config.d.ts +100 -0
  79. package/src/core/agent-config.js +139 -0
  80. package/src/core/api-error.d.ts +48 -0
  81. package/src/core/api-error.js +112 -0
  82. package/src/core/app-owner-fallback.d.ts +21 -0
  83. package/src/core/app-owner-fallback.js +38 -0
  84. package/src/core/app-scope-checker.d.ts +87 -0
  85. package/src/core/app-scope-checker.js +190 -0
  86. package/src/core/auth-errors.d.ts +144 -0
  87. package/src/core/auth-errors.js +154 -0
  88. package/src/core/chat-info-cache.d.ts +57 -0
  89. package/src/core/chat-info-cache.js +152 -0
  90. package/src/core/config-schema.d.ts +448 -0
  91. package/src/core/config-schema.js +200 -0
  92. package/src/core/device-flow.d.ts +77 -0
  93. package/src/core/device-flow.js +212 -0
  94. package/src/core/domains.d.ts +18 -0
  95. package/src/core/domains.js +28 -0
  96. package/src/core/feishu-fetch.d.ts +18 -0
  97. package/src/core/feishu-fetch.js +25 -0
  98. package/src/core/footer-config.d.ts +24 -0
  99. package/src/core/footer-config.js +39 -0
  100. package/src/core/lark-client.d.ts +108 -0
  101. package/src/core/lark-client.js +353 -0
  102. package/src/core/lark-logger.d.ts +23 -0
  103. package/src/core/lark-logger.js +154 -0
  104. package/src/core/lark-ticket.d.ts +29 -0
  105. package/src/core/lark-ticket.js +35 -0
  106. package/src/core/message-unavailable.d.ts +53 -0
  107. package/src/core/message-unavailable.js +130 -0
  108. package/src/core/owner-policy.d.ts +31 -0
  109. package/src/core/owner-policy.js +52 -0
  110. package/src/core/permission-url.d.ts +22 -0
  111. package/src/core/permission-url.js +72 -0
  112. package/src/core/raw-request.d.ts +27 -0
  113. package/src/core/raw-request.js +62 -0
  114. package/src/core/scope-manager.d.ts +168 -0
  115. package/src/core/scope-manager.js +213 -0
  116. package/src/core/security-check.d.ts +72 -0
  117. package/src/core/security-check.js +174 -0
  118. package/src/core/shutdown-hooks.d.ts +22 -0
  119. package/src/core/shutdown-hooks.js +56 -0
  120. package/src/core/targets.d.ts +60 -0
  121. package/src/core/targets.js +164 -0
  122. package/src/core/token-store.d.ts +54 -0
  123. package/src/core/token-store.js +314 -0
  124. package/src/core/tool-client.d.ts +176 -0
  125. package/src/core/tool-client.js +380 -0
  126. package/src/core/tool-scopes.d.ts +153 -0
  127. package/src/core/tool-scopes.js +326 -0
  128. package/src/core/tools-config.d.ts +55 -0
  129. package/src/core/tools-config.js +137 -0
  130. package/src/core/types.d.ts +87 -0
  131. package/src/core/types.js +11 -0
  132. package/src/core/uat-client.d.ts +46 -0
  133. package/src/core/uat-client.js +187 -0
  134. package/src/core/version.d.ts +25 -0
  135. package/src/core/version.js +49 -0
  136. package/src/messaging/converters/audio.d.ts +8 -0
  137. package/src/messaging/converters/audio.js +21 -0
  138. package/src/messaging/converters/calendar.d.ts +13 -0
  139. package/src/messaging/converters/calendar.js +50 -0
  140. package/src/messaging/converters/content-converter.d.ts +41 -0
  141. package/src/messaging/converters/content-converter.js +106 -0
  142. package/src/messaging/converters/file.d.ts +8 -0
  143. package/src/messaging/converters/file.js +20 -0
  144. package/src/messaging/converters/folder.d.ts +8 -0
  145. package/src/messaging/converters/folder.js +20 -0
  146. package/src/messaging/converters/hongbao.d.ts +8 -0
  147. package/src/messaging/converters/hongbao.js +16 -0
  148. package/src/messaging/converters/image.d.ts +8 -0
  149. package/src/messaging/converters/image.js +18 -0
  150. package/src/messaging/converters/index.d.ts +8 -0
  151. package/src/messaging/converters/index.js +50 -0
  152. package/src/messaging/converters/interactive/card-converter.d.ts +76 -0
  153. package/src/messaging/converters/interactive/card-converter.js +1173 -0
  154. package/src/messaging/converters/interactive/card-utils.d.ts +9 -0
  155. package/src/messaging/converters/interactive/card-utils.js +42 -0
  156. package/src/messaging/converters/interactive/index.d.ts +8 -0
  157. package/src/messaging/converters/interactive/index.js +21 -0
  158. package/src/messaging/converters/interactive/legacy.d.ts +11 -0
  159. package/src/messaging/converters/interactive/legacy.js +57 -0
  160. package/src/messaging/converters/interactive/types.d.ts +23 -0
  161. package/src/messaging/converters/interactive/types.js +24 -0
  162. package/src/messaging/converters/location.d.ts +8 -0
  163. package/src/messaging/converters/location.js +19 -0
  164. package/src/messaging/converters/merge-forward.d.ts +32 -0
  165. package/src/messaging/converters/merge-forward.js +225 -0
  166. package/src/messaging/converters/post.d.ts +11 -0
  167. package/src/messaging/converters/post.js +135 -0
  168. package/src/messaging/converters/share.d.ts +9 -0
  169. package/src/messaging/converters/share.js +23 -0
  170. package/src/messaging/converters/sticker.d.ts +8 -0
  171. package/src/messaging/converters/sticker.js +18 -0
  172. package/src/messaging/converters/system.d.ts +12 -0
  173. package/src/messaging/converters/system.js +32 -0
  174. package/src/messaging/converters/text.d.ts +8 -0
  175. package/src/messaging/converters/text.js +14 -0
  176. package/src/messaging/converters/todo.d.ts +8 -0
  177. package/src/messaging/converters/todo.js +41 -0
  178. package/src/messaging/converters/types.d.ts +107 -0
  179. package/src/messaging/converters/types.js +7 -0
  180. package/src/messaging/converters/unknown.d.ts +8 -0
  181. package/src/messaging/converters/unknown.js +16 -0
  182. package/src/messaging/converters/utils.d.ts +22 -0
  183. package/src/messaging/converters/utils.js +51 -0
  184. package/src/messaging/converters/video-chat.d.ts +8 -0
  185. package/src/messaging/converters/video-chat.js +23 -0
  186. package/src/messaging/converters/video.d.ts +8 -0
  187. package/src/messaging/converters/video.js +32 -0
  188. package/src/messaging/converters/vote.d.ts +8 -0
  189. package/src/messaging/converters/vote.js +24 -0
  190. package/src/messaging/inbound/dedup.d.ts +59 -0
  191. package/src/messaging/inbound/dedup.js +116 -0
  192. package/src/messaging/inbound/dispatch-builders.d.ts +84 -0
  193. package/src/messaging/inbound/dispatch-builders.js +152 -0
  194. package/src/messaging/inbound/dispatch-commands.d.ts +27 -0
  195. package/src/messaging/inbound/dispatch-commands.js +112 -0
  196. package/src/messaging/inbound/dispatch-context.d.ts +67 -0
  197. package/src/messaging/inbound/dispatch-context.js +136 -0
  198. package/src/messaging/inbound/dispatch.d.ts +47 -0
  199. package/src/messaging/inbound/dispatch.js +264 -0
  200. package/src/messaging/inbound/enrich.d.ts +102 -0
  201. package/src/messaging/inbound/enrich.js +227 -0
  202. package/src/messaging/inbound/gate-effects.d.ts +23 -0
  203. package/src/messaging/inbound/gate-effects.js +43 -0
  204. package/src/messaging/inbound/gate.d.ts +60 -0
  205. package/src/messaging/inbound/gate.js +233 -0
  206. package/src/messaging/inbound/handler.d.ts +35 -0
  207. package/src/messaging/inbound/handler.js +173 -0
  208. package/src/messaging/inbound/media-resolver.d.ts +32 -0
  209. package/src/messaging/inbound/media-resolver.js +87 -0
  210. package/src/messaging/inbound/mention.d.ts +39 -0
  211. package/src/messaging/inbound/mention.js +81 -0
  212. package/src/messaging/inbound/parse-io.d.ts +50 -0
  213. package/src/messaging/inbound/parse-io.js +81 -0
  214. package/src/messaging/inbound/parse.d.ts +28 -0
  215. package/src/messaging/inbound/parse.js +106 -0
  216. package/src/messaging/inbound/permission.d.ts +17 -0
  217. package/src/messaging/inbound/permission.js +40 -0
  218. package/src/messaging/inbound/policy.d.ts +94 -0
  219. package/src/messaging/inbound/policy.js +160 -0
  220. package/src/messaging/inbound/reaction-handler.d.ts +61 -0
  221. package/src/messaging/inbound/reaction-handler.js +221 -0
  222. package/src/messaging/inbound/user-name-cache.d.ts +82 -0
  223. package/src/messaging/inbound/user-name-cache.js +241 -0
  224. package/src/messaging/outbound/actions.d.ts +16 -0
  225. package/src/messaging/outbound/actions.js +309 -0
  226. package/src/messaging/outbound/chat-manage.d.ts +64 -0
  227. package/src/messaging/outbound/chat-manage.js +111 -0
  228. package/src/messaging/outbound/deliver.d.ts +155 -0
  229. package/src/messaging/outbound/deliver.js +298 -0
  230. package/src/messaging/outbound/fetch.d.ts +12 -0
  231. package/src/messaging/outbound/fetch.js +12 -0
  232. package/src/messaging/outbound/forward.d.ts +26 -0
  233. package/src/messaging/outbound/forward.js +48 -0
  234. package/src/messaging/outbound/media-url-utils.d.ts +29 -0
  235. package/src/messaging/outbound/media-url-utils.js +130 -0
  236. package/src/messaging/outbound/media.d.ts +260 -0
  237. package/src/messaging/outbound/media.js +758 -0
  238. package/src/messaging/outbound/outbound.d.ts +89 -0
  239. package/src/messaging/outbound/outbound.js +121 -0
  240. package/src/messaging/outbound/reactions.d.ts +124 -0
  241. package/src/messaging/outbound/reactions.js +378 -0
  242. package/src/messaging/outbound/send.d.ts +152 -0
  243. package/src/messaging/outbound/send.js +355 -0
  244. package/src/messaging/outbound/typing.d.ts +71 -0
  245. package/src/messaging/outbound/typing.js +179 -0
  246. package/src/messaging/shared/message-lookup.d.ts +54 -0
  247. package/src/messaging/shared/message-lookup.js +117 -0
  248. package/src/messaging/types.d.ts +176 -0
  249. package/src/messaging/types.js +10 -0
  250. package/src/tools/auto-auth.d.ts +56 -0
  251. package/src/tools/auto-auth.js +919 -0
  252. package/src/tools/helpers.d.ts +260 -0
  253. package/src/tools/helpers.js +364 -0
  254. package/src/tools/mcp/doc/create.d.ts +12 -0
  255. package/src/tools/mcp/doc/create.js +44 -0
  256. package/src/tools/mcp/doc/fetch.d.ts +12 -0
  257. package/src/tools/mcp/doc/fetch.js +36 -0
  258. package/src/tools/mcp/doc/index.d.ts +12 -0
  259. package/src/tools/mcp/doc/index.js +41 -0
  260. package/src/tools/mcp/doc/update.d.ts +12 -0
  261. package/src/tools/mcp/doc/update.js +61 -0
  262. package/src/tools/mcp/shared.d.ts +59 -0
  263. package/src/tools/mcp/shared.js +226 -0
  264. package/src/tools/oapi/bitable/app-table-field.d.ts +16 -0
  265. package/src/tools/oapi/bitable/app-table-field.js +222 -0
  266. package/src/tools/oapi/bitable/app-table-record.d.ts +20 -0
  267. package/src/tools/oapi/bitable/app-table-record.js +436 -0
  268. package/src/tools/oapi/bitable/app-table-view.d.ts +17 -0
  269. package/src/tools/oapi/bitable/app-table-view.js +195 -0
  270. package/src/tools/oapi/bitable/app-table.d.ts +19 -0
  271. package/src/tools/oapi/bitable/app-table.js +247 -0
  272. package/src/tools/oapi/bitable/app.d.ts +18 -0
  273. package/src/tools/oapi/bitable/app.js +186 -0
  274. package/src/tools/oapi/bitable/index.d.ts +9 -0
  275. package/src/tools/oapi/bitable/index.js +9 -0
  276. package/src/tools/oapi/calendar/calendar.d.ts +15 -0
  277. package/src/tools/oapi/calendar/calendar.js +122 -0
  278. package/src/tools/oapi/calendar/event-attendee.d.ts +16 -0
  279. package/src/tools/oapi/calendar/event-attendee.js +263 -0
  280. package/src/tools/oapi/calendar/event.d.ts +16 -0
  281. package/src/tools/oapi/calendar/event.js +709 -0
  282. package/src/tools/oapi/calendar/freebusy.d.ts +13 -0
  283. package/src/tools/oapi/calendar/freebusy.js +111 -0
  284. package/src/tools/oapi/calendar/index.d.ts +8 -0
  285. package/src/tools/oapi/calendar/index.js +8 -0
  286. package/src/tools/oapi/chat/chat.d.ts +16 -0
  287. package/src/tools/oapi/chat/chat.js +124 -0
  288. package/src/tools/oapi/chat/index.d.ts +10 -0
  289. package/src/tools/oapi/chat/index.js +15 -0
  290. package/src/tools/oapi/chat/members.d.ts +11 -0
  291. package/src/tools/oapi/chat/members.js +81 -0
  292. package/src/tools/oapi/common/get-user.d.ts +12 -0
  293. package/src/tools/oapi/common/get-user.js +106 -0
  294. package/src/tools/oapi/common/index.d.ts +6 -0
  295. package/src/tools/oapi/common/index.js +6 -0
  296. package/src/tools/oapi/common/search-user.d.ts +11 -0
  297. package/src/tools/oapi/common/search-user.js +73 -0
  298. package/src/tools/oapi/drive/doc-comments.d.ts +15 -0
  299. package/src/tools/oapi/drive/doc-comments.js +279 -0
  300. package/src/tools/oapi/drive/doc-media.d.ts +19 -0
  301. package/src/tools/oapi/drive/doc-media.js +335 -0
  302. package/src/tools/oapi/drive/file.d.ts +19 -0
  303. package/src/tools/oapi/drive/file.js +483 -0
  304. package/src/tools/oapi/drive/index.d.ts +12 -0
  305. package/src/tools/oapi/drive/index.js +36 -0
  306. package/src/tools/oapi/helpers.d.ts +182 -0
  307. package/src/tools/oapi/helpers.js +354 -0
  308. package/src/tools/oapi/im/format-messages.d.ts +50 -0
  309. package/src/tools/oapi/im/format-messages.js +165 -0
  310. package/src/tools/oapi/im/index.d.ts +10 -0
  311. package/src/tools/oapi/im/index.js +17 -0
  312. package/src/tools/oapi/im/message-read.d.ts +13 -0
  313. package/src/tools/oapi/im/message-read.js +411 -0
  314. package/src/tools/oapi/im/message.d.ts +16 -0
  315. package/src/tools/oapi/im/message.js +149 -0
  316. package/src/tools/oapi/im/resource.d.ts +13 -0
  317. package/src/tools/oapi/im/resource.js +150 -0
  318. package/src/tools/oapi/im/time-utils.d.ts +46 -0
  319. package/src/tools/oapi/im/time-utils.js +201 -0
  320. package/src/tools/oapi/im/user-name-uat.d.ts +26 -0
  321. package/src/tools/oapi/im/user-name-uat.js +140 -0
  322. package/src/tools/oapi/index.d.ts +11 -0
  323. package/src/tools/oapi/index.js +58 -0
  324. package/src/tools/oapi/sdk-types.d.ts +96 -0
  325. package/src/tools/oapi/sdk-types.js +12 -0
  326. package/src/tools/oapi/search/doc-search.d.ts +13 -0
  327. package/src/tools/oapi/search/doc-search.js +191 -0
  328. package/src/tools/oapi/search/index.d.ts +12 -0
  329. package/src/tools/oapi/search/index.js +33 -0
  330. package/src/tools/oapi/sheets/index.d.ts +12 -0
  331. package/src/tools/oapi/sheets/index.js +31 -0
  332. package/src/tools/oapi/sheets/sheet.d.ts +16 -0
  333. package/src/tools/oapi/sheets/sheet.js +652 -0
  334. package/src/tools/oapi/task/comment.d.ts +15 -0
  335. package/src/tools/oapi/task/comment.js +140 -0
  336. package/src/tools/oapi/task/index.d.ts +8 -0
  337. package/src/tools/oapi/task/index.js +8 -0
  338. package/src/tools/oapi/task/subtask.d.ts +14 -0
  339. package/src/tools/oapi/task/subtask.js +162 -0
  340. package/src/tools/oapi/task/task.d.ts +16 -0
  341. package/src/tools/oapi/task/task.js +344 -0
  342. package/src/tools/oapi/task/tasklist.d.ts +21 -0
  343. package/src/tools/oapi/task/tasklist.js +321 -0
  344. package/src/tools/oapi/wiki/index.d.ts +12 -0
  345. package/src/tools/oapi/wiki/index.js +34 -0
  346. package/src/tools/oapi/wiki/space-node.d.ts +17 -0
  347. package/src/tools/oapi/wiki/space-node.js +230 -0
  348. package/src/tools/oapi/wiki/space.d.ts +15 -0
  349. package/src/tools/oapi/wiki/space.js +130 -0
  350. package/src/tools/oauth-batch-auth.d.ts +11 -0
  351. package/src/tools/oauth-batch-auth.js +142 -0
  352. package/src/tools/oauth-cards.d.ts +39 -0
  353. package/src/tools/oauth-cards.js +315 -0
  354. package/src/tools/oauth.d.ts +47 -0
  355. package/src/tools/oauth.js +620 -0
  356. package/src/tools/onboarding-auth.d.ts +27 -0
  357. package/src/tools/onboarding-auth.js +130 -0
  358. package/src/tools/tat/im/index.d.ts +15 -0
  359. package/src/tools/tat/im/index.js +18 -0
  360. package/src/tools/tat/im/resource.d.ts +15 -0
  361. package/src/tools/tat/im/resource.js +157 -0
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Directory listing for Feishu peers (users) and groups.
6
+ *
7
+ * Provides both config-based (offline) and live API directory
8
+ * lookups so the outbound subsystem and UI can resolve targets.
9
+ */
10
+ import { getLarkAccount } from '../core/accounts';
11
+ import { LarkClient } from '../core/lark-client';
12
+ import { normalizeFeishuTarget } from '../core/targets';
13
+ // ---------------------------------------------------------------------------
14
+ // Shared helpers
15
+ // ---------------------------------------------------------------------------
16
+ /** Case-insensitive substring match on id and optional name. */
17
+ function matchesQuery(id, name, query) {
18
+ if (!query)
19
+ return true;
20
+ return id.toLowerCase().includes(query) || (name?.toLowerCase().includes(query) ?? false);
21
+ }
22
+ /** Filter items and apply optional limit. */
23
+ function applyLimitSlice(items, limit) {
24
+ return limit && limit > 0 ? items.slice(0, limit) : items;
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // Config-based (offline) directory
28
+ // ---------------------------------------------------------------------------
29
+ /**
30
+ * List users known from the channel config (allowFrom + dms fields).
31
+ *
32
+ * Does not make any API calls -- useful when the bot is not yet
33
+ * connected or when credentials are unavailable.
34
+ */
35
+ export async function listFeishuDirectoryPeers(params) {
36
+ const account = getLarkAccount(params.cfg, params.accountId);
37
+ const feishuCfg = account.config;
38
+ const q = params.query?.trim().toLowerCase() || '';
39
+ const ids = new Set();
40
+ // Collect from allowFrom entries.
41
+ for (const entry of feishuCfg?.allowFrom ?? []) {
42
+ const trimmed = String(entry).trim();
43
+ if (trimmed && trimmed !== '*') {
44
+ ids.add(trimmed);
45
+ }
46
+ }
47
+ // Collect from per-user DM config keys.
48
+ for (const userId of Object.keys(feishuCfg?.dms ?? {})) {
49
+ const trimmed = userId.trim();
50
+ if (trimmed) {
51
+ ids.add(trimmed);
52
+ }
53
+ }
54
+ const peers = Array.from(ids)
55
+ .map((raw) => raw.trim())
56
+ .filter(Boolean)
57
+ .map((raw) => normalizeFeishuTarget(raw) ?? raw)
58
+ .filter((id) => matchesQuery(id, undefined, q))
59
+ .map((id) => ({ kind: 'user', id }));
60
+ return applyLimitSlice(peers, params.limit);
61
+ }
62
+ /**
63
+ * List groups known from the channel config (groups + groupAllowFrom).
64
+ */
65
+ export async function listFeishuDirectoryGroups(params) {
66
+ const account = getLarkAccount(params.cfg, params.accountId);
67
+ const feishuCfg = account.config;
68
+ const q = params.query?.trim().toLowerCase() || '';
69
+ const ids = new Set();
70
+ // Collect from per-group config keys.
71
+ for (const groupId of Object.keys(feishuCfg?.groups ?? {})) {
72
+ const trimmed = groupId.trim();
73
+ if (trimmed && trimmed !== '*') {
74
+ ids.add(trimmed);
75
+ }
76
+ }
77
+ // Collect from groupAllowFrom entries.
78
+ for (const entry of feishuCfg?.groupAllowFrom ?? []) {
79
+ const trimmed = String(entry).trim();
80
+ if (trimmed && trimmed !== '*') {
81
+ ids.add(trimmed);
82
+ }
83
+ }
84
+ const groups = Array.from(ids)
85
+ .map((raw) => raw.trim())
86
+ .filter(Boolean)
87
+ .filter((id) => matchesQuery(id, undefined, q))
88
+ .map((id) => ({ kind: 'group', id }));
89
+ return applyLimitSlice(groups, params.limit);
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Live API directory
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * List users via the Feishu contact/v3/users API.
96
+ *
97
+ * Falls back to config-based listing when credentials are missing or
98
+ * the API call fails.
99
+ */
100
+ export async function listFeishuDirectoryPeersLive(params) {
101
+ const account = getLarkAccount(params.cfg, params.accountId);
102
+ if (!account.configured) {
103
+ return listFeishuDirectoryPeers(params);
104
+ }
105
+ try {
106
+ const client = LarkClient.fromAccount(account).sdk;
107
+ const peers = [];
108
+ const limit = params.limit ?? 50;
109
+ if (limit <= 0)
110
+ return [];
111
+ const q = params.query?.trim().toLowerCase() || '';
112
+ let pageToken;
113
+ do {
114
+ const remaining = limit - peers.length;
115
+ const response = await client.contact.user.list({
116
+ params: {
117
+ page_size: Math.min(remaining, 50),
118
+ page_token: pageToken,
119
+ },
120
+ });
121
+ if (response.code !== 0 || !response.data?.items)
122
+ break;
123
+ for (const user of response.data.items) {
124
+ if (user.open_id && matchesQuery(user.open_id, user.name, q)) {
125
+ peers.push({
126
+ kind: 'user',
127
+ id: user.open_id,
128
+ name: user.name || undefined,
129
+ });
130
+ }
131
+ if (peers.length >= limit)
132
+ break;
133
+ }
134
+ pageToken = response.data?.page_token;
135
+ } while (pageToken && peers.length < limit);
136
+ return peers;
137
+ }
138
+ catch {
139
+ // Fallback to config-based listing on API failure.
140
+ return listFeishuDirectoryPeers(params);
141
+ }
142
+ }
143
+ /**
144
+ * List groups via the Feishu im/v1/chats API.
145
+ *
146
+ * Falls back to config-based listing when credentials are missing or
147
+ * the API call fails.
148
+ */
149
+ export async function listFeishuDirectoryGroupsLive(params) {
150
+ const account = getLarkAccount(params.cfg, params.accountId);
151
+ if (!account.configured) {
152
+ return listFeishuDirectoryGroups(params);
153
+ }
154
+ try {
155
+ const client = LarkClient.fromAccount(account).sdk;
156
+ const groups = [];
157
+ const limit = params.limit ?? 50;
158
+ if (limit <= 0)
159
+ return [];
160
+ const q = params.query?.trim().toLowerCase() || '';
161
+ let pageToken;
162
+ do {
163
+ const remaining = limit - groups.length;
164
+ const response = await client.im.chat.list({
165
+ params: {
166
+ page_size: Math.min(remaining, 100),
167
+ page_token: pageToken,
168
+ },
169
+ });
170
+ if (response.code !== 0 || !response.data?.items)
171
+ break;
172
+ for (const chat of response.data.items) {
173
+ if (chat.chat_id && matchesQuery(chat.chat_id, chat.name, q)) {
174
+ groups.push({
175
+ kind: 'group',
176
+ id: chat.chat_id,
177
+ name: chat.name || undefined,
178
+ });
179
+ }
180
+ if (groups.length >= limit)
181
+ break;
182
+ }
183
+ pageToken = response.data?.page_token;
184
+ } while (pageToken && groups.length < limit);
185
+ return groups;
186
+ }
187
+ catch {
188
+ // Fallback to config-based listing on API failure.
189
+ return listFeishuDirectoryGroups(params);
190
+ }
191
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Event handlers for the Feishu WebSocket monitor.
6
+ *
7
+ * Extracted from monitor.ts to improve testability and reduce
8
+ * function size. Each handler receives a MonitorContext with all
9
+ * dependencies needed to process the event.
10
+ */
11
+ import type { MonitorContext } from './types';
12
+ export declare function handleMessageEvent(ctx: MonitorContext, data: unknown): Promise<void>;
13
+ export declare function handleReactionEvent(ctx: MonitorContext, data: unknown): Promise<void>;
14
+ export declare function handleBotMembershipEvent(ctx: MonitorContext, data: unknown, action: 'added' | 'removed'): Promise<void>;
15
+ export declare function handleCardActionEvent(ctx: MonitorContext, data: unknown): Promise<unknown>;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Event handlers for the Feishu WebSocket monitor.
6
+ *
7
+ * Extracted from monitor.ts to improve testability and reduce
8
+ * function size. Each handler receives a MonitorContext with all
9
+ * dependencies needed to process the event.
10
+ */
11
+ import { handleFeishuMessage } from '../messaging/inbound/handler';
12
+ import { handleFeishuReaction, resolveReactionContext } from '../messaging/inbound/reaction-handler';
13
+ import { isMessageExpired } from '../messaging/inbound/dedup';
14
+ import { withTicket } from '../core/lark-ticket';
15
+ import { larkLogger } from '../core/lark-logger';
16
+ import { handleCardAction } from '../tools/auto-auth';
17
+ import { enqueueFeishuChatTask, buildQueueKey, hasActiveTask, getActiveDispatcher } from './chat-queue';
18
+ import { extractRawTextFromEvent, isLikelyAbortText } from './abort-detect';
19
+ const elog = larkLogger('channel/event-handlers');
20
+ // ---------------------------------------------------------------------------
21
+ // Event ownership validation
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Verify that the event's app_id matches the current account.
25
+ *
26
+ * Lark SDK EventDispatcher flattens the v2 envelope header (which
27
+ * contains `app_id`) into the handler `data` object, so `app_id` is
28
+ * available directly on `data`.
29
+ *
30
+ * Returns `false` (discard event) when the app_id does not match.
31
+ */
32
+ function isEventOwnershipValid(ctx, data) {
33
+ const expectedAppId = ctx.lark.account.appId;
34
+ if (!expectedAppId)
35
+ return true; // appId not configured — skip check
36
+ const eventAppId = data.app_id;
37
+ if (eventAppId == null)
38
+ return true; // SDK did not provide app_id — defensive skip
39
+ if (eventAppId !== expectedAppId) {
40
+ elog.warn('event app_id mismatch, discarding', {
41
+ accountId: ctx.accountId,
42
+ expected: expectedAppId,
43
+ received: String(eventAppId),
44
+ });
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Message handler
51
+ // ---------------------------------------------------------------------------
52
+ export async function handleMessageEvent(ctx, data) {
53
+ if (!isEventOwnershipValid(ctx, data))
54
+ return;
55
+ const { accountId, log, error } = ctx;
56
+ try {
57
+ const event = data;
58
+ const msgId = event.message?.message_id ?? 'unknown';
59
+ const chatId = event.message?.chat_id ?? '';
60
+ const threadId = event.message?.thread_id || undefined;
61
+ // Dedup — skip duplicate messages (e.g. from WebSocket reconnects).
62
+ if (!ctx.messageDedup.tryRecord(msgId, accountId)) {
63
+ log(`feishu[${accountId}]: duplicate message ${msgId}, skipping`);
64
+ return;
65
+ }
66
+ // Expiry — discard stale messages from reconnect replay.
67
+ if (isMessageExpired(event.message?.create_time)) {
68
+ log(`feishu[${accountId}]: message ${msgId} expired, discarding`);
69
+ return;
70
+ }
71
+ // ---- Abort fast-path ----
72
+ // If the message looks like an abort trigger and there is an active
73
+ // reply dispatcher for this chat, fire abortCard() immediately
74
+ // (before the message enters the serial queue) so the streaming
75
+ // card is terminated without waiting for the current task.
76
+ const abortText = extractRawTextFromEvent(event);
77
+ if (abortText && isLikelyAbortText(abortText)) {
78
+ const queueKey = buildQueueKey(accountId, chatId, threadId);
79
+ if (hasActiveTask(queueKey)) {
80
+ const active = getActiveDispatcher(queueKey);
81
+ if (active) {
82
+ log(`feishu[${accountId}]: abort fast-path triggered for chat ${chatId} (text="${abortText}")`);
83
+ active.abortController?.abort();
84
+ active.abortCard().catch((err) => {
85
+ error(`feishu[${accountId}]: abort fast-path abortCard failed: ${String(err)}`);
86
+ });
87
+ }
88
+ }
89
+ }
90
+ const { status } = enqueueFeishuChatTask({
91
+ accountId,
92
+ chatId,
93
+ threadId,
94
+ task: async () => {
95
+ try {
96
+ await withTicket({
97
+ messageId: msgId,
98
+ chatId,
99
+ accountId,
100
+ startTime: Date.now(),
101
+ senderOpenId: event.sender?.sender_id?.open_id || '',
102
+ chatType: event.message?.chat_type || undefined,
103
+ threadId,
104
+ }, () => handleFeishuMessage({
105
+ cfg: ctx.cfg,
106
+ event,
107
+ botOpenId: ctx.lark.botOpenId,
108
+ runtime: ctx.runtime,
109
+ chatHistories: ctx.chatHistories,
110
+ accountId,
111
+ }));
112
+ }
113
+ catch (err) {
114
+ error(`feishu[${accountId}]: error handling message: ${String(err)}`);
115
+ }
116
+ },
117
+ });
118
+ log(`feishu[${accountId}]: message ${msgId} in chat ${chatId}${threadId ? ` thread ${threadId}` : ''} — ${status}`);
119
+ }
120
+ catch (err) {
121
+ error(`feishu[${accountId}]: error handling message: ${String(err)}`);
122
+ }
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Reaction handler
126
+ // ---------------------------------------------------------------------------
127
+ export async function handleReactionEvent(ctx, data) {
128
+ if (!isEventOwnershipValid(ctx, data))
129
+ return;
130
+ const { accountId, log, error } = ctx;
131
+ try {
132
+ const event = data;
133
+ const msgId = event.message_id ?? 'unknown';
134
+ log(`feishu[${accountId}]: reaction event on message ${msgId}`);
135
+ // ---- Dedup: deterministic key based on message + emoji + operator ----
136
+ const emojiType = event.reaction_type?.emoji_type ?? '';
137
+ const operatorOpenId = event.user_id?.open_id ?? '';
138
+ const dedupKey = `${msgId}:reaction:${emojiType}:${operatorOpenId}`;
139
+ if (!ctx.messageDedup.tryRecord(dedupKey, accountId)) {
140
+ log(`feishu[${accountId}]: duplicate reaction ${dedupKey}, skipping`);
141
+ return;
142
+ }
143
+ // ---- Expiry: discard stale reaction events ----
144
+ if (isMessageExpired(event.action_time)) {
145
+ log(`feishu[${accountId}]: reaction on ${msgId} expired, discarding`);
146
+ return;
147
+ }
148
+ // ---- Pre-resolve real chatId before enqueuing ----
149
+ // The API call (3s timeout) runs outside the queue so it doesn't
150
+ // block the serial chain, and is read-only so ordering is irrelevant.
151
+ const preResolved = await resolveReactionContext({
152
+ cfg: ctx.cfg,
153
+ event,
154
+ botOpenId: ctx.lark.botOpenId,
155
+ runtime: ctx.runtime,
156
+ accountId,
157
+ });
158
+ if (!preResolved)
159
+ return;
160
+ // ---- Enqueue with the real chatId (matches normal message queue key) ----
161
+ const { status } = enqueueFeishuChatTask({
162
+ accountId,
163
+ chatId: preResolved.chatId,
164
+ threadId: preResolved.threadId,
165
+ task: async () => {
166
+ try {
167
+ await withTicket({
168
+ messageId: msgId,
169
+ chatId: preResolved.chatId,
170
+ accountId,
171
+ startTime: Date.now(),
172
+ senderOpenId: operatorOpenId,
173
+ chatType: preResolved.chatType,
174
+ threadId: preResolved.threadId,
175
+ }, () => handleFeishuReaction({
176
+ cfg: ctx.cfg,
177
+ event,
178
+ botOpenId: ctx.lark.botOpenId,
179
+ runtime: ctx.runtime,
180
+ chatHistories: ctx.chatHistories,
181
+ accountId,
182
+ preResolved,
183
+ }));
184
+ }
185
+ catch (err) {
186
+ error(`feishu[${accountId}]: error handling reaction: ${String(err)}`);
187
+ }
188
+ },
189
+ });
190
+ log(`feishu[${accountId}]: reaction on ${msgId} (chatId=${preResolved.chatId}) — ${status}`);
191
+ }
192
+ catch (err) {
193
+ error(`feishu[${accountId}]: error handling reaction event: ${String(err)}`);
194
+ }
195
+ }
196
+ // ---------------------------------------------------------------------------
197
+ // Bot membership handler
198
+ // ---------------------------------------------------------------------------
199
+ export async function handleBotMembershipEvent(ctx, data, action) {
200
+ if (!isEventOwnershipValid(ctx, data))
201
+ return;
202
+ const { accountId, log, error } = ctx;
203
+ try {
204
+ const event = data;
205
+ log(`feishu[${accountId}]: bot ${action} ${action === 'removed' ? 'from' : 'to'} chat ${event.chat_id}`);
206
+ }
207
+ catch (err) {
208
+ error(`feishu[${accountId}]: error handling bot ${action} event: ${String(err)}`);
209
+ }
210
+ }
211
+ // ---------------------------------------------------------------------------
212
+ // Card action handler
213
+ // ---------------------------------------------------------------------------
214
+ export async function handleCardActionEvent(ctx, data) {
215
+ try {
216
+ return await handleCardAction(data, ctx.cfg, ctx.accountId);
217
+ }
218
+ catch (err) {
219
+ elog.warn(`card.action.trigger handler error: ${err}`);
220
+ }
221
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * WebSocket monitoring for the Lark/Feishu channel plugin.
6
+ *
7
+ * Manages per-account WSClient connections and routes inbound Feishu
8
+ * events (messages, bot membership changes, read receipts) to the
9
+ * appropriate handlers.
10
+ */
11
+ import type { MonitorFeishuOpts } from './types';
12
+ export type { MonitorFeishuOpts } from './types';
13
+ /**
14
+ * Start monitoring for all enabled Feishu accounts (or a single
15
+ * account when `opts.accountId` is specified).
16
+ */
17
+ export declare function monitorFeishuProvider(opts?: MonitorFeishuOpts): Promise<void>;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * WebSocket monitoring for the Lark/Feishu channel plugin.
6
+ *
7
+ * Manages per-account WSClient connections and routes inbound Feishu
8
+ * events (messages, bot membership changes, read receipts) to the
9
+ * appropriate handlers.
10
+ */
11
+ import { getLarkAccount, getEnabledLarkAccounts } from '../core/accounts';
12
+ import { LarkClient } from '../core/lark-client';
13
+ import { MessageDedup } from '../messaging/inbound/dedup';
14
+ import { larkLogger } from '../core/lark-logger';
15
+ import { drainShutdownHooks } from '../core/shutdown-hooks';
16
+ import { handleMessageEvent, handleReactionEvent, handleBotMembershipEvent, handleCardActionEvent, } from './event-handlers';
17
+ const mlog = larkLogger('channel/monitor');
18
+ // ---------------------------------------------------------------------------
19
+ // Single-account monitor
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Start monitoring a single Feishu account.
23
+ *
24
+ * Creates a LarkClient, probes bot identity, registers event handlers,
25
+ * and starts a WebSocket connection. Returns a Promise that resolves
26
+ * when the abort signal fires (or immediately if already aborted).
27
+ */
28
+ async function monitorSingleAccount(params) {
29
+ const { account, runtime, abortSignal } = params;
30
+ const { accountId } = account;
31
+ const log = runtime?.log ?? ((...args) => mlog.info(args.map(String).join(' ')));
32
+ const error = runtime?.error ?? ((...args) => mlog.error(args.map(String).join(' ')));
33
+ // Only websocket mode is supported in the monitor path.
34
+ const connectionMode = account.config.connectionMode ?? 'websocket';
35
+ if (connectionMode !== 'websocket') {
36
+ log(`feishu[${accountId}]: webhook mode not implemented in monitor`);
37
+ return;
38
+ }
39
+ // Message dedup — filters duplicate deliveries from WebSocket reconnects.
40
+ const dedupCfg = account.config.dedup;
41
+ const messageDedup = new MessageDedup({
42
+ ttlMs: dedupCfg?.ttlMs,
43
+ maxEntries: dedupCfg?.maxEntries,
44
+ });
45
+ log(`feishu[${accountId}]: message dedup enabled (ttl=${messageDedup['ttlMs']}ms, max=${messageDedup['maxEntries']})`);
46
+ log(`feishu[${accountId}]: starting WebSocket connection...`);
47
+ // Create LarkClient instance — manages SDK client, WS, and bot identity.
48
+ const lark = LarkClient.fromAccount(account);
49
+ // Attach dedup instance so it is disposed together with the client.
50
+ lark.messageDedup = messageDedup;
51
+ /** Per-chat history maps (used for group-chat context window). */
52
+ const chatHistories = new Map();
53
+ const ctx = {
54
+ get cfg() {
55
+ return LarkClient.runtime.config.loadConfig();
56
+ },
57
+ lark,
58
+ accountId,
59
+ chatHistories,
60
+ messageDedup,
61
+ runtime,
62
+ log,
63
+ error,
64
+ };
65
+ await lark.startWS({
66
+ handlers: {
67
+ 'im.message.receive_v1': (data) => handleMessageEvent(ctx, data),
68
+ 'im.message.message_read_v1': async () => { },
69
+ 'im.message.reaction.created_v1': (data) => handleReactionEvent(ctx, data),
70
+ 'im.chat.member.bot.added_v1': (data) => handleBotMembershipEvent(ctx, data, 'added'),
71
+ 'im.chat.member.bot.deleted_v1': (data) => handleBotMembershipEvent(ctx, data, 'removed'),
72
+ // 飞书 SDK EventDispatcher.register 不支持带返回值的处理器,此处 as any 是 SDK 类型限制的变通
73
+ 'card.action.trigger': ((data) =>
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ handleCardActionEvent(ctx, data)),
76
+ },
77
+ abortSignal,
78
+ });
79
+ // startWS resolves when abortSignal fires — probe result is logged inside startWS.
80
+ log(`feishu[${accountId}]: bot open_id resolved: ${lark.botOpenId ?? 'unknown'}`);
81
+ log(`feishu[${accountId}]: WebSocket client started`);
82
+ mlog.info(`websocket started for account ${accountId}`);
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // Public API
86
+ // ---------------------------------------------------------------------------
87
+ /**
88
+ * Start monitoring for all enabled Feishu accounts (or a single
89
+ * account when `opts.accountId` is specified).
90
+ */
91
+ export async function monitorFeishuProvider(opts = {}) {
92
+ const cfg = opts.config;
93
+ if (!cfg) {
94
+ throw new Error('Config is required for Feishu monitor');
95
+ }
96
+ // Store the original global config so plugin commands (doctor, diagnose)
97
+ // can access cross-account information even when running inside an
98
+ // account-scoped config context.
99
+ LarkClient.setGlobalConfig(cfg);
100
+ const log = opts.runtime?.log ?? ((...args) => mlog.info(args.map(String).join(' ')));
101
+ // Single-account mode.
102
+ if (opts.accountId) {
103
+ const account = getLarkAccount(cfg, opts.accountId);
104
+ if (!account.enabled || !account.configured) {
105
+ throw new Error(`Feishu account "${opts.accountId}" not configured or disabled`);
106
+ }
107
+ await monitorSingleAccount({
108
+ cfg,
109
+ account,
110
+ runtime: opts.runtime,
111
+ abortSignal: opts.abortSignal,
112
+ });
113
+ await drainShutdownHooks({ log });
114
+ return;
115
+ }
116
+ // Multi-account mode: start all enabled accounts in parallel.
117
+ const accounts = getEnabledLarkAccounts(cfg);
118
+ if (accounts.length === 0) {
119
+ throw new Error('No enabled Feishu accounts configured');
120
+ }
121
+ log(`feishu: starting ${accounts.length} account(s): ${accounts.map((a) => a.accountId).join(', ')}`);
122
+ await Promise.all(accounts.map((account) => monitorSingleAccount({
123
+ cfg,
124
+ account,
125
+ runtime: opts.runtime,
126
+ abortSignal: opts.abortSignal,
127
+ })));
128
+ await drainShutdownHooks({ log });
129
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Onboarding configuration mutation helpers.
6
+ *
7
+ * Pure functions that apply Feishu channel configuration changes
8
+ * to a ClawdbotConfig. Extracted from onboarding.ts for reuse
9
+ * in CLI commands and other configuration flows.
10
+ */
11
+ import type { ClawdbotConfig, DmPolicy } from 'openclaw/plugin-sdk';
12
+ export declare function setFeishuDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy): ClawdbotConfig;
13
+ export declare function setFeishuAllowFrom(cfg: ClawdbotConfig, allowFrom: string[]): ClawdbotConfig;
14
+ export declare function setFeishuGroupPolicy(cfg: ClawdbotConfig, groupPolicy: 'open' | 'allowlist' | 'disabled'): ClawdbotConfig;
15
+ export declare function setFeishuGroupAllowFrom(cfg: ClawdbotConfig, groupAllowFrom: string[]): ClawdbotConfig;
16
+ export declare function setFeishuGroups(cfg: ClawdbotConfig, groups: Record<string, object>): ClawdbotConfig;
17
+ export declare function parseAllowFromInput(raw: string): string[];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Onboarding configuration mutation helpers.
6
+ *
7
+ * Pure functions that apply Feishu channel configuration changes
8
+ * to a ClawdbotConfig. Extracted from onboarding.ts for reuse
9
+ * in CLI commands and other configuration flows.
10
+ */
11
+ import { addWildcardAllowFrom } from 'openclaw/plugin-sdk';
12
+ // ---------------------------------------------------------------------------
13
+ // Config mutation helpers
14
+ // ---------------------------------------------------------------------------
15
+ export function setFeishuDmPolicy(cfg, dmPolicy) {
16
+ const allowFrom = dmPolicy === 'open'
17
+ ? addWildcardAllowFrom(cfg.channels?.feishu?.allowFrom)?.map((entry) => String(entry))
18
+ : undefined;
19
+ return {
20
+ ...cfg,
21
+ channels: {
22
+ ...cfg.channels,
23
+ feishu: {
24
+ ...cfg.channels?.feishu,
25
+ dmPolicy,
26
+ ...(allowFrom ? { allowFrom } : {}),
27
+ },
28
+ },
29
+ };
30
+ }
31
+ export function setFeishuAllowFrom(cfg, allowFrom) {
32
+ return {
33
+ ...cfg,
34
+ channels: {
35
+ ...cfg.channels,
36
+ feishu: {
37
+ ...cfg.channels?.feishu,
38
+ allowFrom,
39
+ },
40
+ },
41
+ };
42
+ }
43
+ export function setFeishuGroupPolicy(cfg, groupPolicy) {
44
+ return {
45
+ ...cfg,
46
+ channels: {
47
+ ...cfg.channels,
48
+ feishu: {
49
+ ...cfg.channels?.feishu,
50
+ enabled: true,
51
+ groupPolicy,
52
+ },
53
+ },
54
+ };
55
+ }
56
+ export function setFeishuGroupAllowFrom(cfg, groupAllowFrom) {
57
+ return {
58
+ ...cfg,
59
+ channels: {
60
+ ...cfg.channels,
61
+ feishu: {
62
+ ...cfg.channels?.feishu,
63
+ groupAllowFrom,
64
+ },
65
+ },
66
+ };
67
+ }
68
+ export function setFeishuGroups(cfg, groups) {
69
+ return {
70
+ ...cfg,
71
+ channels: {
72
+ ...cfg.channels,
73
+ feishu: {
74
+ ...cfg.channels?.feishu,
75
+ groups,
76
+ },
77
+ },
78
+ };
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Input helpers
82
+ // ---------------------------------------------------------------------------
83
+ export function parseAllowFromInput(raw) {
84
+ return raw
85
+ .split(/[\n,;]+/g)
86
+ .map((entry) => entry.trim())
87
+ .filter(Boolean);
88
+ }