@fased/fased 0.1.36 → 0.1.37

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 (244) hide show
  1. package/config/capability-catalog.json +241 -0
  2. package/config/dependency-ownership.json +130 -0
  3. package/config/official-channel-catalog.json +39 -0
  4. package/dist/agents/tools/web-fetch-utils.js +2 -1
  5. package/dist/agents/tools/web-fetch-visibility.js +3 -1
  6. package/dist/brand.js +1 -1
  7. package/dist/browser/pw-session.js +2 -1
  8. package/dist/browser/pw-tools-core.state.js +3 -2
  9. package/dist/browser/runtime-dependency.js +27 -0
  10. package/dist/build-info.json +3 -3
  11. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  12. package/dist/capabilities/catalog.js +228 -0
  13. package/dist/cli/components-cli.js +105 -0
  14. package/dist/cli/hooks-cli.js +1 -1
  15. package/dist/cli/plugins-cli.js +3 -3
  16. package/dist/cli/program/register.subclis.js +9 -0
  17. package/dist/cli/update-cli/update-command.js +1 -1
  18. package/dist/commands/doctor-capabilities.js +26 -0
  19. package/dist/commands/doctor.js +2 -0
  20. package/dist/control-ui/assets/{app-DXYMrRJ9.js → app-JBrTSsNu.js} +1480 -1465
  21. package/dist/control-ui/assets/{index-iGQkJE0l.js → index-H-2DWAnH.js} +3 -3
  22. package/dist/control-ui/index.html +1 -1
  23. package/dist/gateway/method-scopes.js +1 -0
  24. package/dist/gateway/server-methods/plugins-marketplace.js +1 -1
  25. package/dist/gateway/server-methods/services.js +8 -0
  26. package/dist/gateway/server-methods-list.js +1 -0
  27. package/dist/infra/optional-runtime-dependency.js +46 -0
  28. package/dist/media/image-ops.js +6 -1
  29. package/dist/media/input-files.js +11 -2
  30. package/dist/media/mime.js +6 -1
  31. package/dist/memory/sqlite-vec.js +7 -1
  32. package/dist/plugin-sdk/{accounts-VqAX3HJ0.js → accounts-BwkH5bTD.js} +1 -1
  33. package/dist/plugin-sdk/{accounts-COY-15zJ.js → accounts-C5BjfxoO.js} +1 -1
  34. package/dist/plugin-sdk/{accounts-Zgt0NP54.js → accounts-RT6y7n3H.js} +1 -1
  35. package/dist/plugin-sdk/{actions-lB3PCwsz.js → actions-BrU_JMqM.js} +2 -2
  36. package/dist/plugin-sdk/{actions-BI5QViLk.js → actions-CUdVHzeJ.js} +2 -2
  37. package/dist/plugin-sdk/{active-listener-DKZiSaRA.js → active-listener-CE8t5g6l.js} +1 -1
  38. package/dist/plugin-sdk/{active-listener-D6_pdTMC.js → active-listener-Dx1SEGDn.js} +1 -1
  39. package/dist/plugin-sdk/{audio-preflight-Dx8J7XhV.js → audio-preflight-C8SbY4fV.js} +22 -22
  40. package/dist/plugin-sdk/{audio-preflight-CeRstduc.js → audio-preflight-CuABlLDL.js} +22 -22
  41. package/dist/plugin-sdk/{audit-BImNOQvk.js → audit-DxB2Kml5.js} +11 -11
  42. package/dist/plugin-sdk/{audit-CKWkq0Un.js → audit-sSeRDwJb.js} +11 -11
  43. package/dist/plugin-sdk/channel-plugin-common.js +3 -2
  44. package/dist/plugin-sdk/{chunk-skAJZWCT.js → chunk-KCxbJLsV.js} +1 -1
  45. package/dist/plugin-sdk/{chunk-Cjw4712g.js → chunk-M2d7r-Zt.js} +1 -1
  46. package/dist/plugin-sdk/{chunk-items-Df5811D0.js → chunk-items-BGbYyNvO.js} +1 -1
  47. package/dist/plugin-sdk/command-status.js +22 -22
  48. package/dist/plugin-sdk/{commands-registry-5J619bQ2.js → commands-registry-CdT-nl7v.js} +2 -2
  49. package/dist/plugin-sdk/{commands-registry-CoLAT03J.js → commands-registry-DoWdYAJd.js} +2 -2
  50. package/dist/plugin-sdk/{common-CSTxewqY.js → common-Cpw-vuuM.js} +1 -1
  51. package/dist/plugin-sdk/{common-BiF0sTnY.js → common-DJWrErdE.js} +1 -1
  52. package/dist/plugin-sdk/{config-BxVjLTIv.js → config-C0qggNKQ.js} +7 -7
  53. package/dist/plugin-sdk/{config-TFokWwvO.js → config-CgwTaper.js} +45 -2
  54. package/dist/plugin-sdk/{config-DXlISPlx.js → config-DtqAN2L3.js} +45 -2
  55. package/dist/plugin-sdk/{config-DgD5T-Ui.js → config-g1MG9Bet.js} +7 -7
  56. package/dist/plugin-sdk/discord.js +2 -1
  57. package/dist/plugin-sdk/{fetch-guard-DPMynms8.js → fetch-guard-BLdujDnt.js} +1 -1
  58. package/dist/plugin-sdk/{fetch-guard-COLmW2TM.js → fetch-guard-DTKgMN9f.js} +1 -1
  59. package/dist/plugin-sdk/{handle-action-C-UndB8E.js → handle-action-B61D85yo.js} +12 -12
  60. package/dist/plugin-sdk/{handle-action-hhufKxmU.js → handle-action-CjcxGzQD.js} +12 -12
  61. package/dist/plugin-sdk/index.js +22 -22
  62. package/dist/plugin-sdk/{login-DL1oBBfL.js → login-CB_nCjX3.js} +3 -3
  63. package/dist/plugin-sdk/{login-YveoqEvH.js → login-CfRfRNc7.js} +2 -2
  64. package/dist/plugin-sdk/{login-Dnxas1dI.js → login-CzUuDd1v.js} +3 -3
  65. package/dist/plugin-sdk/{login-qr-CVQsGWKq.js → login-qr-D7MEpFza.js} +3 -3
  66. package/dist/plugin-sdk/{login-qr-B343JNXJ.js → login-qr-DgxfBFVN.js} +3 -3
  67. package/dist/plugin-sdk/{manager-BytwRIfK.js → manager-9see0IX5.js} +8 -3
  68. package/dist/plugin-sdk/{manager-BZjd_PCt.js → manager-D2kZ0Cps.js} +8 -3
  69. package/dist/plugin-sdk/{markdown-tables-krKiU4cT.js → markdown-tables-CCSD52rA.js} +1 -1
  70. package/dist/plugin-sdk/{markdown-tables-9rJDQ1X-.js → markdown-tables-DWUNvLrc.js} +1 -1
  71. package/dist/plugin-sdk/{media-D7ZkWU-A.js → media-C7YT4DeU.js} +13 -4
  72. package/dist/plugin-sdk/{media-C6C2c9a4.js → media-Cxw-xLGU.js} +13 -4
  73. package/dist/plugin-sdk/{monitor-CVQNDfhs.js → monitor-9X7wapUN.js} +27 -27
  74. package/dist/plugin-sdk/{monitor-BCxkQBoh.js → monitor-B3IfJzKX.js} +26 -26
  75. package/dist/plugin-sdk/{monitor-BzDW50H6.js → monitor-CTjAST_W.js} +27 -27
  76. package/dist/plugin-sdk/{monitor-BsLMfn3U.js → monitor-tYFfy2u-.js} +26 -26
  77. package/dist/plugin-sdk/{openresponses-http-BB67Z4QM.js → openresponses-http-DEB2fEya.js} +22 -22
  78. package/dist/plugin-sdk/{openresponses-http-NE76GGhj.js → openresponses-http-YgMg1jnp.js} +22 -22
  79. package/dist/plugin-sdk/{ssrf-u0Ul7okY.js → optional-runtime-dependency-szS49voX.js} +46 -2
  80. package/dist/plugin-sdk/{outbound-PGNWU6T-.js → outbound-BCxOxdi-.js} +6 -6
  81. package/dist/plugin-sdk/{outbound-BHWJD9q0.js → outbound-JIwQbpRY.js} +6 -6
  82. package/dist/plugin-sdk/{outbound-attachment-BVzfhqnk.js → outbound-attachment-C-jSFaEB.js} +1 -1
  83. package/dist/plugin-sdk/{outbound-attachment-ClMvNW17.js → outbound-attachment-D6tj5Y5H.js} +1 -1
  84. package/dist/plugin-sdk/{pi-embedded-runner-DQ6UPx6-.js → pi-embedded-runner-CUVk8CIj.js} +72 -60
  85. package/dist/plugin-sdk/{pi-model-discovery-D51L5E9I.js → pi-model-discovery-DadlvsZW.js} +1 -1
  86. package/dist/plugin-sdk/{pi-model-discovery-runtime-DxoFm4Vd.js → pi-model-discovery-runtime-C5ssE6EW.js} +1 -1
  87. package/dist/plugin-sdk/{pi-model-discovery-runtime-DRuh4PNV.js → pi-model-discovery-runtime-DAaOPt64.js} +2 -2
  88. package/dist/plugin-sdk/{pi-model-discovery-runtime-CQOauapl.js → pi-model-discovery-runtime-LB-5lX5L.js} +1 -1
  89. package/dist/plugin-sdk/{pi-model-discovery-runtime-BTWgkx4x.js → pi-model-discovery-runtime-gd77b0UF.js} +2 -2
  90. package/dist/plugin-sdk/{pi-model-discovery-CkH5l9X5.js → pi-model-discovery-yifTIDIn.js} +1 -1
  91. package/dist/plugin-sdk/{pw-ai-DZs6uYRd.js → pw-ai-SNCI9UXT.js} +6 -5
  92. package/dist/plugin-sdk/{pw-ai-Cn-EUPZK.js → pw-ai-sH1Pi-Fh.js} +6 -5
  93. package/dist/plugin-sdk/{registry-B4HvWzob.js → registry-DNkJsNsz.js} +4 -3
  94. package/dist/plugin-sdk/{replies-jDMUBsNO.js → replies-fvzN5wnU.js} +2 -2
  95. package/dist/plugin-sdk/{replies-B_LW9c9f.js → replies-zebgZCHX.js} +2 -2
  96. package/dist/plugin-sdk/{resolve-route-_YZHbS-z.js → resolve-route-CNhTJG_2.js} +1 -1
  97. package/dist/plugin-sdk/{resolve-route-DlHzggnG.js → resolve-route-DO0WR4nB.js} +1 -1
  98. package/dist/plugin-sdk/{resolve-utils-zBhVX3Vx.js → resolve-utils-EFP1DYqG.js} +1 -1
  99. package/dist/plugin-sdk/{retry-policy-DGLs5ebP.js → retry-policy-BLYEgIwP.js} +1 -1
  100. package/dist/plugin-sdk/{retry-policy-DaodShGa.js → retry-policy-sM01QZT2.js} +1 -1
  101. package/dist/plugin-sdk/{runtime-Dd0sswuW.js → runtime-BPyuAmka.js} +4 -3
  102. package/dist/plugin-sdk/{paths-DW-U2r7Y.js → runtime-dependency-B-1u41Ph.js} +27 -3
  103. package/dist/plugin-sdk/{paths-CBCpJ5bu.js → runtime-dependency-Csswyv5r.js} +27 -3
  104. package/dist/plugin-sdk/sat-runtime.js +1 -0
  105. package/dist/plugin-sdk/{send-8remZhsH.js → send-B09HRfHN.js} +6 -6
  106. package/dist/plugin-sdk/{send-DaaAscWj.js → send-BBYcgXA4.js} +5 -5
  107. package/dist/plugin-sdk/{send-DFTB0cnx.js → send-BQXQA81e.js} +6 -6
  108. package/dist/plugin-sdk/{send-CtMVvMJs.js → send-BzFdvD_i.js} +6 -6
  109. package/dist/plugin-sdk/{send-Di9irjS-.js → send-C3u-F_8s.js} +6 -6
  110. package/dist/plugin-sdk/{send-Dl88ZKq2.js → send-D3836_UN.js} +6 -6
  111. package/dist/plugin-sdk/{send-ifgZCHVB.js → send-D7tr-EsH.js} +5 -5
  112. package/dist/plugin-sdk/{send-DvUpYh0d.js → send-DRGzRdI3.js} +6 -6
  113. package/dist/plugin-sdk/{send-D2jlPeKH.js → send-DfkPKz_0.js} +4 -4
  114. package/dist/plugin-sdk/{send-QSt6nbny.js → send-DsxFXrCO.js} +4 -4
  115. package/dist/plugin-sdk/{session-CknrceVU.js → session-DG_bYisw.js} +2 -2
  116. package/dist/plugin-sdk/{session-CyRTcJ8N.js → session-EIStzK0E.js} +2 -2
  117. package/dist/plugin-sdk/{skill-commands-DHxkTA0s.js → skill-commands-CkgGmWBS.js} +2 -2
  118. package/dist/plugin-sdk/{skill-commands-CLXAG5sU.js → skill-commands-hyZA0LE3.js} +2 -2
  119. package/dist/plugin-sdk/{slack-BpJXkwxF.js → slack-C0WZVJPZ.js} +34 -34
  120. package/dist/plugin-sdk/{slack-Br9c6Yzs.js → slack-DtlPw_PN.js} +34 -34
  121. package/dist/plugin-sdk/{slack-actions-8tLGt1Ht.js → slack-actions-BQawWGTJ.js} +11 -11
  122. package/dist/plugin-sdk/{slack-actions-CDSSRo8Y.js → slack-actions-DDMszx56.js} +11 -11
  123. package/dist/plugin-sdk/slack.js +2 -1
  124. package/dist/plugin-sdk/src/brand.d.ts +2 -2
  125. package/dist/plugin-sdk/src/browser/runtime-dependency.d.ts +3 -0
  126. package/dist/plugin-sdk/src/infra/optional-runtime-dependency.d.ts +6 -0
  127. package/dist/plugin-sdk/{ssrf-fG4UHKBw.js → ssrf-Cce6LubA.js} +41 -7
  128. package/dist/plugin-sdk/{status-DoxnzyMC.js → status-CIiBeb9B.js} +72 -60
  129. package/dist/plugin-sdk/{sticker-cache-zmJ39ByC.js → sticker-cache-9houHChV.js} +3 -3
  130. package/dist/plugin-sdk/{sticker-cache-D7WcMYsF.js → sticker-cache-Dwi-XsgB.js} +3 -3
  131. package/dist/plugin-sdk/{tables-CkxblF0u.js → tables-BD7H5ug-.js} +1 -1
  132. package/dist/plugin-sdk/{tables-RBtf8G4P.js → tables-CJE8kB35.js} +1 -1
  133. package/dist/plugin-sdk/{telegram-actions-DAeYXSLk.js → telegram-actions-Bu1edXF2.js} +25 -25
  134. package/dist/plugin-sdk/{telegram-actions-BDeS3ocd.js → telegram-actions-CRXVO8lG.js} +25 -25
  135. package/dist/plugin-sdk/telegram.js +2 -1
  136. package/dist/plugin-sdk/{thread-bindings.lifecycle-DsB94WbE.js → thread-bindings.lifecycle-Djem8FWF.js} +2 -2
  137. package/dist/plugin-sdk/{thread-bindings.lifecycle-DMhV3TsR.js → thread-bindings.lifecycle-gmHv3hc7.js} +2 -2
  138. package/dist/plugin-sdk/{thread-bindings.state-DYWpy7hJ.js → thread-bindings.state-CNT2vxm3.js} +2 -2
  139. package/dist/plugin-sdk/{thread-bindings.state-DG5cBaRn.js → thread-bindings.state-D8kN2X6H.js} +2 -2
  140. package/dist/plugin-sdk/{web-IJ4NoGPJ.js → web-DCrVQr6y.js} +26 -26
  141. package/dist/plugin-sdk/{web-DJwacWQo.js → web-Ds6OWw3C.js} +26 -26
  142. package/dist/plugin-sdk/{whatsapp-actions-DAY8hWpD.js → whatsapp-actions-68Mfs2dJ.js} +12 -12
  143. package/dist/plugin-sdk/{whatsapp-actions-6opfo-oB.js → whatsapp-actions-DRqtzvTO.js} +12 -12
  144. package/dist/plugin-sdk/whatsapp.js +6 -5
  145. package/dist/plugin-sdk/{wsl-B2euLEc2.js → wsl-DcM2PgSS.js} +3 -3
  146. package/dist/plugin-sdk/{wsl-B8eyviuv.js → wsl-PXrdWitE.js} +3 -3
  147. package/dist/plugins/update.js +1 -1
  148. package/dist/tts/tts-core.js +6 -1
  149. package/dist/wizard/onboarding.finalize.js +9 -0
  150. package/extensions/acpx/package.json +1 -1
  151. package/extensions/bluebubbles/package.json +1 -1
  152. package/extensions/copilot-proxy/package.json +1 -1
  153. package/extensions/diagnostics-otel/package.json +1 -1
  154. package/extensions/fased-federation/package.json +1 -1
  155. package/extensions/google-gemini-cli-auth/package.json +1 -1
  156. package/extensions/imessage/package.json +1 -1
  157. package/extensions/irc/package.json +1 -1
  158. package/extensions/line/package.json +1 -1
  159. package/extensions/llm-task/package.json +1 -1
  160. package/extensions/lobster/package.json +1 -1
  161. package/extensions/matrix/CHANGELOG.md +6 -0
  162. package/extensions/matrix/package.json +1 -1
  163. package/extensions/mattermost/package.json +1 -1
  164. package/extensions/memory-core/package.json +2 -2
  165. package/extensions/memory-lancedb/package.json +1 -1
  166. package/extensions/minimax-portal-auth/package.json +1 -1
  167. package/extensions/msteams/CHANGELOG.md +6 -0
  168. package/extensions/msteams/package.json +1 -1
  169. package/extensions/nextcloud-talk/package.json +1 -1
  170. package/extensions/nostr/CHANGELOG.md +6 -0
  171. package/extensions/nostr/package.json +1 -1
  172. package/extensions/open-prose/package.json +1 -1
  173. package/extensions/sat-mining/package.json +1 -1
  174. package/extensions/signal/package.json +1 -1
  175. package/extensions/synology-chat/package.json +1 -1
  176. package/extensions/tlon/package.json +1 -1
  177. package/extensions/twitch/CHANGELOG.md +6 -0
  178. package/extensions/twitch/package.json +1 -1
  179. package/extensions/voice-call/CHANGELOG.md +6 -0
  180. package/extensions/voice-call/package.json +1 -1
  181. package/extensions/zalo/CHANGELOG.md +6 -0
  182. package/extensions/zalo/package.json +1 -1
  183. package/extensions/zalouser/CHANGELOG.md +6 -0
  184. package/extensions/zalouser/package.json +1 -1
  185. package/package.json +22 -15
  186. package/extensions/feishu/fased.plugin.json +0 -10
  187. package/extensions/feishu/index.ts +0 -63
  188. package/extensions/feishu/package.json +0 -33
  189. package/extensions/feishu/skills/feishu-doc/SKILL.md +0 -211
  190. package/extensions/feishu/skills/feishu-doc/references/block-types.md +0 -103
  191. package/extensions/feishu/skills/feishu-drive/SKILL.md +0 -97
  192. package/extensions/feishu/skills/feishu-perm/SKILL.md +0 -119
  193. package/extensions/feishu/skills/feishu-wiki/SKILL.md +0 -111
  194. package/extensions/feishu/src/accounts.ts +0 -159
  195. package/extensions/feishu/src/bitable.ts +0 -713
  196. package/extensions/feishu/src/bot.ts +0 -979
  197. package/extensions/feishu/src/channel.ts +0 -359
  198. package/extensions/feishu/src/client.ts +0 -118
  199. package/extensions/feishu/src/config-schema.ts +0 -226
  200. package/extensions/feishu/src/dedup.ts +0 -54
  201. package/extensions/feishu/src/directory.ts +0 -177
  202. package/extensions/feishu/src/doc-schema.ts +0 -122
  203. package/extensions/feishu/src/docx.ts +0 -998
  204. package/extensions/feishu/src/drive-schema.ts +0 -46
  205. package/extensions/feishu/src/drive.ts +0 -232
  206. package/extensions/feishu/src/dynamic-agent.ts +0 -131
  207. package/extensions/feishu/src/external-keys.ts +0 -19
  208. package/extensions/feishu/src/media.ts +0 -447
  209. package/extensions/feishu/src/mention.ts +0 -133
  210. package/extensions/feishu/src/monitor.ts +0 -434
  211. package/extensions/feishu/src/onboarding.ts +0 -430
  212. package/extensions/feishu/src/outbound.ts +0 -56
  213. package/extensions/feishu/src/perm-schema.ts +0 -52
  214. package/extensions/feishu/src/perm.ts +0 -180
  215. package/extensions/feishu/src/policy.ts +0 -116
  216. package/extensions/feishu/src/probe.ts +0 -44
  217. package/extensions/feishu/src/reactions.ts +0 -160
  218. package/extensions/feishu/src/reply-dispatcher.ts +0 -239
  219. package/extensions/feishu/src/runtime.ts +0 -14
  220. package/extensions/feishu/src/send-result.ts +0 -29
  221. package/extensions/feishu/src/send-target.ts +0 -25
  222. package/extensions/feishu/src/send.ts +0 -313
  223. package/extensions/feishu/src/streaming-card.ts +0 -218
  224. package/extensions/feishu/src/targets.ts +0 -78
  225. package/extensions/feishu/src/tool-account.ts +0 -58
  226. package/extensions/feishu/src/tools-config.ts +0 -21
  227. package/extensions/feishu/src/types.ts +0 -81
  228. package/extensions/feishu/src/typing.ts +0 -80
  229. package/extensions/feishu/src/wiki-schema.ts +0 -55
  230. package/extensions/feishu/src/wiki.ts +0 -237
  231. package/extensions/googlechat/fased.plugin.json +0 -9
  232. package/extensions/googlechat/index.ts +0 -19
  233. package/extensions/googlechat/package.json +0 -37
  234. package/extensions/googlechat/src/accounts.ts +0 -147
  235. package/extensions/googlechat/src/actions.ts +0 -176
  236. package/extensions/googlechat/src/api.ts +0 -282
  237. package/extensions/googlechat/src/auth.ts +0 -137
  238. package/extensions/googlechat/src/channel.ts +0 -591
  239. package/extensions/googlechat/src/monitor.ts +0 -996
  240. package/extensions/googlechat/src/onboarding.ts +0 -344
  241. package/extensions/googlechat/src/runtime.ts +0 -14
  242. package/extensions/googlechat/src/targets.ts +0 -65
  243. package/extensions/googlechat/src/types.config.ts +0 -3
  244. package/extensions/googlechat/src/types.ts +0 -73
