@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,104 +1,83 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
1
4
  import path from "node:path";
5
+ import { promisify } from "node:util";
2
6
  import { resolveGatewayPort, resolveIsNixMode } from "../config/paths.js";
3
7
  import { findExtraGatewayServices, renderGatewayServiceCleanupHints } from "../daemon/inspect.js";
4
- import { findLegacyGatewayServices, uninstallLegacyGatewayServices } from "../daemon/legacy.js";
5
8
  import { renderSystemNodeWarning, resolveSystemNodeInfo } from "../daemon/runtime-paths.js";
6
- import { resolveGatewayService } from "../daemon/service.js";
7
9
  import { auditGatewayServiceConfig, needsNodeRuntimeMigration, SERVICE_AUDIT_CODES, } from "../daemon/service-audit.js";
10
+ import { resolveGatewayService } from "../daemon/service.js";
8
11
  import { note } from "../terminal/note.js";
9
- import { buildGatewayInstallPlan, gatewayInstallErrorHint } from "./daemon-install-helpers.js";
10
- import { DEFAULT_GATEWAY_DAEMON_RUNTIME, GATEWAY_DAEMON_RUNTIME_OPTIONS, } from "./daemon-runtime.js";
12
+ import { buildGatewayInstallPlan } from "./daemon-install-helpers.js";
13
+ import { DEFAULT_GATEWAY_DAEMON_RUNTIME } from "./daemon-runtime.js";
14
+ const execFileAsync = promisify(execFile);
11
15
  function detectGatewayRuntime(programArguments) {
12
16
  const first = programArguments?.[0];
13
17
  if (first) {
14
18
  const base = path.basename(first).toLowerCase();
15
- if (base === "bun" || base === "bun.exe")
19
+ if (base === "bun" || base === "bun.exe") {
16
20
  return "bun";
17
- if (base === "node" || base === "node.exe")
21
+ }
22
+ if (base === "node" || base === "node.exe") {
18
23
  return "node";
24
+ }
19
25
  }
20
26
  return DEFAULT_GATEWAY_DAEMON_RUNTIME;
21
27
  }
22
28
  function findGatewayEntrypoint(programArguments) {
23
- if (!programArguments || programArguments.length === 0)
29
+ if (!programArguments || programArguments.length === 0) {
24
30
  return null;
31
+ }
25
32
  const gatewayIndex = programArguments.indexOf("gateway");
26
- if (gatewayIndex <= 0)
33
+ if (gatewayIndex <= 0) {
27
34
  return null;
35
+ }
28
36
  return programArguments[gatewayIndex - 1] ?? null;
29
37
  }
30
38
  function normalizeExecutablePath(value) {
31
39
  return path.resolve(value);
32
40
  }
