@poolzin/pool-bot 2026.2.21 → 2026.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/agents/api-key-rotation.js +47 -0
  3. package/dist/agents/apply-patch-update.js +19 -9
  4. package/dist/agents/apply-patch.js +72 -47
  5. package/dist/agents/bash-tools.exec.js +141 -559
  6. package/dist/agents/cli-backends.js +49 -6
  7. package/dist/agents/cli-runner/helpers.js +69 -152
  8. package/dist/agents/cli-runner.js +70 -19
  9. package/dist/agents/identity.js +20 -1
  10. package/dist/agents/image-sanitization.js +9 -0
  11. package/dist/agents/live-auth-keys.js +123 -26
  12. package/dist/agents/live-model-filter.js +13 -4
  13. package/dist/agents/model-catalog.js +40 -9
  14. package/dist/agents/model-forward-compat.js +60 -23
  15. package/dist/agents/model-selection.js +134 -41
  16. package/dist/agents/pi-auth-json.js +2 -2
  17. package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
  18. package/dist/agents/pi-embedded-helpers/errors.js +140 -15
  19. package/dist/agents/pi-embedded-helpers/images.js +22 -12
  20. package/dist/agents/pi-embedded-helpers.js +2 -2
  21. package/dist/agents/pi-embedded-runner/abort.js +10 -3
  22. package/dist/agents/pi-embedded-runner/compact.js +230 -32
  23. package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
  24. package/dist/agents/pi-embedded-runner/google.js +109 -19
  25. package/dist/agents/pi-embedded-runner/history.js +35 -17
  26. package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
  27. package/dist/agents/pi-embedded-runner/run/images.js +81 -55
  28. package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
  29. package/dist/agents/pi-embedded-runner/run.js +193 -25
  30. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
  31. package/dist/agents/pi-embedded-runner/runs.js +17 -8
  32. package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
  33. package/dist/agents/pi-embedded-runner.js +1 -1
  34. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
  35. package/dist/agents/pi-embedded-subscribe.js +37 -0
  36. package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
  37. package/dist/agents/pi-model-discovery.js +9 -2
  38. package/dist/agents/pi-tool-definition-adapter.js +60 -8
  39. package/dist/agents/pi-tools.before-tool-call.js +1 -1
  40. package/dist/agents/pi-tools.js +113 -94
  41. package/dist/agents/pi-tools.read.js +337 -38
  42. package/dist/agents/poolbot-tools.js +14 -5
  43. package/dist/agents/sandbox/docker.js +10 -5
  44. package/dist/agents/sandbox/registry.js +96 -46
  45. package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
  46. package/dist/agents/sandbox-paths.js +43 -10
  47. package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
  48. package/dist/agents/session-tool-result-guard.js +39 -39
  49. package/dist/agents/session-transcript-repair.js +36 -33
  50. package/dist/agents/session-write-lock.js +62 -44
  51. package/dist/agents/skills/frontmatter.js +49 -88
  52. package/dist/agents/skills/workspace.js +335 -28
  53. package/dist/agents/subagent-announce.js +508 -174
  54. package/dist/agents/subagent-registry.js +45 -4
  55. package/dist/agents/subagent-spawn.js +16 -33
  56. package/dist/agents/system-prompt-report.js +27 -10
  57. package/dist/agents/system-prompt.js +26 -32
  58. package/dist/agents/tool-call-id.js +69 -17
  59. package/dist/agents/tool-display-common.js +1 -1
  60. package/dist/agents/tool-images.js +64 -31
  61. package/dist/agents/tools/canvas-tool.js +17 -11
  62. package/dist/agents/tools/common.js +37 -19
  63. package/dist/agents/tools/cron-tool.js +40 -38
  64. package/dist/agents/tools/gateway.js +70 -2
  65. package/dist/agents/tools/message-tool.js +181 -40
  66. package/dist/agents/tools/nodes-tool.js +128 -36
  67. package/dist/agents/tools/nodes-utils.js +12 -38
  68. package/dist/agents/tools/session-status-tool.js +24 -71
  69. package/dist/agents/tools/sessions-helpers.js +38 -210
  70. package/dist/agents/tools/sessions-spawn-tool.js +28 -198
  71. package/dist/agents/tools/telegram-actions.js +58 -7
  72. package/dist/agents/tools/web-fetch-utils.js +112 -7
  73. package/dist/agents/tools/web-fetch.js +279 -175
  74. package/dist/agents/tools/web-shared.js +71 -8
  75. package/dist/agents/usage.js +25 -16
  76. package/dist/auto-reply/commands-registry.data.js +85 -11
  77. package/dist/auto-reply/dispatch.js +40 -21
  78. package/dist/auto-reply/reply/abort.js +102 -33
  79. package/dist/auto-reply/reply/commands-core.js +82 -33
  80. package/dist/auto-reply/reply/commands-export-session.js +1 -1
  81. package/dist/auto-reply/reply/commands-info.js +41 -12
  82. package/dist/auto-reply/reply/commands-subagents.js +352 -100
  83. package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
  84. package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
  85. package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
  86. package/dist/auto-reply/reply/inbound-meta.js +12 -1
  87. package/dist/auto-reply/reply/mentions.js +18 -11
  88. package/dist/auto-reply/reply/normalize-reply.js +17 -8
  89. package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
  90. package/dist/auto-reply/reply/session.js +102 -21
  91. package/dist/auto-reply/reply/streaming-directives.js +16 -5
  92. package/dist/auto-reply/status.js +73 -50
  93. package/dist/browser/extension-relay.js +3 -3
  94. package/dist/browser/http-auth.js +1 -1
  95. package/dist/browser/paths.js +2 -2
  96. package/dist/build-info.json +3 -3
  97. package/dist/channels/allowlist-match.js +20 -0
  98. package/dist/channels/allowlists/resolve-utils.js +65 -2
  99. package/dist/channels/chat-type.js +8 -4
  100. package/dist/channels/dock.js +127 -35
  101. package/dist/channels/draft-stream-loop.js +6 -2
  102. package/dist/channels/plugins/actions/telegram.js +42 -18
  103. package/dist/channels/plugins/allowlist-match.js +1 -1
  104. package/dist/channels/plugins/group-mentions.js +51 -41
  105. package/dist/channels/plugins/message-action-names.js +2 -0
  106. package/dist/channels/plugins/message-actions.js +24 -5
  107. package/dist/channels/plugins/normalize/discord.js +26 -4
  108. package/dist/channels/plugins/normalize/signal.js +35 -22
  109. package/dist/channels/plugins/onboarding/helpers.js +8 -26
  110. package/dist/channels/plugins/outbound/imessage.js +15 -14
  111. package/dist/channels/registry.js +20 -7
  112. package/dist/cli/acp-cli.js +7 -5
  113. package/dist/cli/browser-cli-extension.js +25 -12
  114. package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
  115. package/dist/cli/browser-cli-state.js +101 -145
  116. package/dist/cli/command-options.js +28 -0
  117. package/dist/cli/completion-cli.js +6 -6
  118. package/dist/cli/cron-cli/register.cron-add.js +25 -1
  119. package/dist/cli/cron-cli/register.cron-edit.js +44 -0
  120. package/dist/cli/cron-cli/shared.js +7 -1
  121. package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
  122. package/dist/cli/daemon-cli/lifecycle.js +23 -247
  123. package/dist/cli/daemon-cli/register-service-commands.js +25 -4
  124. package/dist/cli/daemon-cli.js +1 -0
  125. package/dist/cli/devices-cli.js +33 -20
  126. package/dist/cli/gateway-cli/register.js +37 -105
  127. package/dist/cli/gateway-cli/run.js +49 -11
  128. package/dist/cli/nodes-camera.js +59 -4
  129. package/dist/cli/nodes-cli/register.camera.js +27 -24
  130. package/dist/cli/nodes-cli/rpc.js +21 -38
  131. package/dist/cli/qr-cli.js +2 -2
  132. package/dist/cli/skills-cli.format.js +2 -2
  133. package/dist/cli/update-cli/progress.js +2 -2
  134. package/dist/cli/update-cli/restart-helper.js +28 -7
  135. package/dist/cli/update-cli/shared.js +7 -7
  136. package/dist/cli/update-cli/status.js +1 -1
  137. package/dist/cli/update-cli/update-command.js +14 -8
  138. package/dist/cli/update-cli/wizard.js +2 -2
  139. package/dist/cli/update-cli.js +21 -1027
  140. package/dist/commands/auth-choice.apply.anthropic.js +10 -2
  141. package/dist/commands/channels/add-mutators.js +3 -35
  142. package/dist/commands/channels/add.js +39 -51
  143. package/dist/commands/config-validation.js +1 -1
  144. package/dist/commands/configure.gateway-auth.js +52 -15
  145. package/dist/commands/configure.gateway.js +84 -40
  146. package/dist/commands/doctor-completion.js +3 -3
  147. package/dist/commands/doctor-config-flow.js +536 -16
  148. package/dist/commands/doctor-gateway-services.js +103 -79
  149. package/dist/commands/doctor-memory-search.js +9 -9
  150. package/dist/commands/doctor-platform-notes.js +57 -30
  151. package/dist/commands/doctor-prompter.js +26 -15
  152. package/dist/commands/doctor-session-locks.js +1 -1
  153. package/dist/commands/doctor.js +21 -9
  154. package/dist/commands/model-picker.js +120 -95
  155. package/dist/commands/models/set.js +2 -21
  156. package/dist/commands/models/shared.js +65 -37
  157. package/dist/commands/onboard-helpers.js +81 -39
  158. package/dist/commands/openai-codex-oauth.js +1 -1
  159. package/dist/commands/sessions.js +52 -53
  160. package/dist/commands/status.summary.js +52 -34
  161. package/dist/commands/test-wizard-helpers.js +2 -2
  162. package/dist/config/defaults.js +79 -42
  163. package/dist/config/group-policy.js +50 -18
  164. package/dist/config/includes.js +37 -10
  165. package/dist/config/schema.help.js +5 -4
  166. package/dist/config/schema.hints.js +2 -2
  167. package/dist/config/schema.labels.js +1 -0
  168. package/dist/config/sessions/group.js +12 -11
  169. package/dist/config/sessions/paths.js +137 -11
  170. package/dist/config/sessions/store.js +185 -65
  171. package/dist/config/sessions/types.js +15 -1
  172. package/dist/config/sessions.js +1 -0
  173. package/dist/config/telegram-custom-commands.js +3 -2
  174. package/dist/config/types.js +2 -0
  175. package/dist/config/zod-schema.agent-defaults.js +6 -27
  176. package/dist/config/zod-schema.agent-runtime.js +171 -79
  177. package/dist/config/zod-schema.providers-core.js +138 -65
  178. package/dist/config/zod-schema.session.js +49 -22
  179. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  180. package/dist/cron/isolated-agent/run.js +224 -57
  181. package/dist/cron/normalize.js +48 -45
  182. package/dist/cron/run-log.js +14 -0
  183. package/dist/cron/service/jobs.js +190 -28
  184. package/dist/cron/service/normalize.js +29 -11
  185. package/dist/cron/service/store.js +30 -44
  186. package/dist/cron/service/timer.js +182 -96
  187. package/dist/cron/service.js +3 -0
  188. package/dist/cron/stagger.js +37 -0
  189. package/dist/daemon/inspect.js +132 -92
  190. package/dist/daemon/runtime-paths.js +25 -4
  191. package/dist/daemon/service-audit.js +47 -16
  192. package/dist/discord/accounts.js +23 -20
  193. package/dist/discord/monitor/agent-components.js +1115 -219
  194. package/dist/discord/monitor/allow-list.js +114 -34
  195. package/dist/discord/monitor/listeners.js +204 -97
  196. package/dist/discord/monitor/message-handler.js +21 -10
  197. package/dist/discord/monitor/message-handler.preflight.js +195 -101
  198. package/dist/discord/monitor/message-handler.process.js +384 -123
  199. package/dist/discord/monitor/message-utils.js +86 -23
  200. package/dist/discord/monitor/native-command.js +77 -57
  201. package/dist/discord/monitor/provider.js +122 -117
  202. package/dist/discord/monitor/reply-context.js +20 -16
  203. package/dist/discord/monitor/reply-delivery.js +40 -8
  204. package/dist/discord/monitor/rest-fetch.js +22 -0
  205. package/dist/discord/monitor/threading.js +117 -24
  206. package/dist/discord/send.js +2 -1
  207. package/dist/discord/send.outbound.js +124 -11
  208. package/dist/discord/send.shared.js +112 -72
  209. package/dist/discord/voice-message.js +3 -3
  210. package/dist/gateway/auth.js +119 -44
  211. package/dist/gateway/call.js +76 -34
  212. package/dist/gateway/channel-health-monitor.js +57 -50
  213. package/dist/gateway/client.js +63 -29
  214. package/dist/gateway/control-ui-contract.js +1 -1
  215. package/dist/gateway/gateway-config-prompts.shared.js +2 -2
  216. package/dist/gateway/net.js +109 -1
  217. package/dist/gateway/protocol/index.js +5 -8
  218. package/dist/gateway/protocol/schema/agent.js +19 -1
  219. package/dist/gateway/protocol/schema/channels.js +21 -0
  220. package/dist/gateway/protocol/schema/cron.js +43 -30
  221. package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
  222. package/dist/gateway/protocol/schema/sessions.js +5 -1
  223. package/dist/gateway/protocol/schema.js +0 -1
  224. package/dist/gateway/server/presence-events.js +12 -0
  225. package/dist/gateway/server/ws-connection/message-handler.js +203 -212
  226. package/dist/gateway/server/ws-connection.js +58 -21
  227. package/dist/gateway/server-broadcast.js +18 -13
  228. package/dist/gateway/server-cron.js +177 -10
  229. package/dist/gateway/server-methods/agent-job.js +131 -38
  230. package/dist/gateway/server-methods/send.js +60 -14
  231. package/dist/gateway/server-methods/sessions.js +160 -96
  232. package/dist/gateway/server-methods/system.js +5 -7
  233. package/dist/gateway/server-methods-list.js +8 -0
  234. package/dist/gateway/server-methods.js +24 -8
  235. package/dist/gateway/server-node-events.js +278 -68
  236. package/dist/gateway/session-utils.fs.js +316 -75
  237. package/dist/gateway/session-utils.js +224 -70
  238. package/dist/gateway/sessions-patch.js +63 -20
  239. package/dist/gateway/test-temp-config.js +1 -1
  240. package/dist/gateway/tools-invoke-http.js +118 -70
  241. package/dist/gateway/ws-log.js +135 -107
  242. package/dist/hooks/frontmatter.js +36 -82
  243. package/dist/hooks/install.js +149 -139
  244. package/dist/hooks/internal-hooks.js +29 -4
  245. package/dist/hooks/plugin-hooks.js +2 -1
  246. package/dist/imessage/monitor/deliver.js +10 -4
  247. package/dist/imessage/monitor/monitor-provider.js +138 -375
  248. package/dist/imessage/monitor/runtime.js +4 -8
  249. package/dist/imessage/send.js +65 -19
  250. package/dist/infra/exec-approvals-allowlist.js +7 -0
  251. package/dist/infra/exec-approvals.js +35 -920
  252. package/dist/infra/exec-safe-bin-trust.js +64 -0
  253. package/dist/infra/heartbeat-runner.js +207 -134
  254. package/dist/infra/heartbeat-wake.js +183 -22
  255. package/dist/infra/install-source-utils.js +47 -0
  256. package/dist/infra/net/ssrf.js +170 -36
  257. package/dist/infra/outbound/deliver.js +224 -58
  258. package/dist/infra/outbound/message-action-spec.js +12 -5
  259. package/dist/infra/outbound/outbound-session.js +27 -25
  260. package/dist/infra/poolbot-root.js +32 -22
  261. package/dist/infra/ports.js +14 -11
  262. package/dist/infra/skills-remote.js +48 -37
  263. package/dist/infra/system-events.js +25 -11
  264. package/dist/infra/system-presence.js +26 -33
  265. package/dist/infra/tmp-poolbot-dir.js +81 -2
  266. package/dist/infra/wsl.js +37 -1
  267. package/dist/line/bot-message-context.js +163 -191
  268. package/dist/logging/subsystem.js +59 -22
  269. package/dist/markdown/ir.js +124 -50
  270. package/dist/media/store.js +1 -1
  271. package/dist/media-understanding/runner.entries.js +42 -25
  272. package/dist/media-understanding/runner.js +53 -488
  273. package/dist/memory/embeddings-gemini.js +53 -38
  274. package/dist/memory/manager-embedding-ops.js +48 -69
  275. package/dist/pairing/pairing-store.js +178 -119
  276. package/dist/plugin-sdk/index.js +34 -6
  277. package/dist/plugins/hooks.js +135 -14
  278. package/dist/plugins/install.js +190 -152
  279. package/dist/polls.js +11 -0
  280. package/dist/routing/resolve-route.js +190 -56
  281. package/dist/routing/session-key.js +38 -22
  282. package/dist/runtime.js +35 -9
  283. package/dist/security/audit-channel.js +1 -1
  284. package/dist/sessions/session-key-utils.js +29 -11
  285. package/dist/shared/frontmatter.js +5 -5
  286. package/dist/shared/node-list-types.js +1 -0
  287. package/dist/shared/string-normalization.js +15 -0
  288. package/dist/signal/monitor/event-handler.js +68 -36
  289. package/dist/signal/send.js +29 -37
  290. package/dist/slack/monitor/allow-list.js +10 -11
  291. package/dist/slack/monitor/commands.js +14 -3
  292. package/dist/slack/monitor/events/interactions.js +4 -4
  293. package/dist/slack/monitor/media.js +224 -16
  294. package/dist/slack/monitor/message-handler/dispatch.js +247 -13
  295. package/dist/slack/monitor/message-handler/prepare.js +128 -45
  296. package/dist/slack/monitor/slash.js +357 -144
  297. package/dist/slack/streaming.js +77 -0
  298. package/dist/telegram/accounts.js +40 -13
  299. package/dist/telegram/allowed-updates.js +3 -0
  300. package/dist/telegram/bot/delivery.js +129 -66
  301. package/dist/telegram/bot/helpers.js +136 -122
  302. package/dist/telegram/bot-handlers.js +600 -339
  303. package/dist/telegram/bot-message-context.js +115 -73
  304. package/dist/telegram/bot-message-dispatch.js +235 -104
  305. package/dist/telegram/bot-native-command-menu.js +3 -1
  306. package/dist/telegram/bot-native-commands.js +213 -193
  307. package/dist/telegram/bot.js +24 -132
  308. package/dist/telegram/draft-stream.js +84 -75
  309. package/dist/telegram/format.js +150 -6
  310. package/dist/telegram/send.js +415 -255
  311. package/dist/telegram/targets.js +21 -2
  312. package/dist/telegram/update-offset-store.js +19 -3
  313. package/dist/terminal/restore.js +5 -2
  314. package/dist/test-utils/fetch-mock.js +5 -0
  315. package/dist/version.js +18 -5
  316. package/dist/web/auto-reply/monitor/broadcast.js +7 -3
  317. package/dist/web/auto-reply/monitor/on-message.js +6 -3
  318. package/dist/web/inbound/media.js +34 -8
  319. package/dist/web/inbound/monitor.js +34 -17
  320. package/dist/web/inbound/send-api.js +18 -17
  321. package/dist/web/outbound.js +12 -5
  322. package/dist/wizard/clack-prompter.js +40 -7
  323. package/extensions/bluebubbles/package.json +1 -1
  324. package/extensions/copilot-proxy/package.json +1 -1
  325. package/extensions/diagnostics-otel/package.json +1 -1
  326. package/extensions/discord/package.json +1 -1
  327. package/extensions/feishu/package.json +1 -1
  328. package/extensions/google-antigravity-auth/package.json +1 -1
  329. package/extensions/google-gemini-cli-auth/package.json +1 -1
  330. package/extensions/googlechat/package.json +1 -1
  331. package/extensions/imessage/package.json +1 -1
  332. package/extensions/irc/package.json +1 -1
  333. package/extensions/line/package.json +1 -1
  334. package/extensions/llm-task/package.json +1 -1
  335. package/extensions/lobster/package.json +1 -1
  336. package/extensions/matrix/CHANGELOG.md +5 -0
  337. package/extensions/matrix/package.json +1 -1
  338. package/extensions/mattermost/package.json +1 -1
  339. package/extensions/memory-core/package.json +1 -1
  340. package/extensions/memory-lancedb/package.json +1 -1
  341. package/extensions/minimax-portal-auth/package.json +1 -1
  342. package/extensions/msteams/CHANGELOG.md +5 -0
  343. package/extensions/msteams/package.json +1 -1
  344. package/extensions/nextcloud-talk/package.json +1 -1
  345. package/extensions/nostr/CHANGELOG.md +5 -0
  346. package/extensions/nostr/package.json +1 -1
  347. package/extensions/open-prose/package.json +1 -1
  348. package/extensions/openai-codex-auth/package.json +1 -1
  349. package/extensions/signal/package.json +1 -1
  350. package/extensions/slack/package.json +1 -1
  351. package/extensions/telegram/package.json +1 -1
  352. package/extensions/tlon/package.json +1 -1
  353. package/extensions/twitch/CHANGELOG.md +5 -0
  354. package/extensions/twitch/package.json +1 -1
  355. package/extensions/voice-call/CHANGELOG.md +5 -0
  356. package/extensions/voice-call/package.json +1 -1
  357. package/extensions/whatsapp/package.json +1 -1
  358. package/extensions/zalo/CHANGELOG.md +5 -0
  359. package/extensions/zalo/package.json +1 -1
  360. package/extensions/zalouser/CHANGELOG.md +5 -0
  361. package/extensions/zalouser/package.json +1 -1
  362. package/package.json +1 -1
  363. package/skills/apple-reminders/SKILL.md +100 -49
  364. package/skills/coding-agent/SKILL.md +34 -28
  365. package/skills/github/SKILL.md +131 -16
  366. package/skills/imsg/SKILL.md +112 -15
  367. package/skills/openhue/SKILL.md +101 -19
  368. package/skills/tmux/SKILL.md +111 -79
  369. package/skills/weather/SKILL.md +88 -25
