@poolzin/pool-bot 2026.2.17 → 2026.2.18

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 (469) hide show
  1. package/CHANGELOG.md +17 -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/discord/src/channel.js +366 -0
  381. package/extensions/discord/src/runtime.js +10 -0
  382. package/extensions/feishu/index.ts +63 -0
  383. package/extensions/feishu/src/accounts.ts +114 -0
  384. package/extensions/feishu/src/bitable.ts +739 -0
  385. package/extensions/feishu/src/bot.ts +965 -0
  386. package/extensions/feishu/src/channel.ts +351 -0
  387. package/extensions/feishu/src/client.ts +118 -0
  388. package/extensions/feishu/src/config-schema.ts +206 -0
  389. package/extensions/feishu/src/dedup.ts +33 -0
  390. package/extensions/feishu/src/directory.ts +177 -0
  391. package/extensions/feishu/src/doc-schema.ts +47 -0
  392. package/extensions/feishu/src/docx.ts +536 -0
  393. package/extensions/feishu/src/drive-schema.ts +46 -0
  394. package/extensions/feishu/src/drive.ts +227 -0
  395. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  396. package/extensions/feishu/src/media.ts +449 -0
  397. package/extensions/feishu/src/mention.ts +126 -0
  398. package/extensions/feishu/src/monitor.ts +330 -0
  399. package/extensions/feishu/src/onboarding.ts +359 -0
  400. package/extensions/feishu/src/outbound.ts +55 -0
  401. package/extensions/feishu/src/perm-schema.ts +52 -0
  402. package/extensions/feishu/src/perm.ts +173 -0
  403. package/extensions/feishu/src/policy.ts +84 -0
  404. package/extensions/feishu/src/probe.ts +44 -0
  405. package/extensions/feishu/src/reactions.ts +160 -0
  406. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  407. package/extensions/feishu/src/runtime.ts +14 -0
  408. package/extensions/feishu/src/send-result.ts +29 -0
  409. package/extensions/feishu/src/send.ts +335 -0
  410. package/extensions/feishu/src/streaming-card.ts +223 -0
  411. package/extensions/feishu/src/targets.ts +78 -0
  412. package/extensions/feishu/src/tools-config.ts +21 -0
  413. package/extensions/feishu/src/types.ts +81 -0
  414. package/extensions/feishu/src/typing.ts +80 -0
  415. package/extensions/feishu/src/wiki-schema.ts +55 -0
  416. package/extensions/feishu/src/wiki.ts +232 -0
  417. package/extensions/imessage/src/channel.js +253 -0
  418. package/extensions/imessage/src/runtime.js +10 -0
  419. package/extensions/irc/index.ts +17 -0
  420. package/extensions/irc/src/accounts.ts +268 -0
  421. package/extensions/irc/src/channel.ts +367 -0
  422. package/extensions/irc/src/client.ts +439 -0
  423. package/extensions/irc/src/config-schema.ts +97 -0
  424. package/extensions/irc/src/connect-options.ts +30 -0
  425. package/extensions/irc/src/control-chars.ts +22 -0
  426. package/extensions/irc/src/inbound.ts +334 -0
  427. package/extensions/irc/src/monitor.ts +147 -0
  428. package/extensions/irc/src/normalize.ts +117 -0
  429. package/extensions/irc/src/onboarding.ts +479 -0
  430. package/extensions/irc/src/policy.ts +157 -0
  431. package/extensions/irc/src/probe.ts +53 -0
  432. package/extensions/irc/src/protocol.ts +169 -0
  433. package/extensions/irc/src/runtime.ts +14 -0
  434. package/extensions/irc/src/send.ts +88 -0
  435. package/extensions/irc/src/types.ts +93 -0
  436. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  437. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  438. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  439. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  440. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  441. package/extensions/minimax-portal-auth/index.ts +161 -0
  442. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  443. package/extensions/msteams/src/file-lock.ts +1 -0
  444. package/extensions/msteams/src/graph.ts +92 -0
  445. package/extensions/msteams/src/mentions.ts +114 -0
  446. package/extensions/msteams/src/test-runtime.ts +16 -0
  447. package/extensions/openai-codex-auth/index.ts +177 -0
  448. package/extensions/phone-control/index.ts +421 -0
  449. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  450. package/extensions/signal/src/channel.js +273 -0
  451. package/extensions/signal/src/runtime.js +10 -0
  452. package/extensions/slack/src/channel.js +489 -0
  453. package/extensions/slack/src/runtime.js +10 -0
  454. package/extensions/talk-voice/index.ts +150 -0
  455. package/extensions/telegram/src/channel.js +424 -0
  456. package/extensions/telegram/src/runtime.js +10 -0
  457. package/extensions/thread-ownership/index.ts +133 -0
  458. package/extensions/tlon/src/account-fields.ts +25 -0
  459. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  460. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  461. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  462. package/extensions/tlon/src/urbit/context.ts +47 -0
  463. package/extensions/tlon/src/urbit/errors.ts +51 -0
  464. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  465. package/extensions/twitch/src/test-fixtures.ts +30 -0
  466. package/extensions/voice-call/src/allowlist.ts +19 -0
  467. package/extensions/whatsapp/src/channel.js +429 -0
  468. package/extensions/whatsapp/src/runtime.js +10 -0
  469. package/package.json +1 -1
