@poolzin/pool-bot 2026.2.11 → 2026.2.18

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 (535) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/agents/agent-scope.js +4 -0
  3. package/dist/agents/announce-idempotency.js +14 -0
  4. package/dist/agents/auth-profiles/usage.js +22 -0
  5. package/dist/agents/auth-profiles.js +1 -1
  6. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
  7. package/dist/agents/bash-tools.exec-runtime.js +438 -0
  8. package/dist/agents/bash-tools.shared.js +6 -0
  9. package/dist/agents/cli-runner/reliability.js +61 -0
  10. package/dist/agents/cli-watchdog-defaults.js +11 -0
  11. package/dist/agents/command-poll-backoff.js +63 -0
  12. package/dist/agents/current-time.js +16 -0
  13. package/dist/agents/glob-pattern.js +42 -0
  14. package/dist/agents/memory-search.js +33 -0
  15. package/dist/agents/model-alias-lines.js +18 -0
  16. package/dist/agents/model-auth-label.js +61 -0
  17. package/dist/agents/model-fallback.js +59 -8
  18. package/dist/agents/models-config.e2e-harness.js +115 -0
  19. package/dist/agents/ollama-stream.js +11 -3
  20. package/dist/agents/openclaw-tools.js +135 -0
  21. package/dist/agents/pi-auth-json.js +118 -0
  22. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
  23. package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
  24. package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
  25. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
  26. package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
  27. package/dist/agents/pi-tools.before-tool-call.js +145 -4
  28. package/dist/agents/pi-tools.js +29 -9
  29. package/dist/agents/pi-tools.policy.js +85 -92
  30. package/dist/agents/pi-tools.schema.js +54 -27
  31. package/dist/agents/queued-file-writer.js +22 -0
  32. package/dist/agents/sandbox/docker.js +133 -40
  33. package/dist/agents/sandbox/fs-bridge.js +146 -0
  34. package/dist/agents/sandbox/fs-paths.js +205 -0
  35. package/dist/agents/sandbox/hash.js +4 -0
  36. package/dist/agents/sandbox/validate-sandbox-security.js +157 -0
  37. package/dist/agents/sandbox-paths.js +3 -0
  38. package/dist/agents/sandbox-tool-policy.js +26 -0
  39. package/dist/agents/sanitize-for-prompt.js +18 -0
  40. package/dist/agents/session-dirs.js +20 -0
  41. package/dist/agents/session-write-lock.js +203 -39
  42. package/dist/agents/skills/filter.js +24 -0
  43. package/dist/agents/skills/tools-dir.js +9 -0
  44. package/dist/agents/skills-install-download.js +290 -0
  45. package/dist/agents/skills-install-output.js +30 -0
  46. package/dist/agents/skills-install.download-test-utils.js +36 -0
  47. package/dist/agents/skills.e2e-test-helpers.js +13 -0
  48. package/dist/agents/subagent-announce-queue.js +59 -15
  49. package/dist/agents/subagent-depth.js +137 -0
  50. package/dist/agents/subagent-registry.js +448 -96
  51. package/dist/agents/subagent-spawn.js +262 -0
  52. package/dist/agents/system-prompt.js +52 -10
  53. package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
  54. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
  55. package/dist/agents/tool-display-common.js +782 -0
  56. package/dist/agents/tool-loop-detection.js +466 -0
  57. package/dist/agents/tool-policy.js +6 -0
  58. package/dist/agents/tools/image-tool.js +1 -1
  59. package/dist/agents/tools/sessions-access.js +178 -0
  60. package/dist/agents/tools/sessions-resolution.js +206 -0
  61. package/dist/agents/tools/subagents-tool.js +616 -0
  62. package/dist/agents/workspace-dir.js +18 -0
  63. package/dist/agents/workspace-dirs.js +14 -0
  64. package/dist/agents/workspace.js +70 -0
  65. package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
  66. package/dist/auto-reply/reply/commands-export-session.js +163 -0
  67. package/dist/auto-reply/reply/commands-mesh.js +245 -0
  68. package/dist/auto-reply/reply/commands-setunset.js +28 -0
  69. package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
  70. package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
  71. package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
  72. package/dist/auto-reply/reply/directive-handling.params.js +1 -0
  73. package/dist/auto-reply/reply/directive-parsing.js +36 -0
  74. package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
  75. package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
  76. package/dist/auto-reply/reply/post-compaction-audit.js +96 -0
  77. package/dist/auto-reply/reply/post-compaction-context.js +98 -0
  78. package/dist/auto-reply/reply/reply-delivery.js +92 -0
  79. package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
  80. package/dist/auto-reply/reply/session-run-accounting.js +33 -0
  81. package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
  82. package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
  83. package/dist/browser/bridge-auth-registry.js +26 -0
  84. package/dist/browser/client-actions-url.js +10 -0
  85. package/dist/browser/control-auth.js +73 -0
  86. package/dist/browser/csrf.js +64 -0
  87. package/dist/browser/http-auth.js +52 -0
  88. package/dist/browser/paths.js +37 -0
  89. package/dist/browser/proxy-files.js +32 -0
  90. package/dist/browser/pw-ai-state.js +7 -0
  91. package/dist/browser/resolved-config-refresh.js +42 -0
  92. package/dist/browser/routes/path-output.js +1 -0
  93. package/dist/browser/server-context.chrome-test-harness.js +20 -0
  94. package/dist/browser/server-middleware.js +31 -0
  95. package/dist/browser/test-port.js +16 -0
  96. package/dist/build-info.json +3 -3
  97. package/dist/canvas-host/file-resolver.js +43 -0
  98. package/dist/channels/account-summary.js +19 -0
  99. package/dist/channels/draft-stream-loop.js +77 -0
  100. package/dist/channels/plugins/account-helpers.js +26 -0
  101. package/dist/channels/telegram/allow-from.js +10 -0
  102. package/dist/cli/browser-cli-resize.js +22 -0
  103. package/dist/cli/browser-cli-shared.js +8 -0
  104. package/dist/cli/clawbot-cli.js +5 -0
  105. package/dist/cli/completion-cli.js +566 -0
  106. package/dist/cli/config-cli.js +63 -5
  107. package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
  108. package/dist/cli/daemon-cli/register-service-commands.js +60 -0
  109. package/dist/cli/daemon-cli-compat.js +80 -0
  110. package/dist/cli/nodes-cli/pairing-render.js +26 -0
  111. package/dist/cli/program/action-reparse.js +17 -0
  112. package/dist/cli/program/command-registry.js +17 -0
  113. package/dist/cli/program/program-context.js +8 -0
  114. package/dist/cli/program/register.subclis.js +7 -0
  115. package/dist/cli/program/routes.js +233 -0
  116. package/dist/cli/qr-cli.js +132 -0
  117. package/dist/cli/requirements-test-fixtures.js +17 -0
  118. package/dist/cli/respawn-policy.js +4 -0
  119. package/dist/cli/shared/parse-port.js +18 -0
  120. package/dist/cli/skills-cli.format.js +241 -0
  121. package/dist/cli/update-cli/progress.js +121 -0
  122. package/dist/cli/update-cli/restart-helper.js +108 -0
  123. package/dist/cli/update-cli/shared.js +196 -0
  124. package/dist/cli/update-cli/status.js +97 -0
  125. package/dist/cli/update-cli/suppress-deprecations.js +17 -0
  126. package/dist/cli/update-cli/update-command.js +506 -0
  127. package/dist/cli/update-cli/wizard.js +130 -0
  128. package/dist/cli/update-cli.js +3 -9
  129. package/dist/cli/windows-argv.js +69 -0
  130. package/dist/commands/auth-choice-legacy.js +20 -0
  131. package/dist/commands/auth-choice.apply-helpers.js +8 -0
  132. package/dist/commands/channel-test-helpers.js +19 -0
  133. package/dist/commands/cleanup-plan.js +10 -0
  134. package/dist/commands/cleanup-utils.js +7 -0
  135. package/dist/commands/config-validation.js +15 -0
  136. package/dist/commands/doctor-completion.js +112 -0
  137. package/dist/commands/doctor-memory-search.js +119 -0
  138. package/dist/commands/doctor-session-locks.js +73 -0
  139. package/dist/commands/doctor.e2e-harness.js +364 -0
  140. package/dist/commands/gateway-presence.js +19 -0
  141. package/dist/commands/model-default.js +35 -0
  142. package/dist/commands/models/fallbacks-shared.js +102 -0
  143. package/dist/commands/models/shared.js +24 -0
  144. package/dist/commands/onboard-auth.config-gateways.js +64 -0
  145. package/dist/commands/onboard-auth.config-litellm.js +45 -0
  146. package/dist/commands/onboard-auth.config-shared.js +116 -0
  147. package/dist/commands/onboard-config.js +16 -0
  148. package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
  149. package/dist/commands/onboard-provider-auth-flags.js +136 -0
  150. package/dist/commands/openai-codex-oauth.js +40 -0
  151. package/dist/commands/test-runtime-config-helpers.js +21 -0
  152. package/dist/commands/test-wizard-helpers.js +68 -0
  153. package/dist/commands/vllm-setup.js +66 -0
  154. package/dist/compat/legacy-names.js +2 -0
  155. package/dist/config/backup-rotation.js +19 -0
  156. package/dist/config/env-preserve.js +122 -0
  157. package/dist/config/includes-scan.js +78 -0
  158. package/dist/config/plugins-allowlist.js +13 -0
  159. package/dist/config/schema.help.js +256 -0
  160. package/dist/config/schema.hints.js +189 -0
  161. package/dist/config/schema.irc.js +20 -0
  162. package/dist/config/schema.labels.js +317 -0
  163. package/dist/config/sessions/delivery-info.js +40 -0
  164. package/dist/config/types.irc.js +1 -0
  165. package/dist/config/zod-schema.agent-defaults.js +14 -0
  166. package/dist/config/zod-schema.agent-model.js +10 -0
  167. package/dist/config/zod-schema.agent-runtime.js +14 -0
  168. package/dist/config/zod-schema.allowdeny.js +35 -0
  169. package/dist/config/zod-schema.sensitive.js +4 -0
  170. package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
  171. package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
  172. package/dist/cron/isolated-agent/subagent-followup.js +127 -0
  173. package/dist/cron/isolated-agent.mocks.js +12 -0
  174. package/dist/cron/isolated-agent.test-setup.js +22 -0
  175. package/dist/cron/legacy-delivery.js +43 -0
  176. package/dist/cron/webhook-url.js +22 -0
  177. package/dist/daemon/arg-split.js +40 -0
  178. package/dist/daemon/exec-file.js +23 -0
  179. package/dist/daemon/output.js +6 -0
  180. package/dist/daemon/runtime-format.js +31 -0
  181. package/dist/daemon/schtasks-exec.js +4 -0
  182. package/dist/daemon/service-audit.js +22 -0
  183. package/dist/discord/client.js +41 -0
  184. package/dist/discord/components-registry.js +57 -0
  185. package/dist/discord/components.js +816 -0
  186. package/dist/discord/guilds.js +12 -0
  187. package/dist/discord/monitor/gateway-plugin.js +48 -0
  188. package/dist/discord/monitor/presence.js +30 -0
  189. package/dist/discord/send.components.js +115 -0
  190. package/dist/discord/send.shared.js +4 -0
  191. package/dist/discord/ui.js +26 -0
  192. package/dist/discord/voice-message.js +254 -0
  193. package/dist/gateway/agent-event-assistant-text.js +5 -0
  194. package/dist/gateway/agent-prompt.js +33 -0
  195. package/dist/gateway/auth-rate-limit.js +136 -0
  196. package/dist/gateway/channel-health-monitor.js +114 -0
  197. package/dist/gateway/control-ui-contract.js +1 -0
  198. package/dist/gateway/control-ui-csp.js +15 -0
  199. package/dist/gateway/gateway-config-prompts.shared.js +25 -0
  200. package/dist/gateway/http-auth-helpers.js +18 -0
  201. package/dist/gateway/http-common.js +18 -0
  202. package/dist/gateway/http-endpoint-helpers.js +27 -0
  203. package/dist/gateway/node-invoke-sanitize.js +11 -0
  204. package/dist/gateway/node-invoke-system-run-approval.js +205 -0
  205. package/dist/gateway/probe-auth.js +21 -0
  206. package/dist/gateway/protocol/index.js +7 -2
  207. package/dist/gateway/protocol/schema/mesh.js +54 -0
  208. package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
  209. package/dist/gateway/protocol/schema.js +1 -0
  210. package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
  211. package/dist/gateway/server-channels.js +11 -0
  212. package/dist/gateway/server-methods/attachment-normalize.js +16 -0
  213. package/dist/gateway/server-methods/base-hash.js +8 -0
  214. package/dist/gateway/server-methods/mesh.js +700 -0
  215. package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
  216. package/dist/gateway/server-methods/restart-request.js +13 -0
  217. package/dist/gateway/server-methods/validation.js +8 -0
  218. package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
  219. package/dist/gateway/server.e2e-registry-helpers.js +1 -0
  220. package/dist/gateway/server.e2e-ws-harness.js +20 -0
  221. package/dist/gateway/test-helpers.js +2 -0
  222. package/dist/gateway/test-helpers.server.js +3 -1
  223. package/dist/gateway/test-http-response.js +12 -0
  224. package/dist/gateway/test-openai-responses-model.js +20 -0
  225. package/dist/gateway/test-temp-config.js +30 -0
  226. package/dist/gateway/test-with-server.js +32 -0
  227. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
  228. package/dist/imessage/monitor/abort-handler.js +23 -0
  229. package/dist/imessage/monitor/inbound-processing.js +346 -0
  230. package/dist/imessage/monitor/parse-notification.js +64 -0
  231. package/dist/imessage/target-parsing-helpers.js +92 -0
  232. package/dist/infra/archive.js +244 -20
  233. package/dist/infra/detect-package-manager.js +26 -0
  234. package/dist/infra/exec-approvals-allowlist.js +257 -0
  235. package/dist/infra/exec-approvals-analysis.js +770 -0
  236. package/dist/infra/exec-approvals.js +13 -0
  237. package/dist/infra/file-lock.js +1 -0
  238. package/dist/infra/gemini-auth.js +39 -0
  239. package/dist/infra/heartbeat-active-hours.js +85 -0
  240. package/dist/infra/heartbeat-events-filter.js +50 -0
  241. package/dist/infra/heartbeat-runner.test-utils.js +39 -0
  242. package/dist/infra/http-body.js +265 -0
  243. package/dist/infra/install-package-dir.js +50 -0
  244. package/dist/infra/install-safe-path.js +49 -0
  245. package/dist/infra/json-files.js +49 -0
  246. package/dist/infra/jsonl-socket.js +52 -0
  247. package/dist/infra/map-size.js +14 -0
  248. package/dist/infra/net/hostname.js +7 -0
  249. package/dist/infra/npm-registry-spec.js +39 -0
  250. package/dist/infra/openclaw-root.js +109 -0
  251. package/dist/infra/outbound/delivery-queue.js +214 -0
  252. package/dist/infra/outbound/identity.js +23 -0
  253. package/dist/infra/outbound/message-action-params.js +307 -0
  254. package/dist/infra/outbound/tool-payload.js +21 -0
  255. package/dist/infra/package-json.js +23 -0
  256. package/dist/infra/pairing-files.js +19 -0
  257. package/dist/infra/pairing-token.js +9 -0
  258. package/dist/infra/path-prepend.js +51 -0
  259. package/dist/infra/path-safety.js +16 -0
  260. package/dist/infra/process-respawn.js +49 -0
  261. package/dist/infra/runtime-status.js +16 -0
  262. package/dist/infra/session-cost-usage.types.js +1 -0
  263. package/dist/infra/session-maintenance-warning.js +89 -0
  264. package/dist/infra/system-run-command.js +78 -0
  265. package/dist/infra/tmp-openclaw-dir.js +81 -0
  266. package/dist/infra/tmp-poolbot-dir.js +2 -0
  267. package/dist/infra/update-channels.js +19 -0
  268. package/dist/line/actions.js +45 -0
  269. package/dist/line/channel-access-token.js +9 -0
  270. package/dist/line/flex-templates/basic-cards.js +332 -0
  271. package/dist/line/flex-templates/common.js +18 -0
  272. package/dist/line/flex-templates/media-control-cards.js +453 -0
  273. package/dist/line/flex-templates/message.js +10 -0
  274. package/dist/line/flex-templates/schedule-cards.js +399 -0
  275. package/dist/line/flex-templates/types.js +1 -0
  276. package/dist/line/webhook-node.js +100 -0
  277. package/dist/line/webhook-utils.js +11 -0
  278. package/dist/logging/diagnostic-session-state.js +73 -0
  279. package/dist/logging/diagnostic.js +22 -0
  280. package/dist/logging/timestamps.js +14 -0
  281. package/dist/markdown/whatsapp.js +62 -0
  282. package/dist/media/base64.js +34 -0
  283. package/dist/media/local-roots.js +32 -0
  284. package/dist/media/outbound-attachment.js +10 -0
  285. package/dist/media/read-response-with-limit.js +41 -0
  286. package/dist/media/sniff-mime-from-base64.js +19 -0
  287. package/dist/media-understanding/audio-preflight.js +67 -0
  288. package/dist/media-understanding/fs.js +13 -0
  289. package/dist/media-understanding/output-extract.js +26 -0
  290. package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
  291. package/dist/media-understanding/providers/google/inline-data.js +64 -0
  292. package/dist/media-understanding/providers/shared.js +7 -0
  293. package/dist/media-understanding/runner.entries.js +459 -0
  294. package/dist/memory/batch-error-utils.js +11 -0
  295. package/dist/memory/batch-http.js +27 -0
  296. package/dist/memory/batch-output.js +29 -0
  297. package/dist/memory/batch-runner.js +22 -0
  298. package/dist/memory/batch-upload.js +23 -0
  299. package/dist/memory/batch-utils.js +26 -0
  300. package/dist/memory/embeddings-debug.js +11 -0
  301. package/dist/memory/embeddings-remote-client.js +22 -0
  302. package/dist/memory/embeddings-remote-fetch.js +14 -0
  303. package/dist/memory/embeddings.js +36 -9
  304. package/dist/memory/hybrid.js +24 -5
  305. package/dist/memory/manager-embedding-ops.js +616 -0
  306. package/dist/memory/manager-sync-ops.js +953 -0
  307. package/dist/memory/manager.js +76 -28
  308. package/dist/memory/mmr.js +164 -0
  309. package/dist/memory/qmd-manager.js +1061 -0
  310. package/dist/memory/qmd-query-parser.js +107 -0
  311. package/dist/memory/qmd-scope.js +93 -0
  312. package/dist/memory/query-expansion.js +331 -0
  313. package/dist/memory/search-manager.js +0 -1
  314. package/dist/memory/sync-index.js +21 -0
  315. package/dist/memory/sync-progress.js +22 -0
  316. package/dist/memory/sync-stale.js +30 -0
  317. package/dist/memory/temporal-decay.js +119 -0
  318. package/dist/memory/test-embeddings-mock.js +16 -0
  319. package/dist/memory/test-manager-helpers.js +14 -0
  320. package/dist/memory/test-runtime-mocks.js +11 -0
  321. package/dist/node-host/invoke-browser.js +177 -0
  322. package/dist/node-host/invoke.js +685 -0
  323. package/dist/pairing/setup-code.js +285 -0
  324. package/dist/plugin-sdk/account-id.js +1 -0
  325. package/dist/plugin-sdk/agent-media-payload.js +13 -0
  326. package/dist/plugin-sdk/allow-from.js +47 -0
  327. package/dist/plugin-sdk/command-auth.js +23 -0
  328. package/dist/plugin-sdk/config-paths.js +9 -0
  329. package/dist/plugin-sdk/file-lock.js +116 -0
  330. package/dist/plugin-sdk/json-store.js +31 -0
  331. package/dist/plugin-sdk/onboarding.js +28 -0
  332. package/dist/plugin-sdk/provider-auth-result.js +29 -0
  333. package/dist/plugin-sdk/slack-message-actions.js +133 -0
  334. package/dist/plugin-sdk/status-helpers.js +35 -0
  335. package/dist/plugin-sdk/text-chunking.js +31 -0
  336. package/dist/plugin-sdk/tool-send.js +12 -0
  337. package/dist/plugin-sdk/webhook-path.js +27 -0
  338. package/dist/plugin-sdk/webhook-targets.js +34 -0
  339. package/dist/plugins/hooks.test-helpers.js +21 -0
  340. package/dist/plugins/uninstall.js +171 -0
  341. package/dist/process/kill-tree.js +98 -0
  342. package/dist/process/supervisor/adapters/child.js +143 -0
  343. package/dist/process/supervisor/adapters/env.js +13 -0
  344. package/dist/process/supervisor/adapters/pty.js +148 -0
  345. package/dist/process/supervisor/index.js +10 -0
  346. package/dist/process/supervisor/registry.js +117 -0
  347. package/dist/process/supervisor/supervisor.js +244 -0
  348. package/dist/process/supervisor/types.js +1 -0
  349. package/dist/providers/google-shared.test-helpers.js +75 -0
  350. package/dist/security/audit-channel.js +419 -0
  351. package/dist/security/audit-tool-policy.js +1 -0
  352. package/dist/security/scan-paths.js +12 -0
  353. package/dist/sessions/input-provenance.js +55 -0
  354. package/dist/sessions/session-key-utils.js +7 -0
  355. package/dist/shared/chat-content.js +31 -0
  356. package/dist/shared/chat-envelope.js +45 -0
  357. package/dist/shared/config-eval.js +117 -0
  358. package/dist/shared/device-auth.js +16 -0
  359. package/dist/shared/entry-metadata.js +9 -0
  360. package/dist/shared/entry-status.js +25 -0
  361. package/dist/shared/frontmatter.js +98 -0
  362. package/dist/shared/model-param-b.js +19 -0
  363. package/dist/shared/net/ipv4.js +17 -0
  364. package/dist/shared/node-match.js +53 -0
  365. package/dist/shared/pid-alive.js +12 -0
  366. package/dist/shared/process-scoped-map.js +10 -0
  367. package/dist/shared/requirements.js +128 -0
  368. package/dist/shared/subagents-format.js +84 -0
  369. package/dist/shared/usage-aggregates.js +28 -0
  370. package/dist/signal/monitor/mentions.js +45 -0
  371. package/dist/signal/rpc-context.js +19 -0
  372. package/dist/slack/blocks-fallback.js +76 -0
  373. package/dist/slack/blocks-input.js +40 -0
  374. package/dist/slack/draft-stream.js +106 -0
  375. package/dist/slack/message-actions.js +51 -0
  376. package/dist/slack/modal-metadata.js +32 -0
  377. package/dist/slack/monitor/events/interactions.js +462 -0
  378. package/dist/slack/monitor/room-context.js +17 -0
  379. package/dist/slack/stream-mode.js +41 -0
  380. package/dist/telegram/bot-native-command-menu.js +64 -0
  381. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  382. package/dist/telegram/button-types.js +1 -0
  383. package/dist/telegram/group-access.js +65 -0
  384. package/dist/telegram/outbound-params.js +21 -0
  385. package/dist/telegram/poll-vote-cache.js +21 -0
  386. package/dist/terminal/health-style.js +36 -0
  387. package/dist/test-utils/chunk-test-helpers.js +21 -0
  388. package/dist/test-utils/env.js +72 -0
  389. package/dist/test-utils/exec-assertions.js +12 -0
  390. package/dist/test-utils/imessage-test-plugin.js +54 -0
  391. package/dist/test-utils/mock-http-response.js +17 -0
  392. package/dist/test-utils/vitest-mock-fn.js +1 -0
  393. package/dist/tts/tts-core.js +550 -0
  394. package/dist/utils/chunk-items.js +10 -0
  395. package/dist/utils/reaction-level.js +52 -0
  396. package/dist/utils/safe-json.js +22 -0
  397. package/dist/utils/with-timeout.js +14 -0
  398. package/dist/web/media.js +17 -5
  399. package/dist/whatsapp/resolve-outbound-target.js +42 -0
  400. package/dist/wizard/onboarding.completion.js +74 -0
  401. package/extensions/bluebubbles/package.json +1 -1
  402. package/extensions/bluebubbles/src/account-resolve.ts +29 -0
  403. package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
  404. package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
  405. package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
  406. package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
  407. package/extensions/bluebubbles/src/multipart.ts +32 -0
  408. package/extensions/bluebubbles/src/send-helpers.ts +53 -0
  409. package/extensions/bluebubbles/src/test-harness.ts +50 -0
  410. package/extensions/bluebubbles/src/test-mocks.ts +11 -0
  411. package/extensions/copilot-proxy/package.json +1 -1
  412. package/extensions/device-pair/index.ts +554 -0
  413. package/extensions/diagnostics-otel/package.json +1 -1
  414. package/extensions/discord/package.json +1 -1
  415. package/extensions/discord/src/channel.js +366 -0
  416. package/extensions/discord/src/runtime.js +10 -0
  417. package/extensions/feishu/index.ts +63 -0
  418. package/extensions/feishu/src/accounts.ts +114 -0
  419. package/extensions/feishu/src/bitable.ts +739 -0
  420. package/extensions/feishu/src/bot.ts +965 -0
  421. package/extensions/feishu/src/channel.ts +351 -0
  422. package/extensions/feishu/src/client.ts +118 -0
  423. package/extensions/feishu/src/config-schema.ts +206 -0
  424. package/extensions/feishu/src/dedup.ts +33 -0
  425. package/extensions/feishu/src/directory.ts +177 -0
  426. package/extensions/feishu/src/doc-schema.ts +47 -0
  427. package/extensions/feishu/src/docx.ts +536 -0
  428. package/extensions/feishu/src/drive-schema.ts +46 -0
  429. package/extensions/feishu/src/drive.ts +227 -0
  430. package/extensions/feishu/src/dynamic-agent.ts +131 -0
  431. package/extensions/feishu/src/media.ts +449 -0
  432. package/extensions/feishu/src/mention.ts +126 -0
  433. package/extensions/feishu/src/monitor.ts +330 -0
  434. package/extensions/feishu/src/onboarding.ts +359 -0
  435. package/extensions/feishu/src/outbound.ts +55 -0
  436. package/extensions/feishu/src/perm-schema.ts +52 -0
  437. package/extensions/feishu/src/perm.ts +173 -0
  438. package/extensions/feishu/src/policy.ts +84 -0
  439. package/extensions/feishu/src/probe.ts +44 -0
  440. package/extensions/feishu/src/reactions.ts +160 -0
  441. package/extensions/feishu/src/reply-dispatcher.ts +239 -0
  442. package/extensions/feishu/src/runtime.ts +14 -0
  443. package/extensions/feishu/src/send-result.ts +29 -0
  444. package/extensions/feishu/src/send.ts +335 -0
  445. package/extensions/feishu/src/streaming-card.ts +223 -0
  446. package/extensions/feishu/src/targets.ts +78 -0
  447. package/extensions/feishu/src/tools-config.ts +21 -0
  448. package/extensions/feishu/src/types.ts +81 -0
  449. package/extensions/feishu/src/typing.ts +80 -0
  450. package/extensions/feishu/src/wiki-schema.ts +55 -0
  451. package/extensions/feishu/src/wiki.ts +232 -0
  452. package/extensions/google-antigravity-auth/package.json +1 -1
  453. package/extensions/google-gemini-cli-auth/package.json +1 -1
  454. package/extensions/googlechat/package.json +1 -1
  455. package/extensions/imessage/package.json +1 -1
  456. package/extensions/imessage/src/channel.js +253 -0
  457. package/extensions/imessage/src/runtime.js +10 -0
  458. package/extensions/irc/index.ts +17 -0
  459. package/extensions/irc/src/accounts.ts +268 -0
  460. package/extensions/irc/src/channel.ts +367 -0
  461. package/extensions/irc/src/client.ts +439 -0
  462. package/extensions/irc/src/config-schema.ts +97 -0
  463. package/extensions/irc/src/connect-options.ts +30 -0
  464. package/extensions/irc/src/control-chars.ts +22 -0
  465. package/extensions/irc/src/inbound.ts +334 -0
  466. package/extensions/irc/src/monitor.ts +147 -0
  467. package/extensions/irc/src/normalize.ts +117 -0
  468. package/extensions/irc/src/onboarding.ts +479 -0
  469. package/extensions/irc/src/policy.ts +157 -0
  470. package/extensions/irc/src/probe.ts +53 -0
  471. package/extensions/irc/src/protocol.ts +169 -0
  472. package/extensions/irc/src/runtime.ts +14 -0
  473. package/extensions/irc/src/send.ts +88 -0
  474. package/extensions/irc/src/types.ts +93 -0
  475. package/extensions/line/package.json +1 -1
  476. package/extensions/llm-task/package.json +1 -1
  477. package/extensions/lobster/package.json +1 -1
  478. package/extensions/matrix/CHANGELOG.md +5 -0
  479. package/extensions/matrix/package.json +1 -1
  480. package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
  481. package/extensions/mattermost/package.json +1 -1
  482. package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
  483. package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
  484. package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
  485. package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
  486. package/extensions/memory-core/package.json +1 -1
  487. package/extensions/memory-lancedb/package.json +1 -1
  488. package/extensions/minimax-portal-auth/index.ts +161 -0
  489. package/extensions/minimax-portal-auth/oauth.ts +247 -0
  490. package/extensions/msteams/CHANGELOG.md +5 -0
  491. package/extensions/msteams/package.json +1 -1
  492. package/extensions/msteams/src/file-lock.ts +1 -0
  493. package/extensions/msteams/src/graph.ts +92 -0
  494. package/extensions/msteams/src/mentions.ts +114 -0
  495. package/extensions/msteams/src/test-runtime.ts +16 -0
  496. package/extensions/nextcloud-talk/package.json +1 -1
  497. package/extensions/nostr/CHANGELOG.md +5 -0
  498. package/extensions/nostr/package.json +1 -1
  499. package/extensions/open-prose/package.json +1 -1
  500. package/extensions/openai-codex-auth/index.ts +177 -0
  501. package/extensions/phone-control/index.ts +421 -0
  502. package/extensions/shared/resolve-target-test-helpers.ts +66 -0
  503. package/extensions/signal/package.json +1 -1
  504. package/extensions/signal/src/channel.js +273 -0
  505. package/extensions/signal/src/runtime.js +10 -0
  506. package/extensions/slack/package.json +1 -1
  507. package/extensions/slack/src/channel.js +489 -0
  508. package/extensions/slack/src/runtime.js +10 -0
  509. package/extensions/talk-voice/index.ts +150 -0
  510. package/extensions/telegram/package.json +1 -1
  511. package/extensions/telegram/src/channel.js +424 -0
  512. package/extensions/telegram/src/runtime.js +10 -0
  513. package/extensions/thread-ownership/index.ts +133 -0
  514. package/extensions/tlon/package.json +1 -1
  515. package/extensions/tlon/src/account-fields.ts +25 -0
  516. package/extensions/tlon/src/urbit/base-url.ts +57 -0
  517. package/extensions/tlon/src/urbit/channel-client.ts +157 -0
  518. package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
  519. package/extensions/tlon/src/urbit/context.ts +47 -0
  520. package/extensions/tlon/src/urbit/errors.ts +51 -0
  521. package/extensions/tlon/src/urbit/fetch.ts +39 -0
  522. package/extensions/twitch/CHANGELOG.md +5 -0
  523. package/extensions/twitch/package.json +1 -1
  524. package/extensions/twitch/src/test-fixtures.ts +30 -0
  525. package/extensions/voice-call/CHANGELOG.md +5 -0
  526. package/extensions/voice-call/package.json +1 -1
  527. package/extensions/voice-call/src/allowlist.ts +19 -0
  528. package/extensions/whatsapp/package.json +1 -1
  529. package/extensions/whatsapp/src/channel.js +429 -0
  530. package/extensions/whatsapp/src/runtime.js +10 -0
  531. package/extensions/zalo/CHANGELOG.md +5 -0
  532. package/extensions/zalo/package.json +1 -1
  533. package/extensions/zalouser/CHANGELOG.md +5 -0
  534. package/extensions/zalouser/package.json +1 -1
  535. package/package.json +1 -1