33
- export async function maybeMigrateLegacyGatewayService(cfg, mode, runtime, prompter) {
34
- const legacyServices = await findLegacyGatewayServices(process.env);
35
- if (legacyServices.length === 0)
36
- return;
37
- note(legacyServices.map((svc) => `- ${svc.label} (${svc.platform}, ${svc.detail})`).join("\n"), "Legacy gateway services detected");
38
- const migrate = await prompter.confirmSkipInNonInteractive({
39
- message: "Migrate legacy gateway services to Poolbot now?",
40
- initialValue: true,
41
- });
42
- if (!migrate)
43
- return;
44
- try {
45
- await uninstallLegacyGatewayServices({
46
- env: process.env,
47
- stdout: process.stdout,
48
- });
41
+ function resolveGatewayAuthToken(cfg, env) {
42
+ const configToken = cfg.gateway?.auth?.token?.trim();
43
+ if (configToken) {
44
+ return configToken;
45
+ }
46
+ const envToken = env.POOLBOT_GATEWAY_TOKEN ?? env.CLAWDBOT_GATEWAY_TOKEN;
47
+ const trimmedEnvToken = envToken?.trim();
48
+ return trimmedEnvToken || undefined;
49
+ }
50
+ function extractDetailPath(detail, prefix) {
51
+ if (!detail.startsWith(prefix)) {
52
+ return null;
49
53
  }
50
- catch (err) {
51
- runtime.error(`Legacy service cleanup failed: ${String(err)}`);
52
- return;
54
+ const value = detail.slice(prefix.length).trim();
55
+ return value.length > 0 ? value : null;
56
+ }
57
+ async function cleanupLegacyLaunchdService(params) {
58
+ const domain = typeof process.getuid === "function" ? `gui/${process.getuid()}` : "gui/501";
59
+ await execFileAsync("launchctl", ["bootout", domain, params.plistPath]).catch(() => undefined);
60
+ await execFileAsync("launchctl", ["unload", params.plistPath]).catch(() => undefined);
61
+ const trashDir = path.join(os.homedir(), ".Trash");
62
+ try {
63
+ await fs.mkdir(trashDir, { recursive: true });
53
64
  }
54
- if (resolveIsNixMode(process.env)) {
55
- note("Nix mode detected; skip installing services.", "Gateway");
56
- return;
65
+ catch {
66
+ // ignore
57
67
  }
58
- if (mode === "remote") {
59
- note("Gateway mode is remote; skipped local service install.", "Gateway");
60
- return;
68
+ try {
69
+ await fs.access(params.plistPath);
61
70
  }
62
- const service = resolveGatewayService();
63
- const loaded = await service.isLoaded({ env: process.env });
64
- if (loaded) {
65
- note(`Poolbot ${service.label} already ${service.loadedText}.`, "Gateway");
66
- return;
71
+ catch {
72
+ return null;
67
73
  }
68
- const install = await prompter.confirmSkipInNonInteractive({
69
- message: "Install Poolbot gateway service now?",
70
- initialValue: true,
71
- });
72
- if (!install)
73
- return;
74
- const daemonRuntime = await prompter.select({
75
- message: "Gateway service runtime",
76
- options: GATEWAY_DAEMON_RUNTIME_OPTIONS,
77
- initialValue: DEFAULT_GATEWAY_DAEMON_RUNTIME,
78
- }, DEFAULT_GATEWAY_DAEMON_RUNTIME);
79
- const port = resolveGatewayPort(cfg, process.env);
80
- const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
81
- env: process.env,
82
- port,
83
- token: cfg.gateway?.auth?.token ??
84
- process.env.POOLBOT_GATEWAY_TOKEN ??
85
- process.env.CLAWDBOT_GATEWAY_TOKEN,
86
- runtime: daemonRuntime,
87
- warn: (message, title) => note(message, title),
88
- config: cfg,
89
- });
74
+ const dest = path.join(trashDir, `${params.label}-${Date.now()}.plist`);
90
75
  try {
91
- await service.install({
92
- env: process.env,
93
- stdout: process.stdout,
94
- programArguments,
95
- workingDirectory,
96
- environment,
97
- });
76
+ await fs.rename(params.plistPath, dest);
77
+ return dest;
98
78
  }
99
- catch (err) {
100
- runtime.error(`Gateway service install failed: ${String(err)}`);
101
- note(gatewayInstallErrorHint(), "Gateway");
79
+ catch {
80
+ return null;
102
81
  }
103
82
  }
104
83
  export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompter) {
@@ -118,11 +97,14 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
118
97
  catch {
119
98
  command = null;
120
99
  }
121
- if (!command)
100
+ if (!command) {
122
101
  return;
102
+ }
103
+ const expectedGatewayToken = resolveGatewayAuthToken(cfg, process.env);
123
104
  const audit = await auditGatewayServiceConfig({
124
105
  env: process.env,
125
106
  command,
107
+ expectedGatewayToken,
126
108
  });
127
109
  const needsNodeRuntime = needsNodeRuntimeMigration(audit.issues);
128
110
  const systemNodeInfo = needsNodeRuntime
@@ -131,8 +113,9 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
131
113
  const systemNodePath = systemNodeInfo?.supported ? systemNodeInfo.path : null;
132
114
  if (needsNodeRuntime && !systemNodePath) {
133
115
  const warning = renderSystemNodeWarning(systemNodeInfo);
134
- if (warning)
116
+ if (warning) {
135
117
  note(warning, "Gateway runtime");
118
+ }
136
119
  note("System Node 22+ not found. Install via Homebrew/apt/choco and rerun doctor to migrate off Bun/version managers.", "Gateway runtime");
137
120
  }
138
121
  const port = resolveGatewayPort(cfg, process.env);
@@ -140,9 +123,7 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
140
123
  const { programArguments, workingDirectory, environment } = await buildGatewayInstallPlan({
141
124
  env: process.env,
142
125
  port,
143
- token: cfg.gateway?.auth?.token ??
144
- process.env.POOLBOT_GATEWAY_TOKEN ??
145
- process.env.CLAWDBOT_GATEWAY_TOKEN,
126
+ token: expectedGatewayToken,
146
127
  runtime: needsNodeRuntime && systemNodePath ? "node" : runtimeChoice,
147
128
  nodePath: systemNodePath ?? undefined,
148
129
  warn: (message, title) => note(message, title),
@@ -160,8 +141,9 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
160
141
  level: "recommended",
161
142
  });
162
143
  }
163
- if (audit.issues.length === 0)
144
+ if (audit.issues.length === 0) {
164
145
  return;
146
+ }
165
147
  note(audit.issues
166
148
  .map((issue) => issue.detail ? `- ${issue.message} (${issue.detail})` : `- ${issue.message}`)
167
149
  .join("\n"), "Gateway service config");
@@ -179,8 +161,9 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
179
161
  message: "Update gateway service config to the recommended defaults now?",
180
162
  initialValue: true,
181
163
  });
182
- if (!repair)
164
+ if (!repair) {
183
165
  return;
166
+ }
184
167
  try {
185
168
  await service.install({
186
169
  env: process.env,
@@ -194,13 +177,54 @@ export async function maybeRepairGatewayServiceConfig(cfg, mode, runtime, prompt
194
177
  runtime.error(`Gateway service update failed: ${String(err)}`);
195
178
  }
196
179
  }
197
- export async function maybeScanExtraGatewayServices(options) {
180
+ export async function maybeScanExtraGatewayServices(options, runtime, prompter) {
198
181
  const extraServices = await findExtraGatewayServices(process.env, {
199
182
  deep: options.deep,
200
183
  });
201
- if (extraServices.length === 0)
184
+ if (extraServices.length === 0) {
202
185
  return;
186
+ }
203
187
  note(extraServices.map((svc) => `- ${svc.label} (${svc.scope}, ${svc.detail})`).join("\n"), "Other gateway-like services detected");
188
+ const legacyServices = extraServices.filter((svc) => svc.legacy === true);
189
+ if (legacyServices.length > 0) {
190
+ const shouldRemove = await prompter.confirmSkipInNonInteractive({
191
+ message: "Remove legacy gateway services (clawdbot/moltbot) now?",
192
+ initialValue: true,
193
+ });
194
+ if (shouldRemove) {
195
+ const removed = [];
196
+ const failed = [];
197
+ for (const svc of legacyServices) {
198
+ if (svc.platform !== "darwin") {
199
+ failed.push(`${svc.label} (${svc.platform})`);
200
+ continue;
201
+ }
202
+ if (svc.scope !== "user") {
203
+ failed.push(`${svc.label} (${svc.scope})`);
204
+ continue;
205
+ }
206
+ const plistPath = extractDetailPath(svc.detail, "plist:");
207
+ if (!plistPath) {
208
+ failed.push(`${svc.label} (missing plist path)`);
209
+ continue;
210
+ }
211
+ const dest = await cleanupLegacyLaunchdService({
212
+ label: svc.label,
213
+ plistPath,
214
+ });
215
+ removed.push(dest ? `${svc.label} -> ${dest}` : svc.label);
216
+ }
217
+ if (removed.length > 0) {
218
+ note(removed.map((line) => `- ${line}`).join("\n"), "Legacy gateway removed");
219
+ }
220
+ if (failed.length > 0) {
221
+ note(failed.map((line) => `- ${line}`).join("\n"), "Legacy gateway cleanup skipped");
222
+ }
223
+ if (removed.length > 0) {
224
+ runtime.log("Legacy gateway services removed. Installing Pool Bot gateway next.");
225
+ }
226
+ }
227
+ }
204
228
  const cleanupHints = renderGatewayServiceCleanupHints();
205
229
  if (cleanupHints.length > 0) {
206
230
  note(cleanupHints.map((hint) => `- ${hint}`).join("\n"), "Cleanup hints");
@@ -7,7 +7,7 @@ import { note } from "../terminal/note.js";
7
7
  import { resolveUserPath } from "../utils.js";
8
8
  /**
9
9
  * Check whether memory search has a usable embedding provider.
10
- * Runs as part of `openclaw doctor` — config-only, no network calls.
10
+ * Runs as part of `poolbot doctor` — config-only, no network calls.
11
11
  */
12
12
  export async function noteMemorySearchHealth(cfg) {
13
13
  const agentId = resolveDefaultAgentId(cfg);
@@ -29,9 +29,9 @@ export async function noteMemorySearchHealth(cfg) {
29
29
  "",
30
30
  "Fix (pick one):",
31
31
  `- Install node-llama-cpp and set a local model path in config`,
32
- `- Switch to a remote provider: ${formatCliCommand("openclaw config set agents.defaults.memorySearch.provider openai")}`,
32
+ `- Switch to a remote provider: ${formatCliCommand("poolbot config set agents.defaults.memorySearch.provider openai")}`,
33
33
  "",
34
- `Verify: ${formatCliCommand("openclaw memory status --deep")}`,
34
+ `Verify: ${formatCliCommand("poolbot memory status --deep")}`,
35
35
  ].join("\n"), "Memory search");
36
36
  return;
37
37
  }
@@ -46,10 +46,10 @@ export async function noteMemorySearchHealth(cfg) {
46
46
  "",
47
47
  "Fix (pick one):",
48
48
  `- Set ${envVar} in your environment`,
49
- `- Add credentials: ${formatCliCommand(`openclaw auth add --provider ${resolved.provider}`)}`,
50
- `- To disable: ${formatCliCommand("openclaw config set agents.defaults.memorySearch.enabled false")}`,
49
+ `- Add credentials: ${formatCliCommand(`poolbot auth add --provider ${resolved.provider}`)}`,
50
+ `- To disable: ${formatCliCommand("poolbot config set agents.defaults.memorySearch.enabled false")}`,
51
51
  "",
52
- `Verify: ${formatCliCommand("openclaw memory status --deep")}`,
52
+ `Verify: ${formatCliCommand("poolbot memory status --deep")}`,
53
53
  ].join("\n"), "Memory search");
54
54
  return;
55
55
  }
@@ -68,11 +68,11 @@ export async function noteMemorySearchHealth(cfg) {
68
68
  "",
69
69
  "Fix (pick one):",
70
70
  "- Set OPENAI_API_KEY or GEMINI_API_KEY in your environment",
71
- `- Add credentials: ${formatCliCommand("openclaw auth add --provider openai")}`,
71
+ `- Add credentials: ${formatCliCommand("poolbot auth add --provider openai")}`,
72
72
  `- For local embeddings: configure agents.defaults.memorySearch.provider and local model path`,
73
- `- To disable: ${formatCliCommand("openclaw config set agents.defaults.memorySearch.enabled false")}`,
73
+ `- To disable: ${formatCliCommand("poolbot config set agents.defaults.memorySearch.enabled false")}`,
74
74
  "",
75
- `Verify: ${formatCliCommand("openclaw memory status --deep")}`,
75
+ `Verify: ${formatCliCommand("poolbot memory status --deep")}`,
76
76
  ].join("\n"), "Memory search");
77
77
  }
78
78
  function hasLocalEmbeddings(local) {
@@ -10,12 +10,15 @@ function resolveHomeDir() {
10
10
  return process.env.HOME ?? os.homedir();
11
11
  }
12
12
  export async function noteMacLaunchAgentOverrides() {
13
- if (process.platform !== "darwin")
13
+ if (process.platform !== "darwin") {
14
14
  return;
15
- const markerPath = path.join(resolveHomeDir(), ".poolbot", "disable-launchagent");
16
- const hasMarker = fs.existsSync(markerPath);
17
- if (!hasMarker)
15
+ }
16
+ const home = resolveHomeDir();
17
+ const markerCandidates = [path.join(home, ".poolbot", "disable-launchagent")];
18
+ const markerPath = markerCandidates.find((candidate) => fs.existsSync(candidate));
19
+ if (!markerPath) {
18
20
  return;
21
+ }
19
22
  const displayMarkerPath = shortenHomePath(markerPath);
20
23
  const lines = [
21
24
  `- LaunchAgent writes are disabled via ${displayMarkerPath}.`,
@@ -43,41 +46,65 @@ function hasConfigGatewayCreds(cfg) {
43
46
  }
44
47
  export async function noteMacLaunchctlGatewayEnvOverrides(cfg, deps) {
45
48
  const platform = deps?.platform ?? process.platform;
46
- if (platform !== "darwin")
49
+ if (platform !== "darwin") {
47
50
  return;
48
- if (!hasConfigGatewayCreds(cfg))
51
+ }
52
+ if (!hasConfigGatewayCreds(cfg)) {
49
53
  return;
54
+ }
50
55
  const getenv = deps?.getenv ?? launchctlGetenv;
51
- // Check both POOLBOT_ (preferred) and CLAWDBOT_ (legacy) launchctl env vars
52
- const poolbotToken = await getenv("POOLBOT_GATEWAY_TOKEN");
53
- const clawdbotToken = await getenv("CLAWDBOT_GATEWAY_TOKEN");
54
- const poolbotPassword = await getenv("POOLBOT_GATEWAY_PASSWORD");
55
- const clawdbotPassword = await getenv("CLAWDBOT_GATEWAY_PASSWORD");
56
- const hasToken = poolbotToken ?? clawdbotToken;
57
- const hasPassword = poolbotPassword ?? clawdbotPassword;
58
- if (!hasToken && !hasPassword)
56
+ const deprecatedLaunchctlEntries = [
57
+ ["CLAWDBOT_GATEWAY_TOKEN", await getenv("CLAWDBOT_GATEWAY_TOKEN")],
58
+ ["CLAWDBOT_GATEWAY_PASSWORD", await getenv("CLAWDBOT_GATEWAY_PASSWORD")],
59
+ ].filter((entry) => Boolean(entry[1]?.trim()));
60
+ if (deprecatedLaunchctlEntries.length > 0) {
61
+ const lines = [
62
+ "- Deprecated launchctl environment variables detected (ignored).",
63
+ ...deprecatedLaunchctlEntries.map(([key]) => `- \`${key}\` is set; use \`POOLBOT_${key.slice(key.indexOf("_") + 1)}\` instead.`),
64
+ ];
65
+ (deps?.noteFn ?? note)(lines.join("\n"), "Gateway (macOS)");
66
+ }
67
+ const tokenEntries = [["POOLBOT_GATEWAY_TOKEN", await getenv("POOLBOT_GATEWAY_TOKEN")]];
68
+ const passwordEntries = [
69
+ ["POOLBOT_GATEWAY_PASSWORD", await getenv("POOLBOT_GATEWAY_PASSWORD")],
70
+ ];
71
+ const tokenEntry = tokenEntries.find(([, value]) => value?.trim());
72
+ const passwordEntry = passwordEntries.find(([, value]) => value?.trim());
73
+ const envToken = tokenEntry?.[1]?.trim() ?? "";
74
+ const envPassword = passwordEntry?.[1]?.trim() ?? "";
75
+ const envTokenKey = tokenEntry?.[0];
76
+ const envPasswordKey = passwordEntry?.[0];
77
+ if (!envToken && !envPassword) {
59
78
  return;
60
- const tokenUnsets = [];
61
- if (poolbotToken)
62
- tokenUnsets.push(" launchctl unsetenv POOLBOT_GATEWAY_TOKEN");
63
- if (clawdbotToken)
64
- tokenUnsets.push(" launchctl unsetenv CLAWDBOT_GATEWAY_TOKEN");
65
- const passwordUnsets = [];
66
- if (poolbotPassword)
67
- passwordUnsets.push(" launchctl unsetenv POOLBOT_GATEWAY_PASSWORD");
68
- if (clawdbotPassword)
69
- passwordUnsets.push(" launchctl unsetenv CLAWDBOT_GATEWAY_PASSWORD");
79
+ }
70
80
  const lines = [
71
81
  "- launchctl environment overrides detected (can cause confusing unauthorized errors).",
72
- hasToken
73
- ? `- \`${poolbotToken ? "POOLBOT" : "CLAWDBOT"}_GATEWAY_TOKEN\` is set; it overrides config tokens.`
82
+ envToken && envTokenKey
83
+ ? `- \`${envTokenKey}\` is set; it overrides config tokens.`
74
84
  : undefined,
75
- hasPassword
76
- ? `- \`${poolbotPassword ? "POOLBOT" : "CLAWDBOT"}_GATEWAY_PASSWORD\` is set; it overrides config passwords.`
85
+ envPassword
86
+ ? `- \`${envPasswordKey ?? "POOLBOT_GATEWAY_PASSWORD"}\` is set; it overrides config passwords.`
77
87
  : undefined,
78
88
  "- Clear overrides and restart the app/gateway:",
79
- ...tokenUnsets,
80
- ...passwordUnsets,
89
+ envTokenKey ? ` launchctl unsetenv ${envTokenKey}` : undefined,
90
+ envPasswordKey ? ` launchctl unsetenv ${envPasswordKey}` : undefined,
81
91
  ].filter((line) => Boolean(line));
82
92
  (deps?.noteFn ?? note)(lines.join("\n"), "Gateway (macOS)");
83
93
  }
94
+ export function noteDeprecatedLegacyEnvVars(env = process.env, deps) {
95
+ const entries = Object.entries(env)
96
+ .filter(([key, value]) => key.startsWith("CLAWDBOT_") && value?.trim())
97
+ .map(([key]) => key);
98
+ if (entries.length === 0) {
99
+ return;
100
+ }
101
+ const lines = [
102
+ "- Deprecated legacy environment variables detected (ignored).",
103
+ "- Use POOLBOT_* equivalents instead:",
104
+ ...entries.map((key) => {
105
+ const suffix = key.slice(key.indexOf("_") + 1);
106
+ return ` ${key} -> POOLBOT_${suffix}`;
107
+ }),
108
+ ];
109
+ (deps?.noteFn ?? note)(lines.join("\n"), "Environment");
110
+ }
@@ -10,48 +10,59 @@ export function createDoctorPrompter(params) {
10
10
  const nonInteractive = requestedNonInteractive || (!isTty && !yes);
11
11
  const canPrompt = isTty && !yes && !nonInteractive;
12
12
  const confirmDefault = async (p) => {
13
- if (nonInteractive)
13
+ if (nonInteractive) {
14
14
  return false;
15
- if (shouldRepair)
15
+ }
16
+ if (shouldRepair) {
16
17
  return true;
17
- if (!canPrompt)
18
+ }
19
+ if (!canPrompt) {
18
20
  return Boolean(p.initialValue ?? false);
19
- return (guardCancel(await confirm({
21
+ }
22
+ return guardCancel(await confirm({
20
23
  ...p,
21
24
  message: stylePromptMessage(p.message),
22
- }), params.runtime) === true);
25
+ }), params.runtime);
23
26
  };
24
27
  return {
25
28
  confirm: confirmDefault,
26
29
  confirmRepair: async (p) => {
27
- if (nonInteractive)
30
+ if (nonInteractive) {
28
31
  return false;
32
+ }
29
33
  return confirmDefault(p);
30
34
  },
31
35
  confirmAggressive: async (p) => {
32
- if (nonInteractive)
36
+ if (nonInteractive) {
33
37
  return false;
34
- if (shouldRepair && shouldForce)
38
+ }
39
+ if (shouldRepair && shouldForce) {
35
40
  return true;
36
- if (shouldRepair && !shouldForce)
41
+ }
42
+ if (shouldRepair && !shouldForce) {
37
43
  return false;
38
- if (!canPrompt)
44
+ }
45
+ if (!canPrompt) {
39
46
  return Boolean(p.initialValue ?? false);
40
- return (guardCancel(await confirm({
47
+ }
48
+ return guardCancel(await confirm({
41
49
  ...p,
42
50
  message: stylePromptMessage(p.message),
43
- }), params.runtime) === true);
51
+ }), params.runtime);
44
52
  },
45
53
  confirmSkipInNonInteractive: async (p) => {
46
- if (nonInteractive)
54
+ if (nonInteractive) {
47
55
  return false;
48
- if (shouldRepair)
56
+ }
57
+ if (shouldRepair) {
49
58
  return true;
59
+ }
50
60
  return confirmDefault(p);
51
61
  },
52
62
  select: async (p, fallback) => {
53
- if (!canPrompt || shouldRepair)
63
+ if (!canPrompt || shouldRepair) {
54
64
  return fallback;
65
+ }
55
66
  return guardCancel(await select({
56
67
  ...p,
57
68
  message: stylePromptMessage(p.message),
@@ -64,7 +64,7 @@ export async function noteSessionLockHealth(params) {
64
64
  ];
65
65
  if (staleCount > 0 && !shouldRepair) {
66
66
  lines.push(`- ${staleCount} lock file${staleCount === 1 ? " is" : "s are"} stale.`);
67
- lines.push('- Run "openclaw doctor --fix" to remove stale lock files automatically.');
67
+ lines.push('- Run "poolbot doctor --fix" to remove stale lock files automatically.');
68
68
  }
69
69
  if (shouldRepair && removedCount > 0) {
70
70
  lines.push(`- Removed ${removedCount} stale session lock file${removedCount === 1 ? "" : "s"}.`);
@@ -16,21 +16,24 @@ import { note } from "../terminal/note.js";
16
16
  import { stylePromptTitle } from "../terminal/prompt-style.js";
17
17
  import { shortenHomePath } from "../utils.js";
18
18
  import { maybeRemoveDeprecatedCliAuthProfiles, maybeRepairAnthropicOAuthProfileId, noteAuthProfileHealth, } from "./doctor-auth.js";
19
+ import { doctorShellCompletion } from "./doctor-completion.js";
19
20
  import { loadAndMaybeMigrateDoctorConfig } from "./doctor-config-flow.js";
20
21
  import { maybeRepairGatewayDaemon } from "./doctor-gateway-daemon-flow.js";
21
22
  import { checkGatewayHealth } from "./doctor-gateway-health.js";
22
- import { maybeMigrateLegacyGatewayService, maybeRepairGatewayServiceConfig, maybeScanExtraGatewayServices, } from "./doctor-gateway-services.js";
23
+ import { maybeRepairGatewayServiceConfig, maybeScanExtraGatewayServices, } from "./doctor-gateway-services.js";
23
24
  import { noteSourceInstallIssues } from "./doctor-install.js";
24
- import { noteMacLaunchAgentOverrides, noteMacLaunchctlGatewayEnvOverrides, } from "./doctor-platform-notes.js";
25
+ import { noteMemorySearchHealth } from "./doctor-memory-search.js";
26
+ import { noteMacLaunchAgentOverrides, noteMacLaunchctlGatewayEnvOverrides, noteDeprecatedLegacyEnvVars, } from "./doctor-platform-notes.js";
25
27
  import { createDoctorPrompter } from "./doctor-prompter.js";
26
28
  import { maybeRepairSandboxImages, noteSandboxScopeWarnings } from "./doctor-sandbox.js";
27
29
  import { noteSecurityWarnings } from "./doctor-security.js";
30
+ import { noteSessionLockHealth } from "./doctor-session-locks.js";
28
31
  import { noteStateIntegrity, noteWorkspaceBackupTip } from "./doctor-state-integrity.js";
29
32
  import { detectLegacyStateMigrations, runLegacyStateMigrations, } from "./doctor-state-migrations.js";
30
33
  import { maybeRepairUiProtocolFreshness } from "./doctor-ui.js";
31
34
  import { maybeOfferUpdateBeforeDoctor } from "./doctor-update.js";
32
- import { MEMORY_SYSTEM_PROMPT, shouldSuggestMemorySystem } from "./doctor-workspace.js";
33
35
  import { noteWorkspaceStatus } from "./doctor-workspace-status.js";
36
+ import { MEMORY_SYSTEM_PROMPT, shouldSuggestMemorySystem } from "./doctor-workspace.js";
34
37
  import { applyWizardMetadata, printWizardHeader, randomToken } from "./onboard-helpers.js";
35
38
  import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js";
36
39
  const intro = (message) => clackIntro(stylePromptTitle(message) ?? message);
@@ -41,7 +44,7 @@ function resolveMode(cfg) {
41
44
  export async function doctorCommand(runtime = defaultRuntime, options = {}) {
42
45
  const prompter = createDoctorPrompter({ runtime, options });
43
46
  printWizardHeader(runtime);
44
- intro("Poolbot doctor");
47
+ intro("Pool Bot doctor");
45
48
  const root = await resolvePoolBotPackageRoot({
46
49
  moduleUrl: import.meta.url,
47
50
  argv1: process.argv[1],
@@ -54,15 +57,19 @@ export async function doctorCommand(runtime = defaultRuntime, options = {}) {
54
57
  confirm: (p) => prompter.confirm(p),
55
58
  outro,
56
59
  });
57
- if (updateResult.handled)
60
+ if (updateResult.handled) {
58
61
  return;
62
+ }
59
63
  await maybeRepairUiProtocolFreshness(runtime, prompter);
60
64
  noteSourceInstallIssues(root);
65
+ noteDeprecatedLegacyEnvVars();
61
66
  const configResult = await loadAndMaybeMigrateDoctorConfig({
62
67
  options,
63
68
  confirm: (p) => prompter.confirm(p),
64
69
  });
65
70
  let cfg = configResult.cfg;
71
+ const cfgForPersistence = structuredClone(cfg);
72
+ const sourceConfigValid = configResult.sourceConfigValid ?? true;
66
73
  const configPath = configResult.path ?? CONFIG_PATH;
67
74
  if (!cfg.gateway?.mode) {
68
75
  const lines = [
@@ -86,7 +93,7 @@ export async function doctorCommand(runtime = defaultRuntime, options = {}) {
86
93
  if (gatewayDetails.remoteFallbackNote) {
87
94
  note(gatewayDetails.remoteFallbackNote, "Gateway");
88
95
  }
89
- if (resolveMode(cfg) === "local") {
96
+ if (resolveMode(cfg) === "local" && sourceConfigValid) {
90
97
  const auth = resolveGatewayAuth({
91
98
  authConfig: cfg.gateway?.auth,
92
99
  tailscaleMode: cfg.gateway?.tailscale?.mode ?? "off",
@@ -141,10 +148,10 @@ export async function doctorCommand(runtime = defaultRuntime, options = {}) {
141
148
  }
142
149
  }
143
150
  await noteStateIntegrity(cfg, prompter, configResult.path ?? CONFIG_PATH);
151
+ await noteSessionLockHealth({ shouldRepair: prompter.shouldRepair });
144
152
  cfg = await maybeRepairSandboxImages(cfg, runtime, prompter);
145
153
  noteSandboxScopeWarnings(cfg);
146
- await maybeMigrateLegacyGatewayService(cfg, resolveMode(cfg), runtime, prompter);
147
- await maybeScanExtraGatewayServices(options);
154
+ await maybeScanExtraGatewayServices(options, runtime, prompter);
148
155
  await maybeRepairGatewayServiceConfig(cfg, resolveMode(cfg), runtime, prompter);
149
156
  await noteMacLaunchAgentOverrides();
150
157
  await noteMacLaunchctlGatewayEnvOverrides(cfg);
@@ -207,6 +214,11 @@ export async function doctorCommand(runtime = defaultRuntime, options = {}) {
207
214
  }
208
215
  }
209
216
  noteWorkspaceStatus(cfg);
217
+ await noteMemorySearchHealth(cfg);
218
+ // Check and fix shell completion
219
+ await doctorShellCompletion(runtime, prompter, {
220
+ nonInteractive: options.nonInteractive,
221
+ });
210
222
  const { healthOk } = await checkGatewayHealth({
211
223
  runtime,
212
224
  cfg,
@@ -220,7 +232,7 @@ export async function doctorCommand(runtime = defaultRuntime, options = {}) {
220
232
  gatewayDetailsMessage: gatewayDetails.message,
221
233
  healthOk,
222
234
  });
223
- const shouldWriteConfig = prompter.shouldRepair || configResult.shouldWriteConfig;
235
+ const shouldWriteConfig = configResult.shouldWriteConfig || JSON.stringify(cfg) !== JSON.stringify(cfgForPersistence);
224
236
  if (shouldWriteConfig) {
225
237
  cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
226
238
  await writeConfigFile(cfg);