@poolzin/pool-bot 2026.1.39 → 2026.2.1

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 (511) hide show
  1. package/assets/chrome-extension/README.md +3 -3
  2. package/assets/chrome-extension/background.js +5 -5
  3. package/assets/chrome-extension/manifest.json +3 -3
  4. package/assets/chrome-extension/options.html +4 -4
  5. package/assets/chrome-extension/options.js +1 -1
  6. package/dist/acp/client.js +3 -3
  7. package/dist/acp/types.js +1 -1
  8. package/dist/agents/agent-paths.js +3 -3
  9. package/dist/agents/auth-profiles/paths.js +3 -3
  10. package/dist/agents/bash-tools.exec.js +76 -25
  11. package/dist/agents/cli-runner/helpers.js +10 -12
  12. package/dist/agents/cli-runner.js +2 -2
  13. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  14. package/dist/agents/compaction.js +16 -2
  15. package/dist/agents/context-window-guard.js +13 -10
  16. package/dist/agents/context.js +4 -4
  17. package/dist/agents/docs-path.js +1 -1
  18. package/dist/agents/identity.js +47 -7
  19. package/dist/agents/memory-search.js +25 -8
  20. package/dist/agents/minimax-vlm.js +1 -1
  21. package/dist/agents/model-auth.js +12 -1
  22. package/dist/agents/model-catalog.js +4 -4
  23. package/dist/agents/model-selection.js +31 -4
  24. package/dist/agents/models-config.js +3 -3
  25. package/dist/agents/models-config.providers.js +147 -39
  26. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  27. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  28. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  29. package/dist/agents/pi-embedded-helpers.js +1 -1
  30. package/dist/agents/pi-embedded-runner/compact.js +9 -8
  31. package/dist/agents/pi-embedded-runner/model.js +63 -4
  32. package/dist/agents/pi-embedded-runner/run/attempt.js +27 -17
  33. package/dist/agents/pi-embedded-runner/run.js +203 -50
  34. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  35. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  36. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  37. package/dist/agents/pi-embedded-subscribe.js +118 -29
  38. package/dist/agents/pi-model-discovery.js +10 -0
  39. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  40. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  41. package/dist/agents/pi-tools.js +20 -10
  42. package/dist/agents/pi-tools.read.js +2 -2
  43. package/dist/agents/poolbot-tools.js +15 -10
  44. package/dist/agents/sandbox-paths.js +31 -0
  45. package/dist/agents/session-file-repair.js +83 -0
  46. package/dist/agents/session-tool-result-guard.js +94 -15
  47. package/dist/agents/session-transcript-repair.js +68 -0
  48. package/dist/agents/shell-utils.js +51 -0
  49. package/dist/agents/skills/bundled-context.js +23 -0
  50. package/dist/agents/skills/bundled-dir.js +41 -7
  51. package/dist/agents/skills/frontmatter.js +1 -1
  52. package/dist/agents/skills/workspace.js +2 -2
  53. package/dist/agents/skills-install.js +60 -23
  54. package/dist/agents/subagent-announce.js +79 -34
  55. package/dist/agents/system-prompt.js +28 -4
  56. package/dist/agents/together-models.js +127 -0
  57. package/dist/agents/tool-images.js +1 -1
  58. package/dist/agents/tool-policy.conformance.js +14 -0
  59. package/dist/agents/tool-policy.js +25 -1
  60. package/dist/agents/tools/browser-tool.js +3 -3
  61. package/dist/agents/tools/cron-tool.js +166 -19
  62. package/dist/agents/tools/discord-actions-presence.js +78 -0
  63. package/dist/agents/tools/image-tool.js +2 -2
  64. package/dist/agents/tools/memory-tool.js +93 -5
  65. package/dist/agents/tools/message-tool.js +56 -2
  66. package/dist/agents/tools/sessions-history-tool.js +69 -1
  67. package/dist/agents/tools/web-search.js +211 -42
  68. package/dist/agents/usage.js +23 -1
  69. package/dist/agents/workspace-run.js +67 -0
  70. package/dist/agents/workspace-templates.js +44 -0
  71. package/dist/auto-reply/command-auth.js +121 -6
  72. package/dist/auto-reply/commands-registry.data.js +1 -1
  73. package/dist/auto-reply/envelope.js +50 -72
  74. package/dist/auto-reply/reply/commands-compact.js +1 -0
  75. package/dist/auto-reply/reply/commands-context-report.js +3 -2
  76. package/dist/auto-reply/reply/commands-context.js +1 -0
  77. package/dist/auto-reply/reply/commands-models.js +107 -60
  78. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  79. package/dist/auto-reply/reply/commands-session.js +2 -2
  80. package/dist/auto-reply/reply/get-reply-run.js +16 -5
  81. package/dist/auto-reply/reply/groups.js +1 -1
  82. package/dist/auto-reply/reply/inbound-context.js +9 -1
  83. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  84. package/dist/auto-reply/reply/model-selection.js +3 -3
  85. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  86. package/dist/auto-reply/status.js +1 -1
  87. package/dist/auto-reply/thinking.js +88 -43
  88. package/dist/browser/bridge-server.js +13 -0
  89. package/dist/browser/cdp.helpers.js +38 -24
  90. package/dist/browser/client-fetch.js +51 -8
  91. package/dist/browser/config.js +2 -11
  92. package/dist/browser/extension-relay.js +104 -43
  93. package/dist/browser/pw-ai.js +1 -1
  94. package/dist/browser/pw-session.js +143 -8
  95. package/dist/browser/pw-tools-core.interactions.js +125 -27
  96. package/dist/browser/pw-tools-core.responses.js +1 -1
  97. package/dist/browser/pw-tools-core.state.js +1 -1
  98. package/dist/browser/routes/agent.act.js +86 -41
  99. package/dist/browser/routes/dispatcher.js +4 -4
  100. package/dist/browser/screenshot.js +1 -1
  101. package/dist/browser/server-context.js +2 -2
  102. package/dist/browser/server.js +13 -0
  103. package/dist/build-info.json +3 -3
  104. package/dist/canvas-host/a2ui.js +3 -3
  105. package/dist/channels/plugins/catalog.js +2 -2
  106. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  107. package/dist/channels/plugins/onboarding/signal.js +1 -1
  108. package/dist/channels/plugins/onboarding/slack.js +4 -4
  109. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  110. package/dist/channels/plugins/pairing-message.js +1 -1
  111. package/dist/channels/reply-prefix.js +8 -1
  112. package/dist/cli/browser-cli-extension.js +2 -2
  113. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  114. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  115. package/dist/cli/cron-cli/shared.js +56 -41
  116. package/dist/cli/dns-cli.js +26 -14
  117. package/dist/cli/docs-cli.js +1 -1
  118. package/dist/cli/gateway-cli/dev.js +1 -1
  119. package/dist/cli/gateway-cli/register.js +37 -19
  120. package/dist/cli/memory-cli.js +30 -20
  121. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  122. package/dist/cli/parse-bytes.js +37 -0
  123. package/dist/cli/plugins-cli.js +1 -1
  124. package/dist/cli/run-main.js +2 -2
  125. package/dist/cli/security-cli.js +1 -1
  126. package/dist/cli/tagline.js +1 -1
  127. package/dist/cli/update-cli.js +173 -52
  128. package/dist/cli/webhooks-cli.js +5 -5
  129. package/dist/commands/agent.js +1 -0
  130. package/dist/commands/agents.commands.add.js +1 -1
  131. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  132. package/dist/commands/auth-choice.apply.js +4 -1
  133. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  134. package/dist/commands/auth-choice.apply.xai.js +63 -0
  135. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  136. package/dist/commands/configure.wizard.js +1 -1
  137. package/dist/commands/dashboard.js +1 -1
  138. package/dist/commands/docs.js +1 -1
  139. package/dist/commands/doctor-config-flow.js +61 -5
  140. package/dist/commands/doctor-gateway-services.js +3 -3
  141. package/dist/commands/doctor-state-migrations.js +1 -1
  142. package/dist/commands/doctor-update.js +3 -3
  143. package/dist/commands/doctor.js +1 -1
  144. package/dist/commands/health.js +1 -1
  145. package/dist/commands/model-allowlist.js +29 -0
  146. package/dist/commands/model-picker.js +2 -1
  147. package/dist/commands/models/list.probe.js +2 -2
  148. package/dist/commands/models/list.registry.js +4 -4
  149. package/dist/commands/models/list.status-command.js +44 -24
  150. package/dist/commands/models/shared.js +15 -0
  151. package/dist/commands/onboard-auth.config-core.js +366 -28
  152. package/dist/commands/onboard-auth.credentials.js +71 -9
  153. package/dist/commands/onboard-auth.js +3 -3
  154. package/dist/commands/onboard-auth.models.js +26 -24
  155. package/dist/commands/onboard-custom.js +384 -0
  156. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  157. package/dist/commands/onboard-non-interactive/local/auth-choice.js +146 -9
  158. package/dist/commands/onboard-skills.js +63 -38
  159. package/dist/commands/openai-model-default.js +41 -0
  160. package/dist/commands/status-all/report-lines.js +1 -1
  161. package/dist/commands/status.command.js +1 -1
  162. package/dist/commands/uninstall.js +3 -3
  163. package/dist/compat/legacy-names.js +1 -1
  164. package/dist/config/defaults.js +3 -2
  165. package/dist/config/io.js +3 -3
  166. package/dist/config/paths.js +136 -35
  167. package/dist/config/plugin-auto-enable.js +21 -5
  168. package/dist/config/redact-snapshot.js +153 -0
  169. package/dist/config/schema.field-metadata.js +590 -0
  170. package/dist/config/schema.js +3 -3
  171. package/dist/config/sessions/store.js +291 -23
  172. package/dist/config/types.memory.js +1 -0
  173. package/dist/config/version.js +4 -4
  174. package/dist/config/zod-schema.agent-defaults.js +3 -0
  175. package/dist/config/zod-schema.agent-runtime.js +13 -2
  176. package/dist/config/zod-schema.providers-core.js +142 -0
  177. package/dist/config/zod-schema.session.js +3 -0
  178. package/dist/cron/delivery.js +57 -0
  179. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  180. package/dist/cron/isolated-agent/helpers.js +22 -5
  181. package/dist/cron/isolated-agent/run.js +171 -63
  182. package/dist/cron/isolated-agent/session.js +2 -0
  183. package/dist/cron/normalize.js +356 -28
  184. package/dist/cron/parse.js +10 -5
  185. package/dist/cron/run-log.js +35 -10
  186. package/dist/cron/schedule.js +41 -6
  187. package/dist/cron/service/jobs.js +208 -35
  188. package/dist/cron/service/ops.js +72 -16
  189. package/dist/cron/service/state.js +2 -0
  190. package/dist/cron/service/store.js +386 -14
  191. package/dist/cron/service/timer.js +390 -147
  192. package/dist/cron/session-reaper.js +86 -0
  193. package/dist/cron/store.js +23 -8
  194. package/dist/cron/validate-timestamp.js +43 -0
  195. package/dist/daemon/constants.js +7 -7
  196. package/dist/daemon/inspect.js +6 -6
  197. package/dist/daemon/systemd-unit.js +1 -1
  198. package/dist/discord/monitor/agent-components.js +438 -0
  199. package/dist/discord/monitor/allow-list.js +28 -5
  200. package/dist/discord/monitor/gateway-registry.js +29 -0
  201. package/dist/discord/monitor/native-command.js +44 -23
  202. package/dist/discord/monitor/sender-identity.js +45 -0
  203. package/dist/discord/pluralkit.js +27 -0
  204. package/dist/discord/send.outbound.js +92 -5
  205. package/dist/discord/send.shared.js +60 -23
  206. package/dist/discord/targets.js +84 -1
  207. package/dist/entry.js +15 -9
  208. package/dist/extensionAPI.js +8 -0
  209. package/dist/gateway/control-ui.js +8 -1
  210. package/dist/gateway/hooks-mapping.js +3 -0
  211. package/dist/gateway/hooks.js +65 -0
  212. package/dist/gateway/live-image-probe.js +1 -66
  213. package/dist/gateway/net.js +96 -31
  214. package/dist/gateway/node-command-policy.js +50 -15
  215. package/dist/gateway/openai-http.js +2 -2
  216. package/dist/gateway/openresponses-http.js +4 -4
  217. package/dist/gateway/origin-check.js +56 -0
  218. package/dist/gateway/protocol/client-info.js +9 -0
  219. package/dist/gateway/protocol/index.js +9 -2
  220. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  221. package/dist/gateway/protocol/schema/cron.js +22 -10
  222. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  223. package/dist/gateway/protocol/schema/sessions.js +12 -0
  224. package/dist/gateway/server/hooks.js +1 -1
  225. package/dist/gateway/server-broadcast.js +26 -9
  226. package/dist/gateway/server-chat.js +112 -23
  227. package/dist/gateway/server-discovery-runtime.js +10 -2
  228. package/dist/gateway/server-discovery.js +2 -2
  229. package/dist/gateway/server-http.js +110 -12
  230. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  231. package/dist/gateway/server-methods/agents.js +321 -2
  232. package/dist/gateway/server-methods/usage.js +559 -16
  233. package/dist/gateway/server-runtime-state.js +22 -8
  234. package/dist/gateway/server-startup-memory.js +16 -0
  235. package/dist/gateway/server.impl.js +7 -3
  236. package/dist/gateway/session-utils.fs.js +23 -25
  237. package/dist/gateway/session-utils.js +20 -10
  238. package/dist/gateway/sessions-patch.js +7 -22
  239. package/dist/gateway/test-helpers.server.js +35 -2
  240. package/dist/hooks/frontmatter.js +1 -1
  241. package/dist/hooks/hooks-status.js +1 -1
  242. package/dist/hooks/install.js +2 -2
  243. package/dist/hooks/loader.js +1 -1
  244. package/dist/hooks/workspace.js +3 -3
  245. package/dist/imessage/constants.js +2 -0
  246. package/dist/imessage/monitor/deliver.js +4 -1
  247. package/dist/imessage/monitor/monitor-provider.js +51 -1
  248. package/dist/index.js +2 -2
  249. package/dist/infra/bonjour-discovery.js +131 -70
  250. package/dist/infra/bonjour.js +3 -3
  251. package/dist/infra/control-ui-assets.js +134 -12
  252. package/dist/infra/errors.js +12 -0
  253. package/dist/infra/exec-approvals.js +266 -57
  254. package/dist/infra/format-time/format-datetime.js +79 -0
  255. package/dist/infra/format-time/format-duration.js +81 -0
  256. package/dist/infra/format-time/format-relative.js +80 -0
  257. package/dist/infra/heartbeat-runner.js +140 -49
  258. package/dist/infra/home-dir.js +54 -0
  259. package/dist/infra/net/fetch-guard.js +122 -0
  260. package/dist/infra/net/ssrf.js +65 -29
  261. package/dist/infra/outbound/abort.js +14 -0
  262. package/dist/infra/outbound/message-action-runner.js +77 -13
  263. package/dist/infra/outbound/outbound-session.js +143 -37
  264. package/dist/infra/path-env.js +3 -3
  265. package/dist/infra/poolbot-root.js +43 -1
  266. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  267. package/dist/infra/restart.js +1 -1
  268. package/dist/infra/session-cost-usage.js +631 -41
  269. package/dist/infra/state-migrations.js +317 -47
  270. package/dist/infra/tailscale.js +1 -1
  271. package/dist/infra/update-global.js +35 -0
  272. package/dist/infra/update-runner.js +149 -43
  273. package/dist/infra/warning-filter.js +65 -0
  274. package/dist/infra/widearea-dns.js +30 -9
  275. package/dist/logging/redact-identifier.js +12 -0
  276. package/dist/macos/relay.js +2 -2
  277. package/dist/media/fetch.js +81 -58
  278. package/dist/media/input-files.js +1 -1
  279. package/dist/media/mime.js +4 -0
  280. package/dist/media/png-encode.js +74 -0
  281. package/dist/media-understanding/apply.js +403 -3
  282. package/dist/media-understanding/attachments.js +38 -27
  283. package/dist/media-understanding/defaults.js +16 -0
  284. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  285. package/dist/media-understanding/providers/google/audio.js +24 -17
  286. package/dist/media-understanding/providers/google/video.js +24 -17
  287. package/dist/media-understanding/providers/image.js +4 -4
  288. package/dist/media-understanding/providers/index.js +4 -1
  289. package/dist/media-understanding/providers/openai/audio.js +22 -14
  290. package/dist/media-understanding/providers/shared.js +16 -11
  291. package/dist/media-understanding/providers/zai/index.js +6 -0
  292. package/dist/media-understanding/runner.js +158 -90
  293. package/dist/memory/backend-config.js +207 -0
  294. package/dist/memory/batch-voyage.js +277 -0
  295. package/dist/memory/embeddings-voyage.js +75 -0
  296. package/dist/memory/embeddings.js +29 -17
  297. package/dist/memory/internal.js +101 -18
  298. package/dist/memory/manager.js +155 -48
  299. package/dist/memory/search-manager.js +173 -0
  300. package/dist/memory/session-files.js +9 -3
  301. package/dist/memory/types.js +1 -0
  302. package/dist/node-host/runner.js +36 -26
  303. package/dist/node-host/with-timeout.js +27 -0
  304. package/dist/pairing/pairing-messages.js +1 -1
  305. package/dist/plugins/commands.js +5 -1
  306. package/dist/plugins/config-state.js +86 -7
  307. package/dist/plugins/discovery.js +1 -1
  308. package/dist/plugins/install.js +2 -2
  309. package/dist/plugins/source-display.js +51 -0
  310. package/dist/plugins/update.js +1 -1
  311. package/dist/process/exec.js +20 -2
  312. package/dist/routing/resolve-route.js +12 -0
  313. package/dist/routing/session-key.js +15 -0
  314. package/dist/runtime.js +2 -0
  315. package/dist/security/audit-extra.async.js +601 -0
  316. package/dist/security/audit-extra.js +2 -830
  317. package/dist/security/audit-extra.sync.js +505 -0
  318. package/dist/security/audit.js +2 -2
  319. package/dist/security/channel-metadata.js +34 -0
  320. package/dist/security/external-content.js +88 -6
  321. package/dist/security/skill-scanner.js +330 -0
  322. package/dist/sessions/session-key-utils.js +7 -0
  323. package/dist/shared/text/reasoning-tags.js +52 -7
  324. package/dist/signal/monitor/event-handler.js +80 -1
  325. package/dist/slack/monitor/media.js +85 -15
  326. package/dist/tailscale/detect.js +145 -0
  327. package/dist/telegram/bot/helpers.js +109 -28
  328. package/dist/telegram/bot-handlers.js +144 -3
  329. package/dist/telegram/bot-message-context.js +38 -11
  330. package/dist/telegram/bot-message-dispatch.js +48 -15
  331. package/dist/telegram/bot-native-commands.js +86 -29
  332. package/dist/telegram/bot.js +30 -29
  333. package/dist/telegram/model-buttons.js +163 -0
  334. package/dist/telegram/monitor.js +110 -85
  335. package/dist/telegram/send.js +129 -47
  336. package/dist/terminal/restore.js +45 -0
  337. package/dist/test-helpers/state-dir-env.js +16 -0
  338. package/dist/test-helpers/workspace.js +11 -0
  339. package/dist/test-utils/channel-plugins.js +82 -0
  340. package/dist/test-utils/ports.js +73 -0
  341. package/dist/tts/tts.js +12 -6
  342. package/dist/tui/tui-session-actions.js +166 -54
  343. package/dist/utils/fetch-timeout.js +20 -0
  344. package/dist/utils/normalize-secret-input.js +19 -0
  345. package/dist/utils/shell-argv.js +61 -0
  346. package/dist/utils/transcript-tools.js +58 -0
  347. package/dist/utils.js +55 -14
  348. package/dist/version.js +42 -5
  349. package/dist/web/qr-image.js +1 -61
  350. package/dist/wizard/onboarding.finalize.js +7 -7
  351. package/dist/wizard/onboarding.js +3 -3
  352. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  353. package/docs/_config.yml +2 -2
  354. package/docs/_layouts/default.html +9 -9
  355. package/docs/concepts/typebox.md +1 -1
  356. package/docs/docs.json +1 -1
  357. package/docs/northflank.mdx +7 -7
  358. package/docs/railway.mdx +3 -3
  359. package/docs/render.mdx +5 -5
  360. package/docs/start/lore.md +2 -2
  361. package/extensions/bluebubbles/index.ts +2 -2
  362. package/extensions/bluebubbles/package.json +1 -1
  363. package/extensions/bluebubbles/src/accounts.ts +8 -8
  364. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  365. package/extensions/bluebubbles/src/actions.ts +5 -5
  366. package/extensions/bluebubbles/src/attachments.ts +2 -2
  367. package/extensions/bluebubbles/src/channel.ts +16 -16
  368. package/extensions/bluebubbles/src/chat.ts +2 -2
  369. package/extensions/bluebubbles/src/media-send.ts +2 -2
  370. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  371. package/extensions/bluebubbles/src/monitor.ts +5 -5
  372. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  373. package/extensions/bluebubbles/src/reactions.ts +2 -2
  374. package/extensions/bluebubbles/src/send.ts +2 -2
  375. package/extensions/copilot-proxy/README.md +1 -1
  376. package/extensions/copilot-proxy/package.json +1 -1
  377. package/extensions/diagnostics-otel/index.ts +2 -2
  378. package/extensions/diagnostics-otel/package.json +1 -1
  379. package/extensions/diagnostics-otel/src/service.ts +3 -3
  380. package/extensions/discord/index.ts +2 -2
  381. package/extensions/discord/package.json +1 -1
  382. package/extensions/google-antigravity-auth/README.md +1 -1
  383. package/extensions/google-antigravity-auth/index.ts +1 -1
  384. package/extensions/google-antigravity-auth/package.json +1 -1
  385. package/extensions/google-gemini-cli-auth/README.md +1 -1
  386. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  387. package/extensions/google-gemini-cli-auth/package.json +1 -1
  388. package/extensions/googlechat/index.ts +3 -3
  389. package/extensions/googlechat/package.json +1 -1
  390. package/extensions/googlechat/src/accounts.ts +8 -8
  391. package/extensions/googlechat/src/actions.ts +6 -6
  392. package/extensions/googlechat/src/channel.ts +21 -21
  393. package/extensions/googlechat/src/monitor.ts +8 -8
  394. package/extensions/googlechat/src/onboarding.ts +10 -10
  395. package/extensions/imessage/index.ts +2 -2
  396. package/extensions/imessage/package.json +1 -1
  397. package/extensions/line/index.ts +2 -2
  398. package/extensions/line/package.json +1 -1
  399. package/extensions/line/src/card-command.ts +2 -2
  400. package/extensions/line/src/channel.logout.test.ts +4 -4
  401. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  402. package/extensions/line/src/channel.ts +3 -3
  403. package/extensions/llm-task/README.md +3 -3
  404. package/extensions/llm-task/index.ts +2 -2
  405. package/extensions/llm-task/package.json +1 -1
  406. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  407. package/extensions/lobster/README.md +6 -6
  408. package/extensions/lobster/index.ts +2 -2
  409. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  410. package/extensions/lobster/src/lobster-tool.ts +2 -2
  411. package/extensions/matrix/index.ts +2 -2
  412. package/extensions/matrix/package.json +1 -1
  413. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  414. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  415. package/extensions/matrix/src/onboarding.ts +1 -1
  416. package/extensions/mattermost/index.ts +2 -2
  417. package/extensions/mattermost/package.json +1 -1
  418. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  419. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  420. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  421. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  422. package/extensions/mattermost/src/onboarding.ts +2 -2
  423. package/extensions/memory-core/index.ts +2 -2
  424. package/extensions/memory-core/package.json +1 -1
  425. package/extensions/memory-lancedb/index.ts +3 -3
  426. package/extensions/memory-lancedb/package.json +1 -1
  427. package/extensions/msteams/index.ts +2 -2
  428. package/extensions/msteams/package.json +1 -1
  429. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  430. package/extensions/msteams/src/channel.ts +2 -2
  431. package/extensions/msteams/src/graph-upload.ts +4 -4
  432. package/extensions/msteams/src/monitor-handler.ts +2 -2
  433. package/extensions/msteams/src/monitor.ts +2 -2
  434. package/extensions/msteams/src/onboarding.ts +9 -9
  435. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  436. package/extensions/msteams/src/send-context.ts +2 -2
  437. package/extensions/msteams/src/send.ts +4 -4
  438. package/extensions/nextcloud-talk/index.ts +2 -2
  439. package/extensions/nextcloud-talk/package.json +1 -1
  440. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  441. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  442. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  443. package/extensions/nostr/README.md +2 -2
  444. package/extensions/nostr/index.ts +5 -5
  445. package/extensions/nostr/package.json +1 -1
  446. package/extensions/nostr/src/types.ts +4 -4
  447. package/extensions/open-prose/index.ts +2 -2
  448. package/extensions/qwen-portal-auth/README.md +1 -1
  449. package/extensions/signal/index.ts +2 -2
  450. package/extensions/signal/package.json +1 -1
  451. package/extensions/slack/index.ts +2 -2
  452. package/extensions/slack/package.json +1 -1
  453. package/extensions/telegram/index.ts +2 -2
  454. package/extensions/telegram/package.json +1 -1
  455. package/extensions/telegram/src/channel.ts +2 -2
  456. package/extensions/tlon/README.md +2 -2
  457. package/extensions/tlon/index.ts +2 -2
  458. package/extensions/tlon/package.json +1 -1
  459. package/extensions/tlon/src/channel.ts +13 -13
  460. package/extensions/tlon/src/monitor/index.ts +3 -3
  461. package/extensions/tlon/src/onboarding.ts +3 -3
  462. package/extensions/tlon/src/types.ts +3 -3
  463. package/extensions/twitch/README.md +1 -1
  464. package/extensions/twitch/index.ts +2 -2
  465. package/extensions/twitch/package.json +1 -1
  466. package/extensions/twitch/src/config.ts +3 -3
  467. package/extensions/twitch/src/monitor.ts +3 -3
  468. package/extensions/twitch/src/onboarding.ts +9 -9
  469. package/extensions/twitch/src/outbound.test.ts +2 -2
  470. package/extensions/twitch/src/plugin.test.ts +2 -2
  471. package/extensions/twitch/src/plugin.ts +8 -8
  472. package/extensions/twitch/src/send.test.ts +2 -2
  473. package/extensions/twitch/src/send.ts +4 -4
  474. package/extensions/twitch/src/token.test.ts +8 -8
  475. package/extensions/twitch/src/token.ts +3 -3
  476. package/extensions/twitch/src/twitch-client.ts +3 -3
  477. package/extensions/twitch/src/types.ts +3 -3
  478. package/extensions/twitch/src/utils/markdown.ts +1 -1
  479. package/extensions/voice-call/README.md +3 -3
  480. package/extensions/voice-call/package.json +1 -1
  481. package/extensions/voice-call/src/core-bridge.ts +2 -2
  482. package/extensions/voice-call/src/response-generator.ts +1 -1
  483. package/extensions/whatsapp/index.ts +2 -2
  484. package/extensions/whatsapp/package.json +1 -1
  485. package/extensions/zalo/README.md +1 -1
  486. package/extensions/zalo/index.ts +2 -2
  487. package/extensions/zalo/package.json +1 -1
  488. package/extensions/zalo/src/accounts.ts +8 -8
  489. package/extensions/zalo/src/actions.ts +4 -4
  490. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  491. package/extensions/zalo/src/channel.ts +18 -18
  492. package/extensions/zalo/src/monitor.ts +9 -9
  493. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  494. package/extensions/zalo/src/onboarding.ts +24 -24
  495. package/extensions/zalo/src/send.ts +2 -2
  496. package/extensions/zalouser/README.md +2 -2
  497. package/extensions/zalouser/index.ts +2 -2
  498. package/extensions/zalouser/package.json +1 -1
  499. package/extensions/zalouser/src/accounts.ts +9 -9
  500. package/extensions/zalouser/src/channel.ts +24 -24
  501. package/extensions/zalouser/src/monitor.ts +4 -4
  502. package/extensions/zalouser/src/onboarding.ts +28 -28
  503. package/package.json +13 -251
  504. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  505. package/skills/tmux/scripts/find-sessions.sh +1 -1
  506. package/CHANGELOG.md +0 -102
  507. package/README-header.png +0 -0
  508. package/git-hooks/pre-commit +0 -4
  509. package/scripts/format-staged.js +0 -148
  510. package/scripts/postinstall.js +0 -300
  511. package/scripts/setup-git-hooks.js +0 -96
