clawdbot 2026.1.4-1

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 (550) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE +21 -0
  3. package/README-header.png +0 -0
  4. package/README.md +297 -0
  5. package/dist/agents/agent-paths.js +17 -0
  6. package/dist/agents/bash-process-registry.js +126 -0
  7. package/dist/agents/bash-tools.js +837 -0
  8. package/dist/agents/clawdbot-tools.js +30 -0
  9. package/dist/agents/clawdis-tools.js +27 -0
  10. package/dist/agents/context.js +34 -0
  11. package/dist/agents/defaults.js +6 -0
  12. package/dist/agents/model-auth.js +112 -0
  13. package/dist/agents/model-catalog.js +55 -0
  14. package/dist/agents/model-fallback.js +191 -0
  15. package/dist/agents/model-scan.js +263 -0
  16. package/dist/agents/model-selection.js +116 -0
  17. package/dist/agents/models-config.js +49 -0
  18. package/dist/agents/pi-embedded-helpers.js +74 -0
  19. package/dist/agents/pi-embedded-runner.js +407 -0
  20. package/dist/agents/pi-embedded-subscribe.js +568 -0
  21. package/dist/agents/pi-embedded-utils.js +20 -0
  22. package/dist/agents/pi-embedded.js +1 -0
  23. package/dist/agents/pi-oauth.js +88 -0
  24. package/dist/agents/pi-tools.js +433 -0
  25. package/dist/agents/sandbox-paths.js +68 -0
  26. package/dist/agents/sandbox.js +644 -0
  27. package/dist/agents/shell-utils.js +53 -0
  28. package/dist/agents/skills-install.js +244 -0
  29. package/dist/agents/skills-status.js +157 -0
  30. package/dist/agents/skills.js +470 -0
  31. package/dist/agents/steerable-agent-loop.js +338 -0
  32. package/dist/agents/steerable-provider-transport.js +48 -0
  33. package/dist/agents/system-prompt.js +104 -0
  34. package/dist/agents/tool-display.js +162 -0
  35. package/dist/agents/tool-images.js +138 -0
  36. package/dist/agents/tools/browser-tool.js +339 -0
  37. package/dist/agents/tools/canvas-tool.js +193 -0
  38. package/dist/agents/tools/common.js +88 -0
  39. package/dist/agents/tools/cron-tool.js +124 -0
  40. package/dist/agents/tools/discord-actions-guild.js +186 -0
  41. package/dist/agents/tools/discord-actions-messaging.js +285 -0
  42. package/dist/agents/tools/discord-actions-moderation.js +70 -0
  43. package/dist/agents/tools/discord-actions.js +56 -0
  44. package/dist/agents/tools/discord-schema.js +199 -0
  45. package/dist/agents/tools/discord-tool.js +16 -0
  46. package/dist/agents/tools/gateway-tool.js +46 -0
  47. package/dist/agents/tools/gateway.js +27 -0
  48. package/dist/agents/tools/image-tool.js +132 -0
  49. package/dist/agents/tools/nodes-tool.js +413 -0
  50. package/dist/agents/tools/nodes-utils.js +92 -0
  51. package/dist/agents/tools/sessions-helpers.js +88 -0
  52. package/dist/agents/tools/sessions-history-tool.js +53 -0
  53. package/dist/agents/tools/sessions-list-tool.js +143 -0
  54. package/dist/agents/tools/sessions-send-helpers.js +100 -0
  55. package/dist/agents/tools/sessions-send-tool.js +347 -0
  56. package/dist/agents/tools/slack-actions.js +129 -0
  57. package/dist/agents/tools/slack-schema.js +59 -0
  58. package/dist/agents/tools/slack-tool.js +16 -0
  59. package/dist/agents/usage.js +39 -0
  60. package/dist/agents/workspace.js +241 -0
  61. package/dist/auto-reply/chunk.js +76 -0
  62. package/dist/auto-reply/envelope.js +38 -0
  63. package/dist/auto-reply/group-activation.js +20 -0
  64. package/dist/auto-reply/heartbeat.js +57 -0
  65. package/dist/auto-reply/model.js +14 -0
  66. package/dist/auto-reply/reply/abort.js +14 -0
  67. package/dist/auto-reply/reply/agent-runner.js +371 -0
  68. package/dist/auto-reply/reply/block-streaming.js +34 -0
  69. package/dist/auto-reply/reply/body.js +29 -0
  70. package/dist/auto-reply/reply/commands.js +207 -0
  71. package/dist/auto-reply/reply/directive-handling.js +361 -0
  72. package/dist/auto-reply/reply/directives.js +47 -0
  73. package/dist/auto-reply/reply/followup-runner.js +149 -0
  74. package/dist/auto-reply/reply/groups.js +91 -0
  75. package/dist/auto-reply/reply/mentions.js +38 -0
  76. package/dist/auto-reply/reply/model-selection.js +114 -0
  77. package/dist/auto-reply/reply/queue.js +399 -0
  78. package/dist/auto-reply/reply/reply-tags.js +26 -0
  79. package/dist/auto-reply/reply/session-updates.js +87 -0
  80. package/dist/auto-reply/reply/session.js +160 -0
  81. package/dist/auto-reply/reply/typing.js +75 -0
  82. package/dist/auto-reply/reply.js +535 -0
  83. package/dist/auto-reply/send-policy.js +28 -0
  84. package/dist/auto-reply/status.js +158 -0
  85. package/dist/auto-reply/templating.js +9 -0
  86. package/dist/auto-reply/thinking.js +49 -0
  87. package/dist/auto-reply/tokens.js +2 -0
  88. package/dist/auto-reply/tool-meta.js +74 -0
  89. package/dist/auto-reply/transcription.js +57 -0
  90. package/dist/auto-reply/types.js +1 -0
  91. package/dist/browser/bridge-server.js +37 -0
  92. package/dist/browser/cdp.js +382 -0
  93. package/dist/browser/chrome.js +432 -0
  94. package/dist/browser/client-actions-core.js +67 -0
  95. package/dist/browser/client-actions-observe.js +24 -0
  96. package/dist/browser/client-actions-types.js +1 -0
  97. package/dist/browser/client-actions.js +3 -0
  98. package/dist/browser/client-fetch.js +43 -0
  99. package/dist/browser/client.js +105 -0
  100. package/dist/browser/config.js +140 -0
  101. package/dist/browser/constants.js +4 -0
  102. package/dist/browser/profiles-service.js +122 -0
  103. package/dist/browser/profiles.js +85 -0
  104. package/dist/browser/pw-ai.js +2 -0
  105. package/dist/browser/pw-session.js +144 -0
  106. package/dist/browser/pw-tools-core.js +363 -0
  107. package/dist/browser/routes/agent.js +535 -0
  108. package/dist/browser/routes/basic.js +155 -0
  109. package/dist/browser/routes/index.js +8 -0
  110. package/dist/browser/routes/tabs.js +105 -0
  111. package/dist/browser/routes/utils.js +62 -0
  112. package/dist/browser/screenshot.js +40 -0
  113. package/dist/browser/server-context.js +377 -0
  114. package/dist/browser/server.js +81 -0
  115. package/dist/browser/target-id.js +18 -0
  116. package/dist/browser/trash.js +21 -0
  117. package/dist/canvas-host/a2ui/.bundle.hash +1 -0
  118. package/dist/canvas-host/a2ui/a2ui.bundle.js +17768 -0
  119. package/dist/canvas-host/a2ui/index.html +246 -0
  120. package/dist/canvas-host/a2ui.js +187 -0
  121. package/dist/canvas-host/server.js +382 -0
  122. package/dist/cli/browser-cli-actions-input.js +459 -0
  123. package/dist/cli/browser-cli-actions-observe.js +56 -0
  124. package/dist/cli/browser-cli-examples.js +31 -0
  125. package/dist/cli/browser-cli-inspect.js +97 -0
  126. package/dist/cli/browser-cli-manage.js +286 -0
  127. package/dist/cli/browser-cli-shared.js +1 -0
  128. package/dist/cli/browser-cli.js +26 -0
  129. package/dist/cli/canvas-cli.js +416 -0
  130. package/dist/cli/cron-cli.js +454 -0
  131. package/dist/cli/deps.js +17 -0
  132. package/dist/cli/dns-cli.js +180 -0
  133. package/dist/cli/gateway-cli.js +489 -0
  134. package/dist/cli/gateway-rpc.js +20 -0
  135. package/dist/cli/hooks-cli.js +135 -0
  136. package/dist/cli/models-cli.js +248 -0
  137. package/dist/cli/nodes-camera.js +57 -0
  138. package/dist/cli/nodes-canvas.js +26 -0
  139. package/dist/cli/nodes-cli.js +946 -0
  140. package/dist/cli/nodes-screen.js +37 -0
  141. package/dist/cli/parse-duration.js +20 -0
  142. package/dist/cli/ports.js +97 -0
  143. package/dist/cli/program.js +406 -0
  144. package/dist/cli/prompt.js +19 -0
  145. package/dist/cli/tui-cli.js +35 -0
  146. package/dist/cli/wait.js +8 -0
  147. package/dist/commands/agent.js +645 -0
  148. package/dist/commands/antigravity-oauth.js +327 -0
  149. package/dist/commands/configure.js +480 -0
  150. package/dist/commands/doctor.js +484 -0
  151. package/dist/commands/health.js +108 -0
  152. package/dist/commands/models/aliases.js +64 -0
  153. package/dist/commands/models/fallbacks.js +99 -0
  154. package/dist/commands/models/image-fallbacks.js +99 -0
  155. package/dist/commands/models/list.js +323 -0
  156. package/dist/commands/models/scan.js +266 -0
  157. package/dist/commands/models/set-image.js +23 -0
  158. package/dist/commands/models/set.js +23 -0
  159. package/dist/commands/models/shared.js +72 -0
  160. package/dist/commands/models.js +7 -0
  161. package/dist/commands/onboard-auth.js +70 -0
  162. package/dist/commands/onboard-helpers.js +295 -0
  163. package/dist/commands/onboard-interactive.js +17 -0
  164. package/dist/commands/onboard-non-interactive.js +202 -0
  165. package/dist/commands/onboard-providers.js +634 -0
  166. package/dist/commands/onboard-remote.js +120 -0
  167. package/dist/commands/onboard-skills.js +148 -0
  168. package/dist/commands/onboard-types.js +1 -0
  169. package/dist/commands/onboard.js +12 -0
  170. package/dist/commands/send.js +124 -0
  171. package/dist/commands/sessions.js +212 -0
  172. package/dist/commands/setup.js +58 -0
  173. package/dist/commands/signal-install.js +135 -0
  174. package/dist/commands/status.js +207 -0
  175. package/dist/commands/update.js +16 -0
  176. package/dist/config/config.js +6 -0
  177. package/dist/config/defaults.js +61 -0
  178. package/dist/config/io.js +147 -0
  179. package/dist/config/legacy-migrate.js +13 -0
  180. package/dist/config/legacy.js +159 -0
  181. package/dist/config/paths.js +71 -0
  182. package/dist/config/schema.js +150 -0
  183. package/dist/config/sessions.js +282 -0
  184. package/dist/config/talk.js +31 -0
  185. package/dist/config/types.js +1 -0
  186. package/dist/config/validation.js +29 -0
  187. package/dist/config/zod-schema.js +831 -0
  188. package/dist/control-ui/assets/index-BFID3yAA.css +1 -0
  189. package/dist/control-ui/assets/index-CE_axlTS.js +2235 -0
  190. package/dist/control-ui/assets/index-CE_axlTS.js.map +1 -0
  191. package/dist/control-ui/index.html +15 -0
  192. package/dist/cron/isolated-agent.js +499 -0
  193. package/dist/cron/run-log.js +72 -0
  194. package/dist/cron/schedule.js +24 -0
  195. package/dist/cron/service.js +471 -0
  196. package/dist/cron/store.js +43 -0
  197. package/dist/cron/types.js +1 -0
  198. package/dist/daemon/constants.js +10 -0
  199. package/dist/daemon/launchd.js +276 -0
  200. package/dist/daemon/legacy.js +63 -0
  201. package/dist/daemon/program-args.js +76 -0
  202. package/dist/daemon/schtasks.js +257 -0
  203. package/dist/daemon/service.js +60 -0
  204. package/dist/daemon/systemd.js +266 -0
  205. package/dist/discord/index.js +2 -0
  206. package/dist/discord/monitor.js +1188 -0
  207. package/dist/discord/probe.js +54 -0
  208. package/dist/discord/send.js +577 -0
  209. package/dist/discord/token.js +8 -0
  210. package/dist/gateway/auth.js +121 -0
  211. package/dist/gateway/call.js +94 -0
  212. package/dist/gateway/chat-attachments.js +41 -0
  213. package/dist/gateway/client.js +180 -0
  214. package/dist/gateway/config-reload.js +274 -0
  215. package/dist/gateway/control-ui.js +184 -0
  216. package/dist/gateway/hooks-mapping.js +282 -0
  217. package/dist/gateway/hooks.js +168 -0
  218. package/dist/gateway/net.js +29 -0
  219. package/dist/gateway/protocol/index.js +61 -0
  220. package/dist/gateway/protocol/schema.js +560 -0
  221. package/dist/gateway/server-bridge-subscriptions.js +93 -0
  222. package/dist/gateway/server-bridge.js +1013 -0
  223. package/dist/gateway/server-browser.js +12 -0
  224. package/dist/gateway/server-chat.js +159 -0
  225. package/dist/gateway/server-constants.js +8 -0
  226. package/dist/gateway/server-discovery.js +62 -0
  227. package/dist/gateway/server-http.js +165 -0
  228. package/dist/gateway/server-methods/agent-job.js +125 -0
  229. package/dist/gateway/server-methods/agent.js +250 -0
  230. package/dist/gateway/server-methods/chat.js +200 -0
  231. package/dist/gateway/server-methods/config.js +50 -0
  232. package/dist/gateway/server-methods/connect.js +6 -0
  233. package/dist/gateway/server-methods/cron.js +83 -0
  234. package/dist/gateway/server-methods/health.js +28 -0
  235. package/dist/gateway/server-methods/models.js +16 -0
  236. package/dist/gateway/server-methods/nodes.js +294 -0
  237. package/dist/gateway/server-methods/providers.js +217 -0
  238. package/dist/gateway/server-methods/send.js +166 -0
  239. package/dist/gateway/server-methods/sessions.js +305 -0
  240. package/dist/gateway/server-methods/skills.js +83 -0
  241. package/dist/gateway/server-methods/system.js +118 -0
  242. package/dist/gateway/server-methods/talk.js +22 -0
  243. package/dist/gateway/server-methods/types.js +1 -0
  244. package/dist/gateway/server-methods/voicewake.js +30 -0
  245. package/dist/gateway/server-methods/web.js +58 -0
  246. package/dist/gateway/server-methods/wizard.js +100 -0
  247. package/dist/gateway/server-methods.js +53 -0
  248. package/dist/gateway/server-providers.js +644 -0
  249. package/dist/gateway/server-shared.js +1 -0
  250. package/dist/gateway/server-utils.js +35 -0
  251. package/dist/gateway/server.js +1437 -0
  252. package/dist/gateway/session-utils.js +216 -0
  253. package/dist/gateway/ws-log.js +349 -0
  254. package/dist/gateway/ws-logging.js +8 -0
  255. package/dist/globals.js +41 -0
  256. package/dist/hooks/gmail-ops.js +236 -0
  257. package/dist/hooks/gmail-setup-utils.js +278 -0
  258. package/dist/hooks/gmail-watcher.js +175 -0
  259. package/dist/hooks/gmail.js +177 -0
  260. package/dist/imessage/client.js +165 -0
  261. package/dist/imessage/index.js +3 -0
  262. package/dist/imessage/monitor.js +272 -0
  263. package/dist/imessage/probe.js +26 -0
  264. package/dist/imessage/send.js +83 -0
  265. package/dist/imessage/targets.js +176 -0
  266. package/dist/index.js +50 -0
  267. package/dist/infra/agent-events.js +46 -0
  268. package/dist/infra/binaries.js +9 -0
  269. package/dist/infra/bonjour-discovery.js +163 -0
  270. package/dist/infra/bonjour.js +200 -0
  271. package/dist/infra/bridge/server.js +562 -0
  272. package/dist/infra/canvas-host-url.js +54 -0
  273. package/dist/infra/env.js +8 -0
  274. package/dist/infra/errors.js +28 -0
  275. package/dist/infra/gateway-lock.js +8 -0
  276. package/dist/infra/heartbeat-events.js +21 -0
  277. package/dist/infra/heartbeat-runner.js +453 -0
  278. package/dist/infra/heartbeat-wake.js +61 -0
  279. package/dist/infra/is-main.js +37 -0
  280. package/dist/infra/machine-name.js +40 -0
  281. package/dist/infra/node-pairing.js +211 -0
  282. package/dist/infra/pam.js +42 -0
  283. package/dist/infra/path-env.js +92 -0
  284. package/dist/infra/ports.js +87 -0
  285. package/dist/infra/provider-summary.js +80 -0
  286. package/dist/infra/restart.js +29 -0
  287. package/dist/infra/retry.js +16 -0
  288. package/dist/infra/runtime-guard.js +59 -0
  289. package/dist/infra/system-events.js +44 -0
  290. package/dist/infra/system-presence.js +216 -0
  291. package/dist/infra/tailnet.js +46 -0
  292. package/dist/infra/tailscale.js +149 -0
  293. package/dist/infra/voicewake.js +77 -0
  294. package/dist/infra/widearea-dns.js +123 -0
  295. package/dist/infra/ws.js +13 -0
  296. package/dist/logger.js +52 -0
  297. package/dist/logging.js +490 -0
  298. package/dist/macos/gateway-daemon.js +141 -0
  299. package/dist/macos/relay.js +46 -0
  300. package/dist/media/constants.js +33 -0
  301. package/dist/media/host.js +42 -0
  302. package/dist/media/image-ops.js +121 -0
  303. package/dist/media/mime.js +115 -0
  304. package/dist/media/parse.js +81 -0
  305. package/dist/media/server.js +64 -0
  306. package/dist/media/store.js +139 -0
  307. package/dist/process/command-queue.js +97 -0
  308. package/dist/process/exec.js +75 -0
  309. package/dist/protocol.schema.json +2918 -0
  310. package/dist/provider-web.js +8 -0
  311. package/dist/providers/web/index.js +2 -0
  312. package/dist/runtime.js +8 -0
  313. package/dist/sessions/send-policy.js +68 -0
  314. package/dist/signal/client.js +134 -0
  315. package/dist/signal/daemon.js +69 -0
  316. package/dist/signal/index.js +3 -0
  317. package/dist/signal/monitor.js +336 -0
  318. package/dist/signal/probe.js +46 -0
  319. package/dist/signal/send.js +91 -0
  320. package/dist/slack/actions.js +97 -0
  321. package/dist/slack/index.js +5 -0
  322. package/dist/slack/monitor.js +1029 -0
  323. package/dist/slack/probe.js +47 -0
  324. package/dist/slack/send.js +131 -0
  325. package/dist/slack/token.js +10 -0
  326. package/dist/telegram/bot.js +394 -0
  327. package/dist/telegram/download.js +34 -0
  328. package/dist/telegram/index.js +4 -0
  329. package/dist/telegram/monitor.js +47 -0
  330. package/dist/telegram/probe.js +63 -0
  331. package/dist/telegram/proxy.js +9 -0
  332. package/dist/telegram/send.js +138 -0
  333. package/dist/telegram/token.js +30 -0
  334. package/dist/telegram/webhook-set.js +12 -0
  335. package/dist/telegram/webhook.js +56 -0
  336. package/dist/tui/commands.js +74 -0
  337. package/dist/tui/components/assistant-message.js +16 -0
  338. package/dist/tui/components/chat-log.js +92 -0
  339. package/dist/tui/components/custom-editor.js +53 -0
  340. package/dist/tui/components/selectors.js +8 -0
  341. package/dist/tui/components/tool-execution.js +111 -0
  342. package/dist/tui/components/user-message.js +17 -0
  343. package/dist/tui/gateway-chat.js +140 -0
  344. package/dist/tui/layout.js +41 -0
  345. package/dist/tui/message-list.js +57 -0
  346. package/dist/tui/theme/theme.js +80 -0
  347. package/dist/tui/theme.js +25 -0
  348. package/dist/tui/tui.js +708 -0
  349. package/dist/utils.js +133 -0
  350. package/dist/version.js +18 -0
  351. package/dist/web/active-listener.js +7 -0
  352. package/dist/web/auto-reply.js +1203 -0
  353. package/dist/web/inbound.js +481 -0
  354. package/dist/web/login-qr.js +204 -0
  355. package/dist/web/login.js +59 -0
  356. package/dist/web/media.js +148 -0
  357. package/dist/web/outbound.js +67 -0
  358. package/dist/web/qr-image.js +97 -0
  359. package/dist/web/reconnect.js +60 -0
  360. package/dist/web/reply-heartbeat-wake.js +61 -0
  361. package/dist/web/session.js +346 -0
  362. package/dist/wizard/clack-prompter.js +56 -0
  363. package/dist/wizard/onboarding.js +452 -0
  364. package/dist/wizard/prompts.js +6 -0
  365. package/dist/wizard/session.js +203 -0
  366. package/docs/AGENTS.default.md +116 -0
  367. package/docs/CNAME +1 -0
  368. package/docs/RELEASING.md +64 -0
  369. package/docs/_config.yml +51 -0
  370. package/docs/_layouts/default.html +145 -0
  371. package/docs/agent-send.md +21 -0
  372. package/docs/agent.md +104 -0
  373. package/docs/android/connect.md +131 -0
  374. package/docs/architecture.md +89 -0
  375. package/docs/assets/markdown.css +130 -0
  376. package/docs/assets/pixel-lobster.svg +60 -0
  377. package/docs/assets/terminal.css +497 -0
  378. package/docs/assets/theme.js +55 -0
  379. package/docs/audio.md +50 -0
  380. package/docs/background-process.md +74 -0
  381. package/docs/bash.md +32 -0
  382. package/docs/bonjour.md +159 -0
  383. package/docs/browser.md +289 -0
  384. package/docs/camera.md +152 -0
  385. package/docs/clawd.md +199 -0
  386. package/docs/clawdbot-mac.md +104 -0
  387. package/docs/configuration.md +1177 -0
  388. package/docs/control-api.md +49 -0
  389. package/docs/control-ui.md +83 -0
  390. package/docs/cron.md +374 -0
  391. package/docs/dashboard.md +17 -0
  392. package/docs/device-models.md +46 -0
  393. package/docs/discord.md +293 -0
  394. package/docs/discovery.md +112 -0
  395. package/docs/docker.md +251 -0
  396. package/docs/docs.json +86 -0
  397. package/docs/doctor.md +47 -0
  398. package/docs/elevated.md +31 -0
  399. package/docs/faq.md +640 -0
  400. package/docs/gateway/pairing.md +109 -0
  401. package/docs/gateway-lock.md +28 -0
  402. package/docs/gateway.md +174 -0
  403. package/docs/gmail-pubsub.md +191 -0
  404. package/docs/grammy.md +27 -0
  405. package/docs/group-messages.md +71 -0
  406. package/docs/groups.md +78 -0
  407. package/docs/health.md +28 -0
  408. package/docs/heartbeat.md +64 -0
  409. package/docs/images.md +52 -0
  410. package/docs/imessage.md +63 -0
  411. package/docs/index.md +182 -0
  412. package/docs/ios/connect.md +177 -0
  413. package/docs/ios/spec.md +236 -0
  414. package/docs/location-command.md +95 -0
  415. package/docs/logging.md +99 -0
  416. package/docs/lore.md +131 -0
  417. package/docs/mac/bun.md +133 -0
  418. package/docs/mac/canvas.md +161 -0
  419. package/docs/mac/child-process.md +72 -0
  420. package/docs/mac/dev-setup.md +81 -0
  421. package/docs/mac/health.md +28 -0
  422. package/docs/mac/icon.md +26 -0
  423. package/docs/mac/logging.md +51 -0
  424. package/docs/mac/menu-bar.md +69 -0
  425. package/docs/mac/peekaboo.md +170 -0
  426. package/docs/mac/permissions.md +40 -0
  427. package/docs/mac/release.md +76 -0
  428. package/docs/mac/remote.md +57 -0
  429. package/docs/mac/signing.md +41 -0
  430. package/docs/mac/skills.md +27 -0
  431. package/docs/mac/voice-overlay.md +52 -0
  432. package/docs/mac/voicewake.md +56 -0
  433. package/docs/mac/webchat.md +27 -0
  434. package/docs/mac/xpc.md +40 -0
  435. package/docs/models.md +90 -0
  436. package/docs/nix.md +49 -0
  437. package/docs/nodes.md +157 -0
  438. package/docs/onboarding-config-protocol.md +29 -0
  439. package/docs/onboarding.md +185 -0
  440. package/docs/presence.md +133 -0
  441. package/docs/queue.md +78 -0
  442. package/docs/refactor/browser-control-simplification.md +58 -0
  443. package/docs/refactor/canvas-a2ui.md +93 -0
  444. package/docs/refactor/cli-unification.md +64 -0
  445. package/docs/refactor/gateway-client.md +31 -0
  446. package/docs/refactor/gateway.md +99 -0
  447. package/docs/refactor/new-arch.md +171 -0
  448. package/docs/refactor/tui.md +26 -0
  449. package/docs/refactor/web-gateway-troubleshooting.md +37 -0
  450. package/docs/refactor/webagent-session.md +46 -0
  451. package/docs/remote-gateway-readme.md +148 -0
  452. package/docs/remote.md +66 -0
  453. package/docs/research/memory.md +227 -0
  454. package/docs/rpc.md +35 -0
  455. package/docs/security.md +168 -0
  456. package/docs/session-tool.md +119 -0
  457. package/docs/session.md +84 -0
  458. package/docs/sessions.md +8 -0
  459. package/docs/setup.md +118 -0
  460. package/docs/signal.md +113 -0
  461. package/docs/skills-config.md +58 -0
  462. package/docs/skills.md +149 -0
  463. package/docs/slack.md +158 -0
  464. package/docs/surface.md +20 -0
  465. package/docs/tailscale.md +71 -0
  466. package/docs/talk.md +79 -0
  467. package/docs/telegram.md +90 -0
  468. package/docs/templates/AGENTS.md +126 -0
  469. package/docs/templates/BOOTSTRAP.md +53 -0
  470. package/docs/templates/IDENTITY.md +17 -0
  471. package/docs/templates/SOUL.md +41 -0
  472. package/docs/templates/TOOLS.md +41 -0
  473. package/docs/templates/USER.md +22 -0
  474. package/docs/test.md +35 -0
  475. package/docs/thinking.md +46 -0
  476. package/docs/tools.md +248 -0
  477. package/docs/troubleshooting.md +227 -0
  478. package/docs/tui.md +69 -0
  479. package/docs/typebox.md +42 -0
  480. package/docs/voicewake.md +61 -0
  481. package/docs/web.md +115 -0
  482. package/docs/webchat.md +34 -0
  483. package/docs/webhook.md +132 -0
  484. package/docs/whatsapp-clawd.jpg +0 -0
  485. package/docs/whatsapp.md +142 -0
  486. package/docs/wizard.md +158 -0
  487. package/package.json +186 -0
  488. package/skills/apple-notes/SKILL.md +50 -0
  489. package/skills/apple-reminders/SKILL.md +67 -0
  490. package/skills/bear-notes/SKILL.md +79 -0
  491. package/skills/bird/SKILL.md +25 -0
  492. package/skills/blogwatcher/SKILL.md +46 -0
  493. package/skills/blucli/SKILL.md +27 -0
  494. package/skills/brave-search/SKILL.md +30 -0
  495. package/skills/brave-search/scripts/content.mjs +53 -0
  496. package/skills/brave-search/scripts/search.mjs +79 -0
  497. package/skills/camsnap/SKILL.md +25 -0
  498. package/skills/clawdhub/SKILL.md +53 -0
  499. package/skills/coding-agent/SKILL.md +275 -0
  500. package/skills/discord/SKILL.md +369 -0
  501. package/skills/eightctl/SKILL.md +29 -0
  502. package/skills/food-order/SKILL.md +41 -0
  503. package/skills/gemini/SKILL.md +23 -0
  504. package/skills/gifgrep/SKILL.md +47 -0
  505. package/skills/github/SKILL.md +47 -0
  506. package/skills/gog/SKILL.md +36 -0
  507. package/skills/goplaces/SKILL.md +30 -0
  508. package/skills/imsg/SKILL.md +25 -0
  509. package/skills/local-places/SERVER_README.md +101 -0
  510. package/skills/local-places/SKILL.md +91 -0
  511. package/skills/local-places/pyproject.toml +27 -0
  512. package/skills/local-places/src/local_places/__init__.py +2 -0
  513. package/skills/local-places/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
  514. package/skills/local-places/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
  515. package/skills/local-places/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
  516. package/skills/local-places/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
  517. package/skills/local-places/src/local_places/google_places.py +314 -0
  518. package/skills/local-places/src/local_places/main.py +65 -0
  519. package/skills/local-places/src/local_places/schemas.py +107 -0
  520. package/skills/mcporter/SKILL.md +38 -0
  521. package/skills/nano-banana-pro/SKILL.md +29 -0
  522. package/skills/nano-banana-pro/scripts/generate_image.py +167 -0
  523. package/skills/nano-pdf/SKILL.md +20 -0
  524. package/skills/notion/SKILL.md +156 -0
  525. package/skills/obsidian/SKILL.md +55 -0
  526. package/skills/openai-image-gen/SKILL.md +31 -0
  527. package/skills/openai-image-gen/scripts/gen.py +173 -0
  528. package/skills/openai-whisper/SKILL.md +19 -0
  529. package/skills/openai-whisper-api/SKILL.md +43 -0
  530. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  531. package/skills/openhue/SKILL.md +30 -0
  532. package/skills/oracle/SKILL.md +105 -0
  533. package/skills/ordercli/SKILL.md +47 -0
  534. package/skills/peekaboo/SKILL.md +153 -0
  535. package/skills/qmd/SKILL.md +26 -0
  536. package/skills/sag/SKILL.md +62 -0
  537. package/skills/slack/SKILL.md +143 -0
  538. package/skills/songsee/SKILL.md +29 -0
  539. package/skills/sonoscli/SKILL.md +26 -0
  540. package/skills/spotify-player/SKILL.md +34 -0
  541. package/skills/summarize/SKILL.md +49 -0
  542. package/skills/things-mac/SKILL.md +61 -0
  543. package/skills/tmux/SKILL.md +121 -0
  544. package/skills/tmux/scripts/find-sessions.sh +112 -0
  545. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  546. package/skills/trello/SKILL.md +84 -0
  547. package/skills/video-frames/SKILL.md +29 -0
  548. package/skills/video-frames/scripts/frame.sh +81 -0
  549. package/skills/wacli/SKILL.md +42 -0
  550. package/skills/weather/SKILL.md +49 -0
