@poolzin/pool-bot 2026.2.0 → 2026.2.2

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README-header.png +0 -0
  3. package/dist/agents/bash-tools.exec.js +76 -25
  4. package/dist/agents/cli-runner/helpers.js +9 -11
  5. package/dist/agents/context.js +1 -1
  6. package/dist/agents/identity.js +47 -7
  7. package/dist/agents/memory-search.js +25 -8
  8. package/dist/agents/model-catalog.js +1 -1
  9. package/dist/agents/model-selection.js +21 -0
  10. package/dist/agents/pi-embedded-block-chunker.js +117 -42
  11. package/dist/agents/pi-embedded-helpers/errors.js +183 -78
  12. package/dist/agents/pi-embedded-helpers.js +1 -1
  13. package/dist/agents/pi-embedded-runner/compact.js +8 -10
  14. package/dist/agents/pi-embedded-runner/model.js +62 -3
  15. package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
  16. package/dist/agents/pi-embedded-runner/run.js +199 -46
  17. package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
  18. package/dist/agents/pi-embedded-subscribe.js +118 -29
  19. package/dist/agents/pi-tools.js +10 -5
  20. package/dist/agents/poolbot-tools.js +15 -10
  21. package/dist/agents/sandbox-paths.js +31 -0
  22. package/dist/agents/session-tool-result-guard.js +94 -15
  23. package/dist/agents/shell-utils.js +51 -0
  24. package/dist/agents/skills/bundled-context.js +23 -0
  25. package/dist/agents/skills/bundled-dir.js +41 -7
  26. package/dist/agents/skills-install.js +60 -23
  27. package/dist/agents/subagent-announce.js +79 -34
  28. package/dist/agents/tool-policy.conformance.js +14 -0
  29. package/dist/agents/tool-policy.js +24 -0
  30. package/dist/agents/tools/cron-tool.js +166 -19
  31. package/dist/agents/tools/discord-actions-presence.js +78 -0
  32. package/dist/agents/tools/image-tool.js +1 -1
  33. package/dist/agents/tools/message-tool.js +56 -2
  34. package/dist/agents/tools/sessions-history-tool.js +69 -1
  35. package/dist/agents/tools/web-search.js +211 -42
  36. package/dist/agents/usage.js +23 -1
  37. package/dist/agents/workspace-run.js +67 -0
  38. package/dist/agents/workspace-templates.js +44 -0
  39. package/dist/auto-reply/command-auth.js +121 -6
  40. package/dist/auto-reply/envelope.js +74 -82
  41. package/dist/auto-reply/reply/commands-compact.js +1 -0
  42. package/dist/auto-reply/reply/commands-context-report.js +1 -0
  43. package/dist/auto-reply/reply/commands-context.js +1 -0
  44. package/dist/auto-reply/reply/commands-models.js +107 -60
  45. package/dist/auto-reply/reply/commands-ptt.js +171 -0
  46. package/dist/auto-reply/reply/get-reply-run.js +2 -1
  47. package/dist/auto-reply/reply/inbound-context.js +5 -1
  48. package/dist/auto-reply/reply/mentions.js +1 -1
  49. package/dist/auto-reply/reply/model-selection.js +3 -3
  50. package/dist/auto-reply/thinking.js +88 -43
  51. package/dist/browser/bridge-server.js +13 -0
  52. package/dist/browser/cdp.helpers.js +38 -24
  53. package/dist/browser/client-fetch.js +50 -7
  54. package/dist/browser/config.js +1 -10
  55. package/dist/browser/extension-relay.js +101 -40
  56. package/dist/browser/pw-ai.js +1 -1
  57. package/dist/browser/pw-session.js +143 -8
  58. package/dist/browser/pw-tools-core.interactions.js +125 -27
  59. package/dist/browser/pw-tools-core.responses.js +1 -1
  60. package/dist/browser/pw-tools-core.state.js +1 -1
  61. package/dist/browser/routes/agent.act.js +86 -41
  62. package/dist/browser/routes/dispatcher.js +4 -4
  63. package/dist/browser/screenshot.js +1 -1
  64. package/dist/browser/server.js +13 -0
  65. package/dist/build-info.json +3 -3
  66. package/dist/canvas-host/a2ui/index.html +28 -28
  67. package/dist/channels/reply-prefix.js +8 -1
  68. package/dist/cli/cron-cli/register.cron-add.js +61 -40
  69. package/dist/cli/cron-cli/register.cron-edit.js +60 -34
  70. package/dist/cli/cron-cli/shared.js +56 -41
  71. package/dist/cli/dns-cli.js +26 -14
  72. package/dist/cli/gateway-cli/register.js +37 -19
  73. package/dist/cli/memory-cli.js +5 -5
  74. package/dist/cli/parse-bytes.js +37 -0
  75. package/dist/cli/update-cli.js +173 -52
  76. package/dist/commands/agent.js +1 -0
  77. package/dist/commands/auth-choice.apply.oauth.js +1 -1
  78. package/dist/commands/doctor-config-flow.js +61 -5
  79. package/dist/commands/doctor-state-migrations.js +1 -1
  80. package/dist/commands/health.js +1 -1
  81. package/dist/commands/model-allowlist.js +29 -0
  82. package/dist/commands/model-picker.js +2 -1
  83. package/dist/commands/models/list.registry.js +1 -1
  84. package/dist/commands/models/list.status-command.js +43 -23
  85. package/dist/commands/models/shared.js +15 -0
  86. package/dist/commands/onboard-custom.js +384 -0
  87. package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
  88. package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
  89. package/dist/commands/onboard-skills.js +63 -38
  90. package/dist/commands/openai-model-default.js +41 -0
  91. package/dist/compat/legacy-names.js +2 -0
  92. package/dist/config/defaults.js +3 -2
  93. package/dist/config/paths.js +136 -35
  94. package/dist/config/plugin-auto-enable.js +21 -5
  95. package/dist/config/redact-snapshot.js +153 -0
  96. package/dist/config/schema.field-metadata.js +590 -0
  97. package/dist/config/schema.js +2 -2
  98. package/dist/config/sessions/store.js +291 -23
  99. package/dist/config/zod-schema.agent-defaults.js +3 -0
  100. package/dist/config/zod-schema.agent-runtime.js +13 -2
  101. package/dist/config/zod-schema.providers-core.js +142 -0
  102. package/dist/config/zod-schema.session.js +3 -0
  103. package/dist/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
  104. package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
  105. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
  106. package/dist/control-ui/index.html +4 -4
  107. package/dist/cron/delivery.js +57 -0
  108. package/dist/cron/isolated-agent/delivery-target.js +18 -3
  109. package/dist/cron/isolated-agent/helpers.js +22 -5
  110. package/dist/cron/isolated-agent/run.js +172 -63
  111. package/dist/cron/isolated-agent/session.js +2 -0
  112. package/dist/cron/normalize.js +356 -28
  113. package/dist/cron/parse.js +10 -5
  114. package/dist/cron/run-log.js +35 -10
  115. package/dist/cron/schedule.js +41 -6
  116. package/dist/cron/service/jobs.js +208 -35
  117. package/dist/cron/service/ops.js +72 -16
  118. package/dist/cron/service/state.js +2 -0
  119. package/dist/cron/service/store.js +386 -14
  120. package/dist/cron/service/timer.js +390 -147
  121. package/dist/cron/session-reaper.js +86 -0
  122. package/dist/cron/store.js +23 -8
  123. package/dist/cron/validate-timestamp.js +43 -0
  124. package/dist/discord/monitor/agent-components.js +438 -0
  125. package/dist/discord/monitor/allow-list.js +28 -5
  126. package/dist/discord/monitor/gateway-registry.js +29 -0
  127. package/dist/discord/monitor/native-command.js +44 -23
  128. package/dist/discord/monitor/sender-identity.js +45 -0
  129. package/dist/discord/pluralkit.js +27 -0
  130. package/dist/discord/send.outbound.js +92 -5
  131. package/dist/discord/send.shared.js +60 -23
  132. package/dist/discord/targets.js +84 -1
  133. package/dist/entry.js +15 -9
  134. package/dist/extensionAPI.js +8 -0
  135. package/dist/gateway/control-ui.js +8 -1
  136. package/dist/gateway/hooks-mapping.js +3 -0
  137. package/dist/gateway/hooks.js +65 -0
  138. package/dist/gateway/net.js +96 -31
  139. package/dist/gateway/node-command-policy.js +50 -15
  140. package/dist/gateway/origin-check.js +56 -0
  141. package/dist/gateway/protocol/client-info.js +9 -0
  142. package/dist/gateway/protocol/index.js +9 -2
  143. package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
  144. package/dist/gateway/protocol/schema/cron.js +22 -10
  145. package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
  146. package/dist/gateway/protocol/schema/sessions.js +12 -0
  147. package/dist/gateway/server/hooks.js +1 -1
  148. package/dist/gateway/server-broadcast.js +26 -9
  149. package/dist/gateway/server-chat.js +112 -23
  150. package/dist/gateway/server-discovery-runtime.js +10 -2
  151. package/dist/gateway/server-http.js +109 -11
  152. package/dist/gateway/server-methods/agent-timestamp.js +60 -0
  153. package/dist/gateway/server-methods/agents.js +321 -2
  154. package/dist/gateway/server-methods/usage.js +559 -16
  155. package/dist/gateway/server-runtime-state.js +22 -8
  156. package/dist/gateway/server-startup-memory.js +16 -0
  157. package/dist/gateway/server.impl.js +5 -1
  158. package/dist/gateway/session-utils.fs.js +23 -25
  159. package/dist/gateway/session-utils.js +20 -10
  160. package/dist/gateway/sessions-patch.js +7 -22
  161. package/dist/gateway/test-helpers.mocks.js +11 -7
  162. package/dist/gateway/test-helpers.server.js +35 -2
  163. package/dist/imessage/constants.js +2 -0
  164. package/dist/imessage/monitor/deliver.js +4 -1
  165. package/dist/imessage/monitor/monitor-provider.js +51 -1
  166. package/dist/infra/bonjour-discovery.js +131 -70
  167. package/dist/infra/control-ui-assets.js +134 -12
  168. package/dist/infra/errors.js +12 -0
  169. package/dist/infra/exec-approvals.js +266 -57
  170. package/dist/infra/format-time/format-datetime.js +79 -0
  171. package/dist/infra/format-time/format-duration.js +81 -0
  172. package/dist/infra/format-time/format-relative.js +80 -0
  173. package/dist/infra/heartbeat-runner.js +140 -49
  174. package/dist/infra/home-dir.js +54 -0
  175. package/dist/infra/net/fetch-guard.js +122 -0
  176. package/dist/infra/net/ssrf.js +65 -29
  177. package/dist/infra/outbound/abort.js +14 -0
  178. package/dist/infra/outbound/message-action-runner.js +77 -13
  179. package/dist/infra/outbound/outbound-session.js +143 -37
  180. package/dist/infra/poolbot-root.js +43 -1
  181. package/dist/infra/session-cost-usage.js +631 -41
  182. package/dist/infra/state-migrations.js +317 -47
  183. package/dist/infra/update-global.js +35 -0
  184. package/dist/infra/update-runner.js +149 -43
  185. package/dist/infra/warning-filter.js +65 -0
  186. package/dist/infra/widearea-dns.js +30 -9
  187. package/dist/logging/redact-identifier.js +12 -0
  188. package/dist/media/fetch.js +81 -58
  189. package/dist/media/store.js +2 -0
  190. package/dist/media-understanding/apply.js +403 -3
  191. package/dist/media-understanding/attachments.js +38 -27
  192. package/dist/media-understanding/defaults.js +16 -0
  193. package/dist/media-understanding/providers/deepgram/audio.js +22 -14
  194. package/dist/media-understanding/providers/google/audio.js +24 -17
  195. package/dist/media-understanding/providers/google/video.js +24 -17
  196. package/dist/media-understanding/providers/image.js +3 -3
  197. package/dist/media-understanding/providers/index.js +4 -1
  198. package/dist/media-understanding/providers/openai/audio.js +22 -14
  199. package/dist/media-understanding/providers/shared.js +16 -11
  200. package/dist/media-understanding/providers/zai/index.js +6 -0
  201. package/dist/media-understanding/runner.js +158 -90
  202. package/dist/memory/batch-voyage.js +277 -0
  203. package/dist/memory/embeddings-voyage.js +75 -0
  204. package/dist/memory/embeddings.js +28 -16
  205. package/dist/memory/internal.js +101 -18
  206. package/dist/memory/manager.js +154 -48
  207. package/dist/memory/search-manager.js +173 -0
  208. package/dist/memory/session-files.js +9 -3
  209. package/dist/node-host/runner.js +34 -24
  210. package/dist/node-host/with-timeout.js +27 -0
  211. package/dist/plugins/commands.js +5 -1
  212. package/dist/plugins/config-state.js +86 -7
  213. package/dist/plugins/source-display.js +51 -0
  214. package/dist/process/exec.js +20 -2
  215. package/dist/routing/resolve-route.js +12 -0
  216. package/dist/routing/session-key.js +15 -0
  217. package/dist/runtime.js +2 -0
  218. package/dist/security/audit-extra.async.js +601 -0
  219. package/dist/security/audit-extra.js +2 -830
  220. package/dist/security/audit-extra.sync.js +505 -0
  221. package/dist/security/channel-metadata.js +34 -0
  222. package/dist/security/external-content.js +88 -6
  223. package/dist/security/skill-scanner.js +330 -0
  224. package/dist/sessions/session-key-utils.js +7 -0
  225. package/dist/signal/monitor/event-handler.js +80 -1
  226. package/dist/slack/monitor/media.js +85 -15
  227. package/dist/tailscale/detect.js +1 -2
  228. package/dist/telegram/bot/helpers.js +109 -28
  229. package/dist/telegram/bot-handlers.js +144 -3
  230. package/dist/telegram/bot-message-context.js +37 -10
  231. package/dist/telegram/bot-message-dispatch.js +54 -17
  232. package/dist/telegram/bot-native-commands.js +86 -29
  233. package/dist/telegram/bot.js +30 -29
  234. package/dist/telegram/model-buttons.js +163 -0
  235. package/dist/telegram/monitor.js +110 -85
  236. package/dist/telegram/send.js +129 -47
  237. package/dist/terminal/restore.js +45 -0
  238. package/dist/test-helpers/state-dir-env.js +16 -0
  239. package/dist/tts/tts.js +12 -6
  240. package/dist/tui/tui-session-actions.js +166 -54
  241. package/dist/utils/fetch-timeout.js +20 -0
  242. package/dist/utils/normalize-secret-input.js +19 -0
  243. package/dist/utils/transcript-tools.js +58 -0
  244. package/dist/utils.js +45 -14
  245. package/dist/version.js +42 -5
  246. package/dist/wizard/clack-prompter.js +9 -6
  247. package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
  248. package/extensions/googlechat/package.json +2 -2
  249. package/extensions/line/node_modules/.bin/poolbot +21 -0
  250. package/extensions/line/package.json +1 -1
  251. package/extensions/matrix/node_modules/.bin/poolbot +21 -0
  252. package/extensions/matrix/package.json +1 -1
  253. package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
  254. package/extensions/memory-core/package.json +4 -1
  255. package/extensions/twitch/node_modules/.bin/poolbot +21 -0
  256. package/extensions/twitch/package.json +1 -1
  257. package/package.json +183 -24
  258. package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
