@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,120 @@
1
+ const LARK_ERROR = {
2
+ /** 应用 scope 不足(租户维度) */
3
+ APP_SCOPE_MISSING: 99991672,
4
+ /** 用户 token scope 不足 */
5
+ USER_SCOPE_INSUFFICIENT: 99991679,
6
+ /** access_token 无效 */
7
+ TOKEN_INVALID: 99991668,
8
+ /** access_token 已过期 */
9
+ TOKEN_EXPIRED: 99991669,
10
+ /** refresh_token 无效 */
11
+ REFRESH_TOKEN_INVALID: 20003,
12
+ /** refresh_token 已过期 */
13
+ REFRESH_TOKEN_EXPIRED: 20004,
14
+ /** refresh_token 缺失 */
15
+ REFRESH_TOKEN_MISSING: 20024,
16
+ /** refresh_token 已被吊销 */
17
+ REFRESH_TOKEN_REVOKED: 20063,
18
+ /** 消息已被撤回 */
19
+ MESSAGE_RECALLED: 230011,
20
+ /** 消息已被删除 */
21
+ MESSAGE_DELETED: 231003
22
+ };
23
+ const REFRESH_TOKEN_IRRECOVERABLE = /* @__PURE__ */ new Set([
24
+ LARK_ERROR.REFRESH_TOKEN_INVALID,
25
+ LARK_ERROR.REFRESH_TOKEN_EXPIRED,
26
+ LARK_ERROR.REFRESH_TOKEN_MISSING,
27
+ LARK_ERROR.REFRESH_TOKEN_REVOKED
28
+ ]);
29
+ const MESSAGE_TERMINAL_CODES = /* @__PURE__ */ new Set([
30
+ LARK_ERROR.MESSAGE_RECALLED,
31
+ LARK_ERROR.MESSAGE_DELETED
32
+ ]);
33
+ const TOKEN_RETRY_CODES = /* @__PURE__ */ new Set([LARK_ERROR.TOKEN_INVALID, LARK_ERROR.TOKEN_EXPIRED]);
34
+ class NeedAuthorizationError extends Error {
35
+ userOpenId;
36
+ constructor(userOpenId) {
37
+ super("need_user_authorization");
38
+ this.name = "NeedAuthorizationError";
39
+ this.userOpenId = userOpenId;
40
+ }
41
+ }
42
+ class AppScopeCheckFailedError extends Error {
43
+ /** 应用 ID,用于生成开放平台权限管理链接。 */
44
+ appId;
45
+ constructor(appId) {
46
+ super("\u5E94\u7528\u7F3A\u5C11 application:application:self_manage \u6743\u9650\uFF0C\u65E0\u6CD5\u67E5\u8BE2\u5E94\u7528\u6743\u9650\u914D\u7F6E\u3002\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u8BE5\u6743\u9650\u3002");
47
+ this.name = "AppScopeCheckFailedError";
48
+ this.appId = appId;
49
+ }
50
+ }
51
+ class AppScopeMissingError extends Error {
52
+ apiName;
53
+ /** OAPI 需要但 APP 未开通的 scope 列表。 */
54
+ missingScopes;
55
+ /** 工具的全部所需 scope(含已开通的),用于应用权限完成后一次性发起用户授权。 */
56
+ allRequiredScopes;
57
+ /** 应用 ID,用于生成开放平台权限管理链接。 */
58
+ appId;
59
+ scopeNeedType;
60
+ /** 触发此错误时使用的 token 类型,用于保持 card action 二次校验一致。 */
61
+ tokenType;
62
+ constructor(info, scopeNeedType, tokenType, allRequiredScopes) {
63
+ if (scopeNeedType === "one") {
64
+ super(`\u5E94\u7528\u7F3A\u5C11\u6743\u9650 [${info.scopes.join(", ")}](\u5F00\u542F\u4EFB\u4E00\u6743\u9650\u5373\u53EF)\uFF0C\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u3002`);
65
+ } else {
66
+ super(`\u5E94\u7528\u7F3A\u5C11\u6743\u9650 [${info.scopes.join(", ")}]\uFF0C\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u3002`);
67
+ }
68
+ this.name = "AppScopeMissingError";
69
+ this.apiName = info.apiName;
70
+ this.missingScopes = info.scopes;
71
+ this.allRequiredScopes = allRequiredScopes;
72
+ this.appId = info.appId;
73
+ this.scopeNeedType = scopeNeedType;
74
+ this.tokenType = tokenType;
75
+ }
76
+ }
77
+ class UserAuthRequiredError extends Error {
78
+ userOpenId;
79
+ apiName;
80
+ /** APP∩OAPI 交集 scope,传给 OAuth authorize。 */
81
+ requiredScopes;
82
+ /** 应用 scope 是否已验证通过。false 时 requiredScopes 可能不准确。 */
83
+ appScopeVerified;
84
+ /** 应用 ID,用于生成开放平台权限管理链接。 */
85
+ appId;
86
+ constructor(userOpenId, info) {
87
+ super("need_user_authorization");
88
+ this.name = "UserAuthRequiredError";
89
+ this.userOpenId = userOpenId;
90
+ this.apiName = info.apiName;
91
+ this.requiredScopes = info.scopes;
92
+ this.appId = info.appId;
93
+ this.appScopeVerified = info.appScopeVerified ?? true;
94
+ }
95
+ }
96
+ class UserScopeInsufficientError extends Error {
97
+ userOpenId;
98
+ apiName;
99
+ /** 缺失的 scope 列表。 */
100
+ missingScopes;
101
+ constructor(userOpenId, info) {
102
+ super("user_scope_insufficient");
103
+ this.name = "UserScopeInsufficientError";
104
+ this.userOpenId = userOpenId;
105
+ this.apiName = info.apiName;
106
+ this.missingScopes = info.scopes;
107
+ }
108
+ }
109
+ export {
110
+ AppScopeCheckFailedError,
111
+ AppScopeMissingError,
112
+ LARK_ERROR,
113
+ MESSAGE_TERMINAL_CODES,
114
+ NeedAuthorizationError,
115
+ REFRESH_TOKEN_IRRECOVERABLE,
116
+ TOKEN_RETRY_CODES,
117
+ UserAuthRequiredError,
118
+ UserScopeInsufficientError
119
+ };
120
+ //# sourceMappingURL=auth-errors.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/auth-errors.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * auth-errors.ts \u2014 \u7EDF\u4E00\u9519\u8BEF\u7C7B\u578B\u5B9A\u4E49\u3002\n *\n * \u6240\u6709\u4E0E\u8BA4\u8BC1/\u6388\u6743/scope \u76F8\u5173\u7684\u9519\u8BEF\u7C7B\u578B\u96C6\u4E2D\u5728\u6B64\u6587\u4EF6\uFF0C\n * \u89E3\u9664 tool-client \u2194 app-scope-checker \u5FAA\u73AF\u4F9D\u8D56\u3002\n *\n * \u5176\u4ED6\u6A21\u5757\u5E94\u76F4\u63A5 import \u6B64\u6587\u4EF6\uFF0C\u6216\u901A\u8FC7 tool-client / uat-client \u7684 re-export \u4F7F\u7528\u3002\n */\n\n// ---------------------------------------------------------------------------\n// Feishu error code constants\n// ---------------------------------------------------------------------------\n\n/** \u98DE\u4E66 OAPI \u9519\u8BEF\u7801\u5E38\u91CF\uFF0C\u66FF\u4EE3\u5404\u5904\u786C\u7F16\u7801\u7684 magic number\u3002 */\nexport const LARK_ERROR = {\n /** \u5E94\u7528 scope \u4E0D\u8DB3\uFF08\u79DF\u6237\u7EF4\u5EA6\uFF09 */\n APP_SCOPE_MISSING: 99991672,\n /** \u7528\u6237 token scope \u4E0D\u8DB3 */\n USER_SCOPE_INSUFFICIENT: 99991679,\n /** access_token \u65E0\u6548 */\n TOKEN_INVALID: 99991668,\n /** access_token \u5DF2\u8FC7\u671F */\n TOKEN_EXPIRED: 99991669,\n /** refresh_token \u65E0\u6548 */\n REFRESH_TOKEN_INVALID: 20003,\n /** refresh_token \u5DF2\u8FC7\u671F */\n REFRESH_TOKEN_EXPIRED: 20004,\n /** refresh_token \u7F3A\u5931 */\n REFRESH_TOKEN_MISSING: 20024,\n /** refresh_token \u5DF2\u88AB\u540A\u9500 */\n REFRESH_TOKEN_REVOKED: 20063,\n /** \u6D88\u606F\u5DF2\u88AB\u64A4\u56DE */\n MESSAGE_RECALLED: 230011,\n /** \u6D88\u606F\u5DF2\u88AB\u5220\u9664 */\n MESSAGE_DELETED: 231003,\n} as const;\n\n/** \u4E0D\u53EF\u6062\u590D\u7684 refresh_token \u9519\u8BEF\u7801\u96C6\u5408\uFF0C\u9047\u5230\u540E\u9700\u8981\u91CD\u65B0\u6388\u6743\u3002 */\nexport const REFRESH_TOKEN_IRRECOVERABLE: ReadonlySet<number> = new Set([\n LARK_ERROR.REFRESH_TOKEN_INVALID,\n LARK_ERROR.REFRESH_TOKEN_EXPIRED,\n LARK_ERROR.REFRESH_TOKEN_MISSING,\n LARK_ERROR.REFRESH_TOKEN_REVOKED,\n]);\n\n/** \u6D88\u606F\u7EC8\u6B62\u9519\u8BEF\u7801\u96C6\u5408\uFF08\u64A4\u56DE/\u5220\u9664\uFF09\uFF0C\u9047\u5230\u540E\u5E94\u505C\u6B62\u5BF9\u8BE5\u6D88\u606F\u7684\u540E\u7EED\u64CD\u4F5C\u3002 */\nexport const MESSAGE_TERMINAL_CODES: ReadonlySet<number> = new Set([\n LARK_ERROR.MESSAGE_RECALLED,\n LARK_ERROR.MESSAGE_DELETED,\n]);\n\n/** access_token \u5931\u6548\u76F8\u5173\u7684\u9519\u8BEF\u7801\u96C6\u5408\uFF0C\u9047\u5230\u540E\u53EF\u5C1D\u8BD5\u5237\u65B0\u91CD\u8BD5\u3002 */\nexport const TOKEN_RETRY_CODES: ReadonlySet<number> = new Set([LARK_ERROR.TOKEN_INVALID, LARK_ERROR.TOKEN_EXPIRED]);\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/** invoke() \u9519\u8BEF\u5171\u4EAB\u7684 scope \u4FE1\u606F\u3002 */\nexport interface ScopeErrorInfo {\n apiName: string;\n scopes: string[];\n /** \u5E94\u7528 scope \u662F\u5426\u5DF2\u9A8C\u8BC1\u901A\u8FC7\u3002false \u8868\u793A app scope \u68C0\u67E5\u5931\u8D25\uFF0Cscope \u4FE1\u606F\u53EF\u80FD\u4E0D\u51C6\u786E\u3002 */\n appScopeVerified?: boolean;\n /** \u5E94\u7528 ID\uFF0C\u7528\u4E8E\u751F\u6210\u5F00\u653E\u5E73\u53F0\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u3002 */\n appId?: string;\n}\n\n/** OAuth \u6388\u6743\u63D0\u793A\u4FE1\u606F\uFF0C\u4E0E handleInvokeError \u8FD4\u56DE\u7684\u7ED3\u6784\u4E00\u81F4\u3002 */\nexport interface AuthHint {\n error: string;\n api: string;\n required_scope: string;\n user_open_id: string;\n message: string;\n next_tool_call: {\n tool: 'feishu_oauth';\n params: { action: 'authorize'; scope: string };\n };\n}\n\n/** tryInvoke \u8FD4\u56DE\u503C\u7684\u5224\u522B\u8054\u5408\u4F53\u3002 */\nexport type TryInvokeResult<T> =\n | { ok: true; data: T }\n | { ok: false; error: string; authHint: AuthHint }\n | { ok: false; error: string; authHint?: undefined };\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when no valid UAT exists and the user needs to (re-)authorise.\n * Callers should catch this and trigger the OAuth flow.\n */\nexport class NeedAuthorizationError extends Error {\n readonly userOpenId: string;\n constructor(userOpenId: string) {\n super('need_user_authorization');\n this.name = 'NeedAuthorizationError';\n this.userOpenId = userOpenId;\n }\n}\n\n/**\n * \u5E94\u7528\u7F3A\u5C11 application:application:self_manage \u6743\u9650\uFF0C\u65E0\u6CD5\u67E5\u8BE2\u5E94\u7528\u6743\u9650\u914D\u7F6E\u3002\n *\n * \u9700\u8981\u7BA1\u7406\u5458\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u5F00\u901A application:application:self_manage \u6743\u9650\u3002\n */\nexport class AppScopeCheckFailedError extends Error {\n /** \u5E94\u7528 ID\uFF0C\u7528\u4E8E\u751F\u6210\u5F00\u653E\u5E73\u53F0\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u3002 */\n readonly appId?: string;\n\n constructor(appId?: string) {\n super('\u5E94\u7528\u7F3A\u5C11 application:application:self_manage \u6743\u9650\uFF0C\u65E0\u6CD5\u67E5\u8BE2\u5E94\u7528\u6743\u9650\u914D\u7F6E\u3002\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u8BE5\u6743\u9650\u3002');\n this.name = 'AppScopeCheckFailedError';\n this.appId = appId;\n }\n}\n\n/**\n * \u5E94\u7528\u672A\u5F00\u901A OAPI \u6240\u9700 scope\u3002\n *\n * \u9700\u8981\u7BA1\u7406\u5458\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u6743\u9650\u3002\n */\nexport class AppScopeMissingError extends Error {\n readonly apiName: string;\n /** OAPI \u9700\u8981\u4F46 APP \u672A\u5F00\u901A\u7684 scope \u5217\u8868\u3002 */\n readonly missingScopes: string[];\n /** \u5DE5\u5177\u7684\u5168\u90E8\u6240\u9700 scope\uFF08\u542B\u5DF2\u5F00\u901A\u7684\uFF09\uFF0C\u7528\u4E8E\u5E94\u7528\u6743\u9650\u5B8C\u6210\u540E\u4E00\u6B21\u6027\u53D1\u8D77\u7528\u6237\u6388\u6743\u3002 */\n readonly allRequiredScopes?: string[];\n /** \u5E94\u7528 ID\uFF0C\u7528\u4E8E\u751F\u6210\u5F00\u653E\u5E73\u53F0\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u3002 */\n readonly appId?: string;\n readonly scopeNeedType?: 'one' | 'all';\n /** \u89E6\u53D1\u6B64\u9519\u8BEF\u65F6\u4F7F\u7528\u7684 token \u7C7B\u578B\uFF0C\u7528\u4E8E\u4FDD\u6301 card action \u4E8C\u6B21\u6821\u9A8C\u4E00\u81F4\u3002 */\n readonly tokenType?: 'user' | 'tenant';\n\n constructor(\n info: ScopeErrorInfo,\n scopeNeedType?: 'one' | 'all',\n tokenType?: 'user' | 'tenant',\n allRequiredScopes?: string[],\n ) {\n if (scopeNeedType === 'one') {\n super(`\u5E94\u7528\u7F3A\u5C11\u6743\u9650 [${info.scopes.join(', ')}](\u5F00\u542F\u4EFB\u4E00\u6743\u9650\u5373\u53EF)\uFF0C\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u3002`);\n } else {\n super(`\u5E94\u7528\u7F3A\u5C11\u6743\u9650 [${info.scopes.join(', ')}]\uFF0C\u8BF7\u7BA1\u7406\u5458\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u3002`);\n }\n this.name = 'AppScopeMissingError';\n this.apiName = info.apiName;\n this.missingScopes = info.scopes;\n this.allRequiredScopes = allRequiredScopes;\n this.appId = info.appId;\n this.scopeNeedType = scopeNeedType;\n this.tokenType = tokenType;\n }\n}\n\n/**\n * \u7528\u6237\u672A\u6388\u6743\u6216 scope \u4E0D\u8DB3\uFF0C\u9700\u8981\u53D1\u8D77 OAuth \u6388\u6743\u3002\n *\n * `requiredScopes` \u4E3A APP\u2229OAPI \u7684\u6709\u6548 scope\uFF0C\u53EF\u76F4\u63A5\u4F20\u7ED9\n * `feishu_oauth authorize --scope`\u3002\n */\nexport class UserAuthRequiredError extends Error {\n readonly userOpenId: string;\n readonly apiName: string;\n /** APP\u2229OAPI \u4EA4\u96C6 scope\uFF0C\u4F20\u7ED9 OAuth authorize\u3002 */\n readonly requiredScopes: string[];\n /** \u5E94\u7528 scope \u662F\u5426\u5DF2\u9A8C\u8BC1\u901A\u8FC7\u3002false \u65F6 requiredScopes \u53EF\u80FD\u4E0D\u51C6\u786E\u3002 */\n readonly appScopeVerified: boolean;\n\n /** \u5E94\u7528 ID\uFF0C\u7528\u4E8E\u751F\u6210\u5F00\u653E\u5E73\u53F0\u6743\u9650\u7BA1\u7406\u94FE\u63A5\u3002 */\n readonly appId?: string;\n\n constructor(userOpenId: string, info: ScopeErrorInfo) {\n super('need_user_authorization');\n this.name = 'UserAuthRequiredError';\n this.userOpenId = userOpenId;\n this.apiName = info.apiName;\n this.requiredScopes = info.scopes;\n this.appId = info.appId;\n this.appScopeVerified = info.appScopeVerified ?? true;\n }\n}\n\n/**\n * \u670D\u52A1\u7AEF\u62A5 99991679 \u2014 \u7528\u6237 token \u7684 scope \u4E0D\u8DB3\u3002\n *\n * \u9700\u8981\u589E\u91CF\u6388\u6743\uFF1A\u7528\u7F3A\u5931\u7684 scope \u53D1\u8D77\u65B0 Device Flow\u3002\n */\nexport class UserScopeInsufficientError extends Error {\n readonly userOpenId: string;\n readonly apiName: string;\n /** \u7F3A\u5931\u7684 scope \u5217\u8868\u3002 */\n readonly missingScopes: string[];\n\n constructor(userOpenId: string, info: ScopeErrorInfo) {\n super('user_scope_insufficient');\n this.name = 'UserScopeInsufficientError';\n this.userOpenId = userOpenId;\n this.apiName = info.apiName;\n this.missingScopes = info.scopes;\n }\n}\n"],
5
+ "mappings": "AAiBO,MAAM,aAAa;AAAA;AAAA,EAExB,mBAAmB;AAAA;AAAA,EAEnB,yBAAyB;AAAA;AAAA,EAEzB,eAAe;AAAA;AAAA,EAEf,eAAe;AAAA;AAAA,EAEf,uBAAuB;AAAA;AAAA,EAEvB,uBAAuB;AAAA;AAAA,EAEvB,uBAAuB;AAAA;AAAA,EAEvB,uBAAuB;AAAA;AAAA,EAEvB,kBAAkB;AAAA;AAAA,EAElB,iBAAiB;AACnB;AAGO,MAAM,8BAAmD,oBAAI,IAAI;AAAA,EACtE,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AACb,CAAC;AAGM,MAAM,yBAA8C,oBAAI,IAAI;AAAA,EACjE,WAAW;AAAA,EACX,WAAW;AACb,CAAC;AAGM,MAAM,oBAAyC,oBAAI,IAAI,CAAC,WAAW,eAAe,WAAW,aAAa,CAAC;AA2C3G,MAAM,+BAA+B,MAAM;AAAA,EACvC;AAAA,EACT,YAAY,YAAoB;AAC9B,UAAM,yBAAyB;AAC/B,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;AAOO,MAAM,iCAAiC,MAAM;AAAA;AAAA,EAEzC;AAAA,EAET,YAAY,OAAgB;AAC1B,UAAM,6OAAwE;AAC9E,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;AAOO,MAAM,6BAA6B,MAAM;AAAA,EACrC;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAET,YACE,MACA,eACA,WACA,mBACA;AACA,QAAI,kBAAkB,OAAO;AAC3B,YAAM,yCAAW,KAAK,OAAO,KAAK,IAAI,CAAC,mIAA0B;AAAA,IACnE,OAAO;AACL,YAAM,yCAAW,KAAK,OAAO,KAAK,IAAI,CAAC,iFAAgB;AAAA,IACzD;AACA,SAAK,OAAO;AACZ,SAAK,UAAU,KAAK;AACpB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,oBAAoB;AACzB,SAAK,QAAQ,KAAK;AAClB,SAAK,gBAAgB;AACrB,SAAK,YAAY;AAAA,EACnB;AACF;AAQO,MAAM,8BAA8B,MAAM;AAAA,EACtC;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAGA;AAAA,EAET,YAAY,YAAoB,MAAsB;AACpD,UAAM,yBAAyB;AAC/B,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,UAAU,KAAK;AACpB,SAAK,iBAAiB,KAAK;AAC3B,SAAK,QAAQ,KAAK;AAClB,SAAK,mBAAmB,KAAK,oBAAoB;AAAA,EACnD;AACF;AAOO,MAAM,mCAAmC,MAAM;AAAA,EAC3C;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAET,YAAY,YAAoB,MAAsB;AACpD,UAAM,yBAAyB;AAC/B,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,UAAU,KAAK;AACpB,SAAK,gBAAgB,KAAK;AAAA,EAC5B;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,102 @@
1
+ import { LarkClient } from "./lark-client";
2
+ import { larkLogger } from "./lark-logger";
3
+ const log = larkLogger("core/chat-info-cache");
4
+ const DEFAULT_MAX_SIZE = 500;
5
+ const DEFAULT_TTL_MS = 60 * 60 * 1e3;
6
+ class ChatInfoCache {
7
+ map = /* @__PURE__ */ new Map();
8
+ maxSize;
9
+ ttlMs;
10
+ constructor(maxSize = DEFAULT_MAX_SIZE, ttlMs = DEFAULT_TTL_MS) {
11
+ this.maxSize = maxSize;
12
+ this.ttlMs = ttlMs;
13
+ }
14
+ get(chatId) {
15
+ const entry = this.map.get(chatId);
16
+ if (!entry) return void 0;
17
+ if (entry.expireAt <= Date.now()) {
18
+ this.map.delete(chatId);
19
+ return void 0;
20
+ }
21
+ this.map.delete(chatId);
22
+ this.map.set(chatId, entry);
23
+ return entry.info;
24
+ }
25
+ set(chatId, info) {
26
+ this.map.delete(chatId);
27
+ this.map.set(chatId, { info, expireAt: Date.now() + this.ttlMs });
28
+ this.evict();
29
+ }
30
+ clear() {
31
+ this.map.clear();
32
+ }
33
+ evict() {
34
+ while (this.map.size > this.maxSize) {
35
+ const oldest = this.map.keys().next().value;
36
+ if (oldest !== void 0) this.map.delete(oldest);
37
+ }
38
+ }
39
+ }
40
+ const registry = /* @__PURE__ */ new Map();
41
+ function getChatInfoCache(accountId) {
42
+ let c = registry.get(accountId);
43
+ if (!c) {
44
+ c = new ChatInfoCache();
45
+ registry.set(accountId, c);
46
+ }
47
+ return c;
48
+ }
49
+ function clearChatInfoCache(accountId) {
50
+ if (accountId !== void 0) {
51
+ registry.get(accountId)?.clear();
52
+ registry.delete(accountId);
53
+ } else {
54
+ for (const c of registry.values()) c.clear();
55
+ registry.clear();
56
+ }
57
+ }
58
+ async function isThreadCapableGroup(params) {
59
+ const { cfg, chatId, accountId } = params;
60
+ const info = await getChatInfo({ cfg, chatId, accountId });
61
+ if (!info) return false;
62
+ return info.chatMode === "topic" || info.groupMessageType === "thread";
63
+ }
64
+ async function getChatInfo(params) {
65
+ const { cfg, chatId, accountId } = params;
66
+ const effectiveAccountId = accountId ?? "default";
67
+ const cache = getChatInfoCache(effectiveAccountId);
68
+ const cached = cache.get(chatId);
69
+ if (cached) return cached;
70
+ try {
71
+ const sdk = LarkClient.fromCfg(cfg, accountId).sdk;
72
+ const response = await sdk.im.chat.get({
73
+ path: { chat_id: chatId }
74
+ });
75
+ const data = response?.data;
76
+ const chatMode = data?.chat_mode ?? "group";
77
+ const groupMessageType = data?.group_message_type;
78
+ const info = {
79
+ chatMode,
80
+ groupMessageType
81
+ };
82
+ cache.set(chatId, info);
83
+ log.info(`resolved ${chatId} \u2192 chat_mode=${chatMode}, group_message_type=${groupMessageType ?? "N/A"}`);
84
+ return info;
85
+ } catch (err) {
86
+ log.error(`failed to get chat info for ${chatId}: ${String(err)}`);
87
+ return void 0;
88
+ }
89
+ }
90
+ async function getChatTypeFeishu(params) {
91
+ const { cfg, chatId, accountId } = params;
92
+ const info = await getChatInfo({ cfg, chatId, accountId });
93
+ if (!info) return "p2p";
94
+ return info.chatMode === "group" || info.chatMode === "topic" ? "group" : "p2p";
95
+ }
96
+ export {
97
+ clearChatInfoCache,
98
+ getChatInfo,
99
+ getChatTypeFeishu,
100
+ isThreadCapableGroup
101
+ };
102
+ //# sourceMappingURL=chat-info-cache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/chat-info-cache.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Account-scoped LRU cache for Feishu group/chat metadata.\n *\n * Caches the result of `im.chat.get` (chat_mode, group_message_type, etc.)\n * to avoid repeated OAPI calls for every inbound message.\n *\n * Key fields cached:\n * - `chat_mode`: \"group\" | \"topic\" | \"p2p\"\n * - `group_message_type`: \"chat\" | \"thread\" (only for chat_mode=group)\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { LarkClient } from './lark-client';\nimport { larkLogger } from './lark-logger';\n\nconst log = larkLogger('core/chat-info-cache');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatInfo {\n chatMode: 'group' | 'topic' | 'p2p';\n groupMessageType?: 'chat' | 'thread';\n}\n\n// ---------------------------------------------------------------------------\n// Cache implementation\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MAX_SIZE = 500;\nconst DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour\n\ninterface CacheEntry {\n info: ChatInfo;\n expireAt: number;\n}\n\nclass ChatInfoCache {\n private map = new Map<string, CacheEntry>();\n private maxSize: number;\n private ttlMs: number;\n\n constructor(maxSize = DEFAULT_MAX_SIZE, ttlMs = DEFAULT_TTL_MS) {\n this.maxSize = maxSize;\n this.ttlMs = ttlMs;\n }\n\n get(chatId: string): ChatInfo | undefined {\n const entry = this.map.get(chatId);\n if (!entry) return undefined;\n if (entry.expireAt <= Date.now()) {\n this.map.delete(chatId);\n return undefined;\n }\n // LRU refresh\n this.map.delete(chatId);\n this.map.set(chatId, entry);\n return entry.info;\n }\n\n set(chatId: string, info: ChatInfo): void {\n this.map.delete(chatId);\n this.map.set(chatId, { info, expireAt: Date.now() + this.ttlMs });\n this.evict();\n }\n\n clear(): void {\n this.map.clear();\n }\n\n private evict(): void {\n while (this.map.size > this.maxSize) {\n const oldest = this.map.keys().next().value;\n if (oldest !== undefined) this.map.delete(oldest);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Account-scoped singleton registry\n// ---------------------------------------------------------------------------\n\nconst registry = new Map<string, ChatInfoCache>();\n\nfunction getChatInfoCache(accountId: string): ChatInfoCache {\n let c = registry.get(accountId);\n if (!c) {\n c = new ChatInfoCache();\n registry.set(accountId, c);\n }\n return c;\n}\n\n/** Clear chat-info caches (called from LarkClient.clearCache). */\nexport function clearChatInfoCache(accountId?: string): void {\n if (accountId !== undefined) {\n registry.get(accountId)?.clear();\n registry.delete(accountId);\n } else {\n for (const c of registry.values()) c.clear();\n registry.clear();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Determine whether a group supports thread sessions.\n *\n * Returns `true` when the group is a topic group (`chat_mode=topic`) or\n * a normal group with thread message mode (`group_message_type=thread`).\n *\n * Results are cached per-account with a 1-hour TTL to minimise OAPI calls.\n */\nexport async function isThreadCapableGroup(params: {\n cfg: ClawdbotConfig;\n chatId: string;\n accountId?: string;\n}): Promise<boolean> {\n const { cfg, chatId, accountId } = params;\n const info = await getChatInfo({ cfg, chatId, accountId });\n if (!info) return false;\n return info.chatMode === 'topic' || info.groupMessageType === 'thread';\n}\n\n/**\n * Fetch (or read from cache) the chat metadata for a given chat ID.\n *\n * Returns `undefined` when the API call fails (best-effort).\n */\nexport async function getChatInfo(params: {\n cfg: ClawdbotConfig;\n chatId: string;\n accountId?: string;\n}): Promise<ChatInfo | undefined> {\n const { cfg, chatId, accountId } = params;\n const effectiveAccountId = accountId ?? 'default';\n const cache = getChatInfoCache(effectiveAccountId);\n\n const cached = cache.get(chatId);\n if (cached) return cached;\n\n try {\n const sdk = LarkClient.fromCfg(cfg, accountId).sdk;\n const response = await sdk.im.chat.get({\n path: { chat_id: chatId },\n });\n\n const data = response?.data as Record<string, unknown> | undefined;\n const chatMode = (data?.chat_mode as string) ?? 'group';\n const groupMessageType = data?.group_message_type as string | undefined;\n\n const info: ChatInfo = {\n chatMode: chatMode as ChatInfo['chatMode'],\n groupMessageType: groupMessageType as ChatInfo['groupMessageType'],\n };\n\n cache.set(chatId, info);\n log.info(`resolved ${chatId} \u2192 chat_mode=${chatMode}, group_message_type=${groupMessageType ?? 'N/A'}`);\n return info;\n } catch (err) {\n log.error(`failed to get chat info for ${chatId}: ${String(err)}`);\n return undefined;\n }\n}\n\n// ---------------------------------------------------------------------------\n// getChatTypeFeishu\n// ---------------------------------------------------------------------------\n\n/**\n * Determine the chat type (p2p or group) for a given chat ID.\n *\n * Delegates to the shared {@link getChatInfo} cache (account-scoped LRU with\n * 1-hour TTL) so that chat metadata is fetched at most once across all\n * call-sites (dispatch, reaction handler, etc.).\n *\n * Falls back to \"p2p\" if the API call fails.\n */\nexport async function getChatTypeFeishu(params: {\n cfg: ClawdbotConfig;\n chatId: string;\n accountId?: string;\n}): Promise<'p2p' | 'group'> {\n const { cfg, chatId, accountId } = params;\n const info = await getChatInfo({ cfg, chatId, accountId });\n if (!info) return 'p2p';\n return info.chatMode === 'group' || info.chatMode === 'topic' ? 'group' : 'p2p';\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,150 @@
1
+ import { z, toJSONSchema } from "zod";
2
+ const DmPolicyEnum = z.enum(["open", "pairing", "allowlist", "disabled"]);
3
+ const GroupPolicyEnum = z.enum(["open", "allowlist", "disabled"]);
4
+ const ConnectionModeEnum = z.enum(["websocket", "webhook"]);
5
+ const ReplyModeValue = z.enum(["auto", "static", "streaming"]);
6
+ const ReplyModeSchema = z.union([
7
+ ReplyModeValue,
8
+ z.object({
9
+ default: ReplyModeValue.optional(),
10
+ group: ReplyModeValue.optional(),
11
+ direct: ReplyModeValue.optional()
12
+ })
13
+ ]).optional();
14
+ const ChunkModeEnum = z.enum(["newline", "paragraph", "none"]);
15
+ const DomainSchema = z.union([z.literal("feishu"), z.literal("lark"), z.string().regex(/^https:\/\//)]).optional();
16
+ const AllowFromSchema = z.union([z.string(), z.array(z.string())]).optional().transform((v) => {
17
+ if (v === void 0 || v === null) return void 0;
18
+ return Array.isArray(v) ? v : [v];
19
+ });
20
+ const ToolPolicySchema = z.object({
21
+ allow: z.array(z.string()).optional(),
22
+ deny: z.array(z.string()).optional()
23
+ }).optional();
24
+ const FeishuToolsFlagSchema = z.object({
25
+ doc: z.boolean().optional(),
26
+ wiki: z.boolean().optional(),
27
+ drive: z.boolean().optional(),
28
+ perm: z.boolean().optional(),
29
+ scopes: z.boolean().optional()
30
+ }).optional();
31
+ const FeishuFooterSchema = z.object({
32
+ status: z.boolean().optional(),
33
+ elapsed: z.boolean().optional()
34
+ }).optional();
35
+ const BlockStreamingCoalesceSchema = z.object({
36
+ minChars: z.number().optional(),
37
+ maxChars: z.number().optional(),
38
+ idleMs: z.number().optional()
39
+ }).optional();
40
+ const MarkdownConfigSchema = z.object({
41
+ tables: z.enum(["off", "bullets", "code"]).optional()
42
+ }).optional();
43
+ const HeartbeatSchema = z.object({
44
+ every: z.string().optional(),
45
+ activeHours: z.object({
46
+ start: z.string().optional(),
47
+ end: z.string().optional(),
48
+ timezone: z.string().optional()
49
+ }).optional(),
50
+ target: z.string().optional(),
51
+ to: z.string().optional(),
52
+ prompt: z.string().optional(),
53
+ accountId: z.string().optional()
54
+ }).optional();
55
+ const CapabilitiesSchema = z.object({
56
+ image: z.boolean().optional(),
57
+ audio: z.boolean().optional(),
58
+ video: z.boolean().optional()
59
+ }).optional();
60
+ const DedupSchema = z.object({
61
+ ttlMs: z.number().optional(),
62
+ // default 43200000 (12h)
63
+ maxEntries: z.number().optional()
64
+ // default 5000
65
+ }).optional();
66
+ const ReactionNotificationModeSchema = z.enum(["off", "own", "all"]).optional();
67
+ const UATConfigSchema = z.object({
68
+ enabled: z.boolean().optional(),
69
+ allowedScopes: z.array(z.string()).optional(),
70
+ blockedScopes: z.array(z.string()).optional()
71
+ }).optional();
72
+ const DmConfigSchema = z.object({
73
+ historyLimit: z.number().optional()
74
+ }).optional();
75
+ const FeishuGroupSchema = z.object({
76
+ groupPolicy: GroupPolicyEnum.optional(),
77
+ requireMention: z.boolean().optional(),
78
+ tools: ToolPolicySchema,
79
+ skills: z.array(z.string()).optional(),
80
+ enabled: z.boolean().optional(),
81
+ allowFrom: AllowFromSchema,
82
+ systemPrompt: z.string().optional()
83
+ });
84
+ const FeishuAccountConfigSchema = z.object({
85
+ appId: z.string().optional(),
86
+ appSecret: z.string().optional(),
87
+ encryptKey: z.string().optional(),
88
+ verificationToken: z.string().optional(),
89
+ name: z.string().optional(),
90
+ enabled: z.boolean().optional(),
91
+ domain: DomainSchema,
92
+ connectionMode: ConnectionModeEnum.optional(),
93
+ webhookPath: z.string().optional(),
94
+ webhookPort: z.number().optional(),
95
+ dmPolicy: DmPolicyEnum.optional(),
96
+ allowFrom: AllowFromSchema,
97
+ groupPolicy: GroupPolicyEnum.optional(),
98
+ groupAllowFrom: AllowFromSchema,
99
+ requireMention: z.boolean().optional(),
100
+ groups: z.record(z.string(), FeishuGroupSchema).optional(),
101
+ historyLimit: z.number().optional(),
102
+ dmHistoryLimit: z.number().optional(),
103
+ dms: DmConfigSchema,
104
+ textChunkLimit: z.number().optional(),
105
+ chunkMode: ChunkModeEnum.optional(),
106
+ blockStreamingCoalesce: BlockStreamingCoalesceSchema,
107
+ mediaMaxMb: z.number().optional(),
108
+ heartbeat: HeartbeatSchema,
109
+ replyMode: ReplyModeSchema,
110
+ streaming: z.boolean().optional(),
111
+ blockStreaming: z.boolean().optional(),
112
+ tools: FeishuToolsFlagSchema,
113
+ footer: FeishuFooterSchema,
114
+ markdown: MarkdownConfigSchema,
115
+ configWrites: z.boolean().optional(),
116
+ capabilities: CapabilitiesSchema,
117
+ dedup: DedupSchema,
118
+ reactionNotifications: ReactionNotificationModeSchema,
119
+ threadSession: z.boolean().optional(),
120
+ uat: UATConfigSchema
121
+ });
122
+ const FeishuConfigSchema = FeishuAccountConfigSchema.extend({
123
+ accounts: z.record(z.string(), FeishuAccountConfigSchema).optional()
124
+ }).superRefine((data, ctx) => {
125
+ if (data.dmPolicy === "open") {
126
+ const list = data.allowFrom;
127
+ const hasWildcard = Array.isArray(list) && list.includes("*");
128
+ if (!hasWildcard) {
129
+ ctx.addIssue({
130
+ code: z.ZodIssueCode.custom,
131
+ path: ["allowFrom"],
132
+ message: 'When dmPolicy is "open", allowFrom must include "*" to permit all senders.'
133
+ });
134
+ }
135
+ }
136
+ });
137
+ const FEISHU_CONFIG_JSON_SCHEMA = toJSONSchema(FeishuConfigSchema, {
138
+ target: "draft-07",
139
+ io: "input",
140
+ unrepresentable: "any"
141
+ });
142
+ export {
143
+ FEISHU_CONFIG_JSON_SCHEMA,
144
+ FeishuAccountConfigSchema,
145
+ FeishuConfigSchema,
146
+ FeishuGroupSchema,
147
+ UATConfigSchema,
148
+ z
149
+ };
150
+ //# sourceMappingURL=config-schema.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/config-schema.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Zod-based configuration schema for the OpenClaw Lark/Feishu channel plugin.\n *\n * Provides runtime validation, sensible defaults, and cross-field refinements\n * so that every consuming module can rely on well-typed configuration objects.\n */\n\nimport { z, toJSONSchema } from 'zod';\n\nexport { z };\n\n// ---------------------------------------------------------------------------\n// Shared micro-schemas\n// ---------------------------------------------------------------------------\n\nconst DmPolicyEnum = z.enum(['open', 'pairing', 'allowlist', 'disabled']);\nconst GroupPolicyEnum = z.enum(['open', 'allowlist', 'disabled']);\nconst ConnectionModeEnum = z.enum(['websocket', 'webhook']);\nconst ReplyModeValue = z.enum(['auto', 'static', 'streaming']);\nconst ReplyModeSchema = z\n .union([\n ReplyModeValue,\n z.object({\n default: ReplyModeValue.optional(),\n group: ReplyModeValue.optional(),\n direct: ReplyModeValue.optional(),\n }),\n ])\n .optional();\nconst ChunkModeEnum = z.enum(['newline', 'paragraph', 'none']);\n\nconst DomainSchema = z.union([z.literal('feishu'), z.literal('lark'), z.string().regex(/^https:\\/\\//)]).optional();\n\nconst AllowFromSchema = z\n .union([z.string(), z.array(z.string())])\n .optional()\n .transform((v) => {\n if (v === undefined || v === null) return undefined;\n return Array.isArray(v) ? v : [v];\n });\n\nconst ToolPolicySchema = z\n .object({\n allow: z.array(z.string()).optional(),\n deny: z.array(z.string()).optional(),\n })\n .optional();\n\nconst FeishuToolsFlagSchema = z\n .object({\n doc: z.boolean().optional(),\n wiki: z.boolean().optional(),\n drive: z.boolean().optional(),\n perm: z.boolean().optional(),\n scopes: z.boolean().optional(),\n })\n .optional();\n\nconst FeishuFooterSchema = z\n .object({\n status: z.boolean().optional(),\n elapsed: z.boolean().optional(),\n })\n .optional();\n\nconst BlockStreamingCoalesceSchema = z\n .object({\n minChars: z.number().optional(),\n maxChars: z.number().optional(),\n idleMs: z.number().optional(),\n })\n .optional();\n\nconst MarkdownConfigSchema = z\n .object({\n tables: z.enum(['off', 'bullets', 'code']).optional(),\n })\n .optional();\n\nconst HeartbeatSchema = z\n .object({\n every: z.string().optional(),\n activeHours: z\n .object({\n start: z.string().optional(),\n end: z.string().optional(),\n timezone: z.string().optional(),\n })\n .optional(),\n target: z.string().optional(),\n to: z.string().optional(),\n prompt: z.string().optional(),\n accountId: z.string().optional(),\n })\n .optional();\n\nconst CapabilitiesSchema = z\n .object({\n image: z.boolean().optional(),\n audio: z.boolean().optional(),\n video: z.boolean().optional(),\n })\n .optional();\n\nconst DedupSchema = z\n .object({\n ttlMs: z.number().optional(), // default 43200000 (12h)\n maxEntries: z.number().optional(), // default 5000\n })\n .optional();\n\nconst ReactionNotificationModeSchema = z.enum(['off', 'own', 'all']).optional();\n\nexport const UATConfigSchema = z\n .object({\n enabled: z.boolean().optional(),\n allowedScopes: z.array(z.string()).optional(),\n blockedScopes: z.array(z.string()).optional(),\n })\n .optional();\n\nconst DmConfigSchema = z\n .object({\n historyLimit: z.number().optional(),\n })\n .optional();\n\n// ---------------------------------------------------------------------------\n// Group schema\n// ---------------------------------------------------------------------------\n\nexport const FeishuGroupSchema = z.object({\n groupPolicy: GroupPolicyEnum.optional(),\n requireMention: z.boolean().optional(),\n tools: ToolPolicySchema,\n skills: z.array(z.string()).optional(),\n enabled: z.boolean().optional(),\n allowFrom: AllowFromSchema,\n systemPrompt: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Account config schema (same shape as top-level minus `accounts`)\n// ---------------------------------------------------------------------------\n\nexport const FeishuAccountConfigSchema = z.object({\n appId: z.string().optional(),\n appSecret: z.string().optional(),\n encryptKey: z.string().optional(),\n verificationToken: z.string().optional(),\n name: z.string().optional(),\n enabled: z.boolean().optional(),\n domain: DomainSchema,\n connectionMode: ConnectionModeEnum.optional(),\n webhookPath: z.string().optional(),\n webhookPort: z.number().optional(),\n dmPolicy: DmPolicyEnum.optional(),\n allowFrom: AllowFromSchema,\n groupPolicy: GroupPolicyEnum.optional(),\n groupAllowFrom: AllowFromSchema,\n requireMention: z.boolean().optional(),\n groups: z.record(z.string(), FeishuGroupSchema).optional(),\n historyLimit: z.number().optional(),\n dmHistoryLimit: z.number().optional(),\n dms: DmConfigSchema,\n textChunkLimit: z.number().optional(),\n chunkMode: ChunkModeEnum.optional(),\n blockStreamingCoalesce: BlockStreamingCoalesceSchema,\n mediaMaxMb: z.number().optional(),\n heartbeat: HeartbeatSchema,\n replyMode: ReplyModeSchema,\n streaming: z.boolean().optional(),\n blockStreaming: z.boolean().optional(),\n tools: FeishuToolsFlagSchema,\n footer: FeishuFooterSchema,\n markdown: MarkdownConfigSchema,\n configWrites: z.boolean().optional(),\n capabilities: CapabilitiesSchema,\n dedup: DedupSchema,\n reactionNotifications: ReactionNotificationModeSchema,\n threadSession: z.boolean().optional(),\n uat: UATConfigSchema,\n});\n\n// ---------------------------------------------------------------------------\n// Top-level Feishu config schema\n// ---------------------------------------------------------------------------\n\nexport const FeishuConfigSchema = FeishuAccountConfigSchema.extend({\n accounts: z.record(z.string(), FeishuAccountConfigSchema).optional(),\n}).superRefine((data, ctx) => {\n // When dmPolicy is \"open\", allowFrom must contain the wildcard \"*\".\n if (data.dmPolicy === 'open') {\n const list = data.allowFrom;\n const hasWildcard = Array.isArray(list) && list.includes('*');\n\n if (!hasWildcard) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n path: ['allowFrom'],\n message: 'When dmPolicy is \"open\", allowFrom must include \"*\" to permit all senders.',\n });\n }\n }\n});\n\n// ---------------------------------------------------------------------------\n// Auto-generated JSON Schema (single source of truth)\n// ---------------------------------------------------------------------------\n\n/**\n * JSON Schema derived from FeishuConfigSchema.\n *\n * - `io: \"input\"` exposes the input type for `.transform()` schemas (e.g. AllowFromSchema).\n * - `unrepresentable: \"any\"` degrades `.superRefine()` constraints to `{}`.\n * - `target: \"draft-07\"` matches the plugin system's expected JSON Schema version.\n */\nexport const FEISHU_CONFIG_JSON_SCHEMA: Record<string, unknown> = toJSONSchema(FeishuConfigSchema, {\n target: 'draft-07',\n io: 'input',\n unrepresentable: 'any',\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,174 @@
1
+ import { larkLogger } from "./lark-logger";
2
+ const log = larkLogger("core/device-flow");
3
+ import { feishuFetch } from "./feishu-fetch";
4
+ function resolveOAuthEndpoints(brand) {
5
+ if (!brand || brand === "feishu") {
6
+ return {
7
+ deviceAuthorization: "https://accounts.feishu.cn/oauth/v1/device_authorization",
8
+ token: "https://open.feishu.cn/open-apis/authen/v2/oauth/token"
9
+ };
10
+ }
11
+ if (brand === "lark") {
12
+ return {
13
+ deviceAuthorization: "https://accounts.larksuite.com/oauth/v1/device_authorization",
14
+ token: "https://open.larksuite.com/open-apis/authen/v2/oauth/token"
15
+ };
16
+ }
17
+ const base = brand.replace(/\/+$/, "");
18
+ let accountsBase = base;
19
+ try {
20
+ const parsed = new URL(base);
21
+ if (parsed.hostname.startsWith("open.")) {
22
+ accountsBase = `${parsed.protocol}//${parsed.hostname.replace(/^open\./, "accounts.")}`;
23
+ }
24
+ } catch {
25
+ }
26
+ return {
27
+ deviceAuthorization: `${accountsBase}/oauth/v1/device_authorization`,
28
+ token: `${base}/open-apis/authen/v2/oauth/token`
29
+ };
30
+ }
31
+ async function requestDeviceAuthorization(params) {
32
+ const { appId, appSecret, brand } = params;
33
+ const endpoints = params.endpoints || resolveOAuthEndpoints(brand);
34
+ let scope = params.scope ?? "";
35
+ if (!scope.includes("offline_access")) {
36
+ scope = scope ? `${scope} offline_access` : "offline_access";
37
+ }
38
+ const basicAuth = Buffer.from(`${appId}:${appSecret}`).toString("base64");
39
+ const body = new URLSearchParams();
40
+ body.set("client_id", appId);
41
+ body.set("scope", scope);
42
+ log.info(
43
+ `requesting device authorization (scope="${scope}") url=${endpoints.deviceAuthorization} token_url=${endpoints.token}`
44
+ );
45
+ const resp = await feishuFetch(endpoints.deviceAuthorization, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/x-www-form-urlencoded",
49
+ Authorization: `Basic ${basicAuth}`
50
+ },
51
+ body: body.toString()
52
+ });
53
+ const text = await resp.text();
54
+ log.info(`response status=${resp.status} body=${text.slice(0, 500)}`);
55
+ let data;
56
+ try {
57
+ data = JSON.parse(text);
58
+ } catch {
59
+ throw new Error(`Device authorization failed: HTTP ${resp.status} \u2013 ${text.slice(0, 200)}`);
60
+ }
61
+ if (!resp.ok || data.error) {
62
+ const msg = data.error_description ?? data.error ?? "Unknown error";
63
+ throw new Error(`Device authorization failed: ${msg}`);
64
+ }
65
+ const expiresIn = data.expires_in ?? 240;
66
+ const interval = data.interval ?? 5;
67
+ log.info(`device_code obtained, expires_in=${expiresIn}s (${Math.round(expiresIn / 60)}min), interval=${interval}s`);
68
+ return {
69
+ deviceCode: data.device_code,
70
+ userCode: data.user_code,
71
+ verificationUri: data.verification_uri,
72
+ verificationUriComplete: data.verification_uri_complete ?? data.verification_uri,
73
+ expiresIn,
74
+ interval
75
+ };
76
+ }
77
+ function sleep(ms, signal) {
78
+ return new Promise((resolve, reject) => {
79
+ const timer = setTimeout(resolve, ms);
80
+ signal?.addEventListener(
81
+ "abort",
82
+ () => {
83
+ clearTimeout(timer);
84
+ reject(new DOMException("Aborted", "AbortError"));
85
+ },
86
+ { once: true }
87
+ );
88
+ });
89
+ }
90
+ async function pollDeviceToken(params) {
91
+ const MAX_POLL_INTERVAL = 60;
92
+ const MAX_POLL_ATTEMPTS = 200;
93
+ const { appId, appSecret, brand, deviceCode, expiresIn, signal } = params;
94
+ let interval = params.interval;
95
+ const endpoints = params.endpoints || resolveOAuthEndpoints(brand);
96
+ const deadline = Date.now() + expiresIn * 1e3;
97
+ let attempts = 0;
98
+ while (Date.now() < deadline && attempts < MAX_POLL_ATTEMPTS) {
99
+ attempts++;
100
+ if (signal?.aborted) {
101
+ return { ok: false, error: "expired_token", message: "Polling was cancelled" };
102
+ }
103
+ await sleep(interval * 1e3, signal);
104
+ let data;
105
+ try {
106
+ const resp = await feishuFetch(endpoints.token, {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
109
+ body: new URLSearchParams({
110
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
111
+ device_code: deviceCode,
112
+ client_id: appId,
113
+ client_secret: appSecret
114
+ }).toString()
115
+ });
116
+ data = await resp.json();
117
+ } catch (err) {
118
+ log.warn(`poll network error: ${err}`);
119
+ interval = Math.min(interval + 1, MAX_POLL_INTERVAL);
120
+ continue;
121
+ }
122
+ const error = data.error;
123
+ if (!error && data.access_token) {
124
+ log.info("token obtained successfully");
125
+ const refreshToken = data.refresh_token ?? "";
126
+ const expiresIn2 = data.expires_in ?? 7200;
127
+ let refreshExpiresIn = data.refresh_token_expires_in ?? 604800;
128
+ if (!refreshToken) {
129
+ log.warn("no refresh_token in response, token will not be refreshable");
130
+ refreshExpiresIn = expiresIn2;
131
+ }
132
+ return {
133
+ ok: true,
134
+ token: {
135
+ accessToken: data.access_token,
136
+ refreshToken,
137
+ expiresIn: expiresIn2,
138
+ refreshExpiresIn,
139
+ scope: data.scope ?? ""
140
+ }
141
+ };
142
+ }
143
+ if (error === "authorization_pending") {
144
+ log.debug("authorization_pending, retrying...");
145
+ continue;
146
+ }
147
+ if (error === "slow_down") {
148
+ interval = Math.min(interval + 5, MAX_POLL_INTERVAL);
149
+ log.info(`slow_down, interval increased to ${interval}s`);
150
+ continue;
151
+ }
152
+ if (error === "access_denied") {
153
+ log.info("user denied authorization");
154
+ return { ok: false, error: "access_denied", message: "\u7528\u6237\u62D2\u7EDD\u4E86\u6388\u6743" };
155
+ }
156
+ if (error === "expired_token" || error === "invalid_grant") {
157
+ log.info(`device code expired/invalid (error=${error})`);
158
+ return { ok: false, error: "expired_token", message: "\u6388\u6743\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77" };
159
+ }
160
+ const desc = data.error_description ?? error ?? "Unknown error";
161
+ log.warn(`unexpected error: error=${error}, desc=${desc}`);
162
+ return { ok: false, error: "expired_token", message: desc };
163
+ }
164
+ if (attempts >= MAX_POLL_ATTEMPTS) {
165
+ log.warn(`max poll attempts (${MAX_POLL_ATTEMPTS}) reached`);
166
+ }
167
+ return { ok: false, error: "expired_token", message: "\u6388\u6743\u8D85\u65F6\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77" };
168
+ }
169
+ export {
170
+ pollDeviceToken,
171
+ requestDeviceAuthorization,
172
+ resolveOAuthEndpoints
173
+ };
174
+ //# sourceMappingURL=device-flow.js.map