@poolzin/pool-bot 2026.2.21 → 2026.2.22

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 (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -1,12 +1,10 @@
1
- import { execFile } from "node:child_process";
2
1
  import fs from "node:fs/promises";
3
2
  import path from "node:path";
4
- import { promisify } from "node:util";
5
- import { GATEWAY_SERVICE_KIND, GATEWAY_SERVICE_MARKER, LEGACY_GATEWAY_LAUNCH_AGENT_LABELS, LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES, LEGACY_GATEWAY_WINDOWS_TASK_NAMES, resolveGatewayLaunchAgentLabel, resolveGatewaySystemdServiceName, resolveGatewayWindowsTaskName, } from "./constants.js";
6
- const EXTRA_MARKERS = ["poolbot"];
7
- const execFileAsync = promisify(execFile);
3
+ import { GATEWAY_SERVICE_KIND, GATEWAY_SERVICE_MARKER, resolveGatewayLaunchAgentLabel, resolveGatewaySystemdServiceName, resolveGatewayWindowsTaskName, } from "./constants.js";
4
+ import { execSchtasks } from "./schtasks-exec.js";
5
+ const EXTRA_MARKERS = ["poolbot", "clawdbot", "moltbot"];
8
6
  export function renderGatewayServiceCleanupHints(env = process.env) {
9
- const profile = env.POOLBOT_PROFILE || env.CLAWDBOT_PROFILE;
7
+ const profile = env.POOLBOT_PROFILE;
10
8
  switch (process.platform) {
11
9
  case "darwin": {
12
10
  const label = resolveGatewayLaunchAgentLabel(profile);
@@ -29,127 +27,173 @@ export function renderGatewayServiceCleanupHints(env = process.env) {
29
27
  }
30
28
  function resolveHomeDir(env) {
31
29
  const home = env.HOME?.trim() || env.USERPROFILE?.trim();
32
- if (!home)
30
+ if (!home) {
33
31
  throw new Error("Missing HOME");
32
+ }
34
33
  return home;
35
34
  }
36
- function containsMarker(content) {
35
+ function detectMarker(content) {
37
36
  const lower = content.toLowerCase();
38
- return EXTRA_MARKERS.some((marker) => lower.includes(marker));
37
+ for (const marker of EXTRA_MARKERS) {
38
+ if (lower.includes(marker)) {
39
+ return marker;
40
+ }
41
+ }
42
+ return null;
39
43
  }
40
44
  function hasGatewayServiceMarker(content) {
41
45
  const lower = content.toLowerCase();
42
- return (lower.includes("poolbot_service_marker") &&
43
- lower.includes(GATEWAY_SERVICE_MARKER.toLowerCase()) &&
44
- lower.includes("poolbot_service_kind") &&
46
+ const markerKeys = ["poolbot_service_marker"];
47
+ const kindKeys = ["poolbot_service_kind"];
48
+ const markerValues = [GATEWAY_SERVICE_MARKER.toLowerCase()];
49
+ const hasMarkerKey = markerKeys.some((key) => lower.includes(key));
50
+ const hasKindKey = kindKeys.some((key) => lower.includes(key));
51
+ const hasMarkerValue = markerValues.some((value) => lower.includes(value));
52
+ return (hasMarkerKey &&
53
+ hasKindKey &&
54
+ hasMarkerValue &&
45
55
  lower.includes(GATEWAY_SERVICE_KIND.toLowerCase()));
46
56
  }
47
- function isPoolbotGatewayLaunchdService(label, contents) {
48
- if (hasGatewayServiceMarker(contents))
57
+ function isPoolBotGatewayLaunchdService(label, contents) {
58
+ if (hasGatewayServiceMarker(contents)) {
49
59
  return true;
60
+ }
50
61
  const lowerContents = contents.toLowerCase();
51
- if (!lowerContents.includes("gateway"))
62
+ if (!lowerContents.includes("gateway")) {
52
63
  return false;
53
- return label.startsWith("com.poolbot.");
64
+ }
65
+ return label.startsWith("ai.poolbot.");
54
66
  }
55
- function isPoolbotGatewaySystemdService(name, contents) {
56
- if (hasGatewayServiceMarker(contents))
67
+ function isPoolBotGatewaySystemdService(name, contents) {
68
+ if (hasGatewayServiceMarker(contents)) {
57
69
  return true;
58
- if (!name.startsWith("poolbot-gateway"))
70
+ }
71
+ if (!name.startsWith("poolbot-gateway")) {
59
72
  return false;
73
+ }
60
74
  return contents.toLowerCase().includes("gateway");
61
75
  }
62
- function isPoolbotGatewayTaskName(name) {
76
+ function isPoolBotGatewayTaskName(name) {
63
77
  const normalized = name.trim().toLowerCase();
64
- if (!normalized)
78
+ if (!normalized) {
65
79
  return false;
80
+ }
66
81
  const defaultName = resolveGatewayWindowsTaskName().toLowerCase();
67
82
  return normalized === defaultName || normalized.startsWith("poolbot gateway");
68
83
  }
69
84
  function tryExtractPlistLabel(contents) {
70
85
  const match = contents.match(/<key>Label<\/key>\s*<string>([\s\S]*?)<\/string>/i);
71
- if (!match)
86
+ if (!match) {
72
87
  return null;
88
+ }
73
89
  return match[1]?.trim() || null;
74
90
  }
75
91
  function isIgnoredLaunchdLabel(label) {
76
- return (label === resolveGatewayLaunchAgentLabel() || LEGACY_GATEWAY_LAUNCH_AGENT_LABELS.includes(label));
92
+ return label === resolveGatewayLaunchAgentLabel();
77
93
  }
78
94
  function isIgnoredSystemdName(name) {
79
- return (name === resolveGatewaySystemdServiceName() ||
80
- LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES.includes(name));
95
+ return name === resolveGatewaySystemdServiceName();
81
96
  }
82
- async function scanLaunchdDir(params) {
83
- const results = [];
84
- let entries = [];
97
+ function isLegacyLabel(label) {
98
+ const lower = label.toLowerCase();
99
+ return lower.includes("clawdbot") || lower.includes("moltbot");
100
+ }
101
+ async function readDirEntries(dir) {
85
102
  try {
86
- entries = await fs.readdir(params.dir);
103
+ return await fs.readdir(dir);
87
104
  }
88
105
  catch {
89
- return results;
106
+ return [];
107
+ }
108
+ }
109
+ async function readUtf8File(filePath) {
110
+ try {
111
+ return await fs.readFile(filePath, "utf8");
112
+ }
113
+ catch {
114
+ return null;
90
115
  }
116
+ }
117
+ async function scanLaunchdDir(params) {
118
+ const results = [];
119
+ const entries = await readDirEntries(params.dir);
91
120
  for (const entry of entries) {
92
- if (!entry.endsWith(".plist"))
121
+ if (!entry.endsWith(".plist")) {
93
122
  continue;
123
+ }
94
124
  const labelFromName = entry.replace(/\.plist$/, "");
95
- if (isIgnoredLaunchdLabel(labelFromName))
125
+ if (isIgnoredLaunchdLabel(labelFromName)) {
96
126
  continue;
97
- const fullPath = path.join(params.dir, entry);
98
- let contents = "";
99
- try {
100
- contents = await fs.readFile(fullPath, "utf8");
101
127
  }
102
- catch {
128
+ const fullPath = path.join(params.dir, entry);
129
+ const contents = await readUtf8File(fullPath);
130
+ if (contents === null) {
103
131
  continue;
104
132
  }
105
- if (!containsMarker(contents))
106
- continue;
133
+ const marker = detectMarker(contents);
107
134
  const label = tryExtractPlistLabel(contents) ?? labelFromName;
108
- if (isIgnoredLaunchdLabel(label))
135
+ if (!marker) {
136
+ const legacyLabel = isLegacyLabel(labelFromName) || isLegacyLabel(label);
137
+ if (!legacyLabel) {
138
+ continue;
139
+ }
140
+ results.push({
141
+ platform: "darwin",
142
+ label,
143
+ detail: `plist: ${fullPath}`,
144
+ scope: params.scope,
145
+ marker: isLegacyLabel(label) ? "clawdbot" : "moltbot",
146
+ legacy: true,
147
+ });
148
+ continue;
149
+ }
150
+ if (isIgnoredLaunchdLabel(label)) {
109
151
  continue;
110
- if (isPoolbotGatewayLaunchdService(label, contents))
152
+ }
153
+ if (marker === "poolbot" && isPoolBotGatewayLaunchdService(label, contents)) {
111
154
  continue;
155
+ }
112
156
  results.push({
113
157
  platform: "darwin",
114
158
  label,
115
159
  detail: `plist: ${fullPath}`,
116
160
  scope: params.scope,
161
+ marker,
162
+ legacy: marker !== "poolbot" || isLegacyLabel(label),
117
163
  });
118
164
  }
119
165
  return results;
120
166
  }
121
167
  async function scanSystemdDir(params) {
122
168
  const results = [];
123
- let entries = [];
124
- try {
125
- entries = await fs.readdir(params.dir);
126
- }
127
- catch {
128
- return results;
129
- }
169
+ const entries = await readDirEntries(params.dir);
130
170
  for (const entry of entries) {
131
- if (!entry.endsWith(".service"))
171
+ if (!entry.endsWith(".service")) {
132
172
  continue;
173
+ }
133
174
  const name = entry.replace(/\.service$/, "");
134
- if (isIgnoredSystemdName(name))
175
+ if (isIgnoredSystemdName(name)) {
135
176
  continue;
136
- const fullPath = path.join(params.dir, entry);
137
- let contents = "";
138
- try {
139
- contents = await fs.readFile(fullPath, "utf8");
140
177
  }
141
- catch {
178
+ const fullPath = path.join(params.dir, entry);
179
+ const contents = await readUtf8File(fullPath);
180
+ if (contents === null) {
142
181
  continue;
143
182
  }
144
- if (!containsMarker(contents))
183
+ const marker = detectMarker(contents);
184
+ if (!marker) {
145
185
  continue;
146
- if (isPoolbotGatewaySystemdService(name, contents))
186
+ }
187
+ if (marker === "poolbot" && isPoolBotGatewaySystemdService(name, contents)) {
147
188
  continue;
189
+ }
148
190
  results.push({
149
191
  platform: "linux",
150
192
  label: entry,
151
193
  detail: `unit: ${fullPath}`,
152
194
  scope: params.scope,
195
+ marker,
196
+ legacy: marker !== "poolbot",
153
197
  });
154
198
  }
155
199
  return results;
@@ -167,56 +211,41 @@ function parseSchtasksList(output) {
167
211
  continue;
168
212
  }
169
213
  const idx = line.indexOf(":");
170
- if (idx <= 0)
214
+ if (idx <= 0) {
171
215
  continue;
216
+ }
172
217
  const key = line.slice(0, idx).trim().toLowerCase();
173
218
  const value = line.slice(idx + 1).trim();
174
- if (!value)
219
+ if (!value) {
175
220
  continue;
221
+ }
176
222
  if (key === "taskname") {
177
- if (current)
223
+ if (current) {
178
224
  tasks.push(current);
225
+ }
179
226
  current = { name: value };
180
227
  continue;
181
228
  }
182
- if (!current)
229
+ if (!current) {
183
230
  continue;
231
+ }
184
232
  if (key === "task to run") {
185
233
  current.taskToRun = value;
186
234
  }
187
235
  }
188
- if (current)
236
+ if (current) {
189
237
  tasks.push(current);
190
- return tasks;
191
- }
192
- async function execSchtasks(args) {
193
- try {
194
- const { stdout, stderr } = await execFileAsync("schtasks", args, {
195
- encoding: "utf8",
196
- windowsHide: true,
197
- });
198
- return {
199
- stdout: String(stdout ?? ""),
200
- stderr: String(stderr ?? ""),
201
- code: 0,
202
- };
203
- }
204
- catch (error) {
205
- const e = error;
206
- return {
207
- stdout: typeof e.stdout === "string" ? e.stdout : "",
208
- stderr: typeof e.stderr === "string" ? e.stderr : typeof e.message === "string" ? e.message : "",
209
- code: typeof e.code === "number" ? e.code : 1,
210
- };
211
238
  }
239
+ return tasks;
212
240
  }
213
241
  export async function findExtraGatewayServices(env, opts = {}) {
214
242
  const results = [];
215
243
  const seen = new Set();
216
244
  const push = (svc) => {
217
245
  const key = `${svc.platform}:${svc.label}:${svc.detail}:${svc.scope}`;
218
- if (seen.has(key))
246
+ if (seen.has(key)) {
219
247
  return;
248
+ }
220
249
  seen.add(key);
221
250
  results.push(svc);
222
251
  };
@@ -281,30 +310,41 @@ export async function findExtraGatewayServices(env, opts = {}) {
281
310
  return results;
282
311
  }
283
312
  if (process.platform === "win32") {
284
- if (!opts.deep)
313
+ if (!opts.deep) {
285
314
  return results;
315
+ }
286
316
  const res = await execSchtasks(["/Query", "/FO", "LIST", "/V"]);
287
- if (res.code !== 0)
317
+ if (res.code !== 0) {
288
318
  return results;
319
+ }
289
320
  const tasks = parseSchtasksList(res.stdout);
290
321
  for (const task of tasks) {
291
322
  const name = task.name.trim();
292
- if (!name)
323
+ if (!name) {
293
324
  continue;
294
- if (isPoolbotGatewayTaskName(name))
295
- continue;
296
- if (LEGACY_GATEWAY_WINDOWS_TASK_NAMES.includes(name))
325
+ }
326
+ if (isPoolBotGatewayTaskName(name)) {
297
327
  continue;
328
+ }
298
329
  const lowerName = name.toLowerCase();
299
330
  const lowerCommand = task.taskToRun?.toLowerCase() ?? "";
300
- const matches = EXTRA_MARKERS.some((marker) => lowerName.includes(marker) || lowerCommand.includes(marker));
301
- if (!matches)
331
+ let marker = null;
332
+ for (const candidate of EXTRA_MARKERS) {
333
+ if (lowerName.includes(candidate) || lowerCommand.includes(candidate)) {
334
+ marker = candidate;
335
+ break;
336
+ }
337
+ }
338
+ if (!marker) {
302
339
  continue;
340
+ }
303
341
  push({
304
342
  platform: "win32",
305
343
  label: name,
306
344
  detail: task.taskToRun ? `task: ${name}, run: ${task.taskToRun}` : name,
307
345
  scope: "system",
346
+ marker,
347
+ legacy: marker !== "poolbot",
308
348
  });
309
349
  }
310
350
  return results;
@@ -16,6 +16,11 @@ const VERSION_MANAGER_MARKERS = [
16
16
  function getPathModule(platform) {
17
17
  return platform === "win32" ? path.win32 : path.posix;
18
18
  }
19
+ function isNodeExecPath(execPath, platform) {
20
+ const pathModule = getPathModule(platform);
21
+ const base = pathModule.basename(execPath).toLowerCase();
22
+ return base === "node" || base === "node.exe";
23
+ }
19
24
  function normalizeForCompare(input, platform) {
20
25
  const pathModule = getPathModule(platform);
21
26
  const normalized = pathModule.normalize(input).replaceAll("\\", "/");
@@ -83,8 +88,9 @@ export async function resolveSystemNodeInfo(params) {
83
88
  const env = params.env ?? process.env;
84
89
  const platform = params.platform ?? process.platform;
85
90
  const systemNode = await resolveSystemNodePath(env, platform);
86
- if (!systemNode)
91
+ if (!systemNode) {
87
92
  return null;
93
+ }
88
94
  const version = await resolveNodeVersion(systemNode, params.execFile ?? execFileAsync);
89
95
  return {
90
96
  path: systemNode,
@@ -93,17 +99,32 @@ export async function resolveSystemNodeInfo(params) {
93
99
  };
94
100
  }
95
101
  export function renderSystemNodeWarning(systemNode, selectedNodePath) {
96
- if (!systemNode || systemNode.supported)
102
+ if (!systemNode || systemNode.supported) {
97
103
  return null;
104
+ }
98
105
  const versionLabel = systemNode.version ?? "unknown";
99
106
  const selectedLabel = selectedNodePath ? ` Using ${selectedNodePath} for the daemon.` : "";
100
107
  return `System Node ${versionLabel} at ${systemNode.path} is below the required Node 22+.${selectedLabel} Install Node 22+ from nodejs.org or Homebrew.`;
101
108
  }
102
109
  export async function resolvePreferredNodePath(params) {
103
- if (params.runtime !== "node")
110
+ if (params.runtime !== "node") {
104
111
  return undefined;
112
+ }
113
+ // Prefer the node that is currently running `poolbot gateway install`.
114
+ // This respects the user's active version manager (fnm/nvm/volta/etc.).
115
+ const platform = params.platform ?? process.platform;
116
+ const currentExecPath = params.execPath ?? process.execPath;
117
+ if (currentExecPath && isNodeExecPath(currentExecPath, platform)) {
118
+ const execFileImpl = params.execFile ?? execFileAsync;
119
+ const version = await resolveNodeVersion(currentExecPath, execFileImpl);
120
+ if (isSupportedNodeVersion(version)) {
121
+ return currentExecPath;
122
+ }
123
+ }
124
+ // Fall back to system node.
105
125
  const systemNode = await resolveSystemNodeInfo(params);
106
- if (!systemNode?.supported)
126
+ if (!systemNode?.supported) {
107
127
  return undefined;
128
+ }
108
129
  return systemNode.path;
109
130
  }
@@ -10,15 +10,16 @@ export const SERVICE_AUDIT_CODES = {
10
10
  gatewayPathMissing: "gateway-path-missing",
11
11
  gatewayPathMissingDirs: "gateway-path-missing-dirs",
12
12
  gatewayPathNonMinimal: "gateway-path-nonminimal",
13
+ gatewayTokenMismatch: "gateway-token-mismatch",
13
14
  gatewayRuntimeBun: "gateway-runtime-bun",
14
15
  gatewayRuntimeNodeVersionManager: "gateway-runtime-node-version-manager",
15
16
  gatewayRuntimeNodeSystemMissing: "gateway-runtime-node-system-missing",
17
+ gatewayTokenDrift: "gateway-token-drift",
16
18
  launchdKeepAlive: "launchd-keep-alive",
17
19
  launchdRunAtLoad: "launchd-run-at-load",
18
20
  systemdAfterNetworkOnline: "systemd-after-network-online",
19
21
  systemdRestartSec: "systemd-restart-sec",
20
22
  systemdWantsNetworkOnline: "systemd-wants-network-online",
21
- gatewayTokenDrift: "gateway-token-drift",
22
23
  };
23
24
  export function needsNodeRuntimeMigration(issues) {
24
25
  return issues.some((issue) => issue.code === SERVICE_AUDIT_CODES.gatewayRuntimeBun ||
@@ -33,29 +34,36 @@ function parseSystemdUnit(content) {
33
34
  let restartSec;
34
35
  for (const rawLine of content.split(/\r?\n/)) {
35
36
  const line = rawLine.trim();
36
- if (!line)
37
+ if (!line) {
37
38
  continue;
38
- if (line.startsWith("#") || line.startsWith(";"))
39
+ }
40
+ if (line.startsWith("#") || line.startsWith(";")) {
39
41
  continue;
40
- if (line.startsWith("["))
42
+ }
43
+ if (line.startsWith("[")) {
41
44
  continue;
45
+ }
42
46
  const idx = line.indexOf("=");
43
- if (idx <= 0)
47
+ if (idx <= 0) {
44
48
  continue;
49
+ }
45
50
  const key = line.slice(0, idx).trim();
46
51
  const value = line.slice(idx + 1).trim();
47
- if (!value)
52
+ if (!value) {
48
53
  continue;
54
+ }
49
55
  if (key === "After") {
50
56
  for (const entry of value.split(/\s+/)) {
51
- if (entry)
57
+ if (entry) {
52
58
  after.add(entry);
59
+ }
53
60
  }
54
61
  }
55
62
  else if (key === "Wants") {
56
63
  for (const entry of value.split(/\s+/)) {
57
- if (entry)
64
+ if (entry) {
58
65
  wants.add(entry);
66
+ }
59
67
  }
60
68
  }
61
69
  else if (key === "RestartSec") {
@@ -65,11 +73,13 @@ function parseSystemdUnit(content) {
65
73
  return { after, wants, restartSec };
66
74
  }
67
75
  function isRestartSecPreferred(value) {
68
- if (!value)
76
+ if (!value) {
69
77
  return false;
78
+ }
70
79
  const parsed = Number.parseFloat(value);
71
- if (!Number.isFinite(parsed))
80
+ if (!Number.isFinite(parsed)) {
72
81
  return false;
82
+ }
73
83
  return Math.abs(parsed - 5) < 0.01;
74
84
  }
75
85
  async function auditSystemdUnit(env, issues) {
@@ -136,8 +146,9 @@ async function auditLaunchdPlist(env, issues) {
136
146
  }
137
147
  }
138
148
  function auditGatewayCommand(programArguments, issues) {
139
- if (!programArguments || programArguments.length === 0)
149
+ if (!programArguments || programArguments.length === 0) {
140
150
  return;
151
+ }
141
152
  if (!hasGatewaySubcommand(programArguments)) {
142
153
  issues.push({
143
154
  code: SERVICE_AUDIT_CODES.gatewayCommandMissing,
@@ -146,6 +157,22 @@ function auditGatewayCommand(programArguments, issues) {
146
157
  });
147
158
  }
148
159
  }
160
+ function auditGatewayToken(command, issues, expectedGatewayToken) {
161
+ const expectedToken = expectedGatewayToken?.trim();
162
+ if (!expectedToken) {
163
+ return;
164
+ }
165
+ const serviceToken = command?.environment?.POOLBOT_GATEWAY_TOKEN?.trim();
166
+ if (serviceToken === expectedToken) {
167
+ return;
168
+ }
169
+ issues.push({
170
+ code: SERVICE_AUDIT_CODES.gatewayTokenMismatch,
171
+ message: "Gateway service POOLBOT_GATEWAY_TOKEN does not match gateway.auth.token in poolbot.json",
172
+ detail: serviceToken ? "service token is stale" : "service token is missing",
173
+ level: "recommended",
174
+ });
175
+ }
149
176
  function isNodeRuntime(execPath) {
150
177
  const base = path.basename(execPath).toLowerCase();
151
178
  return base === "node" || base === "node.exe";
@@ -166,8 +193,9 @@ function normalizePathEntry(entry, platform) {
166
193
  return normalized;
167
194
  }
168
195
  function auditGatewayServicePath(command, issues, env, platform) {
169
- if (platform === "win32")
196
+ if (platform === "win32") {
170
197
  return;
198
+ }
171
199
  const servicePath = command?.environment?.PATH;
172
200
  if (!servicePath) {
173
201
  issues.push({
@@ -182,11 +210,11 @@ function auditGatewayServicePath(command, issues, env, platform) {
182
210
  .split(getPathModule(platform).delimiter)
183
211
  .map((entry) => entry.trim())
184
212
  .filter(Boolean);
185
- const normalizedParts = parts.map((entry) => normalizePathEntry(entry, platform));
213
+ const normalizedParts = new Set(parts.map((entry) => normalizePathEntry(entry, platform)));
186
214
  const normalizedExpected = new Set(expected.map((entry) => normalizePathEntry(entry, platform)));
187
215
  const missing = expected.filter((entry) => {
188
216
  const normalized = normalizePathEntry(entry, platform);
189
- return !normalizedParts.includes(normalized);
217
+ return !normalizedParts.has(normalized);
190
218
  });
191
219
  if (missing.length > 0) {
192
220
  issues.push({
@@ -223,8 +251,9 @@ function auditGatewayServicePath(command, issues, env, platform) {
223
251
  }
224
252
  async function auditGatewayRuntime(env, command, issues, platform) {
225
253
  const execPath = command?.programArguments?.[0];
226
- if (!execPath)
254
+ if (!execPath) {
227
255
  return;
256
+ }
228
257
  if (isBunRuntime(execPath)) {
229
258
  issues.push({
230
259
  code: SERVICE_AUDIT_CODES.gatewayRuntimeBun,
@@ -234,8 +263,9 @@ async function auditGatewayRuntime(env, command, issues, platform) {
234
263
  });
235
264
  return;
236
265
  }
237
- if (!isNodeRuntime(execPath))
266
+ if (!isNodeRuntime(execPath)) {
238
267
  return;
268
+ }
239
269
  if (isVersionManagedNodePath(execPath, platform)) {
240
270
  issues.push({
241
271
  code: SERVICE_AUDIT_CODES.gatewayRuntimeNodeVersionManager,
@@ -280,6 +310,7 @@ export async function auditGatewayServiceConfig(params) {
280
310
  const issues = [];
281
311
  const platform = params.platform ?? process.platform;
282
312
  auditGatewayCommand(params.command?.programArguments, issues);
313
+ auditGatewayToken(params.command, issues, params.expectedGatewayToken);
283
314
  auditGatewayServicePath(params.command, issues, params.env, platform);
284
315
  await auditGatewayRuntime(params.env, params.command, issues, platform);
285
316
  if (platform === "linux") {
@@ -1,27 +1,14 @@
1
- import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
1
+ import { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
2
+ import { normalizeAccountId } from "../routing/session-key.js";
2
3
  import { resolveDiscordToken } from "./token.js";
3
- function listConfiguredAccountIds(cfg) {
4
- const accounts = cfg.channels?.discord?.accounts;
5
- if (!accounts || typeof accounts !== "object")
6
- return [];
7
- return Object.keys(accounts).filter(Boolean);
8
- }
9
- export function listDiscordAccountIds(cfg) {
10
- const ids = listConfiguredAccountIds(cfg);
11
- if (ids.length === 0)
12
- return [DEFAULT_ACCOUNT_ID];
13
- return ids.sort((a, b) => a.localeCompare(b));
14
- }
15
- export function resolveDefaultDiscordAccountId(cfg) {
16
- const ids = listDiscordAccountIds(cfg);
17
- if (ids.includes(DEFAULT_ACCOUNT_ID))
18
- return DEFAULT_ACCOUNT_ID;
19
- return ids[0] ?? DEFAULT_ACCOUNT_ID;
20
- }
4
+ const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("discord");
5
+ export const listDiscordAccountIds = listAccountIds;
6
+ export const resolveDefaultDiscordAccountId = resolveDefaultAccountId;
21
7
  function resolveAccountConfig(cfg, accountId) {
22
8
  const accounts = cfg.channels?.discord?.accounts;
23
- if (!accounts || typeof accounts !== "object")
9
+ if (!accounts || typeof accounts !== "object") {
24
10
  return undefined;
11
+ }
25
12
  return accounts[accountId];
26
13
  }
27
14
  function mergeDiscordAccountConfig(cfg, accountId) {
@@ -29,6 +16,22 @@ function mergeDiscordAccountConfig(cfg, accountId) {
29
16
  const account = resolveAccountConfig(cfg, accountId) ?? {};
30
17
  return { ...base, ...account };
31
18
  }
19
+ export function createDiscordActionGate(params) {
20
+ const accountId = normalizeAccountId(params.accountId);
21
+ const baseActions = params.cfg.channels?.discord?.actions;
22
+ const accountActions = resolveAccountConfig(params.cfg, accountId)?.actions;
23
+ return (key, defaultValue = true) => {
24
+ const accountValue = accountActions?.[key];
25
+ if (accountValue !== undefined) {
26
+ return accountValue;
27
+ }
28
+ const baseValue = baseActions?.[key];
29
+ if (baseValue !== undefined) {
30
+ return baseValue;
31
+ }
32
+ return defaultValue;
33
+ };
34
+ }
32
35
  export function resolveDiscordAccount(params) {
33
36
  const accountId = normalizeAccountId(params.accountId);
34
37
  const baseEnabled = params.cfg.channels?.discord?.enabled !== false;