@poolzin/pool-bot 2026.1.39 → 2026.2.0

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 (312) 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/cli-runner/helpers.js +1 -1
  11. package/dist/agents/cli-runner.js +2 -2
  12. package/dist/agents/cloudflare-ai-gateway.js +31 -0
  13. package/dist/agents/compaction.js +16 -2
  14. package/dist/agents/context-window-guard.js +13 -10
  15. package/dist/agents/context.js +4 -4
  16. package/dist/agents/docs-path.js +1 -1
  17. package/dist/agents/minimax-vlm.js +1 -1
  18. package/dist/agents/model-auth.js +12 -1
  19. package/dist/agents/model-catalog.js +4 -4
  20. package/dist/agents/model-selection.js +10 -4
  21. package/dist/agents/models-config.js +3 -3
  22. package/dist/agents/models-config.providers.js +147 -39
  23. package/dist/agents/pi-embedded-helpers/openai.js +1 -1
  24. package/dist/agents/pi-embedded-runner/compact.js +8 -8
  25. package/dist/agents/pi-embedded-runner/model.js +2 -2
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +6 -6
  27. package/dist/agents/pi-embedded-runner/run.js +4 -4
  28. package/dist/agents/pi-embedded-runner/tool-result-truncation.js +275 -0
  29. package/dist/agents/pi-embedded-runner/utils.js +1 -1
  30. package/dist/agents/pi-model-discovery.js +10 -0
  31. package/dist/agents/pi-tool-definition-adapter.js +50 -9
  32. package/dist/agents/pi-tools.before-tool-call.js +67 -0
  33. package/dist/agents/pi-tools.js +10 -5
  34. package/dist/agents/pi-tools.read.js +2 -2
  35. package/dist/agents/session-file-repair.js +83 -0
  36. package/dist/agents/session-transcript-repair.js +68 -0
  37. package/dist/agents/skills/frontmatter.js +1 -1
  38. package/dist/agents/skills/workspace.js +2 -2
  39. package/dist/agents/system-prompt.js +28 -4
  40. package/dist/agents/together-models.js +127 -0
  41. package/dist/agents/tool-images.js +1 -1
  42. package/dist/agents/tool-policy.js +1 -1
  43. package/dist/agents/tools/browser-tool.js +3 -3
  44. package/dist/agents/tools/image-tool.js +2 -2
  45. package/dist/agents/tools/memory-tool.js +93 -5
  46. package/dist/agents/tools/web-search.js +1 -1
  47. package/dist/auto-reply/commands-registry.data.js +1 -1
  48. package/dist/auto-reply/reply/commands-context-report.js +2 -2
  49. package/dist/auto-reply/reply/commands-session.js +2 -2
  50. package/dist/auto-reply/reply/get-reply-run.js +14 -4
  51. package/dist/auto-reply/reply/groups.js +1 -1
  52. package/dist/auto-reply/reply/inbound-context.js +4 -0
  53. package/dist/auto-reply/reply/inbound-meta.js +130 -0
  54. package/dist/auto-reply/reply/untrusted-context.js +15 -0
  55. package/dist/auto-reply/status.js +1 -1
  56. package/dist/browser/client-fetch.js +1 -1
  57. package/dist/browser/config.js +1 -1
  58. package/dist/browser/extension-relay.js +3 -3
  59. package/dist/browser/server-context.js +2 -2
  60. package/dist/build-info.json +3 -3
  61. package/dist/canvas-host/a2ui.js +3 -3
  62. package/dist/channels/plugins/catalog.js +2 -2
  63. package/dist/channels/plugins/onboarding/imessage.js +1 -1
  64. package/dist/channels/plugins/onboarding/signal.js +1 -1
  65. package/dist/channels/plugins/onboarding/slack.js +4 -4
  66. package/dist/channels/plugins/onboarding/whatsapp.js +3 -3
  67. package/dist/channels/plugins/pairing-message.js +1 -1
  68. package/dist/cli/browser-cli-extension.js +2 -2
  69. package/dist/cli/docs-cli.js +1 -1
  70. package/dist/cli/gateway-cli/dev.js +1 -1
  71. package/dist/cli/memory-cli.js +25 -15
  72. package/dist/cli/nodes-cli/register.canvas.js +1 -1
  73. package/dist/cli/plugins-cli.js +1 -1
  74. package/dist/cli/run-main.js +2 -2
  75. package/dist/cli/security-cli.js +1 -1
  76. package/dist/cli/tagline.js +1 -1
  77. package/dist/cli/update-cli.js +4 -4
  78. package/dist/cli/webhooks-cli.js +5 -5
  79. package/dist/commands/agents.commands.add.js +1 -1
  80. package/dist/commands/auth-choice.apply.api-providers.js +305 -17
  81. package/dist/commands/auth-choice.apply.js +4 -1
  82. package/dist/commands/auth-choice.apply.plugin-provider.js +2 -2
  83. package/dist/commands/auth-choice.apply.xai.js +63 -0
  84. package/dist/commands/auth-choice.preferred-provider.js +7 -1
  85. package/dist/commands/configure.wizard.js +1 -1
  86. package/dist/commands/dashboard.js +1 -1
  87. package/dist/commands/docs.js +1 -1
  88. package/dist/commands/doctor-gateway-services.js +3 -3
  89. package/dist/commands/doctor-update.js +3 -3
  90. package/dist/commands/doctor.js +1 -1
  91. package/dist/commands/models/list.probe.js +2 -2
  92. package/dist/commands/models/list.registry.js +4 -4
  93. package/dist/commands/models/list.status-command.js +2 -2
  94. package/dist/commands/onboard-auth.config-core.js +366 -28
  95. package/dist/commands/onboard-auth.credentials.js +71 -9
  96. package/dist/commands/onboard-auth.js +3 -3
  97. package/dist/commands/onboard-auth.models.js +26 -24
  98. package/dist/commands/onboard-non-interactive/local/auth-choice.js +140 -6
  99. package/dist/commands/status-all/report-lines.js +1 -1
  100. package/dist/commands/status.command.js +1 -1
  101. package/dist/commands/uninstall.js +3 -3
  102. package/dist/compat/legacy-names.js +1 -1
  103. package/dist/config/io.js +3 -3
  104. package/dist/config/schema.js +1 -1
  105. package/dist/config/types.memory.js +1 -0
  106. package/dist/config/version.js +4 -4
  107. package/dist/daemon/constants.js +7 -7
  108. package/dist/daemon/inspect.js +6 -6
  109. package/dist/daemon/systemd-unit.js +1 -1
  110. package/dist/gateway/live-image-probe.js +1 -66
  111. package/dist/gateway/openai-http.js +2 -2
  112. package/dist/gateway/openresponses-http.js +4 -4
  113. package/dist/gateway/server-discovery.js +2 -2
  114. package/dist/gateway/server-http.js +1 -1
  115. package/dist/gateway/server.impl.js +2 -2
  116. package/dist/hooks/frontmatter.js +1 -1
  117. package/dist/hooks/hooks-status.js +1 -1
  118. package/dist/hooks/install.js +2 -2
  119. package/dist/hooks/loader.js +1 -1
  120. package/dist/hooks/workspace.js +3 -3
  121. package/dist/index.js +2 -2
  122. package/dist/infra/bonjour.js +3 -3
  123. package/dist/infra/path-env.js +3 -3
  124. package/dist/infra/provider-usage.fetch.minimax.js +1 -1
  125. package/dist/infra/restart.js +1 -1
  126. package/dist/infra/tailscale.js +1 -1
  127. package/dist/macos/relay.js +2 -2
  128. package/dist/media/input-files.js +1 -1
  129. package/dist/media/mime.js +4 -0
  130. package/dist/media/png-encode.js +74 -0
  131. package/dist/media-understanding/providers/image.js +2 -2
  132. package/dist/memory/backend-config.js +207 -0
  133. package/dist/memory/embeddings.js +1 -1
  134. package/dist/memory/manager.js +1 -0
  135. package/dist/memory/types.js +1 -0
  136. package/dist/node-host/runner.js +2 -2
  137. package/dist/pairing/pairing-messages.js +1 -1
  138. package/dist/plugins/discovery.js +1 -1
  139. package/dist/plugins/install.js +2 -2
  140. package/dist/plugins/update.js +1 -1
  141. package/dist/security/audit.js +2 -2
  142. package/dist/shared/text/reasoning-tags.js +52 -7
  143. package/dist/tailscale/detect.js +146 -0
  144. package/dist/telegram/bot-message-context.js +1 -1
  145. package/dist/test-helpers/workspace.js +11 -0
  146. package/dist/test-utils/channel-plugins.js +82 -0
  147. package/dist/test-utils/ports.js +73 -0
  148. package/dist/utils/shell-argv.js +61 -0
  149. package/dist/utils.js +10 -0
  150. package/dist/web/qr-image.js +1 -61
  151. package/dist/wizard/onboarding.finalize.js +7 -7
  152. package/dist/wizard/onboarding.js +3 -3
  153. package/docs/RELEASE_WORKFOTS_COMPARISON.md +3 -3
  154. package/docs/_config.yml +2 -2
  155. package/docs/_layouts/default.html +9 -9
  156. package/docs/concepts/typebox.md +1 -1
  157. package/docs/docs.json +1 -1
  158. package/docs/northflank.mdx +7 -7
  159. package/docs/railway.mdx +3 -3
  160. package/docs/render.mdx +5 -5
  161. package/docs/start/lore.md +2 -2
  162. package/extensions/bluebubbles/index.ts +2 -2
  163. package/extensions/bluebubbles/package.json +1 -1
  164. package/extensions/bluebubbles/src/accounts.ts +8 -8
  165. package/extensions/bluebubbles/src/actions.test.ts +22 -22
  166. package/extensions/bluebubbles/src/actions.ts +5 -5
  167. package/extensions/bluebubbles/src/attachments.ts +2 -2
  168. package/extensions/bluebubbles/src/channel.ts +16 -16
  169. package/extensions/bluebubbles/src/chat.ts +2 -2
  170. package/extensions/bluebubbles/src/media-send.ts +2 -2
  171. package/extensions/bluebubbles/src/monitor.test.ts +46 -46
  172. package/extensions/bluebubbles/src/monitor.ts +5 -5
  173. package/extensions/bluebubbles/src/onboarding.ts +7 -7
  174. package/extensions/bluebubbles/src/reactions.ts +2 -2
  175. package/extensions/bluebubbles/src/send.ts +2 -2
  176. package/extensions/copilot-proxy/README.md +1 -1
  177. package/extensions/copilot-proxy/package.json +1 -1
  178. package/extensions/diagnostics-otel/index.ts +2 -2
  179. package/extensions/diagnostics-otel/package.json +1 -1
  180. package/extensions/diagnostics-otel/src/service.ts +3 -3
  181. package/extensions/discord/index.ts +2 -2
  182. package/extensions/discord/package.json +1 -1
  183. package/extensions/google-antigravity-auth/README.md +1 -1
  184. package/extensions/google-antigravity-auth/index.ts +1 -1
  185. package/extensions/google-antigravity-auth/package.json +1 -1
  186. package/extensions/google-gemini-cli-auth/README.md +1 -1
  187. package/extensions/google-gemini-cli-auth/oauth.ts +1 -1
  188. package/extensions/google-gemini-cli-auth/package.json +1 -1
  189. package/extensions/googlechat/index.ts +3 -3
  190. package/extensions/googlechat/package.json +1 -1
  191. package/extensions/googlechat/src/accounts.ts +8 -8
  192. package/extensions/googlechat/src/actions.ts +6 -6
  193. package/extensions/googlechat/src/channel.ts +21 -21
  194. package/extensions/googlechat/src/monitor.ts +8 -8
  195. package/extensions/googlechat/src/onboarding.ts +10 -10
  196. package/extensions/imessage/index.ts +2 -2
  197. package/extensions/imessage/package.json +1 -1
  198. package/extensions/line/index.ts +2 -2
  199. package/extensions/line/package.json +1 -1
  200. package/extensions/line/src/card-command.ts +2 -2
  201. package/extensions/line/src/channel.logout.test.ts +4 -4
  202. package/extensions/line/src/channel.sendPayload.test.ts +8 -8
  203. package/extensions/line/src/channel.ts +3 -3
  204. package/extensions/llm-task/README.md +3 -3
  205. package/extensions/llm-task/index.ts +2 -2
  206. package/extensions/llm-task/package.json +1 -1
  207. package/extensions/llm-task/src/llm-task-tool.ts +4 -4
  208. package/extensions/lobster/README.md +6 -6
  209. package/extensions/lobster/index.ts +2 -2
  210. package/extensions/lobster/src/lobster-tool.test.ts +4 -4
  211. package/extensions/lobster/src/lobster-tool.ts +2 -2
  212. package/extensions/matrix/index.ts +2 -2
  213. package/extensions/matrix/package.json +1 -1
  214. package/extensions/matrix/src/matrix/client/config.ts +1 -1
  215. package/extensions/matrix/src/matrix/monitor/handler.ts +1 -1
  216. package/extensions/matrix/src/onboarding.ts +1 -1
  217. package/extensions/mattermost/index.ts +2 -2
  218. package/extensions/mattermost/package.json +1 -1
  219. package/extensions/mattermost/src/mattermost/accounts.ts +8 -8
  220. package/extensions/mattermost/src/mattermost/monitor-helpers.ts +5 -5
  221. package/extensions/mattermost/src/mattermost/monitor.ts +2 -2
  222. package/extensions/mattermost/src/onboarding-helpers.ts +3 -3
  223. package/extensions/mattermost/src/onboarding.ts +2 -2
  224. package/extensions/memory-core/index.ts +2 -2
  225. package/extensions/memory-core/package.json +1 -1
  226. package/extensions/memory-lancedb/index.ts +3 -3
  227. package/extensions/memory-lancedb/package.json +1 -1
  228. package/extensions/msteams/index.ts +2 -2
  229. package/extensions/msteams/package.json +1 -1
  230. package/extensions/msteams/src/channel.directory.test.ts +2 -2
  231. package/extensions/msteams/src/channel.ts +2 -2
  232. package/extensions/msteams/src/graph-upload.ts +4 -4
  233. package/extensions/msteams/src/monitor-handler.ts +2 -2
  234. package/extensions/msteams/src/monitor.ts +2 -2
  235. package/extensions/msteams/src/onboarding.ts +9 -9
  236. package/extensions/msteams/src/reply-dispatcher.ts +2 -2
  237. package/extensions/msteams/src/send-context.ts +2 -2
  238. package/extensions/msteams/src/send.ts +4 -4
  239. package/extensions/nextcloud-talk/index.ts +2 -2
  240. package/extensions/nextcloud-talk/package.json +1 -1
  241. package/extensions/nextcloud-talk/src/channel.ts +7 -7
  242. package/extensions/nextcloud-talk/src/inbound.ts +7 -7
  243. package/extensions/nextcloud-talk/src/onboarding.ts +1 -1
  244. package/extensions/nostr/README.md +2 -2
  245. package/extensions/nostr/index.ts +5 -5
  246. package/extensions/nostr/package.json +1 -1
  247. package/extensions/nostr/src/types.ts +4 -4
  248. package/extensions/open-prose/index.ts +2 -2
  249. package/extensions/qwen-portal-auth/README.md +1 -1
  250. package/extensions/signal/index.ts +2 -2
  251. package/extensions/signal/package.json +1 -1
  252. package/extensions/slack/index.ts +2 -2
  253. package/extensions/slack/package.json +1 -1
  254. package/extensions/telegram/index.ts +2 -2
  255. package/extensions/telegram/package.json +1 -1
  256. package/extensions/telegram/src/channel.ts +2 -2
  257. package/extensions/tlon/README.md +2 -2
  258. package/extensions/tlon/index.ts +2 -2
  259. package/extensions/tlon/package.json +1 -1
  260. package/extensions/tlon/src/channel.ts +13 -13
  261. package/extensions/tlon/src/monitor/index.ts +3 -3
  262. package/extensions/tlon/src/onboarding.ts +3 -3
  263. package/extensions/tlon/src/types.ts +3 -3
  264. package/extensions/twitch/README.md +1 -1
  265. package/extensions/twitch/index.ts +2 -2
  266. package/extensions/twitch/package.json +1 -1
  267. package/extensions/twitch/src/config.ts +3 -3
  268. package/extensions/twitch/src/monitor.ts +3 -3
  269. package/extensions/twitch/src/onboarding.ts +9 -9
  270. package/extensions/twitch/src/outbound.test.ts +2 -2
  271. package/extensions/twitch/src/plugin.test.ts +2 -2
  272. package/extensions/twitch/src/plugin.ts +8 -8
  273. package/extensions/twitch/src/send.test.ts +2 -2
  274. package/extensions/twitch/src/send.ts +4 -4
  275. package/extensions/twitch/src/token.test.ts +8 -8
  276. package/extensions/twitch/src/token.ts +3 -3
  277. package/extensions/twitch/src/twitch-client.ts +3 -3
  278. package/extensions/twitch/src/types.ts +3 -3
  279. package/extensions/twitch/src/utils/markdown.ts +1 -1
  280. package/extensions/voice-call/README.md +3 -3
  281. package/extensions/voice-call/package.json +1 -1
  282. package/extensions/voice-call/src/core-bridge.ts +2 -2
  283. package/extensions/voice-call/src/response-generator.ts +1 -1
  284. package/extensions/whatsapp/index.ts +2 -2
  285. package/extensions/whatsapp/package.json +1 -1
  286. package/extensions/zalo/README.md +1 -1
  287. package/extensions/zalo/index.ts +2 -2
  288. package/extensions/zalo/package.json +1 -1
  289. package/extensions/zalo/src/accounts.ts +8 -8
  290. package/extensions/zalo/src/actions.ts +4 -4
  291. package/extensions/zalo/src/channel.directory.test.ts +2 -2
  292. package/extensions/zalo/src/channel.ts +18 -18
  293. package/extensions/zalo/src/monitor.ts +9 -9
  294. package/extensions/zalo/src/monitor.webhook.test.ts +2 -2
  295. package/extensions/zalo/src/onboarding.ts +24 -24
  296. package/extensions/zalo/src/send.ts +2 -2
  297. package/extensions/zalouser/README.md +2 -2
  298. package/extensions/zalouser/index.ts +2 -2
  299. package/extensions/zalouser/package.json +1 -1
  300. package/extensions/zalouser/src/accounts.ts +9 -9
  301. package/extensions/zalouser/src/channel.ts +24 -24
  302. package/extensions/zalouser/src/monitor.ts +4 -4
  303. package/extensions/zalouser/src/onboarding.ts +28 -28
  304. package/package.json +13 -251
  305. package/skills/nano-banana-pro/scripts/generate_image.py +1 -1
  306. package/skills/tmux/scripts/find-sessions.sh +1 -1
  307. package/CHANGELOG.md +0 -102
  308. package/README-header.png +0 -0
  309. package/git-hooks/pre-commit +0 -4
  310. package/scripts/format-staged.js +0 -148
  311. package/scripts/postinstall.js +0 -300
  312. package/scripts/setup-git-hooks.js +0 -96
