@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,172 @@
1
+ import { execFile as execFileCb } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { mkdir, unlink, readFile, writeFile, chmod } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { homedir } from "node:os";
6
+ import { randomBytes, createCipheriv, createDecipheriv } from "node:crypto";
7
+ import { larkLogger } from "./lark-logger";
8
+ import { maskToken } from "./token-store";
9
+ const log = larkLogger("core/project-token-store");
10
+ const execFile = promisify(execFileCb);
11
+ const PROJECT_SERVICE = "openclaw-feishu-project-uat";
12
+ const REFRESH_AHEAD_MS = 5 * 60 * 1e3;
13
+ function accountKey(appId, userOpenId) {
14
+ return `${appId}:${userOpenId}`;
15
+ }
16
+ const darwinBackend = {
17
+ async get(service, account) {
18
+ try {
19
+ const { stdout } = await execFile("security", ["find-generic-password", "-s", service, "-a", account, "-w"]);
20
+ return stdout.trim() || null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ },
25
+ async set(service, account, data) {
26
+ try {
27
+ await execFile("security", ["delete-generic-password", "-s", service, "-a", account]);
28
+ } catch {
29
+ }
30
+ await execFile("security", ["add-generic-password", "-s", service, "-a", account, "-w", data]);
31
+ },
32
+ async remove(service, account) {
33
+ try {
34
+ await execFile("security", ["delete-generic-password", "-s", service, "-a", account]);
35
+ } catch {
36
+ }
37
+ }
38
+ };
39
+ const MASTER_KEY_BYTES = 32;
40
+ const IV_BYTES = 12;
41
+ const TAG_BYTES = 16;
42
+ function getStorageDir() {
43
+ if (process.platform === "win32") {
44
+ return join(
45
+ process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), "AppData", "Local"),
46
+ PROJECT_SERVICE
47
+ );
48
+ }
49
+ return join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), PROJECT_SERVICE);
50
+ }
51
+ const STORAGE_DIR = getStorageDir();
52
+ const MASTER_KEY_PATH = join(STORAGE_DIR, "master.key");
53
+ function safeFileName(account) {
54
+ return account.replace(/[^a-zA-Z0-9._-]/g, "_") + ".enc";
55
+ }
56
+ async function ensureDir() {
57
+ await mkdir(STORAGE_DIR, { recursive: true, mode: 448 });
58
+ }
59
+ async function getMasterKey() {
60
+ try {
61
+ const key2 = await readFile(MASTER_KEY_PATH);
62
+ if (key2.length === MASTER_KEY_BYTES) return key2;
63
+ } catch (err) {
64
+ if (!(err instanceof Error) || err.code !== "ENOENT") {
65
+ log.warn(`failed to read project master key: ${err instanceof Error ? err.message : err}`);
66
+ }
67
+ }
68
+ await ensureDir();
69
+ const key = randomBytes(MASTER_KEY_BYTES);
70
+ await writeFile(MASTER_KEY_PATH, key, { mode: 384 });
71
+ await chmod(MASTER_KEY_PATH, 384);
72
+ return key;
73
+ }
74
+ function encrypt(plaintext, key) {
75
+ const iv = randomBytes(IV_BYTES);
76
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
77
+ const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
78
+ return Buffer.concat([iv, cipher.getAuthTag(), enc]);
79
+ }
80
+ function decrypt(data, key) {
81
+ if (data.length < IV_BYTES + TAG_BYTES) return null;
82
+ try {
83
+ const iv = data.subarray(0, IV_BYTES);
84
+ const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
85
+ const enc = data.subarray(IV_BYTES + TAG_BYTES);
86
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
87
+ decipher.setAuthTag(tag);
88
+ return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+ const fileBackend = {
94
+ async get(_service, account) {
95
+ try {
96
+ const key = await getMasterKey();
97
+ const data = await readFile(join(STORAGE_DIR, safeFileName(account)));
98
+ return decrypt(data, key);
99
+ } catch {
100
+ return null;
101
+ }
102
+ },
103
+ async set(_service, account, data) {
104
+ const key = await getMasterKey();
105
+ await ensureDir();
106
+ const filePath = join(STORAGE_DIR, safeFileName(account));
107
+ const encrypted = encrypt(data, key);
108
+ await writeFile(filePath, encrypted, { mode: 384 });
109
+ },
110
+ async remove(_service, account) {
111
+ try {
112
+ await unlink(join(STORAGE_DIR, safeFileName(account)));
113
+ } catch {
114
+ }
115
+ }
116
+ };
117
+ function createBackend() {
118
+ return process.platform === "darwin" ? darwinBackend : fileBackend;
119
+ }
120
+ const backend = createBackend();
121
+ async function getProjectStoredToken(appId, userOpenId) {
122
+ try {
123
+ const json = await backend.get(PROJECT_SERVICE, accountKey(appId, userOpenId));
124
+ if (!json) return null;
125
+ return JSON.parse(json);
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ async function setProjectStoredToken(token) {
131
+ const key = accountKey(token.appId, token.userOpenId);
132
+ const payload = JSON.stringify(token);
133
+ await backend.set(PROJECT_SERVICE, key, payload);
134
+ await saveClientIdIndex(token.userOpenId, token.appId);
135
+ log.info(`saved project UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);
136
+ }
137
+ async function removeProjectStoredToken(appId, userOpenId) {
138
+ await backend.remove(PROJECT_SERVICE, accountKey(appId, userOpenId));
139
+ await removeClientIdIndex(userOpenId);
140
+ log.info(`removed project UAT for ${userOpenId}`);
141
+ }
142
+ function projectTokenStatus(token) {
143
+ const now = Date.now();
144
+ if (now < token.expiresAt - REFRESH_AHEAD_MS) return "valid";
145
+ if (now < token.refreshExpiresAt) return "needs_refresh";
146
+ return "expired";
147
+ }
148
+ const CLIENT_ID_SERVICE = "openclaw-feishu-project-cid";
149
+ async function getProjectClientId(userOpenId) {
150
+ try {
151
+ return await backend.get(CLIENT_ID_SERVICE, userOpenId);
152
+ } catch {
153
+ return null;
154
+ }
155
+ }
156
+ async function saveClientIdIndex(userOpenId, clientId) {
157
+ await backend.set(CLIENT_ID_SERVICE, userOpenId, clientId);
158
+ }
159
+ async function removeClientIdIndex(userOpenId) {
160
+ try {
161
+ await backend.remove(CLIENT_ID_SERVICE, userOpenId);
162
+ } catch {
163
+ }
164
+ }
165
+ export {
166
+ getProjectClientId,
167
+ getProjectStoredToken,
168
+ projectTokenStatus,
169
+ removeProjectStoredToken,
170
+ setProjectStoredToken
171
+ };
172
+ //# sourceMappingURL=project-token-store.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/project-token-store.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * \u98DE\u4E66\u9879\u76EE\uFF08Meego\uFF09\u4E13\u7528 Token \u5B58\u50A8\n *\n * \u590D\u7528 token-store \u7684 StoredUAToken \u7ED3\u6784\u548C\u8DE8\u5E73\u53F0\u540E\u7AEF\uFF0C\n * \u4F46\u4F7F\u7528\u72EC\u7ACB\u7684 service key\uFF0C\u4E0E\u98DE\u4E66 Open API \u7684 token \u5206\u5F00\u5B58\u50A8\u3002\n */\n\nimport { execFile as execFileCb } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { mkdir, unlink, readFile, writeFile, chmod } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';\nimport { larkLogger } from './lark-logger';\nimport { maskToken, type StoredUAToken } from './token-store';\n\nconst log = larkLogger('core/project-token-store');\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst PROJECT_SERVICE = 'openclaw-feishu-project-uat';\nconst REFRESH_AHEAD_MS = 5 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountKey(appId: string, userOpenId: string): string {\n return `${appId}:${userOpenId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Backend interface (same as token-store.ts)\n// ---------------------------------------------------------------------------\n\ninterface KeychainBackend {\n get(service: string, account: string): Promise<string | null>;\n set(service: string, account: string, data: string): Promise<void>;\n remove(service: string, account: string): Promise<void>;\n}\n\n// macOS\nconst darwinBackend: KeychainBackend = {\n async get(service, account) {\n try {\n const { stdout } = await execFile('security', ['find-generic-password', '-s', service, '-a', account, '-w']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n },\n async set(service, account, data) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Not found */ }\n await execFile('security', ['add-generic-password', '-s', service, '-a', account, '-w', data]);\n },\n async remove(service, account) {\n try {\n await execFile('security', ['delete-generic-password', '-s', service, '-a', account]);\n } catch { /* Already absent */ }\n },\n};\n\n// Linux / Windows \u2014 AES-256-GCM encrypted files\nconst MASTER_KEY_BYTES = 32;\nconst IV_BYTES = 12;\nconst TAG_BYTES = 16;\n\nfunction getStorageDir(): string {\n if (process.platform === 'win32') {\n return join(\n process.env.LOCALAPPDATA ?? join(process.env.USERPROFILE ?? homedir(), 'AppData', 'Local'),\n PROJECT_SERVICE,\n );\n }\n return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), PROJECT_SERVICE);\n}\n\nconst STORAGE_DIR = getStorageDir();\nconst MASTER_KEY_PATH = join(STORAGE_DIR, 'master.key');\n\nfunction safeFileName(account: string): string {\n return account.replace(/[^a-zA-Z0-9._-]/g, '_') + '.enc';\n}\n\nasync function ensureDir(): Promise<void> {\n await mkdir(STORAGE_DIR, { recursive: true, mode: 0o700 });\n}\n\nasync function getMasterKey(): Promise<Buffer> {\n try {\n const key = await readFile(MASTER_KEY_PATH);\n if (key.length === MASTER_KEY_BYTES) return key;\n } catch (err: unknown) {\n if (!(err instanceof Error) || (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n log.warn(`failed to read project master key: ${err instanceof Error ? err.message : err}`);\n }\n }\n await ensureDir();\n const key = randomBytes(MASTER_KEY_BYTES);\n await writeFile(MASTER_KEY_PATH, key, { mode: 0o600 });\n await chmod(MASTER_KEY_PATH, 0o600);\n return key;\n}\n\nfunction encrypt(plaintext: string, key: Buffer): Buffer {\n const iv = randomBytes(IV_BYTES);\n const cipher = createCipheriv('aes-256-gcm', key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);\n return Buffer.concat([iv, cipher.getAuthTag(), enc]);\n}\n\nfunction decrypt(data: Buffer, key: Buffer): string | null {\n if (data.length < IV_BYTES + TAG_BYTES) return null;\n try {\n const iv = data.subarray(0, IV_BYTES);\n const tag = data.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);\n const enc = data.subarray(IV_BYTES + TAG_BYTES);\n const decipher = createDecipheriv('aes-256-gcm', key, iv);\n decipher.setAuthTag(tag);\n return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');\n } catch {\n return null;\n }\n}\n\nconst fileBackend: KeychainBackend = {\n async get(_service, account) {\n try {\n const key = await getMasterKey();\n const data = await readFile(join(STORAGE_DIR, safeFileName(account)));\n return decrypt(data, key);\n } catch {\n return null;\n }\n },\n async set(_service, account, data) {\n const key = await getMasterKey();\n await ensureDir();\n const filePath = join(STORAGE_DIR, safeFileName(account));\n const encrypted = encrypt(data, key);\n await writeFile(filePath, encrypted, { mode: 0o600 });\n },\n async remove(_service, account) {\n try {\n await unlink(join(STORAGE_DIR, safeFileName(account)));\n } catch { /* Already absent */ }\n },\n};\n\nfunction createBackend(): KeychainBackend {\n return process.platform === 'darwin' ? darwinBackend : fileBackend;\n}\n\nconst backend = createBackend();\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport async function getProjectStoredToken(appId: string, userOpenId: string): Promise<StoredUAToken | null> {\n try {\n const json = await backend.get(PROJECT_SERVICE, accountKey(appId, userOpenId));\n if (!json) return null;\n return JSON.parse(json) as StoredUAToken;\n } catch {\n return null;\n }\n}\n\nexport async function setProjectStoredToken(token: StoredUAToken): Promise<void> {\n const key = accountKey(token.appId, token.userOpenId);\n const payload = JSON.stringify(token);\n await backend.set(PROJECT_SERVICE, key, payload);\n await saveClientIdIndex(token.userOpenId, token.appId);\n log.info(`saved project UAT for ${token.userOpenId} (at:${maskToken(token.accessToken)})`);\n}\n\nexport async function removeProjectStoredToken(appId: string, userOpenId: string): Promise<void> {\n await backend.remove(PROJECT_SERVICE, accountKey(appId, userOpenId));\n await removeClientIdIndex(userOpenId);\n log.info(`removed project UAT for ${userOpenId}`);\n}\n\nexport function projectTokenStatus(token: StoredUAToken): 'valid' | 'needs_refresh' | 'expired' {\n const now = Date.now();\n if (now < token.expiresAt - REFRESH_AHEAD_MS) return 'valid';\n if (now < token.refreshExpiresAt) return 'needs_refresh';\n return 'expired';\n}\n\n// ---------------------------------------------------------------------------\n// clientId \u7D22\u5F15 \u2014 \u4ECE userOpenId \u53CD\u67E5\u52A8\u6001\u6CE8\u518C\u7684 clientId\n// ---------------------------------------------------------------------------\n\nconst CLIENT_ID_SERVICE = 'openclaw-feishu-project-cid';\n\n/**\n * \u83B7\u53D6\u7528\u6237\u5BF9\u5E94\u7684\u98DE\u4E66\u9879\u76EE OAuth clientId\u3002\n * \u52A8\u6001\u6CE8\u518C\u7684 clientId \u5728\u6BCF\u6B21\u6CE8\u518C\u65F6\u53EF\u80FD\u4E0D\u540C\uFF0C\u6B64\u7D22\u5F15\u7528\u4E8E status/revoke \u7B49\n * \u4E0D\u77E5\u9053 clientId \u7684\u573A\u666F\u3002\n */\nexport async function getProjectClientId(userOpenId: string): Promise<string | null> {\n try {\n return await backend.get(CLIENT_ID_SERVICE, userOpenId);\n } catch {\n return null;\n }\n}\n\n/**\n * setProjectStoredToken \u7684\u589E\u5F3A\uFF1A\u540C\u65F6\u4FDD\u5B58 clientId \u7D22\u5F15\u3002\n * \u8C03\u7528\u65B9\u5728 setProjectStoredToken \u540E\u65E0\u9700\u989D\u5916\u8C03\u7528\u3002\n */\nasync function saveClientIdIndex(userOpenId: string, clientId: string): Promise<void> {\n await backend.set(CLIENT_ID_SERVICE, userOpenId, clientId);\n}\n\nasync function removeClientIdIndex(userOpenId: string): Promise<void> {\n try {\n await backend.remove(CLIENT_ID_SERVICE, userOpenId);\n } catch { /* Already absent */ }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["key"]
7
+ }
@@ -0,0 +1,45 @@
1
+ import { feishuFetch } from "./feishu-fetch";
2
+ function resolveDomainUrl(brand) {
3
+ const map = {
4
+ feishu: "https://open.feishu.cn",
5
+ lark: "https://open.larksuite.com"
6
+ };
7
+ return map[brand] ?? `https://${brand}`;
8
+ }
9
+ async function rawLarkRequest(options) {
10
+ const baseUrl = resolveDomainUrl(options.brand);
11
+ const url = new URL(options.path, baseUrl);
12
+ if (options.query) {
13
+ for (const [k, v] of Object.entries(options.query)) {
14
+ url.searchParams.set(k, v);
15
+ }
16
+ }
17
+ const headers = {};
18
+ if (options.accessToken) {
19
+ headers["Authorization"] = `Bearer ${options.accessToken}`;
20
+ }
21
+ if (options.body !== void 0) {
22
+ headers["Content-Type"] = "application/json";
23
+ }
24
+ if (options.headers) {
25
+ Object.assign(headers, options.headers);
26
+ }
27
+ const resp = await feishuFetch(url.toString(), {
28
+ method: options.method ?? "GET",
29
+ headers,
30
+ ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
31
+ });
32
+ const data = await resp.json();
33
+ if (data.code !== void 0 && data.code !== 0) {
34
+ const err = new Error(data.msg ?? `Lark API error: code=${data.code}`);
35
+ err.code = data.code;
36
+ err.msg = data.msg;
37
+ throw err;
38
+ }
39
+ return data;
40
+ }
41
+ export {
42
+ rawLarkRequest,
43
+ resolveDomainUrl
44
+ };
45
+ //# sourceMappingURL=raw-request.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/raw-request.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * raw-request.ts \u2014 \u98DE\u4E66 Open API \u88F8 HTTP \u8BF7\u6C42\u5DE5\u5177\u3002\n *\n * \u4ECE tool-client.ts \u63D0\u53D6\uFF0C\u63D0\u4F9B\u4E0D\u4F9D\u8D56 SDK \u7684\u76F4\u63A5 API \u8C03\u7528\u80FD\u529B\u3002\n * \u7528\u4E8E SDK \u672A\u8986\u76D6\u7684 API \u6216\u9700\u8981\u7CBE\u7EC6\u63A7\u5236\u8BF7\u6C42\u7684\u573A\u666F\u3002\n */\n\nimport type { LarkBrand } from './types';\nimport { feishuFetch } from './feishu-fetch';\n\n// ---------------------------------------------------------------------------\n// Domain URL resolution\n// ---------------------------------------------------------------------------\n\n/** \u5C06 LarkBrand \u6620\u5C04\u4E3A API base URL\u3002 */\nexport function resolveDomainUrl(brand: LarkBrand): string {\n const map: Record<string, string> = {\n feishu: 'https://open.feishu.cn',\n lark: 'https://open.larksuite.com',\n };\n return map[brand] ?? `https://${brand}`;\n}\n\n// ---------------------------------------------------------------------------\n// Raw HTTP request\n// ---------------------------------------------------------------------------\n\nexport interface RawLarkRequestOptions {\n brand: LarkBrand;\n path: string;\n method?: string;\n body?: unknown;\n query?: Record<string, string>;\n headers?: Record<string, string>;\n accessToken?: string;\n}\n\n/**\n * \u53D1\u8D77 raw HTTP \u8BF7\u6C42\u5230\u98DE\u4E66 API\uFF0C\u81EA\u52A8\u5904\u7406\u57DF\u540D\u89E3\u6790\u3001header \u6CE8\u5165\u548C\u9519\u8BEF\u68C0\u6D4B\u3002\n *\n * \u98DE\u4E66 API \u7EDF\u4E00\u9519\u8BEF\u6A21\u5F0F\uFF1A\u8FD4\u56DE JSON \u4E2D `code !== 0` \u8868\u793A\u5931\u8D25\u3002\n */\nexport async function rawLarkRequest<T>(options: RawLarkRequestOptions): Promise<T> {\n const baseUrl = resolveDomainUrl(options.brand);\n const url = new URL(options.path, baseUrl);\n if (options.query) {\n for (const [k, v] of Object.entries(options.query)) {\n url.searchParams.set(k, v);\n }\n }\n\n const headers: Record<string, string> = {};\n if (options.accessToken) {\n headers['Authorization'] = `Bearer ${options.accessToken}`;\n }\n if (options.body !== undefined) {\n headers['Content-Type'] = 'application/json';\n }\n if (options.headers) {\n Object.assign(headers, options.headers);\n }\n\n const resp = await feishuFetch(url.toString(), {\n method: options.method ?? 'GET',\n headers,\n ...(options.body !== undefined ? { body: JSON.stringify(options.body) } : {}),\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const data = (await resp.json()) as any;\n\n // \u98DE\u4E66 API \u7EDF\u4E00\u9519\u8BEF\u6A21\u5F0F\uFF1Acode !== 0\n if (data.code !== undefined && data.code !== 0) {\n const err = new Error(data.msg ?? `Lark API error: code=${data.code}`);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (err as any).code = data.code;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (err as any).msg = data.msg;\n throw err;\n }\n\n return data as T;\n}\n"],
5
+ "mappings": "AAWA,SAAS,mBAAmB;AAOrB,SAAS,iBAAiB,OAA0B;AACzD,QAAM,MAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AACA,SAAO,IAAI,KAAK,KAAK,WAAW,KAAK;AACvC;AAqBA,eAAsB,eAAkB,SAA4C;AAClF,QAAM,UAAU,iBAAiB,QAAQ,KAAK;AAC9C,QAAM,MAAM,IAAI,IAAI,QAAQ,MAAM,OAAO;AACzC,MAAI,QAAQ,OAAO;AACjB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,KAAK,GAAG;AAClD,UAAI,aAAa,IAAI,GAAG,CAAC;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,UAAkC,CAAC;AACzC,MAAI,QAAQ,aAAa;AACvB,YAAQ,eAAe,IAAI,UAAU,QAAQ,WAAW;AAAA,EAC1D;AACA,MAAI,QAAQ,SAAS,QAAW;AAC9B,YAAQ,cAAc,IAAI;AAAA,EAC5B;AACA,MAAI,QAAQ,SAAS;AACnB,WAAO,OAAO,SAAS,QAAQ,OAAO;AAAA,EACxC;AAEA,QAAM,OAAO,MAAM,YAAY,IAAI,SAAS,GAAG;AAAA,IAC7C,QAAQ,QAAQ,UAAU;AAAA,IAC1B;AAAA,IACA,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,QAAQ,IAAI,EAAE,IAAI,CAAC;AAAA,EAC7E,CAAC;AAGD,QAAM,OAAQ,MAAM,KAAK,KAAK;AAG9B,MAAI,KAAK,SAAS,UAAa,KAAK,SAAS,GAAG;AAC9C,UAAM,MAAM,IAAI,MAAM,KAAK,OAAO,wBAAwB,KAAK,IAAI,EAAE;AAErE,IAAC,IAAY,OAAO,KAAK;AAEzB,IAAC,IAAY,MAAM,KAAK;AACxB,UAAM;AAAA,EACR;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,62 @@
1
+ import { TOOL_SCOPES } from "./tool-scopes";
2
+ function getRequiredScopes(toolAction) {
3
+ return TOOL_SCOPES[toolAction] ?? [];
4
+ }
5
+ function getRequiredScopesForActions(toolActions) {
6
+ const scopesSet = /* @__PURE__ */ new Set();
7
+ for (const action of toolActions) {
8
+ const scopes = getRequiredScopes(action);
9
+ scopes.forEach((scope) => scopesSet.add(scope));
10
+ }
11
+ return Array.from(scopesSet).sort();
12
+ }
13
+ function hasRequiredScopes(toolAction) {
14
+ return getRequiredScopes(toolAction).length > 0;
15
+ }
16
+ function getActionsForScope(scope) {
17
+ const actions = [];
18
+ for (const [action, scopes] of Object.entries(TOOL_SCOPES)) {
19
+ if (scopes.includes(scope)) {
20
+ actions.push(action);
21
+ }
22
+ }
23
+ return actions;
24
+ }
25
+ function checkAppScopes(toolAction, appGrantedScopes) {
26
+ const requiredScopes = getRequiredScopes(toolAction);
27
+ if (requiredScopes.length === 0) {
28
+ return true;
29
+ }
30
+ const grantedSet = Array.isArray(appGrantedScopes) ? new Set(appGrantedScopes) : appGrantedScopes;
31
+ return requiredScopes.every((scope) => grantedSet.has(scope));
32
+ }
33
+ function getMissingAppScopes(toolAction, appGrantedScopes) {
34
+ const requiredScopes = getRequiredScopes(toolAction);
35
+ const grantedSet = Array.isArray(appGrantedScopes) ? new Set(appGrantedScopes) : appGrantedScopes;
36
+ return requiredScopes.filter((scope) => !grantedSet.has(scope));
37
+ }
38
+ function checkUserScopes(toolAction, userGrantedScopes) {
39
+ const requiredScopes = getRequiredScopes(toolAction);
40
+ if (requiredScopes.length === 0) {
41
+ return true;
42
+ }
43
+ const grantedSet = Array.isArray(userGrantedScopes) ? new Set(userGrantedScopes) : userGrantedScopes;
44
+ return requiredScopes.every((scope) => grantedSet.has(scope));
45
+ }
46
+ function getMissingUserScopes(toolAction, userGrantedScopes) {
47
+ const requiredScopes = getRequiredScopes(toolAction);
48
+ const grantedSet = Array.isArray(userGrantedScopes) ? new Set(userGrantedScopes) : userGrantedScopes;
49
+ return requiredScopes.filter((scope) => !grantedSet.has(scope));
50
+ }
51
+ export {
52
+ TOOL_SCOPES,
53
+ checkAppScopes,
54
+ checkUserScopes,
55
+ getActionsForScope,
56
+ getMissingAppScopes,
57
+ getMissingUserScopes,
58
+ getRequiredScopes,
59
+ getRequiredScopesForActions,
60
+ hasRequiredScopes
61
+ };
62
+ //# sourceMappingURL=scope-manager.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/scope-manager.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Scope \u7BA1\u7406\u6A21\u5757\n *\n * \u4E3A\u6240\u6709\u5DE5\u5177\u52A8\u4F5C\u63D0\u4F9B\u7C7B\u578B\u5B89\u5168\u7684 scope \u67E5\u8BE2\u548C\u68C0\u67E5\u529F\u80FD\u3002\n *\n * ## \u4E09\u4E2A\u6838\u5FC3\u6982\u5FF5\n *\n * ### 1. Required Scopes\uFF08API \u9700\u8981\u7684\u6743\u9650\uFF09\n * - \u5B9A\u4E49\uFF1A\u6BCF\u4E2A API \u8C03\u7528\u6240\u9700\u7684\u98DE\u4E66\u6743\u9650\u5217\u8868\n * - \u6765\u6E90\uFF1Atool-scopes.ts\uFF08\u624B\u52A8\u7EF4\u62A4\u7684\u7C7B\u578B\u5316\u914D\u7F6E\uFF09\n * - \u793A\u4F8B\uFF1A`[\"calendar:calendar.event:create\", \"calendar:calendar.event:update\"]`\n * - \u7528\u9014\uFF1A\u5224\u65AD\u5E94\u7528\u548C\u7528\u6237\u662F\u5426\u9700\u8981\u7533\u8BF7/\u6388\u6743\u6743\u9650\n *\n * ### 2. App Granted Scopes\uFF08\u5E94\u7528\u5DF2\u5F00\u901A\u7684\u6743\u9650\uFF09\n * - \u5B9A\u4E49\uFF1A\u5E94\u7528\u5728\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u914D\u7F6E\u5E76\u83B7\u5F97\u7BA1\u7406\u5458\u6279\u51C6\u7684\u6743\u9650\n * - \u6765\u6E90\uFF1A\u901A\u8FC7 API \u67E5\u8BE2 `/open-apis/application/v6/applications`\n * - \u4F5C\u7528\uFF1A\u5E94\u7528\u7EA7\u6743\u9650\u524D\u7F6E\u68C0\u67E5\uFF0C\u907F\u514D\u65E0\u6548\u7684\u7528\u6237\u6388\u6743\u8BF7\u6C42\n * - \u68C0\u67E5\u65F6\u673A\uFF1A\u5728\u8BF7\u6C42\u7528\u6237\u6388\u6743\u524D\n *\n * ### 3. User Granted Scopes\uFF08\u7528\u6237\u6388\u6743\u7684\u6743\u9650\uFF09\n * - \u5B9A\u4E49\uFF1A\u7528\u6237\u901A\u8FC7 OAuth \u6D41\u7A0B\u660E\u786E\u6388\u6743\u7ED9\u5E94\u7528\u7684\u6743\u9650\n * - \u6765\u6E90\uFF1AOAuth token \u4E2D\u7684 scope \u5B57\u6BB5\n * - \u4F5C\u7528\uFF1A\u7528\u6237\u7EA7\u6743\u9650\u68C0\u67E5\uFF0C\u786E\u4FDD\u7528\u6237\u5DF2\u6388\u6743\u6240\u9700\u6743\u9650\n * - \u68C0\u67E5\u65F6\u673A\uFF1A\u6BCF\u6B21 API \u8C03\u7528\u524D\n *\n * ## \u6743\u9650\u68C0\u67E5\u6D41\u7A0B\n *\n * ```\n * 1. \u83B7\u53D6 Required Scopes (API \u9700\u8981\u4EC0\u4E48\u6743\u9650\uFF1F)\n * \u2193\n * 2. \u68C0\u67E5 App Granted Scopes (\u5E94\u7528\u5F00\u901A\u4E86\u5417\uFF1F)\n * \u2193 \u662F\n * 3. \u68C0\u67E5 User Granted Scopes (\u7528\u6237\u6388\u6743\u4E86\u5417\uFF1F)\n * \u2193 \u662F\n * 4. \u8C03\u7528 API\n * ```\n */\n\nimport { type ToolActionKey, type ToolScopeMapping, TOOL_SCOPES } from './tool-scopes';\n\n// ===== \u5BFC\u51FA\u7C7B\u578B\u548C\u6570\u636E =====\n\nexport type { ToolActionKey, ToolScopeMapping };\nexport { TOOL_SCOPES };\n\n// ===== \u51FD\u6570\uFF1ARequired Scopes\uFF08API \u9700\u8981\u7684\u6743\u9650\uFF09=====\n\n/**\n * \u83B7\u53D6\u5355\u4E2A\u5DE5\u5177\u52A8\u4F5C\u6240\u9700\u7684 scopes\uFF08Required Scopes\uFF09\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\uFF08\u4F8B\u5982 \"feishu_calendar_event.create\"\uFF09\n * @returns API \u9700\u8981\u7684 scope \u5B57\u7B26\u4E32\u6570\u7EC4\n *\n * @example\n * ```ts\n * const requiredScopes = getRequiredScopes(\"feishu_calendar_event.create\");\n * // \u8FD4\u56DE: [\"calendar:calendar.event:create\", \"calendar:calendar.event:update\"]\n * ```\n */\nexport function getRequiredScopes(toolAction: ToolActionKey): string[] {\n return TOOL_SCOPES[toolAction] ?? [];\n}\n\n/**\n * \u83B7\u53D6\u591A\u4E2A\u5DE5\u5177\u52A8\u4F5C\u7684\u5408\u5E76 Required Scopes\uFF08\u53BB\u91CD\uFF09\n *\n * @param toolActions - \u5DE5\u5177\u52A8\u4F5C\u952E\u6570\u7EC4\n * @returns \u53BB\u91CD\u5E76\u6392\u5E8F\u540E\u7684 scope \u5B57\u7B26\u4E32\u6570\u7EC4\n *\n * @example\n * ```ts\n * const requiredScopes = getRequiredScopesForActions([\n * \"feishu_calendar_event.create\",\n * \"feishu_calendar_event.list\"\n * ]);\n * // \u8FD4\u56DE\u4E24\u4E2A\u52A8\u4F5C\u7684\u6240\u6709\u552F\u4E00 scopes\n * ```\n */\nexport function getRequiredScopesForActions(toolActions: ToolActionKey[]): string[] {\n const scopesSet = new Set<string>();\n\n for (const action of toolActions) {\n const scopes = getRequiredScopes(action);\n scopes.forEach((scope) => scopesSet.add(scope));\n }\n\n return Array.from(scopesSet).sort();\n}\n\n/**\n * \u68C0\u67E5\u5DE5\u5177\u52A8\u4F5C\u662F\u5426\u9700\u8981\u4EFB\u4F55 scope\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\n * @returns \u5982\u679C\u52A8\u4F5C\u9700\u8981\u81F3\u5C11\u4E00\u4E2A scope \u5219\u8FD4\u56DE true\n *\n * @example\n * ```ts\n * hasRequiredScopes(\"feishu_calendar_event.create\"); // true\n * hasRequiredScopes(\"feishu_sheets_spreadsheet.create\"); // false (\u7A7A\u6570\u7EC4)\n * ```\n */\nexport function hasRequiredScopes(toolAction: ToolActionKey): boolean {\n return getRequiredScopes(toolAction).length > 0;\n}\n\n/**\n * \u83B7\u53D6\u9700\u8981\u7279\u5B9A scope \u7684\u6240\u6709\u5DE5\u5177\u52A8\u4F5C\n *\n * @param scope - Scope \u5B57\u7B26\u4E32\uFF08\u4F8B\u5982 \"calendar:calendar.event:create\"\uFF09\n * @returns \u9700\u8981\u6B64 scope \u7684\u5DE5\u5177\u52A8\u4F5C\u952E\u6570\u7EC4\n *\n * @example\n * ```ts\n * const actions = getActionsForScope(\"calendar:calendar.event:create\");\n * // \u8FD4\u56DE: [\"feishu_calendar_event.create\"]\n * ```\n */\nexport function getActionsForScope(scope: string): ToolActionKey[] {\n const actions: ToolActionKey[] = [];\n\n for (const [action, scopes] of Object.entries(TOOL_SCOPES)) {\n if (scopes.includes(scope)) {\n actions.push(action as ToolActionKey);\n }\n }\n\n return actions;\n}\n\n// ===== \u51FD\u6570\uFF1AApp Scope \u68C0\u67E5 =====\n\n/**\n * \u68C0\u67E5\u5E94\u7528\u662F\u5426\u5F00\u901A\u4E86\u5DE5\u5177\u52A8\u4F5C\u6240\u9700\u7684\u6240\u6709\u6743\u9650\uFF08App Granted Scopes\uFF09\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\n * @param appGrantedScopes - \u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u96C6\u5408\uFF08\u6765\u81EA\u5F00\u653E\u5E73\u53F0\uFF09\n * @returns \u5982\u679C\u5E94\u7528\u5DF2\u5F00\u901A\u6240\u6709\u5FC5\u9700\u7684 scopes \u5219\u8FD4\u56DE true\n *\n * @example\n * ```ts\n * const appScopes = new Set([\n * \"calendar:calendar.event:create\",\n * \"calendar:calendar.event:update\"\n * ]);\n * checkAppScopes(\"feishu_calendar_event.create\", appScopes); // true\n *\n * const partialAppScopes = new Set([\"calendar:calendar.event:create\"]);\n * checkAppScopes(\"feishu_calendar_event.create\", partialAppScopes); // false\n * ```\n */\nexport function checkAppScopes(toolAction: ToolActionKey, appGrantedScopes: Set<string> | string[]): boolean {\n const requiredScopes = getRequiredScopes(toolAction);\n\n // \u5982\u679C\u4E0D\u9700\u8981\u4EFB\u4F55 scope\uFF0C\u5219\u603B\u662F\u6EE1\u8DB3\u8981\u6C42\n if (requiredScopes.length === 0) {\n return true;\n }\n\n const grantedSet = Array.isArray(appGrantedScopes) ? new Set(appGrantedScopes) : appGrantedScopes;\n\n return requiredScopes.every((scope) => grantedSet.has(scope));\n}\n\n/**\n * \u83B7\u53D6\u5E94\u7528\u672A\u5F00\u901A\u7684 scopes\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\n * @param appGrantedScopes - \u5E94\u7528\u5DF2\u5F00\u901A\u7684 scope \u96C6\u5408\n * @returns \u5E94\u7528\u672A\u5F00\u901A\u7684 scope \u5B57\u7B26\u4E32\u6570\u7EC4\n *\n * @example\n * ```ts\n * const appScopes = new Set([\"calendar:calendar.event:create\"]);\n * const missing = getMissingAppScopes(\"feishu_calendar_event.create\", appScopes);\n * // \u8FD4\u56DE: [\"calendar:calendar.event:update\"]\n * ```\n */\nexport function getMissingAppScopes(toolAction: ToolActionKey, appGrantedScopes: Set<string> | string[]): string[] {\n const requiredScopes = getRequiredScopes(toolAction);\n const grantedSet = Array.isArray(appGrantedScopes) ? new Set(appGrantedScopes) : appGrantedScopes;\n\n return requiredScopes.filter((scope) => !grantedSet.has(scope));\n}\n\n// ===== \u51FD\u6570\uFF1AUser Scope \u68C0\u67E5 =====\n\n/**\n * \u68C0\u67E5\u7528\u6237\u662F\u5426\u6388\u6743\u4E86\u5DE5\u5177\u52A8\u4F5C\u6240\u9700\u7684\u6240\u6709\u6743\u9650\uFF08User Granted Scopes\uFF09\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\n * @param userGrantedScopes - \u7528\u6237\u5DF2\u6388\u6743\u7684 scope \u96C6\u5408\uFF08\u6765\u81EA OAuth token\uFF09\n * @returns \u5982\u679C\u7528\u6237\u5DF2\u6388\u6743\u6240\u6709\u5FC5\u9700\u7684 scopes \u5219\u8FD4\u56DE true\n *\n * @example\n * ```ts\n * const userScopes = new Set([\n * \"calendar:calendar.event:create\",\n * \"calendar:calendar.event:update\"\n * ]);\n * checkUserScopes(\"feishu_calendar_event.create\", userScopes); // true\n *\n * const partialUserScopes = new Set([\"calendar:calendar.event:create\"]);\n * checkUserScopes(\"feishu_calendar_event.create\", partialUserScopes); // false\n * ```\n */\nexport function checkUserScopes(toolAction: ToolActionKey, userGrantedScopes: Set<string> | string[]): boolean {\n const requiredScopes = getRequiredScopes(toolAction);\n\n // \u5982\u679C\u4E0D\u9700\u8981\u4EFB\u4F55 scope\uFF0C\u5219\u603B\u662F\u6EE1\u8DB3\u8981\u6C42\n if (requiredScopes.length === 0) {\n return true;\n }\n\n const grantedSet = Array.isArray(userGrantedScopes) ? new Set(userGrantedScopes) : userGrantedScopes;\n\n return requiredScopes.every((scope) => grantedSet.has(scope));\n}\n\n/**\n * \u83B7\u53D6\u7528\u6237\u672A\u6388\u6743\u7684 scopes\n *\n * @param toolAction - \u5DE5\u5177\u52A8\u4F5C\u952E\n * @param userGrantedScopes - \u7528\u6237\u5DF2\u6388\u6743\u7684 scope \u96C6\u5408\n * @returns \u7528\u6237\u672A\u6388\u6743\u7684 scope \u5B57\u7B26\u4E32\u6570\u7EC4\n *\n * @example\n * ```ts\n * const userScopes = new Set([\"calendar:calendar.event:create\"]);\n * const missing = getMissingUserScopes(\"feishu_calendar_event.create\", userScopes);\n * // \u8FD4\u56DE: [\"calendar:calendar.event:update\"]\n * ```\n */\nexport function getMissingUserScopes(toolAction: ToolActionKey, userGrantedScopes: Set<string> | string[]): string[] {\n const requiredScopes = getRequiredScopes(toolAction);\n const grantedSet = Array.isArray(userGrantedScopes) ? new Set(userGrantedScopes) : userGrantedScopes;\n\n return requiredScopes.filter((scope) => !grantedSet.has(scope));\n}\n"],
5
+ "mappings": "AAyCA,SAAoD,mBAAmB;AAqBhE,SAAS,kBAAkB,YAAqC;AACrE,SAAO,YAAY,UAAU,KAAK,CAAC;AACrC;AAiBO,SAAS,4BAA4B,aAAwC;AAClF,QAAM,YAAY,oBAAI,IAAY;AAElC,aAAW,UAAU,aAAa;AAChC,UAAM,SAAS,kBAAkB,MAAM;AACvC,WAAO,QAAQ,CAAC,UAAU,UAAU,IAAI,KAAK,CAAC;AAAA,EAChD;AAEA,SAAO,MAAM,KAAK,SAAS,EAAE,KAAK;AACpC;AAcO,SAAS,kBAAkB,YAAoC;AACpE,SAAO,kBAAkB,UAAU,EAAE,SAAS;AAChD;AAcO,SAAS,mBAAmB,OAAgC;AACjE,QAAM,UAA2B,CAAC;AAElC,aAAW,CAAC,QAAQ,MAAM,KAAK,OAAO,QAAQ,WAAW,GAAG;AAC1D,QAAI,OAAO,SAAS,KAAK,GAAG;AAC1B,cAAQ,KAAK,MAAuB;AAAA,IACtC;AAAA,EACF;AAEA,SAAO;AACT;AAuBO,SAAS,eAAe,YAA2B,kBAAmD;AAC3G,QAAM,iBAAiB,kBAAkB,UAAU;AAGnD,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,QAAQ,gBAAgB,IAAI,IAAI,IAAI,gBAAgB,IAAI;AAEjF,SAAO,eAAe,MAAM,CAAC,UAAU,WAAW,IAAI,KAAK,CAAC;AAC9D;AAgBO,SAAS,oBAAoB,YAA2B,kBAAoD;AACjH,QAAM,iBAAiB,kBAAkB,UAAU;AACnD,QAAM,aAAa,MAAM,QAAQ,gBAAgB,IAAI,IAAI,IAAI,gBAAgB,IAAI;AAEjF,SAAO,eAAe,OAAO,CAAC,UAAU,CAAC,WAAW,IAAI,KAAK,CAAC;AAChE;AAuBO,SAAS,gBAAgB,YAA2B,mBAAoD;AAC7G,QAAM,iBAAiB,kBAAkB,UAAU;AAGnD,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM,QAAQ,iBAAiB,IAAI,IAAI,IAAI,iBAAiB,IAAI;AAEnF,SAAO,eAAe,MAAM,CAAC,UAAU,WAAW,IAAI,KAAK,CAAC;AAC9D;AAgBO,SAAS,qBAAqB,YAA2B,mBAAqD;AACnH,QAAM,iBAAiB,kBAAkB,UAAU;AACnD,QAAM,aAAa,MAAM,QAAQ,iBAAiB,IAAI,IAAI,IAAI,iBAAiB,IAAI;AAEnF,SAAO,eAAe,OAAO,CAAC,UAAU,CAAC,WAAW,IAAI,KAAK,CAAC;AAChE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,118 @@
1
+ import { getEnabledLarkAccounts } from "./accounts";
2
+ function checkMultiAccountIsolation(cfg) {
3
+ const accounts = getEnabledLarkAccounts(cfg);
4
+ if (accounts.length <= 1) return { mode: "not-applicable" };
5
+ const appIds = new Set(accounts.map((a) => a.configured ? a.appId : void 0).filter((id) => !!id));
6
+ if (appIds.size <= 1) return { mode: "not-applicable" };
7
+ const feishuBindings = cfg.bindings?.filter((b) => b.match?.channel === "feishu" && b.match?.accountId);
8
+ if (!feishuBindings || feishuBindings.length === 0) {
9
+ return { mode: "shared-implicit", accounts, unboundAccounts: accounts };
10
+ }
11
+ const boundAccountIds = new Set(feishuBindings.map((b) => b.match.accountId));
12
+ const unboundAccounts = accounts.filter((a) => !boundAccountIds.has(a.accountId));
13
+ if (unboundAccounts.length > 0) {
14
+ return { mode: "shared-implicit", accounts, unboundAccounts };
15
+ }
16
+ const agentIds = new Set(feishuBindings.map((b) => b.agentId));
17
+ if (agentIds.size === 1) {
18
+ return {
19
+ mode: "shared-explicit",
20
+ accounts,
21
+ sharedAgentId: agentIds.values().next().value
22
+ };
23
+ }
24
+ return { mode: "isolated", accounts };
25
+ }
26
+ function accountNames(accounts) {
27
+ return accounts.map((a) => a.name ?? a.accountId).join("\u3001");
28
+ }
29
+ function isMultiTenant(cfg) {
30
+ const accounts = getEnabledLarkAccounts(cfg);
31
+ if (accounts.length <= 1) return false;
32
+ const appIds = new Set(accounts.map((a) => a.configured ? a.appId : void 0).filter((id) => !!id));
33
+ return appIds.size > 1;
34
+ }
35
+ const RECOMMENDED_DM_SCOPE = "per-account-channel-peer";
36
+ function needsDmScopeFix(cfg) {
37
+ if (!isMultiTenant(cfg)) return false;
38
+ return cfg.session?.dmScope !== RECOMMENDED_DM_SCOPE;
39
+ }
40
+ function getDmScopeFixCommand(cfg) {
41
+ if (!needsDmScopeFix(cfg)) return null;
42
+ return `openclaw config set session.dmScope "${RECOMMENDED_DM_SCOPE}"`;
43
+ }
44
+ function formatDmScopeWarning() {
45
+ return "\u26A0\uFE0F **\u79C1\u804A\u6D88\u606F\u4E32\u6DF7**\n\n\u5F53\u540C\u4E00\u4E2A\u7528\u6237\u540C\u65F6\u4F7F\u7528\u591A\u4E2A\u673A\u5668\u4EBA\u65F6\uFF0C\u4E0D\u540C\u673A\u5668\u4EBA\u7684\u79C1\u804A\u6D88\u606F\u4F1A\u6DF7\u5728\u540C\u4E00\u6BB5\u5BF9\u8BDD\u91CC\uFF0C\u5BFC\u81F4 AI \u65E0\u6CD5\u533A\u5206\u7528\u6237\u5728\u8DDF\u54EA\u4E2A\u673A\u5668\u4EBA\u8BF4\u8BDD\u3002";
46
+ }
47
+ function formatIsolationWarning(status, cfg) {
48
+ const sections = [];
49
+ if (status.mode === "shared-implicit") {
50
+ const names = accountNames(status.accounts);
51
+ sections.push(
52
+ `\u26A0\uFE0F **\u591A\u4E2A\u673A\u5668\u4EBA\u5171\u7528\u8BB0\u5FC6\uFF0C\u5BF9\u8BDD\u5185\u5BB9\u53EF\u80FD\u4E92\u76F8\u53EF\u89C1**
53
+
54
+ \u5F53\u524D ${status.accounts.length} \u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\uFF08${names}\uFF09\u5171\u7528\u540C\u4E00\u4E2A AI \u8BB0\u5FC6\u3002
55
+ \u7528\u6237 A \u8DDF\u673A\u5668\u4EBA\u300C${status.accounts[0].name ?? status.accounts[0].accountId}\u300D\u8BF4\u7684\u8BDD\uFF0C\u53EF\u80FD\u51FA\u73B0\u5728\u673A\u5668\u4EBA\u300C${status.accounts[1]?.name ?? status.accounts[1]?.accountId ?? "..."}\u300D\u7684\u56DE\u590D\u4E2D\u3002
56
+
57
+ \u{1F449} \u53D1\u9001 **/feishu isolate** \u4E00\u952E\u67E5\u770B\u4FEE\u590D\u65B9\u6848`
58
+ );
59
+ }
60
+ if (cfg && needsDmScopeFix(cfg)) {
61
+ sections.push(formatDmScopeWarning() + "\n\n\u{1F449} \u53D1\u9001 **/feishu isolate** \u4E00\u952E\u67E5\u770B\u4FEE\u590D\u65B9\u6848");
62
+ }
63
+ if (sections.length === 0) return null;
64
+ return sections.join("\n\n---\n\n");
65
+ }
66
+ function generateIsolationFixCommands(cfg) {
67
+ const status = checkMultiAccountIsolation(cfg);
68
+ if (status.mode !== "shared-implicit") return null;
69
+ const accounts = status.accounts;
70
+ const commands = [];
71
+ const agentsList = accounts.map((a) => ({
72
+ id: `feishu-${a.accountId}`,
73
+ name: `\u98DE\u4E66 ${a.name ?? a.accountId}`
74
+ }));
75
+ commands.push(`openclaw config set agents.list '${JSON.stringify(agentsList)}' --json`);
76
+ const bindings = accounts.map((a) => ({
77
+ match: { channel: "feishu", accountId: a.accountId },
78
+ agentId: `feishu-${a.accountId}`
79
+ }));
80
+ commands.push(`openclaw config set bindings '${JSON.stringify(bindings)}' --json`);
81
+ const dmScopeCmd = getDmScopeFixCommand(cfg);
82
+ if (dmScopeCmd) commands.push(dmScopeCmd);
83
+ commands.push("openclaw gateway restart");
84
+ const previewLines = accounts.map((a) => ` ${a.name ?? a.accountId} \u2192 \u72EC\u7ACB\u8BB0\u5FC6\uFF08feishu-${a.accountId}\uFF09`);
85
+ return { commands, preview: previewLines.join("\n") };
86
+ }
87
+ function generateSharedAgentCommands(cfg) {
88
+ const status = checkMultiAccountIsolation(cfg);
89
+ if (status.mode !== "shared-implicit") return null;
90
+ const accounts = status.accounts;
91
+ const commands = [];
92
+ const bindings = accounts.map((a) => ({
93
+ match: { channel: "feishu", accountId: a.accountId },
94
+ agentId: "default"
95
+ }));
96
+ commands.push(`openclaw config set bindings '${JSON.stringify(bindings)}' --json`);
97
+ const dmScopeCmd = getDmScopeFixCommand(cfg);
98
+ if (dmScopeCmd) commands.push(dmScopeCmd);
99
+ commands.push("openclaw gateway restart");
100
+ const previewLines = accounts.map((a) => ` ${a.name ?? a.accountId} \u2192 \u5171\u7528\u8BB0\u5FC6\uFF08default\uFF09`);
101
+ return { commands, preview: previewLines.join("\n") };
102
+ }
103
+ function collectIsolationWarnings(_cfg) {
104
+ return [];
105
+ }
106
+ function emitSecurityWarnings(_cfg, _logger) {
107
+ }
108
+ export {
109
+ checkMultiAccountIsolation,
110
+ collectIsolationWarnings,
111
+ emitSecurityWarnings,
112
+ formatIsolationWarning,
113
+ generateIsolationFixCommands,
114
+ generateSharedAgentCommands,
115
+ getDmScopeFixCommand,
116
+ needsDmScopeFix
117
+ };
118
+ //# sourceMappingURL=security-check.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/security-check.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Multi-account isolation checks.\n *\n * Detects potentially unsafe configurations where multiple Feishu accounts\n * belonging to different tenants (different appId) share the default agent\n * without proper isolation via agents + bindings.\n */\n\nimport type { ClawdbotConfig } from 'openclaw/plugin-sdk';\nimport { getEnabledLarkAccounts } from './accounts';\nimport type { LarkAccount } from './types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type IsolationStatus =\n /** Single account / same appId / no multi-tenant concern */\n | { mode: 'not-applicable' }\n /** All accounts have bindings pointing to different agents */\n | { mode: 'isolated'; accounts: LarkAccount[] }\n /** All accounts have bindings but share the same agent (explicit choice) */\n | { mode: 'shared-explicit'; accounts: LarkAccount[]; sharedAgentId: string }\n /** Some or all accounts have no bindings \u2014 implicit sharing, risky */\n | { mode: 'shared-implicit'; accounts: LarkAccount[]; unboundAccounts: LarkAccount[] };\n\n// ---------------------------------------------------------------------------\n// Check logic\n// ---------------------------------------------------------------------------\n\n/**\n * Diagnose whether multiple enabled accounts from different tenants\n * are properly isolated via agent bindings.\n */\nexport function checkMultiAccountIsolation(cfg: ClawdbotConfig): IsolationStatus {\n const accounts = getEnabledLarkAccounts(cfg);\n if (accounts.length <= 1) return { mode: 'not-applicable' };\n\n const appIds = new Set(accounts.map((a) => (a.configured ? a.appId : undefined)).filter((id): id is string => !!id));\n if (appIds.size <= 1) return { mode: 'not-applicable' };\n\n const feishuBindings = cfg.bindings?.filter((b) => b.match?.channel === 'feishu' && b.match?.accountId);\n\n if (!feishuBindings || feishuBindings.length === 0) {\n return { mode: 'shared-implicit', accounts, unboundAccounts: accounts };\n }\n\n const boundAccountIds = new Set(feishuBindings.map((b) => b.match!.accountId!));\n const unboundAccounts = accounts.filter((a) => !boundAccountIds.has(a.accountId));\n\n if (unboundAccounts.length > 0) {\n return { mode: 'shared-implicit', accounts, unboundAccounts };\n }\n\n const agentIds = new Set(feishuBindings.map((b) => b.agentId));\n if (agentIds.size === 1) {\n return {\n mode: 'shared-explicit',\n accounts,\n sharedAgentId: agentIds.values().next().value!,\n };\n }\n\n return { mode: 'isolated', accounts };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction accountNames(accounts: LarkAccount[]): string {\n return accounts.map((a) => a.name ?? a.accountId).join('\u3001');\n}\n\nfunction isMultiTenant(cfg: ClawdbotConfig): boolean {\n const accounts = getEnabledLarkAccounts(cfg);\n if (accounts.length <= 1) return false;\n const appIds = new Set(accounts.map((a) => (a.configured ? a.appId : undefined)).filter((id): id is string => !!id));\n return appIds.size > 1;\n}\n\n// ---------------------------------------------------------------------------\n// Session dmScope\n// ---------------------------------------------------------------------------\n\nconst RECOMMENDED_DM_SCOPE = 'per-account-channel-peer';\n\n/**\n * Check whether `session.dmScope` is set to per-account isolation.\n *\n * Without this setting, different bots talking to the same user share\n * the same session \u2014 even if agent bindings are configured.\n */\nexport function needsDmScopeFix(cfg: ClawdbotConfig): boolean {\n if (!isMultiTenant(cfg)) return false;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (cfg as any).session?.dmScope !== RECOMMENDED_DM_SCOPE;\n}\n\n/** Return the fix command string, or null if not needed. */\nexport function getDmScopeFixCommand(cfg: ClawdbotConfig): string | null {\n if (!needsDmScopeFix(cfg)) return null;\n return `openclaw config set session.dmScope \"${RECOMMENDED_DM_SCOPE}\"`;\n}\n\n/** User-facing dmScope warning block (markdown). */\nfunction formatDmScopeWarning(): string {\n return (\n '\u26A0\uFE0F **\u79C1\u804A\u6D88\u606F\u4E32\u6DF7**\\n\\n' +\n '\u5F53\u540C\u4E00\u4E2A\u7528\u6237\u540C\u65F6\u4F7F\u7528\u591A\u4E2A\u673A\u5668\u4EBA\u65F6\uFF0C\u4E0D\u540C\u673A\u5668\u4EBA\u7684\u79C1\u804A\u6D88\u606F\u4F1A\u6DF7\u5728\u540C\u4E00\u6BB5\u5BF9\u8BDD\u91CC\uFF0C' +\n '\u5BFC\u81F4 AI \u65E0\u6CD5\u533A\u5206\u7528\u6237\u5728\u8DDF\u54EA\u4E2A\u673A\u5668\u4EBA\u8BF4\u8BDD\u3002'\n );\n}\n\n// ---------------------------------------------------------------------------\n// Warning text for /feishu doctor & /feishu start\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a combined warning block for doctor / start.\n * Returns null when everything is fine.\n */\nexport function formatIsolationWarning(status: IsolationStatus, cfg?: ClawdbotConfig): string | null {\n const sections: string[] = [];\n\n // Agent sharing warning\n if (status.mode === 'shared-implicit') {\n const names = accountNames(status.accounts);\n sections.push(\n `\u26A0\uFE0F **\u591A\u4E2A\u673A\u5668\u4EBA\u5171\u7528\u8BB0\u5FC6\uFF0C\u5BF9\u8BDD\u5185\u5BB9\u53EF\u80FD\u4E92\u76F8\u53EF\u89C1**\\n\\n` +\n `\u5F53\u524D ${status.accounts.length} \u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\uFF08${names}\uFF09\u5171\u7528\u540C\u4E00\u4E2A AI \u8BB0\u5FC6\u3002\\n` +\n `\u7528\u6237 A \u8DDF\u673A\u5668\u4EBA\u300C${status.accounts[0].name ?? status.accounts[0].accountId}\u300D\u8BF4\u7684\u8BDD\uFF0C` +\n `\u53EF\u80FD\u51FA\u73B0\u5728\u673A\u5668\u4EBA\u300C${status.accounts[1]?.name ?? status.accounts[1]?.accountId ?? '...'}\u300D\u7684\u56DE\u590D\u4E2D\u3002\\n\\n` +\n `\uD83D\uDC49 \u53D1\u9001 **/feishu isolate** \u4E00\u952E\u67E5\u770B\u4FEE\u590D\u65B9\u6848`,\n );\n }\n\n // dmScope warning\n if (cfg && needsDmScopeFix(cfg)) {\n sections.push(formatDmScopeWarning() + '\\n\\n' + '\uD83D\uDC49 \u53D1\u9001 **/feishu isolate** \u4E00\u952E\u67E5\u770B\u4FEE\u590D\u65B9\u6848');\n }\n\n if (sections.length === 0) return null;\n return sections.join('\\n\\n---\\n\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Fix command generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate `openclaw config set` commands for per-account isolation.\n */\nexport function generateIsolationFixCommands(cfg: ClawdbotConfig): { commands: string[]; preview: string } | null {\n const status = checkMultiAccountIsolation(cfg);\n if (status.mode !== 'shared-implicit') return null;\n\n const accounts = status.accounts;\n const commands: string[] = [];\n\n const agentsList = accounts.map((a) => ({\n id: `feishu-${a.accountId}`,\n name: `\u98DE\u4E66 ${a.name ?? a.accountId}`,\n }));\n commands.push(`openclaw config set agents.list '${JSON.stringify(agentsList)}' --json`);\n\n const bindings = accounts.map((a) => ({\n match: { channel: 'feishu', accountId: a.accountId },\n agentId: `feishu-${a.accountId}`,\n }));\n commands.push(`openclaw config set bindings '${JSON.stringify(bindings)}' --json`);\n\n const dmScopeCmd = getDmScopeFixCommand(cfg);\n if (dmScopeCmd) commands.push(dmScopeCmd);\n\n commands.push('openclaw gateway restart');\n\n const previewLines = accounts.map((a) => ` ${a.name ?? a.accountId} \u2192 \u72EC\u7ACB\u8BB0\u5FC6\uFF08feishu-${a.accountId}\uFF09`);\n\n return { commands, preview: previewLines.join('\\n') };\n}\n\n/**\n * Generate commands for explicitly sharing the same agent across accounts.\n */\nexport function generateSharedAgentCommands(cfg: ClawdbotConfig): { commands: string[]; preview: string } | null {\n const status = checkMultiAccountIsolation(cfg);\n if (status.mode !== 'shared-implicit') return null;\n\n const accounts = status.accounts;\n const commands: string[] = [];\n\n const bindings = accounts.map((a) => ({\n match: { channel: 'feishu', accountId: a.accountId },\n agentId: 'default',\n }));\n commands.push(`openclaw config set bindings '${JSON.stringify(bindings)}' --json`);\n\n const dmScopeCmd = getDmScopeFixCommand(cfg);\n if (dmScopeCmd) commands.push(dmScopeCmd);\n\n commands.push('openclaw gateway restart');\n\n const previewLines = accounts.map((a) => ` ${a.name ?? a.accountId} \u2192 \u5171\u7528\u8BB0\u5FC6\uFF08default\uFF09`);\n\n return { commands, preview: previewLines.join('\\n') };\n}\n\n// ---------------------------------------------------------------------------\n// collectWarnings adapter (for SDK security.collectWarnings)\n// ---------------------------------------------------------------------------\n\nexport function collectIsolationWarnings(_cfg: ClawdbotConfig): string[] {\n // TODO: \u4EA7\u54C1\u660E\u786E\u591A\u8D26\u53F7\u9694\u79BB\u65B9\u6848\u540E\u518D\u900F\u51FA\u544A\u8B66\n return [];\n}\n\n// ---------------------------------------------------------------------------\n// Startup log\n// ---------------------------------------------------------------------------\n\nexport function emitSecurityWarnings(\n _cfg: ClawdbotConfig,\n _logger: { warn?: (msg: string) => void; info?: (msg: string) => void },\n): void {\n // TODO: \u4EA7\u54C1\u660E\u786E\u591A\u8D26\u53F7\u9694\u79BB\u65B9\u6848\u540E\u518D\u900F\u51FA\u544A\u8B66\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,37 @@
1
+ const hooks = /* @__PURE__ */ new Map();
2
+ function _resetShutdownHooks() {
3
+ hooks.clear();
4
+ }
5
+ function registerShutdownHook(key, cleanup) {
6
+ hooks.set(key, cleanup);
7
+ return () => {
8
+ hooks.delete(key);
9
+ };
10
+ }
11
+ async function drainShutdownHooks(opts) {
12
+ if (hooks.size === 0) return;
13
+ const log = opts?.log;
14
+ const deadline = opts?.deadlineMs ?? 5e3;
15
+ log?.(`graceful shutdown: draining ${hooks.size} cleanup hook(s)`);
16
+ const entries = Array.from(hooks.entries());
17
+ hooks.clear();
18
+ const promises = entries.map(async ([key, cleanup]) => {
19
+ try {
20
+ await cleanup();
21
+ log?.(`graceful shutdown: hook "${key}" done`);
22
+ } catch (err) {
23
+ log?.(`graceful shutdown: hook "${key}" failed: ${String(err)}`);
24
+ }
25
+ });
26
+ let timer;
27
+ const timeoutPromise = new Promise((resolve) => {
28
+ timer = setTimeout(resolve, deadline);
29
+ });
30
+ await Promise.race([Promise.allSettled(promises).then(() => clearTimeout(timer)), timeoutPromise]);
31
+ }
32
+ export {
33
+ _resetShutdownHooks,
34
+ drainShutdownHooks,
35
+ registerShutdownHook
36
+ };
37
+ //# sourceMappingURL=shutdown-hooks.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/shutdown-hooks.ts"],
4
+ "sourcesContent": ["// SPDX-License-Identifier: MIT\n\n/**\n * Process-level graceful shutdown hook registry.\n *\n * Provides a singleton Map of async cleanup callbacks, drained\n * during graceful shutdown by the channel monitor.\n */\n\nconst hooks = new Map<string, () => Promise<void>>();\n\n/** @internal \u2014 test-only reset. */\nexport function _resetShutdownHooks(): void {\n hooks.clear();\n}\n\n/**\n * Register a cleanup callback to run during graceful shutdown.\n *\n * @param key - Unique identifier for this hook (duplicate keys overwrite).\n * @param cleanup - Async function to execute on shutdown.\n * @returns An unregister function \u2014 call it when the resource is\n * released normally (e.g. card streaming completes).\n */\nexport function registerShutdownHook(key: string, cleanup: () => Promise<void>): () => void {\n hooks.set(key, cleanup);\n return () => {\n hooks.delete(key);\n };\n}\n\n/**\n * Drain all registered shutdown hooks (best-effort, bounded by deadline).\n *\n * @param opts - Optional configuration.\n * @param opts.deadlineMs - Maximum time to wait for all hooks (default 5000).\n * @param opts.log - Logger function for progress/error output.\n */\nexport async function drainShutdownHooks(opts?: {\n deadlineMs?: number;\n log?: (...args: unknown[]) => void;\n}): Promise<void> {\n if (hooks.size === 0) return;\n\n const log = opts?.log;\n const deadline = opts?.deadlineMs ?? 5000;\n\n log?.(`graceful shutdown: draining ${hooks.size} cleanup hook(s)`);\n\n const entries = Array.from(hooks.entries());\n hooks.clear();\n\n const promises = entries.map(async ([key, cleanup]) => {\n try {\n await cleanup();\n log?.(`graceful shutdown: hook \"${key}\" done`);\n } catch (err) {\n log?.(`graceful shutdown: hook \"${key}\" failed: ${String(err)}`);\n }\n });\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeoutPromise = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, deadline);\n });\n\n await Promise.race([Promise.allSettled(promises).then(() => clearTimeout(timer)), timeoutPromise]);\n}\n"],
5
+ "mappings": "AASA,MAAM,QAAQ,oBAAI,IAAiC;AAG5C,SAAS,sBAA4B;AAC1C,QAAM,MAAM;AACd;AAUO,SAAS,qBAAqB,KAAa,SAA0C;AAC1F,QAAM,IAAI,KAAK,OAAO;AACtB,SAAO,MAAM;AACX,UAAM,OAAO,GAAG;AAAA,EAClB;AACF;AASA,eAAsB,mBAAmB,MAGvB;AAChB,MAAI,MAAM,SAAS,EAAG;AAEtB,QAAM,MAAM,MAAM;AAClB,QAAM,WAAW,MAAM,cAAc;AAErC,QAAM,+BAA+B,MAAM,IAAI,kBAAkB;AAEjE,QAAM,UAAU,MAAM,KAAK,MAAM,QAAQ,CAAC;AAC1C,QAAM,MAAM;AAEZ,QAAM,WAAW,QAAQ,IAAI,OAAO,CAAC,KAAK,OAAO,MAAM;AACrD,QAAI;AACF,YAAM,QAAQ;AACd,YAAM,4BAA4B,GAAG,QAAQ;AAAA,IAC/C,SAAS,KAAK;AACZ,YAAM,4BAA4B,GAAG,aAAa,OAAO,GAAG,CAAC,EAAE;AAAA,IACjE;AAAA,EACF,CAAC;AAED,MAAI;AACJ,QAAM,iBAAiB,IAAI,QAAc,CAAC,YAAY;AACpD,YAAQ,WAAW,SAAS,QAAQ;AAAA,EACtC,CAAC;AAED,QAAM,QAAQ,KAAK,CAAC,QAAQ,WAAW,QAAQ,EAAE,KAAK,MAAM,aAAa,KAAK,CAAC,GAAG,cAAc,CAAC;AACnG;",
6
+ "names": []
7
+ }
@@ -0,0 +1,55 @@
1
+ const CHAT_PREFIX = "oc_";
2
+ const OPEN_ID_PREFIX = "ou_";
3
+ const TAG_CHAT = "chat:";
4
+ const TAG_USER = "user:";
5
+ const TAG_OPEN_ID = "open_id:";
6
+ const TAG_FEISHU = "feishu:";
7
+ function detectIdType(id) {
8
+ if (!id) return null;
9
+ if (id.startsWith(CHAT_PREFIX)) return "chat_id";
10
+ if (id.startsWith(OPEN_ID_PREFIX)) return "open_id";
11
+ if (/^[a-zA-Z0-9]+$/.test(id)) return "user_id";
12
+ return null;
13
+ }
14
+ function normalizeFeishuTarget(raw) {
15
+ if (!raw) return null;
16
+ const trimmed = raw.trim();
17
+ if (!trimmed) return null;
18
+ if (trimmed.startsWith(TAG_FEISHU)) {
19
+ const inner = trimmed.slice(TAG_FEISHU.length).trim();
20
+ if (inner) return inner;
21
+ }
22
+ if (trimmed.startsWith(TAG_CHAT)) return trimmed.slice(TAG_CHAT.length);
23
+ if (trimmed.startsWith(TAG_USER)) return trimmed.slice(TAG_USER.length);
24
+ if (trimmed.startsWith(TAG_OPEN_ID)) return trimmed.slice(TAG_OPEN_ID.length);
25
+ return trimmed;
26
+ }
27
+ function formatFeishuTarget(id, type) {
28
+ const resolved = type ?? detectIdType(id);
29
+ if (resolved === "chat_id") return `${TAG_CHAT}${id}`;
30
+ return `${TAG_USER}${id}`;
31
+ }
32
+ function resolveReceiveIdType(id) {
33
+ if (id.startsWith(CHAT_PREFIX)) return "chat_id";
34
+ if (id.startsWith(OPEN_ID_PREFIX)) return "open_id";
35
+ return "open_id";
36
+ }
37
+ function normalizeMessageId(messageId) {
38
+ if (!messageId) return void 0;
39
+ const colonIndex = messageId.indexOf(":");
40
+ if (colonIndex >= 0) return messageId.slice(0, colonIndex);
41
+ return messageId;
42
+ }
43
+ function looksLikeFeishuId(raw) {
44
+ if (!raw) return false;
45
+ return raw.startsWith(TAG_CHAT) || raw.startsWith(TAG_USER) || raw.startsWith(TAG_OPEN_ID) || raw.startsWith(CHAT_PREFIX) || raw.startsWith(OPEN_ID_PREFIX);
46
+ }
47
+ export {
48
+ detectIdType,
49
+ formatFeishuTarget,
50
+ looksLikeFeishuId,
51
+ normalizeFeishuTarget,
52
+ normalizeMessageId,
53
+ resolveReceiveIdType
54
+ };
55
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/targets.ts"],
4
+ "sourcesContent": ["/**\n * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates\n * SPDX-License-Identifier: MIT\n *\n * Feishu target ID parsing and formatting utilities.\n *\n * Feishu uses several namespaced identifier prefixes:\n * - `oc_*` -- chat (group / DM) IDs\n * - `ou_*` -- open user IDs\n * - plain alphanumeric strings -- user IDs from the tenant directory\n *\n * This module provides helpers to detect, normalise, and format these IDs\n * for both internal routing and outbound Feishu API calls.\n */\n\nimport type { FeishuIdType } from './types';\n\n// ---------------------------------------------------------------------------\n// Known prefix patterns\n// ---------------------------------------------------------------------------\n\nconst CHAT_PREFIX = 'oc_';\nconst OPEN_ID_PREFIX = 'ou_';\n\n// Canonical routing prefixes used inside OpenClaw (not Feishu-native).\nconst TAG_CHAT = 'chat:';\nconst TAG_USER = 'user:';\nconst TAG_OPEN_ID = 'open_id:';\n\n// Feishu channel prefix (used by SDK for some routing scenarios).\nconst TAG_FEISHU = 'feishu:';\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect the Feishu ID type from a raw identifier string.\n *\n * Returns `null` when the string does not match any known pattern.\n */\nexport function detectIdType(id: string): FeishuIdType | null {\n if (!id) return null;\n if (id.startsWith(CHAT_PREFIX)) return 'chat_id';\n if (id.startsWith(OPEN_ID_PREFIX)) return 'open_id';\n // Plain alphanumeric strings (no prefix) are treated as tenant user IDs.\n if (/^[a-zA-Z0-9]+$/.test(id)) return 'user_id';\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * Strip OpenClaw routing prefixes (`chat:`, `user:`, `open_id:`) from a\n * raw target string, returning the bare Feishu identifier.\n *\n * Returns `null` when the input is empty or falsy.\n */\nexport function normalizeFeishuTarget(raw: string): string | null {\n if (!raw) return null;\n\n const trimmed = raw.trim();\n if (!trimmed) return null;\n\n // Handle Feishu channel prefix (e.g., \"feishu:ou_xxx\" -> \"ou_xxx\")\n if (trimmed.startsWith(TAG_FEISHU)) {\n const inner = trimmed.slice(TAG_FEISHU.length).trim();\n if (inner) return inner;\n }\n\n if (trimmed.startsWith(TAG_CHAT)) return trimmed.slice(TAG_CHAT.length);\n if (trimmed.startsWith(TAG_USER)) return trimmed.slice(TAG_USER.length);\n if (trimmed.startsWith(TAG_OPEN_ID)) return trimmed.slice(TAG_OPEN_ID.length);\n\n return trimmed;\n}\n\n// ---------------------------------------------------------------------------\n// Formatting\n// ---------------------------------------------------------------------------\n\n/**\n * Add the appropriate OpenClaw routing prefix to a bare Feishu identifier.\n *\n * When `type` is omitted, the prefix is inferred via `detectIdType`.\n */\nexport function formatFeishuTarget(id: string, type?: FeishuIdType): string {\n const resolved = type ?? detectIdType(id);\n\n if (resolved === 'chat_id') return `${TAG_CHAT}${id}`;\n return `${TAG_USER}${id}`;\n}\n\n// ---------------------------------------------------------------------------\n// API receive-ID resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Determine the `receive_id_type` query parameter for the Feishu send-message\n * API based on the target identifier.\n */\nexport function resolveReceiveIdType(id: string): 'chat_id' | 'open_id' | 'user_id' {\n if (id.startsWith(CHAT_PREFIX)) return 'chat_id';\n if (id.startsWith(OPEN_ID_PREFIX)) return 'open_id';\n // Default to open_id for any other pattern (safer for outbound API calls).\n return 'open_id';\n}\n\n// ---------------------------------------------------------------------------\n// Message ID normalisation\n// ---------------------------------------------------------------------------\n\n/**\n * \u89C4\u8303\u5316 message_id\uFF0C\u53BB\u9664\u5408\u6210\u540E\u7F00\uFF08\u5982 `om_xxx:auth-complete` \u2192 `om_xxx`\uFF09\u3002\n */\nexport function normalizeMessageId(messageId: string): string;\nexport function normalizeMessageId(messageId: string | undefined): string | undefined;\nexport function normalizeMessageId(messageId: string | undefined): string | undefined {\n if (!messageId) return undefined;\n const colonIndex = messageId.indexOf(':');\n if (colonIndex >= 0) return messageId.slice(0, colonIndex);\n return messageId;\n}\n\n// ---------------------------------------------------------------------------\n// Quick predicate\n// ---------------------------------------------------------------------------\n\n/**\n * Return `true` when a raw string looks like it could be a Feishu target\n * (either an OpenClaw-tagged form or a native prefix).\n */\nexport function looksLikeFeishuId(raw: string): boolean {\n if (!raw) return false;\n return (\n raw.startsWith(TAG_CHAT) ||\n raw.startsWith(TAG_USER) ||\n raw.startsWith(TAG_OPEN_ID) ||\n raw.startsWith(CHAT_PREFIX) ||\n raw.startsWith(OPEN_ID_PREFIX)\n );\n}\n"],
5
+ "mappings": "AAqBA,MAAM,cAAc;AACpB,MAAM,iBAAiB;AAGvB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,cAAc;AAGpB,MAAM,aAAa;AAWZ,SAAS,aAAa,IAAiC;AAC5D,MAAI,CAAC,GAAI,QAAO;AAChB,MAAI,GAAG,WAAW,WAAW,EAAG,QAAO;AACvC,MAAI,GAAG,WAAW,cAAc,EAAG,QAAO;AAE1C,MAAI,iBAAiB,KAAK,EAAE,EAAG,QAAO;AACtC,SAAO;AACT;AAYO,SAAS,sBAAsB,KAA4B;AAChE,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,CAAC,QAAS,QAAO;AAGrB,MAAI,QAAQ,WAAW,UAAU,GAAG;AAClC,UAAM,QAAQ,QAAQ,MAAM,WAAW,MAAM,EAAE,KAAK;AACpD,QAAI,MAAO,QAAO;AAAA,EACpB;AAEA,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO,QAAQ,MAAM,SAAS,MAAM;AACtE,MAAI,QAAQ,WAAW,QAAQ,EAAG,QAAO,QAAQ,MAAM,SAAS,MAAM;AACtE,MAAI,QAAQ,WAAW,WAAW,EAAG,QAAO,QAAQ,MAAM,YAAY,MAAM;AAE5E,SAAO;AACT;AAWO,SAAS,mBAAmB,IAAY,MAA6B;AAC1E,QAAM,WAAW,QAAQ,aAAa,EAAE;AAExC,MAAI,aAAa,UAAW,QAAO,GAAG,QAAQ,GAAG,EAAE;AACnD,SAAO,GAAG,QAAQ,GAAG,EAAE;AACzB;AAUO,SAAS,qBAAqB,IAA+C;AAClF,MAAI,GAAG,WAAW,WAAW,EAAG,QAAO;AACvC,MAAI,GAAG,WAAW,cAAc,EAAG,QAAO;AAE1C,SAAO;AACT;AAWO,SAAS,mBAAmB,WAAmD;AACpF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,aAAa,UAAU,QAAQ,GAAG;AACxC,MAAI,cAAc,EAAG,QAAO,UAAU,MAAM,GAAG,UAAU;AACzD,SAAO;AACT;AAUO,SAAS,kBAAkB,KAAsB;AACtD,MAAI,CAAC,IAAK,QAAO;AACjB,SACE,IAAI,WAAW,QAAQ,KACvB,IAAI,WAAW,QAAQ,KACvB,IAAI,WAAW,WAAW,KAC1B,IAAI,WAAW,WAAW,KAC1B,IAAI,WAAW,cAAc;AAEjC;",
6
+ "names": []
7
+ }