@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,538 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getLarkAccount } from "../core/accounts";
3
+ import { assertOwnerAccessStrict, OwnerAccessDeniedError } from "../core/owner-policy";
4
+ import { LarkClient } from "../core/lark-client";
5
+ import { getAppGrantedScopes } from "../core/app-scope-checker";
6
+ import { getTicket, withTicket } from "../core/lark-ticket";
7
+ import { larkLogger } from "../core/lark-logger";
8
+ const log = larkLogger("tools/oauth");
9
+ import { handleFeishuMessage } from "../messaging/inbound/handler";
10
+ import { formatLarkError } from "../core/api-error";
11
+ import { enqueueFeishuChatTask } from "../channel/chat-queue";
12
+ import { requestDeviceAuthorization, pollDeviceToken } from "../core/device-flow";
13
+ import { getStoredToken, setStoredToken, tokenStatus } from "../core/token-store";
14
+ import { revokeUAT } from "../core/uat-client";
15
+ import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from "../card/cardkit";
16
+ import { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard, buildAuthIdentityMismatchCard } from "./oauth-cards";
17
+ import { json } from "./oapi/helpers";
18
+ const FeishuOAuthSchema = Type.Object(
19
+ {
20
+ action: Type.Union(
21
+ [
22
+ // Type.Literal("authorize"), // 已由 auto-auth 自动处理,不再对外暴露
23
+ Type.Literal("revoke")
24
+ ],
25
+ {
26
+ description: "revoke: \u64A4\u9500\u5F53\u524D\u7528\u6237\u7684\u6388\u6743"
27
+ }
28
+ )
29
+ },
30
+ {
31
+ description: "\u98DE\u4E66\u7528\u6237\u6388\u6743\u7BA1\u7406\u5DE5\u5177\u3002\u3010\u6CE8\u610F\u3011\u6388\u6743\u6D41\u7A0B\u7531\u7CFB\u7EDF\u81EA\u52A8\u53D1\u8D77\uFF0C\u4E0D\u8981\u4E3B\u52A8\u8C03\u7528\u6B64\u5DE5\u5177\u89E6\u53D1\u6388\u6743\uFF01\u6B64\u5DE5\u5177\u4EC5\u7528\u4E8E\u64A4\u9500\u6388\u6743\uFF08revoke\uFF09\u3002\u4E0D\u9700\u8981\u4F20\u5165 user_open_id\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u8BC6\u522B\u5F53\u524D\u7528\u6237\u3002"
32
+ }
33
+ );
34
+ const pendingFlows = /* @__PURE__ */ new Map();
35
+ async function verifyTokenIdentity(brand, accessToken, expectedOpenId) {
36
+ const domain = brand === "lark" ? "https://open.larksuite.com" : "https://open.feishu.cn";
37
+ const url = `${domain}/open-apis/authen/v1/user_info`;
38
+ try {
39
+ const res = await fetch(url, {
40
+ headers: { Authorization: `Bearer ${accessToken}` }
41
+ });
42
+ const data = await res.json();
43
+ if (data.code !== 0) {
44
+ log.warn(`user_info API error: code=${data.code}, msg=${data.msg}`);
45
+ return { valid: false };
46
+ }
47
+ const actualOpenId = data.data?.open_id;
48
+ if (!actualOpenId) {
49
+ log.warn("user_info API returned no open_id");
50
+ return { valid: false };
51
+ }
52
+ return {
53
+ valid: actualOpenId === expectedOpenId,
54
+ actualOpenId
55
+ };
56
+ } catch (err) {
57
+ log.warn(`identity verification request failed: ${err}`);
58
+ return { valid: false };
59
+ }
60
+ }
61
+ function registerFeishuOAuthTool(api) {
62
+ if (!api.config) return;
63
+ const cfg = api.config;
64
+ api.registerTool(
65
+ {
66
+ name: "feishu_oauth",
67
+ label: "Feishu OAuth",
68
+ description: "\u98DE\u4E66\u7528\u6237\u6388\u6743\uFF08OAuth\uFF09\u7BA1\u7406\u5DE5\u5177\u3002\u3010\u6CE8\u610F\u3011\u6388\u6743\u6D41\u7A0B\u7531\u7CFB\u7EDF\u81EA\u52A8\u53D1\u8D77\uFF0C\u4E0D\u8981\u4E3B\u52A8\u8C03\u7528\u6B64\u5DE5\u5177\u89E6\u53D1\u6388\u6743\uFF01\u6B64\u5DE5\u5177\u4EC5\u7528\u4E8E revoke\uFF08\u64A4\u9500\u5F53\u524D\u7528\u6237\u7684\u6388\u6743\uFF09\u3002\u4E0D\u9700\u8981\u4F20\u5165 user_open_id\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u4ECE\u6D88\u606F\u4E0A\u4E0B\u6587\u83B7\u53D6\u5F53\u524D\u7528\u6237\u3002\u3010Token \u8FC7\u671F\u5904\u7406\u3011\u5F53\u8FD4\u56DE token_expired \u9519\u8BEF\u65F6\uFF0C\u8C03\u7528 revoke \u64A4\u9500\u540E\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002",
69
+ parameters: FeishuOAuthSchema,
70
+ async execute(_toolCallId, params) {
71
+ const p = params;
72
+ const ticket = getTicket();
73
+ const senderOpenId = ticket?.senderOpenId;
74
+ if (!senderOpenId) {
75
+ return json({
76
+ error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002"
77
+ });
78
+ }
79
+ const acct = getLarkAccount(cfg, ticket.accountId);
80
+ if (!acct.configured) {
81
+ return json({
82
+ error: `\u8D26\u53F7 ${ticket.accountId} \u7F3A\u5C11 appId \u6216 appSecret \u914D\u7F6E`
83
+ });
84
+ }
85
+ const account = acct;
86
+ try {
87
+ switch (p.action) {
88
+ // ---------------------------------------------------------------
89
+ // AUTHORIZE — 已由 auto-auth 自动处理,此分支不再对外暴露
90
+ // ---------------------------------------------------------------
91
+ // case "authorize": {
92
+ // return await executeAuthorize({
93
+ // account,
94
+ // senderOpenId,
95
+ // scope: p.scope || "",
96
+ // isBatchAuth: false,
97
+ // cfg,
98
+ // ticket,
99
+ // });
100
+ // }
101
+ // ---------------------------------------------------------------
102
+ // STATUS
103
+ // ---------------------------------------------------------------
104
+ // case "status": {
105
+ // const status = await getUATStatus(account.appId, senderOpenId);
106
+ // return json({
107
+ // authorized: status.authorized,
108
+ // scope: status.scope,
109
+ // token_status: status.tokenStatus,
110
+ // granted_at: status.grantedAt
111
+ // ? new Date(status.grantedAt).toISOString()
112
+ // : undefined,
113
+ // expires_at: status.expiresAt
114
+ // ? new Date(status.expiresAt).toISOString()
115
+ // : undefined,
116
+ // });
117
+ // }
118
+ // ---------------------------------------------------------------
119
+ // REVOKE
120
+ // ---------------------------------------------------------------
121
+ case "revoke": {
122
+ await revokeUAT(account.appId, senderOpenId);
123
+ return json({ success: true, message: "\u7528\u6237\u6388\u6743\u5DF2\u64A4\u9500\u3002" });
124
+ }
125
+ default:
126
+ return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${p.action}` });
127
+ }
128
+ } catch (err) {
129
+ log.error(`${p.action} failed: ${err}`);
130
+ return json({ error: formatLarkError(err) });
131
+ }
132
+ }
133
+ },
134
+ { name: "feishu_oauth" }
135
+ );
136
+ api.logger.info?.("feishu_oauth: Registered feishu_oauth tool");
137
+ }
138
+ async function executeAuthorize(params) {
139
+ const {
140
+ account,
141
+ senderOpenId,
142
+ scope,
143
+ isBatchAuth,
144
+ totalAppScopes,
145
+ alreadyGranted,
146
+ batchInfo,
147
+ skipSyntheticMessage,
148
+ showBatchAuthHint,
149
+ forceAuth,
150
+ onAuthComplete,
151
+ cfg,
152
+ ticket
153
+ } = params;
154
+ const { appId, appSecret, brand, accountId } = account;
155
+ const sdk = LarkClient.fromAccount(account).sdk;
156
+ try {
157
+ await assertOwnerAccessStrict(account, sdk, senderOpenId);
158
+ } catch (err) {
159
+ if (err instanceof OwnerAccessDeniedError) {
160
+ log.warn(`non-owner user ${senderOpenId} attempted to authorize`);
161
+ return json({
162
+ error: "permission_denied",
163
+ message: "\u5F53\u524D\u5E94\u7528\u4EC5\u9650\u6240\u6709\u8005\uFF08App Owner\uFF09\u4F7F\u7528\u3002\u60A8\u6CA1\u6709\u6743\u9650\u53D1\u8D77\u6388\u6743\uFF0C\u65E0\u6CD5\u4F7F\u7528\u76F8\u5173\u529F\u80FD\u3002"
164
+ });
165
+ }
166
+ throw err;
167
+ }
168
+ let effectiveScope = scope;
169
+ const existing = forceAuth ? null : await getStoredToken(appId, senderOpenId);
170
+ if (existing && tokenStatus(existing) !== "expired") {
171
+ if (effectiveScope) {
172
+ const requestedScopes = effectiveScope.split(/\s+/).filter(Boolean);
173
+ const grantedScopes = new Set((existing.scope ?? "").split(/\s+/).filter(Boolean));
174
+ const missingScopes = requestedScopes.filter((s) => !grantedScopes.has(s));
175
+ if (missingScopes.length > 0) {
176
+ log.info(`existing token missing scopes [${missingScopes.join(", ")}], starting incremental auth`);
177
+ } else {
178
+ if (onAuthComplete) {
179
+ try {
180
+ await onAuthComplete();
181
+ } catch (e) {
182
+ log.warn(`onAuthComplete failed: ${e}`);
183
+ }
184
+ }
185
+ return json({
186
+ success: true,
187
+ message: "\u7528\u6237\u5DF2\u6388\u6743\uFF0Cscope \u5DF2\u8986\u76D6\u3002",
188
+ authorized: true,
189
+ scope: existing.scope
190
+ });
191
+ }
192
+ } else {
193
+ if (onAuthComplete) {
194
+ try {
195
+ await onAuthComplete();
196
+ } catch (e) {
197
+ log.warn(`onAuthComplete failed: ${e}`);
198
+ }
199
+ }
200
+ return json({
201
+ success: true,
202
+ message: "\u7528\u6237\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002",
203
+ authorized: true,
204
+ scope: existing.scope
205
+ });
206
+ }
207
+ }
208
+ const flowKey = `${appId}:${senderOpenId}`;
209
+ let reuseCardId;
210
+ let reuseSeq = 0;
211
+ if (pendingFlows.has(flowKey)) {
212
+ const oldFlow = pendingFlows.get(flowKey);
213
+ const currentMessageId = ticket?.messageId ?? "";
214
+ if (oldFlow.messageId === currentMessageId) {
215
+ oldFlow.superseded = true;
216
+ oldFlow.controller.abort();
217
+ reuseCardId = oldFlow.cardId;
218
+ reuseSeq = oldFlow.sequence;
219
+ pendingFlows.delete(flowKey);
220
+ if (oldFlow.scope) {
221
+ const oldScopes = oldFlow.scope.split(/\s+/).filter(Boolean);
222
+ const newScopes = effectiveScope?.split(/\s+/).filter(Boolean) ?? [];
223
+ const merged = /* @__PURE__ */ new Set([...oldScopes, ...newScopes]);
224
+ effectiveScope = [...merged].join(" ");
225
+ log.info(`scope merge on reuse: [${[...merged].join(", ")}]`);
226
+ }
227
+ log.info(`same message, replacing flow for user=${senderOpenId}, app=${appId}, reusing cardId=${reuseCardId}`);
228
+ } else {
229
+ oldFlow.superseded = true;
230
+ oldFlow.controller.abort();
231
+ pendingFlows.delete(flowKey);
232
+ log.info(`new message, cancelling old flow for user=${senderOpenId}, app=${appId}, old cardId=${oldFlow.cardId}`);
233
+ try {
234
+ await updateCardKitCardForAuth({
235
+ cfg,
236
+ cardId: oldFlow.cardId,
237
+ card: buildAuthFailedCard("\u65B0\u7684\u6388\u6743\u8BF7\u6C42\u5DF2\u53D1\u8D77"),
238
+ sequence: oldFlow.sequence + 1,
239
+ accountId
240
+ });
241
+ } catch (e) {
242
+ log.warn(`failed to update old card to expired: ${e}`);
243
+ }
244
+ }
245
+ }
246
+ let filteredScope = effectiveScope;
247
+ let unavailableScopes = [];
248
+ if (effectiveScope) {
249
+ try {
250
+ const sdk2 = LarkClient.fromAccount(account).sdk;
251
+ const requestedScopes = effectiveScope.split(/\s+/).filter(Boolean);
252
+ const appScopes = await getAppGrantedScopes(sdk2, appId, "user");
253
+ const availableScopes = requestedScopes.filter((s) => appScopes.includes(s));
254
+ unavailableScopes = requestedScopes.filter((s) => !appScopes.includes(s));
255
+ if (unavailableScopes.length > 0) {
256
+ log.info(`app has not granted scopes [${unavailableScopes.join(", ")}], filtering them out`);
257
+ if (availableScopes.length === 0) {
258
+ const permissionUrl = `https://open.feishu.cn/app/${appId}/permission`;
259
+ return json({
260
+ error: "app_scopes_not_granted",
261
+ message: `\u5E94\u7528\u672A\u5F00\u901A\u4EFB\u4F55\u8BF7\u6C42\u7684\u7528\u6237\u6743\u9650\uFF0C\u65E0\u6CD5\u53D1\u8D77\u6388\u6743\u3002\u8BF7\u5148\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u4EE5\u4E0B\u6743\u9650\uFF1A
262
+ ${unavailableScopes.map((s) => `- ${s}`).join("\n")}
263
+
264
+ \u6743\u9650\u7BA1\u7406\u5730\u5740\uFF1A${permissionUrl}`,
265
+ unavailable_scopes: unavailableScopes,
266
+ app_permission_url: permissionUrl
267
+ });
268
+ }
269
+ filteredScope = availableScopes.join(" ");
270
+ log.info(`proceeding with available scopes [${availableScopes.join(", ")}]`);
271
+ }
272
+ } catch (err) {
273
+ log.warn(`failed to check app scopes, proceeding anyway: ${err}`);
274
+ }
275
+ }
276
+ const deviceAuth = await requestDeviceAuthorization({
277
+ appId,
278
+ appSecret,
279
+ brand,
280
+ scope: filteredScope
281
+ });
282
+ const authCard = buildAuthCard({
283
+ verificationUriComplete: deviceAuth.verificationUriComplete,
284
+ expiresMin: Math.round(deviceAuth.expiresIn / 60),
285
+ scope: filteredScope,
286
+ // 使用过滤后的 scope
287
+ isBatchAuth,
288
+ totalAppScopes,
289
+ alreadyGranted,
290
+ batchInfo,
291
+ filteredScopes: unavailableScopes.length > 0 ? unavailableScopes : void 0,
292
+ appId,
293
+ showBatchAuthHint
294
+ });
295
+ let cardId;
296
+ let seq;
297
+ const chatId = ticket?.chatId;
298
+ if (!chatId || !ticket) {
299
+ return json({ error: "\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807" });
300
+ }
301
+ if (reuseCardId) {
302
+ const newSeq = reuseSeq + 1;
303
+ try {
304
+ await updateCardKitCardForAuth({
305
+ cfg,
306
+ cardId: reuseCardId,
307
+ card: authCard,
308
+ sequence: newSeq,
309
+ accountId
310
+ });
311
+ log.info(`updated existing card ${reuseCardId} with merged scopes, seq=${newSeq}`);
312
+ } catch (err) {
313
+ log.warn(`failed to update existing card, creating new one: ${err}`);
314
+ const newCardId = await createCardEntity({ cfg, card: authCard, accountId });
315
+ if (!newCardId) return json({ error: "\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25" });
316
+ if (chatId) {
317
+ await sendCardByCardId({
318
+ cfg,
319
+ to: chatId,
320
+ cardId: newCardId,
321
+ replyToMessageId: ticket?.messageId?.startsWith("om_") ? ticket.messageId : void 0,
322
+ replyInThread: Boolean(ticket?.threadId),
323
+ accountId
324
+ });
325
+ }
326
+ cardId = newCardId;
327
+ seq = 1;
328
+ reuseCardId = void 0;
329
+ }
330
+ if (reuseCardId) {
331
+ cardId = reuseCardId;
332
+ seq = newSeq;
333
+ } else {
334
+ cardId = cardId;
335
+ seq = seq;
336
+ }
337
+ } else {
338
+ const newCardId = await createCardEntity({ cfg, card: authCard, accountId });
339
+ if (!newCardId) {
340
+ return json({ error: "\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25" });
341
+ }
342
+ await sendCardByCardId({
343
+ cfg,
344
+ to: chatId,
345
+ cardId: newCardId,
346
+ replyToMessageId: ticket?.messageId?.startsWith("om_") ? ticket.messageId : void 0,
347
+ replyInThread: Boolean(ticket?.threadId),
348
+ accountId
349
+ });
350
+ cardId = newCardId;
351
+ seq = 1;
352
+ }
353
+ const abortController = new AbortController();
354
+ const currentFlow = {
355
+ controller: abortController,
356
+ cardId,
357
+ sequence: seq,
358
+ messageId: ticket?.messageId ?? "",
359
+ superseded: false,
360
+ scope: effectiveScope
361
+ };
362
+ pendingFlows.set(flowKey, currentFlow);
363
+ let pendingFlowDelete = false;
364
+ pollDeviceToken({
365
+ appId,
366
+ appSecret,
367
+ brand,
368
+ deviceCode: deviceAuth.deviceCode,
369
+ interval: deviceAuth.interval,
370
+ expiresIn: deviceAuth.expiresIn,
371
+ signal: abortController.signal
372
+ }).then(async (result) => {
373
+ if (currentFlow.superseded) {
374
+ log.info(`flow superseded, skipping card update for cardId=${cardId}`);
375
+ return;
376
+ }
377
+ if (result.ok) {
378
+ const identity = await verifyTokenIdentity(brand, result.token.accessToken, senderOpenId);
379
+ if (!identity.valid) {
380
+ log.warn(
381
+ `identity mismatch! expected=${senderOpenId}, actual=${identity.actualOpenId ?? "unknown"}, cardId=${cardId}`
382
+ );
383
+ try {
384
+ await updateCardKitCardForAuth({
385
+ cfg,
386
+ cardId,
387
+ card: buildAuthIdentityMismatchCard(),
388
+ sequence: ++seq,
389
+ accountId
390
+ });
391
+ } catch (e) {
392
+ log.warn(`failed to update card for identity mismatch: ${e}`);
393
+ }
394
+ pendingFlows.delete(flowKey);
395
+ pendingFlowDelete = true;
396
+ return;
397
+ }
398
+ const now = Date.now();
399
+ const storedToken = {
400
+ userOpenId: senderOpenId,
401
+ appId,
402
+ accessToken: result.token.accessToken,
403
+ refreshToken: result.token.refreshToken,
404
+ expiresAt: now + result.token.expiresIn * 1e3,
405
+ refreshExpiresAt: now + result.token.refreshExpiresIn * 1e3,
406
+ scope: result.token.scope,
407
+ grantedAt: now
408
+ };
409
+ await setStoredToken(storedToken);
410
+ try {
411
+ await updateCardKitCardForAuth({
412
+ cfg,
413
+ cardId,
414
+ card: buildAuthSuccessCard(),
415
+ sequence: ++seq,
416
+ accountId
417
+ });
418
+ } catch (e) {
419
+ log.warn(`failed to update card to success: ${e}`);
420
+ }
421
+ pendingFlows.delete(flowKey);
422
+ pendingFlowDelete = true;
423
+ if (onAuthComplete) {
424
+ try {
425
+ await onAuthComplete();
426
+ } catch (e) {
427
+ log.warn(`onAuthComplete failed: ${e}`);
428
+ }
429
+ }
430
+ if (skipSyntheticMessage) {
431
+ log.info("skipSyntheticMessage=true, skipping synthetic message");
432
+ } else
433
+ try {
434
+ const syntheticMsgId = `${ticket.messageId}:auth-complete`;
435
+ const syntheticEvent = {
436
+ sender: {
437
+ sender_id: { open_id: senderOpenId }
438
+ },
439
+ message: {
440
+ message_id: syntheticMsgId,
441
+ chat_id: chatId,
442
+ chat_type: ticket.chatType ?? "p2p",
443
+ message_type: "text",
444
+ content: JSON.stringify({
445
+ text: "\u6211\u5DF2\u5B8C\u6210\u98DE\u4E66\u8D26\u53F7\u6388\u6743\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002"
446
+ }),
447
+ thread_id: ticket.threadId
448
+ }
449
+ };
450
+ const syntheticRuntime = {
451
+ log: (msg) => log.info(msg),
452
+ error: (msg) => log.error(msg)
453
+ };
454
+ const { status, promise } = enqueueFeishuChatTask({
455
+ accountId,
456
+ chatId,
457
+ threadId: ticket.threadId,
458
+ task: async () => {
459
+ await withTicket(
460
+ {
461
+ messageId: syntheticMsgId,
462
+ chatId,
463
+ accountId,
464
+ startTime: Date.now(),
465
+ senderOpenId,
466
+ chatType: ticket.chatType,
467
+ threadId: ticket.threadId
468
+ },
469
+ () => handleFeishuMessage({
470
+ cfg,
471
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
472
+ event: syntheticEvent,
473
+ accountId,
474
+ forceMention: true,
475
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
476
+ runtime: syntheticRuntime,
477
+ replyToMessageId: ticket.messageId
478
+ })
479
+ );
480
+ }
481
+ });
482
+ log.info(`synthetic message queued (${status})`);
483
+ await promise;
484
+ log.info("synthetic message dispatched after successful auth");
485
+ } catch (e) {
486
+ log.warn(`failed to send synthetic message after auth: ${e}`);
487
+ }
488
+ } else {
489
+ try {
490
+ await updateCardKitCardForAuth({
491
+ cfg,
492
+ cardId,
493
+ card: buildAuthFailedCard(result.message),
494
+ sequence: ++seq,
495
+ accountId
496
+ });
497
+ } catch (e) {
498
+ log.warn(`failed to update card to failure: ${e}`);
499
+ }
500
+ pendingFlows.delete(flowKey);
501
+ pendingFlowDelete = true;
502
+ }
503
+ }).catch((err) => {
504
+ log.error(`polling error: ${err}`);
505
+ }).finally(() => {
506
+ if (!pendingFlowDelete) {
507
+ if (pendingFlows.get(flowKey) === currentFlow) {
508
+ pendingFlows.delete(flowKey);
509
+ }
510
+ }
511
+ });
512
+ const scopeCount = filteredScope.split(/\s+/).filter(Boolean).length;
513
+ let message = isBatchAuth ? `\u5DF2\u53D1\u9001\u6279\u91CF\u6388\u6743\u8BF7\u6C42\u5361\u7247\uFF0C\u5171\u9700\u6388\u6743 ${scopeCount} \u4E2A\u6743\u9650\u3002\u8BF7\u5728\u5361\u7247\u4E2D\u5B8C\u6210\u6388\u6743\u3002` : "\u5DF2\u53D1\u9001\u6388\u6743\u8BF7\u6C42\u5361\u7247\uFF0C\u8BF7\u7528\u6237\u5728\u5361\u7247\u4E2D\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u8BF7\u91CD\u65B0\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002";
514
+ if (batchInfo) {
515
+ message += batchInfo;
516
+ }
517
+ if (unavailableScopes.length > 0) {
518
+ const permissionUrl = `https://open.feishu.cn/app/${appId}/permission`;
519
+ message += `
520
+
521
+ \u26A0\uFE0F **\u6CE8\u610F**\uFF1A\u4EE5\u4E0B\u6743\u9650\u56E0\u5E94\u7528\u672A\u5F00\u901A\u800C\u88AB\u8DF3\u8FC7\uFF0C\u5982\u9700\u4F7F\u7528\u8BF7\u5148\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\uFF1A
522
+ ${unavailableScopes.map((s) => `- ${s}`).join("\n")}
523
+
524
+ \u6743\u9650\u7BA1\u7406\u5730\u5740\uFF1A${permissionUrl}`;
525
+ }
526
+ return json({
527
+ success: true,
528
+ message,
529
+ awaiting_authorization: true,
530
+ filtered_scopes: unavailableScopes.length > 0 ? unavailableScopes : void 0,
531
+ app_permission_url: unavailableScopes.length > 0 ? `https://open.feishu.cn/app/${appId}/permission` : void 0
532
+ });
533
+ }
534
+ export {
535
+ executeAuthorize,
536
+ registerFeishuOAuthTool
537
+ };
538
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/tools/oauth.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * feishu_oauth tool \u2014 User OAuth authorisation management.\n *\n * Actions:\n * - authorize : Initiate Device Flow, send auth card, poll for token.\n * - status : Check whether the current user has a valid UAT.\n * - revoke : Remove the current user's stored UAT.\n *\n * Security:\n * - **Does not** accept a `user_open_id` parameter. The target user is\n * always the message sender, obtained from the LarkTicket.\n * - Token values are never included in the return payload (AI cannot see\n * them).\n */\n\nimport type { OpenClawPluginApi, ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport type { ConfiguredLarkAccount } from '../core/types';\nimport { Type } from '@sinclair/typebox';\nimport { getLarkAccount } from '../core/accounts';\nimport { assertOwnerAccessStrict, OwnerAccessDeniedError } from '../core/owner-policy';\nimport { LarkClient } from '../core/lark-client';\nimport { getAppGrantedScopes } from '../core/app-scope-checker';\nimport type { LarkTicket } from '../core/lark-ticket';\nimport { getTicket, withTicket } from '../core/lark-ticket';\nimport { larkLogger } from '../core/lark-logger';\n\nconst log = larkLogger('tools/oauth');\nimport { handleFeishuMessage } from '../messaging/inbound/handler';\nimport { formatLarkError } from '../core/api-error';\nimport { enqueueFeishuChatTask } from '../channel/chat-queue';\nimport { requestDeviceAuthorization, pollDeviceToken } from '../core/device-flow';\nimport { getStoredToken, setStoredToken, tokenStatus, type StoredUAToken } from '../core/token-store';\nimport { revokeUAT } from '../core/uat-client';\nimport { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';\nimport { buildAuthCard, buildAuthSuccessCard, buildAuthFailedCard, buildAuthIdentityMismatchCard } from './oauth-cards';\nimport { json } from './oapi/helpers';\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nconst FeishuOAuthSchema = Type.Object(\n {\n action: Type.Union(\n [\n // Type.Literal(\"authorize\"), // \u5DF2\u7531 auto-auth \u81EA\u52A8\u5904\u7406\uFF0C\u4E0D\u518D\u5BF9\u5916\u66B4\u9732\n Type.Literal('revoke'),\n ],\n {\n description: 'revoke: \u64A4\u9500\u5F53\u524D\u7528\u6237\u7684\u6388\u6743',\n },\n ),\n },\n {\n description:\n '\u98DE\u4E66\u7528\u6237\u6388\u6743\u7BA1\u7406\u5DE5\u5177\u3002' +\n '\u3010\u6CE8\u610F\u3011\u6388\u6743\u6D41\u7A0B\u7531\u7CFB\u7EDF\u81EA\u52A8\u53D1\u8D77\uFF0C\u4E0D\u8981\u4E3B\u52A8\u8C03\u7528\u6B64\u5DE5\u5177\u89E6\u53D1\u6388\u6743\uFF01' +\n '\u6B64\u5DE5\u5177\u4EC5\u7528\u4E8E\u64A4\u9500\u6388\u6743\uFF08revoke\uFF09\u3002' +\n '\u4E0D\u9700\u8981\u4F20\u5165 user_open_id\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u8BC6\u522B\u5F53\u524D\u7528\u6237\u3002',\n },\n);\n\ninterface FeishuOAuthParams {\n action: 'revoke';\n}\n\n// ---------------------------------------------------------------------------\n// In-flight authorize guard (prevent duplicate device-flows per user)\n// ---------------------------------------------------------------------------\n\ninterface PendingFlow {\n controller: AbortController;\n cardId: string;\n sequence: number;\n messageId: string;\n /** \u88AB\u65B0\u6D41\u66FF\u6362\u540E\u6807\u8BB0\u4E3A true\uFF0C\u65E7\u8F6E\u8BE2\u56DE\u8C03\u68C0\u6D4B\u5230\u540E\u8DF3\u8FC7\u5361\u7247\u66F4\u65B0 */\n superseded: boolean;\n /** \u5F53\u524D flow \u8BF7\u6C42\u7684 scope\uFF08\u7A7A\u683C\u5206\u9694\uFF09\uFF0C\u7528\u4E8E\u540E\u7EED scope \u5408\u5E76 */\n scope?: string;\n}\n\nconst pendingFlows = new Map<string, PendingFlow>();\n\n// ---------------------------------------------------------------------------\n// Identity verification after Device Flow\n// ---------------------------------------------------------------------------\n\n/**\n * \u4F7F\u7528\u521A\u83B7\u53D6\u7684 UAT \u8C03\u7528 /authen/v1/user_info\uFF0C\n * \u9A8C\u8BC1\u5B9E\u9645\u5B8C\u6210 OAuth \u6388\u6743\u7684\u7528\u6237 open_id \u662F\u5426\u4E0E\u9884\u671F\u7684 senderOpenId \u4E00\u81F4\u3002\n *\n * \u9632\u6B62\u7FA4\u804A\u4E2D\u5176\u4ED6\u7528\u6237\u70B9\u51FB\u6388\u6743\u94FE\u63A5\u540E\uFF0C\u9519\u8BEF\u7684 UAT \u88AB\u7ED1\u5B9A\u5230 owner \u7684\u8EAB\u4EFD\u3002\n */\nasync function verifyTokenIdentity(\n brand: string,\n accessToken: string,\n expectedOpenId: string,\n): Promise<{ valid: boolean; actualOpenId?: string }> {\n const domain = brand === 'lark' ? 'https://open.larksuite.com' : 'https://open.feishu.cn';\n const url = `${domain}/open-apis/authen/v1/user_info`;\n\n try {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n const data = (await res.json()) as {\n code?: number;\n msg?: string;\n data?: { open_id?: string };\n };\n if (data.code !== 0) {\n log.warn(`user_info API error: code=${data.code}, msg=${data.msg}`);\n return { valid: false };\n }\n const actualOpenId = data.data?.open_id;\n if (!actualOpenId) {\n log.warn('user_info API returned no open_id');\n return { valid: false };\n }\n return {\n valid: actualOpenId === expectedOpenId,\n actualOpenId,\n };\n } catch (err) {\n log.warn(`identity verification request failed: ${err}`);\n return { valid: false };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Registration\n// ---------------------------------------------------------------------------\n\nexport function registerFeishuOAuthTool(api: OpenClawPluginApi) {\n if (!api.config) return;\n\n const cfg = api.config;\n\n api.registerTool(\n {\n name: 'feishu_oauth',\n label: 'Feishu OAuth',\n description:\n '\u98DE\u4E66\u7528\u6237\u6388\u6743\uFF08OAuth\uFF09\u7BA1\u7406\u5DE5\u5177\u3002' +\n '\u3010\u6CE8\u610F\u3011\u6388\u6743\u6D41\u7A0B\u7531\u7CFB\u7EDF\u81EA\u52A8\u53D1\u8D77\uFF0C\u4E0D\u8981\u4E3B\u52A8\u8C03\u7528\u6B64\u5DE5\u5177\u89E6\u53D1\u6388\u6743\uFF01' +\n '\u6B64\u5DE5\u5177\u4EC5\u7528\u4E8E revoke\uFF08\u64A4\u9500\u5F53\u524D\u7528\u6237\u7684\u6388\u6743\uFF09\u3002' +\n '\u4E0D\u9700\u8981\u4F20\u5165 user_open_id\uFF0C\u7CFB\u7EDF\u81EA\u52A8\u4ECE\u6D88\u606F\u4E0A\u4E0B\u6587\u83B7\u53D6\u5F53\u524D\u7528\u6237\u3002' +\n '\u3010Token \u8FC7\u671F\u5904\u7406\u3011\u5F53\u8FD4\u56DE token_expired \u9519\u8BEF\u65F6\uFF0C\u8C03\u7528 revoke \u64A4\u9500\u540E\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u91CD\u65B0\u53D1\u8D77\u6388\u6743\u6D41\u7A0B\u3002',\n parameters: FeishuOAuthSchema,\n\n async execute(_toolCallId: string, params: unknown) {\n const p = params as FeishuOAuthParams;\n\n // Resolve identity from trace context (set in monitor.ts).\n const ticket = getTicket();\n const senderOpenId = ticket?.senderOpenId;\n if (!senderOpenId) {\n return json({\n error: '\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u7528\u6237\u8EAB\u4EFD\uFF08senderOpenId\uFF09\uFF0C\u8BF7\u5728\u98DE\u4E66\u5BF9\u8BDD\u4E2D\u4F7F\u7528\u6B64\u5DE5\u5177\u3002',\n });\n }\n\n // Use the accountId from LarkTicket to resolve the correct account\n // (important for multi-account setups like prod + boe).\n const acct = getLarkAccount(cfg, ticket.accountId);\n if (!acct.configured) {\n return json({\n error: `\u8D26\u53F7 ${ticket.accountId} \u7F3A\u5C11 appId \u6216 appSecret \u914D\u7F6E`,\n });\n }\n const account = acct; // Now we know it's ConfiguredLarkAccount\n\n try {\n switch (p.action) {\n // ---------------------------------------------------------------\n // AUTHORIZE \u2014 \u5DF2\u7531 auto-auth \u81EA\u52A8\u5904\u7406\uFF0C\u6B64\u5206\u652F\u4E0D\u518D\u5BF9\u5916\u66B4\u9732\n // ---------------------------------------------------------------\n // case \"authorize\": {\n // return await executeAuthorize({\n // account,\n // senderOpenId,\n // scope: p.scope || \"\",\n // isBatchAuth: false,\n // cfg,\n // ticket,\n // });\n // }\n\n // ---------------------------------------------------------------\n // STATUS\n // ---------------------------------------------------------------\n // case \"status\": {\n // const status = await getUATStatus(account.appId, senderOpenId);\n // return json({\n // authorized: status.authorized,\n // scope: status.scope,\n // token_status: status.tokenStatus,\n // granted_at: status.grantedAt\n // ? new Date(status.grantedAt).toISOString()\n // : undefined,\n // expires_at: status.expiresAt\n // ? new Date(status.expiresAt).toISOString()\n // : undefined,\n // });\n // }\n\n // ---------------------------------------------------------------\n // REVOKE\n // ---------------------------------------------------------------\n case 'revoke': {\n await revokeUAT(account.appId, senderOpenId);\n return json({ success: true, message: '\u7528\u6237\u6388\u6743\u5DF2\u64A4\u9500\u3002' });\n }\n\n default:\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return json({ error: `\u672A\u77E5\u64CD\u4F5C: ${(p as any).action}` });\n }\n } catch (err) {\n log.error(`${p.action} failed: ${err}`);\n return json({ error: formatLarkError(err) });\n }\n },\n },\n { name: 'feishu_oauth' },\n );\n\n api.logger.info?.('feishu_oauth: Registered feishu_oauth tool');\n}\n\n// ---------------------------------------------------------------------------\n// Shared authorize logic (used by both feishu_oauth and feishu_oauth_batch_auth)\n// ---------------------------------------------------------------------------\n\nexport interface ExecuteAuthorizeParams {\n account: ConfiguredLarkAccount;\n senderOpenId: string;\n scope: string;\n isBatchAuth?: boolean;\n totalAppScopes?: number;\n alreadyGranted?: number;\n batchInfo?: string; // \u5206\u6279\u6388\u6743\u63D0\u793A\u4FE1\u606F\n skipSyntheticMessage?: boolean; // true \u65F6\u8DF3\u8FC7\u5408\u6210\u6D88\u606F\u53D1\u9001\uFF08onboarding \u573A\u666F\uFF09\n showBatchAuthHint?: boolean; // true \u65F6\u5728\u6388\u6743\u5361\u7247\u5E95\u90E8\u5C55\u793A\"\u6388\u4E88\u6240\u6709\u7528\u6237\u6743\u9650\"\u63D0\u793A\uFF08\u4EC5 auto-auth \u6D41\u7A0B\uFF09\n forceAuth?: boolean; // true \u65F6\u8DF3\u8FC7\u672C\u5730 token \u7F13\u5B58\u68C0\u67E5\uFF0C\u5F3A\u5236\u53D1\u8D77\u65B0 Device Flow\uFF08AppScopeMissing \u573A\u666F\u4E13\u7528\uFF09\n onAuthComplete?: () => void | Promise<void>; // \u6388\u6743\u5B8C\u6210\u56DE\u8C03\uFF08\u7528\u4E8E\u6279\u91CF\u6388\u6743\u94FE\u5F0F\u89E6\u53D1\uFF09\n cfg: ClawdbotConfig;\n ticket: LarkTicket | undefined;\n}\n\n/**\n * \u6267\u884C OAuth \u6388\u6743\u6D41\u7A0B\uFF08Device Flow\uFF09\n * \u53EF\u88AB feishu_oauth \u548C feishu_oauth_batch_auth \u5171\u4EAB\u8C03\u7528\n */\nexport async function executeAuthorize(\n params: ExecuteAuthorizeParams,\n): Promise<{ content: Array<{ type: 'text'; text: string }>; details: unknown }> {\n const {\n account,\n senderOpenId,\n scope,\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n skipSyntheticMessage,\n showBatchAuthHint,\n forceAuth,\n onAuthComplete,\n cfg,\n ticket,\n } = params;\n const { appId, appSecret, brand, accountId } = account;\n\n // 0. Check if the user is the app owner (fail-close: \u5B89\u5168\u4F18\u5148).\n const sdk = LarkClient.fromAccount(account).sdk;\n try {\n await assertOwnerAccessStrict(account, sdk, senderOpenId);\n } catch (err) {\n if (err instanceof OwnerAccessDeniedError) {\n log.warn(`non-owner user ${senderOpenId} attempted to authorize`);\n return json({\n error: 'permission_denied',\n message: '\u5F53\u524D\u5E94\u7528\u4EC5\u9650\u6240\u6709\u8005\uFF08App Owner\uFF09\u4F7F\u7528\u3002\u60A8\u6CA1\u6709\u6743\u9650\u53D1\u8D77\u6388\u6743\uFF0C\u65E0\u6CD5\u4F7F\u7528\u76F8\u5173\u529F\u80FD\u3002',\n });\n }\n throw err;\n }\n\n // effectiveScope\uFF1A\u53EF\u53D8 scope \u53D8\u91CF\uFF0C\u540E\u7EED\u53EF\u80FD\u56E0 pendingFlow \u5408\u5E76\u800C\u6269\u5927\n let effectiveScope = scope;\n\n // 1. Check if user already authorised + scope coverage.\n // forceAuth=true \u65F6\u8DF3\u8FC7\u7F13\u5B58\u68C0\u67E5\uFF0C\u76F4\u63A5\u53D1\u8D77\u65B0 Device Flow\u3002\n // \u7528\u4E8E AppScopeMissing \u573A\u666F\uFF1A\u5E94\u7528\u6743\u9650\u521A\u88AB\u79FB\u9664\u518D\u8865\u56DE\uFF0C\u672C\u5730 UAT \u7F13\u5B58\u7684 scope \u72B6\u6001\u4E0D\u53EF\u4FE1\u3002\n const existing = forceAuth ? null : await getStoredToken(appId, senderOpenId);\n if (existing && tokenStatus(existing) !== 'expired') {\n // \u5982\u679C\u8BF7\u6C42\u4E86\u7279\u5B9A scope\uFF0C\u68C0\u67E5\u662F\u5426\u5DF2\u8986\u76D6\n if (effectiveScope) {\n const requestedScopes = effectiveScope.split(/\\s+/).filter(Boolean);\n const grantedScopes = new Set((existing.scope ?? '').split(/\\s+/).filter(Boolean));\n const missingScopes = requestedScopes.filter((s) => !grantedScopes.has(s));\n\n if (missingScopes.length > 0) {\n // scope \u4E0D\u8DB3 \u2192 \u7EE7\u7EED\u8D70 Device Flow\uFF08\u98DE\u4E66 OAuth \u662F\u589E\u91CF\u6388\u6743\uFF09\n log.info(`existing token missing scopes [${missingScopes.join(', ')}], starting incremental auth`);\n // \u4E0D revoke \u65E7 token\uFF0C\u76F4\u63A5\u7528\u7F3A\u5931\u7684 scope \u53D1\u8D77\u65B0 Device Flow\n // \u98DE\u4E66\u4F1A\u7D2F\u79EF\u6388\u6743\uFF0C\u65B0 token \u5305\u542B\u65E7 + \u65B0 scope\n // \u7EE7\u7EED\u6267\u884C\u4E0B\u9762\u7684 Device Flow \u903B\u8F91\n } else {\n if (onAuthComplete) {\n try {\n await onAuthComplete();\n } catch (e) {\n log.warn(`onAuthComplete failed: ${e}`);\n }\n }\n return json({\n success: true,\n message: '\u7528\u6237\u5DF2\u6388\u6743\uFF0Cscope \u5DF2\u8986\u76D6\u3002',\n authorized: true,\n scope: existing.scope,\n });\n }\n } else {\n if (onAuthComplete) {\n try {\n await onAuthComplete();\n } catch (e) {\n log.warn(`onAuthComplete failed: ${e}`);\n }\n }\n return json({\n success: true,\n message: '\u7528\u6237\u5DF2\u6388\u6743\uFF0C\u65E0\u9700\u91CD\u590D\u6388\u6743\u3002',\n authorized: true,\n scope: existing!.scope,\n });\n }\n }\n\n // 2. Guard against duplicate in-flight flows for this user.\n const flowKey = `${appId}:${senderOpenId}`;\n let reuseCardId: string | undefined;\n let reuseSeq = 0;\n\n if (pendingFlows.has(flowKey)) {\n const oldFlow = pendingFlows.get(flowKey)!;\n const currentMessageId = ticket?.messageId ?? '';\n\n if (oldFlow.messageId === currentMessageId) {\n // \u540C\u4E00\u8F6E\u5DE5\u5177\u8C03\u7528\uFF08messageId \u76F8\u540C\uFF09\u2192 \u590D\u7528\u65E7\u5361\u7247\n oldFlow.superseded = true;\n oldFlow.controller.abort();\n reuseCardId = oldFlow.cardId;\n reuseSeq = oldFlow.sequence;\n pendingFlows.delete(flowKey);\n\n // scope \u5408\u5E76\uFF1A\u5C06\u65E7 flow \u7684 scope \u4E0E\u65B0\u8BF7\u6C42\u5408\u5E76\n if (oldFlow.scope) {\n const oldScopes = oldFlow.scope.split(/\\s+/).filter(Boolean);\n const newScopes = effectiveScope?.split(/\\s+/).filter(Boolean) ?? [];\n const merged = new Set([...oldScopes, ...newScopes]);\n effectiveScope = [...merged].join(' ');\n log.info(`scope merge on reuse: [${[...merged].join(', ')}]`);\n }\n\n log.info(`same message, replacing flow for user=${senderOpenId}, app=${appId}, reusing cardId=${reuseCardId}`);\n } else {\n // \u65B0\u5BF9\u8BDD\uFF08messageId \u4E0D\u540C\uFF09\u2192 \u53D6\u6D88\u65E7\u6D41 + \u65E7\u5361\u7247\u6807\u8BB0\"\u6388\u6743\u672A\u5B8C\u6210\" + \u521B\u5EFA\u65B0\u5361\u7247\n oldFlow.superseded = true;\n oldFlow.controller.abort();\n pendingFlows.delete(flowKey);\n log.info(`new message, cancelling old flow for user=${senderOpenId}, app=${appId}, old cardId=${oldFlow.cardId}`);\n // \u6807\u8BB0\u65E7\u5361\u7247\u4E3A\"\u6388\u6743\u672A\u5B8C\u6210\"\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: oldFlow.cardId,\n card: buildAuthFailedCard('\u65B0\u7684\u6388\u6743\u8BF7\u6C42\u5DF2\u53D1\u8D77'),\n sequence: oldFlow.sequence + 1,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update old card to expired: ${e}`);\n }\n // reuseCardId \u4FDD\u6301 undefined\uFF0C\u540E\u7EED\u4F1A\u521B\u5EFA\u65B0\u5361\u7247\n }\n }\n\n // 2.5 \u5E94\u7528 scope \u9884\u68C0\uFF1A\u8FC7\u6EE4\u6389\u5E94\u7528\u672A\u5F00\u901A\u7684 scope\n let filteredScope = effectiveScope;\n let unavailableScopes: string[] = [];\n\n if (effectiveScope) {\n try {\n const sdk = LarkClient.fromAccount(account).sdk;\n const requestedScopes = effectiveScope.split(/\\s+/).filter(Boolean);\n const appScopes = await getAppGrantedScopes(sdk, appId, 'user');\n\n const availableScopes = requestedScopes.filter((s) => appScopes.includes(s));\n unavailableScopes = requestedScopes.filter((s) => !appScopes.includes(s));\n\n if (unavailableScopes.length > 0) {\n log.info(`app has not granted scopes [${unavailableScopes.join(', ')}], filtering them out`);\n\n if (availableScopes.length === 0) {\n // \u6240\u6709 scope \u90FD\u672A\u5F00\u901A\uFF0C\u76F4\u63A5\u8FD4\u56DE\u9519\u8BEF\n const permissionUrl = `https://open.feishu.cn/app/${appId}/permission`;\n return json({\n error: 'app_scopes_not_granted',\n message: `\u5E94\u7528\u672A\u5F00\u901A\u4EFB\u4F55\u8BF7\u6C42\u7684\u7528\u6237\u6743\u9650\uFF0C\u65E0\u6CD5\u53D1\u8D77\u6388\u6743\u3002\u8BF7\u5148\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\u4EE5\u4E0B\u6743\u9650\uFF1A\\n${unavailableScopes.map((s) => `- ${s}`).join('\\n')}\\n\\n\u6743\u9650\u7BA1\u7406\u5730\u5740\uFF1A${permissionUrl}`,\n unavailable_scopes: unavailableScopes,\n app_permission_url: permissionUrl,\n });\n }\n\n // \u90E8\u5206 scope \u672A\u5F00\u901A\uFF0C\u53EA\u6388\u6743\u5DF2\u5F00\u901A\u7684 scope\n filteredScope = availableScopes.join(' ');\n log.info(`proceeding with available scopes [${availableScopes.join(', ')}]`);\n }\n } catch (err) {\n // \u5982\u679C scope \u68C0\u67E5\u5931\u8D25\uFF0C\u8BB0\u5F55\u65E5\u5FD7\u4F46\u7EE7\u7EED\u6267\u884C\uFF08\u964D\u7EA7\u5904\u7406\uFF09\n log.warn(`failed to check app scopes, proceeding anyway: ${err}`);\n }\n }\n\n // 3. Request device authorisation.\n const deviceAuth = await requestDeviceAuthorization({\n appId,\n appSecret,\n brand,\n scope: filteredScope,\n });\n\n // 4. Build and send authorisation card.\n const authCard = buildAuthCard({\n verificationUriComplete: deviceAuth.verificationUriComplete,\n expiresMin: Math.round(deviceAuth.expiresIn / 60),\n scope: filteredScope, // \u4F7F\u7528\u8FC7\u6EE4\u540E\u7684 scope\n isBatchAuth,\n totalAppScopes,\n alreadyGranted,\n batchInfo,\n filteredScopes: unavailableScopes.length > 0 ? unavailableScopes : undefined,\n appId,\n showBatchAuthHint,\n });\n\n let cardId: string;\n let seq: number;\n const chatId = ticket?.chatId;\n if (!chatId || !ticket) {\n return json({ error: '\u65E0\u6CD5\u786E\u5B9A\u53D1\u9001\u76EE\u6807' });\n }\n\n if (reuseCardId) {\n // \u590D\u7528\u65E7\u5361\u7247\uFF1A\u539F\u5730\u66F4\u65B0\u5185\u5BB9\uFF08scope + \u6388\u6743\u94FE\u63A5\uFF09\uFF0C\u4E0D\u521B\u5EFA\u65B0\u5361\u7247\n const newSeq = reuseSeq + 1;\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId: reuseCardId,\n card: authCard,\n sequence: newSeq,\n accountId,\n });\n log.info(`updated existing card ${reuseCardId} with merged scopes, seq=${newSeq}`);\n } catch (err) {\n log.warn(`failed to update existing card, creating new one: ${err}`);\n // \u964D\u7EA7\uFF1A\u521B\u5EFA\u65B0\u5361\u7247\n const newCardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!newCardId) return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n if (chatId) {\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId: newCardId,\n replyToMessageId: ticket?.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket?.threadId),\n accountId,\n });\n }\n cardId = newCardId;\n seq = 1;\n reuseCardId = undefined;\n }\n if (reuseCardId) {\n cardId = reuseCardId;\n seq = newSeq;\n } else {\n cardId = cardId!;\n seq = seq!;\n }\n } else {\n // \u9996\u6B21\u521B\u5EFA\u5361\u7247\n const newCardId = await createCardEntity({ cfg, card: authCard, accountId });\n if (!newCardId) {\n return json({ error: '\u521B\u5EFA\u6388\u6743\u5361\u7247\u5931\u8D25' });\n }\n\n await sendCardByCardId({\n cfg,\n to: chatId,\n cardId: newCardId,\n replyToMessageId: ticket?.messageId?.startsWith('om_') ? ticket.messageId : undefined,\n replyInThread: Boolean(ticket?.threadId),\n accountId,\n });\n\n cardId = newCardId;\n seq = 1;\n }\n\n // 7. Start background polling.\n const abortController = new AbortController();\n\n const currentFlow: PendingFlow = {\n controller: abortController,\n cardId,\n sequence: seq,\n messageId: ticket?.messageId ?? '',\n superseded: false,\n scope: effectiveScope,\n };\n pendingFlows.set(flowKey, currentFlow);\n let pendingFlowDelete = false;\n // Fire-and-forget \u2013 polling happens asynchronously.\n pollDeviceToken({\n appId,\n appSecret,\n brand,\n deviceCode: deviceAuth.deviceCode,\n interval: deviceAuth.interval,\n expiresIn: deviceAuth.expiresIn,\n signal: abortController.signal,\n })\n .then(async (result) => {\n // \u88AB\u65B0\u6D41\u66FF\u6362\u540E\uFF0C\u8DF3\u8FC7\u6240\u6709\u5361\u7247\u66F4\u65B0\uFF0C\u907F\u514D\u8986\u76D6\u65B0\u6D41\u7684\u5361\u7247\u5185\u5BB9\n if (currentFlow.superseded) {\n log.info(`flow superseded, skipping card update for cardId=${cardId}`);\n return;\n }\n if (result.ok) {\n // ===== \u8EAB\u4EFD\u6821\u9A8C\uFF1A\u9A8C\u8BC1\u5B9E\u9645\u6388\u6743\u7528\u6237\u4E0E\u53D1\u8D77\u4EBA\u4E00\u81F4 =====\n const identity = await verifyTokenIdentity(brand, result.token.accessToken, senderOpenId);\n if (!identity.valid) {\n log.warn(\n `identity mismatch! expected=${senderOpenId}, ` +\n `actual=${identity.actualOpenId ?? 'unknown'}, cardId=${cardId}`,\n );\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId,\n card: buildAuthIdentityMismatchCard(),\n sequence: ++seq,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update card for identity mismatch: ${e}`);\n }\n pendingFlows.delete(flowKey);\n pendingFlowDelete = true;\n return;\n }\n // ===== \u8EAB\u4EFD\u6821\u9A8C\u901A\u8FC7\uFF0C\u7EE7\u7EED\u4FDD\u5B58 token =====\n\n // Save token to Keychain.\n const now = Date.now();\n const storedToken: StoredUAToken = {\n userOpenId: senderOpenId,\n appId,\n accessToken: result.token.accessToken,\n refreshToken: result.token.refreshToken,\n expiresAt: now + result.token.expiresIn * 1000,\n refreshExpiresAt: now + result.token.refreshExpiresIn * 1000,\n scope: result.token.scope,\n grantedAt: now,\n };\n await setStoredToken(storedToken);\n\n // 1. Update card \u2192 success immediately so user sees\n // visual confirmation right away.\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId,\n card: buildAuthSuccessCard(),\n sequence: ++seq,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update card to success: ${e}`);\n }\n // \u5220\u9664 pending flow\n pendingFlows.delete(flowKey);\n pendingFlowDelete = true;\n\n // 2. Send synthetic message to notify AI that auth is\n // complete, so it can automatically retry the operation.\n // Skip when called from onboarding (no AI context to retry).\n // \u8C03\u7528 onAuthComplete \u56DE\u8C03\uFF08\u7528\u4E8E onboarding \u6279\u91CF\u6388\u6743\u94FE\u5F0F\u89E6\u53D1\uFF09\n if (onAuthComplete) {\n try {\n await onAuthComplete();\n } catch (e) {\n log.warn(`onAuthComplete failed: ${e}`);\n }\n }\n\n if (skipSyntheticMessage) {\n log.info('skipSyntheticMessage=true, skipping synthetic message');\n } else\n try {\n // Use a unique message_id for MessageSid (avoids SDK dedup),\n // but pass the real message ID as replyToMessageId so that\n // typing indicators, reply-to threading, and delivery work.\n const syntheticMsgId = `${ticket.messageId}:auth-complete`;\n\n const syntheticEvent = {\n sender: {\n sender_id: { open_id: senderOpenId },\n },\n message: {\n message_id: syntheticMsgId,\n chat_id: chatId,\n chat_type: ticket.chatType ?? ('p2p' as const),\n message_type: 'text',\n content: JSON.stringify({\n text: '\u6211\u5DF2\u5B8C\u6210\u98DE\u4E66\u8D26\u53F7\u6388\u6743\uFF0C\u8BF7\u7EE7\u7EED\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002',\n }),\n thread_id: ticket.threadId,\n },\n };\n\n // Provide a minimal runtime so reply-dispatcher\n // does not crash on `params.runtime.log?.()`.\n const syntheticRuntime = {\n log: (msg: string) => log.info(msg),\n error: (msg: string) => log.error(msg),\n };\n\n const { status, promise } = enqueueFeishuChatTask({\n accountId,\n chatId,\n threadId: ticket.threadId,\n task: async () => {\n await withTicket(\n {\n messageId: syntheticMsgId,\n chatId,\n accountId,\n startTime: Date.now(),\n senderOpenId,\n chatType: ticket.chatType,\n threadId: ticket.threadId,\n },\n () =>\n handleFeishuMessage({\n cfg,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n event: syntheticEvent as any,\n accountId,\n forceMention: true,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n runtime: syntheticRuntime as any,\n replyToMessageId: ticket.messageId,\n }),\n );\n },\n });\n\n log.info(`synthetic message queued (${status})`);\n await promise;\n log.info('synthetic message dispatched after successful auth');\n } catch (e) {\n log.warn(`failed to send synthetic message after auth: ${e}`);\n }\n } else {\n // Update card \u2192 failure.\n try {\n await updateCardKitCardForAuth({\n cfg,\n cardId,\n card: buildAuthFailedCard(result.message),\n sequence: ++seq,\n accountId,\n });\n } catch (e) {\n log.warn(`failed to update card to failure: ${e}`);\n }\n // \u5220\u9664 pending flow\n pendingFlows.delete(flowKey);\n pendingFlowDelete = true;\n }\n })\n .catch((err) => {\n log.error(`polling error: ${err}`);\n })\n .finally(() => {\n if (!pendingFlowDelete) {\n // \u53EA\u5728\u5F53\u524D flow \u4ECD\u662F\u6CE8\u518C\u7684\u90A3\u4E2A\u65F6\u624D\u5220\u9664\uFF0C\u907F\u514D\u65E7\u6D41\u8BEF\u5220\u65B0\u6D41\u7684 entry\n if (pendingFlows.get(flowKey) === currentFlow) {\n pendingFlows.delete(flowKey);\n }\n }\n });\n\n const scopeCount = filteredScope.split(/\\s+/).filter(Boolean).length;\n let message = isBatchAuth\n ? `\u5DF2\u53D1\u9001\u6279\u91CF\u6388\u6743\u8BF7\u6C42\u5361\u7247\uFF0C\u5171\u9700\u6388\u6743 ${scopeCount} \u4E2A\u6743\u9650\u3002\u8BF7\u5728\u5361\u7247\u4E2D\u5B8C\u6210\u6388\u6743\u3002`\n : '\u5DF2\u53D1\u9001\u6388\u6743\u8BF7\u6C42\u5361\u7247\uFF0C\u8BF7\u7528\u6237\u5728\u5361\u7247\u4E2D\u70B9\u51FB\u94FE\u63A5\u5B8C\u6210\u6388\u6743\u3002\u6388\u6743\u5B8C\u6210\u540E\u8BF7\u91CD\u65B0\u6267\u884C\u4E4B\u524D\u7684\u64CD\u4F5C\u3002';\n\n if (batchInfo) {\n message += batchInfo;\n }\n\n // \u5982\u679C\u6709\u88AB\u8FC7\u6EE4\u7684 scope\uFF0C\u6DFB\u52A0\u63D0\u793A\u4FE1\u606F\n if (unavailableScopes.length > 0) {\n const permissionUrl = `https://open.feishu.cn/app/${appId}/permission`;\n message += `\\n\\n\u26A0\uFE0F **\u6CE8\u610F**\uFF1A\u4EE5\u4E0B\u6743\u9650\u56E0\u5E94\u7528\u672A\u5F00\u901A\u800C\u88AB\u8DF3\u8FC7\uFF0C\u5982\u9700\u4F7F\u7528\u8BF7\u5148\u5728\u5F00\u653E\u5E73\u53F0\u5F00\u901A\uFF1A\\n${unavailableScopes.map((s) => `- ${s}`).join('\\n')}\\n\\n\u6743\u9650\u7BA1\u7406\u5730\u5740\uFF1A${permissionUrl}`;\n }\n\n return json({\n success: true,\n message,\n awaiting_authorization: true,\n filtered_scopes: unavailableScopes.length > 0 ? unavailableScopes : undefined,\n app_permission_url: unavailableScopes.length > 0 ? `https://open.feishu.cn/app/${appId}/permission` : undefined,\n });\n}\n"],
5
+ "mappings": "AAoBA,SAAS,YAAY;AACrB,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB,8BAA8B;AAChE,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAEpC,SAAS,WAAW,kBAAkB;AACtC,SAAS,kBAAkB;AAE3B,MAAM,MAAM,WAAW,aAAa;AACpC,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AACtC,SAAS,4BAA4B,uBAAuB;AAC5D,SAAS,gBAAgB,gBAAgB,mBAAuC;AAChF,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB,kBAAkB,gCAAgC;AAC7E,SAAS,eAAe,sBAAsB,qBAAqB,qCAAqC;AACxG,SAAS,YAAY;AAMrB,MAAM,oBAAoB,KAAK;AAAA,EAC7B;AAAA,IACE,QAAQ,KAAK;AAAA,MACX;AAAA;AAAA,QAEE,KAAK,QAAQ,QAAQ;AAAA,MACvB;AAAA,MACA;AAAA,QACE,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,aACE;AAAA,EAIJ;AACF;AAqBA,MAAM,eAAe,oBAAI,IAAyB;AAYlD,eAAe,oBACb,OACA,aACA,gBACoD;AACpD,QAAM,SAAS,UAAU,SAAS,+BAA+B;AACjE,QAAM,MAAM,GAAG,MAAM;AAErB,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,SAAS,EAAE,eAAe,UAAU,WAAW,GAAG;AAAA,IACpD,CAAC;AACD,UAAM,OAAQ,MAAM,IAAI,KAAK;AAK7B,QAAI,KAAK,SAAS,GAAG;AACnB,UAAI,KAAK,6BAA6B,KAAK,IAAI,SAAS,KAAK,GAAG,EAAE;AAClE,aAAO,EAAE,OAAO,MAAM;AAAA,IACxB;AACA,UAAM,eAAe,KAAK,MAAM;AAChC,QAAI,CAAC,cAAc;AACjB,UAAI,KAAK,mCAAmC;AAC5C,aAAO,EAAE,OAAO,MAAM;AAAA,IACxB;AACA,WAAO;AAAA,MACL,OAAO,iBAAiB;AAAA,MACxB;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,KAAK,yCAAyC,GAAG,EAAE;AACvD,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AACF;AAMO,SAAS,wBAAwB,KAAwB;AAC9D,MAAI,CAAC,IAAI,OAAQ;AAEjB,QAAM,MAAM,IAAI;AAEhB,MAAI;AAAA,IACF;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aACE;AAAA,MAKF,YAAY;AAAA,MAEZ,MAAM,QAAQ,aAAqB,QAAiB;AAClD,cAAM,IAAI;AAGV,cAAM,SAAS,UAAU;AACzB,cAAM,eAAe,QAAQ;AAC7B,YAAI,CAAC,cAAc;AACjB,iBAAO,KAAK;AAAA,YACV,OAAO;AAAA,UACT,CAAC;AAAA,QACH;AAIA,cAAM,OAAO,eAAe,KAAK,OAAO,SAAS;AACjD,YAAI,CAAC,KAAK,YAAY;AACpB,iBAAO,KAAK;AAAA,YACV,OAAO,gBAAM,OAAO,SAAS;AAAA,UAC/B,CAAC;AAAA,QACH;AACA,cAAM,UAAU;AAEhB,YAAI;AACF,kBAAQ,EAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAoChB,KAAK,UAAU;AACb,oBAAM,UAAU,QAAQ,OAAO,YAAY;AAC3C,qBAAO,KAAK,EAAE,SAAS,MAAM,SAAS,mDAAW,CAAC;AAAA,YACpD;AAAA,YAEA;AAEE,qBAAO,KAAK,EAAE,OAAO,6BAAU,EAAU,MAAM,GAAG,CAAC;AAAA,UACvD;AAAA,QACF,SAAS,KAAK;AACZ,cAAI,MAAM,GAAG,EAAE,MAAM,YAAY,GAAG,EAAE;AACtC,iBAAO,KAAK,EAAE,OAAO,gBAAgB,GAAG,EAAE,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,IACA,EAAE,MAAM,eAAe;AAAA,EACzB;AAEA,MAAI,OAAO,OAAO,4CAA4C;AAChE;AA0BA,eAAsB,iBACpB,QAC+E;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,EAAE,OAAO,WAAW,OAAO,UAAU,IAAI;AAG/C,QAAM,MAAM,WAAW,YAAY,OAAO,EAAE;AAC5C,MAAI;AACF,UAAM,wBAAwB,SAAS,KAAK,YAAY;AAAA,EAC1D,SAAS,KAAK;AACZ,QAAI,eAAe,wBAAwB;AACzC,UAAI,KAAK,kBAAkB,YAAY,yBAAyB;AAChE,aAAO,KAAK;AAAA,QACV,OAAO;AAAA,QACP,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AAGA,MAAI,iBAAiB;AAKrB,QAAM,WAAW,YAAY,OAAO,MAAM,eAAe,OAAO,YAAY;AAC5E,MAAI,YAAY,YAAY,QAAQ,MAAM,WAAW;AAEnD,QAAI,gBAAgB;AAClB,YAAM,kBAAkB,eAAe,MAAM,KAAK,EAAE,OAAO,OAAO;AAClE,YAAM,gBAAgB,IAAI,KAAK,SAAS,SAAS,IAAI,MAAM,KAAK,EAAE,OAAO,OAAO,CAAC;AACjF,YAAM,gBAAgB,gBAAgB,OAAO,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;AAEzE,UAAI,cAAc,SAAS,GAAG;AAE5B,YAAI,KAAK,kCAAkC,cAAc,KAAK,IAAI,CAAC,8BAA8B;AAAA,MAInG,OAAO;AACL,YAAI,gBAAgB;AAClB,cAAI;AACF,kBAAM,eAAe;AAAA,UACvB,SAAS,GAAG;AACV,gBAAI,KAAK,0BAA0B,CAAC,EAAE;AAAA,UACxC;AAAA,QACF;AACA,eAAO,KAAK;AAAA,UACV,SAAS;AAAA,UACT,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,OAAO,SAAS;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL,UAAI,gBAAgB;AAClB,YAAI;AACF,gBAAM,eAAe;AAAA,QACvB,SAAS,GAAG;AACV,cAAI,KAAK,0BAA0B,CAAC,EAAE;AAAA,QACxC;AAAA,MACF;AACA,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,QACT,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,OAAO,SAAU;AAAA,MACnB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,UAAU,GAAG,KAAK,IAAI,YAAY;AACxC,MAAI;AACJ,MAAI,WAAW;AAEf,MAAI,aAAa,IAAI,OAAO,GAAG;AAC7B,UAAM,UAAU,aAAa,IAAI,OAAO;AACxC,UAAM,mBAAmB,QAAQ,aAAa;AAE9C,QAAI,QAAQ,cAAc,kBAAkB;AAE1C,cAAQ,aAAa;AACrB,cAAQ,WAAW,MAAM;AACzB,oBAAc,QAAQ;AACtB,iBAAW,QAAQ;AACnB,mBAAa,OAAO,OAAO;AAG3B,UAAI,QAAQ,OAAO;AACjB,cAAM,YAAY,QAAQ,MAAM,MAAM,KAAK,EAAE,OAAO,OAAO;AAC3D,cAAM,YAAY,gBAAgB,MAAM,KAAK,EAAE,OAAO,OAAO,KAAK,CAAC;AACnE,cAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,WAAW,GAAG,SAAS,CAAC;AACnD,yBAAiB,CAAC,GAAG,MAAM,EAAE,KAAK,GAAG;AACrC,YAAI,KAAK,0BAA0B,CAAC,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,MAC9D;AAEA,UAAI,KAAK,yCAAyC,YAAY,SAAS,KAAK,oBAAoB,WAAW,EAAE;AAAA,IAC/G,OAAO;AAEL,cAAQ,aAAa;AACrB,cAAQ,WAAW,MAAM;AACzB,mBAAa,OAAO,OAAO;AAC3B,UAAI,KAAK,6CAA6C,YAAY,SAAS,KAAK,gBAAgB,QAAQ,MAAM,EAAE;AAEhH,UAAI;AACF,cAAM,yBAAyB;AAAA,UAC7B;AAAA,UACA,QAAQ,QAAQ;AAAA,UAChB,MAAM,oBAAoB,wDAAW;AAAA,UACrC,UAAU,QAAQ,WAAW;AAAA,UAC7B;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,YAAI,KAAK,yCAAyC,CAAC,EAAE;AAAA,MACvD;AAAA,IAEF;AAAA,EACF;AAGA,MAAI,gBAAgB;AACpB,MAAI,oBAA8B,CAAC;AAEnC,MAAI,gBAAgB;AAClB,QAAI;AACF,YAAMA,OAAM,WAAW,YAAY,OAAO,EAAE;AAC5C,YAAM,kBAAkB,eAAe,MAAM,KAAK,EAAE,OAAO,OAAO;AAClE,YAAM,YAAY,MAAM,oBAAoBA,MAAK,OAAO,MAAM;AAE9D,YAAM,kBAAkB,gBAAgB,OAAO,CAAC,MAAM,UAAU,SAAS,CAAC,CAAC;AAC3E,0BAAoB,gBAAgB,OAAO,CAAC,MAAM,CAAC,UAAU,SAAS,CAAC,CAAC;AAExE,UAAI,kBAAkB,SAAS,GAAG;AAChC,YAAI,KAAK,+BAA+B,kBAAkB,KAAK,IAAI,CAAC,uBAAuB;AAE3F,YAAI,gBAAgB,WAAW,GAAG;AAEhC,gBAAM,gBAAgB,8BAA8B,KAAK;AACzD,iBAAO,KAAK;AAAA,YACV,OAAO;AAAA,YACP,SAAS;AAAA,EAAyC,kBAAkB,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,4CAAc,aAAa;AAAA,YAC9H,oBAAoB;AAAA,YACpB,oBAAoB;AAAA,UACtB,CAAC;AAAA,QACH;AAGA,wBAAgB,gBAAgB,KAAK,GAAG;AACxC,YAAI,KAAK,qCAAqC,gBAAgB,KAAK,IAAI,CAAC,GAAG;AAAA,MAC7E;AAAA,IACF,SAAS,KAAK;AAEZ,UAAI,KAAK,kDAAkD,GAAG,EAAE;AAAA,IAClE;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,2BAA2B;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACT,CAAC;AAGD,QAAM,WAAW,cAAc;AAAA,IAC7B,yBAAyB,WAAW;AAAA,IACpC,YAAY,KAAK,MAAM,WAAW,YAAY,EAAE;AAAA,IAChD,OAAO;AAAA;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,kBAAkB,SAAS,IAAI,oBAAoB;AAAA,IACnE;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI;AACJ,MAAI;AACJ,QAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,UAAU,CAAC,QAAQ;AACtB,WAAO,KAAK,EAAE,OAAO,mDAAW,CAAC;AAAA,EACnC;AAEA,MAAI,aAAa;AAEf,UAAM,SAAS,WAAW;AAC1B,QAAI;AACF,YAAM,yBAAyB;AAAA,QAC7B;AAAA,QACA,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,UAAU;AAAA,QACV;AAAA,MACF,CAAC;AACD,UAAI,KAAK,yBAAyB,WAAW,4BAA4B,MAAM,EAAE;AAAA,IACnF,SAAS,KAAK;AACZ,UAAI,KAAK,qDAAqD,GAAG,EAAE;AAEnE,YAAM,YAAY,MAAM,iBAAiB,EAAE,KAAK,MAAM,UAAU,UAAU,CAAC;AAC3E,UAAI,CAAC,UAAW,QAAO,KAAK,EAAE,OAAO,mDAAW,CAAC;AACjD,UAAI,QAAQ;AACV,cAAM,iBAAiB;AAAA,UACrB;AAAA,UACA,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR,kBAAkB,QAAQ,WAAW,WAAW,KAAK,IAAI,OAAO,YAAY;AAAA,UAC5E,eAAe,QAAQ,QAAQ,QAAQ;AAAA,UACvC;AAAA,QACF,CAAC;AAAA,MACH;AACA,eAAS;AACT,YAAM;AACN,oBAAc;AAAA,IAChB;AACA,QAAI,aAAa;AACf,eAAS;AACT,YAAM;AAAA,IACR,OAAO;AACL,eAAS;AACT,YAAM;AAAA,IACR;AAAA,EACF,OAAO;AAEL,UAAM,YAAY,MAAM,iBAAiB,EAAE,KAAK,MAAM,UAAU,UAAU,CAAC;AAC3E,QAAI,CAAC,WAAW;AACd,aAAO,KAAK,EAAE,OAAO,mDAAW,CAAC;AAAA,IACnC;AAEA,UAAM,iBAAiB;AAAA,MACrB;AAAA,MACA,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,kBAAkB,QAAQ,WAAW,WAAW,KAAK,IAAI,OAAO,YAAY;AAAA,MAC5E,eAAe,QAAQ,QAAQ,QAAQ;AAAA,MACvC;AAAA,IACF,CAAC;AAED,aAAS;AACT,UAAM;AAAA,EACR;AAGA,QAAM,kBAAkB,IAAI,gBAAgB;AAE5C,QAAM,cAA2B;AAAA,IAC/B,YAAY;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,IACV,WAAW,QAAQ,aAAa;AAAA,IAChC,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AACA,eAAa,IAAI,SAAS,WAAW;AACrC,MAAI,oBAAoB;AAExB,kBAAgB;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,WAAW;AAAA,IACvB,UAAU,WAAW;AAAA,IACrB,WAAW,WAAW;AAAA,IACtB,QAAQ,gBAAgB;AAAA,EAC1B,CAAC,EACE,KAAK,OAAO,WAAW;AAEtB,QAAI,YAAY,YAAY;AAC1B,UAAI,KAAK,oDAAoD,MAAM,EAAE;AACrE;AAAA,IACF;AACA,QAAI,OAAO,IAAI;AAEb,YAAM,WAAW,MAAM,oBAAoB,OAAO,OAAO,MAAM,aAAa,YAAY;AACxF,UAAI,CAAC,SAAS,OAAO;AACnB,YAAI;AAAA,UACF,+BAA+B,YAAY,YAC/B,SAAS,gBAAgB,SAAS,YAAY,MAAM;AAAA,QAClE;AACA,YAAI;AACF,gBAAM,yBAAyB;AAAA,YAC7B;AAAA,YACA;AAAA,YACA,MAAM,8BAA8B;AAAA,YACpC,UAAU,EAAE;AAAA,YACZ;AAAA,UACF,CAAC;AAAA,QACH,SAAS,GAAG;AACV,cAAI,KAAK,gDAAgD,CAAC,EAAE;AAAA,QAC9D;AACA,qBAAa,OAAO,OAAO;AAC3B,4BAAoB;AACpB;AAAA,MACF;AAIA,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,cAA6B;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,QACA,aAAa,OAAO,MAAM;AAAA,QAC1B,cAAc,OAAO,MAAM;AAAA,QAC3B,WAAW,MAAM,OAAO,MAAM,YAAY;AAAA,QAC1C,kBAAkB,MAAM,OAAO,MAAM,mBAAmB;AAAA,QACxD,OAAO,OAAO,MAAM;AAAA,QACpB,WAAW;AAAA,MACb;AACA,YAAM,eAAe,WAAW;AAIhC,UAAI;AACF,cAAM,yBAAyB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA,MAAM,qBAAqB;AAAA,UAC3B,UAAU,EAAE;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,YAAI,KAAK,qCAAqC,CAAC,EAAE;AAAA,MACnD;AAEA,mBAAa,OAAO,OAAO;AAC3B,0BAAoB;AAMpB,UAAI,gBAAgB;AAClB,YAAI;AACF,gBAAM,eAAe;AAAA,QACvB,SAAS,GAAG;AACV,cAAI,KAAK,0BAA0B,CAAC,EAAE;AAAA,QACxC;AAAA,MACF;AAEA,UAAI,sBAAsB;AACxB,YAAI,KAAK,uDAAuD;AAAA,MAClE;AACE,YAAI;AAIF,gBAAM,iBAAiB,GAAG,OAAO,SAAS;AAE1C,gBAAM,iBAAiB;AAAA,YACrB,QAAQ;AAAA,cACN,WAAW,EAAE,SAAS,aAAa;AAAA,YACrC;AAAA,YACA,SAAS;AAAA,cACP,YAAY;AAAA,cACZ,SAAS;AAAA,cACT,WAAW,OAAO,YAAa;AAAA,cAC/B,cAAc;AAAA,cACd,SAAS,KAAK,UAAU;AAAA,gBACtB,MAAM;AAAA,cACR,CAAC;AAAA,cACD,WAAW,OAAO;AAAA,YACpB;AAAA,UACF;AAIA,gBAAM,mBAAmB;AAAA,YACvB,KAAK,CAAC,QAAgB,IAAI,KAAK,GAAG;AAAA,YAClC,OAAO,CAAC,QAAgB,IAAI,MAAM,GAAG;AAAA,UACvC;AAEA,gBAAM,EAAE,QAAQ,QAAQ,IAAI,sBAAsB;AAAA,YAChD;AAAA,YACA;AAAA,YACA,UAAU,OAAO;AAAA,YACjB,MAAM,YAAY;AAChB,oBAAM;AAAA,gBACJ;AAAA,kBACE,WAAW;AAAA,kBACX;AAAA,kBACA;AAAA,kBACA,WAAW,KAAK,IAAI;AAAA,kBACpB;AAAA,kBACA,UAAU,OAAO;AAAA,kBACjB,UAAU,OAAO;AAAA,gBACnB;AAAA,gBACA,MACE,oBAAoB;AAAA,kBAClB;AAAA;AAAA,kBAEA,OAAO;AAAA,kBACP;AAAA,kBACA,cAAc;AAAA;AAAA,kBAEd,SAAS;AAAA,kBACT,kBAAkB,OAAO;AAAA,gBAC3B,CAAC;AAAA,cACL;AAAA,YACF;AAAA,UACF,CAAC;AAED,cAAI,KAAK,6BAA6B,MAAM,GAAG;AAC/C,gBAAM;AACN,cAAI,KAAK,oDAAoD;AAAA,QAC/D,SAAS,GAAG;AACV,cAAI,KAAK,gDAAgD,CAAC,EAAE;AAAA,QAC9D;AAAA,IACJ,OAAO;AAEL,UAAI;AACF,cAAM,yBAAyB;AAAA,UAC7B;AAAA,UACA;AAAA,UACA,MAAM,oBAAoB,OAAO,OAAO;AAAA,UACxC,UAAU,EAAE;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH,SAAS,GAAG;AACV,YAAI,KAAK,qCAAqC,CAAC,EAAE;AAAA,MACnD;AAEA,mBAAa,OAAO,OAAO;AAC3B,0BAAoB;AAAA,IACtB;AAAA,EACF,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,QAAI,MAAM,kBAAkB,GAAG,EAAE;AAAA,EACnC,CAAC,EACA,QAAQ,MAAM;AACb,QAAI,CAAC,mBAAmB;AAEtB,UAAI,aAAa,IAAI,OAAO,MAAM,aAAa;AAC7C,qBAAa,OAAO,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,CAAC;AAEH,QAAM,aAAa,cAAc,MAAM,KAAK,EAAE,OAAO,OAAO,EAAE;AAC9D,MAAI,UAAU,cACV,oGAAoB,UAAU,0FAC9B;AAEJ,MAAI,WAAW;AACb,eAAW;AAAA,EACb;AAGA,MAAI,kBAAkB,SAAS,GAAG;AAChC,UAAM,gBAAgB,8BAA8B,KAAK;AACzD,eAAW;AAAA;AAAA;AAAA,EAAgD,kBAAkB,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,4CAAc,aAAa;AAAA,EACzI;AAEA,SAAO,KAAK;AAAA,IACV,SAAS;AAAA,IACT;AAAA,IACA,wBAAwB;AAAA,IACxB,iBAAiB,kBAAkB,SAAS,IAAI,oBAAoB;AAAA,IACpE,oBAAoB,kBAAkB,SAAS,IAAI,8BAA8B,KAAK,gBAAgB;AAAA,EACxG,CAAC;AACH;",
6
+ "names": ["sdk"]
7
+ }