@@ -27,11 +27,13 @@ async function findPackageRoot(startDir, maxDepth = 12) {
27
27
  let current = path.resolve(startDir);
28
28
  for (let i = 0; i < maxDepth; i += 1) {
29
29
  const name = await readPackageName(current);
30
- if (name && CORE_PACKAGE_NAMES.has(name))
30
+ if (name && CORE_PACKAGE_NAMES.has(name)) {
31
31
  return current;
32
+ }
32
33
  const parent = path.dirname(current);
33
- if (parent === current)
34
+ if (parent === current) {
34
35
  break;
36
+ }
35
37
  current = parent;
36
38
  }
37
39
  return null;
@@ -40,11 +42,13 @@ function findPackageRootSync(startDir, maxDepth = 12) {
40
42
  let current = path.resolve(startDir);
41
43
  for (let i = 0; i < maxDepth; i += 1) {
42
44
  const name = readPackageNameSync(current);
43
- if (name && CORE_PACKAGE_NAMES.has(name))
45
+ if (name && CORE_PACKAGE_NAMES.has(name)) {
44
46
  return current;
47
+ }
45
48
  const parent = path.dirname(current);
46
- if (parent === current)
49
+ if (parent === current) {
47
50
  break;
51
+ }
48
52
  current = parent;
49
53
  }
50
54
  return null;
@@ -52,6 +56,17 @@ function findPackageRootSync(startDir, maxDepth = 12) {
52
56
  function candidateDirsFromArgv1(argv1) {
53
57
  const normalized = path.resolve(argv1);
54
58
  const candidates = [path.dirname(normalized)];
59
+ // Resolve symlinks for version managers (nvm, fnm, n, Homebrew/Linuxbrew)
60
+ // that create symlinks in bin/ pointing to the real package location.
61
+ try {
62
+ const resolved = fsSync.realpathSync(normalized);
63
+ if (resolved !== normalized) {
64
+ candidates.push(path.dirname(resolved));
65
+ }
66
+ }
67
+ catch {
68
+ // realpathSync throws if path doesn't exist; keep original candidates
69
+ }
55
70
  const parts = normalized.split(path.sep);
56
71
  const binIndex = parts.lastIndexOf(".bin");
57
72
  if (binIndex > 0 && parts[binIndex - 1] === "node_modules") {
@@ -62,24 +77,24 @@ function candidateDirsFromArgv1(argv1) {
62
77
  return candidates;
63
78
  }
64
79
  export async function resolvePoolBotPackageRoot(opts) {
65
- const candidates = [];
66
- if (opts.moduleUrl) {
67
- candidates.push(path.dirname(fileURLToPath(opts.moduleUrl)));
68
- }
69
- if (opts.argv1) {
70
- candidates.push(...candidateDirsFromArgv1(opts.argv1));
71
- }
72
- if (opts.cwd) {
73
- candidates.push(opts.cwd);
74
- }
75
- for (const candidate of candidates) {
80
+ for (const candidate of buildCandidates(opts)) {
76
81
  const found = await findPackageRoot(candidate);
77
- if (found)
82
+ if (found) {
78
83
  return found;
84
+ }
79
85
  }
80
86
  return null;
81
87
  }
82
88
  export function resolvePoolBotPackageRootSync(opts) {
89
+ for (const candidate of buildCandidates(opts)) {
90
+ const found = findPackageRootSync(candidate);
91
+ if (found) {
92
+ return found;
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+ function buildCandidates(opts) {
83
98
  const candidates = [];
84
99
  if (opts.moduleUrl) {
85
100
  candidates.push(path.dirname(fileURLToPath(opts.moduleUrl)));
@@ -90,10 +105,5 @@ export function resolvePoolBotPackageRootSync(opts) {
90
105
  if (opts.cwd) {
91
106
  candidates.push(opts.cwd);
92
107
  }
93
- for (const candidate of candidates) {
94
- const found = findPackageRootSync(candidate);
95
- if (found)
96
- return found;
97
- }
98
- return null;
108
+ return candidates;
99
109
  }
@@ -2,6 +2,7 @@ import net from "node:net";
2
2
  import { danger, info, shouldLogVerbose, warn } from "../globals.js";
3
3
  import { logDebug } from "../logger.js";
4
4
  import { defaultRuntime } from "../runtime.js";
5
+ import { isErrno } from "./errors.js";
5
6
  import { formatPortDiagnostics } from "./ports-format.js";
6
7
  import { inspectPortUsage } from "./ports-inspect.js";
7
8
  class PortInUseError extends Error {
@@ -14,13 +15,11 @@ class PortInUseError extends Error {
14
15
  this.details = details;
15
16
  }
16
17
  }
17
- function isErrno(err) {
18
- return Boolean(err && typeof err === "object" && "code" in err);
19
- }
20
18
  export async function describePortOwner(port) {
21
19
  const diagnostics = await inspectPortUsage(port);
22
- if (diagnostics.listeners.length === 0)
20
+ if (diagnostics.listeners.length === 0) {
23
21
  return undefined;
22
+ }
24
23
  return formatPortDiagnostics(diagnostics).join("\n");
25
24
  }
26
25
  export async function ensurePortAvailable(port) {
@@ -38,8 +37,7 @@ export async function ensurePortAvailable(port) {
38
37
  }
39
38
  catch (err) {
40
39
  if (isErrno(err) && err.code === "EADDRINUSE") {
41
- const details = await describePortOwner(port);
42
- throw new PortInUseError(port, details);
40
+ throw new PortInUseError(port);
43
41
  }
44
42
  throw err;
45
43
  }
@@ -47,13 +45,15 @@ export async function ensurePortAvailable(port) {
47
45
  export async function handlePortError(err, port, context, runtime = defaultRuntime) {
48
46
  // Uniform messaging for EADDRINUSE with optional owner details.
49
47
  if (err instanceof PortInUseError || (isErrno(err) && err.code === "EADDRINUSE")) {
50
- const details = err instanceof PortInUseError ? err.details : await describePortOwner(port);
48
+ const details = err instanceof PortInUseError
49
+ ? (err.details ?? (await describePortOwner(port)))
50
+ : await describePortOwner(port);
51
51
  runtime.error(danger(`${context} failed: port ${port} is already in use.`));
52
52
  if (details) {
53
53
  runtime.error(info("Port listener details:"));
54
54
  runtime.error(details);
55
55
  if (/poolbot|src\/index\.ts|dist\/index\.js/.test(details)) {
56
- runtime.error(warn("It looks like another poolbot instance is already running. Stop it or pick a different port."));
56
+ runtime.error(warn("It looks like another Pool Bot instance is already running. Stop it or pick a different port."));
57
57
  }
58
58
  }
59
59
  runtime.error(info("Resolve by stopping the process using the port or passing --port <free-port>."));
@@ -63,12 +63,15 @@ export async function handlePortError(err, port, context, runtime = defaultRunti
63
63
  if (shouldLogVerbose()) {
64
64
  const stdout = err?.stdout;
65
65
  const stderr = err?.stderr;
66
- if (stdout?.trim())
66
+ if (stdout?.trim()) {
67
67
  logDebug(`stdout: ${stdout.trim()}`);
68
- if (stderr?.trim())
68
+ }
69
+ if (stderr?.trim()) {
69
70
  logDebug(`stderr: ${stderr.trim()}`);
71
+ }
70
72
  }
71
- return runtime.exit(1);
73
+ runtime.exit(1);
74
+ throw new Error("unreachable");
72
75
  }
73
76
  export { PortInUseError };
74
77
  export { buildPortHints, classifyPortListener, formatPortDiagnostics } from "./ports-format.js";
@@ -1,8 +1,8 @@
1
1
  import { loadWorkspaceSkillEntries } from "../agents/skills.js";
2
- import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
3
- import { listNodePairing, updatePairedNodeMetadata } from "./node-pairing.js";
4
- import { createSubsystemLogger } from "../logging/subsystem.js";
5
2
  import { bumpSkillsSnapshotVersion } from "../agents/skills/refresh.js";
3
+ import { listAgentWorkspaceDirs } from "../agents/workspace-dirs.js";
4
+ import { createSubsystemLogger } from "../logging/subsystem.js";
5
+ import { listNodePairing, updatePairedNodeMetadata } from "./node-pairing.js";
6
6
  const log = createSubsystemLogger("gateway/skills-remote");
7
7
  const remoteNodes = new Map();
8
8
  let remoteRegistry = null;
@@ -14,12 +14,15 @@ function describeNode(nodeId) {
14
14
  return ip ? `${base} @ ${ip}` : base;
15
15
  }
16
16
  function extractErrorMessage(err) {
17
- if (!err)
17
+ if (!err) {
18
18
  return undefined;
19
- if (typeof err === "string")
19
+ }
20
+ if (typeof err === "string") {
20
21
  return err;
21
- if (err instanceof Error)
22
+ }
23
+ if (err instanceof Error) {
22
24
  return err.message;
25
+ }
23
26
  if (typeof err === "object" && "message" in err && typeof err.message === "string") {
24
27
  return err.message;
25
28
  }
@@ -61,12 +64,15 @@ function isMacPlatform(platform, deviceFamily) {
61
64
  const familyNorm = String(deviceFamily ?? "")
62
65
  .trim()
63
66
  .toLowerCase();
64
- if (platformNorm.includes("mac"))
67
+ if (platformNorm.includes("mac")) {
65
68
  return true;
66
- if (platformNorm.includes("darwin"))
69
+ }
70
+ if (platformNorm.includes("darwin")) {
67
71
  return true;
68
- if (familyNorm === "mac")
72
+ }
73
+ if (familyNorm === "mac") {
69
74
  return true;
75
+ }
70
76
  return false;
71
77
  }
72
78
  function supportsSystemRun(commands) {
@@ -123,34 +129,27 @@ export function recordRemoteNodeInfo(node) {
123
129
  export function recordRemoteNodeBins(nodeId, bins) {
124
130
  upsertNode({ nodeId, bins });
125
131
  }
126
- function listWorkspaceDirs(cfg) {
127
- const dirs = new Set();
128
- const list = cfg.agents?.list;
129
- if (Array.isArray(list)) {
130
- for (const entry of list) {
131
- if (entry && typeof entry === "object" && typeof entry.id === "string") {
132
- dirs.add(resolveAgentWorkspaceDir(cfg, entry.id));
133
- }
134
- }
135
- }
136
- dirs.add(resolveAgentWorkspaceDir(cfg, resolveDefaultAgentId(cfg)));
137
- return [...dirs];
132
+ export function removeRemoteNodeInfo(nodeId) {
133
+ remoteNodes.delete(nodeId);
138
134
  }
139
135
  function collectRequiredBins(entries, targetPlatform) {
140
136
  const bins = new Set();
141
137
  for (const entry of entries) {
142
138
  const os = entry.metadata?.os ?? [];
143
- if (os.length > 0 && !os.includes(targetPlatform))
139
+ if (os.length > 0 && !os.includes(targetPlatform)) {
144
140
  continue;
141
+ }
145
142
  const required = entry.metadata?.requires?.bins ?? [];
146
143
  const anyBins = entry.metadata?.requires?.anyBins ?? [];
147
144
  for (const bin of required) {
148
- if (bin.trim())
145
+ if (bin.trim()) {
149
146
  bins.add(bin.trim());
147
+ }
150
148
  }
151
149
  for (const bin of anyBins) {
152
- if (bin.trim())
150
+ if (bin.trim()) {
153
151
  bins.add(bin.trim());
152
+ }
154
153
  }
155
154
  }
156
155
  return [...bins];
@@ -160,8 +159,9 @@ function buildBinProbeScript(bins) {
160
159
  return `for b in ${escaped}; do if command -v "$b" >/dev/null 2>&1; then echo "$b"; fi; done`;
161
160
  }
162
161
  function parseBinProbePayload(payloadJSON, payload) {
163
- if (!payloadJSON && !payload)
162
+ if (!payloadJSON && !payload) {
164
163
  return [];
164
+ }
165
165
  try {
166
166
  const parsed = payloadJSON
167
167
  ? JSON.parse(payloadJSON)
@@ -182,26 +182,32 @@ function parseBinProbePayload(payloadJSON, payload) {
182
182
  return [];
183
183
  }
184
184
  function areBinSetsEqual(a, b) {
185
- if (!a)
185
+ if (!a) {
186
186
  return false;
187
- if (a.size !== b.size)
187
+ }
188
+ if (a.size !== b.size) {
188
189
  return false;
190
+ }
189
191
  for (const bin of b) {
190
- if (!a.has(bin))
192
+ if (!a.has(bin)) {
191
193
  return false;
194
+ }
192
195
  }
193
196
  return true;
194
197
  }
195
198
  export async function refreshRemoteNodeBins(params) {
196
- if (!remoteRegistry)
199
+ if (!remoteRegistry) {
197
200
  return;
198
- if (!isMacPlatform(params.platform, params.deviceFamily))
201
+ }
202
+ if (!isMacPlatform(params.platform, params.deviceFamily)) {
199
203
  return;
204
+ }
200
205
  const canWhich = supportsSystemWhich(params.commands);
201
206
  const canRun = supportsSystemRun(params.commands);
202
- if (!canWhich && !canRun)
207
+ if (!canWhich && !canRun) {
203
208
  return;
204
- const workspaceDirs = listWorkspaceDirs(params.cfg);
209
+ }
210
+ const workspaceDirs = listAgentWorkspaceDirs(params.cfg);
205
211
  const requiredBins = new Set();
206
212
  for (const workspaceDir of workspaceDirs) {
207
213
  const entries = loadWorkspaceSkillEntries(workspaceDir, { config: params.cfg });
@@ -209,8 +215,9 @@ export async function refreshRemoteNodeBins(params) {
209
215
  requiredBins.add(bin);
210
216
  }
211
217
  }
212
- if (requiredBins.size === 0)
218
+ if (requiredBins.size === 0) {
213
219
  return;
220
+ }
214
221
  try {
215
222
  const binsList = [...requiredBins];
216
223
  const res = await remoteRegistry.invoke(canWhich
@@ -237,8 +244,9 @@ export async function refreshRemoteNodeBins(params) {
237
244
  const nextBins = new Set(bins);
238
245
  const hasChanged = !areBinSetsEqual(existingBins, nextBins);
239
246
  recordRemoteNodeBins(params.nodeId, bins);
240
- if (!hasChanged)
247
+ if (!hasChanged) {
241
248
  return;
249
+ }
242
250
  await updatePairedNodeMetadata(params.nodeId, { bins });
243
251
  bumpSkillsSnapshotVersion({ reason: "remote-node" });
244
252
  }
@@ -248,12 +256,14 @@ export async function refreshRemoteNodeBins(params) {
248
256
  }
249
257
  export function getRemoteSkillEligibility() {
250
258
  const macNodes = [...remoteNodes.values()].filter((node) => isMacPlatform(node.platform, node.deviceFamily) && supportsSystemRun(node.commands));
251
- if (macNodes.length === 0)
259
+ if (macNodes.length === 0) {
252
260
  return undefined;
261
+ }
253
262
  const bins = new Set();
254
263
  for (const node of macNodes) {
255
- for (const bin of node.bins)
264
+ for (const bin of node.bins) {
256
265
  bins.add(bin);
266
+ }
257
267
  }
258
268
  const labels = macNodes.map((node) => node.displayName ?? node.nodeId).filter(Boolean);
259
269
  const note = labels.length > 0
@@ -267,8 +277,9 @@ export function getRemoteSkillEligibility() {
267
277
  };
268
278
  }
269
279
  export async function refreshRemoteBinsForConnectedNodes(cfg) {
270
- if (!remoteRegistry)
280
+ if (!remoteRegistry) {
271
281
  return;
282
+ }
272
283
  const connected = remoteRegistry.listConnected();
273
284
  for (const node of connected) {
274
285
  await refreshRemoteNodeBins({
@@ -11,11 +11,13 @@ function requireSessionKey(key) {
11
11
  return trimmed;
12
12
  }
13
13
  function normalizeContextKey(key) {
14
- if (!key)
14
+ if (!key) {
15
15
  return null;
16
+ }
16
17
  const trimmed = key.trim();
17
- if (!trimmed)
18
+ if (!trimmed) {
18
19
  return null;
20
+ }
19
21
  return trimmed.toLowerCase();
20
22
  }
21
23
  export function isSystemEventContextChanged(sessionKey, contextKey) {
@@ -37,21 +39,30 @@ export function enqueueSystemEvent(text, options) {
37
39
  return created;
38
40
  })();
39
41
  const cleaned = text.trim();
40
- if (!cleaned)
42
+ if (!cleaned) {
43
+ return;
44
+ }
45
+ const normalizedContextKey = normalizeContextKey(options?.contextKey);
46
+ entry.lastContextKey = normalizedContextKey;
47
+ if (entry.lastText === cleaned) {
41
48
  return;
42
- entry.lastContextKey = normalizeContextKey(options?.contextKey);
43
- if (entry.lastText === cleaned)
44
- return; // skip consecutive duplicates
49
+ } // skip consecutive duplicates
45
50
  entry.lastText = cleaned;
46
- entry.queue.push({ text: cleaned, ts: Date.now() });
47
- if (entry.queue.length > MAX_EVENTS)
51
+ entry.queue.push({
52
+ text: cleaned,
53
+ ts: Date.now(),
54
+ contextKey: normalizedContextKey,
55
+ });
56
+ if (entry.queue.length > MAX_EVENTS) {
48
57
  entry.queue.shift();
58
+ }
49
59
  }
50
60
  export function drainSystemEventEntries(sessionKey) {
51
61
  const key = requireSessionKey(sessionKey);
52
62
  const entry = queues.get(key);
53
- if (!entry || entry.queue.length === 0)
63
+ if (!entry || entry.queue.length === 0) {
54
64
  return [];
65
+ }
55
66
  const out = entry.queue.slice();
56
67
  entry.queue.length = 0;
57
68
  entry.lastText = null;
@@ -62,9 +73,12 @@ export function drainSystemEventEntries(sessionKey) {
62
73
  export function drainSystemEvents(sessionKey) {
63
74
  return drainSystemEventEntries(sessionKey).map((event) => event.text);
64
75
  }
65
- export function peekSystemEvents(sessionKey) {
76
+ export function peekSystemEventEntries(sessionKey) {
66
77
  const key = requireSessionKey(sessionKey);
67
- return queues.get(key)?.queue.map((e) => e.text) ?? [];
78
+ return queues.get(key)?.queue.map((event) => ({ ...event })) ?? [];
79
+ }
80
+ export function peekSystemEvents(sessionKey) {
81
+ return peekSystemEventEntries(sessionKey).map((event) => event.text);
68
82
  }
69
83
  export function hasSystemEvents(sessionKey) {
70
84
  const key = requireSessionKey(sessionKey);
@@ -1,42 +1,27 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import os from "node:os";
3
+ import { pickPrimaryLanIPv4 } from "../gateway/net.js";
4
+ import { resolveRuntimeServiceVersion } from "../version.js";
3
5
  const entries = new Map();
4
6
  const TTL_MS = 5 * 60 * 1000; // 5 minutes
5
7
  const MAX_ENTRIES = 200;
6
8
  function normalizePresenceKey(key) {
7
- if (!key)
9
+ if (!key) {
8
10
  return undefined;
11
+ }
9
12
  const trimmed = key.trim();
10
- if (!trimmed)
13
+ if (!trimmed) {
11
14
  return undefined;
15
+ }
12
16
  return trimmed.toLowerCase();
13
17
  }
14
18
  function resolvePrimaryIPv4() {
15
- const nets = os.networkInterfaces();
16
- const prefer = ["en0", "eth0"];
17
- const pick = (names) => {
18
- for (const name of names) {
19
- const list = nets[name];
20
- const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
21
- if (entry?.address)
22
- return entry.address;
23
- }
24
- for (const list of Object.values(nets)) {
25
- const entry = list?.find((n) => n.family === "IPv4" && !n.internal);
26
- if (entry?.address)
27
- return entry.address;
28
- }
29
- return undefined;
30
- };
31
- return pick(prefer) ?? os.hostname();
19
+ return pickPrimaryLanIPv4() ?? os.hostname();
32
20
  }
33
21
  function initSelfPresence() {
34
22
  const host = os.hostname();
35
23
  const ip = resolvePrimaryIPv4() ?? undefined;
36
- const version = process.env.POOLBOT_VERSION ??
37
- process.env.CLAWDBOT_VERSION ??
38
- process.env.npm_package_version ??
39
- "unknown";
24
+ const version = resolveRuntimeServiceVersion(process.env, "unknown");
40
25
  const modelIdentifier = (() => {
41
26
  const p = os.platform();
42
27
  if (p === "darwin") {
@@ -58,20 +43,25 @@ function initSelfPresence() {
58
43
  const platform = (() => {
59
44
  const p = os.platform();
60
45
  const rel = os.release();
61
- if (p === "darwin")
46
+ if (p === "darwin") {
62
47
  return `macos ${macOSVersion()}`;
63
- if (p === "win32")
48
+ }
49
+ if (p === "win32") {
64
50
  return `windows ${rel}`;
51
+ }
65
52
  return `${p} ${rel}`;
66
53
  })();
67
54
  const deviceFamily = (() => {
68
55
  const p = os.platform();
69
- if (p === "darwin")
56
+ if (p === "darwin") {
70
57
  return "Mac";
71
- if (p === "win32")
58
+ }
59
+ if (p === "win32") {
72
60
  return "Windows";
73
- if (p === "linux")
61
+ }
62
+ if (p === "linux") {
74
63
  return "Linux";
64
+ }
75
65
  return p;
76
66
  })();
77
67
  const text = `Gateway: ${host}${ip ? ` (${ip})` : ""} · app ${version} · mode gateway · reason self`;
@@ -134,12 +124,14 @@ function parsePresence(text) {
134
124
  function mergeStringList(...values) {
135
125
  const out = new Set();
136
126
  for (const list of values) {
137
- if (!Array.isArray(list))
127
+ if (!Array.isArray(list)) {
138
128
  continue;
129
+ }
139
130
  for (const item of list) {
140
131
  const trimmed = String(item).trim();
141
- if (trimmed)
132
+ if (trimmed) {
142
133
  out.add(trimmed);
134
+ }
143
135
  }
144
136
  }
145
137
  return out.size > 0 ? [...out] : undefined;
@@ -218,17 +210,18 @@ export function listSystemPresence() {
218
210
  // prune expired
219
211
  const now = Date.now();
220
212
  for (const [k, v] of entries) {
221
- if (now - v.ts > TTL_MS)
213
+ if (now - v.ts > TTL_MS) {
222
214
  entries.delete(k);
215
+ }
223
216
  }
224
217
  // enforce max size (LRU by ts)
225
218
  if (entries.size > MAX_ENTRIES) {
226
- const sorted = [...entries.entries()].sort((a, b) => a[1].ts - b[1].ts);
219
+ const sorted = [...entries.entries()].toSorted((a, b) => a[1].ts - b[1].ts);
227
220
  const toDrop = entries.size - MAX_ENTRIES;
228
221
  for (let i = 0; i < toDrop; i++) {
229
222
  entries.delete(sorted[i][0]);
230
223
  }
231
224
  }
232
225
  touchSelfPresence();
233
- return [...entries.values()].sort((a, b) => b.ts - a.ts);
226
+ return [...entries.values()].toSorted((a, b) => b.ts - a.ts);
234
227
  }
@@ -1,2 +1,81 @@
1
- // Re-export from the canonical file (rebrand alias)
2
- export * from "./tmp-openclaw-dir.js";
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export const POSIX_POOLBOT_TMP_DIR = "/tmp/poolbot";
5
+ function isNodeErrorWithCode(err, code) {
6
+ return (typeof err === "object" &&
7
+ err !== null &&
8
+ "code" in err &&
9
+ err.code === code);
10
+ }
11
+ export function resolvePreferredPoolbotTmpDir(options = {}) {
12
+ const accessSync = options.accessSync ?? fs.accessSync;
13
+ const lstatSync = options.lstatSync ?? fs.lstatSync;
14
+ const mkdirSync = options.mkdirSync ?? fs.mkdirSync;
15
+ const getuid = options.getuid ??
16
+ (() => {
17
+ try {
18
+ return typeof process.getuid === "function" ? process.getuid() : undefined;
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ });
24
+ const tmpdir = options.tmpdir ?? os.tmpdir;
25
+ const uid = getuid();
26
+ const isSecureDirForUser = (st) => {
27
+ if (uid === undefined) {
28
+ return true;
29
+ }
30
+ if (typeof st.uid === "number" && st.uid !== uid) {
31
+ return false;
32
+ }
33
+ // Avoid group/other writable dirs when running on multi-user hosts.
34
+ if (typeof st.mode === "number" && (st.mode & 0o022) !== 0) {
35
+ return false;
36
+ }
37
+ return true;
38
+ };
39
+ const fallback = () => {
40
+ const base = tmpdir();
41
+ const suffix = uid === undefined ? "poolbot" : `poolbot-${uid}`;
42
+ return path.join(base, suffix);
43
+ };
44
+ try {
45
+ const preferred = lstatSync(POSIX_POOLBOT_TMP_DIR);
46
+ if (!preferred.isDirectory() || preferred.isSymbolicLink()) {
47
+ return fallback();
48
+ }
49
+ accessSync(POSIX_POOLBOT_TMP_DIR, fs.constants.W_OK | fs.constants.X_OK);
50
+ if (!isSecureDirForUser(preferred)) {
51
+ return fallback();
52
+ }
53
+ return POSIX_POOLBOT_TMP_DIR;
54
+ }
55
+ catch (err) {
56
+ if (!isNodeErrorWithCode(err, "ENOENT")) {
57
+ return fallback();
58
+ }
59
+ }
60
+ try {
61
+ accessSync("/tmp", fs.constants.W_OK | fs.constants.X_OK);
62
+ // Create with a safe default; subsequent callers expect it exists.
63
+ mkdirSync(POSIX_POOLBOT_TMP_DIR, { recursive: true, mode: 0o700 });
64
+ try {
65
+ const preferred = lstatSync(POSIX_POOLBOT_TMP_DIR);
66
+ if (!preferred.isDirectory() || preferred.isSymbolicLink()) {
67
+ return fallback();
68
+ }
69
+ if (!isSecureDirForUser(preferred)) {
70
+ return fallback();
71
+ }
72
+ }
73
+ catch {
74
+ return fallback();
75
+ }
76
+ return POSIX_POOLBOT_TMP_DIR;
77
+ }
78
+ catch {
79
+ return fallback();
80
+ }
81
+ }
package/dist/infra/wsl.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import fs from "node:fs/promises";
2
3
  let wslCached = null;
3
4
  export function isWSLEnv() {
@@ -6,9 +7,44 @@ export function isWSLEnv() {
6
7
  }
7
8
  return false;
8
9
  }
10
+ /**
11
+ * Synchronously check if running in WSL.
12
+ * Checks env vars first, then /proc/version.
13
+ */
14
+ export function isWSLSync() {
15
+ if (process.platform !== "linux") {
16
+ return false;
17
+ }
18
+ if (isWSLEnv()) {
19
+ return true;
20
+ }
21
+ try {
22
+ const release = readFileSync("/proc/version", "utf8").toLowerCase();
23
+ return release.includes("microsoft") || release.includes("wsl");
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * Synchronously check if running in WSL2.
31
+ */
32
+ export function isWSL2Sync() {
33
+ if (!isWSLSync()) {
34
+ return false;
35
+ }
36
+ try {
37
+ const version = readFileSync("/proc/version", "utf8").toLowerCase();
38
+ return version.includes("wsl2") || version.includes("microsoft-standard");
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
9
44
  export async function isWSL() {
10
- if (wslCached !== null)
45
+ if (wslCached !== null) {
11
46
  return wslCached;
47
+ }
12
48
  if (isWSLEnv()) {
13
49
  wslCached = true;
14
50
  return wslCached;