@@ -2,32 +2,37 @@ import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { runCommandWithTimeout } from "../process/exec.js";
5
- import { compareSemverStrings } from "./update-check.js";
6
- import { DEV_BRANCH, isBetaTag, isStableTag } from "./update-channels.js";
7
- import { detectGlobalInstallManagerForRoot, globalInstallArgs } from "./update-global.js";
5
+ import { resolveControlUiDistIndexHealth, resolveControlUiDistIndexPathForRoot, } from "./control-ui-assets.js";
8
6
  import { trimLogTail } from "./restart-sentinel.js";
7
+ import { channelToNpmTag, DEFAULT_PACKAGE_CHANNEL, DEV_BRANCH, isBetaTag, isStableTag, } from "./update-channels.js";
8
+ import { compareSemverStrings } from "./update-check.js";
9
+ import { cleanupGlobalRenameDirs, detectGlobalInstallManagerForRoot, globalInstallArgs, } from "./update-global.js";
9
10
  const DEFAULT_TIMEOUT_MS = 20 * 60_000;
10
11
  const MAX_LOG_CHARS = 8000;
11
12
  const PREFLIGHT_MAX_COMMITS = 10;
12
13
  const START_DIRS = ["cwd", "argv1", "process"];
13
14
  const DEFAULT_PACKAGE_NAME = "poolbot";