@@ -1,28 +1,70 @@
1
1
  const QUICK_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought|antthinking|final)\b/i;
2
- const FINAL_TAG_RE = /<\s*\/?\s*final\b[^>]*>/gi;
3
- const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^>]*>/gi;
2
+ const FINAL_TAG_RE = /<\s*\/?\s*final\b[^<>]*>/gi;
3
+ const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/gi;
4
+ function findCodeRegions(text) {
5
+ const regions = [];
6
+ const fencedRe = /(^|\n)(```|~~~)[^\n]*\n[\s\S]*?(?:\n\2(?:\n|$)|$)/g;
7
+ for (const match of text.matchAll(fencedRe)) {
8
+ const start = (match.index ?? 0) + match[1].length;
9
+ regions.push({ start, end: start + match[0].length - match[1].length });
10
+ }
11
+ const inlineRe = /`+[^`]+`+/g;
12
+ for (const match of text.matchAll(inlineRe)) {
13
+ const start = match.index ?? 0;
14
+ const end = start + match[0].length;
15
+ const insideFenced = regions.some((r) => start >= r.start && end <= r.end);
16
+ if (!insideFenced) {
17
+ regions.push({ start, end });
18
+ }
19
+ }
20
+ regions.sort((a, b) => a.start - b.start);
21
+ return regions;
22
+ }
23
+ function isInsideCode(pos, regions) {
24
+ return regions.some((r) => pos >= r.start && pos < r.end);
25
+ }
4
26
  function applyTrim(value, mode) {
5
- if (mode === "none")
27
+ if (mode === "none") {
6
28
  return value;
7
- if (mode === "start")
29
+ }
30
+ if (mode === "start") {
8
31
  return value.trimStart();
32
+ }
9
33
  return value.trim();
10
34
  }
11
35
  export function stripReasoningTagsFromText(text, options) {
12
- if (!text)
36
+ if (!text) {
13
37
  return text;
14
- if (!QUICK_TAG_RE.test(text))
38
+ }
39
+ if (!QUICK_TAG_RE.test(text)) {
15
40
  return text;
41
+ }
16
42
  const mode = options?.mode ?? "strict";
17
43
  const trimMode = options?.trim ?? "both";
18
44
  let cleaned = text;
19
45
  if (FINAL_TAG_RE.test(cleaned)) {
20
46
  FINAL_TAG_RE.lastIndex = 0;
21
- cleaned = cleaned.replace(FINAL_TAG_RE, "");
47
+ const finalMatches = [];
48
+ const preCodeRegions = findCodeRegions(cleaned);
49
+ for (const match of cleaned.matchAll(FINAL_TAG_RE)) {
50
+ const start = match.index ?? 0;
51
+ finalMatches.push({
52
+ start,
53
+ length: match[0].length,
54
+ inCode: isInsideCode(start, preCodeRegions),
55
+ });
56
+ }
57
+ for (let i = finalMatches.length - 1; i >= 0; i--) {
58
+ const m = finalMatches[i];
59
+ if (!m.inCode) {
60
+ cleaned = cleaned.slice(0, m.start) + cleaned.slice(m.start + m.length);
61
+ }
62
+ }
22
63
  }
23
64
  else {
24
65
  FINAL_TAG_RE.lastIndex = 0;
25
66
  }
67
+ const codeRegions = findCodeRegions(cleaned);
26
68
  THINKING_TAG_RE.lastIndex = 0;
27
69
  let result = "";
28
70
  let lastIndex = 0;
@@ -30,6 +72,9 @@ export function stripReasoningTagsFromText(text, options) {
30
72
  for (const match of cleaned.matchAll(THINKING_TAG_RE)) {
31
73
  const idx = match.index ?? 0;
32
74
  const isClose = match[1] === "/";
75
+ if (isInsideCode(idx, codeRegions)) {
76
+ continue;
77
+ }
33
78
  if (!inThinking) {
34
79
  result += cleaned.slice(lastIndex, idx);
35
80
  if (!isClose) {
@@ -0,0 +1,146 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs/promises";
3
+ import os from "node:os";
4
+ /**
5
+ * Check if Tailscale is installed by looking for the binary.
6
+ */
7
+ async function isTailscaleInstalled() {
8
+ try {
9
+ const platform = os.platform();
10
+ const possiblePaths = [];
11
+ if (platform === "linux") {
12
+ possiblePaths.push("/usr/bin/tailscale", "/usr/local/bin/tailscale");
13
+ }
14
+ else if (platform === "darwin") {
15
+ possiblePaths.push("/Applications/Tailscale.app/Contents/MacOS/Tailscale", "/usr/local/bin/tailscale");
16
+ }
17
+ else if (platform === "win32") {
18
+ possiblePaths.push("C:\\Program Files\\Tailscale\\tailscale.exe", "C:\\ProgramData\\chocolatey\\lib\\tailscale\\tools\\tailscale.exe");
19
+ }
20
+ for (const binPath of possiblePaths) {
21
+ try {
22
+ await fs.access(binPath);
23
+ return true;
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ }
29
+ return false;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Run tailscale status command and parse output.
37
+ */
38
+ async function getTailscaleStatus() {
39
+ try {
40
+ const platform = os.platform();
41
+ const binPath = platform === "win32"
42
+ ? "tailscale.exe"
43
+ : platform === "darwin"
44
+ ? "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
45
+ : "tailscale";
46
+ const timeout = 5000;
47
+ const result = execSync(`"${binPath}" status --json`, {
48
+ encoding: "utf-8",
49
+ stdio: ["ignore", "pipe", "pipe"],
50
+ timeout,
51
+ });
52
+ const status = JSON.parse(result.trim());
53
+ // Extract useful info from Tailscale status JSON
54
+ const self = status.Self || {};
55
+ const backendState = status.BackendState || "";
56
+ return {
57
+ installed: true,
58
+ running: backendState === "Running",
59
+ ip: self.TailscaleIPs?.[0],
60
+ dnsName: self.DNSName,
61
+ hostname: self.HostName,
62
+ tailnet: status.MagicsockName || status.TailnetName,
63
+ online: self.Online ?? false,
64
+ canServe: true, // If running, can serve
65
+ canFunnel: self.Capabilities?.map.includes("funnel") ?? false,
66
+ };
67
+ }
68
+ catch (err) {
69
+ // If command fails, return minimal info
70
+ return {
71
+ installed: await isTailscaleInstalled(),
72
+ running: false,
73
+ canServe: false,
74
+ canFunnel: false,
75
+ online: false,
76
+ };
77
+ }
78
+ }
79
+ /**
80
+ * Auto-detect Tailscale configuration and recommend settings.
81
+ *
82
+ * This is a SAFE detection function - it only reads status and never
83
+ * modifies configuration. Recommendations are purely advisory.
84
+ *
85
+ * @returns Tailscale auto-detection result
86
+ */
87
+ export async function detectTailscaleConfig() {
88
+ const info = {
89
+ installed: await isTailscaleInstalled(),
90
+ running: false,
91
+ canServe: false,
92
+ canFunnel: false,
93
+ online: false,
94
+ };
95
+ if (!info.installed) {
96
+ return { detected: false };
97
+ }
98
+ // Get detailed status if installed
99
+ const status = await getTailscaleStatus();
100
+ Object.assign(info, status);
101
+ // Generate recommendations based on detected state
102
+ const recommended = info.online
103
+ ? {
104
+ mode: info.canFunnel ? "funnel" : "serve",
105
+ bind: "tailnet",
106
+ port: 18789,
107
+ reason: info.canFunnel
108
+ ? "Tailscale is online and supports funnel (public access)"
109
+ : "Tailscale is online - use serve for private tailnet access",
110
+ }
111
+ : info.running
112
+ ? {
113
+ mode: "off",
114
+ bind: "loopback",
115
+ port: 18789,
116
+ reason: "Tailscale is installed but offline - use loopback bind",
117
+ }
118
+ : undefined;
119
+ return {
120
+ detected: true,
121
+ info,
122
+ recommended,
123
+ };
124
+ }
125
+ /**
126
+ * Get a human-readable summary of Tailscale detection.
127
+ */
128
+ export function formatTailscaleDetection(result) {
129
+ if (!result.detected || !result.info) {
130
+ return "Tailscale: Not installed";
131
+ }
132
+ const { info, recommended } = result;
133
+ const parts = [];
134
+ parts.push(`Tailscale: ${info.installed ? "Installed" : "Not installed"}`);
135
+ parts.push(`Status: ${info.running ? (info.online ? "Online" : "Offline") : "Not running"}`);
136
+ if (info.online && info.ip) {
137
+ parts.push(`IP: ${info.ip}`);
138
+ }
139
+ if (info.dnsName) {
140
+ parts.push(`DNS: ${info.dnsName}`);
141
+ }
142
+ if (recommended) {
143
+ parts.push(`\nRecommendation: ${recommended.mode} (${recommended.reason})`);
144
+ }
145
+ return parts.join(" | ");
146
+ }
@@ -141,7 +141,7 @@ export const buildTelegramMessageContext = async ({ primaryCtx, allMedia, storeA
141
141
  await withTelegramApiErrorLogging({
142
142
  operation: "sendMessage",
143
143
  fn: () => bot.api.sendMessage(chatId, [
144
- "Moltbot: access not configured.",
144
+ "Poolbot: access not configured.",
145
145
  "",
146
146
  `Your Telegram user id: ${telegramUserId}`,
147
147
  "",
@@ -0,0 +1,11 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export async function makeTempWorkspace(prefix = "poolbot-workspace-") {
5
+ return fs.mkdtemp(path.join(os.tmpdir(), prefix));
6
+ }
7
+ export async function writeWorkspaceFile(params) {
8
+ const filePath = path.join(params.dir, params.name);
9
+ await fs.writeFile(filePath, params.content, "utf-8");
10
+ return filePath;
11
+ }
@@ -0,0 +1,82 @@
1
+ import { imessageOutbound } from "../channels/plugins/outbound/imessage.js";
2
+ import { normalizeIMessageHandle } from "../imessage/targets.js";
3
+ export const createTestRegistry = (channels = []) => ({
4
+ plugins: [],
5
+ tools: [],
6
+ hooks: [],
7
+ typedHooks: [],
8
+ channels,
9
+ providers: [],
10
+ gatewayHandlers: {},
11
+ httpHandlers: [],
12
+ httpRoutes: [],
13
+ cliRegistrars: [],
14
+ services: [],
15
+ commands: [],
16
+ diagnostics: [],
17
+ });
18
+ export const createIMessageTestPlugin = (params) => ({
19
+ id: "imessage",
20
+ meta: {
21
+ id: "imessage",
22
+ label: "iMessage",
23
+ selectionLabel: "iMessage (imsg)",
24
+ docsPath: "/channels/imessage",
25
+ blurb: "iMessage test stub.",
26
+ aliases: ["imsg"],
27
+ },
28
+ capabilities: { chatTypes: ["direct", "group"], media: true },
29
+ config: {
30
+ listAccountIds: () => [],
31
+ resolveAccount: () => ({}),
32
+ },
33
+ status: {
34
+ collectStatusIssues: (accounts) => accounts.flatMap((account) => {
35
+ const lastError = typeof account.lastError === "string" ? account.lastError.trim() : "";
36
+ if (!lastError)
37
+ return [];
38
+ return [
39
+ {
40
+ channel: "imessage",
41
+ accountId: account.accountId,
42
+ kind: "runtime",
43
+ message: `Channel error: ${lastError}`,
44
+ },
45
+ ];
46
+ }),
47
+ },
48
+ outbound: params?.outbound ?? imessageOutbound,
49
+ messaging: {
50
+ targetResolver: {
51
+ looksLikeId: (raw) => {
52
+ const trimmed = raw.trim();
53
+ if (!trimmed)
54
+ return false;
55
+ if (/^(imessage:|sms:|auto:|chat_id:|chat_guid:|chat_identifier:)/i.test(trimmed)) {
56
+ return true;
57
+ }
58
+ if (trimmed.includes("@"))
59
+ return true;
60
+ return /^\+?\d{3,}$/.test(trimmed);
61
+ },
62
+ hint: "<handle|chat_id:ID>",
63
+ },
64
+ normalizeTarget: (raw) => normalizeIMessageHandle(raw),
65
+ },
66
+ });
67
+ export const createOutboundTestPlugin = (params) => ({
68
+ id: params.id,
69
+ meta: {
70
+ id: params.id,
71
+ label: params.label ?? String(params.id),
72
+ selectionLabel: params.label ?? String(params.id),
73
+ docsPath: params.docsPath ?? `/channels/${params.id}`,
74
+ blurb: "test stub.",
75
+ },
76
+ capabilities: params.capabilities ?? { chatTypes: ["direct"] },
77
+ config: {
78
+ listAccountIds: () => [],
79
+ resolveAccount: () => ({}),
80
+ },
81
+ outbound: params.outbound,
82
+ });
@@ -0,0 +1,73 @@
1
+ import { createServer } from "node:net";
2
+ import { isMainThread, threadId } from "node:worker_threads";
3
+ async function isPortFree(port) {
4
+ if (!Number.isFinite(port) || port <= 0 || port > 65535)
5
+ return false;
6
+ return await new Promise((resolve) => {
7
+ const server = createServer();
8
+ server.once("error", () => resolve(false));
9
+ server.listen(port, "127.0.0.1", () => {
10
+ server.close(() => resolve(true));
11
+ });
12
+ });
13
+ }
14
+ async function getOsFreePort() {
15
+ return await new Promise((resolve, reject) => {
16
+ const server = createServer();
17
+ server.once("error", reject);
18
+ server.listen(0, "127.0.0.1", () => {
19
+ const addr = server.address();
20
+ if (!addr || typeof addr === "string") {
21
+ server.close();
22
+ reject(new Error("failed to acquire free port"));
23
+ return;
24
+ }
25
+ const port = addr.port;
26
+ server.close((err) => (err ? reject(err) : resolve(port)));
27
+ });
28
+ });
29
+ }
30
+ let nextTestPortOffset = 0;
31
+ /**
32
+ * Allocate a deterministic per-worker port block.
33
+ *
34
+ * Motivation: many tests spin up gateway + related services that use derived ports
35
+ * (e.g. +1/+2/+3/+4). If each test just grabs an OS free port, parallel test runs
36
+ * can collide on derived ports and get flaky EADDRINUSE.
37
+ */
38
+ export async function getDeterministicFreePortBlock(params) {
39
+ const offsets = params?.offsets ?? [0, 1, 2, 3, 4];
40
+ const maxOffset = Math.max(...offsets);
41
+ const workerIdRaw = process.env.VITEST_WORKER_ID ?? process.env.VITEST_POOL_ID ?? "";
42
+ const workerId = Number.parseInt(workerIdRaw, 10);
43
+ const shard = Number.isFinite(workerId)
44
+ ? Math.max(0, workerId)
45
+ : isMainThread
46
+ ? Math.abs(process.pid)
47
+ : Math.abs(threadId);
48
+ const rangeSize = 1000;
49
+ const shardCount = 30;
50
+ const base = 30_000 + (Math.abs(shard) % shardCount) * rangeSize; // <= 59_999
51
+ const usable = rangeSize - maxOffset;
52
+ // Allocate in blocks to avoid derived-port overlaps (e.g. port+3).
53
+ const blockSize = Math.max(maxOffset + 1, 8);
54
+ for (let attempt = 0; attempt < usable; attempt += 1) {
55
+ const start = base + ((nextTestPortOffset + attempt) % usable);
56
+ // eslint-disable-next-line no-await-in-loop
57
+ const ok = (await Promise.all(offsets.map((offset) => isPortFree(start + offset)))).every(Boolean);
58
+ if (!ok)
59
+ continue;
60
+ nextTestPortOffset = (nextTestPortOffset + attempt + blockSize) % usable;
61
+ return start;
62
+ }
63
+ // Fallback: let the OS pick a port block (best effort).
64
+ for (let attempt = 0; attempt < 25; attempt += 1) {
65
+ // eslint-disable-next-line no-await-in-loop
66
+ const port = await getOsFreePort();
67
+ // eslint-disable-next-line no-await-in-loop
68
+ const ok = (await Promise.all(offsets.map((offset) => isPortFree(port + offset)))).every(Boolean);
69
+ if (ok)
70
+ return port;
71
+ }
72
+ throw new Error("failed to acquire a free port block");
73
+ }
@@ -0,0 +1,61 @@
1
+ export function splitShellArgs(raw) {
2
+ const tokens = [];
3
+ let buf = "";
4
+ let inSingle = false;
5
+ let inDouble = false;
6
+ let escaped = false;
7
+ const pushToken = () => {
8
+ if (buf.length > 0) {
9
+ tokens.push(buf);
10
+ buf = "";
11
+ }
12
+ };
13
+ for (let i = 0; i < raw.length; i += 1) {
14
+ const ch = raw[i];
15
+ if (escaped) {
16
+ buf += ch;
17
+ escaped = false;
18
+ continue;
19
+ }
20
+ if (!inSingle && !inDouble && ch === "\\") {
21
+ escaped = true;
22
+ continue;
23
+ }
24
+ if (inSingle) {
25
+ if (ch === "'") {
26
+ inSingle = false;
27
+ }
28
+ else {
29
+ buf += ch;
30
+ }
31
+ continue;
32
+ }
33
+ if (inDouble) {
34
+ if (ch === '"') {
35
+ inDouble = false;
36
+ }
37
+ else {
38
+ buf += ch;
39
+ }
40
+ continue;
41
+ }
42
+ if (ch === "'") {
43
+ inSingle = true;
44
+ continue;
45
+ }
46
+ if (ch === '"') {
47
+ inDouble = true;
48
+ continue;
49
+ }
50
+ if (/\s/.test(ch)) {
51
+ pushToken();
52
+ continue;
53
+ }
54
+ buf += ch;
55
+ }
56
+ if (escaped || inSingle || inDouble) {
57
+ return null;
58
+ }
59
+ pushToken();
60
+ return tokens;
61
+ }
package/dist/utils.js CHANGED
@@ -243,5 +243,15 @@ export function formatTerminalLink(label, url, opts) {
243
243
  }
244
244
  return `\u001b]8;;${safeUrl}\u0007${safeLabel}\u001b]8;;\u0007`;
245
245
  }
246
+ /**
247
+ * Strict plain-object check — returns true only for `Object.create(null)` or `{}` literals.
248
+ * Arrays, Dates, RegExps, class instances, and null/undefined all return false.
249
+ */
250
+ export function isPlainObject(value) {
251
+ if (typeof value !== "object" || value === null)
252
+ return false;
253
+ const proto = Object.getPrototypeOf(value);
254
+ return proto === Object.prototype || proto === null;
255
+ }
246
256
  // Configuration root; can be overridden via CLAWDBOT_STATE_DIR.
247
257
  export const CONFIG_DIR = resolveConfigDir();
@@ -1,6 +1,6 @@
1
- import { deflateSync } from "node:zlib";
2
1
  import QRCodeModule from "qrcode-terminal/vendor/QRCode/index.js";
3
2
  import QRErrorCorrectLevelModule from "qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js";
3
+ import { encodePngRgba, fillPixel } from "../media/png-encode.js";
4
4
  const QRCode = QRCodeModule;
5
5
  const QRErrorCorrectLevel = QRErrorCorrectLevelModule;
6
6
  function createQrMatrix(input) {
@@ -9,66 +9,6 @@ function createQrMatrix(input) {
9
9
  qr.make();
10
10
  return qr;
11
11
  }
12
- function fillPixel(buf, x, y, width, r, g, b, a = 255) {
13
- const idx = (y * width + x) * 4;
14
- buf[idx] = r;
15
- buf[idx + 1] = g;
16
- buf[idx + 2] = b;
17
- buf[idx + 3] = a;
18
- }
19
- function crcTable() {
20
- const table = new Uint32Array(256);
21
- for (let i = 0; i < 256; i += 1) {
22
- let c = i;
23
- for (let k = 0; k < 8; k += 1) {
24
- c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
25
- }
26
- table[i] = c >>> 0;
27
- }
28
- return table;
29
- }
30
- const CRC_TABLE = crcTable();
31
- function crc32(buf) {
32
- let crc = 0xffffffff;
33
- for (let i = 0; i < buf.length; i += 1) {
34
- crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
35
- }
36
- return (crc ^ 0xffffffff) >>> 0;
37
- }
38
- function pngChunk(type, data) {
39
- const typeBuf = Buffer.from(type, "ascii");
40
- const len = Buffer.alloc(4);
41
- len.writeUInt32BE(data.length, 0);
42
- const crc = crc32(Buffer.concat([typeBuf, data]));
43
- const crcBuf = Buffer.alloc(4);
44
- crcBuf.writeUInt32BE(crc, 0);
45
- return Buffer.concat([len, typeBuf, data, crcBuf]);
46
- }
47
- function encodePngRgba(buffer, width, height) {
48
- const stride = width * 4;
49
- const raw = Buffer.alloc((stride + 1) * height);
50
- for (let row = 0; row < height; row += 1) {
51
- const rawOffset = row * (stride + 1);
52
- raw[rawOffset] = 0; // filter: none
53
- buffer.copy(raw, rawOffset + 1, row * stride, row * stride + stride);
54
- }
55
- const compressed = deflateSync(raw);
56
- const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
57
- const ihdr = Buffer.alloc(13);
58
- ihdr.writeUInt32BE(width, 0);
59
- ihdr.writeUInt32BE(height, 4);
60
- ihdr[8] = 8; // bit depth
61
- ihdr[9] = 6; // color type RGBA
62
- ihdr[10] = 0; // compression
63
- ihdr[11] = 0; // filter
64
- ihdr[12] = 0; // interlace
65
- return Buffer.concat([
66
- signature,
67
- pngChunk("IHDR", ihdr),
68
- pngChunk("IDAT", compressed),
69
- pngChunk("IEND", Buffer.alloc(0)),
70
- ]);
71
- }
72
12
  export async function renderQrPngBase64(input, opts = {}) {
73
13
  const { scale = 6, marginModules = 4 } = opts;
74
14
  const qr = createQrMatrix(input);
@@ -269,8 +269,8 @@ export async function finalizeOnboardingWizard(options) {
269
269
  await prompter.note([
270
270
  `Dashboard link (with token): ${authedUrl}`,
271
271
  controlUiOpened
272
- ? "Opened in your browser. Keep that tab to control Moltbot."
273
- : "Copy/paste this URL in a browser on this machine to control Moltbot.",
272
+ ? "Opened in your browser. Keep that tab to control Poolbot."
273
+ : "Copy/paste this URL in a browser on this machine to control Poolbot.",
274
274
  controlUiOpenHint,
275
275
  ]
276
276
  .filter(Boolean)
@@ -314,8 +314,8 @@ export async function finalizeOnboardingWizard(options) {
314
314
  await prompter.note([
315
315
  `Dashboard link (with token): ${authedUrl}`,
316
316
  controlUiOpened
317
- ? "Opened in your browser. Keep that tab to control Moltbot."
318
- : "Copy/paste this URL in a browser on this machine to control Moltbot.",
317
+ ? "Opened in your browser. Keep that tab to control Poolbot."
318
+ : "Copy/paste this URL in a browser on this machine to control Poolbot.",
319
319
  controlUiOpenHint,
320
320
  ]
321
321
  .filter(Boolean)
@@ -336,7 +336,7 @@ export async function finalizeOnboardingWizard(options) {
336
336
  : [
337
337
  "If you want your agent to be able to search the web, you’ll need an API key.",
338
338
  "",
339
- "Moltbot uses Brave Search for the `web_search` tool. Without a Brave Search API key, web search won’t work.",
339
+ "Poolbot uses Brave Search for the `web_search` tool. Without a Brave Search API key, web search won’t work.",
340
340
  "",
341
341
  "Set it up interactively:",
342
342
  `- Run: ${formatCliCommand("poolbot configure --section web")}`,
@@ -347,8 +347,8 @@ export async function finalizeOnboardingWizard(options) {
347
347
  ].join("\n"), "Web search (optional)");
348
348
  await prompter.note('What now: https://molt.bot/showcase ("What People Are Building").', "What now");
349
349
  await prompter.outro(controlUiOpened
350
- ? "Onboarding complete. Dashboard opened with your token; keep that tab to control Moltbot."
350
+ ? "Onboarding complete. Dashboard opened with your token; keep that tab to control Poolbot."
351
351
  : seededInBackground
352
352
  ? "Onboarding complete. Web UI seeded in the background; open it anytime with the tokenized link above."
353
- : "Onboarding complete. Use the tokenized dashboard link above to control Moltbot.");
353
+ : "Onboarding complete. Use the tokenized dashboard link above to control Poolbot.");
354
354
  }
@@ -22,11 +22,11 @@ async function requireRiskAcknowledgement(params) {
22
22
  await params.prompter.note([
23
23
  "Security warning — please read.",
24
24
  "",
25
- "Moltbot is a hobby project and still in beta. Expect sharp edges.",
25
+ "Poolbot is a hobby project and still in beta. Expect sharp edges.",
26
26
  "This bot can read files and run actions if tools are enabled.",
27
27
  "A bad prompt can trick it into doing unsafe things.",
28
28
  "",
29
- "If you’re not comfortable with basic security and access control, don’t run Moltbot.",
29
+ "If you’re not comfortable with basic security and access control, don’t run Poolbot.",
30
30
  "Ask someone experienced to help before enabling tools or exposing it to the internet.",
31
31
  "",
32
32
  "Recommended baseline:",
@@ -51,7 +51,7 @@ async function requireRiskAcknowledgement(params) {
51
51
  }
52
52
  export async function runOnboardingWizard(opts, runtime = defaultRuntime, prompter) {
53
53
  printWizardHeader(runtime);
54
- await prompter.intro("Moltbot onboarding");
54
+ await prompter.intro("Poolbot onboarding");
55
55
  await requireRiskAcknowledgement({ opts, prompter });
56
56
  const snapshot = await readConfigFileSnapshot();
57
57
  let baseConfig = snapshot.valid ? snapshot.config : {};
@@ -5,7 +5,7 @@
5
5
  Existem **dois workflows de release** diferentes no pool-bot:
6
6
 
7
7
  ### 1. `release.yml` - Binários CLI
8
- - **O que faz:** Gera binários standalone do Moltbot CLI
8
+ - **O que faz:** Gera binários standalone do Poolbot CLI
9
9
  - **Ferramenta:** `pkg`
10
10
  - **Saída:**
11
11
  - Windows: `poolbot-win-x64.exe` (CLI)
@@ -38,13 +38,13 @@ Existem **dois workflows de release** diferentes no pool-bot:
38
38
  ## Quando Usar Cada Workflow
39
39
 
40
40
  ### release.yml (CLI)
41
- - Quando você quer instalar o Moltbot como ferramenta de CLI
41
+ - Quando você quer instalar o Poolbot como ferramenta de CLI
42
42
  - Quando você quer executar `poolbot gateway`, `poolbot tui`, etc.
43
43
  - Quando você quer integrar em scripts de automação
44
44
  - Para desenvolvedores e usuários técnicos
45
45
 
46
46
  ### release-apps.yml (Apps)
47
- - Quando você quer usar o Moltbot como app normal
47
+ - Quando você quer usar o Poolbot como app normal
48
48
  - Quando você quer notificações nativas no Windows
49
49
  - Quando você quer Canvas, Chat e Camera no Android
50
50
  - Para usuários finais, não técnicos
package/docs/_config.yml CHANGED
@@ -1,4 +1,4 @@
1
- title: "Moltbot Docs"
1
+ title: "Poolbot Docs"
2
2
  description: "A TypeScript/Node gateway + macOS/iOS/Android companions for WhatsApp (web) and Telegram (bot)."
3
3
  markdown: kramdown
4
4
  highlighter: rouge
@@ -22,7 +22,7 @@ defaults:
22
22
  nav:
23
23
  - title: "Home"
24
24
  url: "/"
25
- - title: "Moltbot Assistant"
25
+ - title: "Poolbot Assistant"
26
26
  url: "/start/clawd/"
27
27
  - title: "Gateway"
28
28
  url: "/gateway/"