@@ -1,359 +0,0 @@
1
- import type { ChannelMeta, ChannelPlugin, FasedAgentConfig } from "fased/plugin-sdk";
2
- import {
3
- buildBaseChannelStatusSummary,
4
- createDefaultChannelRuntimeState,
5
- DEFAULT_ACCOUNT_ID,
6
- PAIRING_APPROVED_MESSAGE,
7
- resolveAllowlistProviderRuntimeGroupPolicy,
8
- resolveDefaultGroupPolicy,
9
- } from "fased/plugin-sdk";
10
- import {
11
- resolveFeishuAccount,
12
- resolveFeishuCredentials,
13
- listFeishuAccountIds,
14
- resolveDefaultFeishuAccountId,
15
- } from "./accounts.js";
16
- import {
17
- listFeishuDirectoryPeers,
18
- listFeishuDirectoryGroups,
19
- listFeishuDirectoryPeersLive,
20
- listFeishuDirectoryGroupsLive,
21
- } from "./directory.js";
22
- import { feishuOnboardingAdapter } from "./onboarding.js";
23
- import { feishuOutbound } from "./outbound.js";
24
- import { resolveFeishuGroupToolPolicy } from "./policy.js";
25
- import { probeFeishu } from "./probe.js";
26
- import { sendMessageFeishu } from "./send.js";
27
- import { normalizeFeishuTarget, looksLikeFeishuId, formatFeishuTarget } from "./targets.js";
28
- import type { ResolvedFeishuAccount, FeishuConfig } from "./types.js";
29
-
30
- const meta: ChannelMeta = {
31
- id: "feishu",
32
- label: "Feishu",
33
- selectionLabel: "Feishu/Lark (飞书)",
34
- docsPath: "/channels/feishu",
35
- docsLabel: "feishu",
36
- blurb: "飞书/Lark enterprise messaging.",
37
- aliases: ["lark"],
38
- order: 70,
39
- };
40
-
41
- export const feishuPlugin: ChannelPlugin<ResolvedFeishuAccount> = {
42
- id: "feishu",
43
- meta: {
44
- ...meta,
45
- },
46
- pairing: {
47
- idLabel: "feishuUserId",
48
- normalizeAllowEntry: (entry) => entry.replace(/^(feishu|user|open_id):/i, ""),
49
- notifyApproval: async ({ cfg, id }) => {
50
- await sendMessageFeishu({
51
- cfg,
52
- to: id,
53
- text: PAIRING_APPROVED_MESSAGE,
54
- });
55
- },
56
- },
57
- capabilities: {
58
- chatTypes: ["direct", "channel"],
59
- polls: false,
60
- threads: true,
61
- media: true,
62
- reactions: true,
63
- edit: true,
64
- reply: true,
65
- },
66
- agentPrompt: {
67
- messageToolHints: () => [
68
- "- Feishu targeting: omit `target` to reply to the current conversation (auto-inferred). Explicit targets: `user:open_id` or `chat:chat_id`.",
69
- "- Feishu supports interactive cards for rich messages.",
70
- ],
71
- },
72
- groups: {
73
- resolveToolPolicy: resolveFeishuGroupToolPolicy,
74
- },
75
- reload: { configPrefixes: ["channels.feishu"] },
76
- configSchema: {
77
- schema: {
78
- type: "object",
79
- additionalProperties: false,
80
- properties: {
81
- enabled: { type: "boolean" },
82
- appId: { type: "string" },
83
- appSecret: { type: "string" },
84
- encryptKey: { type: "string" },
85
- verificationToken: { type: "string" },
86
- domain: {
87
- oneOf: [
88
- { type: "string", enum: ["feishu", "lark"] },
89
- { type: "string", format: "uri", pattern: "^https://" },
90
- ],
91
- },
92
- connectionMode: { type: "string", enum: ["websocket", "webhook"] },
93
- webhookPath: { type: "string" },
94
- webhookHost: { type: "string" },
95
- webhookPort: { type: "integer", minimum: 1 },
96
- dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
97
- allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } },
98
- groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
99
- groupAllowFrom: {
100
- type: "array",
101
- items: { oneOf: [{ type: "string" }, { type: "number" }] },
102
- },
103
- requireMention: { type: "boolean" },
104
- topicSessionMode: { type: "string", enum: ["disabled", "enabled"] },
105
- historyLimit: { type: "integer", minimum: 0 },
106
- dmHistoryLimit: { type: "integer", minimum: 0 },
107
- textChunkLimit: { type: "integer", minimum: 1 },
108
- chunkMode: { type: "string", enum: ["length", "newline"] },
109
- mediaMaxMb: { type: "number", minimum: 0 },
110
- renderMode: { type: "string", enum: ["auto", "raw", "card"] },
111
- accounts: {
112
- type: "object",
113
- additionalProperties: {
114
- type: "object",
115
- properties: {
116
- enabled: { type: "boolean" },
117
- name: { type: "string" },
118
- appId: { type: "string" },
119
- appSecret: { type: "string" },
120
- encryptKey: { type: "string" },
121
- verificationToken: { type: "string" },
122
- domain: { type: "string", enum: ["feishu", "lark"] },
123
- connectionMode: { type: "string", enum: ["websocket", "webhook"] },
124
- webhookHost: { type: "string" },
125
- webhookPath: { type: "string" },
126
- webhookPort: { type: "integer", minimum: 1 },
127
- },
128
- },
129
- },
130
- },
131
- },
132
- },
133
- config: {
134
- listAccountIds: (cfg) => listFeishuAccountIds(cfg),
135
- resolveAccount: (cfg, accountId) => resolveFeishuAccount({ cfg, accountId }),
136
- defaultAccountId: (cfg) => resolveDefaultFeishuAccountId(cfg),
137
- setAccountEnabled: ({ cfg, accountId, enabled }) => {
138
- const account = resolveFeishuAccount({ cfg, accountId });
139
- const isDefault = accountId === DEFAULT_ACCOUNT_ID;
140
-
141
- if (isDefault) {
142
- // For default account, set top-level enabled
143
- return {
144
- ...cfg,
145
- channels: {
146
- ...cfg.channels,
147
- feishu: {
148
- ...cfg.channels?.feishu,
149
- enabled,
150
- },
151
- },
152
- };
153
- }
154
-
155
- // For named accounts, set enabled in accounts[accountId]
156
- const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
157
- return {
158
- ...cfg,
159
- channels: {
160
- ...cfg.channels,
161
- feishu: {
162
- ...feishuCfg,
163
- accounts: {
164
- ...feishuCfg?.accounts,
165
- [accountId]: {
166
- ...feishuCfg?.accounts?.[accountId],
167
- enabled,
168
- },
169
- },
170
- },
171
- },
172
- };
173
- },
174
- deleteAccount: ({ cfg, accountId }) => {
175
- const isDefault = accountId === DEFAULT_ACCOUNT_ID;
176
-
177
- if (isDefault) {
178
- // Delete entire feishu config
179
- const next = { ...cfg } as FasedAgentConfig;
180
- const nextChannels = { ...cfg.channels };
181
- delete (nextChannels as Record<string, unknown>).feishu;
182
- if (Object.keys(nextChannels).length > 0) {
183
- next.channels = nextChannels;
184
- } else {
185
- delete next.channels;
186
- }
187
- return next;
188
- }
189
-
190
- // Delete specific account from accounts
191
- const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
192
- const accounts = { ...feishuCfg?.accounts };
193
- delete accounts[accountId];
194
-
195
- return {
196
- ...cfg,
197
- channels: {
198
- ...cfg.channels,
199
- feishu: {
200
- ...feishuCfg,
201
- accounts: Object.keys(accounts).length > 0 ? accounts : undefined,
202
- },
203
- },
204
- };
205
- },
206
- isConfigured: (account) => account.configured,
207
- describeAccount: (account) => ({
208
- accountId: account.accountId,
209
- enabled: account.enabled,
210
- configured: account.configured,
211
- name: account.name,
212
- appId: account.appId,
213
- domain: account.domain,
214
- }),
215
- resolveAllowFrom: ({ cfg, accountId }) => {
216
- const account = resolveFeishuAccount({ cfg, accountId });
217
- return (account.config?.allowFrom ?? []).map((entry) => String(entry));
218
- },
219
- formatAllowFrom: ({ allowFrom }) =>
220
- allowFrom
221
- .map((entry) => String(entry).trim())
222
- .filter(Boolean)
223
- .map((entry) => entry.toLowerCase()),
224
- },
225
- security: {
226
- collectWarnings: ({ cfg, accountId }) => {
227
- const account = resolveFeishuAccount({ cfg, accountId });
228
- const feishuCfg = account.config;
229
- const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg);
230
- const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({
231
- providerConfigPresent: cfg.channels?.feishu !== undefined,
232
- groupPolicy: feishuCfg?.groupPolicy,
233
- defaultGroupPolicy,
234
- });
235
- if (groupPolicy !== "open") return [];
236
- return [
237
- `- Feishu[${account.accountId}] groups: groupPolicy="open" allows any member to trigger (mention-gated). Set channels.feishu.groupPolicy="allowlist" + channels.feishu.groupAllowFrom to restrict senders.`,
238
- ];
239
- },
240
- },
241
- setup: {
242
- resolveAccountId: () => DEFAULT_ACCOUNT_ID,
243
- applyAccountConfig: ({ cfg, accountId }) => {
244
- const isDefault = !accountId || accountId === DEFAULT_ACCOUNT_ID;
245
-
246
- if (isDefault) {
247
- return {
248
- ...cfg,
249
- channels: {
250
- ...cfg.channels,
251
- feishu: {
252
- ...cfg.channels?.feishu,
253
- enabled: true,
254
- },
255
- },
256
- };
257
- }
258
-
259
- const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
260
- return {
261
- ...cfg,
262
- channels: {
263
- ...cfg.channels,
264
- feishu: {
265
- ...feishuCfg,
266
- accounts: {
267
- ...feishuCfg?.accounts,
268
- [accountId]: {
269
- ...feishuCfg?.accounts?.[accountId],
270
- enabled: true,
271
- },
272
- },
273
- },
274
- },
275
- };
276
- },
277
- },
278
- onboarding: feishuOnboardingAdapter,
279
- messaging: {
280
- normalizeTarget: (raw) => normalizeFeishuTarget(raw) ?? undefined,
281
- targetResolver: {
282
- looksLikeId: looksLikeFeishuId,
283
- hint: "<chatId|user:openId|chat:chatId>",
284
- },
285
- },
286
- directory: {
287
- self: async () => null,
288
- listPeers: async ({ cfg, query, limit, accountId }) =>
289
- listFeishuDirectoryPeers({
290
- cfg,
291
- query: query ?? undefined,
292
- limit: limit ?? undefined,
293
- accountId: accountId ?? undefined,
294
- }),
295
- listGroups: async ({ cfg, query, limit, accountId }) =>
296
- listFeishuDirectoryGroups({
297
- cfg,
298
- query: query ?? undefined,
299
- limit: limit ?? undefined,
300
- accountId: accountId ?? undefined,
301
- }),
302
- listPeersLive: async ({ cfg, query, limit, accountId }) =>
303
- listFeishuDirectoryPeersLive({
304
- cfg,
305
- query: query ?? undefined,
306
- limit: limit ?? undefined,
307
- accountId: accountId ?? undefined,
308
- }),
309
- listGroupsLive: async ({ cfg, query, limit, accountId }) =>
310
- listFeishuDirectoryGroupsLive({
311
- cfg,
312
- query: query ?? undefined,
313
- limit: limit ?? undefined,
314
- accountId: accountId ?? undefined,
315
- }),
316
- },
317
- outbound: feishuOutbound,
318
- status: {
319
- defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }),
320
- buildChannelSummary: ({ snapshot }) => ({
321
- ...buildBaseChannelStatusSummary(snapshot),
322
- port: snapshot.port ?? null,
323
- probe: snapshot.probe,
324
- lastProbeAt: snapshot.lastProbeAt ?? null,
325
- }),
326
- probeAccount: async ({ account }) => await probeFeishu(account),
327
- buildAccountSnapshot: ({ account, runtime, probe }) => ({
328
- accountId: account.accountId,
329
- enabled: account.enabled,
330
- configured: account.configured,
331
- name: account.name,
332
- appId: account.appId,
333
- domain: account.domain,
334
- running: runtime?.running ?? false,
335
- lastStartAt: runtime?.lastStartAt ?? null,
336
- lastStopAt: runtime?.lastStopAt ?? null,
337
- lastError: runtime?.lastError ?? null,
338
- port: runtime?.port ?? null,
339
- probe,
340
- }),
341
- },
342
- gateway: {
343
- startAccount: async (ctx) => {
344
- const { monitorFeishuProvider } = await import("./monitor.js");
345
- const account = resolveFeishuAccount({ cfg: ctx.cfg, accountId: ctx.accountId });
346
- const port = account.config?.webhookPort ?? null;
347
- ctx.setStatus({ accountId: ctx.accountId, port });
348
- ctx.log?.info(
349
- `starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
350
- );
351
- return monitorFeishuProvider({
352
- config: ctx.cfg,
353
- runtime: ctx.runtime,
354
- abortSignal: ctx.abortSignal,
355
- accountId: ctx.accountId,
356
- });
357
- },
358
- },
359
- };
@@ -1,118 +0,0 @@
1
- import * as Lark from "@larksuiteoapi/node-sdk";
2
- import type { FeishuDomain, ResolvedFeishuAccount } from "./types.js";
3
-
4
- // Multi-account client cache
5
- const clientCache = new Map<
6
- string,
7
- {
8
- client: Lark.Client;
9
- config: { appId: string; appSecret: string; domain?: FeishuDomain };
10
- }
11
- >();
12
-
13
- function resolveDomain(domain: FeishuDomain | undefined): Lark.Domain | string {
14
- if (domain === "lark") {
15
- return Lark.Domain.Lark;
16
- }
17
- if (domain === "feishu" || !domain) {
18
- return Lark.Domain.Feishu;
19
- }
20
- return domain.replace(/\/+$/, ""); // Custom URL for private deployment
21
- }
22
-
23
- /**
24
- * Credentials needed to create a Feishu client.
25
- * Both FeishuConfig and ResolvedFeishuAccount satisfy this interface.
26
- */
27
- export type FeishuClientCredentials = {
28
- accountId?: string;
29
- appId?: string;
30
- appSecret?: string;
31
- domain?: FeishuDomain;
32
- };
33
-
34
- /**
35
- * Create or get a cached Feishu client for an account.
36
- * Accepts any object with appId, appSecret, and optional domain/accountId.
37
- */
38
- export function createFeishuClient(creds: FeishuClientCredentials): Lark.Client {
39
- const { accountId = "default", appId, appSecret, domain } = creds;
40
-
41
- if (!appId || !appSecret) {
42
- throw new Error(`Feishu credentials not configured for account "${accountId}"`);
43
- }
44
-
45
- // Check cache
46
- const cached = clientCache.get(accountId);
47
- if (
48
- cached &&
49
- cached.config.appId === appId &&
50
- cached.config.appSecret === appSecret &&
51
- cached.config.domain === domain
52
- ) {
53
- return cached.client;
54
- }
55
-
56
- // Create new client
57
- const client = new Lark.Client({
58
- appId,
59
- appSecret,
60
- appType: Lark.AppType.SelfBuild,
61
- domain: resolveDomain(domain),
62
- });
63
-
64
- // Cache it
65
- clientCache.set(accountId, {
66
- client,
67
- config: { appId, appSecret, domain },
68
- });
69
-
70
- return client;
71
- }
72
-
73
- /**
74
- * Create a Feishu WebSocket client for an account.
75
- * Note: WSClient is not cached since each call creates a new connection.
76
- */
77
- export function createFeishuWSClient(account: ResolvedFeishuAccount): Lark.WSClient {
78
- const { accountId, appId, appSecret, domain } = account;
79
-
80
- if (!appId || !appSecret) {
81
- throw new Error(`Feishu credentials not configured for account "${accountId}"`);
82
- }
83
-
84
- return new Lark.WSClient({
85
- appId,
86
- appSecret,
87
- domain: resolveDomain(domain),
88
- loggerLevel: Lark.LoggerLevel.info,
89
- });
90
- }
91
-
92
- /**
93
- * Create an event dispatcher for an account.
94
- */
95
- export function createEventDispatcher(account: ResolvedFeishuAccount): Lark.EventDispatcher {
96
- return new Lark.EventDispatcher({
97
- encryptKey: account.encryptKey,
98
- verificationToken: account.verificationToken,
99
- });
100
- }
101
-
102
- /**
103
- * Get a cached client for an account (if exists).
104
- */
105
- export function getFeishuClient(accountId: string): Lark.Client | null {
106
- return clientCache.get(accountId)?.client ?? null;
107
- }
108
-
109
- /**
110
- * Clear client cache for a specific account or all accounts.
111
- */
112
- export function clearClientCache(accountId?: string): void {
113
- if (accountId) {
114
- clientCache.delete(accountId);
115
- } else {
116
- clientCache.clear();
117
- }
118
- }
@@ -1,226 +0,0 @@
1
- import { z } from "zod";
2
- export { z };
3
-
4
- const DmPolicySchema = z.enum(["open", "pairing", "allowlist"]);
5
- const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]);
6
- const FeishuDomainSchema = z.union([
7
- z.enum(["feishu", "lark"]),
8
- z.string().url().startsWith("https://"),
9
- ]);
10
- const FeishuConnectionModeSchema = z.enum(["websocket", "webhook"]);
11
-
12
- const ToolPolicySchema = z
13
- .object({
14
- allow: z.array(z.string()).optional(),
15
- deny: z.array(z.string()).optional(),
16
- })
17
- .strict()
18
- .optional();
19
-
20
- const DmConfigSchema = z
21
- .object({
22
- enabled: z.boolean().optional(),
23
- systemPrompt: z.string().optional(),
24
- })
25
- .strict()
26
- .optional();
27
-
28
- const MarkdownConfigSchema = z
29
- .object({
30
- mode: z.enum(["native", "escape", "strip"]).optional(),
31
- tableMode: z.enum(["native", "ascii", "simple"]).optional(),
32
- })
33
- .strict()
34
- .optional();
35
-
36
- // Message render mode: auto (default) = detect markdown, raw = plain text, card = always card
37
- const RenderModeSchema = z.enum(["auto", "raw", "card"]).optional();
38
-
39
- // Streaming card mode: when enabled, card replies use Feishu's Card Kit streaming API
40
- // for incremental text display with a "Thinking..." placeholder
41
- const StreamingModeSchema = z.boolean().optional();
42
-
43
- const BlockStreamingCoalesceSchema = z
44
- .object({
45
- enabled: z.boolean().optional(),
46
- minDelayMs: z.number().int().positive().optional(),
47
- maxDelayMs: z.number().int().positive().optional(),
48
- })
49
- .strict()
50
- .optional();
51
-
52
- const ChannelHeartbeatVisibilitySchema = z
53
- .object({
54
- visibility: z.enum(["visible", "hidden"]).optional(),
55
- intervalMs: z.number().int().positive().optional(),
56
- })
57
- .strict()
58
- .optional();
59
-
60
- /**
61
- * Dynamic agent creation configuration.
62
- * When enabled, a new agent is created for each unique DM user.
63
- */
64
- const DynamicAgentCreationSchema = z
65
- .object({
66
- enabled: z.boolean().optional(),
67
- workspaceTemplate: z.string().optional(),
68
- agentDirTemplate: z.string().optional(),
69
- maxAgents: z.number().int().positive().optional(),
70
- })
71
- .strict()
72
- .optional();
73
-
74
- /**
75
- * Feishu tools configuration.
76
- * Controls which tool categories are enabled.
77
- *
78
- * Dependencies:
79
- * - wiki requires doc (wiki content is edited via doc tools)
80
- * - perm can work independently but is typically used with drive
81
- */
82
- const FeishuToolsConfigSchema = z
83
- .object({
84
- doc: z.boolean().optional(), // Document operations (default: true)
85
- wiki: z.boolean().optional(), // Knowledge base operations (default: true, requires doc)
86
- drive: z.boolean().optional(), // Cloud storage operations (default: true)
87
- perm: z.boolean().optional(), // Permission management (default: false, sensitive)
88
- scopes: z.boolean().optional(), // App scopes diagnostic (default: true)
89
- })
90
- .strict()
91
- .optional();
92
-
93
- /**
94
- * Topic session isolation mode for group chats.
95
- * - "disabled" (default): All messages in a group share one session
96
- * - "enabled": Messages in different topics get separate sessions
97
- *
98
- * When enabled, the session key becomes `chat:{chatId}:topic:{rootId}`
99
- * for messages within a topic thread, allowing isolated conversations.
100
- */
101
- const TopicSessionModeSchema = z.enum(["disabled", "enabled"]).optional();
102
-
103
- export const FeishuGroupSchema = z
104
- .object({
105
- requireMention: z.boolean().optional(),
106
- tools: ToolPolicySchema,
107
- skills: z.array(z.string()).optional(),
108
- enabled: z.boolean().optional(),
109
- allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
110
- systemPrompt: z.string().optional(),
111
- topicSessionMode: TopicSessionModeSchema,
112
- })
113
- .strict();
114
-
115
- const FeishuSharedConfigShape = {
116
- webhookHost: z.string().optional(),
117
- webhookPort: z.number().int().positive().optional(),
118
- capabilities: z.array(z.string()).optional(),
119
- markdown: MarkdownConfigSchema,
120
- configWrites: z.boolean().optional(),
121
- dmPolicy: DmPolicySchema.optional(),
122
- allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
123
- groupPolicy: GroupPolicySchema.optional(),
124
- groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
125
- requireMention: z.boolean().optional(),
126
- groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
127
- historyLimit: z.number().int().min(0).optional(),
128
- dmHistoryLimit: z.number().int().min(0).optional(),
129
- dms: z.record(z.string(), DmConfigSchema).optional(),
130
- textChunkLimit: z.number().int().positive().optional(),
131
- chunkMode: z.enum(["length", "newline"]).optional(),
132
- blockStreamingCoalesce: BlockStreamingCoalesceSchema,
133
- mediaMaxMb: z.number().positive().optional(),
134
- heartbeat: ChannelHeartbeatVisibilitySchema,
135
- renderMode: RenderModeSchema,
136
- streaming: StreamingModeSchema,
137
- tools: FeishuToolsConfigSchema,
138
- };
139
-
140
- /**
141
- * Per-account configuration.
142
- * All fields are optional - missing fields inherit from top-level config.
143
- */
144
- export const FeishuAccountConfigSchema = z
145
- .object({
146
- enabled: z.boolean().optional(),
147
- name: z.string().optional(), // Display name for this account
148
- appId: z.string().optional(),
149
- appSecret: z.string().optional(),
150
- encryptKey: z.string().optional(),
151
- verificationToken: z.string().optional(),
152
- domain: FeishuDomainSchema.optional(),
153
- connectionMode: FeishuConnectionModeSchema.optional(),
154
- webhookPath: z.string().optional(),
155
- ...FeishuSharedConfigShape,
156
- })
157
- .strict();
158
-
159
- export const FeishuConfigSchema = z
160
- .object({
161
- enabled: z.boolean().optional(),
162
- // Top-level credentials (backward compatible for single-account mode)
163
- appId: z.string().optional(),
164
- appSecret: z.string().optional(),
165
- encryptKey: z.string().optional(),
166
- verificationToken: z.string().optional(),
167
- domain: FeishuDomainSchema.optional().default("feishu"),
168
- connectionMode: FeishuConnectionModeSchema.optional().default("websocket"),
169
- webhookPath: z.string().optional().default("/feishu/events"),
170
- ...FeishuSharedConfigShape,
171
- dmPolicy: DmPolicySchema.optional().default("pairing"),
172
- groupPolicy: GroupPolicySchema.optional().default("allowlist"),
173
- requireMention: z.boolean().optional().default(true),
174
- topicSessionMode: TopicSessionModeSchema,
175
- // Dynamic agent creation for DM users
176
- dynamicAgentCreation: DynamicAgentCreationSchema,
177
- // Multi-account configuration
178
- accounts: z.record(z.string(), FeishuAccountConfigSchema.optional()).optional(),
179
- })
180
- .strict()
181
- .superRefine((value, ctx) => {
182
- const defaultConnectionMode = value.connectionMode ?? "websocket";
183
- const defaultVerificationToken = value.verificationToken?.trim();
184
- if (defaultConnectionMode === "webhook" && !defaultVerificationToken) {
185
- ctx.addIssue({
186
- code: z.ZodIssueCode.custom,
187
- path: ["verificationToken"],
188
- message:
189
- 'channels.feishu.connectionMode="webhook" requires channels.feishu.verificationToken',
190
- });
191
- }
192
-
193
- for (const [accountId, account] of Object.entries(value.accounts ?? {})) {
194
- if (!account) {
195
- continue;
196
- }
197
- const accountConnectionMode = account.connectionMode ?? defaultConnectionMode;
198
- if (accountConnectionMode !== "webhook") {
199
- continue;
200
- }
201
- const accountVerificationToken =
202
- account.verificationToken?.trim() || defaultVerificationToken;
203
- if (!accountVerificationToken) {
204
- ctx.addIssue({
205
- code: z.ZodIssueCode.custom,
206
- path: ["accounts", accountId, "verificationToken"],
207
- message:
208
- `channels.feishu.accounts.${accountId}.connectionMode="webhook" requires ` +
209
- "a verificationToken (account-level or top-level)",
210
- });
211
- }
212
- }
213
-
214
- if (value.dmPolicy === "open") {
215
- const allowFrom = value.allowFrom ?? [];
216
- const hasWildcard = allowFrom.some((entry) => String(entry).trim() === "*");
217
- if (!hasWildcard) {
218
- ctx.addIssue({
219
- code: z.ZodIssueCode.custom,
220
- path: ["allowFrom"],
221
- message:
222
- 'channels.feishu.dmPolicy="open" requires channels.feishu.allowFrom to include "*"',
223
- });
224
- }
225
- }
226
- });