14
- const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME, "poolbot"]);
15
+ const CORE_PACKAGE_NAMES = new Set([DEFAULT_PACKAGE_NAME]);
15
16
  function normalizeDir(value) {
16
- if (!value)
17
+ if (!value) {
17
18
  return null;
19
+ }
18
20
  const trimmed = value.trim();
19
- if (!trimmed)
21
+ if (!trimmed) {
20
22
  return null;
23
+ }
21
24
  return path.resolve(trimmed);
22
25
  }
23
26
  function resolveNodeModulesBinPackageRoot(argv1) {
24
27
  const normalized = path.resolve(argv1);
25
28
  const parts = normalized.split(path.sep);
26
29
  const binIndex = parts.lastIndexOf(".bin");
27
- if (binIndex <= 0)
30
+ if (binIndex <= 0) {
28
31
  return null;
29
- if (parts[binIndex - 1] !== "node_modules")
32
+ }
33
+ if (parts[binIndex - 1] !== "node_modules") {
30
34
  return null;
35
+ }
31
36
  const binName = path.basename(normalized);
32
37
  const nodeModulesDir = parts.slice(0, binIndex).join(path.sep);
33
38
  return path.join(nodeModulesDir, binName);
@@ -35,18 +40,21 @@ function resolveNodeModulesBinPackageRoot(argv1) {
35
40
  function buildStartDirs(opts) {
36
41
  const dirs = [];
37
42
  const cwd = normalizeDir(opts.cwd);
38
- if (cwd)
43
+ if (cwd) {
39
44
  dirs.push(cwd);
45
+ }
40
46
  const argv1 = normalizeDir(opts.argv1);
41
47
  if (argv1) {
42
48
  dirs.push(path.dirname(argv1));
43
49
  const packageRoot = resolveNodeModulesBinPackageRoot(argv1);
44
- if (packageRoot)
50
+ if (packageRoot) {
45
51
  dirs.push(packageRoot);
52
+ }
46
53
  }
47
54
  const proc = normalizeDir(process.cwd());
48
- if (proc)
55
+ if (proc) {
49
56
  dirs.push(proc);
57
+ }
50
58
  return Array.from(new Set(dirs));
51
59
  }
52
60
  async function readPackageVersion(root) {
@@ -74,8 +82,9 @@ async function readBranchName(runCommand, root, timeoutMs) {
74
82
  const res = await runCommand(["git", "-C", root, "rev-parse", "--abbrev-ref", "HEAD"], {
75
83
  timeoutMs,
76
84
  }).catch(() => null);
77
- if (!res || res.code !== 0)
85
+ if (!res || res.code !== 0) {
78
86
  return null;
87
+ }
79
88
  const branch = res.stdout.trim();
80
89
  return branch || null;
81
90
  }
@@ -83,8 +92,9 @@ async function listGitTags(runCommand, root, timeoutMs, pattern = "v*") {
83
92
  const res = await runCommand(["git", "-C", root, "tag", "--list", pattern, "--sort=-v:refname"], {
84
93
  timeoutMs,
85
94
  }).catch(() => null);
86
- if (!res || res.code !== 0)
95
+ if (!res || res.code !== 0) {
87
96
  return [];
97
+ }
88
98
  return res.stdout
89
99
  .split("\n")
90
100
  .map((line) => line.trim())
@@ -95,13 +105,16 @@ async function resolveChannelTag(runCommand, root, timeoutMs, channel) {
95
105
  if (channel === "beta") {
96
106
  const betaTag = tags.find((tag) => isBetaTag(tag)) ?? null;
97
107
  const stableTag = tags.find((tag) => isStableTag(tag)) ?? null;
98
- if (!betaTag)
108
+ if (!betaTag) {
99
109
  return stableTag;
100
- if (!stableTag)
110
+ }
111
+ if (!stableTag) {
101
112
  return betaTag;
113
+ }
102
114
  const cmp = compareSemverStrings(betaTag, stableTag);
103
- if (cmp != null && cmp < 0)
115
+ if (cmp != null && cmp < 0) {
104
116
  return stableTag;
117
+ }
105
118
  return betaTag;
106
119
  }
107
120
  return tags.find((tag) => isStableTag(tag)) ?? null;
@@ -113,8 +126,9 @@ async function resolveGitRoot(runCommand, candidates, timeoutMs) {
113
126
  });
114
127
  if (res.code === 0) {
115
128
  const root = res.stdout.trim();
116
- if (root)
129
+ if (root) {
117
130
  return root;
131
+ }
118
132
  }
119
133
  }
120
134
  return null;
@@ -128,15 +142,17 @@ async function findPackageRoot(candidates) {
128
142
  const raw = await fs.readFile(pkgPath, "utf-8");
129
143
  const parsed = JSON.parse(raw);
130
144
  const name = parsed?.name?.trim();
131
- if (name && CORE_PACKAGE_NAMES.has(name))
145
+ if (name && CORE_PACKAGE_NAMES.has(name)) {
132
146
  return current;
147
+ }
133
148
  }
134
149
  catch {
135
150
  // ignore
136
151
  }
137
152
  const parent = path.dirname(current);
138
- if (parent === current)
153
+ if (parent === current) {
139
154
  break;
155
+ }
140
156
  current = parent;
141
157
  }
142
158
  }
@@ -147,19 +163,23 @@ async function detectPackageManager(root) {
147
163
  const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
148
164
  const parsed = JSON.parse(raw);
149
165
  const pm = parsed?.packageManager?.split("@")[0]?.trim();
150
- if (pm === "pnpm" || pm === "bun" || pm === "npm")
166
+ if (pm === "pnpm" || pm === "bun" || pm === "npm") {
151
167
  return pm;
168
+ }
152
169
  }
153
170
  catch {
154
171
  // ignore
155
172
  }
156
173
  const files = await fs.readdir(root).catch(() => []);
157
- if (files.includes("pnpm-lock.yaml"))
174
+ if (files.includes("pnpm-lock.yaml")) {
158
175
  return "pnpm";
159
- if (files.includes("bun.lockb"))
176
+ }
177
+ if (files.includes("bun.lockb")) {
160
178
  return "bun";
161
- if (files.includes("package-lock.json"))
179
+ }
180
+ if (files.includes("package-lock.json")) {
162
181
  return "npm";
182
+ }
163
183
  return "npm";
164
184
  }
