@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/device-flow.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * OAuth 2.0 Device Authorization Grant (RFC 8628) for Lark/Feishu.\n *\n * Two-step flow:\n * 1. `requestDeviceAuthorization` \u2013 obtains device_code + user_code.\n * 2. `pollDeviceToken` \u2013 polls the token endpoint until the user authorises,\n * rejects, or the code expires.\n *\n * All HTTP calls use the built-in `fetch` (Node 18+). The Lark SDK is not\n * used here because these OAuth endpoints are outside the SDK's scope.\n */\n\nimport type { LarkBrand } from './types';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/device-flow');\nimport { feishuFetch } from './feishu-fetch';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DeviceAuthResponse {\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number; // seconds\n interval: number; // recommended polling interval (seconds)\n}\n\nexport interface DeviceFlowTokenData {\n accessToken: string;\n refreshToken: string;\n expiresIn: number; // seconds\n refreshExpiresIn: number; // seconds\n scope: string;\n}\n\nexport type DeviceFlowResult =\n | { ok: true; token: DeviceFlowTokenData }\n | { ok: false; error: DeviceFlowError; message: string };\n\nexport type DeviceFlowError = 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token';\n\n// ---------------------------------------------------------------------------\n// Endpoint resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the two OAuth endpoint URLs based on the configured brand.\n */\nexport function resolveOAuthEndpoints(brand: LarkBrand): {\n deviceAuthorization: string;\n token: string;\n} {\n if (!brand || brand === 'feishu') {\n return {\n deviceAuthorization: 'https://accounts.feishu.cn/oauth/v1/device_authorization',\n token: 'https://open.feishu.cn/open-apis/authen/v2/oauth/token',\n };\n }\n if (brand === 'lark') {\n return {\n deviceAuthorization: 'https://accounts.larksuite.com/oauth/v1/device_authorization',\n token: 'https://open.larksuite.com/open-apis/authen/v2/oauth/token',\n };\n }\n // Custom domain \u2013 derive paths by convention.\n // Smart derivation: open.X \u2192 accounts.X for the device authorization endpoint.\n const base = brand.replace(/\\/+$/, '');\n let accountsBase = base;\n try {\n const parsed = new URL(base);\n if (parsed.hostname.startsWith('open.')) {\n accountsBase = `${parsed.protocol}//${parsed.hostname.replace(/^open\\./, 'accounts.')}`;\n }\n } catch {\n /* fallback to base */\n }\n\n return {\n deviceAuthorization: `${accountsBase}/oauth/v1/device_authorization`,\n token: `${base}/open-apis/authen/v2/oauth/token`,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Step 1 \u2013 Device Authorization Request\n// ---------------------------------------------------------------------------\n\n/**\n * Request a device authorisation code from the Feishu OAuth server.\n *\n * Uses Confidential Client authentication (HTTP Basic with appId:appSecret).\n * The `offline_access` scope is automatically appended so that the token\n * response includes a refresh_token.\n */\nexport async function requestDeviceAuthorization(params: {\n appId: string;\n appSecret: string;\n brand: LarkBrand;\n scope?: string;\n endpoints?: { deviceAuthorization: string; token: string };\n}): Promise<DeviceAuthResponse> {\n const { appId, appSecret, brand } = params;\n const endpoints = params.endpoints || resolveOAuthEndpoints(brand);\n\n // Ensure offline_access is always requested.\n let scope = params.scope ?? '';\n if (!scope.includes('offline_access')) {\n scope = scope ? `${scope} offline_access` : 'offline_access';\n }\n\n const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString('base64');\n\n const body = new URLSearchParams();\n body.set('client_id', appId);\n body.set('scope', scope);\n\n log.info(\n `requesting device authorization (scope=\"${scope}\") url=${endpoints.deviceAuthorization} token_url=${endpoints.token}`,\n );\n\n const resp = await feishuFetch(endpoints.deviceAuthorization, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Basic ${basicAuth}`,\n },\n body: body.toString(),\n });\n\n const text = await resp.text();\n log.info(`response status=${resp.status} body=${text.slice(0, 500)}`);\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(text) as Record<string, unknown>;\n } catch {\n throw new Error(`Device authorization failed: HTTP ${resp.status} \u2013 ${text.slice(0, 200)}`);\n }\n\n if (!resp.ok || data.error) {\n const msg = (data.error_description as string) ?? (data.error as string) ?? 'Unknown error';\n throw new Error(`Device authorization failed: ${msg}`);\n }\n\n const expiresIn = (data.expires_in as number) ?? 240;\n const interval = (data.interval as number) ?? 5;\n log.info(`device_code obtained, expires_in=${expiresIn}s (${Math.round(expiresIn / 60)}min), interval=${interval}s`);\n\n return {\n deviceCode: data.device_code as string,\n userCode: data.user_code as string,\n verificationUri: data.verification_uri as string,\n verificationUriComplete: (data.verification_uri_complete as string) ?? (data.verification_uri as string),\n expiresIn,\n interval,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Step 2 \u2013 Poll Token Endpoint\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\n/**\n * Poll the token endpoint until the user authorises, rejects, or the code\n * expires.\n *\n * Handles `authorization_pending` (keep polling), `slow_down` (back off by\n * +5 s), `access_denied` and `expired_token` (terminal errors).\n *\n * Pass an `AbortSignal` to cancel polling from the outside.\n */\nexport async function pollDeviceToken(params: {\n appId: string;\n appSecret: string;\n brand: LarkBrand;\n deviceCode: string;\n interval: number;\n expiresIn: number;\n signal?: AbortSignal;\n endpoints?: { deviceAuthorization: string; token: string };\n}): Promise<DeviceFlowResult> {\n const MAX_POLL_INTERVAL = 60; // slow_down \u6700\u5927\u95F4\u9694 60 \u79D2\n const MAX_POLL_ATTEMPTS = 200; // \u5B89\u5168\u4E0A\u9650\uFF08\u8FDC\u8D85\u8BBE\u5907\u7801\u6709\u6548\u671F\uFF09\n\n const { appId, appSecret, brand, deviceCode, expiresIn, signal } = params;\n let interval = params.interval;\n const endpoints = params.endpoints || resolveOAuthEndpoints(brand);\n const deadline = Date.now() + expiresIn * 1000;\n let attempts = 0;\n\n while (Date.now() < deadline && attempts < MAX_POLL_ATTEMPTS) {\n attempts++;\n if (signal?.aborted) {\n return { ok: false, error: 'expired_token', message: 'Polling was cancelled' };\n }\n\n await sleep(interval * 1000, signal);\n\n let data: Record<string, unknown>;\n try {\n const resp = await feishuFetch(endpoints.token, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: appId,\n client_secret: appSecret,\n }).toString(),\n });\n data = (await resp.json()) as Record<string, unknown>;\n } catch (err) {\n log.warn(`poll network error: ${err}`);\n interval = Math.min(interval + 1, MAX_POLL_INTERVAL);\n continue;\n }\n\n const error = data.error as string | undefined;\n\n if (!error && data.access_token) {\n log.info('token obtained successfully');\n const refreshToken = (data.refresh_token as string) ?? '';\n const expiresIn = (data.expires_in as number) ?? 7200;\n let refreshExpiresIn = (data.refresh_token_expires_in as number) ?? 604800;\n if (!refreshToken) {\n log.warn('no refresh_token in response, token will not be refreshable');\n refreshExpiresIn = expiresIn;\n }\n return {\n ok: true,\n token: {\n accessToken: data.access_token as string,\n refreshToken,\n expiresIn,\n refreshExpiresIn,\n scope: (data.scope as string) ?? '',\n },\n };\n }\n\n if (error === 'authorization_pending') {\n log.debug('authorization_pending, retrying...');\n continue;\n }\n\n if (error === 'slow_down') {\n interval = Math.min(interval + 5, MAX_POLL_INTERVAL);\n log.info(`slow_down, interval increased to ${interval}s`);\n continue;\n }\n\n if (error === 'access_denied') {\n log.info('user denied authorization');\n return { ok: false, error: 'access_denied', message: '\u7528\u6237\u62D2\u7EDD\u4E86\u6388\u6743' };\n }\n\n if (error === 'expired_token' || error === 'invalid_grant') {\n log.info(`device code expired/invalid (error=${error})`);\n return { ok: false, error: 'expired_token', message: '\u6388\u6743\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77' };\n }\n\n // Unknown error \u2013 treat as terminal.\n const desc = (data.error_description as string) ?? error ?? 'Unknown error';\n log.warn(`unexpected error: error=${error}, desc=${desc}`);\n return { ok: false, error: 'expired_token', message: desc };\n }\n\n if (attempts >= MAX_POLL_ATTEMPTS) {\n log.warn(`max poll attempts (${MAX_POLL_ATTEMPTS}) reached`);\n }\n return { ok: false, error: 'expired_token', message: '\u6388\u6743\u8D85\u65F6\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77' };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["expiresIn"]
7
+ }
@@ -0,0 +1,12 @@
1
+ import { getUserAgent } from "./version";
2
+ function feishuFetch(url, init) {
3
+ const headers = {
4
+ ...init?.headers,
5
+ "User-Agent": getUserAgent()
6
+ };
7
+ return fetch(url, { ...init, headers });
8
+ }
9
+ export {
10
+ feishuFetch
11
+ };
12
+ //# sourceMappingURL=feishu-fetch.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/feishu-fetch.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Header-aware fetch for Feishu API calls.\n *\n * Drop-in replacement for `fetch()` that automatically injects\n * the User-Agent header.\n */\n\nimport { getUserAgent } from './version';\n\n/**\n * Drop-in replacement for `fetch()` that automatically injects\n * the User-Agent header.\n *\n * Used by `device-flow.ts` and `uat-client.ts` so that the custom\n * User-Agent is transparently applied without changing every\n * call-site's signature.\n */\nexport function feishuFetch(url: string | URL | Request, init?: RequestInit): Promise<Response> {\n const headers = {\n ...init?.headers,\n 'User-Agent': getUserAgent(),\n };\n\n return fetch(url, { ...init, headers });\n}\n"],
5
+ "mappings": "AAUA,SAAS,oBAAoB;AAUtB,SAAS,YAAY,KAA6B,MAAuC;AAC9F,QAAM,UAAU;AAAA,IACd,GAAG,MAAM;AAAA,IACT,cAAc,aAAa;AAAA,EAC7B;AAEA,SAAO,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC;AACxC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ const DEFAULT_FOOTER_CONFIG = {
2
+ status: false,
3
+ elapsed: false
4
+ };
5
+ function resolveFooterConfig(cfg) {
6
+ if (!cfg) return { ...DEFAULT_FOOTER_CONFIG };
7
+ return {
8
+ status: cfg.status ?? DEFAULT_FOOTER_CONFIG.status,
9
+ elapsed: cfg.elapsed ?? DEFAULT_FOOTER_CONFIG.elapsed
10
+ };
11
+ }
12
+ export {
13
+ DEFAULT_FOOTER_CONFIG,
14
+ resolveFooterConfig
15
+ };
16
+ //# sourceMappingURL=footer-config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/footer-config.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Default values and resolution logic for the Feishu card footer configuration.\n *\n * Each boolean flag controls whether a particular metadata item is displayed\n * in the card footer (e.g. elapsed time, model name).\n */\n\nimport type { FeishuFooterConfig } from './types';\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\n/**\n * The default footer configuration.\n *\n * By default all metadata items are hidden \u2014 neither status text\n * (\"\u5DF2\u5B8C\u6210\" / \"\u51FA\u9519\" / \"\u5DF2\u505C\u6B62\") nor elapsed time are shown.\n */\nexport const DEFAULT_FOOTER_CONFIG: Required<FeishuFooterConfig> = {\n status: false,\n elapsed: false,\n};\n\n// ---------------------------------------------------------------------------\n// Resolver\n// ---------------------------------------------------------------------------\n\n/**\n * Merge a partial footer configuration with `DEFAULT_FOOTER_CONFIG`.\n *\n * Fields present in the input take precedence; anything absent falls back\n * to the default value.\n */\nexport function resolveFooterConfig(cfg?: FeishuFooterConfig): Required<FeishuFooterConfig> {\n if (!cfg) return { ...DEFAULT_FOOTER_CONFIG };\n return {\n status: cfg.status ?? DEFAULT_FOOTER_CONFIG.status,\n elapsed: cfg.elapsed ?? DEFAULT_FOOTER_CONFIG.elapsed,\n };\n}\n"],
5
+ "mappings": "AAsBO,MAAM,wBAAsD;AAAA,EACjE,QAAQ;AAAA,EACR,SAAS;AACX;AAYO,SAAS,oBAAoB,KAAwD;AAC1F,MAAI,CAAC,IAAK,QAAO,EAAE,GAAG,sBAAsB;AAC5C,SAAO;AAAA,IACL,QAAQ,IAAI,UAAU,sBAAsB;AAAA,IAC5C,SAAS,IAAI,WAAW,sBAAsB;AAAA,EAChD;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,322 @@
1
+ import * as Lark from "@larksuiteoapi/node-sdk";
2
+ import { getLarkAccount } from "./accounts";
3
+ import { clearUserNameCache } from "../messaging/inbound/user-name-cache";
4
+ import { clearChatInfoCache } from "./chat-info-cache";
5
+ import { getUserAgent } from "./version";
6
+ import { larkLogger } from "./lark-logger";
7
+ const log = larkLogger("core/lark-client");
8
+ const GLOBAL_LARK_USER_AGENT_KEY = "LARK_USER_AGENT";
9
+ function installGlobalUserAgent() {
10
+ globalThis[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();
11
+ }
12
+ installGlobalUserAgent();
13
+ Lark.defaultHttpInstance.interceptors.request.handlers = [];
14
+ Lark.defaultHttpInstance.interceptors.request.use(
15
+ (req) => {
16
+ if (req.headers) {
17
+ req.headers["User-Agent"] = getUserAgent();
18
+ }
19
+ return req;
20
+ },
21
+ void 0,
22
+ { synchronous: true }
23
+ );
24
+ const BRAND_TO_DOMAIN = {
25
+ feishu: Lark.Domain.Feishu,
26
+ lark: Lark.Domain.Lark
27
+ };
28
+ function resolveBrand(brand) {
29
+ return BRAND_TO_DOMAIN[brand ?? "feishu"] ?? brand.replace(/\/+$/, "");
30
+ }
31
+ const cache = /* @__PURE__ */ new Map();
32
+ class LarkClient {
33
+ account;
34
+ _sdk = null;
35
+ _wsClient = null;
36
+ _botOpenId;
37
+ _botName;
38
+ _lastProbeResult = null;
39
+ _lastProbeAt = 0;
40
+ /** Attached message deduplicator — disposed together with the client. */
41
+ messageDedup = null;
42
+ // ---- Plugin runtime (singleton) ------------------------------------------
43
+ static _runtime = null;
44
+ /** Persist the runtime instance for later retrieval (activate 阶段调用一次). */
45
+ static setRuntime(runtime) {
46
+ LarkClient._runtime = runtime;
47
+ }
48
+ /** Retrieve the stored runtime instance. Throws if not yet initialised. */
49
+ static get runtime() {
50
+ if (!LarkClient._runtime) {
51
+ throw new Error(
52
+ "Feishu plugin runtime has not been initialised. Ensure LarkClient.setRuntime() is called during plugin activation."
53
+ );
54
+ }
55
+ return LarkClient._runtime;
56
+ }
57
+ // ---- Global config (singleton) -------------------------------------------
58
+ //
59
+ // Plugin commands receive an account-scoped config (channels.feishu replaced
60
+ // with the merged per-account config, `accounts` map stripped). Commands
61
+ // that need cross-account visibility (e.g. doctor, diagnose) read the
62
+ // original global config from here.
63
+ static _globalConfig = null;
64
+ /** Store the original global config (called during monitor startup). */
65
+ static setGlobalConfig(cfg) {
66
+ LarkClient._globalConfig = cfg;
67
+ }
68
+ /** Retrieve the stored global config, or `null` if not yet set. */
69
+ static get globalConfig() {
70
+ return LarkClient._globalConfig;
71
+ }
72
+ // --------------------------------------------------------------------------
73
+ constructor(account) {
74
+ this.account = account;
75
+ }
76
+ /** Shorthand for `this.account.accountId`. */
77
+ get accountId() {
78
+ return this.account.accountId;
79
+ }
80
+ // ---- Static factory / cache ------------------------------------------------
81
+ /** Resolve account from config and return a cached `LarkClient`. */
82
+ static fromCfg(cfg, accountId) {
83
+ return LarkClient.fromAccount(getLarkAccount(cfg, accountId));
84
+ }
85
+ /**
86
+ * Get (or create) a cached `LarkClient` for the given account.
87
+ * If the cached instance has stale credentials it is replaced.
88
+ */
89
+ static fromAccount(account) {
90
+ const existing = cache.get(account.accountId);
91
+ if (existing && existing.account.appId === account.appId && existing.account.appSecret === account.appSecret) {
92
+ return existing;
93
+ }
94
+ if (existing) {
95
+ log.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
96
+ existing.dispose();
97
+ }
98
+ const instance = new LarkClient(account);
99
+ cache.set(account.accountId, instance);
100
+ return instance;
101
+ }
102
+ /**
103
+ * Create an ephemeral `LarkClient` from bare credentials.
104
+ * The instance is **not** added to the global cache — suitable for
105
+ * one-off probe / diagnose calls that should not pollute account state.
106
+ */
107
+ static fromCredentials(credentials) {
108
+ const base = {
109
+ accountId: credentials.accountId ?? "default",
110
+ enabled: true,
111
+ brand: credentials.brand ?? "feishu",
112
+ config: {}
113
+ };
114
+ const account = credentials.appId && credentials.appSecret ? { ...base, configured: true, appId: credentials.appId, appSecret: credentials.appSecret } : { ...base, configured: false, appId: credentials.appId, appSecret: credentials.appSecret };
115
+ return new LarkClient(account);
116
+ }
117
+ /** Look up a cached instance by accountId. */
118
+ static get(accountId) {
119
+ return cache.get(accountId) ?? null;
120
+ }
121
+ /**
122
+ * Dispose one or all cached instances.
123
+ * With `accountId` — dispose that single instance.
124
+ * Without — dispose every cached instance and clear the cache.
125
+ */
126
+ static clearCache(accountId) {
127
+ if (accountId !== void 0) {
128
+ cache.get(accountId)?.dispose();
129
+ clearUserNameCache(accountId);
130
+ clearChatInfoCache(accountId);
131
+ } else {
132
+ for (const inst of cache.values()) inst.dispose();
133
+ clearUserNameCache();
134
+ clearChatInfoCache();
135
+ }
136
+ }
137
+ // ---- SDK client (lazy) -----------------------------------------------------
138
+ /** Lazily-created Lark SDK client. */
139
+ get sdk() {
140
+ if (!this._sdk) {
141
+ const { appId, appSecret } = this.requireCredentials();
142
+ this._sdk = new Lark.Client({
143
+ appId,
144
+ appSecret,
145
+ appType: Lark.AppType.SelfBuild,
146
+ domain: resolveBrand(this.account.brand)
147
+ });
148
+ }
149
+ return this._sdk;
150
+ }
151
+ // ---- Bot identity ----------------------------------------------------------
152
+ /**
153
+ * Probe bot identity via the `bot/v3/info` API.
154
+ * Results are cached on the instance for subsequent access via
155
+ * `botOpenId` / `botName`.
156
+ */
157
+ async probe(opts) {
158
+ const maxAge = opts?.maxAgeMs ?? 0;
159
+ if (maxAge > 0 && this._lastProbeResult && Date.now() - this._lastProbeAt < maxAge) {
160
+ return this._lastProbeResult;
161
+ }
162
+ if (!this.account.appId || !this.account.appSecret) {
163
+ return { ok: false, error: "missing credentials (appId, appSecret)" };
164
+ }
165
+ try {
166
+ const res = await this.sdk.request({
167
+ method: "GET",
168
+ url: "/open-apis/bot/v3/info",
169
+ data: {}
170
+ });
171
+ if (res.code !== 0) {
172
+ const result2 = {
173
+ ok: false,
174
+ appId: this.account.appId,
175
+ error: `API error: ${res.msg || `code ${res.code}`}`
176
+ };
177
+ this._lastProbeResult = result2;
178
+ this._lastProbeAt = Date.now();
179
+ return result2;
180
+ }
181
+ const bot = res.bot || res.data?.bot;
182
+ this._botOpenId = bot?.open_id;
183
+ this._botName = bot?.bot_name;
184
+ const result = {
185
+ ok: true,
186
+ appId: this.account.appId,
187
+ botName: this._botName,
188
+ botOpenId: this._botOpenId
189
+ };
190
+ this._lastProbeResult = result;
191
+ this._lastProbeAt = Date.now();
192
+ return result;
193
+ } catch (err) {
194
+ const result = {
195
+ ok: false,
196
+ appId: this.account.appId,
197
+ error: err instanceof Error ? err.message : String(err)
198
+ };
199
+ this._lastProbeResult = result;
200
+ this._lastProbeAt = Date.now();
201
+ return result;
202
+ }
203
+ }
204
+ /** Cached bot open_id (available after `probe()` or `startWS()`). */
205
+ get botOpenId() {
206
+ return this._botOpenId;
207
+ }
208
+ /** Cached bot name (available after `probe()` or `startWS()`). */
209
+ get botName() {
210
+ return this._botName;
211
+ }
212
+ // ---- WebSocket lifecycle ---------------------------------------------------
213
+ /**
214
+ * Start WebSocket event monitoring.
215
+ *
216
+ * Flow: probe bot identity → EventDispatcher → WSClient → start.
217
+ * The returned Promise resolves when `abortSignal` fires.
218
+ */
219
+ async startWS(opts) {
220
+ const { handlers, abortSignal, autoProbe = true } = opts;
221
+ if (autoProbe) await this.probe();
222
+ const dispatcher = new Lark.EventDispatcher({
223
+ encryptKey: this.account.encryptKey ?? "",
224
+ verificationToken: this.account.verificationToken ?? ""
225
+ });
226
+ dispatcher.register(handlers);
227
+ const { appId, appSecret } = this.requireCredentials();
228
+ if (this._wsClient) {
229
+ log.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
230
+ try {
231
+ this._wsClient.close({ force: true });
232
+ } catch {
233
+ }
234
+ this._wsClient = null;
235
+ }
236
+ this._wsClient = new Lark.WSClient({
237
+ appId,
238
+ appSecret,
239
+ domain: resolveBrand(this.account.brand),
240
+ loggerLevel: Lark.LoggerLevel.info
241
+ });
242
+ const wsClientAny = this._wsClient;
243
+ const origHandleEventData = wsClientAny.handleEventData.bind(wsClientAny);
244
+ wsClientAny.handleEventData = (data) => {
245
+ const msgType = data.headers?.find?.((h) => h.key === "type")?.value;
246
+ if (msgType === "card") {
247
+ const patchedData = {
248
+ ...data,
249
+ headers: data.headers.map((h) => h.key === "type" ? { ...h, value: "event" } : h)
250
+ };
251
+ return origHandleEventData(patchedData);
252
+ }
253
+ return origHandleEventData(data);
254
+ };
255
+ await this.waitForAbort(dispatcher, abortSignal);
256
+ }
257
+ /** Whether a WebSocket client is currently active. */
258
+ get wsConnected() {
259
+ return this._wsClient !== null;
260
+ }
261
+ /** Disconnect WebSocket but keep instance in cache. */
262
+ disconnect() {
263
+ if (this._wsClient) {
264
+ log.info(`disconnecting WebSocket`, { accountId: this.accountId });
265
+ try {
266
+ this._wsClient.close({ force: true });
267
+ } catch {
268
+ }
269
+ }
270
+ this._wsClient = null;
271
+ if (this.messageDedup) {
272
+ log.info(`disposing message dedup`, { accountId: this.accountId, size: this.messageDedup.size });
273
+ this.messageDedup.dispose();
274
+ this.messageDedup = null;
275
+ }
276
+ }
277
+ /** Disconnect + remove from cache. */
278
+ dispose() {
279
+ this.disconnect();
280
+ cache.delete(this.accountId);
281
+ }
282
+ // ---- Private helpers -------------------------------------------------------
283
+ /** Assert credentials exist or throw. */
284
+ requireCredentials() {
285
+ const appId = this.account.appId;
286
+ const appSecret = this.account.appSecret;
287
+ if (!appId || !appSecret) {
288
+ throw new Error(`LarkClient[${this.accountId}]: appId and appSecret are required`);
289
+ }
290
+ return { appId, appSecret };
291
+ }
292
+ /**
293
+ * Start the WSClient and return a promise that resolves when the
294
+ * abort signal fires (or immediately if already aborted).
295
+ */
296
+ waitForAbort(dispatcher, signal) {
297
+ return new Promise((resolve, reject) => {
298
+ if (signal?.aborted) {
299
+ this.disconnect();
300
+ return resolve();
301
+ }
302
+ signal?.addEventListener(
303
+ "abort",
304
+ () => {
305
+ this.disconnect();
306
+ resolve();
307
+ },
308
+ { once: true }
309
+ );
310
+ try {
311
+ void this._wsClient.start({ eventDispatcher: dispatcher });
312
+ } catch (err) {
313
+ this.disconnect();
314
+ reject(err);
315
+ }
316
+ });
317
+ }
318
+ }
319
+ export {
320
+ LarkClient
321
+ };
322
+ //# sourceMappingURL=lark-client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/lark-client.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Feishu / Lark SDK client management.\n *\n * Provides `LarkClient` \u2014 a unified manager for Lark SDK client instances,\n * WebSocket connections, EventDispatcher lifecycle, and bot identity.\n *\n * Consumers obtain instances via factory methods:\n * - `LarkClient.fromCfg(cfg, accountId)` \u2014 resolve account from config\n * - `LarkClient.fromAccount(account)` \u2014 from a pre-resolved account\n * - `LarkClient.fromCredentials(credentials)` \u2014 ephemeral instance (not cached)\n */\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport * as Lark from '@larksuiteoapi/node-sdk';\n\nimport type { ClawdbotConfig, PluginRuntime } from 'openclaw/plugin-sdk';\nimport type { LarkBrand, LarkAccount, FeishuProbeResult } from './types';\nimport { getLarkAccount } from './accounts';\nimport { clearUserNameCache } from '../messaging/inbound/user-name-cache';\nimport { clearChatInfoCache } from './chat-info-cache';\nimport { getUserAgent } from './version';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/lark-client');\nimport type { MessageDedup } from '../messaging/inbound/dedup';\n\n// ---------------------------------------------------------------------------\n// \u6CE8\u5165 User-Agent \u5230\u6240\u6709\u98DE\u4E66 SDK \u8BF7\u6C42\n// ---------------------------------------------------------------------------\n\nconst GLOBAL_LARK_USER_AGENT_KEY = 'LARK_USER_AGENT';\n\nfunction installGlobalUserAgent(): void {\n // node-sdk \u5185\u7F6E\u62E6\u622A\u5668\u6700\u7EC8\u4F1A\u8BFB\u53D6 global.LARK_USER_AGENT \u5E76\u8986\u76D6 User-Agent\n (globalThis as Record<string, unknown>)[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();\n}\n\ninstallGlobalUserAgent();\nLark.defaultHttpInstance.interceptors.request.handlers = [];\n// \u4F7F\u7528 interceptors \u5728\u6240\u6709 HTTP \u8BF7\u6C42\u4E2D\u6CE8\u5165 User-Agent header\nLark.defaultHttpInstance.interceptors.request.use(\n (req) => {\n if (req.headers) {\n req.headers['User-Agent'] = getUserAgent();\n }\n return req;\n },\n undefined,\n { synchronous: true },\n);\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n/** Credential set accepted by the ephemeral `fromCredentials` factory. */\nexport interface LarkClientCredentials {\n accountId?: string;\n appId?: string;\n appSecret?: string;\n brand?: LarkBrand;\n}\n\n// ---------------------------------------------------------------------------\n// Brand \u2192 SDK domain\n// ---------------------------------------------------------------------------\n\nconst BRAND_TO_DOMAIN: Record<string, Lark.Domain> = {\n feishu: Lark.Domain.Feishu,\n lark: Lark.Domain.Lark,\n};\n\n/** Map a `LarkBrand` to the SDK `domain` parameter. */\nfunction resolveBrand(brand: LarkBrand | undefined): Lark.Domain | string {\n return BRAND_TO_DOMAIN[brand ?? 'feishu'] ?? brand!.replace(/\\/+$/, '');\n}\n\n// ---------------------------------------------------------------------------\n// LarkClient\n// ---------------------------------------------------------------------------\n\n/** Instance cache keyed by accountId. */\nconst cache = new Map<string, LarkClient>();\n\nexport class LarkClient {\n readonly account: LarkAccount;\n\n private _sdk: Lark.Client | null = null;\n private _wsClient: Lark.WSClient | null = null;\n private _botOpenId: string | undefined;\n private _botName: string | undefined;\n private _lastProbeResult: FeishuProbeResult | null = null;\n private _lastProbeAt = 0;\n\n /** Attached message deduplicator \u2014 disposed together with the client. */\n messageDedup: MessageDedup | null = null;\n\n // ---- Plugin runtime (singleton) ------------------------------------------\n\n private static _runtime: PluginRuntime | null = null;\n\n /** Persist the runtime instance for later retrieval (activate \u9636\u6BB5\u8C03\u7528\u4E00\u6B21). */\n static setRuntime(runtime: PluginRuntime): void {\n LarkClient._runtime = runtime;\n }\n\n /** Retrieve the stored runtime instance. Throws if not yet initialised. */\n static get runtime(): PluginRuntime {\n if (!LarkClient._runtime) {\n throw new Error(\n 'Feishu plugin runtime has not been initialised. ' +\n 'Ensure LarkClient.setRuntime() is called during plugin activation.',\n );\n }\n return LarkClient._runtime;\n }\n\n // ---- Global config (singleton) -------------------------------------------\n //\n // Plugin commands receive an account-scoped config (channels.feishu replaced\n // with the merged per-account config, `accounts` map stripped). Commands\n // that need cross-account visibility (e.g. doctor, diagnose) read the\n // original global config from here.\n\n private static _globalConfig: ClawdbotConfig | null = null;\n\n /** Store the original global config (called during monitor startup). */\n static setGlobalConfig(cfg: ClawdbotConfig): void {\n LarkClient._globalConfig = cfg;\n }\n\n /** Retrieve the stored global config, or `null` if not yet set. */\n static get globalConfig(): ClawdbotConfig | null {\n return LarkClient._globalConfig;\n }\n\n // --------------------------------------------------------------------------\n\n private constructor(account: LarkAccount) {\n this.account = account;\n }\n\n /** Shorthand for `this.account.accountId`. */\n get accountId(): string {\n return this.account.accountId;\n }\n\n // ---- Static factory / cache ------------------------------------------------\n\n /** Resolve account from config and return a cached `LarkClient`. */\n static fromCfg(cfg: ClawdbotConfig, accountId?: string): LarkClient {\n return LarkClient.fromAccount(getLarkAccount(cfg, accountId));\n }\n\n /**\n * Get (or create) a cached `LarkClient` for the given account.\n * If the cached instance has stale credentials it is replaced.\n */\n static fromAccount(account: LarkAccount): LarkClient {\n const existing = cache.get(account.accountId);\n if (existing && existing.account.appId === account.appId && existing.account.appSecret === account.appSecret) {\n return existing;\n }\n // Credentials changed \u2014 tear down the stale instance before replacing it.\n if (existing) {\n log.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });\n existing.dispose();\n }\n const instance = new LarkClient(account);\n cache.set(account.accountId, instance);\n return instance;\n }\n\n /**\n * Create an ephemeral `LarkClient` from bare credentials.\n * The instance is **not** added to the global cache \u2014 suitable for\n * one-off probe / diagnose calls that should not pollute account state.\n */\n static fromCredentials(credentials: LarkClientCredentials): LarkClient {\n const base = {\n accountId: credentials.accountId ?? 'default',\n enabled: true as const,\n brand: credentials.brand ?? ('feishu' as const),\n config: {} as any,\n };\n\n const account: LarkAccount =\n credentials.appId && credentials.appSecret\n ? { ...base, configured: true as const, appId: credentials.appId, appSecret: credentials.appSecret }\n : { ...base, configured: false as const, appId: credentials.appId, appSecret: credentials.appSecret };\n\n return new LarkClient(account);\n }\n\n /** Look up a cached instance by accountId. */\n static get(accountId: string): LarkClient | null {\n return cache.get(accountId) ?? null;\n }\n\n /**\n * Dispose one or all cached instances.\n * With `accountId` \u2014 dispose that single instance.\n * Without \u2014 dispose every cached instance and clear the cache.\n */\n static clearCache(accountId?: string): void {\n if (accountId !== undefined) {\n cache.get(accountId)?.dispose();\n clearUserNameCache(accountId);\n clearChatInfoCache(accountId);\n } else {\n for (const inst of cache.values()) inst.dispose();\n clearUserNameCache();\n clearChatInfoCache();\n }\n }\n\n // ---- SDK client (lazy) -----------------------------------------------------\n\n /** Lazily-created Lark SDK client. */\n get sdk(): Lark.Client {\n if (!this._sdk) {\n const { appId, appSecret } = this.requireCredentials();\n this._sdk = new Lark.Client({\n appId,\n appSecret,\n appType: Lark.AppType.SelfBuild,\n domain: resolveBrand(this.account.brand),\n });\n }\n return this._sdk;\n }\n\n // ---- Bot identity ----------------------------------------------------------\n\n /**\n * Probe bot identity via the `bot/v3/info` API.\n * Results are cached on the instance for subsequent access via\n * `botOpenId` / `botName`.\n */\n async probe(opts?: { maxAgeMs?: number }): Promise<FeishuProbeResult> {\n const maxAge = opts?.maxAgeMs ?? 0;\n\n if (maxAge > 0 && this._lastProbeResult && Date.now() - this._lastProbeAt < maxAge) {\n return this._lastProbeResult;\n }\n\n if (!this.account.appId || !this.account.appSecret) {\n return { ok: false, error: 'missing credentials (appId, appSecret)' };\n }\n\n try {\n const res = await (this.sdk as any).request({\n method: 'GET',\n url: '/open-apis/bot/v3/info',\n data: {},\n });\n\n if (res.code !== 0) {\n const result: FeishuProbeResult = {\n ok: false,\n appId: this.account.appId,\n error: `API error: ${res.msg || `code ${res.code}`}`,\n };\n this._lastProbeResult = result;\n this._lastProbeAt = Date.now();\n return result;\n }\n\n const bot = res.bot || res.data?.bot;\n this._botOpenId = bot?.open_id;\n this._botName = bot?.bot_name;\n\n const result: FeishuProbeResult = {\n ok: true,\n appId: this.account.appId,\n botName: this._botName,\n botOpenId: this._botOpenId,\n };\n this._lastProbeResult = result;\n this._lastProbeAt = Date.now();\n return result;\n } catch (err) {\n const result: FeishuProbeResult = {\n ok: false,\n appId: this.account.appId,\n error: err instanceof Error ? err.message : String(err),\n };\n this._lastProbeResult = result;\n this._lastProbeAt = Date.now();\n return result;\n }\n }\n\n /** Cached bot open_id (available after `probe()` or `startWS()`). */\n get botOpenId(): string | undefined {\n return this._botOpenId;\n }\n\n /** Cached bot name (available after `probe()` or `startWS()`). */\n get botName(): string | undefined {\n return this._botName;\n }\n\n // ---- WebSocket lifecycle ---------------------------------------------------\n\n /**\n * Start WebSocket event monitoring.\n *\n * Flow: probe bot identity \u2192 EventDispatcher \u2192 WSClient \u2192 start.\n * The returned Promise resolves when `abortSignal` fires.\n */\n async startWS(opts: {\n handlers: Record<string, (data: unknown) => Promise<void>>;\n abortSignal?: AbortSignal;\n autoProbe?: boolean;\n }): Promise<void> {\n const { handlers, abortSignal, autoProbe = true } = opts;\n\n if (autoProbe) await this.probe();\n\n const dispatcher = new Lark.EventDispatcher({\n encryptKey: this.account.encryptKey ?? '',\n verificationToken: this.account.verificationToken ?? '',\n });\n dispatcher.register(handlers as any);\n\n const { appId, appSecret } = this.requireCredentials();\n // Close any existing WSClient before creating a new one to prevent\n // orphaned connections when startWS is called multiple times.\n if (this._wsClient) {\n log.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });\n try {\n this._wsClient.close({ force: true });\n } catch {\n // Ignore \u2014 the old client may already be torn down.\n }\n this._wsClient = null;\n }\n\n this._wsClient = new Lark.WSClient({\n appId,\n appSecret,\n domain: resolveBrand(this.account.brand),\n loggerLevel: Lark.LoggerLevel.info,\n });\n\n // SDK \u7684 handleEventData \u53EA\u5904\u7406 type=\"event\"\uFF0Ccard action \u56DE\u8C03\u662F type=\"card\" \u4F1A\u88AB\u4E22\u5F03\u3002\n // \u6253 patch \u5C06 \"card\" \u7C7B\u578B\u6D88\u606F\u6539\u6210 \"event\" \u540E\u4EA4\u7ED9\u539F handler\uFF0C\u8BA9 EventDispatcher \u6B63\u5E38\u8DEF\u7531\u3002\n const wsClientAny = this._wsClient as any;\n const origHandleEventData = wsClientAny.handleEventData.bind(wsClientAny);\n wsClientAny.handleEventData = (data: any) => {\n const msgType = data.headers?.find?.((h: any) => h.key === 'type')?.value;\n if (msgType === 'card') {\n const patchedData = {\n ...data,\n headers: data.headers.map((h: any) => (h.key === 'type' ? { ...h, value: 'event' } : h)),\n };\n return origHandleEventData(patchedData);\n }\n return origHandleEventData(data);\n };\n\n await this.waitForAbort(dispatcher, abortSignal);\n }\n\n /** Whether a WebSocket client is currently active. */\n get wsConnected(): boolean {\n return this._wsClient !== null;\n }\n\n /** Disconnect WebSocket but keep instance in cache. */\n disconnect(): void {\n if (this._wsClient) {\n log.info(`disconnecting WebSocket`, { accountId: this.accountId });\n try {\n this._wsClient.close({ force: true });\n } catch {\n // Ignore errors during close \u2014 the client may already be torn down.\n }\n }\n this._wsClient = null;\n if (this.messageDedup) {\n log.info(`disposing message dedup`, { accountId: this.accountId, size: this.messageDedup.size });\n this.messageDedup.dispose();\n this.messageDedup = null;\n }\n }\n\n /** Disconnect + remove from cache. */\n dispose(): void {\n this.disconnect();\n cache.delete(this.accountId);\n }\n\n // ---- Private helpers -------------------------------------------------------\n\n /** Assert credentials exist or throw. */\n private requireCredentials(): { appId: string; appSecret: string } {\n const appId = this.account.appId;\n const appSecret = this.account.appSecret;\n if (!appId || !appSecret) {\n throw new Error(`LarkClient[${this.accountId}]: appId and appSecret are required`);\n }\n return { appId, appSecret };\n }\n\n /**\n * Start the WSClient and return a promise that resolves when the\n * abort signal fires (or immediately if already aborted).\n */\n private waitForAbort(dispatcher: Lark.EventDispatcher, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n this.disconnect();\n return resolve();\n }\n\n signal?.addEventListener(\n 'abort',\n () => {\n this.disconnect();\n resolve();\n },\n { once: true },\n );\n\n try {\n void this._wsClient!.start({ eventDispatcher: dispatcher });\n } catch (err) {\n this.disconnect();\n reject(err);\n }\n });\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["result"]
7
+ }
@@ -0,0 +1,92 @@
1
+ import { LarkClient } from "./lark-client";
2
+ import { getTicket } from "./lark-ticket";
3
+ const CYAN = "\x1B[36m";
4
+ const YELLOW = "\x1B[33m";
5
+ const RED = "\x1B[31m";
6
+ const GRAY = "\x1B[90m";
7
+ const RESET = "\x1B[0m";
8
+ function consoleFallback(subsystem) {
9
+ const tag = `feishu/${subsystem}`;
10
+ return {
11
+ debug: (msg, meta) => console.debug(`${GRAY}[${tag}]${RESET}`, msg, ...meta ? [meta] : []),
12
+ info: (msg, meta) => console.log(`${CYAN}[${tag}]${RESET}`, msg, ...meta ? [meta] : []),
13
+ warn: (msg, meta) => console.warn(`${YELLOW}[${tag}]${RESET}`, msg, ...meta ? [meta] : []),
14
+ error: (msg, meta) => console.error(`${RED}[${tag}]${RESET}`, msg, ...meta ? [meta] : [])
15
+ };
16
+ }
17
+ function resolveRuntimeLogger(subsystem) {
18
+ try {
19
+ return LarkClient.runtime.logging.getChildLogger({
20
+ subsystem: `feishu/${subsystem}`
21
+ });
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+ function getTraceMeta() {
27
+ const ctx = getTicket();
28
+ if (!ctx) return null;
29
+ const trace = {
30
+ accountId: ctx.accountId,
31
+ messageId: ctx.messageId,
32
+ chatId: ctx.chatId
33
+ };
34
+ if (ctx.senderOpenId) trace.senderOpenId = ctx.senderOpenId;
35
+ return trace;
36
+ }
37
+ function enrichMeta(meta) {
38
+ const trace = getTraceMeta();
39
+ if (!trace) return meta ?? {};
40
+ return meta ? { ...trace, ...meta } : trace;
41
+ }
42
+ function buildTracePrefix() {
43
+ const ctx = getTicket();
44
+ if (!ctx) return "feishu:";
45
+ return `feishu[${ctx.accountId}][msg:${ctx.messageId}]:`;
46
+ }
47
+ function formatMessage(message, meta) {
48
+ const prefix = buildTracePrefix();
49
+ if (!meta || Object.keys(meta).length === 0) return `${prefix} ${message}`;
50
+ const parts = Object.entries(meta).map(([k, v]) => {
51
+ if (v === void 0 || v === null) return null;
52
+ if (typeof v === "object") return `${k}=${JSON.stringify(v)}`;
53
+ return `${k}=${v}`;
54
+ }).filter(Boolean);
55
+ return parts.length > 0 ? `${prefix} ${message} (${parts.join(", ")})` : `${prefix} ${message}`;
56
+ }
57
+ function createLarkLogger(subsystem) {
58
+ let cachedLogger = null;
59
+ let resolved = false;
60
+ function getLogger() {
61
+ if (!resolved) {
62
+ cachedLogger = resolveRuntimeLogger(subsystem);
63
+ if (cachedLogger) resolved = true;
64
+ }
65
+ return cachedLogger ?? consoleFallback(subsystem);
66
+ }
67
+ return {
68
+ subsystem,
69
+ debug(message, meta) {
70
+ getLogger().debug?.(formatMessage(message, meta), enrichMeta(meta));
71
+ },
72
+ info(message, meta) {
73
+ getLogger().info(formatMessage(message, meta), enrichMeta(meta));
74
+ },
75
+ warn(message, meta) {
76
+ getLogger().warn(formatMessage(message, meta), enrichMeta(meta));
77
+ },
78
+ error(message, meta) {
79
+ getLogger().error(formatMessage(message, meta), enrichMeta(meta));
80
+ },
81
+ child(name) {
82
+ return createLarkLogger(`${subsystem}/${name}`);
83
+ }
84
+ };
85
+ }
86
+ function larkLogger(subsystem) {
87
+ return createLarkLogger(subsystem);
88
+ }
89
+ export {
90
+ larkLogger
91
+ };
92
+ //# sourceMappingURL=lark-logger.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/lark-logger.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Structured logger factory for the Feishu plugin.\n *\n * Wraps `PluginRuntime.logging.getChildLogger()` with automatic\n * LarkTicket injection from AsyncLocalStorage and a console fallback\n * when the runtime is not yet initialised.\n *\n * Usage:\n * const log = larkLogger(\"card/streaming\");\n * log.info(\"created entity\", { cardId, sequence });\n */\n\nimport type { RuntimeLogger } from 'openclaw/plugin-sdk';\nimport { LarkClient } from './lark-client';\nimport { getTicket } from './lark-ticket';\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\nexport interface LarkLogger {\n readonly subsystem: string;\n debug(message: string, meta?: Record<string, unknown>): void;\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n child(name: string): LarkLogger;\n}\n\n// ---------------------------------------------------------------------------\n// Console fallback (with ANSI colors)\n// ---------------------------------------------------------------------------\n\n// ANSI escape codes for colored console output\nconst CYAN = '\\x1b[36m';\nconst YELLOW = '\\x1b[33m';\nconst RED = '\\x1b[31m';\nconst GRAY = '\\x1b[90m';\nconst RESET = '\\x1b[0m';\n\nfunction consoleFallback(subsystem: string): RuntimeLogger {\n const tag = `feishu/${subsystem}`;\n /* eslint-disable no-console -- logger\u5E95\u5C42\u5B9E\u73B0\uFF0Cconsole \u662F\u6700\u7EC8\u8F93\u51FA\u76EE\u6807 */\n return {\n debug: (msg, meta) => console.debug(`${GRAY}[${tag}]${RESET}`, msg, ...(meta ? [meta] : [])),\n info: (msg, meta) => console.log(`${CYAN}[${tag}]${RESET}`, msg, ...(meta ? [meta] : [])),\n warn: (msg, meta) => console.warn(`${YELLOW}[${tag}]${RESET}`, msg, ...(meta ? [meta] : [])),\n error: (msg, meta) => console.error(`${RED}[${tag}]${RESET}`, msg, ...(meta ? [meta] : [])),\n };\n /* eslint-enable no-console */\n}\n\n// ---------------------------------------------------------------------------\n// Lazy runtime resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveRuntimeLogger(subsystem: string): RuntimeLogger | null {\n try {\n return LarkClient.runtime.logging.getChildLogger({\n subsystem: `feishu/${subsystem}`,\n });\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// LarkTicket enrichment\n// ---------------------------------------------------------------------------\n\nfunction getTraceMeta(): Record<string, unknown> | null {\n const ctx = getTicket();\n if (!ctx) return null;\n const trace: Record<string, unknown> = {\n accountId: ctx.accountId,\n messageId: ctx.messageId,\n chatId: ctx.chatId,\n };\n if (ctx.senderOpenId) trace.senderOpenId = ctx.senderOpenId;\n return trace;\n}\n\nfunction enrichMeta(meta: Record<string, unknown> | undefined): Record<string, unknown> {\n const trace = getTraceMeta();\n if (!trace) return meta ?? {};\n return meta ? { ...trace, ...meta } : trace;\n}\n\n// ---------------------------------------------------------------------------\n// Message formatting\n// ---------------------------------------------------------------------------\n\n/**\n * Build a trace-aware prefix like `feishu[default][msg:om_xxx]:`.\n *\n * Mirrors the format used by `trace.ts` so log lines are consistent\n * across the old and new logging systems.\n */\nfunction buildTracePrefix(): string {\n const ctx = getTicket();\n if (!ctx) return 'feishu:';\n return `feishu[${ctx.accountId}][msg:${ctx.messageId}]:`;\n}\n\n/**\n * Format message with inline meta for text-based log output.\n *\n * RuntimeLogger implementations typically ignore the `meta` parameter in\n * their text output (gateway.log / console). To ensure meta is always\n * visible, we serialize user-supplied meta into the message string and\n * prepend the trace context prefix (accountId + messageId).\n *\n * Example:\n * formatMessage(\"card.create response\", { code: 0, cardId: \"c_xxx\" })\n * \u2192 \"feishu[default][msg:om_xxx]: card.create response (code=0, cardId=c_xxx)\"\n */\nfunction formatMessage(message: string, meta: Record<string, unknown> | undefined): string {\n const prefix = buildTracePrefix();\n if (!meta || Object.keys(meta).length === 0) return `${prefix} ${message}`;\n const parts = Object.entries(meta)\n .map(([k, v]) => {\n if (v === undefined || v === null) return null;\n if (typeof v === 'object') return `${k}=${JSON.stringify(v)}`;\n return `${k}=${v}`;\n })\n .filter(Boolean);\n return parts.length > 0 ? `${prefix} ${message} (${parts.join(', ')})` : `${prefix} ${message}`;\n}\n\n// ---------------------------------------------------------------------------\n// LarkLogger implementation\n// ---------------------------------------------------------------------------\n\nfunction createLarkLogger(subsystem: string): LarkLogger {\n // RuntimeLogger is resolved lazily on first log call so that module-level\n // `larkLogger()` calls work even before `LarkClient.setRuntime()`.\n let cachedLogger: RuntimeLogger | null = null;\n let resolved = false;\n\n function getLogger(): RuntimeLogger {\n if (!resolved) {\n cachedLogger = resolveRuntimeLogger(subsystem);\n if (cachedLogger) resolved = true;\n }\n return cachedLogger ?? consoleFallback(subsystem);\n }\n\n return {\n subsystem,\n debug(message: string, meta?: Record<string, unknown>): void {\n getLogger().debug?.(formatMessage(message, meta), enrichMeta(meta));\n },\n info(message: string, meta?: Record<string, unknown>): void {\n getLogger().info(formatMessage(message, meta), enrichMeta(meta));\n },\n warn(message: string, meta?: Record<string, unknown>): void {\n getLogger().warn(formatMessage(message, meta), enrichMeta(meta));\n },\n error(message: string, meta?: Record<string, unknown>): void {\n getLogger().error(formatMessage(message, meta), enrichMeta(meta));\n },\n child(name: string): LarkLogger {\n return createLarkLogger(`${subsystem}/${name}`);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public factory\n// ---------------------------------------------------------------------------\n\nexport function larkLogger(subsystem: string): LarkLogger {\n return createLarkLogger(subsystem);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const store = new AsyncLocalStorage();
3
+ function withTicket(ticket, fn) {
4
+ return store.run(ticket, fn);
5
+ }
6
+ function getTicket() {
7
+ return store.getStore();
8
+ }
9
+ function ticketElapsed() {
10
+ const t = store.getStore();
11
+ return t ? Date.now() - t.startTime : 0;
12
+ }
13
+ export {
14
+ getTicket,
15
+ ticketElapsed,
16
+ withTicket
17
+ };
18
+ //# sourceMappingURL=lark-ticket.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/lark-ticket.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Request-level ticket for the Feishu plugin.\n *\n * Uses Node.js AsyncLocalStorage to propagate a ticket (message_id,\n * chat_id, account_id) through the entire async call chain without passing\n * parameters explicitly. Call {@link withTicket} at the event entry point\n * (monitor.ts) and use {@link getTicket} anywhere downstream.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface LarkTicket {\n messageId: string;\n chatId: string;\n accountId: string;\n startTime: number;\n senderOpenId?: string;\n chatType?: 'p2p' | 'group';\n threadId?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Storage\n// ---------------------------------------------------------------------------\n\nconst store = new AsyncLocalStorage<LarkTicket>();\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run `fn` within a ticket context. All async operations spawned inside\n * `fn` will inherit the context and can access it via {@link getTicket}.\n */\nexport function withTicket<T>(ticket: LarkTicket, fn: () => T | Promise<T>): T | Promise<T> {\n return store.run(ticket, fn);\n}\n\n/** Return the current ticket, or `undefined` if not inside withTicket. */\nexport function getTicket(): LarkTicket | undefined {\n return store.getStore();\n}\n\n/** Milliseconds elapsed since the current ticket was created, or 0. */\nexport function ticketElapsed(): number {\n const t = store.getStore();\n return t ? Date.now() - t.startTime : 0;\n}\n"],
5
+ "mappings": "AAYA,SAAS,yBAAyB;AAoBlC,MAAM,QAAQ,IAAI,kBAA8B;AAUzC,SAAS,WAAc,QAAoB,IAA0C;AAC1F,SAAO,MAAM,IAAI,QAAQ,EAAE;AAC7B;AAGO,SAAS,YAAoC;AAClD,SAAO,MAAM,SAAS;AACxB;AAGO,SAAS,gBAAwB;AACtC,QAAM,IAAI,MAAM,SAAS;AACzB,SAAO,IAAI,KAAK,IAAI,IAAI,EAAE,YAAY;AACxC;",
6
+ "names": []
7
+ }