@larksuite/openclaw-lark 2026.3.9

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 (689) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +283 -0
  3. package/index.d.ts +37 -0
  4. package/index.d.ts.map +1 -0
  5. package/index.js +119 -0
  6. package/index.js.map +1 -0
  7. package/openclaw.plugin.json +10 -0
  8. package/package.json +47 -0
  9. package/skills/feishu-bitable/SKILL.md +248 -0
  10. package/skills/feishu-bitable/references/examples.md +813 -0
  11. package/skills/feishu-bitable/references/field-properties.md +763 -0
  12. package/skills/feishu-bitable/references/record-values.md +911 -0
  13. package/skills/feishu-calendar/SKILL.md +244 -0
  14. package/skills/feishu-channel-rules/SKILL.md +18 -0
  15. package/skills/feishu-channel-rules/references/markdown-syntax.md +138 -0
  16. package/skills/feishu-create-doc/SKILL.md +719 -0
  17. package/skills/feishu-fetch-doc/SKILL.md +93 -0
  18. package/skills/feishu-im-read/SKILL.md +163 -0
  19. package/skills/feishu-task/SKILL.md +293 -0
  20. package/skills/feishu-troubleshoot/SKILL.md +70 -0
  21. package/skills/feishu-update-doc/SKILL.md +285 -0
  22. package/src/card/builder.d.ts +100 -0
  23. package/src/card/builder.d.ts.map +1 -0
  24. package/src/card/builder.js +381 -0
  25. package/src/card/builder.js.map +1 -0
  26. package/src/card/cardkit.d.ts +91 -0
  27. package/src/card/cardkit.d.ts.map +1 -0
  28. package/src/card/cardkit.js +182 -0
  29. package/src/card/cardkit.js.map +1 -0
  30. package/src/card/flush-controller.d.ts +46 -0
  31. package/src/card/flush-controller.d.ts.map +1 -0
  32. package/src/card/flush-controller.js +135 -0
  33. package/src/card/flush-controller.js.map +1 -0
  34. package/src/card/markdown-style.d.ts +17 -0
  35. package/src/card/markdown-style.d.ts.map +1 -0
  36. package/src/card/markdown-style.js +98 -0
  37. package/src/card/markdown-style.js.map +1 -0
  38. package/src/card/reply-dispatcher-types.d.ts +121 -0
  39. package/src/card/reply-dispatcher-types.d.ts.map +1 -0
  40. package/src/card/reply-dispatcher-types.js +58 -0
  41. package/src/card/reply-dispatcher-types.js.map +1 -0
  42. package/src/card/reply-dispatcher.d.ts +16 -0
  43. package/src/card/reply-dispatcher.d.ts.map +1 -0
  44. package/src/card/reply-dispatcher.js +293 -0
  45. package/src/card/reply-dispatcher.js.map +1 -0
  46. package/src/card/reply-mode.d.ts +39 -0
  47. package/src/card/reply-mode.d.ts.map +1 -0
  48. package/src/card/reply-mode.js +66 -0
  49. package/src/card/reply-mode.js.map +1 -0
  50. package/src/card/streaming-card-controller.d.ts +88 -0
  51. package/src/card/streaming-card-controller.d.ts.map +1 -0
  52. package/src/card/streaming-card-controller.js +717 -0
  53. package/src/card/streaming-card-controller.js.map +1 -0
  54. package/src/card/unavailable-guard.d.ts +36 -0
  55. package/src/card/unavailable-guard.d.ts.map +1 -0
  56. package/src/card/unavailable-guard.js +84 -0
  57. package/src/card/unavailable-guard.js.map +1 -0
  58. package/src/channel/abort-detect.d.ts +35 -0
  59. package/src/channel/abort-detect.d.ts.map +1 -0
  60. package/src/channel/abort-detect.js +125 -0
  61. package/src/channel/abort-detect.js.map +1 -0
  62. package/src/channel/chat-queue.d.ts +42 -0
  63. package/src/channel/chat-queue.d.ts.map +1 -0
  64. package/src/channel/chat-queue.js +59 -0
  65. package/src/channel/chat-queue.js.map +1 -0
  66. package/src/channel/config-adapter.d.ts +24 -0
  67. package/src/channel/config-adapter.d.ts.map +1 -0
  68. package/src/channel/config-adapter.js +102 -0
  69. package/src/channel/config-adapter.js.map +1 -0
  70. package/src/channel/directory.d.ts +58 -0
  71. package/src/channel/directory.d.ts.map +1 -0
  72. package/src/channel/directory.js +192 -0
  73. package/src/channel/directory.js.map +1 -0
  74. package/src/channel/event-handlers.d.ts +16 -0
  75. package/src/channel/event-handlers.d.ts.map +1 -0
  76. package/src/channel/event-handlers.js +222 -0
  77. package/src/channel/event-handlers.js.map +1 -0
  78. package/src/channel/monitor.d.ts +18 -0
  79. package/src/channel/monitor.d.ts.map +1 -0
  80. package/src/channel/monitor.js +128 -0
  81. package/src/channel/monitor.js.map +1 -0
  82. package/src/channel/onboarding-config.d.ts +18 -0
  83. package/src/channel/onboarding-config.d.ts.map +1 -0
  84. package/src/channel/onboarding-config.js +89 -0
  85. package/src/channel/onboarding-config.js.map +1 -0
  86. package/src/channel/onboarding-migrate.d.ts +26 -0
  87. package/src/channel/onboarding-migrate.d.ts.map +1 -0
  88. package/src/channel/onboarding-migrate.js +68 -0
  89. package/src/channel/onboarding-migrate.js.map +1 -0
  90. package/src/channel/onboarding.d.ts +13 -0
  91. package/src/channel/onboarding.d.ts.map +1 -0
  92. package/src/channel/onboarding.js +297 -0
  93. package/src/channel/onboarding.js.map +1 -0
  94. package/src/channel/plugin.d.ts +14 -0
  95. package/src/channel/plugin.d.ts.map +1 -0
  96. package/src/channel/plugin.js +279 -0
  97. package/src/channel/plugin.js.map +1 -0
  98. package/src/channel/probe.d.ts +15 -0
  99. package/src/channel/probe.d.ts.map +1 -0
  100. package/src/channel/probe.js +22 -0
  101. package/src/channel/probe.js.map +1 -0
  102. package/src/channel/types.d.ts +37 -0
  103. package/src/channel/types.d.ts.map +1 -0
  104. package/src/channel/types.js +8 -0
  105. package/src/channel/types.js.map +1 -0
  106. package/src/commands/auth.d.ts +16 -0
  107. package/src/commands/auth.d.ts.map +1 -0
  108. package/src/commands/auth.js +85 -0
  109. package/src/commands/auth.js.map +1 -0
  110. package/src/commands/diagnose.d.ts +70 -0
  111. package/src/commands/diagnose.d.ts.map +1 -0
  112. package/src/commands/diagnose.js +808 -0
  113. package/src/commands/diagnose.js.map +1 -0
  114. package/src/commands/doctor.d.ts +18 -0
  115. package/src/commands/doctor.d.ts.map +1 -0
  116. package/src/commands/doctor.js +439 -0
  117. package/src/commands/doctor.js.map +1 -0
  118. package/src/commands/index.d.ts +9 -0
  119. package/src/commands/index.d.ts.map +1 -0
  120. package/src/commands/index.js +208 -0
  121. package/src/commands/index.js.map +1 -0
  122. package/src/core/accounts.d.ts +38 -0
  123. package/src/core/accounts.d.ts.map +1 -0
  124. package/src/core/accounts.js +179 -0
  125. package/src/core/accounts.js.map +1 -0
  126. package/src/core/agent-config.d.ts +101 -0
  127. package/src/core/agent-config.d.ts.map +1 -0
  128. package/src/core/agent-config.js +140 -0
  129. package/src/core/agent-config.js.map +1 -0
  130. package/src/core/api-error.d.ts +49 -0
  131. package/src/core/api-error.d.ts.map +1 -0
  132. package/src/core/api-error.js +113 -0
  133. package/src/core/api-error.js.map +1 -0
  134. package/src/core/app-owner-fallback.d.ts +22 -0
  135. package/src/core/app-owner-fallback.d.ts.map +1 -0
  136. package/src/core/app-owner-fallback.js +39 -0
  137. package/src/core/app-owner-fallback.js.map +1 -0
  138. package/src/core/app-scope-checker.d.ts +88 -0
  139. package/src/core/app-scope-checker.d.ts.map +1 -0
  140. package/src/core/app-scope-checker.js +191 -0
  141. package/src/core/app-scope-checker.js.map +1 -0
  142. package/src/core/auth-errors.d.ts +143 -0
  143. package/src/core/auth-errors.d.ts.map +1 -0
  144. package/src/core/auth-errors.js +156 -0
  145. package/src/core/auth-errors.js.map +1 -0
  146. package/src/core/chat-info-cache.d.ts +58 -0
  147. package/src/core/chat-info-cache.d.ts.map +1 -0
  148. package/src/core/chat-info-cache.js +153 -0
  149. package/src/core/chat-info-cache.js.map +1 -0
  150. package/src/core/config-schema.d.ts +449 -0
  151. package/src/core/config-schema.d.ts.map +1 -0
  152. package/src/core/config-schema.js +201 -0
  153. package/src/core/config-schema.js.map +1 -0
  154. package/src/core/device-flow.d.ts +78 -0
  155. package/src/core/device-flow.d.ts.map +1 -0
  156. package/src/core/device-flow.js +213 -0
  157. package/src/core/device-flow.js.map +1 -0
  158. package/src/core/feishu-fetch.d.ts +19 -0
  159. package/src/core/feishu-fetch.d.ts.map +1 -0
  160. package/src/core/feishu-fetch.js +26 -0
  161. package/src/core/feishu-fetch.js.map +1 -0
  162. package/src/core/footer-config.d.ts +25 -0
  163. package/src/core/footer-config.d.ts.map +1 -0
  164. package/src/core/footer-config.js +40 -0
  165. package/src/core/footer-config.js.map +1 -0
  166. package/src/core/lark-client.d.ts +109 -0
  167. package/src/core/lark-client.d.ts.map +1 -0
  168. package/src/core/lark-client.js +354 -0
  169. package/src/core/lark-client.js.map +1 -0
  170. package/src/core/lark-logger.d.ts +24 -0
  171. package/src/core/lark-logger.d.ts.map +1 -0
  172. package/src/core/lark-logger.js +155 -0
  173. package/src/core/lark-logger.js.map +1 -0
  174. package/src/core/lark-ticket.d.ts +30 -0
  175. package/src/core/lark-ticket.d.ts.map +1 -0
  176. package/src/core/lark-ticket.js +36 -0
  177. package/src/core/lark-ticket.js.map +1 -0
  178. package/src/core/message-unavailable.d.ts +54 -0
  179. package/src/core/message-unavailable.d.ts.map +1 -0
  180. package/src/core/message-unavailable.js +131 -0
  181. package/src/core/message-unavailable.js.map +1 -0
  182. package/src/core/owner-policy.d.ts +32 -0
  183. package/src/core/owner-policy.d.ts.map +1 -0
  184. package/src/core/owner-policy.js +53 -0
  185. package/src/core/owner-policy.js.map +1 -0
  186. package/src/core/permission-url.d.ts +23 -0
  187. package/src/core/permission-url.d.ts.map +1 -0
  188. package/src/core/permission-url.js +73 -0
  189. package/src/core/permission-url.js.map +1 -0
  190. package/src/core/raw-request.d.ts +28 -0
  191. package/src/core/raw-request.d.ts.map +1 -0
  192. package/src/core/raw-request.js +63 -0
  193. package/src/core/raw-request.js.map +1 -0
  194. package/src/core/scope-manager.d.ts +169 -0
  195. package/src/core/scope-manager.d.ts.map +1 -0
  196. package/src/core/scope-manager.js +214 -0
  197. package/src/core/scope-manager.js.map +1 -0
  198. package/src/core/security-check.d.ts +73 -0
  199. package/src/core/security-check.d.ts.map +1 -0
  200. package/src/core/security-check.js +175 -0
  201. package/src/core/security-check.js.map +1 -0
  202. package/src/core/shutdown-hooks.d.ts +23 -0
  203. package/src/core/shutdown-hooks.d.ts.map +1 -0
  204. package/src/core/shutdown-hooks.js +57 -0
  205. package/src/core/shutdown-hooks.js.map +1 -0
  206. package/src/core/targets.d.ts +50 -0
  207. package/src/core/targets.d.ts.map +1 -0
  208. package/src/core/targets.js +128 -0
  209. package/src/core/targets.js.map +1 -0
  210. package/src/core/token-store.d.ts +55 -0
  211. package/src/core/token-store.d.ts.map +1 -0
  212. package/src/core/token-store.js +315 -0
  213. package/src/core/token-store.js.map +1 -0
  214. package/src/core/tool-client.d.ts +177 -0
  215. package/src/core/tool-client.d.ts.map +1 -0
  216. package/src/core/tool-client.js +381 -0
  217. package/src/core/tool-client.js.map +1 -0
  218. package/src/core/tool-scopes.d.ts +154 -0
  219. package/src/core/tool-scopes.d.ts.map +1 -0
  220. package/src/core/tool-scopes.js +326 -0
  221. package/src/core/tool-scopes.js.map +1 -0
  222. package/src/core/tools-config.d.ts +35 -0
  223. package/src/core/tools-config.d.ts.map +1 -0
  224. package/src/core/tools-config.js +88 -0
  225. package/src/core/tools-config.js.map +1 -0
  226. package/src/core/types.d.ts +88 -0
  227. package/src/core/types.d.ts.map +1 -0
  228. package/src/core/types.js +12 -0
  229. package/src/core/types.js.map +1 -0
  230. package/src/core/uat-client.d.ts +47 -0
  231. package/src/core/uat-client.d.ts.map +1 -0
  232. package/src/core/uat-client.js +173 -0
  233. package/src/core/uat-client.js.map +1 -0
  234. package/src/core/version.d.ts +26 -0
  235. package/src/core/version.d.ts.map +1 -0
  236. package/src/core/version.js +50 -0
  237. package/src/core/version.js.map +1 -0
  238. package/src/messaging/converters/audio.d.ts +9 -0
  239. package/src/messaging/converters/audio.d.ts.map +1 -0
  240. package/src/messaging/converters/audio.js +22 -0
  241. package/src/messaging/converters/audio.js.map +1 -0
  242. package/src/messaging/converters/calendar.d.ts +14 -0
  243. package/src/messaging/converters/calendar.d.ts.map +1 -0
  244. package/src/messaging/converters/calendar.js +51 -0
  245. package/src/messaging/converters/calendar.js.map +1 -0
  246. package/src/messaging/converters/content-converter.d.ts +42 -0
  247. package/src/messaging/converters/content-converter.d.ts.map +1 -0
  248. package/src/messaging/converters/content-converter.js +107 -0
  249. package/src/messaging/converters/content-converter.js.map +1 -0
  250. package/src/messaging/converters/file.d.ts +9 -0
  251. package/src/messaging/converters/file.d.ts.map +1 -0
  252. package/src/messaging/converters/file.js +21 -0
  253. package/src/messaging/converters/file.js.map +1 -0
  254. package/src/messaging/converters/folder.d.ts +9 -0
  255. package/src/messaging/converters/folder.d.ts.map +1 -0
  256. package/src/messaging/converters/folder.js +21 -0
  257. package/src/messaging/converters/folder.js.map +1 -0
  258. package/src/messaging/converters/hongbao.d.ts +9 -0
  259. package/src/messaging/converters/hongbao.d.ts.map +1 -0
  260. package/src/messaging/converters/hongbao.js +17 -0
  261. package/src/messaging/converters/hongbao.js.map +1 -0
  262. package/src/messaging/converters/image.d.ts +9 -0
  263. package/src/messaging/converters/image.d.ts.map +1 -0
  264. package/src/messaging/converters/image.js +19 -0
  265. package/src/messaging/converters/image.js.map +1 -0
  266. package/src/messaging/converters/index.d.ts +9 -0
  267. package/src/messaging/converters/index.d.ts.map +1 -0
  268. package/src/messaging/converters/index.js +51 -0
  269. package/src/messaging/converters/index.js.map +1 -0
  270. package/src/messaging/converters/interactive/card-converter.d.ts +77 -0
  271. package/src/messaging/converters/interactive/card-converter.d.ts.map +1 -0
  272. package/src/messaging/converters/interactive/card-converter.js +1174 -0
  273. package/src/messaging/converters/interactive/card-converter.js.map +1 -0
  274. package/src/messaging/converters/interactive/card-utils.d.ts +10 -0
  275. package/src/messaging/converters/interactive/card-utils.d.ts.map +1 -0
  276. package/src/messaging/converters/interactive/card-utils.js +43 -0
  277. package/src/messaging/converters/interactive/card-utils.js.map +1 -0
  278. package/src/messaging/converters/interactive/index.d.ts +9 -0
  279. package/src/messaging/converters/interactive/index.d.ts.map +1 -0
  280. package/src/messaging/converters/interactive/index.js +22 -0
  281. package/src/messaging/converters/interactive/index.js.map +1 -0
  282. package/src/messaging/converters/interactive/legacy.d.ts +12 -0
  283. package/src/messaging/converters/interactive/legacy.d.ts.map +1 -0
  284. package/src/messaging/converters/interactive/legacy.js +58 -0
  285. package/src/messaging/converters/interactive/legacy.js.map +1 -0
  286. package/src/messaging/converters/interactive/types.d.ts +24 -0
  287. package/src/messaging/converters/interactive/types.d.ts.map +1 -0
  288. package/src/messaging/converters/interactive/types.js +25 -0
  289. package/src/messaging/converters/interactive/types.js.map +1 -0
  290. package/src/messaging/converters/location.d.ts +9 -0
  291. package/src/messaging/converters/location.d.ts.map +1 -0
  292. package/src/messaging/converters/location.js +20 -0
  293. package/src/messaging/converters/location.js.map +1 -0
  294. package/src/messaging/converters/merge-forward.d.ts +33 -0
  295. package/src/messaging/converters/merge-forward.d.ts.map +1 -0
  296. package/src/messaging/converters/merge-forward.js +226 -0
  297. package/src/messaging/converters/merge-forward.js.map +1 -0
  298. package/src/messaging/converters/post.d.ts +12 -0
  299. package/src/messaging/converters/post.d.ts.map +1 -0
  300. package/src/messaging/converters/post.js +136 -0
  301. package/src/messaging/converters/post.js.map +1 -0
  302. package/src/messaging/converters/share.d.ts +10 -0
  303. package/src/messaging/converters/share.d.ts.map +1 -0
  304. package/src/messaging/converters/share.js +24 -0
  305. package/src/messaging/converters/share.js.map +1 -0
  306. package/src/messaging/converters/sticker.d.ts +9 -0
  307. package/src/messaging/converters/sticker.d.ts.map +1 -0
  308. package/src/messaging/converters/sticker.js +19 -0
  309. package/src/messaging/converters/sticker.js.map +1 -0
  310. package/src/messaging/converters/system.d.ts +13 -0
  311. package/src/messaging/converters/system.d.ts.map +1 -0
  312. package/src/messaging/converters/system.js +33 -0
  313. package/src/messaging/converters/system.js.map +1 -0
  314. package/src/messaging/converters/text.d.ts +9 -0
  315. package/src/messaging/converters/text.d.ts.map +1 -0
  316. package/src/messaging/converters/text.js +15 -0
  317. package/src/messaging/converters/text.js.map +1 -0
  318. package/src/messaging/converters/todo.d.ts +9 -0
  319. package/src/messaging/converters/todo.d.ts.map +1 -0
  320. package/src/messaging/converters/todo.js +42 -0
  321. package/src/messaging/converters/todo.js.map +1 -0
  322. package/src/messaging/converters/types.d.ts +108 -0
  323. package/src/messaging/converters/types.d.ts.map +1 -0
  324. package/src/messaging/converters/types.js +8 -0
  325. package/src/messaging/converters/types.js.map +1 -0
  326. package/src/messaging/converters/unknown.d.ts +9 -0
  327. package/src/messaging/converters/unknown.d.ts.map +1 -0
  328. package/src/messaging/converters/unknown.js +17 -0
  329. package/src/messaging/converters/unknown.js.map +1 -0
  330. package/src/messaging/converters/utils.d.ts +23 -0
  331. package/src/messaging/converters/utils.d.ts.map +1 -0
  332. package/src/messaging/converters/utils.js +52 -0
  333. package/src/messaging/converters/utils.js.map +1 -0
  334. package/src/messaging/converters/video-chat.d.ts +9 -0
  335. package/src/messaging/converters/video-chat.d.ts.map +1 -0
  336. package/src/messaging/converters/video-chat.js +24 -0
  337. package/src/messaging/converters/video-chat.js.map +1 -0
  338. package/src/messaging/converters/video.d.ts +9 -0
  339. package/src/messaging/converters/video.d.ts.map +1 -0
  340. package/src/messaging/converters/video.js +33 -0
  341. package/src/messaging/converters/video.js.map +1 -0
  342. package/src/messaging/converters/vote.d.ts +9 -0
  343. package/src/messaging/converters/vote.d.ts.map +1 -0
  344. package/src/messaging/converters/vote.js +25 -0
  345. package/src/messaging/converters/vote.js.map +1 -0
  346. package/src/messaging/inbound/dedup.d.ts +60 -0
  347. package/src/messaging/inbound/dedup.d.ts.map +1 -0
  348. package/src/messaging/inbound/dedup.js +117 -0
  349. package/src/messaging/inbound/dedup.js.map +1 -0
  350. package/src/messaging/inbound/dispatch-builders.d.ts +84 -0
  351. package/src/messaging/inbound/dispatch-builders.d.ts.map +1 -0
  352. package/src/messaging/inbound/dispatch-builders.js +153 -0
  353. package/src/messaging/inbound/dispatch-builders.js.map +1 -0
  354. package/src/messaging/inbound/dispatch-commands.d.ts +28 -0
  355. package/src/messaging/inbound/dispatch-commands.d.ts.map +1 -0
  356. package/src/messaging/inbound/dispatch-commands.js +113 -0
  357. package/src/messaging/inbound/dispatch-commands.js.map +1 -0
  358. package/src/messaging/inbound/dispatch-context.d.ts +68 -0
  359. package/src/messaging/inbound/dispatch-context.d.ts.map +1 -0
  360. package/src/messaging/inbound/dispatch-context.js +137 -0
  361. package/src/messaging/inbound/dispatch-context.js.map +1 -0
  362. package/src/messaging/inbound/dispatch.d.ts +48 -0
  363. package/src/messaging/inbound/dispatch.d.ts.map +1 -0
  364. package/src/messaging/inbound/dispatch.js +193 -0
  365. package/src/messaging/inbound/dispatch.js.map +1 -0
  366. package/src/messaging/inbound/enrich.d.ts +103 -0
  367. package/src/messaging/inbound/enrich.d.ts.map +1 -0
  368. package/src/messaging/inbound/enrich.js +228 -0
  369. package/src/messaging/inbound/enrich.js.map +1 -0
  370. package/src/messaging/inbound/gate-effects.d.ts +24 -0
  371. package/src/messaging/inbound/gate-effects.d.ts.map +1 -0
  372. package/src/messaging/inbound/gate-effects.js +44 -0
  373. package/src/messaging/inbound/gate-effects.js.map +1 -0
  374. package/src/messaging/inbound/gate.d.ts +61 -0
  375. package/src/messaging/inbound/gate.d.ts.map +1 -0
  376. package/src/messaging/inbound/gate.js +234 -0
  377. package/src/messaging/inbound/gate.js.map +1 -0
  378. package/src/messaging/inbound/handler.d.ts +36 -0
  379. package/src/messaging/inbound/handler.d.ts.map +1 -0
  380. package/src/messaging/inbound/handler.js +174 -0
  381. package/src/messaging/inbound/handler.js.map +1 -0
  382. package/src/messaging/inbound/media-resolver.d.ts +33 -0
  383. package/src/messaging/inbound/media-resolver.d.ts.map +1 -0
  384. package/src/messaging/inbound/media-resolver.js +88 -0
  385. package/src/messaging/inbound/media-resolver.js.map +1 -0
  386. package/src/messaging/inbound/mention.d.ts +40 -0
  387. package/src/messaging/inbound/mention.d.ts.map +1 -0
  388. package/src/messaging/inbound/mention.js +82 -0
  389. package/src/messaging/inbound/mention.js.map +1 -0
  390. package/src/messaging/inbound/parse-io.d.ts +51 -0
  391. package/src/messaging/inbound/parse-io.d.ts.map +1 -0
  392. package/src/messaging/inbound/parse-io.js +82 -0
  393. package/src/messaging/inbound/parse-io.js.map +1 -0
  394. package/src/messaging/inbound/parse.d.ts +29 -0
  395. package/src/messaging/inbound/parse.d.ts.map +1 -0
  396. package/src/messaging/inbound/parse.js +107 -0
  397. package/src/messaging/inbound/parse.js.map +1 -0
  398. package/src/messaging/inbound/permission.d.ts +18 -0
  399. package/src/messaging/inbound/permission.d.ts.map +1 -0
  400. package/src/messaging/inbound/permission.js +41 -0
  401. package/src/messaging/inbound/permission.js.map +1 -0
  402. package/src/messaging/inbound/policy.d.ts +95 -0
  403. package/src/messaging/inbound/policy.d.ts.map +1 -0
  404. package/src/messaging/inbound/policy.js +161 -0
  405. package/src/messaging/inbound/policy.js.map +1 -0
  406. package/src/messaging/inbound/reaction-handler.d.ts +62 -0
  407. package/src/messaging/inbound/reaction-handler.d.ts.map +1 -0
  408. package/src/messaging/inbound/reaction-handler.js +221 -0
  409. package/src/messaging/inbound/reaction-handler.js.map +1 -0
  410. package/src/messaging/inbound/user-name-cache.d.ts +83 -0
  411. package/src/messaging/inbound/user-name-cache.d.ts.map +1 -0
  412. package/src/messaging/inbound/user-name-cache.js +242 -0
  413. package/src/messaging/inbound/user-name-cache.js.map +1 -0
  414. package/src/messaging/outbound/actions.d.ts +17 -0
  415. package/src/messaging/outbound/actions.d.ts.map +1 -0
  416. package/src/messaging/outbound/actions.js +310 -0
  417. package/src/messaging/outbound/actions.js.map +1 -0
  418. package/src/messaging/outbound/chat-manage.d.ts +65 -0
  419. package/src/messaging/outbound/chat-manage.d.ts.map +1 -0
  420. package/src/messaging/outbound/chat-manage.js +112 -0
  421. package/src/messaging/outbound/chat-manage.js.map +1 -0
  422. package/src/messaging/outbound/deliver.d.ts +156 -0
  423. package/src/messaging/outbound/deliver.d.ts.map +1 -0
  424. package/src/messaging/outbound/deliver.js +299 -0
  425. package/src/messaging/outbound/deliver.js.map +1 -0
  426. package/src/messaging/outbound/fetch.d.ts +13 -0
  427. package/src/messaging/outbound/fetch.d.ts.map +1 -0
  428. package/src/messaging/outbound/fetch.js +13 -0
  429. package/src/messaging/outbound/fetch.js.map +1 -0
  430. package/src/messaging/outbound/forward.d.ts +27 -0
  431. package/src/messaging/outbound/forward.d.ts.map +1 -0
  432. package/src/messaging/outbound/forward.js +49 -0
  433. package/src/messaging/outbound/forward.js.map +1 -0
  434. package/src/messaging/outbound/media-url-utils.d.ts +30 -0
  435. package/src/messaging/outbound/media-url-utils.d.ts.map +1 -0
  436. package/src/messaging/outbound/media-url-utils.js +131 -0
  437. package/src/messaging/outbound/media-url-utils.js.map +1 -0
  438. package/src/messaging/outbound/media.d.ts +256 -0
  439. package/src/messaging/outbound/media.d.ts.map +1 -0
  440. package/src/messaging/outbound/media.js +749 -0
  441. package/src/messaging/outbound/media.js.map +1 -0
  442. package/src/messaging/outbound/outbound.d.ts +90 -0
  443. package/src/messaging/outbound/outbound.d.ts.map +1 -0
  444. package/src/messaging/outbound/outbound.js +112 -0
  445. package/src/messaging/outbound/outbound.js.map +1 -0
  446. package/src/messaging/outbound/reactions.d.ts +125 -0
  447. package/src/messaging/outbound/reactions.d.ts.map +1 -0
  448. package/src/messaging/outbound/reactions.js +379 -0
  449. package/src/messaging/outbound/reactions.js.map +1 -0
  450. package/src/messaging/outbound/send.d.ts +136 -0
  451. package/src/messaging/outbound/send.d.ts.map +1 -0
  452. package/src/messaging/outbound/send.js +289 -0
  453. package/src/messaging/outbound/send.js.map +1 -0
  454. package/src/messaging/outbound/typing.d.ts +61 -0
  455. package/src/messaging/outbound/typing.d.ts.map +1 -0
  456. package/src/messaging/outbound/typing.js +136 -0
  457. package/src/messaging/outbound/typing.js.map +1 -0
  458. package/src/messaging/shared/message-lookup.d.ts +55 -0
  459. package/src/messaging/shared/message-lookup.d.ts.map +1 -0
  460. package/src/messaging/shared/message-lookup.js +118 -0
  461. package/src/messaging/shared/message-lookup.js.map +1 -0
  462. package/src/messaging/types.d.ts +177 -0
  463. package/src/messaging/types.d.ts.map +1 -0
  464. package/src/messaging/types.js +11 -0
  465. package/src/messaging/types.js.map +1 -0
  466. package/src/tools/auto-auth.d.ts +57 -0
  467. package/src/tools/auto-auth.d.ts.map +1 -0
  468. package/src/tools/auto-auth.js +925 -0
  469. package/src/tools/auto-auth.js.map +1 -0
  470. package/src/tools/helpers.d.ts +220 -0
  471. package/src/tools/helpers.d.ts.map +1 -0
  472. package/src/tools/helpers.js +298 -0
  473. package/src/tools/helpers.js.map +1 -0
  474. package/src/tools/mcp/doc/create.d.ts +13 -0
  475. package/src/tools/mcp/doc/create.d.ts.map +1 -0
  476. package/src/tools/mcp/doc/create.js +45 -0
  477. package/src/tools/mcp/doc/create.js.map +1 -0
  478. package/src/tools/mcp/doc/fetch.d.ts +13 -0
  479. package/src/tools/mcp/doc/fetch.d.ts.map +1 -0
  480. package/src/tools/mcp/doc/fetch.js +37 -0
  481. package/src/tools/mcp/doc/fetch.js.map +1 -0
  482. package/src/tools/mcp/doc/index.d.ts +13 -0
  483. package/src/tools/mcp/doc/index.d.ts.map +1 -0
  484. package/src/tools/mcp/doc/index.js +42 -0
  485. package/src/tools/mcp/doc/index.js.map +1 -0
  486. package/src/tools/mcp/doc/update.d.ts +13 -0
  487. package/src/tools/mcp/doc/update.d.ts.map +1 -0
  488. package/src/tools/mcp/doc/update.js +62 -0
  489. package/src/tools/mcp/doc/update.js.map +1 -0
  490. package/src/tools/mcp/shared.d.ts +58 -0
  491. package/src/tools/mcp/shared.d.ts.map +1 -0
  492. package/src/tools/mcp/shared.js +224 -0
  493. package/src/tools/mcp/shared.js.map +1 -0
  494. package/src/tools/oapi/bitable/app-table-field.d.ts +17 -0
  495. package/src/tools/oapi/bitable/app-table-field.d.ts.map +1 -0
  496. package/src/tools/oapi/bitable/app-table-field.js +224 -0
  497. package/src/tools/oapi/bitable/app-table-field.js.map +1 -0
  498. package/src/tools/oapi/bitable/app-table-record.d.ts +21 -0
  499. package/src/tools/oapi/bitable/app-table-record.d.ts.map +1 -0
  500. package/src/tools/oapi/bitable/app-table-record.js +449 -0
  501. package/src/tools/oapi/bitable/app-table-record.js.map +1 -0
  502. package/src/tools/oapi/bitable/app-table-view.d.ts +18 -0
  503. package/src/tools/oapi/bitable/app-table-view.d.ts.map +1 -0
  504. package/src/tools/oapi/bitable/app-table-view.js +197 -0
  505. package/src/tools/oapi/bitable/app-table-view.js.map +1 -0
  506. package/src/tools/oapi/bitable/app-table.d.ts +20 -0
  507. package/src/tools/oapi/bitable/app-table.d.ts.map +1 -0
  508. package/src/tools/oapi/bitable/app-table.js +249 -0
  509. package/src/tools/oapi/bitable/app-table.js.map +1 -0
  510. package/src/tools/oapi/bitable/app.d.ts +19 -0
  511. package/src/tools/oapi/bitable/app.d.ts.map +1 -0
  512. package/src/tools/oapi/bitable/app.js +188 -0
  513. package/src/tools/oapi/bitable/app.js.map +1 -0
  514. package/src/tools/oapi/bitable/index.d.ts +10 -0
  515. package/src/tools/oapi/bitable/index.d.ts.map +1 -0
  516. package/src/tools/oapi/bitable/index.js +10 -0
  517. package/src/tools/oapi/bitable/index.js.map +1 -0
  518. package/src/tools/oapi/calendar/calendar.d.ts +16 -0
  519. package/src/tools/oapi/calendar/calendar.d.ts.map +1 -0
  520. package/src/tools/oapi/calendar/calendar.js +124 -0
  521. package/src/tools/oapi/calendar/calendar.js.map +1 -0
  522. package/src/tools/oapi/calendar/event-attendee.d.ts +17 -0
  523. package/src/tools/oapi/calendar/event-attendee.d.ts.map +1 -0
  524. package/src/tools/oapi/calendar/event-attendee.js +275 -0
  525. package/src/tools/oapi/calendar/event-attendee.js.map +1 -0
  526. package/src/tools/oapi/calendar/event.d.ts +17 -0
  527. package/src/tools/oapi/calendar/event.d.ts.map +1 -0
  528. package/src/tools/oapi/calendar/event.js +721 -0
  529. package/src/tools/oapi/calendar/event.js.map +1 -0
  530. package/src/tools/oapi/calendar/freebusy.d.ts +14 -0
  531. package/src/tools/oapi/calendar/freebusy.d.ts.map +1 -0
  532. package/src/tools/oapi/calendar/freebusy.js +113 -0
  533. package/src/tools/oapi/calendar/freebusy.js.map +1 -0
  534. package/src/tools/oapi/calendar/index.d.ts +9 -0
  535. package/src/tools/oapi/calendar/index.d.ts.map +1 -0
  536. package/src/tools/oapi/calendar/index.js +9 -0
  537. package/src/tools/oapi/calendar/index.js.map +1 -0
  538. package/src/tools/oapi/chat/chat.d.ts +17 -0
  539. package/src/tools/oapi/chat/chat.d.ts.map +1 -0
  540. package/src/tools/oapi/chat/chat.js +126 -0
  541. package/src/tools/oapi/chat/chat.js.map +1 -0
  542. package/src/tools/oapi/chat/index.d.ts +11 -0
  543. package/src/tools/oapi/chat/index.d.ts.map +1 -0
  544. package/src/tools/oapi/chat/index.js +16 -0
  545. package/src/tools/oapi/chat/index.js.map +1 -0
  546. package/src/tools/oapi/chat/members.d.ts +12 -0
  547. package/src/tools/oapi/chat/members.d.ts.map +1 -0
  548. package/src/tools/oapi/chat/members.js +83 -0
  549. package/src/tools/oapi/chat/members.js.map +1 -0
  550. package/src/tools/oapi/common/get-user.d.ts +13 -0
  551. package/src/tools/oapi/common/get-user.d.ts.map +1 -0
  552. package/src/tools/oapi/common/get-user.js +108 -0
  553. package/src/tools/oapi/common/get-user.js.map +1 -0
  554. package/src/tools/oapi/common/index.d.ts +7 -0
  555. package/src/tools/oapi/common/index.d.ts.map +1 -0
  556. package/src/tools/oapi/common/index.js +7 -0
  557. package/src/tools/oapi/common/index.js.map +1 -0
  558. package/src/tools/oapi/common/search-user.d.ts +12 -0
  559. package/src/tools/oapi/common/search-user.d.ts.map +1 -0
  560. package/src/tools/oapi/common/search-user.js +75 -0
  561. package/src/tools/oapi/common/search-user.js.map +1 -0
  562. package/src/tools/oapi/drive/doc-comments.d.ts +16 -0
  563. package/src/tools/oapi/drive/doc-comments.d.ts.map +1 -0
  564. package/src/tools/oapi/drive/doc-comments.js +288 -0
  565. package/src/tools/oapi/drive/doc-comments.js.map +1 -0
  566. package/src/tools/oapi/drive/doc-media.d.ts +20 -0
  567. package/src/tools/oapi/drive/doc-media.d.ts.map +1 -0
  568. package/src/tools/oapi/drive/doc-media.js +337 -0
  569. package/src/tools/oapi/drive/doc-media.js.map +1 -0
  570. package/src/tools/oapi/drive/file.d.ts +20 -0
  571. package/src/tools/oapi/drive/file.d.ts.map +1 -0
  572. package/src/tools/oapi/drive/file.js +485 -0
  573. package/src/tools/oapi/drive/file.js.map +1 -0
  574. package/src/tools/oapi/drive/index.d.ts +13 -0
  575. package/src/tools/oapi/drive/index.d.ts.map +1 -0
  576. package/src/tools/oapi/drive/index.js +37 -0
  577. package/src/tools/oapi/drive/index.js.map +1 -0
  578. package/src/tools/oapi/helpers.d.ts +174 -0
  579. package/src/tools/oapi/helpers.d.ts.map +1 -0
  580. package/src/tools/oapi/helpers.js +341 -0
  581. package/src/tools/oapi/helpers.js.map +1 -0
  582. package/src/tools/oapi/im/format-messages.d.ts +51 -0
  583. package/src/tools/oapi/im/format-messages.d.ts.map +1 -0
  584. package/src/tools/oapi/im/format-messages.js +166 -0
  585. package/src/tools/oapi/im/format-messages.js.map +1 -0
  586. package/src/tools/oapi/im/index.d.ts +11 -0
  587. package/src/tools/oapi/im/index.d.ts.map +1 -0
  588. package/src/tools/oapi/im/index.js +18 -0
  589. package/src/tools/oapi/im/index.js.map +1 -0
  590. package/src/tools/oapi/im/message-read.d.ts +14 -0
  591. package/src/tools/oapi/im/message-read.d.ts.map +1 -0
  592. package/src/tools/oapi/im/message-read.js +412 -0
  593. package/src/tools/oapi/im/message-read.js.map +1 -0
  594. package/src/tools/oapi/im/message.d.ts +17 -0
  595. package/src/tools/oapi/im/message.d.ts.map +1 -0
  596. package/src/tools/oapi/im/message.js +171 -0
  597. package/src/tools/oapi/im/message.js.map +1 -0
  598. package/src/tools/oapi/im/resource.d.ts +14 -0
  599. package/src/tools/oapi/im/resource.d.ts.map +1 -0
  600. package/src/tools/oapi/im/resource.js +152 -0
  601. package/src/tools/oapi/im/resource.js.map +1 -0
  602. package/src/tools/oapi/im/time-utils.d.ts +47 -0
  603. package/src/tools/oapi/im/time-utils.d.ts.map +1 -0
  604. package/src/tools/oapi/im/time-utils.js +202 -0
  605. package/src/tools/oapi/im/time-utils.js.map +1 -0
  606. package/src/tools/oapi/im/user-name-uat.d.ts +24 -0
  607. package/src/tools/oapi/im/user-name-uat.d.ts.map +1 -0
  608. package/src/tools/oapi/im/user-name-uat.js +128 -0
  609. package/src/tools/oapi/im/user-name-uat.js.map +1 -0
  610. package/src/tools/oapi/index.d.ts +12 -0
  611. package/src/tools/oapi/index.d.ts.map +1 -0
  612. package/src/tools/oapi/index.js +59 -0
  613. package/src/tools/oapi/index.js.map +1 -0
  614. package/src/tools/oapi/sdk-types.d.ts +97 -0
  615. package/src/tools/oapi/sdk-types.d.ts.map +1 -0
  616. package/src/tools/oapi/sdk-types.js +13 -0
  617. package/src/tools/oapi/sdk-types.js.map +1 -0
  618. package/src/tools/oapi/search/doc-search.d.ts +14 -0
  619. package/src/tools/oapi/search/doc-search.d.ts.map +1 -0
  620. package/src/tools/oapi/search/doc-search.js +203 -0
  621. package/src/tools/oapi/search/doc-search.js.map +1 -0
  622. package/src/tools/oapi/search/index.d.ts +13 -0
  623. package/src/tools/oapi/search/index.d.ts.map +1 -0
  624. package/src/tools/oapi/search/index.js +34 -0
  625. package/src/tools/oapi/search/index.js.map +1 -0
  626. package/src/tools/oapi/sheets/index.d.ts +13 -0
  627. package/src/tools/oapi/sheets/index.d.ts.map +1 -0
  628. package/src/tools/oapi/sheets/index.js +32 -0
  629. package/src/tools/oapi/sheets/index.js.map +1 -0
  630. package/src/tools/oapi/sheets/sheet.d.ts +17 -0
  631. package/src/tools/oapi/sheets/sheet.d.ts.map +1 -0
  632. package/src/tools/oapi/sheets/sheet.js +652 -0
  633. package/src/tools/oapi/sheets/sheet.js.map +1 -0
  634. package/src/tools/oapi/task/comment.d.ts +16 -0
  635. package/src/tools/oapi/task/comment.d.ts.map +1 -0
  636. package/src/tools/oapi/task/comment.js +142 -0
  637. package/src/tools/oapi/task/comment.js.map +1 -0
  638. package/src/tools/oapi/task/index.d.ts +9 -0
  639. package/src/tools/oapi/task/index.d.ts.map +1 -0
  640. package/src/tools/oapi/task/index.js +9 -0
  641. package/src/tools/oapi/task/index.js.map +1 -0
  642. package/src/tools/oapi/task/subtask.d.ts +15 -0
  643. package/src/tools/oapi/task/subtask.d.ts.map +1 -0
  644. package/src/tools/oapi/task/subtask.js +164 -0
  645. package/src/tools/oapi/task/subtask.js.map +1 -0
  646. package/src/tools/oapi/task/task.d.ts +17 -0
  647. package/src/tools/oapi/task/task.d.ts.map +1 -0
  648. package/src/tools/oapi/task/task.js +346 -0
  649. package/src/tools/oapi/task/task.js.map +1 -0
  650. package/src/tools/oapi/task/tasklist.d.ts +22 -0
  651. package/src/tools/oapi/task/tasklist.d.ts.map +1 -0
  652. package/src/tools/oapi/task/tasklist.js +323 -0
  653. package/src/tools/oapi/task/tasklist.js.map +1 -0
  654. package/src/tools/oapi/wiki/index.d.ts +13 -0
  655. package/src/tools/oapi/wiki/index.d.ts.map +1 -0
  656. package/src/tools/oapi/wiki/index.js +35 -0
  657. package/src/tools/oapi/wiki/index.js.map +1 -0
  658. package/src/tools/oapi/wiki/space-node.d.ts +18 -0
  659. package/src/tools/oapi/wiki/space-node.d.ts.map +1 -0
  660. package/src/tools/oapi/wiki/space-node.js +253 -0
  661. package/src/tools/oapi/wiki/space-node.js.map +1 -0
  662. package/src/tools/oapi/wiki/space.d.ts +16 -0
  663. package/src/tools/oapi/wiki/space.d.ts.map +1 -0
  664. package/src/tools/oapi/wiki/space.js +132 -0
  665. package/src/tools/oapi/wiki/space.js.map +1 -0
  666. package/src/tools/oauth-batch-auth.d.ts +12 -0
  667. package/src/tools/oauth-batch-auth.d.ts.map +1 -0
  668. package/src/tools/oauth-batch-auth.js +142 -0
  669. package/src/tools/oauth-batch-auth.js.map +1 -0
  670. package/src/tools/oauth-cards.d.ts +27 -0
  671. package/src/tools/oauth-cards.d.ts.map +1 -0
  672. package/src/tools/oauth-cards.js +251 -0
  673. package/src/tools/oauth-cards.js.map +1 -0
  674. package/src/tools/oauth.d.ts +48 -0
  675. package/src/tools/oauth.d.ts.map +1 -0
  676. package/src/tools/oauth.js +619 -0
  677. package/src/tools/oauth.js.map +1 -0
  678. package/src/tools/onboarding-auth.d.ts +28 -0
  679. package/src/tools/onboarding-auth.d.ts.map +1 -0
  680. package/src/tools/onboarding-auth.js +131 -0
  681. package/src/tools/onboarding-auth.js.map +1 -0
  682. package/src/tools/tat/im/index.d.ts +16 -0
  683. package/src/tools/tat/im/index.d.ts.map +1 -0
  684. package/src/tools/tat/im/index.js +19 -0
  685. package/src/tools/tat/im/index.js.map +1 -0
  686. package/src/tools/tat/im/resource.d.ts +16 -0
  687. package/src/tools/tat/im/resource.d.ts.map +1 -0
  688. package/src/tools/tat/im/resource.js +158 -0
  689. package/src/tools/tat/im/resource.js.map +1 -0