165
185
  async function runStep(opts) {
@@ -193,27 +213,34 @@ async function runStep(opts) {
193
213
  };
194
214
  }
195
215
  function managerScriptArgs(manager, script, args = []) {
196
- if (manager === "pnpm")
216
+ if (manager === "pnpm") {
197
217
  return ["pnpm", script, ...args];
198
- if (manager === "bun")
218
+ }
219
+ if (manager === "bun") {
199
220
  return ["bun", "run", script, ...args];
200
- if (args.length > 0)
221
+ }
222
+ if (args.length > 0) {
201
223
  return ["npm", "run", script, "--", ...args];
224
+ }
202
225
  return ["npm", "run", script];
203
226
  }
204
227
  function managerInstallArgs(manager) {
205
- if (manager === "pnpm")
228
+ if (manager === "pnpm") {
206
229
  return ["pnpm", "install"];
207
- if (manager === "bun")
230
+ }
231
+ if (manager === "bun") {
208
232
  return ["bun", "install"];
233
+ }
209
234
  return ["npm", "install"];
210
235
  }
211
236
  function normalizeTag(tag) {
212
237
  const trimmed = tag?.trim();
213
- if (!trimmed)
238
+ if (!trimmed) {
214
239
  return "latest";
215
- if (trimmed.startsWith("poolbot@"))
240
+ }
241
+ if (trimmed.startsWith("poolbot@")) {
216
242
  return trimmed.slice("poolbot@".length);
243
+ }
217
244
  if (trimmed.startsWith(`${DEFAULT_PACKAGE_NAME}@`)) {
218
245
  return trimmed.slice(`${DEFAULT_PACKAGE_NAME}@`.length);
219
246
  }
@@ -392,20 +419,24 @@ export async function runGatewayUpdate(opts = {}) {
392
419
  const shortSha = sha.slice(0, 8);
393
420
  const checkoutStep = await runStep(step(`preflight checkout (${shortSha})`, ["git", "-C", worktreeDir, "checkout", "--detach", sha], worktreeDir));
394
421
  steps.push(checkoutStep);
395
- if (checkoutStep.exitCode !== 0)
422
+ if (checkoutStep.exitCode !== 0) {
396
423
  continue;
424
+ }
397
425
  const depsStep = await runStep(step(`preflight deps install (${shortSha})`, managerInstallArgs(manager), worktreeDir));
398
426
  steps.push(depsStep);
399
- if (depsStep.exitCode !== 0)
400
- continue;
401
- const lintStep = await runStep(step(`preflight lint (${shortSha})`, managerScriptArgs(manager, "lint"), worktreeDir));
402
- steps.push(lintStep);
403
- if (lintStep.exitCode !== 0)
427
+ if (depsStep.exitCode !== 0) {
404
428
  continue;
429
+ }
405
430
  const buildStep = await runStep(step(`preflight build (${shortSha})`, managerScriptArgs(manager, "build"), worktreeDir));
406
431
  steps.push(buildStep);
407
- if (buildStep.exitCode !== 0)
432
+ if (buildStep.exitCode !== 0) {
433
+ continue;
434
+ }
435
+ const lintStep = await runStep(step(`preflight lint (${shortSha})`, managerScriptArgs(manager, "lint"), worktreeDir));
436
+ steps.push(lintStep);
437
+ if (lintStep.exitCode !== 0) {
408
438
  continue;
439
+ }
409
440
  selectedSha = sha;
410
441
  break;
411
442
  }
@@ -504,12 +535,81 @@ export async function runGatewayUpdate(opts = {}) {
504
535
  steps.push(buildStep);
505
536
  const uiBuildStep = await runStep(step("ui:build", managerScriptArgs(manager, "ui:build"), gitRoot));
506
537
  steps.push(uiBuildStep);
507
- // Restore dist/control-ui/ to committed state to prevent dirty repo after update
508
- // (ui:build regenerates assets with new hashes, which would block future updates)
509
- const restoreUiStep = await runStep(step("restore control-ui", ["git", "-C", gitRoot, "checkout", "--", "dist/control-ui/"], gitRoot));
510
- steps.push(restoreUiStep);
511
- const doctorStep = await runStep(step("poolbot doctor", managerScriptArgs(manager, "poolbot", ["doctor", "--non-interactive"]), gitRoot, { CLAWDBOT_UPDATE_IN_PROGRESS: "1" }));
538
+ const doctorEntry = path.join(gitRoot, "poolbot.mjs");
539
+ const doctorEntryExists = await fs
540
+ .stat(doctorEntry)
541
+ .then(() => true)
542
+ .catch(() => false);
543
+ if (!doctorEntryExists) {
544
+ steps.push({
545
+ name: "poolbot doctor entry",
546
+ command: `verify ${doctorEntry}`,
547
+ cwd: gitRoot,
548
+ durationMs: 0,
549
+ exitCode: 1,
550
+ stderrTail: `missing ${doctorEntry}`,
551
+ });
552
+ return {
553
+ status: "error",
554
+ mode: "git",
555
+ root: gitRoot,
556
+ reason: "doctor-entry-missing",
557
+ before: { sha: beforeSha, version: beforeVersion },
558
+ steps,
559
+ durationMs: Date.now() - startedAt,
560
+ };
561
+ }
562
+ const doctorArgv = [process.execPath, doctorEntry, "doctor", "--non-interactive"];
563
+ const doctorStep = await runStep(step("poolbot doctor", doctorArgv, gitRoot, { CLAWDBOT_UPDATE_IN_PROGRESS: "1" }));
512
564
  steps.push(doctorStep);
565
+ const uiIndexHealth = await resolveControlUiDistIndexHealth({ root: gitRoot });
566
+ if (!uiIndexHealth.exists) {
567
+ const repairArgv = managerScriptArgs(manager, "ui:build");
568
+ const started = Date.now();
569
+ const repairResult = await runCommand(repairArgv, { cwd: gitRoot, timeoutMs });
570
+ const repairStep = {
571
+ name: "ui:build (post-doctor repair)",
572
+ command: repairArgv.join(" "),
573
+ cwd: gitRoot,
574
+ durationMs: Date.now() - started,
575
+ exitCode: repairResult.code,
576
+ stdoutTail: trimLogTail(repairResult.stdout, MAX_LOG_CHARS),
577
+ stderrTail: trimLogTail(repairResult.stderr, MAX_LOG_CHARS),
578
+ };
579
+ steps.push(repairStep);
580
+ if (repairResult.code !== 0) {
581
+ return {
582
+ status: "error",
583
+ mode: "git",
584
+ root: gitRoot,
585
+ reason: repairStep.name,
586
+ before: { sha: beforeSha, version: beforeVersion },
587
+ steps,
588
+ durationMs: Date.now() - startedAt,
589
+ };
590
+ }
591
+ const repairedUiIndexHealth = await resolveControlUiDistIndexHealth({ root: gitRoot });
592
+ if (!repairedUiIndexHealth.exists) {
593
+ const uiIndexPath = repairedUiIndexHealth.indexPath ?? resolveControlUiDistIndexPathForRoot(gitRoot);
594
+ steps.push({
595
+ name: "ui assets verify",
596
+ command: `verify ${uiIndexPath}`,
597
+ cwd: gitRoot,
598
+ durationMs: 0,
599
+ exitCode: 1,
600
+ stderrTail: `missing ${uiIndexPath}`,
601
+ });
602
+ return {
603
+ status: "error",
604
+ mode: "git",
605
+ root: gitRoot,
606
+ reason: "ui-assets-missing",
607
+ before: { sha: beforeSha, version: beforeVersion },
608
+ steps,
609
+ durationMs: Date.now() - startedAt,
610
+ };
611
+ }
612
+ }
513
613
  const failedStep = steps.find((s) => s.exitCode !== 0);
514
614
  const afterShaStep = await runStep(step("git rev-parse HEAD (after)", ["git", "-C", gitRoot, "rev-parse", "HEAD"], gitRoot));
515
615
  steps.push(afterShaStep);
@@ -541,7 +641,13 @@ export async function runGatewayUpdate(opts = {}) {
541
641
  const globalManager = await detectGlobalInstallManagerForRoot(runCommand, pkgRoot, timeoutMs);
542
642
  if (globalManager) {
543
643
  const packageName = (await readPackageName(pkgRoot)) ?? DEFAULT_PACKAGE_NAME;
544
- const spec = `${packageName}@${normalizeTag(opts.tag)}`;
644
+ await cleanupGlobalRenameDirs({
645
+ globalRoot: path.dirname(pkgRoot),
646
+ packageName,
647
+ });
648
+ const channel = opts.channel ?? DEFAULT_PACKAGE_CHANNEL;
649
+ const tag = normalizeTag(opts.tag ?? channelToNpmTag(channel));
650
+ const spec = `${packageName}@${tag}`;
545
651
  const updateStep = await runStep({
546
652
  runCommand,
547
653
  name: "global update",
@@ -0,0 +1,65 @@
1
+ const warningFilterKey = Symbol.for("poolbot.warning-filter");
2
+ export function shouldIgnoreWarning(warning) {
3
+ if (warning.code === "DEP0040" && warning.message?.includes("punycode")) {
4
+ return true;
5
+ }
6
+ if (warning.code === "DEP0060" && warning.message?.includes("util._extend")) {
7
+ return true;
8
+ }
9
+ if (warning.name === "ExperimentalWarning" &&
10
+ warning.message?.includes("SQLite is an experimental feature")) {
11
+ return true;
12
+ }
13
+ return false;
14
+ }
15
+ function normalizeWarningArgs(args) {
16
+ const warningArg = args[0];
17
+ const secondArg = args[1];
18
+ const thirdArg = args[2];
19
+ let name;
20
+ let code;
21
+ let message;
22
+ if (warningArg instanceof Error) {
23
+ name = warningArg.name;
24
+ message = warningArg.message;
25
+ code = warningArg.code;
26
+ }
27
+ else if (typeof warningArg === "string") {
28
+ message = warningArg;
29
+ }
30
+ if (secondArg && typeof secondArg === "object" && !Array.isArray(secondArg)) {
31
+ const options = secondArg;
32
+ if (typeof options.type === "string") {
33
+ name = options.type;
34
+ }
35
+ if (typeof options.code === "string") {
36
+ code = options.code;
37
+ }
38
+ }
39
+ else {
40
+ if (typeof secondArg === "string") {
41
+ name = secondArg;
42
+ }
43
+ if (typeof thirdArg === "string") {
44
+ code = thirdArg;
45
+ }
46
+ }
47
+ return { name, code, message };
48
+ }
49
+ export function installProcessWarningFilter() {
50
+ const globalState = globalThis;
51
+ if (globalState[warningFilterKey]?.installed) {
52
+ return;
53
+ }
54
+ const originalEmitWarning = process.emitWarning.bind(process);
55
+ const wrappedEmitWarning = ((...args) => {
56
+ if (shouldIgnoreWarning(normalizeWarningArgs(args))) {
57
+ return;
58
+ }
59
+ return Reflect.apply(originalEmitWarning, process, args);
60
+ });
61
+ process.emitWarning = wrappedEmitWarning;
62
+ globalState[warningFilterKey] = {
63
+ installed: true,
64
+ };
65
+ }
@@ -2,10 +2,23 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { CONFIG_DIR, ensureDir } from "../utils.js";
5
- export const WIDE_AREA_DISCOVERY_DOMAIN = "poolbot.internal.";
6
- export const WIDE_AREA_ZONE_FILENAME = "poolbot.internal.db";
7
- export function getWideAreaZonePath() {
8
- return path.join(CONFIG_DIR, "dns", WIDE_AREA_ZONE_FILENAME);
5
+ export function normalizeWideAreaDomain(raw) {
6
+ const trimmed = raw?.trim();
7
+ if (!trimmed) {
8
+ return null;
9
+ }
10
+ return trimmed.endsWith(".") ? trimmed : `${trimmed}.`;
11
+ }
12
+ export function resolveWideAreaDiscoveryDomain(params) {
13
+ const env = params?.env ?? process.env;
14
+ const candidate = params?.configDomain ?? env.CLAWDBOT_WIDE_AREA_DOMAIN ?? null;
15
+ return normalizeWideAreaDomain(candidate);
16
+ }
17
+ function zoneFilenameForDomain(domain) {
18
+ return `${domain.replace(/\.$/, "")}.db`;
19
+ }
20
+ export function getWideAreaZonePath(domain) {
21
+ return path.join(CONFIG_DIR, "dns", zoneFilenameForDomain(domain));
9
22
  }
10
23
  function dnsLabel(raw, fallback) {
11
24
  const normalized = raw
@@ -30,17 +43,20 @@ function formatYyyyMmDd(date) {
30
43
  function nextSerial(existingSerial, now) {
31
44
  const today = formatYyyyMmDd(now);
32
45
  const base = Number.parseInt(`${today}01`, 10);
33
- if (!existingSerial || !Number.isFinite(existingSerial))
46
+ if (!existingSerial || !Number.isFinite(existingSerial)) {
34
47
  return base;
48
+ }
35
49
  const existing = String(existingSerial);
36
- if (existing.startsWith(today))
50
+ if (existing.startsWith(today)) {
37
51
  return existingSerial + 1;
52
+ }
38
53
  return base;
39
54
  }
40
55
  function extractSerial(zoneText) {
41
56
  const match = zoneText.match(/^\s*@\s+IN\s+SOA\s+\S+\s+\S+\s+(\d+)\s+/m);
42
- if (!match)
57
+ if (!match) {
43
58
  return null;
59
+ }
44
60
  const parsed = Number.parseInt(match[1], 10);
45
61
  return Number.isFinite(parsed) ? parsed : null;
46
62
  }
@@ -61,6 +77,7 @@ function renderZone(opts) {
61
77
  const hostname = os.hostname().split(".")[0] ?? "poolbot";
62
78
  const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "poolbot");
63
79
  const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "poolbot-gw");
80
+ const domain = normalizeWideAreaDomain(opts.domain) ?? "local.";
64
81
  const txt = [
65
82
  `displayName=${opts.displayName.trim() || hostname}`,
66
83
  `role=gateway`,
@@ -83,7 +100,7 @@ function renderZone(opts) {
83
100
  txt.push(`cliPath=${opts.cliPath.trim()}`);
84
101
  }
85
102
  const records = [];
86
- records.push(`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`);
103
+ records.push(`$ORIGIN ${domain}`);
87
104
  records.push(`$TTL 60`);
88
105
  const soaLine = `@ IN SOA ns1 hostmaster ${opts.serial} 7200 3600 1209600 60`;
89
106
  records.push(soaLine);
@@ -107,7 +124,11 @@ export function renderWideAreaGatewayZoneText(opts) {
107
124
  return renderZone(opts);
108
125
  }
109
126
  export async function writeWideAreaGatewayZone(opts) {
110
- const zonePath = getWideAreaZonePath();
127
+ const domain = normalizeWideAreaDomain(opts.domain);
128
+ if (!domain) {
129
+ throw new Error("wide-area discovery domain is required");
130
+ }
131
+ const zonePath = getWideAreaZonePath(domain);
111
132
  await ensureDir(path.dirname(zonePath));
112
133
  const existing = (() => {
113
134
  try {
@@ -0,0 +1,12 @@
1
+ import crypto from "node:crypto";
2
+ export function sha256HexPrefix(value, len = 12) {
3
+ const safeLen = Number.isFinite(len) ? Math.max(1, Math.floor(len)) : 12;
4
+ return crypto.createHash("sha256").update(value).digest("hex").slice(0, safeLen);
5
+ }
6
+ export function redactIdentifier(value, opts) {
7
+ const trimmed = value?.trim();
8
+ if (!trimmed) {
9
+ return "-";
10
+ }
11
+ return `sha256:${sha256HexPrefix(trimmed, opts?.len ?? 12)}`;
12
+ }