@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
@@ -35,6 +35,19 @@ export function resolveExecApprovalsPath() {
35
35
  export function resolveExecApprovalsSocketPath() {
36
36
  return expandHome(DEFAULT_SOCKET);
37
37
  }
38
+ export function mergeExecApprovalsSocketDefaults(params) {
39
+ const currentSocketPath = params.current?.socket?.path?.trim();
40
+ const currentToken = params.current?.socket?.token?.trim();
41
+ const socketPath = params.normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath();
42
+ const token = params.normalized.socket?.token?.trim() ?? currentToken ?? "";
43
+ return {
44
+ ...params.normalized,
45
+ socket: {
46
+ path: socketPath,
47
+ token,
48
+ },
49
+ };
50
+ }
38
51
  function normalizeAllowlistPattern(value) {
39
52
  const trimmed = value?.trim() ?? "";
40
53
  return trimmed ? trimmed.toLowerCase() : null;
@@ -0,0 +1 @@
1
+ export { acquireFileLock, withFileLock } from "../plugin-sdk/file-lock.js";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared Gemini authentication utilities.
3
+ *
4
+ * Supports both traditional API keys and OAuth JSON format.
5
+ */
6
+ /**
7
+ * Parse Gemini API key and return appropriate auth headers.
8
+ *
9
+ * OAuth format: `{"token": "...", "projectId": "..."}`
10
+ *
11
+ * @param apiKey - Either a traditional API key string or OAuth JSON
12
+ * @returns Headers object with appropriate authentication
13
+ */
14
+ export function parseGeminiAuth(apiKey) {
15
+ // Try parsing as OAuth JSON format
16
+ if (apiKey.startsWith("{")) {
17
+ try {
18
+ const parsed = JSON.parse(apiKey);
19
+ if (typeof parsed.token === "string" && parsed.token) {
20
+ return {
21
+ headers: {
22
+ Authorization: `Bearer ${parsed.token}`,
23
+ "Content-Type": "application/json",
24
+ },
25
+ };
26
+ }
27
+ }
28
+ catch {
29
+ // Parse failed, fallback to API key mode
30
+ }
31
+ }
32
+ // Default: traditional API key
33
+ return {
34
+ headers: {
35
+ "x-goog-api-key": apiKey,
36
+ "Content-Type": "application/json",
37
+ },
38
+ };
39
+ }
@@ -0,0 +1,85 @@
1
+ import { resolveUserTimezone } from "../agents/date-time.js";
2
+ const ACTIVE_HOURS_TIME_PATTERN = /^([01]\d|2[0-3]|24):([0-5]\d)$/;
3
+ function resolveActiveHoursTimezone(cfg, raw) {
4
+ const trimmed = raw?.trim();
5
+ if (!trimmed || trimmed === "user") {
6
+ return resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
7
+ }
8
+ if (trimmed === "local") {
9
+ const host = Intl.DateTimeFormat().resolvedOptions().timeZone;
10
+ return host?.trim() || "UTC";
11
+ }
12
+ try {
13
+ new Intl.DateTimeFormat("en-US", { timeZone: trimmed }).format(new Date());
14
+ return trimmed;
15
+ }
16
+ catch {
17
+ return resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
18
+ }
19
+ }
20
+ function parseActiveHoursTime(opts, raw) {
21
+ if (!raw || !ACTIVE_HOURS_TIME_PATTERN.test(raw)) {
22
+ return null;
23
+ }
24
+ const [hourStr, minuteStr] = raw.split(":");
25
+ const hour = Number(hourStr);
26
+ const minute = Number(minuteStr);
27
+ if (!Number.isFinite(hour) || !Number.isFinite(minute)) {
28
+ return null;
29
+ }
30
+ if (hour === 24) {
31
+ if (!opts.allow24 || minute !== 0) {
32
+ return null;
33
+ }
34
+ return 24 * 60;
35
+ }
36
+ return hour * 60 + minute;
37
+ }
38
+ function resolveMinutesInTimeZone(nowMs, timeZone) {
39
+ try {
40
+ const parts = new Intl.DateTimeFormat("en-US", {
41
+ timeZone,
42
+ hour: "2-digit",
43
+ minute: "2-digit",
44
+ hourCycle: "h23",
45
+ }).formatToParts(new Date(nowMs));
46
+ const map = {};
47
+ for (const part of parts) {
48
+ if (part.type !== "literal") {
49
+ map[part.type] = part.value;
50
+ }
51
+ }
52
+ const hour = Number(map.hour);
53
+ const minute = Number(map.minute);
54
+ if (!Number.isFinite(hour) || !Number.isFinite(minute)) {
55
+ return null;
56
+ }
57
+ return hour * 60 + minute;
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ export function isWithinActiveHours(cfg, heartbeat, nowMs) {
64
+ const active = heartbeat?.activeHours;
65
+ if (!active) {
66
+ return true;
67
+ }
68
+ const startMin = parseActiveHoursTime({ allow24: false }, active.start);
69
+ const endMin = parseActiveHoursTime({ allow24: true }, active.end);
70
+ if (startMin === null || endMin === null) {
71
+ return true;
72
+ }
73
+ if (startMin === endMin) {
74
+ return true;
75
+ }
76
+ const timeZone = resolveActiveHoursTimezone(cfg, active.timezone);
77
+ const currentMin = resolveMinutesInTimeZone(nowMs ?? Date.now(), timeZone);
78
+ if (currentMin === null) {
79
+ return true;
80
+ }
81
+ if (endMin > startMin) {
82
+ return currentMin >= startMin && currentMin < endMin;
83
+ }
84
+ return currentMin >= startMin || currentMin < endMin;
85
+ }
@@ -0,0 +1,50 @@
1
+ import { HEARTBEAT_TOKEN } from "../auto-reply/tokens.js";
2
+ // Build a dynamic prompt for cron events by embedding the actual event content.
3
+ // This ensures the model sees the reminder text directly instead of relying on
4
+ // "shown in the system messages above" which may not be visible in context.
5
+ export function buildCronEventPrompt(pendingEvents) {
6
+ const eventText = pendingEvents.join("\n").trim();
7
+ if (!eventText) {
8
+ return ("A scheduled cron event was triggered, but no event content was found. " +
9
+ "Reply HEARTBEAT_OK.");
10
+ }
11
+ return ("A scheduled reminder has been triggered. The reminder content is:\n\n" +
12
+ eventText +
13
+ "\n\nPlease relay this reminder to the user in a helpful and friendly way.");
14
+ }
15
+ const HEARTBEAT_OK_PREFIX = HEARTBEAT_TOKEN.toLowerCase();
16
+ // Detect heartbeat-specific noise so cron reminders don't trigger on non-reminder events.
17
+ function isHeartbeatAckEvent(evt) {
18
+ const trimmed = evt.trim();
19
+ if (!trimmed) {
20
+ return false;
21
+ }
22
+ const lower = trimmed.toLowerCase();
23
+ if (!lower.startsWith(HEARTBEAT_OK_PREFIX)) {
24
+ return false;
25
+ }
26
+ const suffix = lower.slice(HEARTBEAT_OK_PREFIX.length);
27
+ if (suffix.length === 0) {
28
+ return true;
29
+ }
30
+ return !/[a-z0-9_]/.test(suffix[0]);
31
+ }
32
+ function isHeartbeatNoiseEvent(evt) {
33
+ const lower = evt.trim().toLowerCase();
34
+ if (!lower) {
35
+ return false;
36
+ }
37
+ return (isHeartbeatAckEvent(lower) ||
38
+ lower.includes("heartbeat poll") ||
39
+ lower.includes("heartbeat wake"));
40
+ }
41
+ export function isExecCompletionEvent(evt) {
42
+ return evt.toLowerCase().includes("exec finished");
43
+ }
44
+ // Returns true when a system event should be treated as real cron reminder content.
45
+ export function isCronSystemEvent(evt) {
46
+ if (!evt.trim()) {
47
+ return false;
48
+ }
49
+ return !isHeartbeatNoiseEvent(evt) && !isExecCompletionEvent(evt);
50
+ }
@@ -0,0 +1,39 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { vi } from "vitest";
5
+ import * as replyModule from "../auto-reply/reply.js";
6
+ export async function seedSessionStore(storePath, sessionKey, session) {
7
+ await fs.writeFile(storePath, JSON.stringify({
8
+ [sessionKey]: {
9
+ sessionId: session.sessionId ?? "sid",
10
+ updatedAt: session.updatedAt ?? Date.now(),
11
+ ...session,
12
+ },
13
+ }, null, 2));
14
+ }
15
+ export async function withTempHeartbeatSandbox(fn, options) {
16
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), options?.prefix ?? "openclaw-hb-"));
17
+ const storePath = path.join(tmpDir, "sessions.json");
18
+ const replySpy = vi.spyOn(replyModule, "getReplyFromConfig");
19
+ const previousEnv = new Map();
20
+ for (const envName of options?.unsetEnvVars ?? []) {
21
+ previousEnv.set(envName, process.env[envName]);
22
+ process.env[envName] = "";
23
+ }
24
+ try {
25
+ return await fn({ tmpDir, storePath, replySpy });
26
+ }
27
+ finally {
28
+ replySpy.mockRestore();
29
+ for (const [envName, previousValue] of previousEnv.entries()) {
30
+ if (previousValue === undefined) {
31
+ delete process.env[envName];
32
+ }
33
+ else {
34
+ process.env[envName] = previousValue;
35
+ }
36
+ }
37
+ await fs.rm(tmpDir, { recursive: true, force: true });
38
+ }
39
+ }
@@ -0,0 +1,265 @@
1
+ export const DEFAULT_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
2
+ export const DEFAULT_WEBHOOK_BODY_TIMEOUT_MS = 30_000;
3
+ const DEFAULT_ERROR_MESSAGE = {
4
+ PAYLOAD_TOO_LARGE: "PayloadTooLarge",
5
+ REQUEST_BODY_TIMEOUT: "RequestBodyTimeout",
6
+ CONNECTION_CLOSED: "RequestBodyConnectionClosed",
7
+ };
8
+ const DEFAULT_ERROR_STATUS_CODE = {
9
+ PAYLOAD_TOO_LARGE: 413,
10
+ REQUEST_BODY_TIMEOUT: 408,
11
+ CONNECTION_CLOSED: 400,
12
+ };
13
+ const DEFAULT_RESPONSE_MESSAGE = {
14
+ PAYLOAD_TOO_LARGE: "Payload too large",
15
+ REQUEST_BODY_TIMEOUT: "Request body timeout",
16
+ CONNECTION_CLOSED: "Connection closed",
17
+ };
18
+ export class RequestBodyLimitError extends Error {
19
+ code;
20
+ statusCode;
21
+ constructor(init) {
22
+ super(init.message ?? DEFAULT_ERROR_MESSAGE[init.code]);
23
+ this.name = "RequestBodyLimitError";
24
+ this.code = init.code;
25
+ this.statusCode = DEFAULT_ERROR_STATUS_CODE[init.code];
26
+ }
27
+ }
28
+ export function isRequestBodyLimitError(error, code) {
29
+ if (!(error instanceof RequestBodyLimitError)) {
30
+ return false;
31
+ }
32
+ if (!code) {
33
+ return true;
34
+ }
35
+ return error.code === code;
36
+ }
37
+ export function requestBodyErrorToText(code) {
38
+ return DEFAULT_RESPONSE_MESSAGE[code];
39
+ }
40
+ function parseContentLengthHeader(req) {
41
+ const header = req.headers["content-length"];
42
+ const raw = Array.isArray(header) ? header[0] : header;
43
+ if (typeof raw !== "string") {
44
+ return null;
45
+ }
46
+ const parsed = Number.parseInt(raw, 10);
47
+ if (!Number.isFinite(parsed) || parsed < 0) {
48
+ return null;
49
+ }
50
+ return parsed;
51
+ }
52
+ export async function readRequestBodyWithLimit(req, options) {
53
+ const maxBytes = Number.isFinite(options.maxBytes)
54
+ ? Math.max(1, Math.floor(options.maxBytes))
55
+ : 1;
56
+ const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)
57
+ ? Math.max(1, Math.floor(options.timeoutMs))
58
+ : DEFAULT_WEBHOOK_BODY_TIMEOUT_MS;
59
+ const encoding = options.encoding ?? "utf-8";
60
+ const declaredLength = parseContentLengthHeader(req);
61
+ if (declaredLength !== null && declaredLength > maxBytes) {
62
+ const error = new RequestBodyLimitError({ code: "PAYLOAD_TOO_LARGE" });
63
+ if (!req.destroyed) {
64
+ // Limit violations are expected user input; destroying with an Error causes
65
+ // an async 'error' event which can crash the process if no listener remains.
66
+ req.destroy();
67
+ }
68
+ throw error;
69
+ }
70
+ return await new Promise((resolve, reject) => {
71
+ let done = false;
72
+ let ended = false;
73
+ let totalBytes = 0;
74
+ const chunks = [];
75
+ const cleanup = () => {
76
+ req.removeListener("data", onData);
77
+ req.removeListener("end", onEnd);
78
+ req.removeListener("error", onError);
79
+ req.removeListener("close", onClose);
80
+ clearTimeout(timer);
81
+ };
82
+ const finish = (cb) => {
83
+ if (done) {
84
+ return;
85
+ }
86
+ done = true;
87
+ cleanup();
88
+ cb();
89
+ };
90
+ const fail = (error) => {
91
+ finish(() => reject(error));
92
+ };
93
+ const timer = setTimeout(() => {
94
+ const error = new RequestBodyLimitError({ code: "REQUEST_BODY_TIMEOUT" });
95
+ if (!req.destroyed) {
96
+ req.destroy();
97
+ }
98
+ fail(error);
99
+ }, timeoutMs);
100
+ const onData = (chunk) => {
101
+ if (done) {
102
+ return;
103
+ }
104
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
105
+ totalBytes += buffer.length;
106
+ if (totalBytes > maxBytes) {
107
+ const error = new RequestBodyLimitError({ code: "PAYLOAD_TOO_LARGE" });
108
+ if (!req.destroyed) {
109
+ req.destroy();
110
+ }
111
+ fail(error);
112
+ return;
113
+ }
114
+ chunks.push(buffer);
115
+ };
116
+ const onEnd = () => {
117
+ ended = true;
118
+ finish(() => resolve(Buffer.concat(chunks).toString(encoding)));
119
+ };
120
+ const onError = (error) => {
121
+ if (done) {
122
+ return;
123
+ }
124
+ fail(error);
125
+ };
126
+ const onClose = () => {
127
+ if (done || ended) {
128
+ return;
129
+ }
130
+ fail(new RequestBodyLimitError({ code: "CONNECTION_CLOSED" }));
131
+ };
132
+ req.on("data", onData);
133
+ req.on("end", onEnd);
134
+ req.on("error", onError);
135
+ req.on("close", onClose);
136
+ });
137
+ }
138
+ export async function readJsonBodyWithLimit(req, options) {
139
+ try {
140
+ const raw = await readRequestBodyWithLimit(req, options);
141
+ const trimmed = raw.trim();
142
+ if (!trimmed) {
143
+ if (options.emptyObjectOnEmpty === false) {
144
+ return { ok: false, code: "INVALID_JSON", error: "empty payload" };
145
+ }
146
+ return { ok: true, value: {} };
147
+ }
148
+ try {
149
+ return { ok: true, value: JSON.parse(trimmed) };
150
+ }
151
+ catch (error) {
152
+ return {
153
+ ok: false,
154
+ code: "INVALID_JSON",
155
+ error: error instanceof Error ? error.message : String(error),
156
+ };
157
+ }
158
+ }
159
+ catch (error) {
160
+ if (isRequestBodyLimitError(error)) {
161
+ return { ok: false, code: error.code, error: requestBodyErrorToText(error.code) };
162
+ }
163
+ return {
164
+ ok: false,
165
+ code: "INVALID_JSON",
166
+ error: error instanceof Error ? error.message : String(error),
167
+ };
168
+ }
169
+ }
170
+ export function installRequestBodyLimitGuard(req, res, options) {
171
+ const maxBytes = Number.isFinite(options.maxBytes)
172
+ ? Math.max(1, Math.floor(options.maxBytes))
173
+ : 1;
174
+ const timeoutMs = typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs)
175
+ ? Math.max(1, Math.floor(options.timeoutMs))
176
+ : DEFAULT_WEBHOOK_BODY_TIMEOUT_MS;
177
+ const responseFormat = options.responseFormat ?? "json";
178
+ const customText = options.responseText ?? {};
179
+ let tripped = false;
180
+ let reason = null;
181
+ let done = false;
182
+ let ended = false;
183
+ let totalBytes = 0;
184
+ const cleanup = () => {
185
+ req.removeListener("data", onData);
186
+ req.removeListener("end", onEnd);
187
+ req.removeListener("close", onClose);
188
+ req.removeListener("error", onError);
189
+ clearTimeout(timer);
190
+ };
191
+ const finish = () => {
192
+ if (done) {
193
+ return;
194
+ }
195
+ done = true;
196
+ cleanup();
197
+ };
198
+ const respond = (error) => {
199
+ const text = customText[error.code] ?? requestBodyErrorToText(error.code);
200
+ if (!res.headersSent) {
201
+ res.statusCode = error.statusCode;
202
+ if (responseFormat === "text") {
203
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
204
+ res.end(text);
205
+ }
206
+ else {
207
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
208
+ res.end(JSON.stringify({ error: text }));
209
+ }
210
+ }
211
+ };
212
+ const trip = (error) => {
213
+ if (tripped) {
214
+ return;
215
+ }
216
+ tripped = true;
217
+ reason = error.code;
218
+ finish();
219
+ respond(error);
220
+ if (!req.destroyed) {
221
+ // Limit violations are expected user input; destroying with an Error causes
222
+ // an async 'error' event which can crash the process if no listener remains.
223
+ req.destroy();
224
+ }
225
+ };
226
+ const onData = (chunk) => {
227
+ if (done) {
228
+ return;
229
+ }
230
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
231
+ totalBytes += buffer.length;
232
+ if (totalBytes > maxBytes) {
233
+ trip(new RequestBodyLimitError({ code: "PAYLOAD_TOO_LARGE" }));
234
+ }
235
+ };
236
+ const onEnd = () => {
237
+ ended = true;
238
+ finish();
239
+ };
240
+ const onClose = () => {
241
+ if (done || ended) {
242
+ return;
243
+ }
244
+ finish();
245
+ };
246
+ const onError = () => {
247
+ finish();
248
+ };
249
+ const timer = setTimeout(() => {
250
+ trip(new RequestBodyLimitError({ code: "REQUEST_BODY_TIMEOUT" }));
251
+ }, timeoutMs);
252
+ req.on("data", onData);
253
+ req.on("end", onEnd);
254
+ req.on("close", onClose);
255
+ req.on("error", onError);
256
+ const declaredLength = parseContentLengthHeader(req);
257
+ if (declaredLength !== null && declaredLength > maxBytes) {
258
+ trip(new RequestBodyLimitError({ code: "PAYLOAD_TOO_LARGE" }));
259
+ }
260
+ return {
261
+ dispose: finish,
262
+ isTripped: () => tripped,
263
+ code: () => reason,
264
+ };
265
+ }
@@ -0,0 +1,50 @@
1
+ import fs from "node:fs/promises";
2
+ import { runCommandWithTimeout } from "../process/exec.js";
3
+ import { fileExists } from "./archive.js";
4
+ export async function installPackageDir(params) {
5
+ params.logger?.info?.(`Installing to ${params.targetDir}…`);
6
+ let backupDir = null;
7
+ if (params.mode === "update" && (await fileExists(params.targetDir))) {
8
+ backupDir = `${params.targetDir}.backup-${Date.now()}`;
9
+ await fs.rename(params.targetDir, backupDir);
10
+ }
11
+ const rollback = async () => {
12
+ if (!backupDir) {
13
+ return;
14
+ }
15
+ await fs.rm(params.targetDir, { recursive: true, force: true }).catch(() => undefined);
16
+ await fs.rename(backupDir, params.targetDir).catch(() => undefined);
17
+ };
18
+ try {
19
+ await fs.cp(params.sourceDir, params.targetDir, { recursive: true });
20
+ }
21
+ catch (err) {
22
+ await rollback();
23
+ return { ok: false, error: `${params.copyErrorPrefix}: ${String(err)}` };
24
+ }
25
+ try {
26
+ await params.afterCopy?.();
27
+ }
28
+ catch (err) {
29
+ await rollback();
30
+ return { ok: false, error: `post-copy validation failed: ${String(err)}` };
31
+ }
32
+ if (params.hasDeps) {
33
+ params.logger?.info?.(params.depsLogMessage);
34
+ const npmRes = await runCommandWithTimeout(["npm", "install", "--omit=dev", "--silent", "--ignore-scripts"], {
35
+ timeoutMs: Math.max(params.timeoutMs, 300_000),
36
+ cwd: params.targetDir,
37
+ });
38
+ if (npmRes.code !== 0) {
39
+ await rollback();
40
+ return {
41
+ ok: false,
42
+ error: `npm install failed: ${npmRes.stderr.trim() || npmRes.stdout.trim()}`,
43
+ };
44
+ }
45
+ }
46
+ if (backupDir) {
47
+ await fs.rm(backupDir, { recursive: true, force: true }).catch(() => undefined);
48
+ }
49
+ return { ok: true };
50
+ }
@@ -0,0 +1,49 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ export function unscopedPackageName(name) {
4
+ const trimmed = name.trim();
5
+ if (!trimmed) {
6
+ return trimmed;
7
+ }
8
+ return trimmed.includes("/") ? (trimmed.split("/").pop() ?? trimmed) : trimmed;
9
+ }
10
+ export function safeDirName(input) {
11
+ const trimmed = input.trim();
12
+ if (!trimmed) {
13
+ return trimmed;
14
+ }
15
+ return trimmed.replaceAll("/", "__").replaceAll("\\", "__");
16
+ }
17
+ export function safePathSegmentHashed(input) {
18
+ const trimmed = input.trim();
19
+ const base = trimmed
20
+ .replaceAll(/[\\/]/g, "-")
21
+ .replaceAll(/[^a-zA-Z0-9._-]/g, "-")
22
+ .replaceAll(/-+/g, "-")
23
+ .replaceAll(/^-+/g, "")
24
+ .replaceAll(/-+$/g, "");
25
+ const normalized = base.length > 0 ? base : "skill";
26
+ const safe = normalized === "." || normalized === ".." ? "skill" : normalized;
27
+ const hash = createHash("sha256").update(trimmed).digest("hex").slice(0, 10);
28
+ if (safe !== trimmed) {
29
+ const prefix = safe.length > 50 ? safe.slice(0, 50) : safe;
30
+ return `${prefix}-${hash}`;
31
+ }
32
+ if (safe.length > 60) {
33
+ return `${safe.slice(0, 50)}-${hash}`;
34
+ }
35
+ return safe;
36
+ }
37
+ export function resolveSafeInstallDir(params) {
38
+ const targetDir = path.join(params.baseDir, safeDirName(params.id));
39
+ const resolvedBase = path.resolve(params.baseDir);
40
+ const resolvedTarget = path.resolve(targetDir);
41
+ const relative = path.relative(resolvedBase, resolvedTarget);
42
+ if (!relative ||
43
+ relative === ".." ||
44
+ relative.startsWith(`..${path.sep}`) ||
45
+ path.isAbsolute(relative)) {
46
+ return { ok: false, error: params.invalidNameMessage };
47
+ }
48
+ return { ok: true, path: targetDir };
49
+ }
@@ -0,0 +1,49 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ export async function readJsonFile(filePath) {
5
+ try {
6
+ const raw = await fs.readFile(filePath, "utf8");
7
+ return JSON.parse(raw);
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ export async function writeJsonAtomic(filePath, value, options) {
14
+ const mode = options?.mode ?? 0o600;
15
+ const dir = path.dirname(filePath);
16
+ await fs.mkdir(dir, { recursive: true });
17
+ const tmp = `${filePath}.${randomUUID()}.tmp`;
18
+ await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
19
+ try {
20
+ await fs.chmod(tmp, mode);
21
+ }
22
+ catch {
23
+ // best-effort; ignore on platforms without chmod
24
+ }
25
+ await fs.rename(tmp, filePath);
26
+ try {
27
+ await fs.chmod(filePath, mode);
28
+ }
29
+ catch {
30
+ // best-effort; ignore on platforms without chmod
31
+ }
32
+ }
33
+ export function createAsyncLock() {
34
+ let lock = Promise.resolve();
35
+ return async function withLock(fn) {
36
+ const prev = lock;
37
+ let release;
38
+ lock = new Promise((resolve) => {
39
+ release = resolve;
40
+ });
41
+ await prev;
42
+ try {
43
+ return await fn();
44
+ }
45
+ finally {
46
+ release?.();
47
+ }
48
+ };
49
+ }