@poolzin/pool-bot 2026.2.17 → 2026.2.19

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 (488) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  5. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  6. package/dist/agents/bash-tools.shared.js +6 -0
  7. package/dist/agents/cli-runner/reliability.js +61 -0
  8. package/dist/agents/cli-watchdog-defaults.js +11 -0
  9. package/dist/agents/command-poll-backoff.js +63 -0
  10. package/dist/agents/current-time.js +16 -0
  11. package/dist/agents/model-alias-lines.js +18 -0
  12. package/dist/agents/model-auth-label.js +61 -0
  13. package/dist/agents/models-config.e2e-harness.js +115 -0
  14. package/dist/agents/ollama-stream.js +11 -3
  15. package/dist/agents/openclaw-tools.js +135 -0
  16. package/dist/agents/pi-auth-json.js +118 -0
  17. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  18. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  19. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  20. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  21. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  22. package/dist/agents/pi-tools.js +2 -0
  23. package/dist/agents/queued-file-writer.js +22 -0
  24. package/dist/agents/sandbox/docker.js +133 -40
  25. package/dist/agents/sandbox/fs-bridge.js +146 -0
  26. package/dist/agents/sandbox/fs-paths.js +205 -0
  27. package/dist/agents/sandbox/hash.js +4 -0
  28. package/dist/agents/sandbox-paths.js +3 -0
  29. package/dist/agents/session-dirs.js +20 -0
  30. package/dist/agents/skills/filter.js +24 -0
  31. package/dist/agents/skills/tools-dir.js +9 -0
  32. package/dist/agents/skills-install-download.js +290 -0
  33. package/dist/agents/skills-install-output.js +30 -0
  34. package/dist/agents/skills-install.download-test-utils.js +36 -0
  35. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  36. package/dist/agents/subagent-announce-queue.js +59 -15
  37. package/dist/agents/subagent-depth.js +137 -0
  38. package/dist/agents/subagent-registry.js +448 -96
  39. package/dist/agents/subagent-spawn.js +262 -0
  40. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  41. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  42. package/dist/agents/tool-display-common.js +782 -0
  43. package/dist/agents/tools/image-tool.js +1 -1
  44. package/dist/agents/tools/sessions-access.js +178 -0
  45. package/dist/agents/tools/sessions-resolution.js +206 -0
  46. package/dist/agents/tools/subagents-tool.js +616 -0
  47. package/dist/agents/workspace-dir.js +18 -0
  48. package/dist/agents/workspace-dirs.js +14 -0
  49. package/dist/agents/workspace.js +70 -0
  50. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  51. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  52. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  53. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  54. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  55. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  56. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  57. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  58. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  59. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  60. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  61. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  62. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  63. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  64. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  65. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  66. package/dist/browser/bridge-auth-registry.js +26 -0
  67. package/dist/browser/client-actions-url.js +10 -0
  68. package/dist/browser/control-auth.js +73 -0
  69. package/dist/browser/csrf.js +64 -0
  70. package/dist/browser/http-auth.js +52 -0
  71. package/dist/browser/paths.js +37 -0
  72. package/dist/browser/proxy-files.js +32 -0
  73. package/dist/browser/pw-ai-state.js +7 -0
  74. package/dist/browser/resolved-config-refresh.js +42 -0
  75. package/dist/browser/routes/path-output.js +1 -0
  76. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  77. package/dist/browser/server-middleware.js +31 -0
  78. package/dist/browser/test-port.js +16 -0
  79. package/dist/build-info.json +3 -3
  80. package/dist/canvas-host/file-resolver.js +43 -0
  81. package/dist/channels/account-summary.js +19 -0
  82. package/dist/channels/draft-stream-loop.js +77 -0
  83. package/dist/channels/plugins/account-helpers.js +26 -0
  84. package/dist/channels/telegram/allow-from.js +10 -0
  85. package/dist/cli/browser-cli-resize.js +22 -0
  86. package/dist/cli/browser-cli-shared.js +8 -0
  87. package/dist/cli/clawbot-cli.js +5 -0
  88. package/dist/cli/completion-cli.js +566 -0
  89. package/dist/cli/config-cli.js +63 -5
  90. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  91. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  92. package/dist/cli/daemon-cli-compat.js +80 -0
  93. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  94. package/dist/cli/program/action-reparse.js +17 -0
  95. package/dist/cli/program/command-registry.js +17 -0
  96. package/dist/cli/program/program-context.js +8 -0
  97. package/dist/cli/program/register.subclis.js +7 -0
  98. package/dist/cli/program/routes.js +233 -0
  99. package/dist/cli/qr-cli.js +132 -0
  100. package/dist/cli/requirements-test-fixtures.js +17 -0
  101. package/dist/cli/respawn-policy.js +4 -0
  102. package/dist/cli/shared/parse-port.js +18 -0
  103. package/dist/cli/skills-cli.format.js +241 -0
  104. package/dist/cli/update-cli/progress.js +121 -0
  105. package/dist/cli/update-cli/restart-helper.js +108 -0
  106. package/dist/cli/update-cli/shared.js +196 -0
  107. package/dist/cli/update-cli/status.js +97 -0
  108. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  109. package/dist/cli/update-cli/update-command.js +506 -0
  110. package/dist/cli/update-cli/wizard.js +130 -0
  111. package/dist/cli/update-cli.js +3 -9
  112. package/dist/cli/windows-argv.js +69 -0
  113. package/dist/commands/auth-choice-legacy.js +20 -0
  114. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  115. package/dist/commands/channel-test-helpers.js +19 -0
  116. package/dist/commands/cleanup-plan.js +10 -0
  117. package/dist/commands/cleanup-utils.js +7 -0
  118. package/dist/commands/config-validation.js +15 -0
  119. package/dist/commands/doctor-completion.js +112 -0
  120. package/dist/commands/doctor-memory-search.js +119 -0
  121. package/dist/commands/doctor-session-locks.js +73 -0
  122. package/dist/commands/doctor.e2e-harness.js +364 -0
  123. package/dist/commands/gateway-presence.js +19 -0
  124. package/dist/commands/model-default.js +35 -0
  125. package/dist/commands/models/fallbacks-shared.js +102 -0
  126. package/dist/commands/models/shared.js +24 -0
  127. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  128. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  129. package/dist/commands/onboard-auth.config-shared.js +116 -0
  130. package/dist/commands/onboard-config.js +16 -0
  131. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  132. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  133. package/dist/commands/openai-codex-oauth.js +40 -0
  134. package/dist/commands/test-runtime-config-helpers.js +21 -0
  135. package/dist/commands/test-wizard-helpers.js +68 -0
  136. package/dist/commands/vllm-setup.js +66 -0
  137. package/dist/compat/legacy-names.js +2 -0
  138. package/dist/config/backup-rotation.js +19 -0
  139. package/dist/config/env-preserve.js +122 -0
  140. package/dist/config/includes-scan.js +78 -0
  141. package/dist/config/plugins-allowlist.js +13 -0
  142. package/dist/config/schema.help.js +256 -0
  143. package/dist/config/schema.hints.js +189 -0
  144. package/dist/config/schema.irc.js +20 -0
  145. package/dist/config/schema.labels.js +317 -0
  146. package/dist/config/sessions/delivery-info.js +40 -0
  147. package/dist/config/types.irc.js +1 -0
  148. package/dist/config/zod-schema.agent-model.js +10 -0
  149. package/dist/config/zod-schema.allowdeny.js +35 -0
  150. package/dist/config/zod-schema.sensitive.js +4 -0
  151. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  152. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  153. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  154. package/dist/cron/isolated-agent.mocks.js +12 -0
  155. package/dist/cron/isolated-agent.test-setup.js +22 -0
  156. package/dist/cron/legacy-delivery.js +43 -0
  157. package/dist/cron/webhook-url.js +22 -0
  158. package/dist/daemon/arg-split.js +40 -0
  159. package/dist/daemon/exec-file.js +23 -0
  160. package/dist/daemon/output.js +6 -0
  161. package/dist/daemon/runtime-format.js +31 -0
  162. package/dist/daemon/schtasks-exec.js +4 -0
  163. package/dist/daemon/service-audit.js +22 -0
  164. package/dist/discord/client.js +41 -0
  165. package/dist/discord/components-registry.js +57 -0
  166. package/dist/discord/components.js +816 -0
  167. package/dist/discord/guilds.js +12 -0
  168. package/dist/discord/monitor/gateway-plugin.js +48 -0
  169. package/dist/discord/monitor/presence.js +30 -0
  170. package/dist/discord/send.components.js +115 -0
  171. package/dist/discord/send.shared.js +4 -0
  172. package/dist/discord/ui.js +26 -0
  173. package/dist/discord/voice-message.js +254 -0
  174. package/dist/gateway/agent-event-assistant-text.js +5 -0
  175. package/dist/gateway/agent-prompt.js +33 -0
  176. package/dist/gateway/auth-rate-limit.js +136 -0
  177. package/dist/gateway/channel-health-monitor.js +114 -0
  178. package/dist/gateway/control-ui-contract.js +1 -0
  179. package/dist/gateway/control-ui-csp.js +15 -0
  180. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  181. package/dist/gateway/http-auth-helpers.js +18 -0
  182. package/dist/gateway/http-common.js +18 -0
  183. package/dist/gateway/http-endpoint-helpers.js +27 -0
  184. package/dist/gateway/node-invoke-sanitize.js +11 -0
  185. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  186. package/dist/gateway/probe-auth.js +21 -0
  187. package/dist/gateway/protocol/index.js +7 -2
  188. package/dist/gateway/protocol/schema/mesh.js +54 -0
  189. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  190. package/dist/gateway/protocol/schema.js +1 -0
  191. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  192. package/dist/gateway/server-channels.js +11 -0
  193. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  194. package/dist/gateway/server-methods/base-hash.js +8 -0
  195. package/dist/gateway/server-methods/mesh.js +700 -0
  196. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  197. package/dist/gateway/server-methods/restart-request.js +13 -0
  198. package/dist/gateway/server-methods/validation.js +8 -0
  199. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  200. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  201. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  202. package/dist/gateway/test-helpers.js +2 -0
  203. package/dist/gateway/test-helpers.server.js +3 -1
  204. package/dist/gateway/test-http-response.js +12 -0
  205. package/dist/gateway/test-openai-responses-model.js +20 -0
  206. package/dist/gateway/test-temp-config.js +30 -0
  207. package/dist/gateway/test-with-server.js +32 -0
  208. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  209. package/dist/imessage/monitor/abort-handler.js +23 -0
  210. package/dist/imessage/monitor/inbound-processing.js +346 -0
  211. package/dist/imessage/monitor/parse-notification.js +64 -0
  212. package/dist/imessage/target-parsing-helpers.js +92 -0
  213. package/dist/infra/archive.js +244 -20
  214. package/dist/infra/detect-package-manager.js +26 -0
  215. package/dist/infra/exec-approvals-allowlist.js +257 -0
  216. package/dist/infra/exec-approvals-analysis.js +770 -0
  217. package/dist/infra/exec-approvals.js +13 -0
  218. package/dist/infra/file-lock.js +1 -0
  219. package/dist/infra/gemini-auth.js +39 -0
  220. package/dist/infra/heartbeat-active-hours.js +85 -0
  221. package/dist/infra/heartbeat-events-filter.js +50 -0
  222. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  223. package/dist/infra/http-body.js +265 -0
  224. package/dist/infra/install-package-dir.js +50 -0
  225. package/dist/infra/install-safe-path.js +49 -0
  226. package/dist/infra/json-files.js +49 -0
  227. package/dist/infra/jsonl-socket.js +52 -0
  228. package/dist/infra/map-size.js +14 -0
  229. package/dist/infra/net/hostname.js +7 -0
  230. package/dist/infra/npm-registry-spec.js +39 -0
  231. package/dist/infra/openclaw-root.js +109 -0
  232. package/dist/infra/outbound/delivery-queue.js +214 -0
  233. package/dist/infra/outbound/identity.js +23 -0
  234. package/dist/infra/outbound/message-action-params.js +307 -0
  235. package/dist/infra/outbound/tool-payload.js +21 -0
  236. package/dist/infra/package-json.js +23 -0
  237. package/dist/infra/pairing-files.js +19 -0
  238. package/dist/infra/pairing-token.js +9 -0
  239. package/dist/infra/path-prepend.js +51 -0
  240. package/dist/infra/process-respawn.js +49 -0
  241. package/dist/infra/runtime-status.js +16 -0
  242. package/dist/infra/session-cost-usage.types.js +1 -0
  243. package/dist/infra/session-maintenance-warning.js +89 -0
  244. package/dist/infra/system-run-command.js +78 -0
  245. package/dist/infra/tmp-openclaw-dir.js +81 -0
  246. package/dist/infra/tmp-poolbot-dir.js +2 -0
  247. package/dist/infra/update-channels.js +19 -0
  248. package/dist/line/actions.js +45 -0
  249. package/dist/line/channel-access-token.js +9 -0
  250. package/dist/line/flex-templates/basic-cards.js +332 -0
  251. package/dist/line/flex-templates/common.js +18 -0
  252. package/dist/line/flex-templates/media-control-cards.js +453 -0
  253. package/dist/line/flex-templates/message.js +10 -0
  254. package/dist/line/flex-templates/schedule-cards.js +399 -0
  255. package/dist/line/flex-templates/types.js +1 -0
  256. package/dist/line/webhook-node.js +100 -0
  257. package/dist/line/webhook-utils.js +11 -0
  258. package/dist/logging/timestamps.js +14 -0
  259. package/dist/markdown/whatsapp.js +62 -0
  260. package/dist/media/base64.js +34 -0
  261. package/dist/media/local-roots.js +32 -0
  262. package/dist/media/outbound-attachment.js +10 -0
  263. package/dist/media/read-response-with-limit.js +41 -0
  264. package/dist/media/sniff-mime-from-base64.js +19 -0
  265. package/dist/media-understanding/audio-preflight.js +67 -0
  266. package/dist/media-understanding/fs.js +13 -0
  267. package/dist/media-understanding/output-extract.js +26 -0
  268. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  269. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  270. package/dist/media-understanding/providers/shared.js +7 -0
  271. package/dist/media-understanding/runner.entries.js +459 -0
  272. package/dist/memory/batch-error-utils.js +11 -0
  273. package/dist/memory/batch-http.js +27 -0
  274. package/dist/memory/batch-output.js +29 -0
  275. package/dist/memory/batch-runner.js +22 -0
  276. package/dist/memory/batch-upload.js +23 -0
  277. package/dist/memory/batch-utils.js +26 -0
  278. package/dist/memory/embeddings-debug.js +11 -0
  279. package/dist/memory/embeddings-remote-client.js +22 -0
  280. package/dist/memory/embeddings-remote-fetch.js +14 -0
  281. package/dist/memory/manager-embedding-ops.js +616 -0
  282. package/dist/memory/manager-sync-ops.js +953 -0
  283. package/dist/memory/qmd-manager.js +1061 -0
  284. package/dist/memory/qmd-query-parser.js +107 -0
  285. package/dist/memory/qmd-scope.js +93 -0
  286. package/dist/memory/search-manager.js +0 -1
  287. package/dist/memory/sync-index.js +21 -0
  288. package/dist/memory/sync-progress.js +22 -0
  289. package/dist/memory/sync-stale.js +30 -0
  290. package/dist/memory/test-embeddings-mock.js +16 -0
  291. package/dist/memory/test-manager-helpers.js +14 -0
  292. package/dist/memory/test-runtime-mocks.js +11 -0
  293. package/dist/node-host/invoke-browser.js +177 -0
  294. package/dist/node-host/invoke.js +685 -0
  295. package/dist/pairing/setup-code.js +285 -0
  296. package/dist/plugin-sdk/account-id.js +1 -0
  297. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  298. package/dist/plugin-sdk/allow-from.js +47 -0
  299. package/dist/plugin-sdk/command-auth.js +23 -0
  300. package/dist/plugin-sdk/config-paths.js +9 -0
  301. package/dist/plugin-sdk/file-lock.js +116 -0
  302. package/dist/plugin-sdk/json-store.js +31 -0
  303. package/dist/plugin-sdk/onboarding.js +28 -0
  304. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  305. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  306. package/dist/plugin-sdk/status-helpers.js +35 -0
  307. package/dist/plugin-sdk/text-chunking.js +31 -0
  308. package/dist/plugin-sdk/tool-send.js +12 -0
  309. package/dist/plugin-sdk/webhook-path.js +27 -0
  310. package/dist/plugin-sdk/webhook-targets.js +34 -0
  311. package/dist/plugins/hooks.test-helpers.js +21 -0
  312. package/dist/plugins/uninstall.js +171 -0
  313. package/dist/process/supervisor/adapters/child.js +143 -0
  314. package/dist/process/supervisor/adapters/env.js +13 -0
  315. package/dist/process/supervisor/adapters/pty.js +148 -0
  316. package/dist/process/supervisor/index.js +10 -0
  317. package/dist/process/supervisor/registry.js +117 -0
  318. package/dist/process/supervisor/supervisor.js +244 -0
  319. package/dist/process/supervisor/types.js +1 -0
  320. package/dist/providers/google-shared.test-helpers.js +75 -0
  321. package/dist/security/audit-channel.js +419 -0
  322. package/dist/security/audit-tool-policy.js +1 -0
  323. package/dist/security/scan-paths.js +12 -0
  324. package/dist/sessions/input-provenance.js +55 -0
  325. package/dist/sessions/session-key-utils.js +7 -0
  326. package/dist/shared/chat-content.js +31 -0
  327. package/dist/shared/chat-envelope.js +45 -0
  328. package/dist/shared/config-eval.js +117 -0
  329. package/dist/shared/device-auth.js +16 -0
  330. package/dist/shared/entry-metadata.js +9 -0
  331. package/dist/shared/entry-status.js +25 -0
  332. package/dist/shared/frontmatter.js +98 -0
  333. package/dist/shared/model-param-b.js +19 -0
  334. package/dist/shared/net/ipv4.js +17 -0
  335. package/dist/shared/node-match.js +53 -0
  336. package/dist/shared/requirements.js +128 -0
  337. package/dist/shared/subagents-format.js +84 -0
  338. package/dist/shared/usage-aggregates.js +28 -0
  339. package/dist/signal/monitor/mentions.js +45 -0
  340. package/dist/signal/rpc-context.js +19 -0
  341. package/dist/slack/blocks-fallback.js +76 -0
  342. package/dist/slack/blocks-input.js +40 -0
  343. package/dist/slack/draft-stream.js +106 -0
  344. package/dist/slack/message-actions.js +51 -0
  345. package/dist/slack/modal-metadata.js +32 -0
  346. package/dist/slack/monitor/events/interactions.js +462 -0
  347. package/dist/slack/monitor/room-context.js +17 -0
  348. package/dist/slack/stream-mode.js +41 -0
  349. package/dist/telegram/bot-native-command-menu.js +64 -0
  350. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  351. package/dist/telegram/button-types.js +1 -0
  352. package/dist/telegram/group-access.js +65 -0
  353. package/dist/telegram/outbound-params.js +21 -0
  354. package/dist/telegram/poll-vote-cache.js +21 -0
  355. package/dist/terminal/health-style.js +36 -0
  356. package/dist/test-utils/chunk-test-helpers.js +21 -0
  357. package/dist/test-utils/env.js +72 -0
  358. package/dist/test-utils/exec-assertions.js +12 -0
  359. package/dist/test-utils/imessage-test-plugin.js +54 -0
  360. package/dist/test-utils/mock-http-response.js +17 -0
  361. package/dist/test-utils/vitest-mock-fn.js +1 -0
  362. package/dist/tts/tts-core.js +550 -0
  363. package/dist/utils/chunk-items.js +10 -0
  364. package/dist/utils/reaction-level.js +52 -0
  365. package/dist/utils/safe-json.js +22 -0
  366. package/dist/utils/with-timeout.js +14 -0
  367. package/dist/web/media.js +17 -5
  368. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  369. package/dist/wizard/onboarding.completion.js +74 -0
  370. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  371. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  372. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  373. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  374. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  375. package/extensions/bluebubbles/src/multipart.ts +32 -0
  376. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  377. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  378. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  379. package/extensions/device-pair/index.ts +554 -0
  380. package/extensions/device-pair/poolbot.plugin.json +20 -0
  381. package/extensions/discord/src/channel.js +366 -0
  382. package/extensions/discord/src/runtime.js +10 -0
  383. package/extensions/feishu/index.ts +63 -0
  384. package/extensions/feishu/package.json +37 -0
  385. package/extensions/feishu/poolbot.plugin.json +10 -0
  386. package/extensions/feishu/skills/feishu-doc/SKILL.md +105 -0
  387. package/extensions/feishu/skills/feishu-doc/references/block-types.md +103 -0
  388. package/extensions/feishu/skills/feishu-drive/SKILL.md +97 -0
  389. package/extensions/feishu/skills/feishu-perm/SKILL.md +119 -0
  390. package/extensions/feishu/skills/feishu-wiki/SKILL.md +111 -0
  391. package/extensions/feishu/src/accounts.ts +114 -0
  392. package/extensions/feishu/src/bitable.ts +739 -0
  393. package/extensions/feishu/src/bot.ts +965 -0
  394. package/extensions/feishu/src/channel.ts +351 -0
  395. package/extensions/feishu/src/client.ts +118 -0
  396. package/extensions/feishu/src/config-schema.ts +206 -0
  397. package/extensions/feishu/src/dedup.ts +33 -0
  398. package/extensions/feishu/src/directory.ts +177 -0
  399. package/extensions/feishu/src/doc-schema.ts +47 -0
  400. package/extensions/feishu/src/docx.ts +536 -0
  401. package/extensions/feishu/src/drive-schema.ts +46 -0
  402. package/extensions/feishu/src/drive.ts +227 -0
  403. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  404. package/extensions/feishu/src/media.ts +449 -0
  405. package/extensions/feishu/src/mention.ts +126 -0
  406. package/extensions/feishu/src/monitor.ts +330 -0
  407. package/extensions/feishu/src/onboarding.ts +359 -0
  408. package/extensions/feishu/src/outbound.ts +55 -0
  409. package/extensions/feishu/src/perm-schema.ts +52 -0
  410. package/extensions/feishu/src/perm.ts +173 -0
  411. package/extensions/feishu/src/policy.ts +84 -0
  412. package/extensions/feishu/src/probe.ts +44 -0
  413. package/extensions/feishu/src/reactions.ts +160 -0
  414. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  415. package/extensions/feishu/src/runtime.ts +14 -0
  416. package/extensions/feishu/src/send-result.ts +29 -0
  417. package/extensions/feishu/src/send.ts +335 -0
  418. package/extensions/feishu/src/streaming-card.ts +223 -0
  419. package/extensions/feishu/src/targets.ts +78 -0
  420. package/extensions/feishu/src/tools-config.ts +21 -0
  421. package/extensions/feishu/src/types.ts +81 -0
  422. package/extensions/feishu/src/typing.ts +80 -0
  423. package/extensions/feishu/src/wiki-schema.ts +55 -0
  424. package/extensions/feishu/src/wiki.ts +232 -0
  425. package/extensions/imessage/src/channel.js +253 -0
  426. package/extensions/imessage/src/runtime.js +10 -0
  427. package/extensions/irc/index.ts +17 -0
  428. package/extensions/irc/package.json +14 -0
  429. package/extensions/irc/poolbot.plugin.json +9 -0
  430. package/extensions/irc/src/accounts.ts +268 -0
  431. package/extensions/irc/src/channel.ts +367 -0
  432. package/extensions/irc/src/client.ts +439 -0
  433. package/extensions/irc/src/config-schema.ts +97 -0
  434. package/extensions/irc/src/connect-options.ts +30 -0
  435. package/extensions/irc/src/control-chars.ts +22 -0
  436. package/extensions/irc/src/inbound.ts +334 -0
  437. package/extensions/irc/src/monitor.ts +147 -0
  438. package/extensions/irc/src/normalize.ts +117 -0
  439. package/extensions/irc/src/onboarding.ts +479 -0
  440. package/extensions/irc/src/policy.ts +157 -0
  441. package/extensions/irc/src/probe.ts +53 -0
  442. package/extensions/irc/src/protocol.ts +169 -0
  443. package/extensions/irc/src/runtime.ts +14 -0
  444. package/extensions/irc/src/send.ts +88 -0
  445. package/extensions/irc/src/types.ts +93 -0
  446. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  447. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  448. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  449. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  450. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  451. package/extensions/minimax-portal-auth/README.md +33 -0
  452. package/extensions/minimax-portal-auth/index.ts +161 -0
  453. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  454. package/extensions/minimax-portal-auth/package.json +15 -0
  455. package/extensions/minimax-portal-auth/poolbot.plugin.json +9 -0
  456. package/extensions/msteams/src/file-lock.ts +1 -0
  457. package/extensions/msteams/src/graph.ts +92 -0
  458. package/extensions/msteams/src/mentions.ts +114 -0
  459. package/extensions/msteams/src/test-runtime.ts +16 -0
  460. package/extensions/openai-codex-auth/README.md +82 -0
  461. package/extensions/openai-codex-auth/index.ts +177 -0
  462. package/extensions/openai-codex-auth/package.json +15 -0
  463. package/extensions/openai-codex-auth/poolbot.plugin.json +9 -0
  464. package/extensions/phone-control/index.ts +421 -0
  465. package/extensions/phone-control/poolbot.plugin.json +10 -0
  466. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  467. package/extensions/signal/src/channel.js +273 -0
  468. package/extensions/signal/src/runtime.js +10 -0
  469. package/extensions/slack/src/channel.js +489 -0
  470. package/extensions/slack/src/runtime.js +10 -0
  471. package/extensions/talk-voice/index.ts +150 -0
  472. package/extensions/talk-voice/poolbot.plugin.json +10 -0
  473. package/extensions/telegram/src/channel.js +424 -0
  474. package/extensions/telegram/src/runtime.js +10 -0
  475. package/extensions/thread-ownership/index.ts +133 -0
  476. package/extensions/thread-ownership/poolbot.plugin.json +28 -0
  477. package/extensions/tlon/src/account-fields.ts +25 -0
  478. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  479. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  480. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  481. package/extensions/tlon/src/urbit/context.ts +47 -0
  482. package/extensions/tlon/src/urbit/errors.ts +51 -0
  483. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  484. package/extensions/twitch/src/test-fixtures.ts +30 -0
  485. package/extensions/voice-call/src/allowlist.ts +19 -0
  486. package/extensions/whatsapp/src/channel.js +429 -0
  487. package/extensions/whatsapp/src/runtime.js +10 -0
  488. package/package.json +1 -1