@@ -1,8 +1,10 @@
1
1
  import { buildMessagingTarget, ensureTargetId, requireTargetKind, } from "../channels/targets.js";
2
+ import { listDiscordDirectoryPeersLive } from "./directory-live.js";
2
3
  export function parseDiscordTarget(raw, options = {}) {
3
4
  const trimmed = raw.trim();
4
- if (!trimmed)
5
+ if (!trimmed) {
5
6
  return undefined;
7
+ }
6
8
  const mentionMatch = trimmed.match(/^<@!?(\d+)>$/);
7
9
  if (mentionMatch) {
8
10
  return buildMessagingTarget("user", mentionMatch[1], trimmed);
@@ -41,3 +43,84 @@ export function resolveDiscordChannelId(raw) {
41
43
  const target = parseDiscordTarget(raw, { defaultKind: "channel" });
42
44
  return requireTargetKind({ platform: "Discord", target, kind: "channel" });
43
45
  }
46
+ /**
47
+ * Resolve a Discord username to user ID using the directory lookup.
48
+ * This enables sending DMs by username instead of requiring explicit user IDs.
49
+ *
50
+ * @param raw - The username or raw target string (e.g., "john.doe")
51
+ * @param options - Directory configuration params (cfg, accountId, limit)
52
+ * @param parseOptions - Messaging target parsing options (defaults, ambiguity message)
53
+ * @returns Parsed MessagingTarget with user ID, or undefined if not found
54
+ */
55
+ export async function resolveDiscordTarget(raw, options, parseOptions = {}) {
56
+ const trimmed = raw.trim();
57
+ if (!trimmed) {
58
+ return undefined;
59
+ }
60
+ const likelyUsername = isLikelyUsername(trimmed);
61
+ const shouldLookup = isExplicitUserLookup(trimmed, parseOptions) || likelyUsername;
62
+ // Parse directly if it's already a known format. Use a safe parse so ambiguous
63
+ // numeric targets don't throw when we still want to attempt username lookup.
64
+ const directParse = safeParseDiscordTarget(trimmed, parseOptions);
65
+ if (directParse && directParse.kind !== "channel" && !likelyUsername) {
66
+ return directParse;
67
+ }
68
+ if (!shouldLookup) {
69
+ return directParse ?? parseDiscordTarget(trimmed, parseOptions);
70
+ }
71
+ // Try to resolve as a username via directory lookup
72
+ try {
73
+ const directoryEntries = await listDiscordDirectoryPeersLive({
74
+ ...options,
75
+ query: trimmed,
76
+ limit: 1,
77
+ });
78
+ const match = directoryEntries[0];
79
+ if (match && match.kind === "user") {
80
+ // Extract user ID from the directory entry (format: "user:<id>")
81
+ const userId = match.id.replace(/^user:/, "");
82
+ return buildMessagingTarget("user", userId, trimmed);
83
+ }
84
+ }
85
+ catch {
86
+ // Directory lookup failed - fall through to parse as-is
87
+ // This preserves existing behavior for channel names
88
+ }
89
+ // Fallback to original parsing (for channels, etc.)
90
+ return parseDiscordTarget(trimmed, parseOptions);
91
+ }
92
+ function safeParseDiscordTarget(input, options) {
93
+ try {
94
+ return parseDiscordTarget(input, options);
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ function isExplicitUserLookup(input, options) {
101
+ if (/^<@!?(\d+)>$/.test(input)) {
102
+ return true;
103
+ }
104
+ if (/^(user:|discord:)/.test(input)) {
105
+ return true;
106
+ }
107
+ if (input.startsWith("@")) {
108
+ return true;
109
+ }
110
+ if (/^\d+$/.test(input)) {
111
+ return options.defaultKind === "user";
112
+ }
113
+ return false;
114
+ }
115
+ /**
116
+ * Check if a string looks like a Discord username (not a mention, prefix, or ID).
117
+ * Usernames typically don't start with special characters except underscore.
118
+ */
119
+ function isLikelyUsername(input) {
120
+ // Skip if it's already a known format
121
+ if (/^(user:|channel:|discord:|@|<@!?)|[\d]+$/.test(input)) {
122
+ return false;
123
+ }
124
+ // Likely a username if it doesn't match known patterns
125
+ return true;
126
+ }
package/dist/entry.js CHANGED
@@ -3,32 +3,38 @@ import { spawn } from "node:child_process";
3
3
  import path from "node:path";
4
4
  import process from "node:process";
5
5
  import { applyCliProfileEnv, parseCliProfileArgs } from "./cli/profile.js";
6
- import { isTruthyEnvValue } from "./infra/env.js";
6
+ import { isTruthyEnvValue, normalizeEnv } from "./infra/env.js";
7
7
  import { installProcessWarningFilter } from "./infra/warnings.js";
8
8
  import { attachChildProcessBridge } from "./process/child-process-bridge.js";
9
9
  process.title = "poolbot";
10
10
  installProcessWarningFilter();
11
+ normalizeEnv();
11
12
  if (process.argv.includes("--no-color")) {
12
13
  process.env.NO_COLOR = "1";
13
14
  process.env.FORCE_COLOR = "0";
14
15
  }
15
16
  const EXPERIMENTAL_WARNING_FLAG = "--disable-warning=ExperimentalWarning";
16
- function hasExperimentalWarningSuppressed(nodeOptions) {
17
- if (!nodeOptions)
18
- return false;
19
- return nodeOptions.includes(EXPERIMENTAL_WARNING_FLAG) || nodeOptions.includes("--no-warnings");
17
+ function hasExperimentalWarningSuppressed() {
18
+ const nodeOptions = process.env.NODE_OPTIONS ?? "";
19
+ if (nodeOptions.includes(EXPERIMENTAL_WARNING_FLAG) || nodeOptions.includes("--no-warnings"))
20
+ return true;
21
+ for (const arg of process.execArgv) {
22
+ if (arg === EXPERIMENTAL_WARNING_FLAG || arg === "--no-warnings")
23
+ return true;
24
+ }
25
+ return false;
20
26
  }
21
27
  function ensureExperimentalWarningSuppressed() {
22
28
  if (isTruthyEnvValue(process.env.CLAWDBOT_NO_RESPAWN))
23
29
  return false;
24
30
  if (isTruthyEnvValue(process.env.CLAWDBOT_NODE_OPTIONS_READY))
25
31
  return false;
26
- const nodeOptions = process.env.NODE_OPTIONS ?? "";
27
- if (hasExperimentalWarningSuppressed(nodeOptions))
32
+ if (hasExperimentalWarningSuppressed())
28
33
  return false;
34
+ // Respawn guard (and keep recursion bounded if something goes wrong).
29
35
  process.env.CLAWDBOT_NODE_OPTIONS_READY = "1";
30
- process.env.NODE_OPTIONS = `${nodeOptions} ${EXPERIMENTAL_WARNING_FLAG}`.trim();
31
- const child = spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
36
+ // Pass flag as a Node CLI option, not via NODE_OPTIONS (--disable-warning is disallowed in NODE_OPTIONS).
37
+ const child = spawn(process.execPath, [EXPERIMENTAL_WARNING_FLAG, ...process.execArgv, ...process.argv.slice(1)], {
32
38
  stdio: "inherit",
33
39
  env: process.env,
34
40
  });
@@ -0,0 +1,8 @@
1
+ export { resolveAgentDir, resolveAgentWorkspaceDir } from "./agents/agent-scope.js";
2
+ export { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./agents/defaults.js";
3
+ export { resolveAgentIdentity } from "./agents/identity.js";
4
+ export { resolveThinkingDefault } from "./agents/model-selection.js";
5
+ export { runEmbeddedPiAgent } from "./agents/pi-embedded.js";
6
+ export { resolveAgentTimeoutMs } from "./agents/timeout.js";
7
+ export { ensureAgentWorkspace } from "./agents/workspace.js";
8
+ export { resolveStorePath, loadSessionStore, saveSessionStore, resolveSessionFilePath, } from "./config/sessions.js";
@@ -204,7 +204,14 @@ export function handleControlUiHttpRequest(req, res, opts) {
204
204
  if (!pathname.startsWith(`${basePath}/`))
205
205
  return false;
206
206
  }
207
- const root = resolveControlUiRoot();
207
+ const root = (() => {
208
+ if (opts?.root) {
209
+ if (opts.root.kind === "resolved")
210
+ return opts.root.path;
211
+ return null;
212
+ }
213
+ return resolveControlUiRoot();
214
+ })();
208
215
  if (!root) {
209
216
  res.statusCode = 503;
210
217
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
@@ -87,6 +87,7 @@ function normalizeHookMapping(mapping, index, transformsDir) {
87
87
  action,
88
88
  wakeMode,
89
89
  name: mapping.name,
90
+ agentId: mapping.agentId?.trim() || undefined,
90
91
  sessionKey: mapping.sessionKey,
91
92
  messageTemplate: mapping.messageTemplate,
92
93
  textTemplate: mapping.textTemplate,
@@ -131,6 +132,7 @@ function buildActionFromMapping(mapping, ctx) {
131
132
  kind: "agent",
132
133
  message,
133
134
  name: renderOptional(mapping.name, ctx),
135
+ agentId: mapping.agentId,
134
136
  wakeMode: mapping.wakeMode ?? "now",
135
137
  sessionKey: renderOptional(mapping.sessionKey, ctx),
136
138
  deliver: mapping.deliver,
@@ -162,6 +164,7 @@ function mergeAction(base, override, defaultAction) {
162
164
  message,
163
165
  wakeMode,
164
166
  name: override.name ?? baseAgent?.name,
167
+ agentId: override.agentId ?? baseAgent?.agentId,
165
168
  sessionKey: override.sessionKey ?? baseAgent?.sessionKey,
166
169
  deliver: typeof override.deliver === "boolean" ? override.deliver : baseAgent?.deliver,
167
170
  allowUnsafeExternalContent: typeof override.allowUnsafeExternalContent === "boolean"
@@ -1,5 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js";
2
3
  import { listChannelPlugins } from "../channels/plugins/index.js";
4
+ import { normalizeAgentId } from "../routing/session-key.js";
3
5
  import { normalizeMessageChannel } from "../utils/message-channel.js";
4
6
  import { resolveHookMappings } from "./hooks-mapping.js";
5
7
  const DEFAULT_HOOKS_PATH = "/hooks";
@@ -21,13 +23,48 @@ export function resolveHooksConfig(cfg) {
21
23
  ? cfg.hooks.maxBodyBytes
22
24
  : DEFAULT_HOOKS_MAX_BODY_BYTES;
23
25
  const mappings = resolveHookMappings(cfg.hooks);
26
+ const defaultAgentId = resolveDefaultAgentId(cfg);
27
+ const knownAgentIds = resolveKnownAgentIds(cfg, defaultAgentId);
28
+ const allowedAgentIds = resolveAllowedAgentIds(cfg.hooks?.allowedAgentIds);
24
29
  return {
25
30
  basePath: trimmed,
26
31
  token,
27
32
  maxBodyBytes,
28
33
  mappings,
34
+ agentPolicy: {
35
+ defaultAgentId,
36
+ knownAgentIds,
37
+ allowedAgentIds,
38
+ },
29
39
  };
30
40
  }
41
+ function resolveKnownAgentIds(cfg, defaultAgentId) {
42
+ const known = new Set(listAgentIds(cfg));
43
+ known.add(defaultAgentId);
44
+ return known;
45
+ }
46
+ function resolveAllowedAgentIds(raw) {
47
+ if (!Array.isArray(raw)) {
48
+ return undefined;
49
+ }
50
+ const allowed = new Set();
51
+ let hasWildcard = false;
52
+ for (const entry of raw) {
53
+ const trimmed = entry.trim();
54
+ if (!trimmed) {
55
+ continue;
56
+ }
57
+ if (trimmed === "*") {
58
+ hasWildcard = true;
59
+ break;
60
+ }
61
+ allowed.add(normalizeAgentId(trimmed));
62
+ }
63
+ if (hasWildcard) {
64
+ return undefined;
65
+ }
66
+ return allowed;
67
+ }
31
68
  export function extractHookToken(req, url) {
32
69
  const auth = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : "";
33
70
  if (auth.toLowerCase().startsWith("bearer ")) {
@@ -120,12 +157,39 @@ export function resolveHookChannel(raw) {
120
157
  export function resolveHookDeliver(raw) {
121
158
  return raw !== false;
122
159
  }
160
+ export function resolveHookTargetAgentId(hooksConfig, agentId) {
161
+ const raw = agentId?.trim();
162
+ if (!raw) {
163
+ return undefined;
164
+ }
165
+ const normalized = normalizeAgentId(raw);
166
+ if (hooksConfig.agentPolicy.knownAgentIds.has(normalized)) {
167
+ return normalized;
168
+ }
169
+ return hooksConfig.agentPolicy.defaultAgentId;
170
+ }
171
+ export function isHookAgentAllowed(hooksConfig, agentId) {
172
+ // Keep backwards compatibility for callers that omit agentId.
173
+ const raw = agentId?.trim();
174
+ if (!raw) {
175
+ return true;
176
+ }
177
+ const allowed = hooksConfig.agentPolicy.allowedAgentIds;
178
+ if (allowed === undefined) {
179
+ return true;
180
+ }
181
+ const resolved = resolveHookTargetAgentId(hooksConfig, raw);
182
+ return resolved ? allowed.has(resolved) : false;
183
+ }
184
+ export const getHookAgentPolicyError = () => "agentId is not allowed by hooks.allowedAgentIds";
123
185
  export function normalizeAgentPayload(payload, opts) {
124
186
  const message = typeof payload.message === "string" ? payload.message.trim() : "";
125
187
  if (!message)
126
188
  return { ok: false, error: "message required" };
127
189
  const nameRaw = payload.name;
128
190
  const name = typeof nameRaw === "string" && nameRaw.trim() ? nameRaw.trim() : "Hook";
191
+ const agentIdRaw = payload.agentId;
192
+ const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : undefined;
129
193
  const wakeMode = payload.wakeMode === "next-heartbeat" ? "next-heartbeat" : "now";
130
194
  const sessionKeyRaw = payload.sessionKey;
131
195
  const idFactory = opts?.idFactory ?? randomUUID;
@@ -154,6 +218,7 @@ export function normalizeAgentPayload(payload, opts) {
154
218
  value: {
155
219
  message,
156
220
  name,
221
+ agentId,
157
222
  wakeMode,
158
223
  sessionKey,
159
224
  deliver,
@@ -1,69 +1,4 @@
1
- import { deflateSync } from "node:zlib";
2
- const CRC_TABLE = (() => {
3
- const table = new Uint32Array(256);
4
- for (let i = 0; i < 256; i += 1) {
5
- let c = i;
6
- for (let k = 0; k < 8; k += 1) {
7
- c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
8
- }
9
- table[i] = c >>> 0;
10
- }
11
- return table;
12
- })();
13
- function crc32(buf) {
14
- let crc = 0xffffffff;
15
- for (let i = 0; i < buf.length; i += 1) {
16
- crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
17
- }
18
- return (crc ^ 0xffffffff) >>> 0;
19
- }
20
- function pngChunk(type, data) {
21
- const typeBuf = Buffer.from(type, "ascii");
22
- const len = Buffer.alloc(4);
23
- len.writeUInt32BE(data.length, 0);
24
- const crc = crc32(Buffer.concat([typeBuf, data]));
25
- const crcBuf = Buffer.alloc(4);
26
- crcBuf.writeUInt32BE(crc, 0);
27
- return Buffer.concat([len, typeBuf, data, crcBuf]);
28
- }
29
- function encodePngRgba(buffer, width, height) {
30
- const stride = width * 4;
31
- const raw = Buffer.alloc((stride + 1) * height);
32
- for (let row = 0; row < height; row += 1) {
33
- const rawOffset = row * (stride + 1);
34
- raw[rawOffset] = 0; // filter: none
35
- buffer.copy(raw, rawOffset + 1, row * stride, row * stride + stride);
36
- }
37
- const compressed = deflateSync(raw);
38
- const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
39
- const ihdr = Buffer.alloc(13);
40
- ihdr.writeUInt32BE(width, 0);
41
- ihdr.writeUInt32BE(height, 4);
42
- ihdr[8] = 8; // bit depth
43
- ihdr[9] = 6; // color type RGBA
44
- ihdr[10] = 0; // compression
45
- ihdr[11] = 0; // filter
46
- ihdr[12] = 0; // interlace
47
- return Buffer.concat([
48
- signature,
49
- pngChunk("IHDR", ihdr),
50
- pngChunk("IDAT", compressed),
51
- pngChunk("IEND", Buffer.alloc(0)),
52
- ]);
53
- }
54
- function fillPixel(buf, x, y, width, r, g, b, a = 255) {
55
- if (x < 0 || y < 0)
56
- return;
57
- if (x >= width)
58
- return;
59
- const idx = (y * width + x) * 4;
60
- if (idx < 0 || idx + 3 >= buf.length)
61
- return;
62
- buf[idx] = r;
63
- buf[idx + 1] = g;
64
- buf[idx + 2] = b;
65
- buf[idx + 3] = a;
66
- }
1
+ import { encodePngRgba, fillPixel } from "../media/png-encode.js";
67
2
  const GLYPH_ROWS_5X7 = {
68
3
  "0": [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
69
4
  "1": [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
@@ -1,83 +1,125 @@
1
1
  import net from "node:net";
2
+ import os from "node:os";
2
3
  import { pickPrimaryTailnetIPv4, pickPrimaryTailnetIPv6 } from "../infra/tailnet.js";
4
+ /**
5
+ * Pick the primary non-internal IPv4 address (LAN IP).
6
+ * Prefers common interface names (en0, eth0) then falls back to any external IPv4.
7
+ */
8
+ export function pickPrimaryLanIPv4() {
9
+ const nets = os.networkInterfaces();
10
+ const preferredNames = ["en0", "eth0"];
11
+ for (const name of preferredNames) {
12
+ const list = nets[name];
13
+ const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
14
+ if (entry?.address) {
15
+ return entry.address;
16
+ }
17
+ }
18
+ for (const list of Object.values(nets)) {
19
+ const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
20
+ if (entry?.address) {
21
+ return entry.address;
22
+ }
23
+ }
24
+ return undefined;
25
+ }
3
26
  export function isLoopbackAddress(ip) {
4
- if (!ip)
27
+ if (!ip) {
5
28
  return false;
6
- if (ip === "127.0.0.1")
29
+ }
30
+ if (ip === "127.0.0.1") {
7
31
  return true;
8
- if (ip.startsWith("127."))
32
+ }
33
+ if (ip.startsWith("127.")) {
9
34
  return true;
10
- if (ip === "::1")
35
+ }
36
+ if (ip === "::1") {
11
37
  return true;
12
- if (ip.startsWith("::ffff:127."))
38
+ }
39
+ if (ip.startsWith("::ffff:127.")) {
13
40
  return true;
41
+ }
14
42
  return false;
15
43
  }
16
44
  function normalizeIPv4MappedAddress(ip) {
17
- if (ip.startsWith("::ffff:"))
45
+ if (ip.startsWith("::ffff:")) {
18
46
  return ip.slice("::ffff:".length);
47
+ }
19
48
  return ip;
20
49
  }
21
50
  function normalizeIp(ip) {
22
51
  const trimmed = ip?.trim();
23
- if (!trimmed)
52
+ if (!trimmed) {
24
53
  return undefined;
54
+ }
25
55
  return normalizeIPv4MappedAddress(trimmed.toLowerCase());
26
56
  }
27
57
  function stripOptionalPort(ip) {
28
58
  if (ip.startsWith("[")) {
29
59
  const end = ip.indexOf("]");
30
- if (end !== -1)
60
+ if (end !== -1) {
31
61
  return ip.slice(1, end);
62
+ }
32
63
  }
33
- if (net.isIP(ip))
64
+ if (net.isIP(ip)) {
34
65
  return ip;
66
+ }
35
67
  const lastColon = ip.lastIndexOf(":");
36
68
  if (lastColon > -1 && ip.includes(".") && ip.indexOf(":") === lastColon) {
37
69
  const candidate = ip.slice(0, lastColon);
38
- if (net.isIP(candidate) === 4)
70
+ if (net.isIP(candidate) === 4) {
39
71
  return candidate;
72
+ }
40
73
  }
41
74
  return ip;
42
75
  }
43
76
  export function parseForwardedForClientIp(forwardedFor) {
44
77
  const raw = forwardedFor?.split(",")[0]?.trim();
45
- if (!raw)
78
+ if (!raw) {
46
79
  return undefined;
80
+ }
47
81
  return normalizeIp(stripOptionalPort(raw));
48
82
  }
49
83
  function parseRealIp(realIp) {
50
84
  const raw = realIp?.trim();
51
- if (!raw)
85
+ if (!raw) {
52
86
  return undefined;
87
+ }
53
88
  return normalizeIp(stripOptionalPort(raw));
54
89
  }
55
90
  export function isTrustedProxyAddress(ip, trustedProxies) {
56
91
  const normalized = normalizeIp(ip);
57
- if (!normalized || !trustedProxies || trustedProxies.length === 0)
92
+ if (!normalized || !trustedProxies || trustedProxies.length === 0) {
58
93
  return false;
94
+ }
59
95
  return trustedProxies.some((proxy) => normalizeIp(proxy) === normalized);
60
96
  }
61
97
  export function resolveGatewayClientIp(params) {
62
98
  const remote = normalizeIp(params.remoteAddr);
63
- if (!remote)
99
+ if (!remote) {
64
100
  return undefined;
65
- if (!isTrustedProxyAddress(remote, params.trustedProxies))
101
+ }
102
+ if (!isTrustedProxyAddress(remote, params.trustedProxies)) {
66
103
  return remote;
104
+ }
67
105
  return parseForwardedForClientIp(params.forwardedFor) ?? parseRealIp(params.realIp) ?? remote;
68
106
  }
69
107
  export function isLocalGatewayAddress(ip) {
70
- if (isLoopbackAddress(ip))
108
+ if (isLoopbackAddress(ip)) {
71
109
  return true;
72
- if (!ip)
110
+ }
111
+ if (!ip) {
73
112
  return false;
113
+ }
74
114
  const normalized = normalizeIPv4MappedAddress(ip.trim().toLowerCase());
75
115
  const tailnetIPv4 = pickPrimaryTailnetIPv4();
76
- if (tailnetIPv4 && normalized === tailnetIPv4.toLowerCase())
116
+ if (tailnetIPv4 && normalized === tailnetIPv4.toLowerCase()) {
77
117
  return true;
118
+ }
78
119
  const tailnetIPv6 = pickPrimaryTailnetIPv6();
79
- if (tailnetIPv6 && ip.trim().toLowerCase() === tailnetIPv6.toLowerCase())
120
+ if (tailnetIPv6 && ip.trim().toLowerCase() === tailnetIPv6.toLowerCase()) {
80
121
  return true;
122
+ }
81
123
  return false;
82
124
  }
83
125
  /**
@@ -96,16 +138,19 @@ export async function resolveGatewayBindHost(bind, customHost) {
96
138
  const mode = bind ?? "loopback";
97
139
  if (mode === "loopback") {
98
140
  // 127.0.0.1 rarely fails, but handle gracefully
99
- if (await canBindToHost("127.0.0.1"))
141
+ if (await canBindToHost("127.0.0.1")) {
100
142
  return "127.0.0.1";
143
+ }
101
144
  return "0.0.0.0"; // extreme fallback
102
145
  }
103
146
  if (mode === "tailnet") {
104
147
  const tailnetIP = pickPrimaryTailnetIPv4();
105
- if (tailnetIP && (await canBindToHost(tailnetIP)))
148
+ if (tailnetIP && (await canBindToHost(tailnetIP))) {
106
149
  return tailnetIP;
107
- if (await canBindToHost("127.0.0.1"))
150
+ }
151
+ if (await canBindToHost("127.0.0.1")) {
108
152
  return "127.0.0.1";
153
+ }
109
154
  return "0.0.0.0";
110
155
  }
111
156
  if (mode === "lan") {
@@ -113,16 +158,19 @@ export async function resolveGatewayBindHost(bind, customHost) {
113
158
  }
114
159
  if (mode === "custom") {
115
160
  const host = customHost?.trim();
116
- if (!host)
117
- return "0.0.0.0"; // invalid config → fall back to all
118
- if (isValidIPv4(host) && (await canBindToHost(host)))
161
+ if (!host) {
162
+ return "0.0.0.0";
163
+ } // invalid config → fall back to all
164
+ if (isValidIPv4(host) && (await canBindToHost(host))) {
119
165
  return host;
166
+ }
120
167
  // Custom IP failed → fall back to LAN
121
168
  return "0.0.0.0";
122
169
  }
123
170
  if (mode === "auto") {
124
- if (await canBindToHost("127.0.0.1"))
171
+ if (await canBindToHost("127.0.0.1")) {
125
172
  return "127.0.0.1";
173
+ }
126
174
  return "0.0.0.0";
127
175
  }
128
176
  return "0.0.0.0";
@@ -149,11 +197,13 @@ export async function canBindToHost(host) {
149
197
  });
150
198
  }
151
199
  export async function resolveGatewayListenHosts(bindHost, opts) {
152
- if (bindHost !== "127.0.0.1")
200
+ if (bindHost !== "127.0.0.1") {
153
201
  return [bindHost];
202
+ }
154
203
  const canBind = opts?.canBindToHost ?? canBindToHost;
155
- if (await canBind("::1"))
204
+ if (await canBind("::1")) {
156
205
  return [bindHost, "::1"];
206
+ }
157
207
  return [bindHost];
158
208
  }
159
209
  /**
@@ -162,15 +212,30 @@ export async function resolveGatewayListenHosts(bindHost, opts) {
162
212
  * @param host - The string to validate
163
213
  * @returns True if valid IPv4 format
164
214
  */
165
- function isValidIPv4(host) {
215
+ export function isValidIPv4(host) {
166
216
  const parts = host.split(".");
167
- if (parts.length !== 4)
217
+ if (parts.length !== 4) {
168
218
  return false;
219
+ }
169
220
  return parts.every((part) => {
170
221
  const n = parseInt(part, 10);
171
222
  return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n);
172
223
  });
173
224
  }
225
+ /**
226
+ * Check if a hostname or IP refers to the local machine.
227
+ * Handles: localhost, 127.x.x.x, ::1, [::1], ::ffff:127.x.x.x
228
+ * Note: 0.0.0.0 and :: are NOT loopback - they bind to all interfaces.
229
+ */
174
230
  export function isLoopbackHost(host) {
175
- return isLoopbackAddress(host);
231
+ if (!host) {
232
+ return false;
233
+ }
234
+ const h = host.trim().toLowerCase();
235
+ if (h === "localhost") {
236
+ return true;
237
+ }
238
+ // Handle bracketed IPv6 addresses like [::1]
239
+ const unbracket = h.startsWith("[") && h.endsWith("]") ? h.slice(1, -1) : h;
240
+ return isLoopbackAddress(unbracket);
176
241
  }