@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,554 @@
1
+ import os from "node:os";
2
+ import type { PoolBotPluginApi } from "poolbot/plugin-sdk";
3
+ import { approveDevicePairing, listDevicePairing } from "poolbot/plugin-sdk";
4
+ import qrcode from "qrcode-terminal";
5
+
6
+ function renderQrAscii(data: string): Promise<string> {
7
+ return new Promise((resolve) => {
8
+ qrcode.generate(data, { small: true }, (output: string) => {
9
+ resolve(output);
10
+ });
11
+ });
12
+ }
13
+
14
+ const DEFAULT_GATEWAY_PORT = 18789;
15
+
16
+ type DevicePairPluginConfig = {
17
+ publicUrl?: string;
18
+ };
19
+
20
+ type SetupPayload = {
21
+ url: string;
22
+ token?: string;
23
+ password?: string;
24
+ };
25
+
26
+ type ResolveUrlResult = {
27
+ url?: string;
28
+ source?: string;
29
+ error?: string;
30
+ };
31
+
32
+ type ResolveAuthResult = {
33
+ token?: string;
34
+ password?: string;
35
+ label?: string;
36
+ error?: string;
37
+ };
38
+
39
+ function normalizeUrl(raw: string, schemeFallback: "ws" | "wss"): string | null {
40
+ const trimmed = raw.trim();
41
+ if (!trimmed) {
42
+ return null;
43
+ }
44
+ try {
45
+ const parsed = new URL(trimmed);
46
+ const scheme = parsed.protocol.replace(":", "");
47
+ if (!scheme) {
48
+ return null;
49
+ }
50
+ const resolvedScheme = scheme === "http" ? "ws" : scheme === "https" ? "wss" : scheme;
51
+ if (resolvedScheme !== "ws" && resolvedScheme !== "wss") {
52
+ return null;
53
+ }
54
+ const host = parsed.hostname;
55
+ if (!host) {
56
+ return null;
57
+ }
58
+ const port = parsed.port ? `:${parsed.port}` : "";
59
+ return `${resolvedScheme}://${host}${port}`;
60
+ } catch {
61
+ // Fall through to host:port parsing.
62
+ }
63
+
64
+ const withoutPath = trimmed.split("/")[0] ?? "";
65
+ if (!withoutPath) {
66
+ return null;
67
+ }
68
+ return `${schemeFallback}://${withoutPath}`;
69
+ }
70
+
71
+ function resolveGatewayPort(cfg: PoolBotPluginApi["config"]): number {
72
+ const envRaw =
73
+ process.env.POOLBOT_GATEWAY_PORT?.trim() || process.env.CLAWDBOT_GATEWAY_PORT?.trim();
74
+ if (envRaw) {
75
+ const parsed = Number.parseInt(envRaw, 10);
76
+ if (Number.isFinite(parsed) && parsed > 0) {
77
+ return parsed;
78
+ }
79
+ }
80
+ const configPort = cfg.gateway?.port;
81
+ if (typeof configPort === "number" && Number.isFinite(configPort) && configPort > 0) {
82
+ return configPort;
83
+ }
84
+ return DEFAULT_GATEWAY_PORT;
85
+ }
86
+
87
+ function resolveScheme(
88
+ cfg: PoolBotPluginApi["config"],
89
+ opts?: { forceSecure?: boolean },
90
+ ): "ws" | "wss" {
91
+ if (opts?.forceSecure) {
92
+ return "wss";
93
+ }
94
+ return cfg.gateway?.tls?.enabled === true ? "wss" : "ws";
95
+ }
96
+
97
+ function isPrivateIPv4(address: string): boolean {
98
+ const parts = address.split(".");
99
+ if (parts.length != 4) {
100
+ return false;
101
+ }
102
+ const octets = parts.map((part) => Number.parseInt(part, 10));
103
+ if (octets.some((value) => !Number.isFinite(value) || value < 0 || value > 255)) {
104
+ return false;
105
+ }
106
+ const [a, b] = octets;
107
+ if (a === 10) {
108
+ return true;
109
+ }
110
+ if (a === 172 && b >= 16 && b <= 31) {
111
+ return true;
112
+ }
113
+ if (a === 192 && b === 168) {
114
+ return true;
115
+ }
116
+ return false;
117
+ }
118
+
119
+ function isTailnetIPv4(address: string): boolean {
120
+ const parts = address.split(".");
121
+ if (parts.length !== 4) {
122
+ return false;
123
+ }
124
+ const octets = parts.map((part) => Number.parseInt(part, 10));
125
+ if (octets.some((value) => !Number.isFinite(value) || value < 0 || value > 255)) {
126
+ return false;
127
+ }
128
+ const [a, b] = octets;
129
+ return a === 100 && b >= 64 && b <= 127;
130
+ }
131
+
132
+ function pickMatchingIPv4(predicate: (address: string) => boolean): string | null {
133
+ const nets = os.networkInterfaces();
134
+ for (const entries of Object.values(nets)) {
135
+ if (!entries) {
136
+ continue;
137
+ }
138
+ for (const entry of entries) {
139
+ const family = entry?.family;
140
+ // Check for IPv4 (string "IPv4" on Node 18+, number 4 on older)
141
+ const isIpv4 = family === "IPv4" || String(family) === "4";
142
+ if (!entry || entry.internal || !isIpv4) {
143
+ continue;
144
+ }
145
+ const address = entry.address?.trim() ?? "";
146
+ if (!address) {
147
+ continue;
148
+ }
149
+ if (predicate(address)) {
150
+ return address;
151
+ }
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+
157
+ function pickLanIPv4(): string | null {
158
+ return pickMatchingIPv4(isPrivateIPv4);
159
+ }
160
+
161
+ function pickTailnetIPv4(): string | null {
162
+ return pickMatchingIPv4(isTailnetIPv4);
163
+ }
164
+
165
+ async function resolveTailnetHost(api: PoolBotPluginApi): Promise<string | null> {
166
+ const candidates = ["tailscale", "/Applications/Tailscale.app/Contents/MacOS/Tailscale"];
167
+ for (const candidate of candidates) {
168
+ try {
169
+ const result = await api.runtime.system.runCommandWithTimeout(
170
+ [candidate, "status", "--json"],
171
+ {
172
+ timeoutMs: 5000,
173
+ },
174
+ );
175
+ if (result.code !== 0) {
176
+ continue;
177
+ }
178
+ const raw = result.stdout.trim();
179
+ if (!raw) {
180
+ continue;
181
+ }
182
+ const parsed = parsePossiblyNoisyJsonObject(raw);
183
+ const self =
184
+ typeof parsed.Self === "object" && parsed.Self !== null
185
+ ? (parsed.Self as Record<string, unknown>)
186
+ : undefined;
187
+ const dns = typeof self?.DNSName === "string" ? self.DNSName : undefined;
188
+ if (dns && dns.length > 0) {
189
+ return dns.replace(/\.$/, "");
190
+ }
191
+ const ips = Array.isArray(self?.TailscaleIPs) ? (self?.TailscaleIPs as string[]) : [];
192
+ if (ips.length > 0) {
193
+ return ips[0] ?? null;
194
+ }
195
+ } catch {
196
+ continue;
197
+ }
198
+ }
199
+ return null;
200
+ }
201
+
202
+ function parsePossiblyNoisyJsonObject(raw: string): Record<string, unknown> {
203
+ const start = raw.indexOf("{");
204
+ const end = raw.lastIndexOf("}");
205
+ if (start === -1 || end <= start) {
206
+ return {};
207
+ }
208
+ try {
209
+ return JSON.parse(raw.slice(start, end + 1)) as Record<string, unknown>;
210
+ } catch {
211
+ return {};
212
+ }
213
+ }
214
+
215
+ function resolveAuth(cfg: PoolBotPluginApi["config"]): ResolveAuthResult {
216
+ const mode = cfg.gateway?.auth?.mode;
217
+ const token =
218
+ process.env.POOLBOT_GATEWAY_TOKEN?.trim() ||
219
+ process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
220
+ cfg.gateway?.auth?.token?.trim();
221
+ const password =
222
+ process.env.POOLBOT_GATEWAY_PASSWORD?.trim() ||
223
+ process.env.CLAWDBOT_GATEWAY_PASSWORD?.trim() ||
224
+ cfg.gateway?.auth?.password?.trim();
225
+
226
+ if (mode === "password") {
227
+ if (!password) {
228
+ return { error: "Gateway auth is set to password, but no password is configured." };
229
+ }
230
+ return { password, label: "password" };
231
+ }
232
+ if (mode === "token") {
233
+ if (!token) {
234
+ return { error: "Gateway auth is set to token, but no token is configured." };
235
+ }
236
+ return { token, label: "token" };
237
+ }
238
+ if (token) {
239
+ return { token, label: "token" };
240
+ }
241
+ if (password) {
242
+ return { password, label: "password" };
243
+ }
244
+ return { error: "Gateway auth is not configured (no token or password)." };
245
+ }
246
+
247
+ async function resolveGatewayUrl(api: PoolBotPluginApi): Promise<ResolveUrlResult> {
248
+ const cfg = api.config;
249
+ const pluginCfg = (api.pluginConfig ?? {}) as DevicePairPluginConfig;
250
+ const scheme = resolveScheme(cfg);
251
+ const port = resolveGatewayPort(cfg);
252
+
253
+ if (typeof pluginCfg.publicUrl === "string" && pluginCfg.publicUrl.trim()) {
254
+ const url = normalizeUrl(pluginCfg.publicUrl, scheme);
255
+ if (url) {
256
+ return { url, source: "plugins.entries.device-pair.config.publicUrl" };
257
+ }
258
+ return { error: "Configured publicUrl is invalid." };
259
+ }
260
+
261
+ const tailscaleMode = cfg.gateway?.tailscale?.mode ?? "off";
262
+ if (tailscaleMode === "serve" || tailscaleMode === "funnel") {
263
+ const host = await resolveTailnetHost(api);
264
+ if (!host) {
265
+ return { error: "Tailscale Serve is enabled, but MagicDNS could not be resolved." };
266
+ }
267
+ return { url: `wss://${host}`, source: `gateway.tailscale.mode=${tailscaleMode}` };
268
+ }
269
+
270
+ const remoteUrl = cfg.gateway?.remote?.url;
271
+ if (typeof remoteUrl === "string" && remoteUrl.trim()) {
272
+ const url = normalizeUrl(remoteUrl, scheme);
273
+ if (url) {
274
+ return { url, source: "gateway.remote.url" };
275
+ }
276
+ }
277
+
278
+ const bind = cfg.gateway?.bind ?? "loopback";
279
+ if (bind === "custom") {
280
+ const host = cfg.gateway?.customBindHost?.trim();
281
+ if (host) {
282
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=custom" };
283
+ }
284
+ return { error: "gateway.bind=custom requires gateway.customBindHost." };
285
+ }
286
+
287
+ if (bind === "tailnet") {
288
+ const host = pickTailnetIPv4();
289
+ if (host) {
290
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=tailnet" };
291
+ }
292
+ return { error: "gateway.bind=tailnet set, but no tailnet IP was found." };
293
+ }
294
+
295
+ if (bind === "lan") {
296
+ const host = pickLanIPv4();
297
+ if (host) {
298
+ return { url: `${scheme}://${host}:${port}`, source: "gateway.bind=lan" };
299
+ }
300
+ return { error: "gateway.bind=lan set, but no private LAN IP was found." };
301
+ }
302
+
303
+ return {
304
+ error:
305
+ "Gateway is only bound to loopback. Set gateway.bind=lan, enable tailscale serve, or configure plugins.entries.device-pair.config.publicUrl.",
306
+ };
307
+ }
308
+
309
+ function encodeSetupCode(payload: SetupPayload): string {
310
+ const json = JSON.stringify(payload);
311
+ const base64 = Buffer.from(json, "utf8").toString("base64");
312
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
313
+ }
314
+
315
+ function formatSetupReply(payload: SetupPayload, authLabel: string): string {
316
+ const setupCode = encodeSetupCode(payload);
317
+ return [
318
+ "Pairing setup code generated.",
319
+ "",
320
+ "1) Open the iOS app → Settings → Gateway",
321
+ "2) Paste the setup code below and tap Connect",
322
+ "3) Back here, run /pair approve",
323
+ "",
324
+ "Setup code:",
325
+ setupCode,
326
+ "",
327
+ `Gateway: ${payload.url}`,
328
+ `Auth: ${authLabel}`,
329
+ ].join("\n");
330
+ }
331
+
332
+ function formatSetupInstructions(): string {
333
+ return [
334
+ "Pairing setup code generated.",
335
+ "",
336
+ "1) Open the iOS app → Settings → Gateway",
337
+ "2) Paste the setup code from my next message and tap Connect",
338
+ "3) Back here, run /pair approve",
339
+ ].join("\n");
340
+ }
341
+
342
+ type PendingPairingRequest = {
343
+ requestId: string;
344
+ deviceId: string;
345
+ displayName?: string;
346
+ platform?: string;
347
+ remoteIp?: string;
348
+ ts?: number;
349
+ };
350
+
351
+ function formatPendingRequests(pending: PendingPairingRequest[]): string {
352
+ if (pending.length === 0) {
353
+ return "No pending device pairing requests.";
354
+ }
355
+ const lines: string[] = ["Pending device pairing requests:"];
356
+ for (const req of pending) {
357
+ const label = req.displayName?.trim() || req.deviceId;
358
+ const platform = req.platform?.trim();
359
+ const ip = req.remoteIp?.trim();
360
+ const parts = [
361
+ `- ${req.requestId}`,
362
+ label ? `name=${label}` : null,
363
+ platform ? `platform=${platform}` : null,
364
+ ip ? `ip=${ip}` : null,
365
+ ].filter(Boolean);
366
+ lines.push(parts.join(" · "));
367
+ }
368
+ return lines.join("\n");
369
+ }
370
+
371
+ export default function register(api: PoolBotPluginApi) {
372
+ api.registerCommand({
373
+ name: "pair",
374
+ description: "Generate setup codes and approve device pairing requests.",
375
+ acceptsArgs: true,
376
+ handler: async (ctx) => {
377
+ const args = ctx.args?.trim() ?? "";
378
+ const tokens = args.split(/\s+/).filter(Boolean);
379
+ const action = tokens[0]?.toLowerCase() ?? "";
380
+ api.logger.info?.(
381
+ `device-pair: /pair invoked channel=${ctx.channel} sender=${ctx.senderId ?? "unknown"} action=${
382
+ action || "new"
383
+ }`,
384
+ );
385
+
386
+ if (action === "status" || action === "pending") {
387
+ const list = await listDevicePairing();
388
+ return { text: formatPendingRequests(list.pending) };
389
+ }
390
+
391
+ if (action === "approve") {
392
+ const requested = tokens[1]?.trim();
393
+ const list = await listDevicePairing();
394
+ if (list.pending.length === 0) {
395
+ return { text: "No pending device pairing requests." };
396
+ }
397
+
398
+ let pending: (typeof list.pending)[number] | undefined;
399
+ if (requested) {
400
+ if (requested.toLowerCase() === "latest") {
401
+ pending = [...list.pending].toSorted((a, b) => (b.ts ?? 0) - (a.ts ?? 0))[0];
402
+ } else {
403
+ pending = list.pending.find((entry) => entry.requestId === requested);
404
+ }
405
+ } else if (list.pending.length === 1) {
406
+ pending = list.pending[0];
407
+ } else {
408
+ return {
409
+ text:
410
+ `${formatPendingRequests(list.pending)}\n\n` +
411
+ "Multiple pending requests found. Approve one explicitly:\n" +
412
+ "/pair approve <requestId>\n" +
413
+ "Or approve the most recent:\n" +
414
+ "/pair approve latest",
415
+ };
416
+ }
417
+ if (!pending) {
418
+ return { text: "Pairing request not found." };
419
+ }
420
+ const approved = await approveDevicePairing(pending.requestId);
421
+ if (!approved) {
422
+ return { text: "Pairing request not found." };
423
+ }
424
+ const label = approved.device.displayName?.trim() || approved.device.deviceId;
425
+ const platform = approved.device.platform?.trim();
426
+ const platformLabel = platform ? ` (${platform})` : "";
427
+ return { text: `✅ Paired ${label}${platformLabel}.` };
428
+ }
429
+
430
+ const auth = resolveAuth(api.config);
431
+ if (auth.error) {
432
+ return { text: `Error: ${auth.error}` };
433
+ }
434
+
435
+ const urlResult = await resolveGatewayUrl(api);
436
+ if (!urlResult.url) {
437
+ return { text: `Error: ${urlResult.error ?? "Gateway URL unavailable."}` };
438
+ }
439
+
440
+ const payload: SetupPayload = {
441
+ url: urlResult.url,
442
+ token: auth.token,
443
+ password: auth.password,
444
+ };
445
+
446
+ if (action === "qr") {
447
+ const setupCode = encodeSetupCode(payload);
448
+ const qrAscii = await renderQrAscii(setupCode);
449
+ const authLabel = auth.label ?? "auth";
450
+
451
+ const channel = ctx.channel;
452
+ const target = ctx.senderId?.trim() || ctx.from?.trim() || ctx.to?.trim() || "";
453
+
454
+ if (channel === "telegram" && target) {
455
+ try {
456
+ const send = api.runtime?.channel?.telegram?.sendMessageTelegram;
457
+ if (send) {
458
+ await send(
459
+ target,
460
+ ["Scan this QR code with the OpenClaw iOS app:", "", "```", qrAscii, "```"].join(
461
+ "\n",
462
+ ),
463
+ {
464
+ ...(ctx.messageThreadId != null ? { messageThreadId: ctx.messageThreadId } : {}),
465
+ ...(ctx.accountId ? { accountId: ctx.accountId } : {}),
466
+ },
467
+ );
468
+ return {
469
+ text: [
470
+ `Gateway: ${payload.url}`,
471
+ `Auth: ${authLabel}`,
472
+ "",
473
+ "After scanning, come back here and run `/pair approve` to complete pairing.",
474
+ ].join("\n"),
475
+ };
476
+ }
477
+ } catch (err) {
478
+ api.logger.warn?.(
479
+ `device-pair: telegram QR send failed, falling back (${String(
480
+ (err as Error)?.message ?? err,
481
+ )})`,
482
+ );
483
+ }
484
+ }
485
+
486
+ // Render based on channel capability
487
+ api.logger.info?.(`device-pair: QR fallback channel=${channel} target=${target}`);
488
+ const infoLines = [
489
+ `Gateway: ${payload.url}`,
490
+ `Auth: ${authLabel}`,
491
+ "",
492
+ "After scanning, run `/pair approve` to complete pairing.",
493
+ ];
494
+
495
+ // WebUI + CLI/TUI: ASCII QR
496
+ return {
497
+ text: [
498
+ "Scan this QR code with the OpenClaw iOS app:",
499
+ "",
500
+ "```",
501
+ qrAscii,
502
+ "```",
503
+ "",
504
+ ...infoLines,
505
+ ].join("\n"),
506
+ };
507
+ }
508
+
509
+ const channel = ctx.channel;
510
+ const target = ctx.senderId?.trim() || ctx.from?.trim() || ctx.to?.trim() || "";
511
+ const authLabel = auth.label ?? "auth";
512
+
513
+ if (channel === "telegram" && target) {
514
+ try {
515
+ const runtimeKeys = Object.keys(api.runtime ?? {});
516
+ const channelKeys = Object.keys(api.runtime?.channel ?? {});
517
+ api.logger.debug?.(
518
+ `device-pair: runtime keys=${runtimeKeys.join(",") || "none"} channel keys=${
519
+ channelKeys.join(",") || "none"
520
+ }`,
521
+ );
522
+ const send = api.runtime?.channel?.telegram?.sendMessageTelegram;
523
+ if (!send) {
524
+ throw new Error(
525
+ `telegram runtime unavailable (runtime keys: ${runtimeKeys.join(",")}; channel keys: ${channelKeys.join(
526
+ ",",
527
+ )})`,
528
+ );
529
+ }
530
+ await send(target, formatSetupInstructions(), {
531
+ ...(ctx.messageThreadId != null ? { messageThreadId: ctx.messageThreadId } : {}),
532
+ ...(ctx.accountId ? { accountId: ctx.accountId } : {}),
533
+ });
534
+ api.logger.info?.(
535
+ `device-pair: telegram split send ok target=${target} account=${ctx.accountId ?? "none"} thread=${
536
+ ctx.messageThreadId ?? "none"
537
+ }`,
538
+ );
539
+ return { text: encodeSetupCode(payload) };
540
+ } catch (err) {
541
+ api.logger.warn?.(
542
+ `device-pair: telegram split send failed, falling back to single message (${String(
543
+ (err as Error)?.message ?? err,
544
+ )})`,
545
+ );
546
+ }
547
+ }
548
+
549
+ return {
550
+ text: formatSetupReply(payload, authLabel),
551
+ };
552
+ },
553
+ });
554
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "id": "device-pair",
3
+ "name": "Device Pairing",
4
+ "description": "Generate setup codes and approve device pairing requests.",
5
+ "configSchema": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "publicUrl": {
10
+ "type": "string"
11
+ }
12
+ }
13
+ },
14
+ "uiHints": {
15
+ "publicUrl": {
16
+ "label": "Gateway URL",
17
+ "help": "Public WebSocket URL used for /pair setup codes (ws/wss or http/https)."
18
+ }
19
+ }
20
+ }