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,946 @@
1
+ import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
2
+ import { defaultRuntime } from "../runtime.js";
3
+ import { cameraTempPath, parseCameraClipPayload, parseCameraSnapPayload, writeBase64ToFile, } from "./nodes-camera.js";
4
+ import { canvasSnapshotTempPath, parseCanvasSnapshotPayload, } from "./nodes-canvas.js";
5
+ import { parseScreenRecordPayload, screenRecordTempPath, writeScreenRecordToFile, } from "./nodes-screen.js";
6
+ import { parseDurationMs } from "./parse-duration.js";
7
+ const nodesCallOpts = (cmd, defaults) => cmd
8
+ .option("--url <url>", "Gateway WebSocket URL (defaults to gateway.remote.url when configured)")
9
+ .option("--token <token>", "Gateway token (if required)")
10
+ .option("--timeout <ms>", "Timeout in ms", String(defaults?.timeoutMs ?? 10_000))
11
+ .option("--json", "Output JSON", false);
12
+ const callGatewayCli = async (method, opts, params) => callGateway({
13
+ url: opts.url,
14
+ token: opts.token,
15
+ method,
16
+ params,
17
+ timeoutMs: Number(opts.timeout ?? 10_000),
18
+ clientName: "cli",
19
+ mode: "cli",
20
+ });
21
+ function formatAge(msAgo) {
22
+ const s = Math.max(0, Math.floor(msAgo / 1000));
23
+ if (s < 60)
24
+ return `${s}s`;
25
+ const m = Math.floor(s / 60);
26
+ if (m < 60)
27
+ return `${m}m`;
28
+ const h = Math.floor(m / 60);
29
+ if (h < 24)
30
+ return `${h}h`;
31
+ const d = Math.floor(h / 24);
32
+ return `${d}d`;
33
+ }
34
+ function parsePairingList(value) {
35
+ const obj = typeof value === "object" && value !== null
36
+ ? value
37
+ : {};
38
+ const pending = Array.isArray(obj.pending)
39
+ ? obj.pending
40
+ : [];
41
+ const paired = Array.isArray(obj.paired) ? obj.paired : [];
42
+ return { pending, paired };
43
+ }
44
+ function parseNodeList(value) {
45
+ const obj = typeof value === "object" && value !== null
46
+ ? value
47
+ : {};
48
+ return Array.isArray(obj.nodes) ? obj.nodes : [];
49
+ }
50
+ function formatPermissions(raw) {
51
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
52
+ return null;
53
+ const entries = Object.entries(raw)
54
+ .map(([key, value]) => [String(key).trim(), value === true])
55
+ .filter(([key]) => key.length > 0)
56
+ .sort((a, b) => a[0].localeCompare(b[0]));
57
+ if (entries.length === 0)
58
+ return null;
59
+ const parts = entries.map(([key, granted]) => `${key}=${granted ? "yes" : "no"}`);
60
+ return `[${parts.join(", ")}]`;
61
+ }
62
+ function unauthorizedHintForMessage(message) {
63
+ const haystack = message.toLowerCase();
64
+ if (haystack.includes("unauthorizedclient") ||
65
+ haystack.includes("bridge client is not authorized") ||
66
+ haystack.includes("unsigned bridge clients are not allowed")) {
67
+ return [
68
+ "peekaboo bridge rejected the client.",
69
+ "sign the peekaboo CLI (TeamID Y5PE65HELJ) or launch the host with",
70
+ "PEEKABOO_ALLOW_UNSIGNED_SOCKET_CLIENTS=1 for local dev.",
71
+ ].join(" ");
72
+ }
73
+ return null;
74
+ }
75
+ function normalizeNodeKey(value) {
76
+ return value
77
+ .toLowerCase()
78
+ .replace(/[^a-z0-9]+/g, "-")
79
+ .replace(/^-+/, "")
80
+ .replace(/-+$/, "");
81
+ }
82
+ function parseEnvPairs(pairs) {
83
+ if (!Array.isArray(pairs) || pairs.length === 0)
84
+ return undefined;
85
+ const env = {};
86
+ for (const pair of pairs) {
87
+ const idx = pair.indexOf("=");
88
+ if (idx <= 0)
89
+ continue;
90
+ const key = pair.slice(0, idx).trim();
91
+ const value = pair.slice(idx + 1);
92
+ if (!key)
93
+ continue;
94
+ env[key] = value;
95
+ }
96
+ return Object.keys(env).length > 0 ? env : undefined;
97
+ }
98
+ async function resolveNodeId(opts, query) {
99
+ const q = String(query ?? "").trim();
100
+ if (!q)
101
+ throw new Error("node required");
102
+ let nodes = [];
103
+ try {
104
+ const res = (await callGatewayCli("node.list", opts, {}));
105
+ nodes = parseNodeList(res);
106
+ }
107
+ catch {
108
+ const res = (await callGatewayCli("node.pair.list", opts, {}));
109
+ const { paired } = parsePairingList(res);
110
+ nodes = paired.map((n) => ({
111
+ nodeId: n.nodeId,
112
+ displayName: n.displayName,
113
+ platform: n.platform,
114
+ version: n.version,
115
+ remoteIp: n.remoteIp,
116
+ }));
117
+ }
118
+ const qNorm = normalizeNodeKey(q);
119
+ const matches = nodes.filter((n) => {
120
+ if (n.nodeId === q)
121
+ return true;
122
+ if (typeof n.remoteIp === "string" && n.remoteIp === q)
123
+ return true;
124
+ const name = typeof n.displayName === "string" ? n.displayName : "";
125
+ if (name && normalizeNodeKey(name) === qNorm)
126
+ return true;
127
+ if (q.length >= 6 && n.nodeId.startsWith(q))
128
+ return true;
129
+ return false;
130
+ });
131
+ if (matches.length === 1)
132
+ return matches[0].nodeId;
133
+ if (matches.length === 0) {
134
+ const known = nodes
135
+ .map((n) => n.displayName || n.remoteIp || n.nodeId)
136
+ .filter(Boolean)
137
+ .join(", ");
138
+ throw new Error(`unknown node: ${q}${known ? ` (known: ${known})` : ""}`);
139
+ }
140
+ throw new Error(`ambiguous node: ${q} (matches: ${matches
141
+ .map((n) => n.displayName || n.remoteIp || n.nodeId)
142
+ .join(", ")})`);
143
+ }
144
+ export function registerNodesCli(program) {
145
+ const nodes = program
146
+ .command("nodes")
147
+ .description("Manage gateway-owned node pairing");
148
+ nodesCallOpts(nodes
149
+ .command("status")
150
+ .description("List known nodes with connection status and capabilities")
151
+ .action(async (opts) => {
152
+ try {
153
+ const result = (await callGatewayCli("node.list", opts, {}));
154
+ if (opts.json) {
155
+ defaultRuntime.log(JSON.stringify(result, null, 2));
156
+ return;
157
+ }
158
+ const nodes = parseNodeList(result);
159
+ const pairedCount = nodes.filter((n) => Boolean(n.paired)).length;
160
+ const connectedCount = nodes.filter((n) => Boolean(n.connected)).length;
161
+ defaultRuntime.log(`Known: ${nodes.length} · Paired: ${pairedCount} · Connected: ${connectedCount}`);
162
+ for (const n of nodes) {
163
+ const name = n.displayName || n.nodeId;
164
+ const ip = n.remoteIp ? ` · ${n.remoteIp}` : "";
165
+ const device = n.deviceFamily ? ` · device: ${n.deviceFamily}` : "";
166
+ const hw = n.modelIdentifier ? ` · hw: ${n.modelIdentifier}` : "";
167
+ const perms = formatPermissions(n.permissions);
168
+ const permsText = perms ? ` · perms: ${perms}` : "";
169
+ const caps = Array.isArray(n.caps) && n.caps.length > 0
170
+ ? `[${n.caps.map(String).filter(Boolean).sort().join(",")}]`
171
+ : Array.isArray(n.caps)
172
+ ? "[]"
173
+ : "?";
174
+ const pairing = n.paired ? "paired" : "unpaired";
175
+ defaultRuntime.log(`- ${name} · ${n.nodeId}${ip}${device}${hw}${permsText} · ${pairing} · ${n.connected ? "connected" : "disconnected"} · caps: ${caps}`);
176
+ }
177
+ }
178
+ catch (err) {
179
+ defaultRuntime.error(`nodes status failed: ${String(err)}`);
180
+ defaultRuntime.exit(1);
181
+ }
182
+ }));
183
+ nodesCallOpts(nodes
184
+ .command("describe")
185
+ .description("Describe a node (capabilities + supported invoke commands)")
186
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
187
+ .action(async (opts) => {
188
+ try {
189
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
190
+ const result = (await callGatewayCli("node.describe", opts, {
191
+ nodeId,
192
+ }));
193
+ if (opts.json) {
194
+ defaultRuntime.log(JSON.stringify(result, null, 2));
195
+ return;
196
+ }
197
+ const obj = typeof result === "object" && result !== null
198
+ ? result
199
+ : {};
200
+ const displayName = typeof obj.displayName === "string" ? obj.displayName : nodeId;
201
+ const connected = Boolean(obj.connected);
202
+ const caps = Array.isArray(obj.caps)
203
+ ? obj.caps.map(String).filter(Boolean).sort()
204
+ : null;
205
+ const commands = Array.isArray(obj.commands)
206
+ ? obj.commands.map(String).filter(Boolean).sort()
207
+ : [];
208
+ const perms = formatPermissions(obj.permissions);
209
+ const family = typeof obj.deviceFamily === "string" ? obj.deviceFamily : null;
210
+ const model = typeof obj.modelIdentifier === "string"
211
+ ? obj.modelIdentifier
212
+ : null;
213
+ const ip = typeof obj.remoteIp === "string" ? obj.remoteIp : null;
214
+ const parts = ["Node:", displayName, nodeId];
215
+ if (ip)
216
+ parts.push(ip);
217
+ if (family)
218
+ parts.push(`device: ${family}`);
219
+ if (model)
220
+ parts.push(`hw: ${model}`);
221
+ if (perms)
222
+ parts.push(`perms: ${perms}`);
223
+ parts.push(connected ? "connected" : "disconnected");
224
+ parts.push(`caps: ${caps ? `[${caps.join(",")}]` : "?"}`);
225
+ defaultRuntime.log(parts.join(" · "));
226
+ defaultRuntime.log("Commands:");
227
+ if (commands.length === 0) {
228
+ defaultRuntime.log("- (none reported)");
229
+ return;
230
+ }
231
+ for (const c of commands)
232
+ defaultRuntime.log(`- ${c}`);
233
+ }
234
+ catch (err) {
235
+ defaultRuntime.error(`nodes describe failed: ${String(err)}`);
236
+ defaultRuntime.exit(1);
237
+ }
238
+ }));
239
+ nodesCallOpts(nodes
240
+ .command("list")
241
+ .description("List pending and paired nodes")
242
+ .action(async (opts) => {
243
+ try {
244
+ const result = (await callGatewayCli("node.pair.list", opts, {}));
245
+ if (opts.json) {
246
+ defaultRuntime.log(JSON.stringify(result, null, 2));
247
+ return;
248
+ }
249
+ const { pending, paired } = parsePairingList(result);
250
+ defaultRuntime.log(`Pending: ${pending.length} · Paired: ${paired.length}`);
251
+ if (pending.length > 0) {
252
+ defaultRuntime.log("\nPending:");
253
+ for (const r of pending) {
254
+ const name = r.displayName || r.nodeId;
255
+ const repair = r.isRepair ? " (repair)" : "";
256
+ const ip = r.remoteIp ? ` · ${r.remoteIp}` : "";
257
+ const age = typeof r.ts === "number"
258
+ ? ` · ${formatAge(Date.now() - r.ts)} ago`
259
+ : "";
260
+ defaultRuntime.log(`- ${r.requestId}: ${name}${repair}${ip}${age}`);
261
+ }
262
+ }
263
+ if (paired.length > 0) {
264
+ defaultRuntime.log("\nPaired:");
265
+ for (const n of paired) {
266
+ const name = n.displayName || n.nodeId;
267
+ const ip = n.remoteIp ? ` · ${n.remoteIp}` : "";
268
+ defaultRuntime.log(`- ${n.nodeId}: ${name}${ip}`);
269
+ }
270
+ }
271
+ }
272
+ catch (err) {
273
+ defaultRuntime.error(`nodes list failed: ${String(err)}`);
274
+ defaultRuntime.exit(1);
275
+ }
276
+ }));
277
+ nodesCallOpts(nodes
278
+ .command("pending")
279
+ .description("List pending pairing requests")
280
+ .action(async (opts) => {
281
+ try {
282
+ const result = (await callGatewayCli("node.pair.list", opts, {}));
283
+ const { pending } = parsePairingList(result);
284
+ if (opts.json) {
285
+ defaultRuntime.log(JSON.stringify(pending, null, 2));
286
+ return;
287
+ }
288
+ if (pending.length === 0) {
289
+ defaultRuntime.log("No pending pairing requests.");
290
+ return;
291
+ }
292
+ for (const r of pending) {
293
+ const name = r.displayName || r.nodeId;
294
+ const repair = r.isRepair ? " (repair)" : "";
295
+ const ip = r.remoteIp ? ` · ${r.remoteIp}` : "";
296
+ const age = typeof r.ts === "number"
297
+ ? ` · ${formatAge(Date.now() - r.ts)} ago`
298
+ : "";
299
+ defaultRuntime.log(`- ${r.requestId}: ${name}${repair}${ip}${age}`);
300
+ }
301
+ }
302
+ catch (err) {
303
+ defaultRuntime.error(`nodes pending failed: ${String(err)}`);
304
+ defaultRuntime.exit(1);
305
+ }
306
+ }));
307
+ nodesCallOpts(nodes
308
+ .command("approve")
309
+ .description("Approve a pending pairing request")
310
+ .argument("<requestId>", "Pending request id")
311
+ .action(async (requestId, opts) => {
312
+ try {
313
+ const result = await callGatewayCli("node.pair.approve", opts, {
314
+ requestId,
315
+ });
316
+ defaultRuntime.log(JSON.stringify(result, null, 2));
317
+ }
318
+ catch (err) {
319
+ defaultRuntime.error(`nodes approve failed: ${String(err)}`);
320
+ defaultRuntime.exit(1);
321
+ }
322
+ }));
323
+ nodesCallOpts(nodes
324
+ .command("reject")
325
+ .description("Reject a pending pairing request")
326
+ .argument("<requestId>", "Pending request id")
327
+ .action(async (requestId, opts) => {
328
+ try {
329
+ const result = await callGatewayCli("node.pair.reject", opts, {
330
+ requestId,
331
+ });
332
+ defaultRuntime.log(JSON.stringify(result, null, 2));
333
+ }
334
+ catch (err) {
335
+ defaultRuntime.error(`nodes reject failed: ${String(err)}`);
336
+ defaultRuntime.exit(1);
337
+ }
338
+ }));
339
+ nodesCallOpts(nodes
340
+ .command("rename")
341
+ .description("Rename a paired node (display name override)")
342
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
343
+ .requiredOption("--name <displayName>", "New display name")
344
+ .action(async (opts) => {
345
+ try {
346
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
347
+ const name = String(opts.name ?? "").trim();
348
+ if (!nodeId || !name) {
349
+ defaultRuntime.error("--node and --name required");
350
+ defaultRuntime.exit(1);
351
+ return;
352
+ }
353
+ const result = await callGatewayCli("node.rename", opts, {
354
+ nodeId,
355
+ displayName: name,
356
+ });
357
+ if (opts.json) {
358
+ defaultRuntime.log(JSON.stringify(result, null, 2));
359
+ return;
360
+ }
361
+ defaultRuntime.log(`node rename ok: ${nodeId} -> ${name}`);
362
+ }
363
+ catch (err) {
364
+ defaultRuntime.error(`nodes rename failed: ${String(err)}`);
365
+ defaultRuntime.exit(1);
366
+ }
367
+ }));
368
+ nodesCallOpts(nodes
369
+ .command("invoke")
370
+ .description("Invoke a command on a paired node")
371
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
372
+ .requiredOption("--command <command>", "Command (e.g. canvas.eval)")
373
+ .option("--params <json>", "JSON object string for params", "{}")
374
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 15000)", "15000")
375
+ .option("--idempotency-key <key>", "Idempotency key (optional)")
376
+ .action(async (opts) => {
377
+ try {
378
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
379
+ const command = String(opts.command ?? "").trim();
380
+ if (!nodeId || !command) {
381
+ defaultRuntime.error("--node and --command required");
382
+ defaultRuntime.exit(1);
383
+ return;
384
+ }
385
+ const params = JSON.parse(String(opts.params ?? "{}"));
386
+ const timeoutMs = opts.invokeTimeout
387
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
388
+ : undefined;
389
+ const invokeParams = {
390
+ nodeId,
391
+ command,
392
+ params,
393
+ idempotencyKey: String(opts.idempotencyKey ?? randomIdempotencyKey()),
394
+ };
395
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
396
+ invokeParams.timeoutMs = timeoutMs;
397
+ }
398
+ const result = await callGatewayCli("node.invoke", opts, invokeParams);
399
+ defaultRuntime.log(JSON.stringify(result, null, 2));
400
+ }
401
+ catch (err) {
402
+ defaultRuntime.error(`nodes invoke failed: ${String(err)}`);
403
+ defaultRuntime.exit(1);
404
+ }
405
+ }), { timeoutMs: 30_000 });
406
+ nodesCallOpts(nodes
407
+ .command("run")
408
+ .description("Run a shell command on a node (mac only)")
409
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
410
+ .option("--cwd <path>", "Working directory")
411
+ .option("--env <key=val>", "Environment override (repeatable)", (value, prev = []) => [...prev, value])
412
+ .option("--command-timeout <ms>", "Command timeout (ms)")
413
+ .option("--needs-screen-recording", "Require screen recording permission")
414
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 30000)", "30000")
415
+ .argument("<command...>", "Command and args")
416
+ .action(async (command, opts) => {
417
+ try {
418
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
419
+ if (!Array.isArray(command) || command.length === 0) {
420
+ throw new Error("command required");
421
+ }
422
+ const env = parseEnvPairs(opts.env);
423
+ const timeoutMs = opts.commandTimeout
424
+ ? Number.parseInt(String(opts.commandTimeout), 10)
425
+ : undefined;
426
+ const invokeTimeout = opts.invokeTimeout
427
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
428
+ : undefined;
429
+ const invokeParams = {
430
+ nodeId,
431
+ command: "system.run",
432
+ params: {
433
+ command,
434
+ cwd: opts.cwd,
435
+ env,
436
+ timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : undefined,
437
+ needsScreenRecording: opts.needsScreenRecording === true,
438
+ },
439
+ idempotencyKey: String(opts.idempotencyKey ?? randomIdempotencyKey()),
440
+ };
441
+ if (typeof invokeTimeout === "number" &&
442
+ Number.isFinite(invokeTimeout)) {
443
+ invokeParams.timeoutMs = invokeTimeout;
444
+ }
445
+ const result = (await callGatewayCli("node.invoke", opts, invokeParams));
446
+ if (opts.json) {
447
+ defaultRuntime.log(JSON.stringify(result, null, 2));
448
+ return;
449
+ }
450
+ const payload = typeof result === "object" && result !== null
451
+ ? result.payload
452
+ : undefined;
453
+ const stdout = typeof payload?.stdout === "string" ? payload.stdout : "";
454
+ const stderr = typeof payload?.stderr === "string" ? payload.stderr : "";
455
+ const exitCode = typeof payload?.exitCode === "number" ? payload.exitCode : null;
456
+ const timedOut = payload?.timedOut === true;
457
+ const success = payload?.success === true;
458
+ if (stdout)
459
+ process.stdout.write(stdout);
460
+ if (stderr)
461
+ process.stderr.write(stderr);
462
+ if (timedOut) {
463
+ defaultRuntime.error("run timed out");
464
+ defaultRuntime.exit(1);
465
+ return;
466
+ }
467
+ if (exitCode !== null && exitCode !== 0) {
468
+ const hint = unauthorizedHintForMessage(`${stderr}\n${stdout}`);
469
+ if (hint)
470
+ defaultRuntime.error(hint);
471
+ }
472
+ if (exitCode !== null && exitCode !== 0 && !success) {
473
+ defaultRuntime.error(`run exit ${exitCode}`);
474
+ defaultRuntime.exit(1);
475
+ return;
476
+ }
477
+ }
478
+ catch (err) {
479
+ defaultRuntime.error(`nodes run failed: ${String(err)}`);
480
+ const hint = unauthorizedHintForMessage(String(err));
481
+ if (hint)
482
+ defaultRuntime.error(hint);
483
+ defaultRuntime.exit(1);
484
+ }
485
+ }), { timeoutMs: 35_000 });
486
+ nodesCallOpts(nodes
487
+ .command("notify")
488
+ .description("Send a local notification on a node (mac only)")
489
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
490
+ .option("--title <text>", "Notification title")
491
+ .option("--body <text>", "Notification body")
492
+ .option("--sound <name>", "Notification sound")
493
+ .option("--priority <passive|active|timeSensitive>", "Notification priority")
494
+ .option("--delivery <system|overlay|auto>", "Delivery mode", "system")
495
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 15000)", "15000")
496
+ .action(async (opts) => {
497
+ try {
498
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
499
+ const title = String(opts.title ?? "").trim();
500
+ const body = String(opts.body ?? "").trim();
501
+ if (!title && !body) {
502
+ throw new Error("missing --title or --body");
503
+ }
504
+ const invokeTimeout = opts.invokeTimeout
505
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
506
+ : undefined;
507
+ const invokeParams = {
508
+ nodeId,
509
+ command: "system.notify",
510
+ params: {
511
+ title,
512
+ body,
513
+ sound: opts.sound,
514
+ priority: opts.priority,
515
+ delivery: opts.delivery,
516
+ },
517
+ idempotencyKey: String(opts.idempotencyKey ?? randomIdempotencyKey()),
518
+ };
519
+ if (typeof invokeTimeout === "number" &&
520
+ Number.isFinite(invokeTimeout)) {
521
+ invokeParams.timeoutMs = invokeTimeout;
522
+ }
523
+ const result = await callGatewayCli("node.invoke", opts, invokeParams);
524
+ if (opts.json) {
525
+ defaultRuntime.log(JSON.stringify(result, null, 2));
526
+ return;
527
+ }
528
+ defaultRuntime.log("notify ok");
529
+ }
530
+ catch (err) {
531
+ defaultRuntime.error(`nodes notify failed: ${String(err)}`);
532
+ defaultRuntime.exit(1);
533
+ }
534
+ }));
535
+ const parseFacing = (value) => {
536
+ const v = String(value ?? "")
537
+ .trim()
538
+ .toLowerCase();
539
+ if (v === "front" || v === "back")
540
+ return v;
541
+ throw new Error(`invalid facing: ${value} (expected front|back)`);
542
+ };
543
+ const camera = nodes
544
+ .command("camera")
545
+ .description("Capture camera media from a paired node");
546
+ const canvas = nodes
547
+ .command("canvas")
548
+ .description("Capture or render canvas content from a paired node");
549
+ nodesCallOpts(canvas
550
+ .command("snapshot")
551
+ .description("Capture a canvas snapshot (prints MEDIA:<path>)")
552
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
553
+ .option("--format <png|jpg|jpeg>", "Image format", "jpg")
554
+ .option("--max-width <px>", "Max width in px (optional)")
555
+ .option("--quality <0-1>", "JPEG quality (optional)")
556
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 20000)", "20000")
557
+ .action(async (opts) => {
558
+ try {
559
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
560
+ const formatOpt = String(opts.format ?? "jpg")
561
+ .trim()
562
+ .toLowerCase();
563
+ const formatForParams = formatOpt === "jpg"
564
+ ? "jpeg"
565
+ : formatOpt === "jpeg"
566
+ ? "jpeg"
567
+ : "png";
568
+ if (formatForParams !== "png" && formatForParams !== "jpeg") {
569
+ throw new Error(`invalid format: ${String(opts.format)} (expected png|jpg|jpeg)`);
570
+ }
571
+ const maxWidth = opts.maxWidth
572
+ ? Number.parseInt(String(opts.maxWidth), 10)
573
+ : undefined;
574
+ const quality = opts.quality
575
+ ? Number.parseFloat(String(opts.quality))
576
+ : undefined;
577
+ const timeoutMs = opts.invokeTimeout
578
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
579
+ : undefined;
580
+ const invokeParams = {
581
+ nodeId,
582
+ command: "canvas.snapshot",
583
+ params: {
584
+ format: formatForParams,
585
+ maxWidth: Number.isFinite(maxWidth) ? maxWidth : undefined,
586
+ quality: Number.isFinite(quality) ? quality : undefined,
587
+ },
588
+ idempotencyKey: randomIdempotencyKey(),
589
+ };
590
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
591
+ invokeParams.timeoutMs = timeoutMs;
592
+ }
593
+ const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
594
+ const res = typeof raw === "object" && raw !== null
595
+ ? raw
596
+ : {};
597
+ const payload = parseCanvasSnapshotPayload(res.payload);
598
+ const filePath = canvasSnapshotTempPath({
599
+ ext: payload.format === "jpeg" ? "jpg" : payload.format,
600
+ });
601
+ await writeBase64ToFile(filePath, payload.base64);
602
+ if (opts.json) {
603
+ defaultRuntime.log(JSON.stringify({ file: { path: filePath, format: payload.format } }, null, 2));
604
+ return;
605
+ }
606
+ defaultRuntime.log(`MEDIA:${filePath}`);
607
+ }
608
+ catch (err) {
609
+ defaultRuntime.error(`nodes canvas snapshot failed: ${String(err)}`);
610
+ defaultRuntime.exit(1);
611
+ }
612
+ }), { timeoutMs: 60_000 });
613
+ nodesCallOpts(camera
614
+ .command("list")
615
+ .description("List available cameras on a node")
616
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
617
+ .action(async (opts) => {
618
+ try {
619
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
620
+ const raw = (await callGatewayCli("node.invoke", opts, {
621
+ nodeId,
622
+ command: "camera.list",
623
+ params: {},
624
+ idempotencyKey: randomIdempotencyKey(),
625
+ }));
626
+ const res = typeof raw === "object" && raw !== null
627
+ ? raw
628
+ : {};
629
+ const payload = typeof res.payload === "object" && res.payload !== null
630
+ ? res.payload
631
+ : {};
632
+ const devices = Array.isArray(payload.devices)
633
+ ? payload.devices
634
+ : [];
635
+ if (opts.json) {
636
+ defaultRuntime.log(JSON.stringify({ devices }, null, 2));
637
+ return;
638
+ }
639
+ if (devices.length === 0) {
640
+ defaultRuntime.log("No cameras reported.");
641
+ return;
642
+ }
643
+ for (const device of devices) {
644
+ const id = typeof device.id === "string" ? device.id : "";
645
+ const name = typeof device.name === "string" ? device.name : "Unknown Camera";
646
+ const position = typeof device.position === "string"
647
+ ? device.position
648
+ : "unspecified";
649
+ defaultRuntime.log(`${name} (${position})${id ? ` — ${id}` : ""}`);
650
+ }
651
+ }
652
+ catch (err) {
653
+ defaultRuntime.error(`nodes camera list failed: ${String(err)}`);
654
+ defaultRuntime.exit(1);
655
+ }
656
+ }), { timeoutMs: 60_000 });
657
+ nodesCallOpts(camera
658
+ .command("snap")
659
+ .description("Capture a photo from a node camera (prints MEDIA:<path>)")
660
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
661
+ .option("--facing <front|back|both>", "Camera facing", "both")
662
+ .option("--device-id <id>", "Camera device id (from nodes camera list)")
663
+ .option("--max-width <px>", "Max width in px (optional)")
664
+ .option("--quality <0-1>", "JPEG quality (default 0.9)")
665
+ .option("--delay-ms <ms>", "Delay before capture in ms (macOS default 2000)")
666
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 20000)", "20000")
667
+ .action(async (opts) => {
668
+ try {
669
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
670
+ const facingOpt = String(opts.facing ?? "both")
671
+ .trim()
672
+ .toLowerCase();
673
+ const facings = facingOpt === "both"
674
+ ? ["front", "back"]
675
+ : facingOpt === "front" || facingOpt === "back"
676
+ ? [facingOpt]
677
+ : (() => {
678
+ throw new Error(`invalid facing: ${String(opts.facing)} (expected front|back|both)`);
679
+ })();
680
+ const maxWidth = opts.maxWidth
681
+ ? Number.parseInt(String(opts.maxWidth), 10)
682
+ : undefined;
683
+ const quality = opts.quality
684
+ ? Number.parseFloat(String(opts.quality))
685
+ : undefined;
686
+ const delayMs = opts.delayMs
687
+ ? Number.parseInt(String(opts.delayMs), 10)
688
+ : undefined;
689
+ const deviceId = opts.deviceId
690
+ ? String(opts.deviceId).trim()
691
+ : undefined;
692
+ const timeoutMs = opts.invokeTimeout
693
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
694
+ : undefined;
695
+ const results = [];
696
+ for (const facing of facings) {
697
+ const invokeParams = {
698
+ nodeId,
699
+ command: "camera.snap",
700
+ params: {
701
+ facing,
702
+ maxWidth: Number.isFinite(maxWidth) ? maxWidth : undefined,
703
+ quality: Number.isFinite(quality) ? quality : undefined,
704
+ format: "jpg",
705
+ delayMs: Number.isFinite(delayMs) ? delayMs : undefined,
706
+ deviceId: deviceId || undefined,
707
+ },
708
+ idempotencyKey: randomIdempotencyKey(),
709
+ };
710
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
711
+ invokeParams.timeoutMs = timeoutMs;
712
+ }
713
+ const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
714
+ const res = typeof raw === "object" && raw !== null
715
+ ? raw
716
+ : {};
717
+ const payload = parseCameraSnapPayload(res.payload);
718
+ const filePath = cameraTempPath({
719
+ kind: "snap",
720
+ facing,
721
+ ext: payload.format === "jpeg" ? "jpg" : payload.format,
722
+ });
723
+ await writeBase64ToFile(filePath, payload.base64);
724
+ results.push({
725
+ facing,
726
+ path: filePath,
727
+ width: payload.width,
728
+ height: payload.height,
729
+ });
730
+ }
731
+ if (opts.json) {
732
+ defaultRuntime.log(JSON.stringify({ files: results }, null, 2));
733
+ return;
734
+ }
735
+ defaultRuntime.log(results.map((r) => `MEDIA:${r.path}`).join("\n"));
736
+ }
737
+ catch (err) {
738
+ defaultRuntime.error(`nodes camera snap failed: ${String(err)}`);
739
+ defaultRuntime.exit(1);
740
+ }
741
+ }), { timeoutMs: 60_000 });
742
+ nodesCallOpts(camera
743
+ .command("clip")
744
+ .description("Capture a short video clip from a node camera (prints MEDIA:<path>)")
745
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
746
+ .option("--facing <front|back>", "Camera facing", "front")
747
+ .option("--device-id <id>", "Camera device id (from nodes camera list)")
748
+ .option("--duration <ms|10s|1m>", "Duration (default 3000ms; supports ms/s/m, e.g. 10s)", "3000")
749
+ .option("--no-audio", "Disable audio capture")
750
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 90000)", "90000")
751
+ .action(async (opts) => {
752
+ try {
753
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
754
+ const facing = parseFacing(String(opts.facing ?? "front"));
755
+ const durationMs = parseDurationMs(String(opts.duration ?? "3000"));
756
+ const includeAudio = opts.audio !== false;
757
+ const timeoutMs = opts.invokeTimeout
758
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
759
+ : undefined;
760
+ const deviceId = opts.deviceId
761
+ ? String(opts.deviceId).trim()
762
+ : undefined;
763
+ const invokeParams = {
764
+ nodeId,
765
+ command: "camera.clip",
766
+ params: {
767
+ facing,
768
+ durationMs: Number.isFinite(durationMs) ? durationMs : undefined,
769
+ includeAudio,
770
+ format: "mp4",
771
+ deviceId: deviceId || undefined,
772
+ },
773
+ idempotencyKey: randomIdempotencyKey(),
774
+ };
775
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
776
+ invokeParams.timeoutMs = timeoutMs;
777
+ }
778
+ const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
779
+ const res = typeof raw === "object" && raw !== null
780
+ ? raw
781
+ : {};
782
+ const payload = parseCameraClipPayload(res.payload);
783
+ const filePath = cameraTempPath({
784
+ kind: "clip",
785
+ facing,
786
+ ext: payload.format,
787
+ });
788
+ await writeBase64ToFile(filePath, payload.base64);
789
+ if (opts.json) {
790
+ defaultRuntime.log(JSON.stringify({
791
+ file: {
792
+ facing,
793
+ path: filePath,
794
+ durationMs: payload.durationMs,
795
+ hasAudio: payload.hasAudio,
796
+ },
797
+ }, null, 2));
798
+ return;
799
+ }
800
+ defaultRuntime.log(`MEDIA:${filePath}`);
801
+ }
802
+ catch (err) {
803
+ defaultRuntime.error(`nodes camera clip failed: ${String(err)}`);
804
+ defaultRuntime.exit(1);
805
+ }
806
+ }), { timeoutMs: 90_000 });
807
+ const screen = nodes
808
+ .command("screen")
809
+ .description("Capture screen recordings from a paired node");
810
+ nodesCallOpts(screen
811
+ .command("record")
812
+ .description("Capture a short screen recording from a node (prints MEDIA:<path>)")
813
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
814
+ .option("--screen <index>", "Screen index (0 = primary)", "0")
815
+ .option("--duration <ms|10s>", "Clip duration (ms or 10s)", "10000")
816
+ .option("--fps <fps>", "Frames per second", "10")
817
+ .option("--no-audio", "Disable microphone audio capture")
818
+ .option("--out <path>", "Output path")
819
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 120000)", "120000")
820
+ .action(async (opts) => {
821
+ try {
822
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
823
+ const durationMs = parseDurationMs(opts.duration ?? "");
824
+ const screenIndex = Number.parseInt(String(opts.screen ?? "0"), 10);
825
+ const fps = Number.parseFloat(String(opts.fps ?? "10"));
826
+ const timeoutMs = opts.invokeTimeout
827
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
828
+ : undefined;
829
+ const invokeParams = {
830
+ nodeId,
831
+ command: "screen.record",
832
+ params: {
833
+ durationMs: Number.isFinite(durationMs) ? durationMs : undefined,
834
+ screenIndex: Number.isFinite(screenIndex)
835
+ ? screenIndex
836
+ : undefined,
837
+ fps: Number.isFinite(fps) ? fps : undefined,
838
+ format: "mp4",
839
+ includeAudio: opts.audio !== false,
840
+ },
841
+ idempotencyKey: randomIdempotencyKey(),
842
+ };
843
+ if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
844
+ invokeParams.timeoutMs = timeoutMs;
845
+ }
846
+ const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
847
+ const res = typeof raw === "object" && raw !== null
848
+ ? raw
849
+ : {};
850
+ const parsed = parseScreenRecordPayload(res.payload);
851
+ const filePath = opts.out ??
852
+ screenRecordTempPath({
853
+ ext: parsed.format || "mp4",
854
+ });
855
+ const written = await writeScreenRecordToFile(filePath, parsed.base64);
856
+ if (opts.json) {
857
+ defaultRuntime.log(JSON.stringify({
858
+ file: {
859
+ path: written.path,
860
+ durationMs: parsed.durationMs,
861
+ fps: parsed.fps,
862
+ screenIndex: parsed.screenIndex,
863
+ hasAudio: parsed.hasAudio,
864
+ },
865
+ }, null, 2));
866
+ return;
867
+ }
868
+ defaultRuntime.log(`MEDIA:${written.path}`);
869
+ }
870
+ catch (err) {
871
+ defaultRuntime.error(`nodes screen record failed: ${String(err)}`);
872
+ defaultRuntime.exit(1);
873
+ }
874
+ }), { timeoutMs: 180_000 });
875
+ const location = nodes
876
+ .command("location")
877
+ .description("Fetch location from a paired node");
878
+ nodesCallOpts(location
879
+ .command("get")
880
+ .description("Fetch the current location from a node")
881
+ .requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
882
+ .option("--max-age <ms>", "Use cached location newer than this (ms)")
883
+ .option("--accuracy <coarse|balanced|precise>", "Desired accuracy (default: balanced/precise depending on node setting)")
884
+ .option("--location-timeout <ms>", "Location fix timeout (ms)", "10000")
885
+ .option("--invoke-timeout <ms>", "Node invoke timeout in ms (default 20000)", "20000")
886
+ .action(async (opts) => {
887
+ try {
888
+ const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
889
+ const maxAgeMs = opts.maxAge
890
+ ? Number.parseInt(String(opts.maxAge), 10)
891
+ : undefined;
892
+ const desiredAccuracyRaw = typeof opts.accuracy === "string"
893
+ ? opts.accuracy.trim().toLowerCase()
894
+ : undefined;
895
+ const desiredAccuracy = desiredAccuracyRaw === "coarse" ||
896
+ desiredAccuracyRaw === "balanced" ||
897
+ desiredAccuracyRaw === "precise"
898
+ ? desiredAccuracyRaw
899
+ : undefined;
900
+ const timeoutMs = opts.locationTimeout
901
+ ? Number.parseInt(String(opts.locationTimeout), 10)
902
+ : undefined;
903
+ const invokeTimeoutMs = opts.invokeTimeout
904
+ ? Number.parseInt(String(opts.invokeTimeout), 10)
905
+ : undefined;
906
+ const invokeParams = {
907
+ nodeId,
908
+ command: "location.get",
909
+ params: {
910
+ maxAgeMs: Number.isFinite(maxAgeMs) ? maxAgeMs : undefined,
911
+ desiredAccuracy,
912
+ timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : undefined,
913
+ },
914
+ idempotencyKey: randomIdempotencyKey(),
915
+ };
916
+ if (typeof invokeTimeoutMs === "number" &&
917
+ Number.isFinite(invokeTimeoutMs)) {
918
+ invokeParams.timeoutMs = invokeTimeoutMs;
919
+ }
920
+ const raw = (await callGatewayCli("node.invoke", opts, invokeParams));
921
+ const res = typeof raw === "object" && raw !== null
922
+ ? raw
923
+ : {};
924
+ const payload = res.payload && typeof res.payload === "object"
925
+ ? res.payload
926
+ : {};
927
+ if (opts.json) {
928
+ defaultRuntime.log(JSON.stringify(payload, null, 2));
929
+ return;
930
+ }
931
+ const lat = payload.lat;
932
+ const lon = payload.lon;
933
+ const acc = payload.accuracyMeters;
934
+ if (typeof lat === "number" && typeof lon === "number") {
935
+ const accText = typeof acc === "number" ? ` ±${acc.toFixed(1)}m` : "";
936
+ defaultRuntime.log(`${lat},${lon}${accText}`);
937
+ return;
938
+ }
939
+ defaultRuntime.log(JSON.stringify(payload));
940
+ }
941
+ catch (err) {
942
+ defaultRuntime.error(`nodes location get failed: ${String(err)}`);
943
+ defaultRuntime.exit(1);
944
+ }
945
+ }), { timeoutMs: 30_000 });
946
+ }