@@ -0,0 +1,330 @@
1
+ import * as http from "http";
2
+ import * as Lark from "@larksuiteoapi/node-sdk";
3
+ import {
4
+ type ClawdbotConfig,
5
+ type RuntimeEnv,
6
+ type HistoryEntry,
7
+ installRequestBodyLimitGuard,
8
+ } from "poolbot/plugin-sdk";
9
+ import { resolveFeishuAccount, listEnabledFeishuAccounts } from "./accounts.js";
10
+ import { handleFeishuMessage, type FeishuMessageEvent, type FeishuBotAddedEvent } from "./bot.js";
11
+ import { createFeishuWSClient, createEventDispatcher } from "./client.js";
12
+ import { probeFeishu } from "./probe.js";
13
+ import type { ResolvedFeishuAccount } from "./types.js";
14
+
15
+ export type MonitorFeishuOpts = {
16
+ config?: ClawdbotConfig;
17
+ runtime?: RuntimeEnv;
18
+ abortSignal?: AbortSignal;
19
+ accountId?: string;
20
+ };
21
+
22
+ // Per-account WebSocket clients, HTTP servers, and bot info
23
+ const wsClients = new Map<string, Lark.WSClient>();
24
+ const httpServers = new Map<string, http.Server>();
25
+ const botOpenIds = new Map<string, string>();
26
+ const FEISHU_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
27
+ const FEISHU_WEBHOOK_BODY_TIMEOUT_MS = 30_000;
28
+
29
+ async function fetchBotOpenId(account: ResolvedFeishuAccount): Promise<string | undefined> {
30
+ try {
31
+ const result = await probeFeishu(account);
32
+ return result.ok ? result.botOpenId : undefined;
33
+ } catch {
34
+ return undefined;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Register common event handlers on an EventDispatcher.
40
+ * When fireAndForget is true (webhook mode), message handling is not awaited
41
+ * to avoid blocking the HTTP response (Lark requires <3s response).
42
+ */
43
+ function registerEventHandlers(
44
+ eventDispatcher: Lark.EventDispatcher,
45
+ context: {
46
+ cfg: ClawdbotConfig;
47
+ accountId: string;
48
+ runtime?: RuntimeEnv;
49
+ chatHistories: Map<string, HistoryEntry[]>;
50
+ fireAndForget?: boolean;
51
+ },
52
+ ) {
53
+ const { cfg, accountId, runtime, chatHistories, fireAndForget } = context;
54
+ const log = runtime?.log ?? console.log;
55
+ const error = runtime?.error ?? console.error;
56
+
57
+ eventDispatcher.register({
58
+ "im.message.receive_v1": async (data) => {
59
+ try {
60
+ const event = data as unknown as FeishuMessageEvent;
61
+ const promise = handleFeishuMessage({
62
+ cfg,
63
+ event,
64
+ botOpenId: botOpenIds.get(accountId),
65
+ runtime,
66
+ chatHistories,
67
+ accountId,
68
+ });
69
+ if (fireAndForget) {
70
+ promise.catch((err) => {
71
+ error(`feishu[${accountId}]: error handling message: ${String(err)}`);
72
+ });
73
+ } else {
74
+ await promise;
75
+ }
76
+ } catch (err) {
77
+ error(`feishu[${accountId}]: error handling message: ${String(err)}`);
78
+ }
79
+ },
80
+ "im.message.message_read_v1": async () => {
81
+ // Ignore read receipts
82
+ },
83
+ "im.chat.member.bot.added_v1": async (data) => {
84
+ try {
85
+ const event = data as unknown as FeishuBotAddedEvent;
86
+ log(`feishu[${accountId}]: bot added to chat ${event.chat_id}`);
87
+ } catch (err) {
88
+ error(`feishu[${accountId}]: error handling bot added event: ${String(err)}`);
89
+ }
90
+ },
91
+ "im.chat.member.bot.deleted_v1": async (data) => {
92
+ try {
93
+ const event = data as unknown as { chat_id: string };
94
+ log(`feishu[${accountId}]: bot removed from chat ${event.chat_id}`);
95
+ } catch (err) {
96
+ error(`feishu[${accountId}]: error handling bot removed event: ${String(err)}`);
97
+ }
98
+ },
99
+ });
100
+ }
101
+
102
+ type MonitorAccountParams = {
103
+ cfg: ClawdbotConfig;
104
+ account: ResolvedFeishuAccount;
105
+ runtime?: RuntimeEnv;
106
+ abortSignal?: AbortSignal;
107
+ };
108
+
109
+ /**
110
+ * Monitor a single Feishu account.
111
+ */
112
+ async function monitorSingleAccount(params: MonitorAccountParams): Promise<void> {
113
+ const { cfg, account, runtime, abortSignal } = params;
114
+ const { accountId } = account;
115
+ const log = runtime?.log ?? console.log;
116
+
117
+ // Fetch bot open_id
118
+ const botOpenId = await fetchBotOpenId(account);
119
+ botOpenIds.set(accountId, botOpenId ?? "");
120
+ log(`feishu[${accountId}]: bot open_id resolved: ${botOpenId ?? "unknown"}`);
121
+
122
+ const connectionMode = account.config.connectionMode ?? "websocket";
123
+ const eventDispatcher = createEventDispatcher(account);
124
+ const chatHistories = new Map<string, HistoryEntry[]>();
125
+
126
+ registerEventHandlers(eventDispatcher, {
127
+ cfg,
128
+ accountId,
129
+ runtime,
130
+ chatHistories,
131
+ fireAndForget: connectionMode === "webhook",
132
+ });
133
+
134
+ if (connectionMode === "webhook") {
135
+ return monitorWebhook({ params, accountId, eventDispatcher });
136
+ }
137
+
138
+ return monitorWebSocket({ params, accountId, eventDispatcher });
139
+ }
140
+
141
+ type ConnectionParams = {
142
+ params: MonitorAccountParams;
143
+ accountId: string;
144
+ eventDispatcher: Lark.EventDispatcher;
145
+ };
146
+
147
+ async function monitorWebSocket({
148
+ params,
149
+ accountId,
150
+ eventDispatcher,
151
+ }: ConnectionParams): Promise<void> {
152
+ const { account, runtime, abortSignal } = params;
153
+ const log = runtime?.log ?? console.log;
154
+ const error = runtime?.error ?? console.error;
155
+
156
+ log(`feishu[${accountId}]: starting WebSocket connection...`);
157
+
158
+ const wsClient = createFeishuWSClient(account);
159
+ wsClients.set(accountId, wsClient);
160
+
161
+ return new Promise((resolve, reject) => {
162
+ const cleanup = () => {
163
+ wsClients.delete(accountId);
164
+ botOpenIds.delete(accountId);
165
+ };
166
+
167
+ const handleAbort = () => {
168
+ log(`feishu[${accountId}]: abort signal received, stopping`);
169
+ cleanup();
170
+ resolve();
171
+ };
172
+
173
+ if (abortSignal?.aborted) {
174
+ cleanup();
175
+ resolve();
176
+ return;
177
+ }
178
+
179
+ abortSignal?.addEventListener("abort", handleAbort, { once: true });
180
+
181
+ try {
182
+ wsClient.start({ eventDispatcher });
183
+ log(`feishu[${accountId}]: WebSocket client started`);
184
+ } catch (err) {
185
+ cleanup();
186
+ abortSignal?.removeEventListener("abort", handleAbort);
187
+ reject(err);
188
+ }
189
+ });
190
+ }
191
+
192
+ async function monitorWebhook({
193
+ params,
194
+ accountId,
195
+ eventDispatcher,
196
+ }: ConnectionParams): Promise<void> {
197
+ const { account, runtime, abortSignal } = params;
198
+ const log = runtime?.log ?? console.log;
199
+ const error = runtime?.error ?? console.error;
200
+
201
+ const port = account.config.webhookPort ?? 3000;
202
+ const path = account.config.webhookPath ?? "/feishu/events";
203
+
204
+ log(`feishu[${accountId}]: starting Webhook server on port ${port}, path ${path}...`);
205
+
206
+ const server = http.createServer();
207
+ const webhookHandler = Lark.adaptDefault(path, eventDispatcher, { autoChallenge: true });
208
+ server.on("request", (req, res) => {
209
+ const guard = installRequestBodyLimitGuard(req, res, {
210
+ maxBytes: FEISHU_WEBHOOK_MAX_BODY_BYTES,
211
+ timeoutMs: FEISHU_WEBHOOK_BODY_TIMEOUT_MS,
212
+ responseFormat: "text",
213
+ });
214
+ if (guard.isTripped()) {
215
+ return;
216
+ }
217
+ void Promise.resolve(webhookHandler(req, res))
218
+ .catch((err) => {
219
+ if (!guard.isTripped()) {
220
+ error(`feishu[${accountId}]: webhook handler error: ${String(err)}`);
221
+ }
222
+ })
223
+ .finally(() => {
224
+ guard.dispose();
225
+ });
226
+ });
227
+ httpServers.set(accountId, server);
228
+
229
+ return new Promise((resolve, reject) => {
230
+ const cleanup = () => {
231
+ server.close();
232
+ httpServers.delete(accountId);
233
+ botOpenIds.delete(accountId);
234
+ };
235
+
236
+ const handleAbort = () => {
237
+ log(`feishu[${accountId}]: abort signal received, stopping Webhook server`);
238
+ cleanup();
239
+ resolve();
240
+ };
241
+
242
+ if (abortSignal?.aborted) {
243
+ cleanup();
244
+ resolve();
245
+ return;
246
+ }
247
+
248
+ abortSignal?.addEventListener("abort", handleAbort, { once: true });
249
+
250
+ server.listen(port, () => {
251
+ log(`feishu[${accountId}]: Webhook server listening on port ${port}`);
252
+ });
253
+
254
+ server.on("error", (err) => {
255
+ error(`feishu[${accountId}]: Webhook server error: ${err}`);
256
+ abortSignal?.removeEventListener("abort", handleAbort);
257
+ reject(err);
258
+ });
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Main entry: start monitoring for all enabled accounts.
264
+ */
265
+ export async function monitorFeishuProvider(opts: MonitorFeishuOpts = {}): Promise<void> {
266
+ const cfg = opts.config;
267
+ if (!cfg) {
268
+ throw new Error("Config is required for Feishu monitor");
269
+ }
270
+
271
+ const log = opts.runtime?.log ?? console.log;
272
+
273
+ // If accountId is specified, only monitor that account
274
+ if (opts.accountId) {
275
+ const account = resolveFeishuAccount({ cfg, accountId: opts.accountId });
276
+ if (!account.enabled || !account.configured) {
277
+ throw new Error(`Feishu account "${opts.accountId}" not configured or disabled`);
278
+ }
279
+ return monitorSingleAccount({
280
+ cfg,
281
+ account,
282
+ runtime: opts.runtime,
283
+ abortSignal: opts.abortSignal,
284
+ });
285
+ }
286
+
287
+ // Otherwise, start all enabled accounts
288
+ const accounts = listEnabledFeishuAccounts(cfg);
289
+ if (accounts.length === 0) {
290
+ throw new Error("No enabled Feishu accounts configured");
291
+ }
292
+
293
+ log(
294
+ `feishu: starting ${accounts.length} account(s): ${accounts.map((a) => a.accountId).join(", ")}`,
295
+ );
296
+
297
+ // Start all accounts in parallel
298
+ await Promise.all(
299
+ accounts.map((account) =>
300
+ monitorSingleAccount({
301
+ cfg,
302
+ account,
303
+ runtime: opts.runtime,
304
+ abortSignal: opts.abortSignal,
305
+ }),
306
+ ),
307
+ );
308
+ }
309
+
310
+ /**
311
+ * Stop monitoring for a specific account or all accounts.
312
+ */
313
+ export function stopFeishuMonitor(accountId?: string): void {
314
+ if (accountId) {
315
+ wsClients.delete(accountId);
316
+ const server = httpServers.get(accountId);
317
+ if (server) {
318
+ server.close();
319
+ httpServers.delete(accountId);
320
+ }
321
+ botOpenIds.delete(accountId);
322
+ } else {
323
+ wsClients.clear();
324
+ for (const server of httpServers.values()) {
325
+ server.close();
326
+ }
327
+ httpServers.clear();
328
+ botOpenIds.clear();
329
+ }
330
+ }
@@ -0,0 +1,359 @@
1
+ import type {
2
+ ChannelOnboardingAdapter,
3
+ ChannelOnboardingDmPolicy,
4
+ ClawdbotConfig,
5
+ DmPolicy,
6
+ WizardPrompter,
7
+ } from "poolbot/plugin-sdk";
8
+ import { addWildcardAllowFrom, DEFAULT_ACCOUNT_ID, formatDocsLink } from "poolbot/plugin-sdk";
9
+ import { resolveFeishuCredentials } from "./accounts.js";
10
+ import { probeFeishu } from "./probe.js";
11
+ import type { FeishuConfig } from "./types.js";
12
+
13
+ const channel = "feishu" as const;
14
+
15
+ function setFeishuDmPolicy(cfg: ClawdbotConfig, dmPolicy: DmPolicy): ClawdbotConfig {
16
+ const allowFrom =
17
+ dmPolicy === "open"
18
+ ? addWildcardAllowFrom(cfg.channels?.feishu?.allowFrom)?.map((entry) => String(entry))
19
+ : undefined;
20
+ return {
21
+ ...cfg,
22
+ channels: {
23
+ ...cfg.channels,
24
+ feishu: {
25
+ ...cfg.channels?.feishu,
26
+ dmPolicy,
27
+ ...(allowFrom ? { allowFrom } : {}),
28
+ },
29
+ },
30
+ };
31
+ }
32
+
33
+ function setFeishuAllowFrom(cfg: ClawdbotConfig, allowFrom: string[]): ClawdbotConfig {
34
+ return {
35
+ ...cfg,
36
+ channels: {
37
+ ...cfg.channels,
38
+ feishu: {
39
+ ...cfg.channels?.feishu,
40
+ allowFrom,
41
+ },
42
+ },
43
+ };
44
+ }
45
+
46
+ function parseAllowFromInput(raw: string): string[] {
47
+ return raw
48
+ .split(/[\n,;]+/g)
49
+ .map((entry) => entry.trim())
50
+ .filter(Boolean);
51
+ }
52
+
53
+ async function promptFeishuAllowFrom(params: {
54
+ cfg: ClawdbotConfig;
55
+ prompter: WizardPrompter;
56
+ }): Promise<ClawdbotConfig> {
57
+ const existing = params.cfg.channels?.feishu?.allowFrom ?? [];
58
+ await params.prompter.note(
59
+ [
60
+ "Allowlist Feishu DMs by open_id or user_id.",
61
+ "You can find user open_id in Feishu admin console or via API.",
62
+ "Examples:",
63
+ "- ou_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
64
+ "- on_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
65
+ ].join("\n"),
66
+ "Feishu allowlist",
67
+ );
68
+
69
+ while (true) {
70
+ const entry = await params.prompter.text({
71
+ message: "Feishu allowFrom (user open_ids)",
72
+ placeholder: "ou_xxxxx, ou_yyyyy",
73
+ initialValue: existing[0] ? String(existing[0]) : undefined,
74
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
75
+ });
76
+ const parts = parseAllowFromInput(String(entry));
77
+ if (parts.length === 0) {
78
+ await params.prompter.note("Enter at least one user.", "Feishu allowlist");
79
+ continue;
80
+ }
81
+
82
+ const unique = [
83
+ ...new Set([
84
+ ...existing.map((v: string | number) => String(v).trim()).filter(Boolean),
85
+ ...parts,
86
+ ]),
87
+ ];
88
+ return setFeishuAllowFrom(params.cfg, unique);
89
+ }
90
+ }
91
+
92
+ async function noteFeishuCredentialHelp(prompter: WizardPrompter): Promise<void> {
93
+ await prompter.note(
94
+ [
95
+ "1) Go to Feishu Open Platform (open.feishu.cn)",
96
+ "2) Create a self-built app",
97
+ "3) Get App ID and App Secret from Credentials page",
98
+ "4) Enable required permissions: im:message, im:chat, contact:user.base:readonly",
99
+ "5) Publish the app or add it to a test group",
100
+ "Tip: you can also set FEISHU_APP_ID / FEISHU_APP_SECRET env vars.",
101
+ `Docs: ${formatDocsLink("/channels/feishu", "feishu")}`,
102
+ ].join("\n"),
103
+ "Feishu credentials",
104
+ );
105
+ }
106
+
107
+ function setFeishuGroupPolicy(
108
+ cfg: ClawdbotConfig,
109
+ groupPolicy: "open" | "allowlist" | "disabled",
110
+ ): ClawdbotConfig {
111
+ return {
112
+ ...cfg,
113
+ channels: {
114
+ ...cfg.channels,
115
+ feishu: {
116
+ ...cfg.channels?.feishu,
117
+ enabled: true,
118
+ groupPolicy,
119
+ },
120
+ },
121
+ };
122
+ }
123
+
124
+ function setFeishuGroupAllowFrom(cfg: ClawdbotConfig, groupAllowFrom: string[]): ClawdbotConfig {
125
+ return {
126
+ ...cfg,
127
+ channels: {
128
+ ...cfg.channels,
129
+ feishu: {
130
+ ...cfg.channels?.feishu,
131
+ groupAllowFrom,
132
+ },
133
+ },
134
+ };
135
+ }
136
+
137
+ const dmPolicy: ChannelOnboardingDmPolicy = {
138
+ label: "Feishu",
139
+ channel,
140
+ policyKey: "channels.feishu.dmPolicy",
141
+ allowFromKey: "channels.feishu.allowFrom",
142
+ getCurrent: (cfg) => (cfg.channels?.feishu as FeishuConfig | undefined)?.dmPolicy ?? "pairing",
143
+ setPolicy: (cfg, policy) => setFeishuDmPolicy(cfg, policy),
144
+ promptAllowFrom: promptFeishuAllowFrom,
145
+ };
146
+
147
+ export const feishuOnboardingAdapter: ChannelOnboardingAdapter = {
148
+ channel,
149
+ getStatus: async ({ cfg }) => {
150
+ const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
151
+ const configured = Boolean(resolveFeishuCredentials(feishuCfg));
152
+
153
+ // Try to probe if configured
154
+ let probeResult = null;
155
+ if (configured && feishuCfg) {
156
+ try {
157
+ probeResult = await probeFeishu(feishuCfg);
158
+ } catch {
159
+ // Ignore probe errors
160
+ }
161
+ }
162
+
163
+ const statusLines: string[] = [];
164
+ if (!configured) {
165
+ statusLines.push("Feishu: needs app credentials");
166
+ } else if (probeResult?.ok) {
167
+ statusLines.push(
168
+ `Feishu: connected as ${probeResult.botName ?? probeResult.botOpenId ?? "bot"}`,
169
+ );
170
+ } else {
171
+ statusLines.push("Feishu: configured (connection not verified)");
172
+ }
173
+
174
+ return {
175
+ channel,
176
+ configured,
177
+ statusLines,
178
+ selectionHint: configured ? "configured" : "needs app creds",
179
+ quickstartScore: configured ? 2 : 0,
180
+ };
181
+ },
182
+
183
+ configure: async ({ cfg, prompter }) => {
184
+ const feishuCfg = cfg.channels?.feishu as FeishuConfig | undefined;
185
+ const resolved = resolveFeishuCredentials(feishuCfg);
186
+ const hasConfigCreds = Boolean(feishuCfg?.appId?.trim() && feishuCfg?.appSecret?.trim());
187
+ const canUseEnv = Boolean(
188
+ !hasConfigCreds && process.env.FEISHU_APP_ID?.trim() && process.env.FEISHU_APP_SECRET?.trim(),
189
+ );
190
+
191
+ let next = cfg;
192
+ let appId: string | null = null;
193
+ let appSecret: string | null = null;
194
+
195
+ if (!resolved) {
196
+ await noteFeishuCredentialHelp(prompter);
197
+ }
198
+
199
+ if (canUseEnv) {
200
+ const keepEnv = await prompter.confirm({
201
+ message: "FEISHU_APP_ID + FEISHU_APP_SECRET detected. Use env vars?",
202
+ initialValue: true,
203
+ });
204
+ if (keepEnv) {
205
+ next = {
206
+ ...next,
207
+ channels: {
208
+ ...next.channels,
209
+ feishu: { ...next.channels?.feishu, enabled: true },
210
+ },
211
+ };
212
+ } else {
213
+ appId = String(
214
+ await prompter.text({
215
+ message: "Enter Feishu App ID",
216
+ validate: (value) => (value?.trim() ? undefined : "Required"),
217
+ }),
218
+ ).trim();
219
+ appSecret = String(
220
+ await prompter.text({
221
+ message: "Enter Feishu App Secret",
222
+ validate: (value) => (value?.trim() ? undefined : "Required"),
223
+ }),
224
+ ).trim();
225
+ }
226
+ } else if (hasConfigCreds) {
227
+ const keep = await prompter.confirm({
228
+ message: "Feishu credentials already configured. Keep them?",
229
+ initialValue: true,
230
+ });
231
+ if (!keep) {
232
+ appId = String(
233
+ await prompter.text({
234
+ message: "Enter Feishu App ID",
235
+ validate: (value) => (value?.trim() ? undefined : "Required"),
236
+ }),
237
+ ).trim();
238
+ appSecret = String(
239
+ await prompter.text({
240
+ message: "Enter Feishu App Secret",
241
+ validate: (value) => (value?.trim() ? undefined : "Required"),
242
+ }),
243
+ ).trim();
244
+ }
245
+ } else {
246
+ appId = String(
247
+ await prompter.text({
248
+ message: "Enter Feishu App ID",
249
+ validate: (value) => (value?.trim() ? undefined : "Required"),
250
+ }),
251
+ ).trim();
252
+ appSecret = String(
253
+ await prompter.text({
254
+ message: "Enter Feishu App Secret",
255
+ validate: (value) => (value?.trim() ? undefined : "Required"),
256
+ }),
257
+ ).trim();
258
+ }
259
+
260
+ if (appId && appSecret) {
261
+ next = {
262
+ ...next,
263
+ channels: {
264
+ ...next.channels,
265
+ feishu: {
266
+ ...next.channels?.feishu,
267
+ enabled: true,
268
+ appId,
269
+ appSecret,
270
+ },
271
+ },
272
+ };
273
+
274
+ // Test connection
275
+ const testCfg = next.channels?.feishu as FeishuConfig;
276
+ try {
277
+ const probe = await probeFeishu(testCfg);
278
+ if (probe.ok) {
279
+ await prompter.note(
280
+ `Connected as ${probe.botName ?? probe.botOpenId ?? "bot"}`,
281
+ "Feishu connection test",
282
+ );
283
+ } else {
284
+ await prompter.note(
285
+ `Connection failed: ${probe.error ?? "unknown error"}`,
286
+ "Feishu connection test",
287
+ );
288
+ }
289
+ } catch (err) {
290
+ await prompter.note(`Connection test failed: ${String(err)}`, "Feishu connection test");
291
+ }
292
+ }
293
+
294
+ // Domain selection
295
+ const currentDomain = (next.channels?.feishu as FeishuConfig | undefined)?.domain ?? "feishu";
296
+ const domain = await prompter.select({
297
+ message: "Which Feishu domain?",
298
+ options: [
299
+ { value: "feishu", label: "Feishu (feishu.cn) - China" },
300
+ { value: "lark", label: "Lark (larksuite.com) - International" },
301
+ ],
302
+ initialValue: currentDomain,
303
+ });
304
+ if (domain) {
305
+ next = {
306
+ ...next,
307
+ channels: {
308
+ ...next.channels,
309
+ feishu: {
310
+ ...next.channels?.feishu,
311
+ domain: domain as "feishu" | "lark",
312
+ },
313
+ },
314
+ };
315
+ }
316
+
317
+ // Group policy
318
+ const groupPolicy = await prompter.select({
319
+ message: "Group chat policy",
320
+ options: [
321
+ { value: "allowlist", label: "Allowlist - only respond in specific groups" },
322
+ { value: "open", label: "Open - respond in all groups (requires mention)" },
323
+ { value: "disabled", label: "Disabled - don't respond in groups" },
324
+ ],
325
+ initialValue: (next.channels?.feishu as FeishuConfig | undefined)?.groupPolicy ?? "allowlist",
326
+ });
327
+ if (groupPolicy) {
328
+ next = setFeishuGroupPolicy(next, groupPolicy as "open" | "allowlist" | "disabled");
329
+ }
330
+
331
+ // Group allowlist if needed
332
+ if (groupPolicy === "allowlist") {
333
+ const existing = (next.channels?.feishu as FeishuConfig | undefined)?.groupAllowFrom ?? [];
334
+ const entry = await prompter.text({
335
+ message: "Group chat allowlist (chat_ids)",
336
+ placeholder: "oc_xxxxx, oc_yyyyy",
337
+ initialValue: existing.length > 0 ? existing.map(String).join(", ") : undefined,
338
+ });
339
+ if (entry) {
340
+ const parts = parseAllowFromInput(String(entry));
341
+ if (parts.length > 0) {
342
+ next = setFeishuGroupAllowFrom(next, parts);
343
+ }
344
+ }
345
+ }
346
+
347
+ return { cfg: next, accountId: DEFAULT_ACCOUNT_ID };
348
+ },
349
+
350
+ dmPolicy,
351
+
352
+ disable: (cfg) => ({
353
+ ...cfg,
354
+ channels: {
355
+ ...cfg.channels,
356
+ feishu: { ...cfg.channels?.feishu, enabled: false },
357
+ },
358
+ }),
359
+ };