@@ -0,0 +1,925 @@
1
+ /**
2
+ * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * auto-auth.ts — 工具层自动授权处理。
6
+ *
7
+ * 当 OAPI 工具遇到授权问题时,直接在工具层处理,不再让 AI 判断:
8
+ *
9
+ * - UserAuthRequiredError (appScopeVerified=true)
10
+ * → 直接调用 executeAuthorize 发起 OAuth Device Flow 卡片
11
+ *
12
+ * - UserScopeInsufficientError
13
+ * → 直接调用 executeAuthorize(使用 missingScopes)
14
+ *
15
+ * - AppScopeMissingError
16
+ * → 发送应用权限引导卡片;用户点击"我已完成"后:
17
+ * 1. 更新卡片为处理中状态
18
+ * 2. invalidateAppScopeCache
19
+ * 3. 发送中间合成消息告知 AI("应用权限已确认,正在发起用户授权...")
20
+ * 4. 调用 executeAuthorize 发起 OAuth Device Flow
21
+ *
22
+ * - 其他情况(AppScopeCheckFailedError、appScopeVerified=false 等)
23
+ * → 回退到原 handleInvokeError(不触发自动授权)
24
+ *
25
+ * 降级策略(保守):以下情况均回退到 handleInvokeError:
26
+ * - 无 LarkTicket(非消息场景)
27
+ * - 无 senderOpenId(无法确定授权对象)
28
+ * - 账号未配置(!acct.configured)
29
+ * - 任何步骤抛出异常
30
+ */
31
+ import { getTicket } from '../core/lark-ticket';
32
+ import { larkLogger } from '../core/lark-logger';
33
+ const log = larkLogger('tools/auto-auth');
34
+ import { getLarkAccount } from '../core/accounts';
35
+ import { UserAuthRequiredError, UserScopeInsufficientError, AppScopeMissingError } from '../core/tool-client';
36
+ import { invalidateAppScopeCache, getAppGrantedScopes, isAppScopeSatisfied } from '../core/app-scope-checker';
37
+ import { LarkClient } from '../core/lark-client';
38
+ import { createCardEntity, sendCardByCardId, updateCardKitCardForAuth } from '../card/cardkit';
39
+ import { executeAuthorize } from './oauth';
40
+ import { formatLarkError, json } from './oapi/helpers';
41
+ import { OwnerAccessDeniedError } from '../core/owner-policy';
42
+ import { enqueueFeishuChatTask } from '../channel/chat-queue';
43
+ import { handleFeishuMessage } from '../messaging/inbound/handler';
44
+ import { withTicket } from '../core/lark-ticket';
45
+ /**
46
+ * 防抖缓冲区 Map。
47
+ *
48
+ * Key 规则:
49
+ * 用户授权:`user:${accountId}:${senderOpenId}:${messageId}`
50
+ * 应用授权:`app:${accountId}:${chatId}:${messageId}`
51
+ */
52
+ const authBatches = new Map();
53
+ /** 防抖窗口(毫秒) */
54
+ const AUTH_DEBOUNCE_MS = 50;
55
+ /** 用户授权防抖窗口(毫秒)。比 app auth 的 50ms 更长,保证应用权限卡片先发出。 */
56
+ const AUTH_USER_DEBOUNCE_MS = 150;
57
+ /**
58
+ * Scope 更新防抖窗口(毫秒)。
59
+ * 比初始防抖更长,因为工具调用可能间隔数十到数百毫秒顺序到达。
60
+ * 需要等足够久以收集所有后续到达的 scope 后再一次性更新卡片。
61
+ */
62
+ const AUTH_UPDATE_DEBOUNCE_MS = 500;
63
+ /**
64
+ * 冷却期(毫秒)。
65
+ * flushFn 执行完毕后,entry 继续保留在 Map 中这么长时间,
66
+ * 防止后续顺序到达的工具调用创建重复卡片。
67
+ */
68
+ const AUTH_COOLDOWN_MS = 30_000;
69
+ /**
70
+ * 将授权请求入队到防抖缓冲区。
71
+ *
72
+ * 同一 bufferKey 的请求会被合并:
73
+ * - collecting 阶段:scope 集合取并集,共享同一个 flushFn 执行结果
74
+ * - executing 阶段:flushFn 已在运行,后续请求直接复用已有结果(不重复发卡片)
75
+ *
76
+ * @param bufferKey - 缓冲区 key(区分不同用户/会话)
77
+ * @param scopes - 本次请求需要的 scope 列表
78
+ * @param ctx - 上下文信息(仅第一个请求的被采用)
79
+ * @param flushFn - 定时器到期后执行的实际授权函数,接收合并后的 scope 数组
80
+ */
81
+ function enqueueAuthRequest(bufferKey, scopes, ctx, flushFn, debounceMs = AUTH_DEBOUNCE_MS) {
82
+ const existing = authBatches.get(bufferKey);
83
+ if (existing) {
84
+ // 不论哪个阶段,都追加 scope
85
+ for (const s of scopes)
86
+ existing.scopes.add(s);
87
+ if (existing.phase === 'executing') {
88
+ // flushFn 已在执行或已完成(卡片已发出),复用结果
89
+ // 同时触发延迟刷新:用合并后的 scope 重新调用 flushFn 更新卡片
90
+ log.info(`auth in-flight, piggyback → key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);
91
+ // 防抖 + 互斥:多个快速到达的请求只触发一次卡片更新
92
+ if (existing.updateTimer)
93
+ clearTimeout(existing.updateTimer);
94
+ existing.updateTimer = setTimeout(async () => {
95
+ existing.updateTimer = null;
96
+ // 互斥:如果上一轮更新还在执行,标记 pendingReupdate 等它结束后重跑
97
+ if (existing.isUpdating) {
98
+ existing.pendingReupdate = true;
99
+ log.info(`scope update deferred (previous update still running) → key=${bufferKey}`);
100
+ return;
101
+ }
102
+ existing.isUpdating = true;
103
+ try {
104
+ const mergedScopes = [...existing.scopes];
105
+ log.info(`scope update flush → key=${bufferKey}, scopes=[${mergedScopes.join(', ')}]`);
106
+ // 重新调用 flushFn(executeAuthorize 会检测到 pendingFlow,
107
+ // 原地更新旧卡片内容 + 重启 Device Flow)
108
+ await existing.flushFn(mergedScopes);
109
+ }
110
+ catch (err) {
111
+ log.warn(`scope update failed: ${err}`);
112
+ }
113
+ finally {
114
+ existing.isUpdating = false;
115
+ // 如果锁定期间有新 scope 到达,再跑一轮
116
+ if (existing.pendingReupdate) {
117
+ existing.pendingReupdate = false;
118
+ const finalScopes = [...existing.scopes];
119
+ log.info(`scope reupdate → key=${bufferKey}, scopes=[${finalScopes.join(', ')}]`);
120
+ try {
121
+ await existing.flushFn(finalScopes);
122
+ }
123
+ catch (err) {
124
+ log.warn(`scope reupdate failed: ${err}`);
125
+ }
126
+ }
127
+ }
128
+ }, AUTH_UPDATE_DEBOUNCE_MS);
129
+ return existing.resultPromise;
130
+ }
131
+ // collecting 阶段:正常合并
132
+ log.info(`debounce merge → key=${bufferKey}, scopes=[${[...existing.scopes].join(', ')}]`);
133
+ return new Promise((resolve, reject) => {
134
+ existing.waiters.push({ resolve, reject });
135
+ });
136
+ }
137
+ // 创建新缓冲区(collecting 阶段)
138
+ const entry = {
139
+ phase: 'collecting',
140
+ scopes: new Set(scopes),
141
+ waiters: [],
142
+ timer: null,
143
+ resultPromise: null,
144
+ updateTimer: null,
145
+ isUpdating: false,
146
+ pendingReupdate: false,
147
+ flushFn: null,
148
+ account: ctx.account,
149
+ cfg: ctx.cfg,
150
+ ticket: ctx.ticket,
151
+ };
152
+ const promise = new Promise((resolve, reject) => {
153
+ entry.waiters.push({ resolve, reject });
154
+ });
155
+ entry.timer = setTimeout(async () => {
156
+ // 转入 executing 阶段(不从 Map 中删除,阻止后续请求创建新卡片)
157
+ entry.phase = 'executing';
158
+ entry.timer = null;
159
+ entry.flushFn = flushFn; // 保存引用,供 executing 阶段 scope 更新时重新调用
160
+ const mergedScopes = [...entry.scopes];
161
+ log.info(`debounce flush → key=${bufferKey}, ` + `waiters=${entry.waiters.length}, scopes=[${mergedScopes.join(', ')}]`);
162
+ // 将 flushFn 的 Promise 存入 entry,供 executing 阶段的后来者复用
163
+ entry.resultPromise = flushFn(mergedScopes);
164
+ try {
165
+ const result = await entry.resultPromise;
166
+ for (const w of entry.waiters)
167
+ w.resolve(result);
168
+ }
169
+ catch (err) {
170
+ for (const w of entry.waiters)
171
+ w.reject(err);
172
+ }
173
+ finally {
174
+ // 进入冷却期:entry 继续留在 Map 中,后续到达的工具调用
175
+ // 会命中 executing 分支并复用 resultPromise,不会创建新卡片。
176
+ // 冷却期结束后清理。
177
+ setTimeout(() => authBatches.delete(bufferKey), AUTH_COOLDOWN_MS);
178
+ }
179
+ }, debounceMs);
180
+ authBatches.set(bufferKey, entry);
181
+ return promise;
182
+ }
183
+ /** TTL:15 分钟后自动清理,防止内存泄漏。 */
184
+ const PENDING_FLOW_TTL_MS = 15 * 60 * 1000;
185
+ /** 计算去重 key(chatId + messageId + 有序 scopes)。 */
186
+ function makeDedupKey(chatId, messageId, scopes) {
187
+ return chatId + '\0' + messageId + '\0' + [...scopes].sort().join(',');
188
+ }
189
+ /**
190
+ * 应用权限授权流管理器 — 统一管理三个关联索引的一致性。
191
+ *
192
+ * 替代原来散布的 pendingAppAuthFlows / dedupIndex / activeAppCardIndex 三个 Map,
193
+ * 确保注册、删除、迁移操作的原子性。
194
+ */
195
+ class AppAuthFlowManager {
196
+ flows = new Map();
197
+ dedupIndex = new Map();
198
+ activeCardIndex = new Map();
199
+ /** 原子注册新流程(同时写入 3 个索引 + 设置统一 TTL) */
200
+ register(operationId, flow, dedupKey, activeCardKey) {
201
+ const registered = { ...flow, dedupKey, activeCardKey };
202
+ this.flows.set(operationId, registered);
203
+ this.dedupIndex.set(dedupKey, operationId);
204
+ this.activeCardIndex.set(activeCardKey, operationId);
205
+ // 统一 TTL 清理
206
+ setTimeout(() => {
207
+ if (!this.flows.has(operationId))
208
+ return; // 已被手动清理,跳过
209
+ this.remove(operationId);
210
+ }, PENDING_FLOW_TTL_MS);
211
+ }
212
+ /** 只需 operationId 即可原子清理所有索引 */
213
+ remove(operationId) {
214
+ const flow = this.flows.get(operationId);
215
+ if (!flow)
216
+ return;
217
+ // 联动清理延迟用户授权队列(防止内存泄漏)
218
+ if (flow.ticket?.senderOpenId) {
219
+ const deferKey = `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;
220
+ deferredUserAuth.delete(deferKey);
221
+ }
222
+ this.flows.delete(operationId);
223
+ // 条件删除:防止误删已被新 flow 覆盖的索引
224
+ if (this.dedupIndex.get(flow.dedupKey) === operationId) {
225
+ this.dedupIndex.delete(flow.dedupKey);
226
+ }
227
+ if (this.activeCardIndex.get(flow.activeCardKey) === operationId) {
228
+ this.activeCardIndex.delete(flow.activeCardKey);
229
+ }
230
+ }
231
+ /**
232
+ * 迁移到新 operationId(卡片复用场景:按钮回调需要匹配新 ID)。
233
+ * 原子操作:清理旧索引 → 更新 flow → 建立新索引 → 注册新 TTL。
234
+ *
235
+ * 修复原代码卡片复用路径缺少 TTL 注册导致的内存泄漏。
236
+ */
237
+ migrateToNewOperationId(oldOperationId, newOperationId, updates) {
238
+ const flow = this.flows.get(oldOperationId);
239
+ if (!flow)
240
+ return undefined;
241
+ // 清理旧索引
242
+ this.flows.delete(oldOperationId);
243
+ if (updates?.dedupKey) {
244
+ if (this.dedupIndex.get(flow.dedupKey) === oldOperationId) {
245
+ this.dedupIndex.delete(flow.dedupKey);
246
+ }
247
+ flow.dedupKey = updates.dedupKey;
248
+ }
249
+ if (updates?.requiredScopes)
250
+ flow.requiredScopes = updates.requiredScopes;
251
+ if (updates?.scopeNeedType)
252
+ flow.scopeNeedType = updates.scopeNeedType;
253
+ // 建立新索引
254
+ this.flows.set(newOperationId, flow);
255
+ this.dedupIndex.set(flow.dedupKey, newOperationId);
256
+ this.activeCardIndex.set(flow.activeCardKey, newOperationId);
257
+ // 为新 operationId 注册 TTL(修复原代码的内存泄漏)
258
+ setTimeout(() => {
259
+ if (!this.flows.has(newOperationId))
260
+ return;
261
+ this.remove(newOperationId);
262
+ }, PENDING_FLOW_TTL_MS);
263
+ return flow;
264
+ }
265
+ /** 通过 operationId 查询(card action 回调用) */
266
+ getByOperationId(id) {
267
+ return this.flows.get(id);
268
+ }
269
+ /** 通过去重键查询(避免发送重复卡片) */
270
+ getByDedupKey(key) {
271
+ const opId = this.dedupIndex.get(key);
272
+ if (!opId)
273
+ return undefined;
274
+ const flow = this.flows.get(opId);
275
+ return flow ? { operationId: opId, flow } : undefined;
276
+ }
277
+ /** 通过活跃卡片键查询(同消息卡片复用) */
278
+ getByActiveCardKey(key) {
279
+ const opId = this.activeCardIndex.get(key);
280
+ if (!opId)
281
+ return undefined;
282
+ const flow = this.flows.get(opId);
283
+ return flow ? { operationId: opId, flow } : undefined;
284
+ }
285
+ }
286
+ const appAuthFlows = new AppAuthFlowManager();
287
+ /** 延迟用户授权队列。Key: `${accountId}:${senderOpenId}:${messageId}` */
288
+ const deferredUserAuth = new Map();
289
+ /**
290
+ * 检查指定消息上下文是否有未完成的应用权限授权流程。
291
+ * 检查两个来源:
292
+ * 1. authBatches 中的 app auth entry(collecting/executing 阶段)
293
+ * 2. appAuthFlows 中的活跃流(卡片已发送,等待用户点击"已完成")
294
+ */
295
+ function hasActiveAppAuthForMessage(ticket) {
296
+ const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
297
+ const appEntry = authBatches.get(appKey);
298
+ if (appEntry && (appEntry.phase === 'collecting' || appEntry.phase === 'executing')) {
299
+ return true;
300
+ }
301
+ const activeCardKey = `${ticket.chatId}:${ticket.messageId}`;
302
+ return !!appAuthFlows.getByActiveCardKey(activeCardKey);
303
+ }
304
+ /**
305
+ * 将用户授权 scope 添加到延迟队列。
306
+ * 多个工具调用的 scope 会被合并到同一个 entry。
307
+ */
308
+ function addToDeferredUserAuth(ticket, scopes, account, cfg) {
309
+ const key = `${ticket.accountId}:${ticket.senderOpenId}:${ticket.messageId}`;
310
+ const existing = deferredUserAuth.get(key);
311
+ if (existing) {
312
+ for (const s of scopes)
313
+ existing.scopes.add(s);
314
+ log.info(`deferred user auth scope merge → key=${key}, scopes=[${[...existing.scopes].join(', ')}]`);
315
+ }
316
+ else {
317
+ deferredUserAuth.set(key, { scopes: new Set(scopes), account, cfg, ticket });
318
+ log.info(`deferred user auth created → key=${key}, scopes=[${scopes.join(', ')}]`);
319
+ }
320
+ }
321
+ // ---------------------------------------------------------------------------
322
+ // Card builders — CardKit v2 格式
323
+ // ---------------------------------------------------------------------------
324
+ /**
325
+ * 构建应用权限引导卡片。
326
+ *
327
+ * 蓝色 header,列出缺失的 scope,提供权限管理链接和"我已完成,继续授权"按钮。
328
+ */
329
+ function buildAppScopeMissingCard(params) {
330
+ const { missingScopes, appId, operationId } = params;
331
+ const authUrl = appId
332
+ ? `https://open.feishu.cn/app/${appId}/auth?q=${encodeURIComponent(missingScopes.join(','))}&op_from=feishu-openclaw&token_type=user`
333
+ : 'https://open.feishu.cn/';
334
+ const multiUrl = { url: authUrl, pc_url: authUrl, android_url: authUrl, ios_url: authUrl };
335
+ const scopeList = missingScopes.map((s) => `• ${s}`).join('\n');
336
+ return {
337
+ schema: '2.0',
338
+ config: { wide_screen_mode: true },
339
+ header: {
340
+ title: { tag: 'plain_text', content: '🔐 需要申请权限才能继续' },
341
+ template: 'orange',
342
+ },
343
+ body: {
344
+ elements: [
345
+ {
346
+ tag: 'markdown',
347
+ content: '调用前,请你先申请以下**所有**权限:',
348
+ text_size: 'normal',
349
+ },
350
+ {
351
+ tag: 'column_set',
352
+ flex_mode: 'none',
353
+ background_style: 'grey',
354
+ horizontal_spacing: 'default',
355
+ columns: [
356
+ {
357
+ tag: 'column',
358
+ width: 'weighted',
359
+ weight: 1,
360
+ vertical_align: 'center',
361
+ elements: [{ tag: 'markdown', content: scopeList }],
362
+ },
363
+ ],
364
+ },
365
+ { tag: 'hr' },
366
+ {
367
+ tag: 'column_set',
368
+ flex_mode: 'none',
369
+ horizontal_spacing: 'default',
370
+ columns: [
371
+ {
372
+ tag: 'column',
373
+ width: 'weighted',
374
+ weight: 3,
375
+ vertical_align: 'center',
376
+ elements: [{ tag: 'markdown', content: '**第一步:申请所有权限**' }],
377
+ },
378
+ {
379
+ tag: 'column',
380
+ width: 'weighted',
381
+ weight: 1,
382
+ vertical_align: 'center',
383
+ elements: [
384
+ {
385
+ tag: 'button',
386
+ text: { tag: 'plain_text', content: '去申请' },
387
+ type: 'primary',
388
+ multi_url: multiUrl,
389
+ },
390
+ ],
391
+ },
392
+ ],
393
+ },
394
+ {
395
+ tag: 'column_set',
396
+ flex_mode: 'none',
397
+ horizontal_spacing: 'default',
398
+ columns: [
399
+ {
400
+ tag: 'column',
401
+ width: 'weighted',
402
+ weight: 3,
403
+ vertical_align: 'center',
404
+ elements: [{ tag: 'markdown', content: '**第二步:创建版本并审核通过**' }],
405
+ },
406
+ {
407
+ tag: 'column',
408
+ width: 'weighted',
409
+ weight: 1,
410
+ vertical_align: 'center',
411
+ elements: [
412
+ {
413
+ tag: 'button',
414
+ text: { tag: 'plain_text', content: '已完成' },
415
+ type: 'default',
416
+ value: { action: 'app_auth_done', operation_id: operationId },
417
+ },
418
+ ],
419
+ },
420
+ ],
421
+ },
422
+ ],
423
+ },
424
+ };
425
+ }
426
+ /**
427
+ * 构建应用权限引导卡片的"处理中"状态(用户点击按钮后更新)。
428
+ */
429
+ function buildAppAuthProgressCard() {
430
+ return {
431
+ schema: '2.0',
432
+ config: { wide_screen_mode: false },
433
+ header: {
434
+ title: { tag: 'plain_text', content: '应用权限已开通' },
435
+ subtitle: { tag: 'plain_text', content: '' },
436
+ template: 'green',
437
+ padding: '12px 12px 12px 12px',
438
+ icon: { tag: 'standard_icon', token: 'yes_filled' },
439
+ },
440
+ body: {
441
+ elements: [
442
+ {
443
+ tag: 'markdown',
444
+ content: '您的应用权限已开通,正在为您发起用户授权',
445
+ text_size: 'normal',
446
+ },
447
+ ],
448
+ },
449
+ };
450
+ }
451
+ // ---------------------------------------------------------------------------
452
+ // Internal helpers
453
+ // ---------------------------------------------------------------------------
454
+ /**
455
+ * 发送应用权限引导卡片,并将 flow 存入 pendingAppAuthFlows。
456
+ * 返回工具结果(告知 AI 等待用户操作)。
457
+ */
458
+ async function sendAppScopeCard(params) {
459
+ const { account, missingScopes, appId, scopeNeedType, tokenType, cfg, ticket } = params;
460
+ const { accountId, chatId, messageId } = ticket;
461
+ const activeCardKey = `${chatId}:${messageId}`;
462
+ // ---- 去重:避免并发工具调用时发出多张内容相同的卡片 ----
463
+ const dedup = makeDedupKey(chatId, messageId, missingScopes);
464
+ const existingEntry = appAuthFlows.getByDedupKey(dedup);
465
+ if (existingEntry) {
466
+ log.info(`dedup – app-scope card already pending for chatId=${chatId}, ` +
467
+ `scopes=[${missingScopes.join(', ')}], skipping duplicate send`);
468
+ return json({
469
+ awaiting_app_authorization: true,
470
+ message: '已向用户发送授权引导卡片,等待用户完成授权操作。' +
471
+ '请告知用户:按照卡片提示完成授权,完成后系统将自动重试之前的操作。' +
472
+ '请等待用户完成卡片操作,不要建议其他替代方案。',
473
+ missing_scopes: missingScopes,
474
+ });
475
+ }
476
+ // ---- 卡片复用:同一 chatId+messageId 已有活跃卡片时,原地更新而非创建新卡片 ----
477
+ const activeEntry = appAuthFlows.getByActiveCardKey(activeCardKey);
478
+ if (activeEntry) {
479
+ const { operationId: activeOpId, flow: activeFlow } = activeEntry;
480
+ // 更新已有卡片的内容(合并后的 scope)
481
+ const newOperationId = Date.now().toString(36) + Math.random().toString(36).slice(2);
482
+ const card = buildAppScopeMissingCard({ missingScopes, appId, operationId: newOperationId });
483
+ const newSeq = activeFlow.sequence + 1;
484
+ // TOCTOU 修复:先原子迁移(同步操作),再 await 更新卡片
485
+ const newDedup = makeDedupKey(chatId, messageId, missingScopes);
486
+ const migrated = appAuthFlows.migrateToNewOperationId(activeOpId, newOperationId, {
487
+ dedupKey: newDedup,
488
+ requiredScopes: missingScopes,
489
+ scopeNeedType,
490
+ });
491
+ if (!migrated) {
492
+ // 被其他并发请求抢先迁移了,降级到新建卡片
493
+ log.info(`migrate raced, falling through to new card creation`);
494
+ }
495
+ else {
496
+ try {
497
+ await updateCardKitCardForAuth({
498
+ cfg,
499
+ cardId: activeFlow.cardId,
500
+ card,
501
+ sequence: newSeq,
502
+ accountId,
503
+ });
504
+ log.info(`app-scope card updated in-place, cardId=${activeFlow.cardId}, ` +
505
+ `seq=${newSeq}, scopes=[${missingScopes.join(', ')}]`);
506
+ // 更新 sequence(migrate 不处理 sequence)
507
+ migrated.sequence = newSeq;
508
+ return json({
509
+ awaiting_app_authorization: true,
510
+ message: '已向用户发送授权引导卡片,等待用户完成授权操作。' +
511
+ '请告知用户:按照卡片提示完成授权,完成后系统将自动重试之前的操作。' +
512
+ '请等待用户完成卡片操作,不要建议其他替代方案。',
513
+ missing_scopes: missingScopes,
514
+ });
515
+ }
516
+ catch (err) {
517
+ // 回滚:删除已迁移的 flow
518
+ appAuthFlows.remove(newOperationId);
519
+ log.warn(`failed to update existing app-scope card, creating new one: ${err}`);
520
+ // 降级:走下面的新建卡片路径
521
+ }
522
+ }
523
+ }
524
+ const operationId = Date.now().toString(36) + Math.random().toString(36).slice(2);
525
+ const card = buildAppScopeMissingCard({ missingScopes, appId, operationId });
526
+ // 创建 CardKit 卡片实体
527
+ const cardId = await createCardEntity({ cfg, card, accountId });
528
+ if (!cardId) {
529
+ log.warn('createCardEntity failed for app-scope card, falling back');
530
+ return json({
531
+ error: 'app_scope_missing',
532
+ missing_scopes: missingScopes,
533
+ message: `应用缺少以下权限:${missingScopes.join(', ')},` +
534
+ `请管理员在开放平台开通后重试。` +
535
+ (appId ? `\n权限管理:https://open.feishu.cn/app/${appId}/permission` : ''),
536
+ });
537
+ }
538
+ // 发送到当前会话
539
+ const replyToMsgId = ticket.messageId?.startsWith('om_') ? ticket.messageId : undefined;
540
+ await sendCardByCardId({
541
+ cfg,
542
+ to: chatId,
543
+ cardId,
544
+ replyToMessageId: replyToMsgId,
545
+ replyInThread: Boolean(ticket?.threadId),
546
+ accountId,
547
+ });
548
+ // 原子注册到管理器(统一 TTL 清理)
549
+ const flow = {
550
+ appId: appId ?? account.appId,
551
+ accountId,
552
+ cardId,
553
+ sequence: 0,
554
+ requiredScopes: missingScopes,
555
+ scopeNeedType,
556
+ tokenType,
557
+ cfg,
558
+ ticket,
559
+ };
560
+ appAuthFlows.register(operationId, flow, dedup, activeCardKey);
561
+ log.info(`app-scope card sent, operationId=${operationId}, scopes=[${missingScopes.join(', ')}]`);
562
+ return json({
563
+ awaiting_app_authorization: true,
564
+ message: '已向用户发送授权引导卡片,等待用户完成授权操作。' +
565
+ '请告知用户:按照卡片提示完成授权,完成后系统将自动重试之前的操作。' +
566
+ '请等待用户完成卡片操作,不要建议其他替代方案。',
567
+ missing_scopes: missingScopes,
568
+ });
569
+ }
570
+ // ---------------------------------------------------------------------------
571
+ // Card action handler (exported for monitor.ts)
572
+ // ---------------------------------------------------------------------------
573
+ /**
574
+ * 处理 card.action.trigger 回调事件(由 monitor.ts 调用)。
575
+ *
576
+ * 当用户点击应用权限引导卡片的"我已完成,继续授权"按钮时:
577
+ * 1. 更新卡片为"处理中"状态
578
+ * 2. 清除应用 scope 缓存
579
+ * 3. 发送中间合成消息告知 AI
580
+ * 4. 发起 OAuth Device Flow
581
+ *
582
+ * 注意:函数体内的主要逻辑通过 setImmediate + fire-and-forget 异步执行,
583
+ * 确保 Feishu card.action.trigger 回调在 3 秒内返回。
584
+ */
585
+ export async function handleCardAction(data, cfg, accountId) {
586
+ let action;
587
+ let operationId;
588
+ let senderOpenId;
589
+ try {
590
+ const event = data;
591
+ action = event.action?.value?.action;
592
+ operationId = event.action?.value?.operation_id;
593
+ senderOpenId = event.operator?.open_id;
594
+ }
595
+ catch {
596
+ return;
597
+ }
598
+ if (action !== 'app_auth_done' || !operationId)
599
+ return;
600
+ const flow = appAuthFlows.getByOperationId(operationId);
601
+ if (!flow) {
602
+ log.warn(`card action ${operationId} not found (expired or already handled)`);
603
+ return;
604
+ }
605
+ log.info(`app_auth_done clicked by ${senderOpenId}, operationId=${operationId}`);
606
+ // scope 校验在同步路径完成(3 秒内返回 toast response)
607
+ invalidateAppScopeCache(flow.appId);
608
+ const acct = getLarkAccount(flow.cfg, flow.accountId);
609
+ if (!acct.configured) {
610
+ log.warn(`account ${flow.accountId} not configured, skipping OAuth`);
611
+ return;
612
+ }
613
+ const sdk = LarkClient.fromAccount(acct).sdk;
614
+ let grantedScopes = [];
615
+ try {
616
+ // 使用与原始 AppScopeMissingError 相同的 tokenType,保证校验逻辑完全一致
617
+ grantedScopes = await getAppGrantedScopes(sdk, flow.appId, flow.tokenType);
618
+ }
619
+ catch (err) {
620
+ log.warn(`failed to re-check app scopes: ${err}, proceeding anyway`);
621
+ }
622
+ // 使用共享函数 isAppScopeSatisfied,与 tool-client invoke() 逻辑完全一致:
623
+ // - scopeNeedType "all" → 全部必须有
624
+ // - 默认"one" → 交集非空即可
625
+ // - grantedScopes 为空 → 视为满足(API 失败退回服务端判断)
626
+ if (!isAppScopeSatisfied(grantedScopes, flow.requiredScopes, flow.scopeNeedType)) {
627
+ log.warn(`app scopes still missing after user confirmation: [${flow.requiredScopes.join(', ')}]`);
628
+ return {
629
+ toast: {
630
+ type: 'error',
631
+ content: '权限尚未开通,请确认已申请并审核通过后再试',
632
+ },
633
+ };
634
+ }
635
+ log.info(`app scopes verified, proceeding with OAuth`);
636
+ // ★ 在 remove() 之前先取出延迟队列数据,避免 remove() 的联动清理提前删掉它
637
+ const deferKey = flow.ticket.senderOpenId
638
+ ? `${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`
639
+ : undefined;
640
+ const consumedDeferred = deferKey ? deferredUserAuth.get(deferKey) : undefined;
641
+ if (consumedDeferred && deferKey) {
642
+ deferredUserAuth.delete(deferKey);
643
+ log.info(`consumed deferred user auth scopes: [${[...consumedDeferred.scopes].join(', ')}]`);
644
+ }
645
+ // 校验通过才删除,防止用户在权限通过前多次点击无法重试
646
+ appAuthFlows.remove(operationId);
647
+ // 通过回调返回值直接更新卡片(方式一:3 秒内立即更新)。
648
+ // 飞书文档要求 card 字段必须包含 type + data 包装:
649
+ // { card: { type: "raw", data: { schema: "2.0", ... } } }
650
+ // 注意:不能在回调返回前调用 card.update API,飞书文档明确说明
651
+ // "延时更新必须在响应回调请求之后执行,并行执行或提前执行会出现更新失败"。
652
+ const successCard = buildAppAuthProgressCard();
653
+ // 后台异步:回调响应之后再执行 API 更新 + OAuth
654
+ setImmediate(async () => {
655
+ try {
656
+ // 通过 API 再次更新卡片(确保所有查看者都看到更新,不只是点击者)
657
+ try {
658
+ await updateCardKitCardForAuth({
659
+ cfg,
660
+ cardId: flow.cardId,
661
+ card: successCard,
662
+ sequence: flow.sequence + 1,
663
+ accountId,
664
+ });
665
+ }
666
+ catch (err) {
667
+ log.warn(`failed to update app-scope card to progress via API: ${err}`);
668
+ }
669
+ // 发起 OAuth Device Flow(完成后 executeAuthorize 会自动发合成消息触发 AI 重试)
670
+ if (!flow.ticket.senderOpenId) {
671
+ log.warn('no senderOpenId in ticket, skipping OAuth');
672
+ return;
673
+ }
674
+ // 收集所有来源的 scope(过滤 offline_access:仅 app 级需要,device-flow 自动追加)
675
+ const mergedScopes = new Set(flow.requiredScopes.filter((s) => s !== 'offline_access'));
676
+ // 来源 1: 延迟用户授权队列(已在同步路径中提前取出,见 consumedDeferred)
677
+ if (consumedDeferred) {
678
+ for (const s of consumedDeferred.scopes)
679
+ mergedScopes.add(s);
680
+ }
681
+ // 来源 2: 现有 user auth batch(向后兼容,处理未被延迟拦截的 user auth)
682
+ const userBatchKey = `user:${flow.accountId}:${flow.ticket.senderOpenId}:${flow.ticket.messageId}`;
683
+ const userBatch = authBatches.get(userBatchKey);
684
+ if (userBatch) {
685
+ for (const s of userBatch.scopes)
686
+ mergedScopes.add(s);
687
+ log.info(`merged user batch scopes into app auth completion: [${[...mergedScopes].join(', ')}]`);
688
+ }
689
+ if (mergedScopes.size === 0) {
690
+ // 无业务 scope 需要用户授权(例如 offline_access 是唯一缺失的应用权限,
691
+ // 且没有其他工具产生用户授权需求)。跳过 OAuth,直接发合成消息触发 AI 重试,
692
+ // 重试时工具会自然发现需要用户授权并发起正确的 OAuth 流程。
693
+ log.info('no business scopes to authorize after app auth, sending synthetic message for retry');
694
+ const syntheticMsgId = `${flow.ticket.messageId}:app-auth-complete`;
695
+ const syntheticEvent = {
696
+ sender: { sender_id: { open_id: flow.ticket.senderOpenId } },
697
+ message: {
698
+ message_id: syntheticMsgId,
699
+ chat_id: flow.ticket.chatId,
700
+ chat_type: flow.ticket.chatType ?? 'p2p',
701
+ message_type: 'text',
702
+ content: JSON.stringify({ text: '应用权限已开通,请继续执行之前的操作。' }),
703
+ thread_id: flow.ticket.threadId,
704
+ },
705
+ };
706
+ const syntheticRuntime = {
707
+ log: (msg) => log.info(msg),
708
+ error: (msg) => log.error(msg),
709
+ };
710
+ const { promise } = enqueueFeishuChatTask({
711
+ accountId: flow.accountId,
712
+ chatId: flow.ticket.chatId,
713
+ threadId: flow.ticket.threadId,
714
+ task: async () => {
715
+ await withTicket({
716
+ messageId: syntheticMsgId,
717
+ chatId: flow.ticket.chatId,
718
+ accountId: flow.accountId,
719
+ startTime: Date.now(),
720
+ senderOpenId: flow.ticket.senderOpenId,
721
+ chatType: flow.ticket.chatType,
722
+ threadId: flow.ticket.threadId,
723
+ }, () => handleFeishuMessage({
724
+ cfg: flow.cfg,
725
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
726
+ event: syntheticEvent,
727
+ accountId: flow.accountId,
728
+ forceMention: true,
729
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
730
+ runtime: syntheticRuntime,
731
+ replyToMessageId: flow.ticket.messageId,
732
+ }));
733
+ },
734
+ });
735
+ await promise;
736
+ log.info('synthetic message dispatched after app-auth-only completion');
737
+ }
738
+ else {
739
+ await executeAuthorize({
740
+ account: acct,
741
+ senderOpenId: flow.ticket.senderOpenId,
742
+ scope: [...mergedScopes].join(' '),
743
+ showBatchAuthHint: true,
744
+ forceAuth: true, // 应用权限刚经历移除→补回,不信任本地 UAT 缓存
745
+ cfg: flow.cfg,
746
+ ticket: flow.ticket,
747
+ });
748
+ }
749
+ }
750
+ catch (err) {
751
+ log.error(`handleCardAction background task failed: ${err}`);
752
+ }
753
+ });
754
+ // 回调返回值:通过 card 字段立即更新卡片 + toast 提示
755
+ return {
756
+ toast: {
757
+ type: 'success',
758
+ content: '权限确认成功',
759
+ },
760
+ card: {
761
+ type: 'raw',
762
+ data: successCard,
763
+ },
764
+ };
765
+ }
766
+ // ---------------------------------------------------------------------------
767
+ // Main export
768
+ // ---------------------------------------------------------------------------
769
+ /**
770
+ * 统一处理 `client.invoke()` 抛出的错误,支持自动发起 OAuth 授权。
771
+ *
772
+ * 替代 `handleInvokeError`,在工具层直接处理授权问题:
773
+ * - 用户授权类错误 → 直接 executeAuthorize(发 Device Flow 卡片)
774
+ * - 应用权限缺失 → 发送引导卡片,用户确认后自动接力 OAuth
775
+ * - 其他错误 → 回退到 handleInvokeError 的标准处理
776
+ *
777
+ * @param err - invoke() 或其他逻辑抛出的错误
778
+ * @param cfg - OpenClaw 配置对象(从工具注册函数的闭包中获取)
779
+ */
780
+ export async function handleInvokeErrorWithAutoAuth(err, cfg) {
781
+ const ticket = getTicket();
782
+ // --- Path 0:Owner 访问拒绝 → 直接返回友好提示 ---
783
+ if (err instanceof OwnerAccessDeniedError) {
784
+ return json({
785
+ error: 'permission_denied',
786
+ message: '当前应用仅限所有者(App Owner)使用。您没有权限使用相关功能。',
787
+ user_open_id: err.userOpenId,
788
+ // 注意:不序列化 err.appOwnerId,避免泄露 owner 的 open_id
789
+ });
790
+ }
791
+ if (ticket) {
792
+ const senderOpenId = ticket.senderOpenId;
793
+ // --- Path 1:用户授权类错误 → 防抖合并后发起 OAuth ---
794
+ if (senderOpenId) {
795
+ // 1a. 用户未授权或 token scope 不足(且 app scope 已验证)
796
+ if (err instanceof UserAuthRequiredError && err.appScopeVerified) {
797
+ const scopes = err.requiredScopes;
798
+ try {
799
+ const acct = getLarkAccount(cfg, ticket.accountId);
800
+ if (acct.configured) {
801
+ // ★ 延迟检查:如果同一消息有未完成的应用权限流程,
802
+ // 将用户授权 scope 收集到延迟队列,等应用授权完成后统一发起 OAuth
803
+ if (hasActiveAppAuthForMessage(ticket)) {
804
+ addToDeferredUserAuth(ticket, scopes, acct, cfg);
805
+ log.info(`UserAuthRequiredError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);
806
+ return json({
807
+ awaiting_app_authorization: true,
808
+ user_auth_deferred: true,
809
+ message: '应用权限尚未开通,将在应用权限通过后自动为您发起用户授权。' +
810
+ '请先按照应用权限卡片的提示完成操作。' +
811
+ '请等待用户完成卡片操作,不要建议其他替代方案。',
812
+ deferred_scopes: scopes,
813
+ });
814
+ }
815
+ const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;
816
+ log.info(`UserAuthRequiredError → enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);
817
+ return await enqueueAuthRequest(bufferKey, scopes, { account: acct, cfg, ticket }, async (mergedScopes) => {
818
+ // 等待同一消息的 app auth 卡片先发出
819
+ const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
820
+ const appEntry = authBatches.get(appKey);
821
+ if (appEntry?.resultPromise) {
822
+ await appEntry.resultPromise.catch(() => { });
823
+ }
824
+ return executeAuthorize({
825
+ account: acct,
826
+ senderOpenId,
827
+ scope: mergedScopes.join(' '),
828
+ showBatchAuthHint: true,
829
+ cfg,
830
+ ticket,
831
+ });
832
+ }, AUTH_USER_DEBOUNCE_MS);
833
+ }
834
+ }
835
+ catch (autoAuthErr) {
836
+ log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);
837
+ }
838
+ }
839
+ // 1b. 用户 token 存在但 scope 不足(服务端 LARK_ERROR.USER_SCOPE_INSUFFICIENT / 99991679)
840
+ if (err instanceof UserScopeInsufficientError) {
841
+ const scopes = err.missingScopes;
842
+ try {
843
+ const acct = getLarkAccount(cfg, ticket.accountId);
844
+ if (acct.configured) {
845
+ // ★ 延迟检查:同 Path 1a
846
+ if (hasActiveAppAuthForMessage(ticket)) {
847
+ addToDeferredUserAuth(ticket, scopes, acct, cfg);
848
+ log.info(`UserScopeInsufficientError deferred (app auth pending), scopes=[${scopes.join(', ')}]`);
849
+ return json({
850
+ awaiting_app_authorization: true,
851
+ user_auth_deferred: true,
852
+ message: '应用权限尚未开通,将在应用权限通过后自动为您发起用户授权。' +
853
+ '请先按照应用权限卡片的提示完成操作。' +
854
+ '请等待用户完成卡片操作,不要建议其他替代方案。',
855
+ deferred_scopes: scopes,
856
+ });
857
+ }
858
+ const bufferKey = `user:${ticket.accountId}:${senderOpenId}:${ticket.messageId}`;
859
+ log.info(`UserScopeInsufficientError → enqueue, key=${bufferKey}, scopes=[${scopes.join(', ')}]`);
860
+ return await enqueueAuthRequest(bufferKey, scopes, { account: acct, cfg, ticket }, async (mergedScopes) => {
861
+ // 等待同一消息的 app auth 卡片先发出
862
+ const appKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
863
+ const appEntry = authBatches.get(appKey);
864
+ if (appEntry?.resultPromise) {
865
+ await appEntry.resultPromise.catch(() => { });
866
+ }
867
+ return executeAuthorize({
868
+ account: acct,
869
+ senderOpenId,
870
+ scope: mergedScopes.join(' '),
871
+ showBatchAuthHint: true,
872
+ cfg,
873
+ ticket,
874
+ });
875
+ }, AUTH_USER_DEBOUNCE_MS);
876
+ }
877
+ }
878
+ catch (autoAuthErr) {
879
+ log.warn(`executeAuthorize failed: ${autoAuthErr}, falling back`);
880
+ }
881
+ }
882
+ }
883
+ else {
884
+ log.error(`senderOpenId not found ${err}`);
885
+ }
886
+ // --- Path 2:应用权限缺失 → 防抖合并后发送引导卡片 ---
887
+ if (err instanceof AppScopeMissingError && ticket.chatId) {
888
+ // 捕获当前错误的附加信息,供 flushFn 使用
889
+ const appScopeErr = err;
890
+ try {
891
+ const acct = getLarkAccount(cfg, ticket.accountId);
892
+ if (acct.configured) {
893
+ // ★ 将工具的全部所需 scope 加入延迟用户授权队列。
894
+ // 应用权限完成后 handleCardAction 会消费这些 scope,
895
+ // 与 flow.requiredScopes(仅 app 缺失的)合并,一次性发起 OAuth。
896
+ if (senderOpenId && appScopeErr.allRequiredScopes?.length) {
897
+ addToDeferredUserAuth(ticket, appScopeErr.allRequiredScopes, acct, cfg);
898
+ log.info(`AppScopeMissingError → deferred allRequiredScopes=[${appScopeErr.allRequiredScopes.join(', ')}]`);
899
+ }
900
+ const bufferKey = `app:${ticket.accountId}:${ticket.chatId}:${ticket.messageId}`;
901
+ log.info(`AppScopeMissingError → enqueue, key=${bufferKey}, ` + `scopes=[${appScopeErr.missingScopes.join(', ')}]`);
902
+ return await enqueueAuthRequest(bufferKey, appScopeErr.missingScopes, { account: acct, cfg, ticket }, (mergedScopes) => sendAppScopeCard({
903
+ account: acct,
904
+ missingScopes: mergedScopes,
905
+ appId: appScopeErr.appId,
906
+ scopeNeedType: 'all', // 合并后所有 scope 都需要
907
+ tokenType: appScopeErr.tokenType,
908
+ cfg,
909
+ ticket,
910
+ }));
911
+ }
912
+ }
913
+ catch (cardErr) {
914
+ log.warn(`sendAppScopeCard failed: ${cardErr}, falling back`);
915
+ }
916
+ }
917
+ }
918
+ else {
919
+ log.error(`ticket not found ${err}`);
920
+ }
921
+ return json({
922
+ error: formatLarkError(err),
923
+ });
924
+ }
925
+ //# sourceMappingURL=auto-auth.js.map