@@ -0,0 +1,770 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { splitShellArgs } from "../utils/shell-argv.js";
5
+ export const DEFAULT_SAFE_BINS = ["jq", "grep", "cut", "sort", "uniq", "head", "tail", "tr", "wc"];
6
+ function expandHome(value) {
7
+ if (!value) {
8
+ return value;
9
+ }
10
+ if (value === "~") {
11
+ return os.homedir();
12
+ }
13
+ if (value.startsWith("~/")) {
14
+ return path.join(os.homedir(), value.slice(2));
15
+ }
16
+ return value;
17
+ }
18
+ function isExecutableFile(filePath) {
19
+ try {
20
+ const stat = fs.statSync(filePath);
21
+ if (!stat.isFile()) {
22
+ return false;
23
+ }
24
+ if (process.platform !== "win32") {
25
+ fs.accessSync(filePath, fs.constants.X_OK);
26
+ }
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ function parseFirstToken(command) {
34
+ const trimmed = command.trim();
35
+ if (!trimmed) {
36
+ return null;
37
+ }
38
+ const first = trimmed[0];
39
+ if (first === '"' || first === "'") {
40
+ const end = trimmed.indexOf(first, 1);
41
+ if (end > 1) {
42
+ return trimmed.slice(1, end);
43
+ }
44
+ return trimmed.slice(1);
45
+ }
46
+ const match = /^[^\s]+/.exec(trimmed);
47
+ return match ? match[0] : null;
48
+ }
49
+ function resolveExecutablePath(rawExecutable, cwd, env) {
50
+ const expanded = rawExecutable.startsWith("~") ? expandHome(rawExecutable) : rawExecutable;
51
+ if (expanded.includes("/") || expanded.includes("\\")) {
52
+ if (path.isAbsolute(expanded)) {
53
+ return isExecutableFile(expanded) ? expanded : undefined;
54
+ }
55
+ const base = cwd && cwd.trim() ? cwd.trim() : process.cwd();
56
+ const candidate = path.resolve(base, expanded);
57
+ return isExecutableFile(candidate) ? candidate : undefined;
58
+ }
59
+ const envPath = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? "";
60
+ const entries = envPath.split(path.delimiter).filter(Boolean);
61
+ const hasExtension = process.platform === "win32" && path.extname(expanded).length > 0;
62
+ const extensions = process.platform === "win32"
63
+ ? hasExtension
64
+ ? [""]
65
+ : (env?.PATHEXT ??
66
+ env?.Pathext ??
67
+ process.env.PATHEXT ??
68
+ process.env.Pathext ??
69
+ ".EXE;.CMD;.BAT;.COM")
70
+ .split(";")
71
+ .map((ext) => ext.toLowerCase())
72
+ : [""];
73
+ for (const entry of entries) {
74
+ for (const ext of extensions) {
75
+ const candidate = path.join(entry, expanded + ext);
76
+ if (isExecutableFile(candidate)) {
77
+ return candidate;
78
+ }
79
+ }
80
+ }
81
+ return undefined;
82
+ }
83
+ export function resolveCommandResolution(command, cwd, env) {
84
+ const rawExecutable = parseFirstToken(command);
85
+ if (!rawExecutable) {
86
+ return null;
87
+ }
88
+ const resolvedPath = resolveExecutablePath(rawExecutable, cwd, env);
89
+ const executableName = resolvedPath ? path.basename(resolvedPath) : rawExecutable;
90
+ return { rawExecutable, resolvedPath, executableName };
91
+ }
92
+ export function resolveCommandResolutionFromArgv(argv, cwd, env) {
93
+ const rawExecutable = argv[0]?.trim();
94
+ if (!rawExecutable) {
95
+ return null;
96
+ }
97
+ const resolvedPath = resolveExecutablePath(rawExecutable, cwd, env);
98
+ const executableName = resolvedPath ? path.basename(resolvedPath) : rawExecutable;
99
+ return { rawExecutable, resolvedPath, executableName };
100
+ }
101
+ function normalizeMatchTarget(value) {
102
+ if (process.platform === "win32") {
103
+ const stripped = value.replace(/^\\\\[?.]\\/, "");
104
+ return stripped.replace(/\\/g, "/").toLowerCase();
105
+ }
106
+ return value.replace(/\\\\/g, "/").toLowerCase();
107
+ }
108
+ function tryRealpath(value) {
109
+ try {
110
+ return fs.realpathSync(value);
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
116
+ function globToRegExp(pattern) {
117
+ let regex = "^";
118
+ let i = 0;
119
+ while (i < pattern.length) {
120
+ const ch = pattern[i];
121
+ if (ch === "*") {
122
+ const next = pattern[i + 1];
123
+ if (next === "*") {
124
+ regex += ".*";
125
+ i += 2;
126
+ continue;
127
+ }
128
+ regex += "[^/]*";
129
+ i += 1;
130
+ continue;
131
+ }
132
+ if (ch === "?") {
133
+ regex += ".";
134
+ i += 1;
135
+ continue;
136
+ }
137
+ regex += ch.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&");
138
+ i += 1;
139
+ }
140
+ regex += "$";
141
+ return new RegExp(regex, "i");
142
+ }
143
+ function matchesPattern(pattern, target) {
144
+ const trimmed = pattern.trim();
145
+ if (!trimmed) {
146
+ return false;
147
+ }
148
+ const expanded = trimmed.startsWith("~") ? expandHome(trimmed) : trimmed;
149
+ const hasWildcard = /[*?]/.test(expanded);
150
+ let normalizedPattern = expanded;
151
+ let normalizedTarget = target;
152
+ if (process.platform === "win32" && !hasWildcard) {
153
+ normalizedPattern = tryRealpath(expanded) ?? expanded;
154
+ normalizedTarget = tryRealpath(target) ?? target;
155
+ }
156
+ normalizedPattern = normalizeMatchTarget(normalizedPattern);
157
+ normalizedTarget = normalizeMatchTarget(normalizedTarget);
158
+ const regex = globToRegExp(normalizedPattern);
159
+ return regex.test(normalizedTarget);
160
+ }
161
+ export function resolveAllowlistCandidatePath(resolution, cwd) {
162
+ if (!resolution) {
163
+ return undefined;
164
+ }
165
+ if (resolution.resolvedPath) {
166
+ return resolution.resolvedPath;
167
+ }
168
+ const raw = resolution.rawExecutable?.trim();
169
+ if (!raw) {
170
+ return undefined;
171
+ }
172
+ const expanded = raw.startsWith("~") ? expandHome(raw) : raw;
173
+ if (!expanded.includes("/") && !expanded.includes("\\")) {
174
+ return undefined;
175
+ }
176
+ if (path.isAbsolute(expanded)) {
177
+ return expanded;
178
+ }
179
+ const base = cwd && cwd.trim() ? cwd.trim() : process.cwd();
180
+ return path.resolve(base, expanded);
181
+ }
182
+ export function matchAllowlist(entries, resolution) {
183
+ if (!entries.length || !resolution?.resolvedPath) {
184
+ return null;
185
+ }
186
+ const resolvedPath = resolution.resolvedPath;
187
+ for (const entry of entries) {
188
+ const pattern = entry.pattern?.trim();
189
+ if (!pattern) {
190
+ continue;
191
+ }
192
+ const hasPath = pattern.includes("/") || pattern.includes("\\") || pattern.includes("~");
193
+ if (!hasPath) {
194
+ continue;
195
+ }
196
+ if (matchesPattern(pattern, resolvedPath)) {
197
+ return entry;
198
+ }
199
+ }
200
+ return null;
201
+ }
202
+ const DISALLOWED_PIPELINE_TOKENS = new Set([">", "<", "`", "\n", "\r", "(", ")"]);
203
+ const DOUBLE_QUOTE_ESCAPES = new Set(["\\", '"', "$", "`", "\n", "\r"]);
204
+ const WINDOWS_UNSUPPORTED_TOKENS = new Set([
205
+ "&",
206
+ "|",
207
+ "<",
208
+ ">",
209
+ "^",
210
+ "(",
211
+ ")",
212
+ "%",
213
+ "!",
214
+ "\n",
215
+ "\r",
216
+ ]);
217
+ function isDoubleQuoteEscape(next) {
218
+ return Boolean(next && DOUBLE_QUOTE_ESCAPES.has(next));
219
+ }
220
+ function splitShellPipeline(command) {
221
+ const parseHeredocDelimiter = (source, start) => {
222
+ let i = start;
223
+ while (i < source.length && (source[i] === " " || source[i] === "\t")) {
224
+ i += 1;
225
+ }
226
+ if (i >= source.length) {
227
+ return null;
228
+ }
229
+ const first = source[i];
230
+ if (first === "'" || first === '"') {
231
+ const quote = first;
232
+ i += 1;
233
+ let delimiter = "";
234
+ while (i < source.length) {
235
+ const ch = source[i];
236
+ if (ch === "\n" || ch === "\r") {
237
+ return null;
238
+ }
239
+ if (quote === '"' && ch === "\\" && i + 1 < source.length) {
240
+ delimiter += source[i + 1];
241
+ i += 2;
242
+ continue;
243
+ }
244
+ if (ch === quote) {
245
+ return { delimiter, end: i + 1 };
246
+ }
247
+ delimiter += ch;
248
+ i += 1;
249
+ }
250
+ return null;
251
+ }
252
+ let delimiter = "";
253
+ while (i < source.length) {
254
+ const ch = source[i];
255
+ if (/\s/.test(ch) || ch === "|" || ch === "&" || ch === ";" || ch === "<" || ch === ">") {
256
+ break;
257
+ }
258
+ delimiter += ch;
259
+ i += 1;
260
+ }
261
+ if (!delimiter) {
262
+ return null;
263
+ }
264
+ return { delimiter, end: i };
265
+ };
266
+ const segments = [];
267
+ let buf = "";
268
+ let inSingle = false;
269
+ let inDouble = false;
270
+ let escaped = false;
271
+ let emptySegment = false;
272
+ const pendingHeredocs = [];
273
+ let inHeredocBody = false;
274
+ let heredocLine = "";
275
+ const pushPart = () => {
276
+ const trimmed = buf.trim();
277
+ if (trimmed) {
278
+ segments.push(trimmed);
279
+ }
280
+ buf = "";
281
+ };
282
+ for (let i = 0; i < command.length; i += 1) {
283
+ const ch = command[i];
284
+ const next = command[i + 1];
285
+ if (inHeredocBody) {
286
+ if (ch === "\n" || ch === "\r") {
287
+ const current = pendingHeredocs[0];
288
+ if (current) {
289
+ const line = current.stripTabs ? heredocLine.replace(/^\t+/, "") : heredocLine;
290
+ if (line === current.delimiter) {
291
+ pendingHeredocs.shift();
292
+ }
293
+ }
294
+ heredocLine = "";
295
+ if (pendingHeredocs.length === 0) {
296
+ inHeredocBody = false;
297
+ }
298
+ if (ch === "\r" && next === "\n") {
299
+ i += 1;
300
+ }
301
+ }
302
+ else {
303
+ heredocLine += ch;
304
+ }
305
+ continue;
306
+ }
307
+ if (escaped) {
308
+ buf += ch;
309
+ escaped = false;
310
+ emptySegment = false;
311
+ continue;
312
+ }
313
+ if (!inSingle && !inDouble && ch === "\\") {
314
+ escaped = true;
315
+ buf += ch;
316
+ emptySegment = false;
317
+ continue;
318
+ }
319
+ if (inSingle) {
320
+ if (ch === "'") {
321
+ inSingle = false;
322
+ }
323
+ buf += ch;
324
+ emptySegment = false;
325
+ continue;
326
+ }
327
+ if (inDouble) {
328
+ if (ch === "\\" && isDoubleQuoteEscape(next)) {
329
+ buf += ch;
330
+ buf += next;
331
+ i += 1;
332
+ emptySegment = false;
333
+ continue;
334
+ }
335
+ if (ch === "$" && next === "(") {
336
+ return { ok: false, reason: "unsupported shell token: $()", segments: [] };
337
+ }
338
+ if (ch === "`") {
339
+ return { ok: false, reason: "unsupported shell token: `", segments: [] };
340
+ }
341
+ if (ch === "\n" || ch === "\r") {
342
+ return { ok: false, reason: "unsupported shell token: newline", segments: [] };
343
+ }
344
+ if (ch === '"') {
345
+ inDouble = false;
346
+ }
347
+ buf += ch;
348
+ emptySegment = false;
349
+ continue;
350
+ }
351
+ if (ch === "'") {
352
+ inSingle = true;
353
+ buf += ch;
354
+ emptySegment = false;
355
+ continue;
356
+ }
357
+ if (ch === '"') {
358
+ inDouble = true;
359
+ buf += ch;
360
+ emptySegment = false;
361
+ continue;
362
+ }
363
+ if ((ch === "\n" || ch === "\r") && pendingHeredocs.length > 0) {
364
+ inHeredocBody = true;
365
+ heredocLine = "";
366
+ if (ch === "\r" && next === "\n") {
367
+ i += 1;
368
+ }
369
+ continue;
370
+ }
371
+ if (ch === "|" && next === "|") {
372
+ return { ok: false, reason: "unsupported shell token: ||", segments: [] };
373
+ }
374
+ if (ch === "|" && next === "&") {
375
+ return { ok: false, reason: "unsupported shell token: |&", segments: [] };
376
+ }
377
+ if (ch === "|") {
378
+ emptySegment = true;
379
+ pushPart();
380
+ continue;
381
+ }
382
+ if (ch === "&" || ch === ";") {
383
+ return { ok: false, reason: `unsupported shell token: ${ch}`, segments: [] };
384
+ }
385
+ if (ch === "<" && next === "<") {
386
+ buf += "<<";
387
+ emptySegment = false;
388
+ i += 1;
389
+ let scanIndex = i + 1;
390
+ let stripTabs = false;
391
+ if (command[scanIndex] === "-") {
392
+ stripTabs = true;
393
+ buf += "-";
394
+ scanIndex += 1;
395
+ }
396
+ const parsed = parseHeredocDelimiter(command, scanIndex);
397
+ if (parsed) {
398
+ pendingHeredocs.push({ delimiter: parsed.delimiter, stripTabs });
399
+ buf += command.slice(scanIndex, parsed.end);
400
+ i = parsed.end - 1;
401
+ }
402
+ continue;
403
+ }
404
+ if (DISALLOWED_PIPELINE_TOKENS.has(ch)) {
405
+ return { ok: false, reason: `unsupported shell token: ${ch}`, segments: [] };
406
+ }
407
+ if (ch === "$" && next === "(") {
408
+ return { ok: false, reason: "unsupported shell token: $()", segments: [] };
409
+ }
410
+ buf += ch;
411
+ emptySegment = false;
412
+ }
413
+ if (inHeredocBody && pendingHeredocs.length > 0) {
414
+ const current = pendingHeredocs[0];
415
+ const line = current.stripTabs ? heredocLine.replace(/^\t+/, "") : heredocLine;
416
+ if (line === current.delimiter) {
417
+ pendingHeredocs.shift();
418
+ }
419
+ }
420
+ if (escaped || inSingle || inDouble) {
421
+ return { ok: false, reason: "unterminated shell quote/escape", segments: [] };
422
+ }
423
+ pushPart();
424
+ if (emptySegment || segments.length === 0) {
425
+ return {
426
+ ok: false,
427
+ reason: segments.length === 0 ? "empty command" : "empty pipeline segment",
428
+ segments: [],
429
+ };
430
+ }
431
+ return { ok: true, segments };
432
+ }
433
+ function findWindowsUnsupportedToken(command) {
434
+ for (const ch of command) {
435
+ if (WINDOWS_UNSUPPORTED_TOKENS.has(ch)) {
436
+ if (ch === "\n" || ch === "\r") {
437
+ return "newline";
438
+ }
439
+ return ch;
440
+ }
441
+ }
442
+ return null;
443
+ }
444
+ function tokenizeWindowsSegment(segment) {
445
+ const tokens = [];
446
+ let buf = "";
447
+ let inDouble = false;
448
+ const pushToken = () => {
449
+ if (buf.length > 0) {
450
+ tokens.push(buf);
451
+ buf = "";
452
+ }
453
+ };
454
+ for (let i = 0; i < segment.length; i += 1) {
455
+ const ch = segment[i];
456
+ if (ch === '"') {
457
+ inDouble = !inDouble;
458
+ continue;
459
+ }
460
+ if (!inDouble && /\s/.test(ch)) {
461
+ pushToken();
462
+ continue;
463
+ }
464
+ buf += ch;
465
+ }
466
+ if (inDouble) {
467
+ return null;
468
+ }
469
+ pushToken();
470
+ return tokens.length > 0 ? tokens : null;
471
+ }
472
+ function analyzeWindowsShellCommand(params) {
473
+ const unsupported = findWindowsUnsupportedToken(params.command);
474
+ if (unsupported) {
475
+ return {
476
+ ok: false,
477
+ reason: `unsupported windows shell token: ${unsupported}`,
478
+ segments: [],
479
+ };
480
+ }
481
+ const argv = tokenizeWindowsSegment(params.command);
482
+ if (!argv || argv.length === 0) {
483
+ return { ok: false, reason: "unable to parse windows command", segments: [] };
484
+ }
485
+ return {
486
+ ok: true,
487
+ segments: [
488
+ {
489
+ raw: params.command,
490
+ argv,
491
+ resolution: resolveCommandResolutionFromArgv(argv, params.cwd, params.env),
492
+ },
493
+ ],
494
+ };
495
+ }
496
+ export function isWindowsPlatform(platform) {
497
+ const normalized = String(platform ?? "")
498
+ .trim()
499
+ .toLowerCase();
500
+ return normalized.startsWith("win");
501
+ }
502
+ function parseSegmentsFromParts(parts, cwd, env) {
503
+ const segments = [];
504
+ for (const raw of parts) {
505
+ const argv = splitShellArgs(raw);
506
+ if (!argv || argv.length === 0) {
507
+ return null;
508
+ }
509
+ segments.push({
510
+ raw,
511
+ argv,
512
+ resolution: resolveCommandResolutionFromArgv(argv, cwd, env),
513
+ });
514
+ }
515
+ return segments;
516
+ }
517
+ /**
518
+ * Splits a command string by chain operators (&&, ||, ;) while preserving the operators.
519
+ * Returns null when no chain is present or when the chain is malformed.
520
+ */
521
+ export function splitCommandChainWithOperators(command) {
522
+ const parts = [];
523
+ let buf = "";
524
+ let inSingle = false;
525
+ let inDouble = false;
526
+ let escaped = false;
527
+ let foundChain = false;
528
+ let invalidChain = false;
529
+ const pushPart = (opToNext) => {
530
+ const trimmed = buf.trim();
531
+ buf = "";
532
+ if (!trimmed) {
533
+ return false;
534
+ }
535
+ parts.push({ part: trimmed, opToNext });
536
+ return true;
537
+ };
538
+ for (let i = 0; i < command.length; i += 1) {
539
+ const ch = command[i];
540
+ const next = command[i + 1];
541
+ if (escaped) {
542
+ buf += ch;
543
+ escaped = false;
544
+ continue;
545
+ }
546
+ if (!inSingle && !inDouble && ch === "\\") {
547
+ escaped = true;
548
+ buf += ch;
549
+ continue;
550
+ }
551
+ if (inSingle) {
552
+ if (ch === "'") {
553
+ inSingle = false;
554
+ }
555
+ buf += ch;
556
+ continue;
557
+ }
558
+ if (inDouble) {
559
+ if (ch === "\\" && isDoubleQuoteEscape(next)) {
560
+ buf += ch;
561
+ buf += next;
562
+ i += 1;
563
+ continue;
564
+ }
565
+ if (ch === '"') {
566
+ inDouble = false;
567
+ }
568
+ buf += ch;
569
+ continue;
570
+ }
571
+ if (ch === "'") {
572
+ inSingle = true;
573
+ buf += ch;
574
+ continue;
575
+ }
576
+ if (ch === '"') {
577
+ inDouble = true;
578
+ buf += ch;
579
+ continue;
580
+ }
581
+ if (ch === "&" && next === "&") {
582
+ if (!pushPart("&&")) {
583
+ invalidChain = true;
584
+ }
585
+ i += 1;
586
+ foundChain = true;
587
+ continue;
588
+ }
589
+ if (ch === "|" && next === "|") {
590
+ if (!pushPart("||")) {
591
+ invalidChain = true;
592
+ }
593
+ i += 1;
594
+ foundChain = true;
595
+ continue;
596
+ }
597
+ if (ch === ";") {
598
+ if (!pushPart(";")) {
599
+ invalidChain = true;
600
+ }
601
+ foundChain = true;
602
+ continue;
603
+ }
604
+ buf += ch;
605
+ }
606
+ if (!foundChain) {
607
+ return null;
608
+ }
609
+ const trimmed = buf.trim();
610
+ if (!trimmed) {
611
+ return null;
612
+ }
613
+ parts.push({ part: trimmed, opToNext: null });
614
+ if (invalidChain || parts.length === 0) {
615
+ return null;
616
+ }
617
+ return parts;
618
+ }
619
+ function shellEscapeSingleArg(value) {
620
+ // Shell-safe across sh/bash/zsh: single-quote everything, escape embedded single quotes.
621
+ // Example: foo'bar -> 'foo'"'"'bar'
622
+ const singleQuoteEscape = `'"'"'`;
623
+ return `'${value.replace(/'/g, singleQuoteEscape)}'`;
624
+ }
625
+ /**
626
+ * Builds a shell command string that preserves pipes/chaining, but forces *arguments* to be
627
+ * literal (no globbing, no env-var expansion) by single-quoting every argv token.
628
+ *
629
+ * Used to make "safe bins" actually stdin-only even though execution happens via `shell -c`.
630
+ */
631
+ export function buildSafeShellCommand(params) {
632
+ const platform = params.platform ?? null;
633
+ if (isWindowsPlatform(platform)) {
634
+ return { ok: false, reason: "unsupported platform" };
635
+ }
636
+ const source = params.command.trim();
637
+ if (!source) {
638
+ return { ok: false, reason: "empty command" };
639
+ }
640
+ const chain = splitCommandChainWithOperators(source);
641
+ const chainParts = chain ?? [{ part: source, opToNext: null }];
642
+ let out = "";
643
+ for (let i = 0; i < chainParts.length; i += 1) {
644
+ const part = chainParts[i];
645
+ const pipelineSplit = splitShellPipeline(part.part);
646
+ if (!pipelineSplit.ok) {
647
+ return { ok: false, reason: pipelineSplit.reason ?? "unable to parse pipeline" };
648
+ }
649
+ const renderedSegments = [];
650
+ for (const segmentRaw of pipelineSplit.segments) {
651
+ const argv = splitShellArgs(segmentRaw);
652
+ if (!argv || argv.length === 0) {
653
+ return { ok: false, reason: "unable to parse shell segment" };
654
+ }
655
+ renderedSegments.push(argv.map((token) => shellEscapeSingleArg(token)).join(" "));
656
+ }
657
+ out += renderedSegments.join(" | ");
658
+ if (part.opToNext) {
659
+ out += ` ${part.opToNext} `;
660
+ }
661
+ }
662
+ return { ok: true, command: out };
663
+ }
664
+ function renderQuotedArgv(argv) {
665
+ return argv.map((token) => shellEscapeSingleArg(token)).join(" ");
666
+ }
667
+ /**
668
+ * Rebuilds a shell command and selectively single-quotes argv tokens for segments that
669
+ * must be treated as literal (safeBins hardening) while preserving the rest of the
670
+ * shell syntax (pipes + chaining).
671
+ */
672
+ export function buildSafeBinsShellCommand(params) {
673
+ const platform = params.platform ?? null;
674
+ if (isWindowsPlatform(platform)) {
675
+ return { ok: false, reason: "unsupported platform" };
676
+ }
677
+ if (params.segments.length !== params.segmentSatisfiedBy.length) {
678
+ return { ok: false, reason: "segment metadata mismatch" };
679
+ }
680
+ const chain = splitCommandChainWithOperators(params.command.trim());
681
+ const chainParts = chain ?? [{ part: params.command.trim(), opToNext: null }];
682
+ let segIndex = 0;
683
+ let out = "";
684
+ for (const part of chainParts) {
685
+ const pipelineSplit = splitShellPipeline(part.part);
686
+ if (!pipelineSplit.ok) {
687
+ return { ok: false, reason: pipelineSplit.reason ?? "unable to parse pipeline" };
688
+ }
689
+ const rendered = [];
690
+ for (const raw of pipelineSplit.segments) {
691
+ const seg = params.segments[segIndex];
692
+ const by = params.segmentSatisfiedBy[segIndex];
693
+ if (!seg || by === undefined) {
694
+ return { ok: false, reason: "segment mapping failed" };
695
+ }
696
+ const needsLiteral = by === "safeBins";
697
+ rendered.push(needsLiteral ? renderQuotedArgv(seg.argv) : raw.trim());
698
+ segIndex += 1;
699
+ }
700
+ out += rendered.join(" | ");
701
+ if (part.opToNext) {
702
+ out += ` ${part.opToNext} `;
703
+ }
704
+ }
705
+ if (segIndex !== params.segments.length) {
706
+ return { ok: false, reason: "segment count mismatch" };
707
+ }
708
+ return { ok: true, command: out };
709
+ }
710
+ /**
711
+ * Splits a command string by chain operators (&&, ||, ;) while respecting quotes.
712
+ * Returns null when no chain is present or when the chain is malformed.
713
+ */
714
+ export function splitCommandChain(command) {
715
+ const parts = splitCommandChainWithOperators(command);
716
+ if (!parts) {
717
+ return null;
718
+ }
719
+ return parts.map((p) => p.part);
720
+ }
721
+ export function analyzeShellCommand(params) {
722
+ if (isWindowsPlatform(params.platform)) {
723
+ return analyzeWindowsShellCommand(params);
724
+ }
725
+ // First try splitting by chain operators (&&, ||, ;)
726
+ const chainParts = splitCommandChain(params.command);
727
+ if (chainParts) {
728
+ const chains = [];
729
+ const allSegments = [];
730
+ for (const part of chainParts) {
731
+ const pipelineSplit = splitShellPipeline(part);
732
+ if (!pipelineSplit.ok) {
733
+ return { ok: false, reason: pipelineSplit.reason, segments: [] };
734
+ }
735
+ const segments = parseSegmentsFromParts(pipelineSplit.segments, params.cwd, params.env);
736
+ if (!segments) {
737
+ return { ok: false, reason: "unable to parse shell segment", segments: [] };
738
+ }
739
+ chains.push(segments);
740
+ allSegments.push(...segments);
741
+ }
742
+ return { ok: true, segments: allSegments, chains };
743
+ }
744
+ // No chain operators, parse as simple pipeline
745
+ const split = splitShellPipeline(params.command);
746
+ if (!split.ok) {
747
+ return { ok: false, reason: split.reason, segments: [] };
748
+ }
749
+ const segments = parseSegmentsFromParts(split.segments, params.cwd, params.env);
750
+ if (!segments) {
751
+ return { ok: false, reason: "unable to parse shell segment", segments: [] };
752
+ }
753
+ return { ok: true, segments };
754
+ }
755
+ export function analyzeArgvCommand(params) {
756
+ const argv = params.argv.filter((entry) => entry.trim().length > 0);
757
+ if (argv.length === 0) {
758
+ return { ok: false, reason: "empty argv", segments: [] };
759
+ }
760
+ return {
761
+ ok: true,
762
+ segments: [
763
+ {
764
+ raw: argv.join(" "),
765
+ argv,
766
+ resolution: resolveCommandResolutionFromArgv(argv, params.cwd, params.env),
767
+ },
768
+ ],
769
+ };
770
+ }