@@ -0,0 +1,479 @@
1
+ import {
2
+ addWildcardAllowFrom,
3
+ DEFAULT_ACCOUNT_ID,
4
+ formatDocsLink,
5
+ promptAccountId,
6
+ promptChannelAccessConfig,
7
+ type ChannelOnboardingAdapter,
8
+ type ChannelOnboardingDmPolicy,
9
+ type DmPolicy,
10
+ type WizardPrompter,
11
+ } from "poolbot/plugin-sdk";
12
+ import { listIrcAccountIds, resolveDefaultIrcAccountId, resolveIrcAccount } from "./accounts.js";
13
+ import {
14
+ isChannelTarget,
15
+ normalizeIrcAllowEntry,
16
+ normalizeIrcMessagingTarget,
17
+ } from "./normalize.js";
18
+ import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";
19
+
20
+ const channel = "irc" as const;
21
+
22
+ function parseListInput(raw: string): string[] {
23
+ return raw
24
+ .split(/[\n,;]+/g)
25
+ .map((entry) => entry.trim())
26
+ .filter(Boolean);
27
+ }
28
+
29
+ function parsePort(raw: string, fallback: number): number {
30
+ const trimmed = raw.trim();
31
+ if (!trimmed) {
32
+ return fallback;
33
+ }
34
+ const parsed = Number.parseInt(trimmed, 10);
35
+ if (!Number.isFinite(parsed) || parsed < 1 || parsed > 65535) {
36
+ return fallback;
37
+ }
38
+ return parsed;
39
+ }
40
+
41
+ function normalizeGroupEntry(raw: string): string | null {
42
+ const trimmed = raw.trim();
43
+ if (!trimmed) {
44
+ return null;
45
+ }
46
+ if (trimmed === "*") {
47
+ return "*";
48
+ }
49
+ const normalized = normalizeIrcMessagingTarget(trimmed) ?? trimmed;
50
+ if (isChannelTarget(normalized)) {
51
+ return normalized;
52
+ }
53
+ return `#${normalized.replace(/^#+/, "")}`;
54
+ }
55
+
56
+ function updateIrcAccountConfig(
57
+ cfg: CoreConfig,
58
+ accountId: string,
59
+ patch: Partial<IrcAccountConfig>,
60
+ ): CoreConfig {
61
+ const current = cfg.channels?.irc ?? {};
62
+ if (accountId === DEFAULT_ACCOUNT_ID) {
63
+ return {
64
+ ...cfg,
65
+ channels: {
66
+ ...cfg.channels,
67
+ irc: {
68
+ ...current,
69
+ ...patch,
70
+ },
71
+ },
72
+ };
73
+ }
74
+ return {
75
+ ...cfg,
76
+ channels: {
77
+ ...cfg.channels,
78
+ irc: {
79
+ ...current,
80
+ accounts: {
81
+ ...current.accounts,
82
+ [accountId]: {
83
+ ...current.accounts?.[accountId],
84
+ ...patch,
85
+ },
86
+ },
87
+ },
88
+ },
89
+ };
90
+ }
91
+
92
+ function setIrcDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConfig {
93
+ const allowFrom =
94
+ dmPolicy === "open" ? addWildcardAllowFrom(cfg.channels?.irc?.allowFrom) : undefined;
95
+ return {
96
+ ...cfg,
97
+ channels: {
98
+ ...cfg.channels,
99
+ irc: {
100
+ ...cfg.channels?.irc,
101
+ dmPolicy,
102
+ ...(allowFrom ? { allowFrom } : {}),
103
+ },
104
+ },
105
+ };
106
+ }
107
+
108
+ function setIrcAllowFrom(cfg: CoreConfig, allowFrom: string[]): CoreConfig {
109
+ return {
110
+ ...cfg,
111
+ channels: {
112
+ ...cfg.channels,
113
+ irc: {
114
+ ...cfg.channels?.irc,
115
+ allowFrom,
116
+ },
117
+ },
118
+ };
119
+ }
120
+
121
+ function setIrcNickServ(
122
+ cfg: CoreConfig,
123
+ accountId: string,
124
+ nickserv?: IrcNickServConfig,
125
+ ): CoreConfig {
126
+ return updateIrcAccountConfig(cfg, accountId, { nickserv });
127
+ }
128
+
129
+ function setIrcGroupAccess(
130
+ cfg: CoreConfig,
131
+ accountId: string,
132
+ policy: "open" | "allowlist" | "disabled",
133
+ entries: string[],
134
+ ): CoreConfig {
135
+ if (policy !== "allowlist") {
136
+ return updateIrcAccountConfig(cfg, accountId, { enabled: true, groupPolicy: policy });
137
+ }
138
+ const normalizedEntries = [
139
+ ...new Set(entries.map((entry) => normalizeGroupEntry(entry)).filter(Boolean)),
140
+ ];
141
+ const groups = Object.fromEntries(normalizedEntries.map((entry) => [entry, {}]));
142
+ return updateIrcAccountConfig(cfg, accountId, {
143
+ enabled: true,
144
+ groupPolicy: "allowlist",
145
+ groups,
146
+ });
147
+ }
148
+
149
+ async function noteIrcSetupHelp(prompter: WizardPrompter): Promise<void> {
150
+ await prompter.note(
151
+ [
152
+ "IRC needs server host + bot nick.",
153
+ "Recommended: TLS on port 6697.",
154
+ "Optional: NickServ identify/register can be configured in onboarding.",
155
+ 'Set channels.irc.groupPolicy="allowlist" and channels.irc.groups for tighter channel control.',
156
+ 'Note: IRC channels are mention-gated by default. To allow unmentioned replies, set channels.irc.groups["#channel"].requireMention=false (or "*" for all).',
157
+ "Env vars supported: IRC_HOST, IRC_PORT, IRC_TLS, IRC_NICK, IRC_USERNAME, IRC_REALNAME, IRC_PASSWORD, IRC_CHANNELS, IRC_NICKSERV_PASSWORD, IRC_NICKSERV_REGISTER_EMAIL.",
158
+ `Docs: ${formatDocsLink("/channels/irc", "channels/irc")}`,
159
+ ].join("\n"),
160
+ "IRC setup",
161
+ );
162
+ }
163
+
164
+ async function promptIrcAllowFrom(params: {
165
+ cfg: CoreConfig;
166
+ prompter: WizardPrompter;
167
+ accountId?: string;
168
+ }): Promise<CoreConfig> {
169
+ const existing = params.cfg.channels?.irc?.allowFrom ?? [];
170
+
171
+ await params.prompter.note(
172
+ [
173
+ "Allowlist IRC DMs by sender.",
174
+ "Examples:",
175
+ "- alice",
176
+ "- alice!ident@example.org",
177
+ "Multiple entries: comma-separated.",
178
+ ].join("\n"),
179
+ "IRC allowlist",
180
+ );
181
+
182
+ const raw = await params.prompter.text({
183
+ message: "IRC allowFrom (nick or nick!user@host)",
184
+ placeholder: "alice, bob!ident@example.org",
185
+ initialValue: existing[0] ? String(existing[0]) : undefined,
186
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
187
+ });
188
+
189
+ const parsed = parseListInput(String(raw));
190
+ const normalized = [
191
+ ...new Set(
192
+ parsed
193
+ .map((entry) => normalizeIrcAllowEntry(entry))
194
+ .map((entry) => entry.trim())
195
+ .filter(Boolean),
196
+ ),
197
+ ];
198
+ return setIrcAllowFrom(params.cfg, normalized);
199
+ }
200
+
201
+ async function promptIrcNickServConfig(params: {
202
+ cfg: CoreConfig;
203
+ prompter: WizardPrompter;
204
+ accountId: string;
205
+ }): Promise<CoreConfig> {
206
+ const resolved = resolveIrcAccount({ cfg: params.cfg, accountId: params.accountId });
207
+ const existing = resolved.config.nickserv;
208
+ const hasExisting = Boolean(existing?.password || existing?.passwordFile);
209
+ const wants = await params.prompter.confirm({
210
+ message: hasExisting ? "Update NickServ settings?" : "Configure NickServ identify/register?",
211
+ initialValue: hasExisting,
212
+ });
213
+ if (!wants) {
214
+ return params.cfg;
215
+ }
216
+
217
+ const service = String(
218
+ await params.prompter.text({
219
+ message: "NickServ service nick",
220
+ initialValue: existing?.service || "NickServ",
221
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
222
+ }),
223
+ ).trim();
224
+
225
+ const useEnvPassword =
226
+ params.accountId === DEFAULT_ACCOUNT_ID &&
227
+ Boolean(process.env.IRC_NICKSERV_PASSWORD?.trim()) &&
228
+ !(existing?.password || existing?.passwordFile)
229
+ ? await params.prompter.confirm({
230
+ message: "IRC_NICKSERV_PASSWORD detected. Use env var?",
231
+ initialValue: true,
232
+ })
233
+ : false;
234
+
235
+ const password = useEnvPassword
236
+ ? undefined
237
+ : String(
238
+ await params.prompter.text({
239
+ message: "NickServ password (blank to disable NickServ auth)",
240
+ validate: () => undefined,
241
+ }),
242
+ ).trim();
243
+
244
+ if (!password && !useEnvPassword) {
245
+ return setIrcNickServ(params.cfg, params.accountId, {
246
+ enabled: false,
247
+ service,
248
+ });
249
+ }
250
+
251
+ const register = await params.prompter.confirm({
252
+ message: "Send NickServ REGISTER on connect?",
253
+ initialValue: existing?.register ?? false,
254
+ });
255
+ const registerEmail = register
256
+ ? String(
257
+ await params.prompter.text({
258
+ message: "NickServ register email",
259
+ initialValue:
260
+ existing?.registerEmail ||
261
+ (params.accountId === DEFAULT_ACCOUNT_ID
262
+ ? process.env.IRC_NICKSERV_REGISTER_EMAIL
263
+ : undefined),
264
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
265
+ }),
266
+ ).trim()
267
+ : undefined;
268
+
269
+ return setIrcNickServ(params.cfg, params.accountId, {
270
+ enabled: true,
271
+ service,
272
+ ...(password ? { password } : {}),
273
+ register,
274
+ ...(registerEmail ? { registerEmail } : {}),
275
+ });
276
+ }
277
+
278
+ const dmPolicy: ChannelOnboardingDmPolicy = {
279
+ label: "IRC",
280
+ channel,
281
+ policyKey: "channels.irc.dmPolicy",
282
+ allowFromKey: "channels.irc.allowFrom",
283
+ getCurrent: (cfg) => (cfg as CoreConfig).channels?.irc?.dmPolicy ?? "pairing",
284
+ setPolicy: (cfg, policy) => setIrcDmPolicy(cfg as CoreConfig, policy),
285
+ promptAllowFrom: promptIrcAllowFrom,
286
+ };
287
+
288
+ export const ircOnboardingAdapter: ChannelOnboardingAdapter = {
289
+ channel,
290
+ getStatus: async ({ cfg }) => {
291
+ const coreCfg = cfg as CoreConfig;
292
+ const configured = listIrcAccountIds(coreCfg).some(
293
+ (accountId) => resolveIrcAccount({ cfg: coreCfg, accountId }).configured,
294
+ );
295
+ return {
296
+ channel,
297
+ configured,
298
+ statusLines: [`IRC: ${configured ? "configured" : "needs host + nick"}`],
299
+ selectionHint: configured ? "configured" : "needs host + nick",
300
+ quickstartScore: configured ? 1 : 0,
301
+ };
302
+ },
303
+ configure: async ({
304
+ cfg,
305
+ prompter,
306
+ accountOverrides,
307
+ shouldPromptAccountIds,
308
+ forceAllowFrom,
309
+ }) => {
310
+ let next = cfg as CoreConfig;
311
+ const ircOverride = accountOverrides.irc?.trim();
312
+ const defaultAccountId = resolveDefaultIrcAccountId(next);
313
+ let accountId = ircOverride || defaultAccountId;
314
+ if (shouldPromptAccountIds && !ircOverride) {
315
+ accountId = await promptAccountId({
316
+ cfg: next,
317
+ prompter,
318
+ label: "IRC",
319
+ currentId: accountId,
320
+ listAccountIds: listIrcAccountIds,
321
+ defaultAccountId,
322
+ });
323
+ }
324
+
325
+ const resolved = resolveIrcAccount({ cfg: next, accountId });
326
+ const isDefaultAccount = accountId === DEFAULT_ACCOUNT_ID;
327
+ const envHost = isDefaultAccount ? process.env.IRC_HOST?.trim() : "";
328
+ const envNick = isDefaultAccount ? process.env.IRC_NICK?.trim() : "";
329
+ const envReady = Boolean(envHost && envNick);
330
+
331
+ if (!resolved.configured) {
332
+ await noteIrcSetupHelp(prompter);
333
+ }
334
+
335
+ let useEnv = false;
336
+ if (envReady && isDefaultAccount && !resolved.config.host && !resolved.config.nick) {
337
+ useEnv = await prompter.confirm({
338
+ message: "IRC_HOST and IRC_NICK detected. Use env vars?",
339
+ initialValue: true,
340
+ });
341
+ }
342
+
343
+ if (useEnv) {
344
+ next = updateIrcAccountConfig(next, accountId, { enabled: true });
345
+ } else {
346
+ const host = String(
347
+ await prompter.text({
348
+ message: "IRC server host",
349
+ initialValue: resolved.config.host || envHost || undefined,
350
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
351
+ }),
352
+ ).trim();
353
+
354
+ const tls = await prompter.confirm({
355
+ message: "Use TLS for IRC?",
356
+ initialValue: resolved.config.tls ?? true,
357
+ });
358
+ const defaultPort = resolved.config.port ?? (tls ? 6697 : 6667);
359
+ const portInput = await prompter.text({
360
+ message: "IRC server port",
361
+ initialValue: String(defaultPort),
362
+ validate: (value) => {
363
+ const parsed = Number.parseInt(String(value ?? "").trim(), 10);
364
+ return Number.isFinite(parsed) && parsed >= 1 && parsed <= 65535
365
+ ? undefined
366
+ : "Use a port between 1 and 65535";
367
+ },
368
+ });
369
+ const port = parsePort(String(portInput), defaultPort);
370
+
371
+ const nick = String(
372
+ await prompter.text({
373
+ message: "IRC nick",
374
+ initialValue: resolved.config.nick || envNick || undefined,
375
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
376
+ }),
377
+ ).trim();
378
+
379
+ const username = String(
380
+ await prompter.text({
381
+ message: "IRC username",
382
+ initialValue: resolved.config.username || nick || "poolbot",
383
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
384
+ }),
385
+ ).trim();
386
+
387
+ const realname = String(
388
+ await prompter.text({
389
+ message: "IRC real name",
390
+ initialValue: resolved.config.realname || "OpenClaw",
391
+ validate: (value) => (String(value ?? "").trim() ? undefined : "Required"),
392
+ }),
393
+ ).trim();
394
+
395
+ const channelsRaw = await prompter.text({
396
+ message: "Auto-join IRC channels (optional, comma-separated)",
397
+ placeholder: "#openclaw, #ops",
398
+ initialValue: (resolved.config.channels ?? []).join(", "),
399
+ });
400
+ const channels = [
401
+ ...new Set(
402
+ parseListInput(String(channelsRaw))
403
+ .map((entry) => normalizeGroupEntry(entry))
404
+ .filter((entry): entry is string => Boolean(entry && entry !== "*"))
405
+ .filter((entry) => isChannelTarget(entry)),
406
+ ),
407
+ ];
408
+
409
+ next = updateIrcAccountConfig(next, accountId, {
410
+ enabled: true,
411
+ host,
412
+ port,
413
+ tls,
414
+ nick,
415
+ username,
416
+ realname,
417
+ channels: channels.length > 0 ? channels : undefined,
418
+ });
419
+ }
420
+
421
+ const afterConfig = resolveIrcAccount({ cfg: next, accountId });
422
+ const accessConfig = await promptChannelAccessConfig({
423
+ prompter,
424
+ label: "IRC channels",
425
+ currentPolicy: afterConfig.config.groupPolicy ?? "allowlist",
426
+ currentEntries: Object.keys(afterConfig.config.groups ?? {}),
427
+ placeholder: "#openclaw, #ops, *",
428
+ updatePrompt: Boolean(afterConfig.config.groups),
429
+ });
430
+ if (accessConfig) {
431
+ next = setIrcGroupAccess(next, accountId, accessConfig.policy, accessConfig.entries);
432
+
433
+ // Mention gating: groups/channels are mention-gated by default. Make this explicit in onboarding.
434
+ const wantsMentions = await prompter.confirm({
435
+ message: "Require @mention to reply in IRC channels?",
436
+ initialValue: true,
437
+ });
438
+ if (!wantsMentions) {
439
+ const resolvedAfter = resolveIrcAccount({ cfg: next, accountId });
440
+ const groups = resolvedAfter.config.groups ?? {};
441
+ const patched = Object.fromEntries(
442
+ Object.entries(groups).map(([key, value]) => [key, { ...value, requireMention: false }]),
443
+ );
444
+ next = updateIrcAccountConfig(next, accountId, { groups: patched });
445
+ }
446
+ }
447
+
448
+ if (forceAllowFrom) {
449
+ next = await promptIrcAllowFrom({ cfg: next, prompter, accountId });
450
+ }
451
+ next = await promptIrcNickServConfig({
452
+ cfg: next,
453
+ prompter,
454
+ accountId,
455
+ });
456
+
457
+ await prompter.note(
458
+ [
459
+ "Next: restart gateway and verify status.",
460
+ "Command: openclaw channels status --probe",
461
+ `Docs: ${formatDocsLink("/channels/irc", "channels/irc")}`,
462
+ ].join("\n"),
463
+ "IRC next steps",
464
+ );
465
+
466
+ return { cfg: next, accountId };
467
+ },
468
+ dmPolicy,
469
+ disable: (cfg) => ({
470
+ ...(cfg as CoreConfig),
471
+ channels: {
472
+ ...(cfg as CoreConfig).channels,
473
+ irc: {
474
+ ...(cfg as CoreConfig).channels?.irc,
475
+ enabled: false,
476
+ },
477
+ },
478
+ }),
479
+ };
@@ -0,0 +1,157 @@
1
+ import { normalizeIrcAllowlist, resolveIrcAllowlistMatch } from "./normalize.js";
2
+ import type { IrcAccountConfig, IrcChannelConfig } from "./types.js";
3
+ import type { IrcInboundMessage } from "./types.js";
4
+
5
+ export type IrcGroupMatch = {
6
+ allowed: boolean;
7
+ groupConfig?: IrcChannelConfig;
8
+ wildcardConfig?: IrcChannelConfig;
9
+ hasConfiguredGroups: boolean;
10
+ };
11
+
12
+ export type IrcGroupAccessGate = {
13
+ allowed: boolean;
14
+ reason: string;
15
+ };
16
+
17
+ export function resolveIrcGroupMatch(params: {
18
+ groups?: Record<string, IrcChannelConfig>;
19
+ target: string;
20
+ }): IrcGroupMatch {
21
+ const groups = params.groups ?? {};
22
+ const hasConfiguredGroups = Object.keys(groups).length > 0;
23
+
24
+ // IRC channel targets are case-insensitive, but config keys are plain strings.
25
+ // To avoid surprising drops (e.g. "#TUIRC-DEV" vs "#tuirc-dev"), match
26
+ // group config keys case-insensitively.
27
+ const direct = groups[params.target];
28
+ if (direct) {
29
+ return {
30
+ // "allowed" means the target matched an allowlisted key.
31
+ // Explicit disables are handled later by resolveIrcGroupAccessGate.
32
+ allowed: true,
33
+ groupConfig: direct,
34
+ wildcardConfig: groups["*"],
35
+ hasConfiguredGroups,
36
+ };
37
+ }
38
+
39
+ const targetLower = params.target.toLowerCase();
40
+ const directKey = Object.keys(groups).find((key) => key.toLowerCase() === targetLower);
41
+ if (directKey) {
42
+ const matched = groups[directKey];
43
+ if (matched) {
44
+ return {
45
+ // "allowed" means the target matched an allowlisted key.
46
+ // Explicit disables are handled later by resolveIrcGroupAccessGate.
47
+ allowed: true,
48
+ groupConfig: matched,
49
+ wildcardConfig: groups["*"],
50
+ hasConfiguredGroups,
51
+ };
52
+ }
53
+ }
54
+
55
+ const wildcard = groups["*"];
56
+ if (wildcard) {
57
+ return {
58
+ // "allowed" means the target matched an allowlisted key.
59
+ // Explicit disables are handled later by resolveIrcGroupAccessGate.
60
+ allowed: true,
61
+ wildcardConfig: wildcard,
62
+ hasConfiguredGroups,
63
+ };
64
+ }
65
+ return {
66
+ allowed: false,
67
+ hasConfiguredGroups,
68
+ };
69
+ }
70
+
71
+ export function resolveIrcGroupAccessGate(params: {
72
+ groupPolicy: IrcAccountConfig["groupPolicy"];
73
+ groupMatch: IrcGroupMatch;
74
+ }): IrcGroupAccessGate {
75
+ const policy = params.groupPolicy ?? "allowlist";
76
+ if (policy === "disabled") {
77
+ return { allowed: false, reason: "groupPolicy=disabled" };
78
+ }
79
+
80
+ // In open mode, unconfigured channels are allowed (mention-gated) but explicit
81
+ // per-channel/wildcard disables still apply.
82
+ if (policy === "allowlist") {
83
+ if (!params.groupMatch.hasConfiguredGroups) {
84
+ return {
85
+ allowed: false,
86
+ reason: "groupPolicy=allowlist and no groups configured",
87
+ };
88
+ }
89
+ if (!params.groupMatch.allowed) {
90
+ return { allowed: false, reason: "not allowlisted" };
91
+ }
92
+ }
93
+
94
+ if (
95
+ params.groupMatch.groupConfig?.enabled === false ||
96
+ params.groupMatch.wildcardConfig?.enabled === false
97
+ ) {
98
+ return { allowed: false, reason: "disabled" };
99
+ }
100
+
101
+ return { allowed: true, reason: policy === "open" ? "open" : "allowlisted" };
102
+ }
103
+
104
+ export function resolveIrcRequireMention(params: {
105
+ groupConfig?: IrcChannelConfig;
106
+ wildcardConfig?: IrcChannelConfig;
107
+ }): boolean {
108
+ if (params.groupConfig?.requireMention !== undefined) {
109
+ return params.groupConfig.requireMention;
110
+ }
111
+ if (params.wildcardConfig?.requireMention !== undefined) {
112
+ return params.wildcardConfig.requireMention;
113
+ }
114
+ return true;
115
+ }
116
+
117
+ export function resolveIrcMentionGate(params: {
118
+ isGroup: boolean;
119
+ requireMention: boolean;
120
+ wasMentioned: boolean;
121
+ hasControlCommand: boolean;
122
+ allowTextCommands: boolean;
123
+ commandAuthorized: boolean;
124
+ }): { shouldSkip: boolean; reason: string } {
125
+ if (!params.isGroup) {
126
+ return { shouldSkip: false, reason: "direct" };
127
+ }
128
+ if (!params.requireMention) {
129
+ return { shouldSkip: false, reason: "mention-not-required" };
130
+ }
131
+ if (params.wasMentioned) {
132
+ return { shouldSkip: false, reason: "mentioned" };
133
+ }
134
+ if (params.hasControlCommand && params.allowTextCommands && params.commandAuthorized) {
135
+ return { shouldSkip: false, reason: "authorized-command" };
136
+ }
137
+ return { shouldSkip: true, reason: "missing-mention" };
138
+ }
139
+
140
+ export function resolveIrcGroupSenderAllowed(params: {
141
+ groupPolicy: IrcAccountConfig["groupPolicy"];
142
+ message: IrcInboundMessage;
143
+ outerAllowFrom: string[];
144
+ innerAllowFrom: string[];
145
+ }): boolean {
146
+ const policy = params.groupPolicy ?? "allowlist";
147
+ const inner = normalizeIrcAllowlist(params.innerAllowFrom);
148
+ const outer = normalizeIrcAllowlist(params.outerAllowFrom);
149
+
150
+ if (inner.length > 0) {
151
+ return resolveIrcAllowlistMatch({ allowFrom: inner, message: params.message }).allowed;
152
+ }
153
+ if (outer.length > 0) {
154
+ return resolveIrcAllowlistMatch({ allowFrom: outer, message: params.message }).allowed;
155
+ }
156
+ return policy === "open";
157
+ }
@@ -0,0 +1,53 @@
1
+ import { resolveIrcAccount } from "./accounts.js";
2
+ import { connectIrcClient } from "./client.js";
3
+ import { buildIrcConnectOptions } from "./connect-options.js";
4
+ import type { CoreConfig, IrcProbe } from "./types.js";
5
+
6
+ function formatError(err: unknown): string {
7
+ if (err instanceof Error) {
8
+ return err.message;
9
+ }
10
+ return typeof err === "string" ? err : JSON.stringify(err);
11
+ }
12
+
13
+ export async function probeIrc(
14
+ cfg: CoreConfig,
15
+ opts?: { accountId?: string; timeoutMs?: number },
16
+ ): Promise<IrcProbe> {
17
+ const account = resolveIrcAccount({ cfg, accountId: opts?.accountId });
18
+ const base: IrcProbe = {
19
+ ok: false,
20
+ host: account.host,
21
+ port: account.port,
22
+ tls: account.tls,
23
+ nick: account.nick,
24
+ };
25
+
26
+ if (!account.configured) {
27
+ return {
28
+ ...base,
29
+ error: "missing host or nick",
30
+ };
31
+ }
32
+
33
+ const started = Date.now();
34
+ try {
35
+ const client = await connectIrcClient(
36
+ buildIrcConnectOptions(account, {
37
+ connectTimeoutMs: opts?.timeoutMs ?? 8000,
38
+ }),
39
+ );
40
+ const elapsed = Date.now() - started;
41
+ client.quit("probe");
42
+ return {
43
+ ...base,
44
+ ok: true,
45
+ latencyMs: elapsed,
46
+ };
47
+ } catch (err) {
48
+ return {
49
+ ...base,
50
+ error: formatError(err),
51
+ };
52
+ }
53
+ }