@@ -0,0 +1,644 @@
1
+ import { spawn } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { startBrowserBridgeServer, stopBrowserBridgeServer, } from "../browser/bridge-server.js";
7
+ import { resolveProfile, } from "../browser/config.js";
8
+ import { DEFAULT_CLAWD_BROWSER_COLOR } from "../browser/constants.js";
9
+ import { STATE_DIR_CLAWDBOT } from "../config/config.js";
10
+ import { defaultRuntime } from "../runtime.js";
11
+ import { resolveUserPath } from "../utils.js";
12
+ import { DEFAULT_AGENT_WORKSPACE_DIR, DEFAULT_AGENTS_FILENAME, DEFAULT_BOOTSTRAP_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, DEFAULT_USER_FILENAME, ensureAgentWorkspace, } from "./workspace.js";
13
+ const DEFAULT_SANDBOX_WORKSPACE_ROOT = path.join(os.homedir(), ".clawdbot", "sandboxes");
14
+ export const DEFAULT_SANDBOX_IMAGE = "clawdbot-sandbox:bookworm-slim";
15
+ const DEFAULT_SANDBOX_CONTAINER_PREFIX = "clawdbot-sbx-";
16
+ const DEFAULT_SANDBOX_WORKDIR = "/workspace";
17
+ const DEFAULT_SANDBOX_IDLE_HOURS = 24;
18
+ const DEFAULT_SANDBOX_MAX_AGE_DAYS = 7;
19
+ const DEFAULT_TOOL_ALLOW = ["bash", "process", "read", "write", "edit"];
20
+ const DEFAULT_TOOL_DENY = [
21
+ "browser",
22
+ "canvas",
23
+ "nodes",
24
+ "cron",
25
+ "discord",
26
+ "gateway",
27
+ ];
28
+ export const DEFAULT_SANDBOX_BROWSER_IMAGE = "clawdbot-sandbox-browser:bookworm-slim";
29
+ export const DEFAULT_SANDBOX_COMMON_IMAGE = "clawdbot-sandbox-common:bookworm-slim";
30
+ const DEFAULT_SANDBOX_BROWSER_PREFIX = "clawdbot-sbx-browser-";
31
+ const DEFAULT_SANDBOX_BROWSER_CDP_PORT = 9222;
32
+ const DEFAULT_SANDBOX_BROWSER_VNC_PORT = 5900;
33
+ const DEFAULT_SANDBOX_BROWSER_NOVNC_PORT = 6080;
34
+ const SANDBOX_STATE_DIR = path.join(STATE_DIR_CLAWDBOT, "sandbox");
35
+ const SANDBOX_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "containers.json");
36
+ const SANDBOX_BROWSER_REGISTRY_PATH = path.join(SANDBOX_STATE_DIR, "browsers.json");
37
+ let lastPruneAtMs = 0;
38
+ const BROWSER_BRIDGES = new Map();
39
+ function normalizeToolList(values) {
40
+ if (!values)
41
+ return [];
42
+ return values
43
+ .map((value) => value.trim())
44
+ .filter(Boolean)
45
+ .map((value) => value.toLowerCase());
46
+ }
47
+ function isToolAllowed(policy, name) {
48
+ const deny = new Set(normalizeToolList(policy.deny));
49
+ if (deny.has(name.toLowerCase()))
50
+ return false;
51
+ const allow = normalizeToolList(policy.allow);
52
+ if (allow.length === 0)
53
+ return true;
54
+ return allow.includes(name.toLowerCase());
55
+ }
56
+ function defaultSandboxConfig(cfg) {
57
+ const agent = cfg?.agent?.sandbox;
58
+ return {
59
+ mode: agent?.mode ?? "off",
60
+ perSession: agent?.perSession ?? true,
61
+ workspaceRoot: agent?.workspaceRoot ?? DEFAULT_SANDBOX_WORKSPACE_ROOT,
62
+ docker: {
63
+ image: agent?.docker?.image ?? DEFAULT_SANDBOX_IMAGE,
64
+ containerPrefix: agent?.docker?.containerPrefix ?? DEFAULT_SANDBOX_CONTAINER_PREFIX,
65
+ workdir: agent?.docker?.workdir ?? DEFAULT_SANDBOX_WORKDIR,
66
+ readOnlyRoot: agent?.docker?.readOnlyRoot ?? true,
67
+ tmpfs: agent?.docker?.tmpfs ?? ["/tmp", "/var/tmp", "/run"],
68
+ network: agent?.docker?.network ?? "none",
69
+ user: agent?.docker?.user,
70
+ capDrop: agent?.docker?.capDrop ?? ["ALL"],
71
+ env: agent?.docker?.env ?? { LANG: "C.UTF-8" },
72
+ setupCommand: agent?.docker?.setupCommand,
73
+ pidsLimit: agent?.docker?.pidsLimit,
74
+ memory: agent?.docker?.memory,
75
+ memorySwap: agent?.docker?.memorySwap,
76
+ cpus: agent?.docker?.cpus,
77
+ ulimits: agent?.docker?.ulimits,
78
+ seccompProfile: agent?.docker?.seccompProfile,
79
+ apparmorProfile: agent?.docker?.apparmorProfile,
80
+ dns: agent?.docker?.dns,
81
+ extraHosts: agent?.docker?.extraHosts,
82
+ },
83
+ browser: {
84
+ enabled: agent?.browser?.enabled ?? false,
85
+ image: agent?.browser?.image ?? DEFAULT_SANDBOX_BROWSER_IMAGE,
86
+ containerPrefix: agent?.browser?.containerPrefix ?? DEFAULT_SANDBOX_BROWSER_PREFIX,
87
+ cdpPort: agent?.browser?.cdpPort ?? DEFAULT_SANDBOX_BROWSER_CDP_PORT,
88
+ vncPort: agent?.browser?.vncPort ?? DEFAULT_SANDBOX_BROWSER_VNC_PORT,
89
+ noVncPort: agent?.browser?.noVncPort ?? DEFAULT_SANDBOX_BROWSER_NOVNC_PORT,
90
+ headless: agent?.browser?.headless ?? false,
91
+ enableNoVnc: agent?.browser?.enableNoVnc ?? true,
92
+ },
93
+ tools: {
94
+ allow: agent?.tools?.allow ?? DEFAULT_TOOL_ALLOW,
95
+ deny: agent?.tools?.deny ?? DEFAULT_TOOL_DENY,
96
+ },
97
+ prune: {
98
+ idleHours: agent?.prune?.idleHours ?? DEFAULT_SANDBOX_IDLE_HOURS,
99
+ maxAgeDays: agent?.prune?.maxAgeDays ?? DEFAULT_SANDBOX_MAX_AGE_DAYS,
100
+ },
101
+ };
102
+ }
103
+ function shouldSandboxSession(cfg, sessionKey, mainKey) {
104
+ if (cfg.mode === "off")
105
+ return false;
106
+ if (cfg.mode === "all")
107
+ return true;
108
+ return sessionKey.trim() !== mainKey.trim();
109
+ }
110
+ function slugifySessionKey(value) {
111
+ const trimmed = value.trim() || "session";
112
+ const hash = crypto
113
+ .createHash("sha1")
114
+ .update(trimmed)
115
+ .digest("hex")
116
+ .slice(0, 8);
117
+ const safe = trimmed
118
+ .toLowerCase()
119
+ .replace(/[^a-z0-9._-]+/g, "-")
120
+ .replace(/^-+|-+$/g, "");
121
+ const base = safe.slice(0, 32) || "session";
122
+ return `${base}-${hash}`;
123
+ }
124
+ function resolveSandboxWorkspaceDir(root, sessionKey) {
125
+ const resolvedRoot = resolveUserPath(root);
126
+ const slug = slugifySessionKey(sessionKey);
127
+ return path.join(resolvedRoot, slug);
128
+ }
129
+ async function readRegistry() {
130
+ try {
131
+ const raw = await fs.readFile(SANDBOX_REGISTRY_PATH, "utf-8");
132
+ const parsed = JSON.parse(raw);
133
+ if (parsed && Array.isArray(parsed.entries))
134
+ return parsed;
135
+ }
136
+ catch {
137
+ // ignore
138
+ }
139
+ return { entries: [] };
140
+ }
141
+ async function writeRegistry(registry) {
142
+ await fs.mkdir(SANDBOX_STATE_DIR, { recursive: true });
143
+ await fs.writeFile(SANDBOX_REGISTRY_PATH, `${JSON.stringify(registry, null, 2)}\n`, "utf-8");
144
+ }
145
+ async function updateRegistry(entry) {
146
+ const registry = await readRegistry();
147
+ const existing = registry.entries.find((item) => item.containerName === entry.containerName);
148
+ const next = registry.entries.filter((item) => item.containerName !== entry.containerName);
149
+ next.push({
150
+ ...entry,
151
+ createdAtMs: existing?.createdAtMs ?? entry.createdAtMs,
152
+ image: existing?.image ?? entry.image,
153
+ });
154
+ await writeRegistry({ entries: next });
155
+ }
156
+ async function removeRegistryEntry(containerName) {
157
+ const registry = await readRegistry();
158
+ const next = registry.entries.filter((item) => item.containerName !== containerName);
159
+ if (next.length === registry.entries.length)
160
+ return;
161
+ await writeRegistry({ entries: next });
162
+ }
163
+ async function readBrowserRegistry() {
164
+ try {
165
+ const raw = await fs.readFile(SANDBOX_BROWSER_REGISTRY_PATH, "utf-8");
166
+ const parsed = JSON.parse(raw);
167
+ if (parsed && Array.isArray(parsed.entries))
168
+ return parsed;
169
+ }
170
+ catch {
171
+ // ignore
172
+ }
173
+ return { entries: [] };
174
+ }
175
+ async function writeBrowserRegistry(registry) {
176
+ await fs.mkdir(SANDBOX_STATE_DIR, { recursive: true });
177
+ await fs.writeFile(SANDBOX_BROWSER_REGISTRY_PATH, `${JSON.stringify(registry, null, 2)}\n`, "utf-8");
178
+ }
179
+ async function updateBrowserRegistry(entry) {
180
+ const registry = await readBrowserRegistry();
181
+ const existing = registry.entries.find((item) => item.containerName === entry.containerName);
182
+ const next = registry.entries.filter((item) => item.containerName !== entry.containerName);
183
+ next.push({
184
+ ...entry,
185
+ createdAtMs: existing?.createdAtMs ?? entry.createdAtMs,
186
+ image: existing?.image ?? entry.image,
187
+ });
188
+ await writeBrowserRegistry({ entries: next });
189
+ }
190
+ async function removeBrowserRegistryEntry(containerName) {
191
+ const registry = await readBrowserRegistry();
192
+ const next = registry.entries.filter((item) => item.containerName !== containerName);
193
+ if (next.length === registry.entries.length)
194
+ return;
195
+ await writeBrowserRegistry({ entries: next });
196
+ }
197
+ function execDocker(args, opts) {
198
+ return new Promise((resolve, reject) => {
199
+ const child = spawn("docker", args, {
200
+ stdio: ["ignore", "pipe", "pipe"],
201
+ });
202
+ let stdout = "";
203
+ let stderr = "";
204
+ child.stdout?.on("data", (chunk) => {
205
+ stdout += chunk.toString();
206
+ });
207
+ child.stderr?.on("data", (chunk) => {
208
+ stderr += chunk.toString();
209
+ });
210
+ child.on("close", (code) => {
211
+ const exitCode = code ?? 0;
212
+ if (exitCode !== 0 && !opts?.allowFailure) {
213
+ reject(new Error(stderr.trim() || `docker ${args.join(" ")} failed`));
214
+ return;
215
+ }
216
+ resolve({ stdout, stderr, code: exitCode });
217
+ });
218
+ });
219
+ }
220
+ async function readDockerPort(containerName, port) {
221
+ const result = await execDocker(["port", containerName, `${port}/tcp`], {
222
+ allowFailure: true,
223
+ });
224
+ if (result.code !== 0)
225
+ return null;
226
+ const line = result.stdout.trim().split(/\r?\n/)[0] ?? "";
227
+ const match = line.match(/:(\d+)\s*$/);
228
+ if (!match)
229
+ return null;
230
+ const mapped = Number.parseInt(match[1] ?? "", 10);
231
+ return Number.isFinite(mapped) ? mapped : null;
232
+ }
233
+ async function dockerImageExists(image) {
234
+ const result = await execDocker(["image", "inspect", image], {
235
+ allowFailure: true,
236
+ });
237
+ return result.code === 0;
238
+ }
239
+ async function ensureDockerImage(image) {
240
+ const exists = await dockerImageExists(image);
241
+ if (exists)
242
+ return;
243
+ if (image === DEFAULT_SANDBOX_IMAGE) {
244
+ await execDocker(["pull", "debian:bookworm-slim"]);
245
+ await execDocker(["tag", "debian:bookworm-slim", DEFAULT_SANDBOX_IMAGE]);
246
+ return;
247
+ }
248
+ throw new Error(`Sandbox image not found: ${image}. Build or pull it first.`);
249
+ }
250
+ async function dockerContainerState(name) {
251
+ const result = await execDocker(["inspect", "-f", "{{.State.Running}}", name], { allowFailure: true });
252
+ if (result.code !== 0)
253
+ return { exists: false, running: false };
254
+ return { exists: true, running: result.stdout.trim() === "true" };
255
+ }
256
+ async function ensureSandboxWorkspace(workspaceDir, seedFrom) {
257
+ await fs.mkdir(workspaceDir, { recursive: true });
258
+ if (seedFrom) {
259
+ const seed = resolveUserPath(seedFrom);
260
+ const files = [
261
+ DEFAULT_AGENTS_FILENAME,
262
+ DEFAULT_SOUL_FILENAME,
263
+ DEFAULT_TOOLS_FILENAME,
264
+ DEFAULT_IDENTITY_FILENAME,
265
+ DEFAULT_USER_FILENAME,
266
+ DEFAULT_BOOTSTRAP_FILENAME,
267
+ ];
268
+ for (const name of files) {
269
+ const src = path.join(seed, name);
270
+ const dest = path.join(workspaceDir, name);
271
+ try {
272
+ await fs.access(dest);
273
+ }
274
+ catch {
275
+ try {
276
+ const content = await fs.readFile(src, "utf-8");
277
+ await fs.writeFile(dest, content, { encoding: "utf-8", flag: "wx" });
278
+ }
279
+ catch {
280
+ // ignore missing seed file
281
+ }
282
+ }
283
+ }
284
+ }
285
+ await ensureAgentWorkspace({ dir: workspaceDir, ensureBootstrapFiles: true });
286
+ }
287
+ function normalizeDockerLimit(value) {
288
+ if (value === undefined || value === null)
289
+ return undefined;
290
+ if (typeof value === "number") {
291
+ return Number.isFinite(value) ? String(value) : undefined;
292
+ }
293
+ const trimmed = value.trim();
294
+ return trimmed ? trimmed : undefined;
295
+ }
296
+ function formatUlimitValue(name, value) {
297
+ if (!name.trim())
298
+ return null;
299
+ if (typeof value === "number" || typeof value === "string") {
300
+ const raw = String(value).trim();
301
+ return raw ? `${name}=${raw}` : null;
302
+ }
303
+ const soft = typeof value.soft === "number" ? Math.max(0, value.soft) : undefined;
304
+ const hard = typeof value.hard === "number" ? Math.max(0, value.hard) : undefined;
305
+ if (soft === undefined && hard === undefined)
306
+ return null;
307
+ if (soft === undefined)
308
+ return `${name}=${hard}`;
309
+ if (hard === undefined)
310
+ return `${name}=${soft}`;
311
+ return `${name}=${soft}:${hard}`;
312
+ }
313
+ export function buildSandboxCreateArgs(params) {
314
+ const createdAtMs = params.createdAtMs ?? Date.now();
315
+ const args = ["create", "--name", params.name];
316
+ args.push("--label", "clawdbot.sandbox=1");
317
+ args.push("--label", `clawdbot.sessionKey=${params.sessionKey}`);
318
+ args.push("--label", `clawdbot.createdAtMs=${createdAtMs}`);
319
+ for (const [key, value] of Object.entries(params.labels ?? {})) {
320
+ if (key && value)
321
+ args.push("--label", `${key}=${value}`);
322
+ }
323
+ if (params.cfg.readOnlyRoot)
324
+ args.push("--read-only");
325
+ for (const entry of params.cfg.tmpfs) {
326
+ args.push("--tmpfs", entry);
327
+ }
328
+ if (params.cfg.network)
329
+ args.push("--network", params.cfg.network);
330
+ if (params.cfg.user)
331
+ args.push("--user", params.cfg.user);
332
+ for (const cap of params.cfg.capDrop) {
333
+ args.push("--cap-drop", cap);
334
+ }
335
+ args.push("--security-opt", "no-new-privileges");
336
+ if (params.cfg.seccompProfile) {
337
+ args.push("--security-opt", `seccomp=${params.cfg.seccompProfile}`);
338
+ }
339
+ if (params.cfg.apparmorProfile) {
340
+ args.push("--security-opt", `apparmor=${params.cfg.apparmorProfile}`);
341
+ }
342
+ for (const entry of params.cfg.dns ?? []) {
343
+ if (entry.trim())
344
+ args.push("--dns", entry);
345
+ }
346
+ for (const entry of params.cfg.extraHosts ?? []) {
347
+ if (entry.trim())
348
+ args.push("--add-host", entry);
349
+ }
350
+ if (typeof params.cfg.pidsLimit === "number" && params.cfg.pidsLimit > 0) {
351
+ args.push("--pids-limit", String(params.cfg.pidsLimit));
352
+ }
353
+ const memory = normalizeDockerLimit(params.cfg.memory);
354
+ if (memory)
355
+ args.push("--memory", memory);
356
+ const memorySwap = normalizeDockerLimit(params.cfg.memorySwap);
357
+ if (memorySwap)
358
+ args.push("--memory-swap", memorySwap);
359
+ if (typeof params.cfg.cpus === "number" && params.cfg.cpus > 0) {
360
+ args.push("--cpus", String(params.cfg.cpus));
361
+ }
362
+ for (const [name, value] of Object.entries(params.cfg.ulimits ?? {})) {
363
+ const formatted = formatUlimitValue(name, value);
364
+ if (formatted)
365
+ args.push("--ulimit", formatted);
366
+ }
367
+ return args;
368
+ }
369
+ async function createSandboxContainer(params) {
370
+ const { name, cfg, workspaceDir, sessionKey } = params;
371
+ await ensureDockerImage(cfg.image);
372
+ const args = buildSandboxCreateArgs({
373
+ name,
374
+ cfg,
375
+ sessionKey,
376
+ });
377
+ args.push("--workdir", cfg.workdir);
378
+ args.push("-v", `${workspaceDir}:${cfg.workdir}`);
379
+ args.push(cfg.image, "sleep", "infinity");
380
+ await execDocker(args);
381
+ await execDocker(["start", name]);
382
+ if (cfg.setupCommand?.trim()) {
383
+ await execDocker(["exec", "-i", name, "sh", "-lc", cfg.setupCommand]);
384
+ }
385
+ }
386
+ async function ensureSandboxContainer(params) {
387
+ const slug = params.cfg.perSession
388
+ ? slugifySessionKey(params.sessionKey)
389
+ : "shared";
390
+ const name = `${params.cfg.docker.containerPrefix}${slug}`;
391
+ const containerName = name.slice(0, 63);
392
+ const state = await dockerContainerState(containerName);
393
+ if (!state.exists) {
394
+ await createSandboxContainer({
395
+ name: containerName,
396
+ cfg: params.cfg.docker,
397
+ workspaceDir: params.workspaceDir,
398
+ sessionKey: params.sessionKey,
399
+ });
400
+ }
401
+ else if (!state.running) {
402
+ await execDocker(["start", containerName]);
403
+ }
404
+ const now = Date.now();
405
+ await updateRegistry({
406
+ containerName,
407
+ sessionKey: params.sessionKey,
408
+ createdAtMs: now,
409
+ lastUsedAtMs: now,
410
+ image: params.cfg.docker.image,
411
+ });
412
+ return containerName;
413
+ }
414
+ async function ensureSandboxBrowserImage(image) {
415
+ const exists = await dockerImageExists(image);
416
+ if (exists)
417
+ return;
418
+ throw new Error(`Sandbox browser image not found: ${image}. Build it with scripts/sandbox-browser-setup.sh.`);
419
+ }
420
+ function buildSandboxBrowserResolvedConfig(params) {
421
+ const controlHost = "127.0.0.1";
422
+ const controlUrl = `http://${controlHost}:${params.controlPort}`;
423
+ const cdpHost = "127.0.0.1";
424
+ return {
425
+ enabled: true,
426
+ controlUrl,
427
+ controlHost,
428
+ controlPort: params.controlPort,
429
+ cdpProtocol: "http",
430
+ cdpHost,
431
+ cdpIsLoopback: true,
432
+ color: DEFAULT_CLAWD_BROWSER_COLOR,
433
+ executablePath: undefined,
434
+ headless: params.headless,
435
+ noSandbox: false,
436
+ attachOnly: true,
437
+ defaultProfile: "clawd",
438
+ profiles: {
439
+ clawd: { cdpPort: params.cdpPort, color: DEFAULT_CLAWD_BROWSER_COLOR },
440
+ },
441
+ };
442
+ }
443
+ async function ensureSandboxBrowser(params) {
444
+ if (!params.cfg.browser.enabled)
445
+ return null;
446
+ if (!isToolAllowed(params.cfg.tools, "browser"))
447
+ return null;
448
+ const slug = params.cfg.perSession
449
+ ? slugifySessionKey(params.sessionKey)
450
+ : "shared";
451
+ const name = `${params.cfg.browser.containerPrefix}${slug}`;
452
+ const containerName = name.slice(0, 63);
453
+ const state = await dockerContainerState(containerName);
454
+ if (!state.exists) {
455
+ await ensureSandboxBrowserImage(params.cfg.browser.image);
456
+ const args = buildSandboxCreateArgs({
457
+ name: containerName,
458
+ cfg: params.cfg.docker,
459
+ sessionKey: params.sessionKey,
460
+ labels: { "clawdbot.sandboxBrowser": "1" },
461
+ });
462
+ args.push("-v", `${params.workspaceDir}:${params.cfg.docker.workdir}`);
463
+ args.push("-p", `127.0.0.1::${params.cfg.browser.cdpPort}`);
464
+ if (params.cfg.browser.enableNoVnc && !params.cfg.browser.headless) {
465
+ args.push("-p", `127.0.0.1::${params.cfg.browser.noVncPort}`);
466
+ }
467
+ args.push("-e", `CLAWDBOT_BROWSER_HEADLESS=${params.cfg.browser.headless ? "1" : "0"}`);
468
+ args.push("-e", `CLAWDBOT_BROWSER_ENABLE_NOVNC=${params.cfg.browser.enableNoVnc ? "1" : "0"}`);
469
+ args.push("-e", `CLAWDBOT_BROWSER_CDP_PORT=${params.cfg.browser.cdpPort}`);
470
+ args.push("-e", `CLAWDBOT_BROWSER_VNC_PORT=${params.cfg.browser.vncPort}`);
471
+ args.push("-e", `CLAWDBOT_BROWSER_NOVNC_PORT=${params.cfg.browser.noVncPort}`);
472
+ args.push(params.cfg.browser.image);
473
+ await execDocker(args);
474
+ await execDocker(["start", containerName]);
475
+ }
476
+ else if (!state.running) {
477
+ await execDocker(["start", containerName]);
478
+ }
479
+ const mappedCdp = await readDockerPort(containerName, params.cfg.browser.cdpPort);
480
+ if (!mappedCdp) {
481
+ throw new Error(`Failed to resolve CDP port mapping for ${containerName}.`);
482
+ }
483
+ const mappedNoVnc = params.cfg.browser.enableNoVnc && !params.cfg.browser.headless
484
+ ? await readDockerPort(containerName, params.cfg.browser.noVncPort)
485
+ : null;
486
+ const existing = BROWSER_BRIDGES.get(params.sessionKey);
487
+ const existingProfile = existing
488
+ ? resolveProfile(existing.bridge.state.resolved, "clawd")
489
+ : null;
490
+ const shouldReuse = existing &&
491
+ existing.containerName === containerName &&
492
+ existingProfile?.cdpPort === mappedCdp;
493
+ if (existing && !shouldReuse) {
494
+ await stopBrowserBridgeServer(existing.bridge.server).catch(() => undefined);
495
+ BROWSER_BRIDGES.delete(params.sessionKey);
496
+ }
497
+ let bridge;
498
+ if (shouldReuse && existing) {
499
+ bridge = existing.bridge;
500
+ }
501
+ else {
502
+ bridge = await startBrowserBridgeServer({
503
+ resolved: buildSandboxBrowserResolvedConfig({
504
+ controlPort: 0,
505
+ cdpPort: mappedCdp,
506
+ headless: params.cfg.browser.headless,
507
+ }),
508
+ });
509
+ }
510
+ if (!shouldReuse) {
511
+ BROWSER_BRIDGES.set(params.sessionKey, { bridge, containerName });
512
+ }
513
+ const now = Date.now();
514
+ await updateBrowserRegistry({
515
+ containerName,
516
+ sessionKey: params.sessionKey,
517
+ createdAtMs: now,
518
+ lastUsedAtMs: now,
519
+ image: params.cfg.browser.image,
520
+ cdpPort: mappedCdp,
521
+ noVncPort: mappedNoVnc ?? undefined,
522
+ });
523
+ const noVncUrl = mappedNoVnc &&
524
+ params.cfg.browser.enableNoVnc &&
525
+ !params.cfg.browser.headless
526
+ ? `http://127.0.0.1:${mappedNoVnc}/vnc.html?autoconnect=1&resize=remote`
527
+ : undefined;
528
+ return {
529
+ controlUrl: bridge.baseUrl,
530
+ noVncUrl,
531
+ containerName,
532
+ };
533
+ }
534
+ async function pruneSandboxContainers(cfg) {
535
+ const now = Date.now();
536
+ const idleHours = cfg.prune.idleHours;
537
+ const maxAgeDays = cfg.prune.maxAgeDays;
538
+ if (idleHours === 0 && maxAgeDays === 0)
539
+ return;
540
+ const registry = await readRegistry();
541
+ for (const entry of registry.entries) {
542
+ const idleMs = now - entry.lastUsedAtMs;
543
+ const ageMs = now - entry.createdAtMs;
544
+ if ((idleHours > 0 && idleMs > idleHours * 60 * 60 * 1000) ||
545
+ (maxAgeDays > 0 && ageMs > maxAgeDays * 24 * 60 * 60 * 1000)) {
546
+ try {
547
+ await execDocker(["rm", "-f", entry.containerName], {
548
+ allowFailure: true,
549
+ });
550
+ }
551
+ catch {
552
+ // ignore prune failures
553
+ }
554
+ finally {
555
+ await removeRegistryEntry(entry.containerName);
556
+ }
557
+ }
558
+ }
559
+ }
560
+ async function pruneSandboxBrowsers(cfg) {
561
+ const now = Date.now();
562
+ const idleHours = cfg.prune.idleHours;
563
+ const maxAgeDays = cfg.prune.maxAgeDays;
564
+ if (idleHours === 0 && maxAgeDays === 0)
565
+ return;
566
+ const registry = await readBrowserRegistry();
567
+ for (const entry of registry.entries) {
568
+ const idleMs = now - entry.lastUsedAtMs;
569
+ const ageMs = now - entry.createdAtMs;
570
+ if ((idleHours > 0 && idleMs > idleHours * 60 * 60 * 1000) ||
571
+ (maxAgeDays > 0 && ageMs > maxAgeDays * 24 * 60 * 60 * 1000)) {
572
+ try {
573
+ await execDocker(["rm", "-f", entry.containerName], {
574
+ allowFailure: true,
575
+ });
576
+ }
577
+ catch {
578
+ // ignore prune failures
579
+ }
580
+ finally {
581
+ await removeBrowserRegistryEntry(entry.containerName);
582
+ const bridge = BROWSER_BRIDGES.get(entry.sessionKey);
583
+ if (bridge?.containerName === entry.containerName) {
584
+ await stopBrowserBridgeServer(bridge.bridge.server).catch(() => undefined);
585
+ BROWSER_BRIDGES.delete(entry.sessionKey);
586
+ }
587
+ }
588
+ }
589
+ }
590
+ }
591
+ async function maybePruneSandboxes(cfg) {
592
+ const now = Date.now();
593
+ if (now - lastPruneAtMs < 5 * 60 * 1000)
594
+ return;
595
+ lastPruneAtMs = now;
596
+ try {
597
+ await pruneSandboxContainers(cfg);
598
+ await pruneSandboxBrowsers(cfg);
599
+ }
600
+ catch (error) {
601
+ const message = error instanceof Error
602
+ ? error.message
603
+ : typeof error === "string"
604
+ ? error
605
+ : JSON.stringify(error);
606
+ defaultRuntime.error?.(`Sandbox prune failed: ${message ?? "unknown error"}`);
607
+ }
608
+ }
609
+ export async function resolveSandboxContext(params) {
610
+ const rawSessionKey = params.sessionKey?.trim();
611
+ if (!rawSessionKey)
612
+ return null;
613
+ const cfg = defaultSandboxConfig(params.config);
614
+ const mainKey = params.config?.session?.mainKey?.trim() || "main";
615
+ if (!shouldSandboxSession(cfg, rawSessionKey, mainKey))
616
+ return null;
617
+ await maybePruneSandboxes(cfg);
618
+ const workspaceRoot = resolveUserPath(cfg.workspaceRoot);
619
+ const workspaceDir = cfg.perSession
620
+ ? resolveSandboxWorkspaceDir(workspaceRoot, rawSessionKey)
621
+ : workspaceRoot;
622
+ const seedWorkspace = params.workspaceDir?.trim() || DEFAULT_AGENT_WORKSPACE_DIR;
623
+ await ensureSandboxWorkspace(workspaceDir, seedWorkspace);
624
+ const containerName = await ensureSandboxContainer({
625
+ sessionKey: rawSessionKey,
626
+ workspaceDir,
627
+ cfg,
628
+ });
629
+ const browser = await ensureSandboxBrowser({
630
+ sessionKey: rawSessionKey,
631
+ workspaceDir,
632
+ cfg,
633
+ });
634
+ return {
635
+ enabled: true,
636
+ sessionKey: rawSessionKey,
637
+ workspaceDir,
638
+ containerName,
639
+ containerWorkdir: cfg.docker.workdir,
640
+ docker: cfg.docker,
641
+ tools: cfg.tools,
642
+ browser: browser ?? undefined,
643
+ };
644
+ }
@@ -0,0 +1,53 @@
1
+ import { spawn } from "node:child_process";
2
+ export function getShellConfig() {
3
+ if (process.platform === "win32") {
4
+ const shell = process.env.COMSPEC?.trim() || "cmd.exe";
5
+ return { shell, args: ["/d", "/s", "/c"] };
6
+ }
7
+ const shell = process.env.SHELL?.trim() || "sh";
8
+ return { shell, args: ["-c"] };
9
+ }
10
+ export function sanitizeBinaryOutput(text) {
11
+ const scrubbed = text.replace(/[\p{Format}\p{Surrogate}]/gu, "");
12
+ if (!scrubbed)
13
+ return scrubbed;
14
+ const chunks = [];
15
+ for (const char of scrubbed) {
16
+ const code = char.codePointAt(0);
17
+ if (code == null)
18
+ continue;
19
+ if (code === 0x09 || code === 0x0a || code === 0x0d) {
20
+ chunks.push(char);
21
+ continue;
22
+ }
23
+ if (code < 0x20)
24
+ continue;
25
+ chunks.push(char);
26
+ }
27
+ return chunks.join("");
28
+ }
29
+ export function killProcessTree(pid) {
30
+ if (process.platform === "win32") {
31
+ try {
32
+ spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
33
+ stdio: "ignore",
34
+ detached: true,
35
+ });
36
+ }
37
+ catch {
38
+ // ignore errors if taskkill fails
39
+ }
40
+ return;
41
+ }
42
+ try {
43
+ process.kill(-pid, "SIGKILL");
44
+ }
45
+ catch {
46
+ try {
47
+ process.kill(pid, "SIGKILL");
48
+ }
49
+ catch {
50
+ // process already dead
51
+ }
52
+ }
53
+ }