clawdbot 2026.1.4

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 (503) hide show
  1. package/CHANGELOG.md +115 -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/cron/isolated-agent.js +499 -0
  189. package/dist/cron/run-log.js +72 -0
  190. package/dist/cron/schedule.js +24 -0
  191. package/dist/cron/service.js +471 -0
  192. package/dist/cron/store.js +43 -0
  193. package/dist/cron/types.js +1 -0
  194. package/dist/discord/index.js +2 -0
  195. package/dist/discord/monitor.js +1188 -0
  196. package/dist/discord/probe.js +54 -0
  197. package/dist/discord/send.js +577 -0
  198. package/dist/discord/token.js +8 -0
  199. package/dist/gateway/auth.js +121 -0
  200. package/dist/gateway/call.js +94 -0
  201. package/dist/gateway/chat-attachments.js +41 -0
  202. package/dist/gateway/client.js +180 -0
  203. package/dist/gateway/config-reload.js +274 -0
  204. package/dist/gateway/control-ui.js +184 -0
  205. package/dist/gateway/hooks-mapping.js +282 -0
  206. package/dist/gateway/hooks.js +168 -0
  207. package/dist/gateway/net.js +29 -0
  208. package/dist/gateway/protocol/index.js +61 -0
  209. package/dist/gateway/protocol/schema.js +560 -0
  210. package/dist/gateway/server-bridge-subscriptions.js +93 -0
  211. package/dist/gateway/server-bridge.js +1013 -0
  212. package/dist/gateway/server-browser.js +12 -0
  213. package/dist/gateway/server-chat.js +159 -0
  214. package/dist/gateway/server-constants.js +8 -0
  215. package/dist/gateway/server-discovery.js +62 -0
  216. package/dist/gateway/server-http.js +165 -0
  217. package/dist/gateway/server-methods/agent-job.js +125 -0
  218. package/dist/gateway/server-methods/agent.js +250 -0
  219. package/dist/gateway/server-methods/chat.js +200 -0
  220. package/dist/gateway/server-methods/config.js +50 -0
  221. package/dist/gateway/server-methods/connect.js +6 -0
  222. package/dist/gateway/server-methods/cron.js +83 -0
  223. package/dist/gateway/server-methods/health.js +28 -0
  224. package/dist/gateway/server-methods/models.js +16 -0
  225. package/dist/gateway/server-methods/nodes.js +294 -0
  226. package/dist/gateway/server-methods/providers.js +217 -0
  227. package/dist/gateway/server-methods/send.js +166 -0
  228. package/dist/gateway/server-methods/sessions.js +305 -0
  229. package/dist/gateway/server-methods/skills.js +83 -0
  230. package/dist/gateway/server-methods/system.js +118 -0
  231. package/dist/gateway/server-methods/talk.js +22 -0
  232. package/dist/gateway/server-methods/types.js +1 -0
  233. package/dist/gateway/server-methods/voicewake.js +30 -0
  234. package/dist/gateway/server-methods/web.js +58 -0
  235. package/dist/gateway/server-methods/wizard.js +100 -0
  236. package/dist/gateway/server-methods.js +53 -0
  237. package/dist/gateway/server-providers.js +644 -0
  238. package/dist/gateway/server-shared.js +1 -0
  239. package/dist/gateway/server-utils.js +35 -0
  240. package/dist/gateway/server.js +1437 -0
  241. package/dist/gateway/session-utils.js +216 -0
  242. package/dist/gateway/ws-log.js +349 -0
  243. package/dist/gateway/ws-logging.js +8 -0
  244. package/dist/globals.js +41 -0
  245. package/dist/hooks/gmail-ops.js +236 -0
  246. package/dist/hooks/gmail-setup-utils.js +278 -0
  247. package/dist/hooks/gmail-watcher.js +175 -0
  248. package/dist/hooks/gmail.js +177 -0
  249. package/dist/index.js +50 -0
  250. package/dist/infra/agent-events.js +46 -0
  251. package/dist/infra/binaries.js +9 -0
  252. package/dist/infra/bonjour-discovery.js +163 -0
  253. package/dist/infra/bonjour.js +200 -0
  254. package/dist/infra/bridge/server.js +562 -0
  255. package/dist/infra/canvas-host-url.js +54 -0
  256. package/dist/infra/env.js +8 -0
  257. package/dist/infra/errors.js +28 -0
  258. package/dist/infra/gateway-lock.js +8 -0
  259. package/dist/infra/heartbeat-events.js +21 -0
  260. package/dist/infra/heartbeat-runner.js +453 -0
  261. package/dist/infra/heartbeat-wake.js +61 -0
  262. package/dist/infra/is-main.js +37 -0
  263. package/dist/infra/machine-name.js +40 -0
  264. package/dist/infra/node-pairing.js +211 -0
  265. package/dist/infra/pam.js +42 -0
  266. package/dist/infra/path-env.js +92 -0
  267. package/dist/infra/ports.js +87 -0
  268. package/dist/infra/provider-summary.js +80 -0
  269. package/dist/infra/restart.js +29 -0
  270. package/dist/infra/retry.js +16 -0
  271. package/dist/infra/runtime-guard.js +59 -0
  272. package/dist/infra/system-events.js +44 -0
  273. package/dist/infra/system-presence.js +216 -0
  274. package/dist/infra/tailnet.js +46 -0
  275. package/dist/infra/tailscale.js +149 -0
  276. package/dist/infra/voicewake.js +77 -0
  277. package/dist/infra/widearea-dns.js +123 -0
  278. package/dist/infra/ws.js +13 -0
  279. package/dist/logger.js +52 -0
  280. package/dist/logging.js +490 -0
  281. package/dist/macos/gateway-daemon.js +141 -0
  282. package/dist/macos/relay.js +46 -0
  283. package/dist/media/constants.js +33 -0
  284. package/dist/media/host.js +42 -0
  285. package/dist/media/image-ops.js +121 -0
  286. package/dist/media/mime.js +115 -0
  287. package/dist/media/parse.js +81 -0
  288. package/dist/media/server.js +64 -0
  289. package/dist/media/store.js +139 -0
  290. package/dist/process/command-queue.js +97 -0
  291. package/dist/process/exec.js +75 -0
  292. package/dist/protocol.schema.json +2918 -0
  293. package/dist/provider-web.js +8 -0
  294. package/dist/providers/web/index.js +2 -0
  295. package/dist/runtime.js +8 -0
  296. package/dist/telegram/bot.js +394 -0
  297. package/dist/telegram/download.js +34 -0
  298. package/dist/telegram/index.js +4 -0
  299. package/dist/telegram/monitor.js +47 -0
  300. package/dist/telegram/probe.js +63 -0
  301. package/dist/telegram/proxy.js +9 -0
  302. package/dist/telegram/send.js +138 -0
  303. package/dist/telegram/token.js +30 -0
  304. package/dist/telegram/webhook-set.js +12 -0
  305. package/dist/telegram/webhook.js +56 -0
  306. package/dist/utils.js +133 -0
  307. package/dist/version.js +18 -0
  308. package/dist/web/active-listener.js +7 -0
  309. package/dist/web/auto-reply.js +1203 -0
  310. package/dist/web/inbound.js +481 -0
  311. package/dist/web/login-qr.js +204 -0
  312. package/dist/web/login.js +59 -0
  313. package/dist/web/media.js +148 -0
  314. package/dist/web/outbound.js +67 -0
  315. package/dist/web/qr-image.js +97 -0
  316. package/dist/web/reconnect.js +60 -0
  317. package/dist/web/reply-heartbeat-wake.js +61 -0
  318. package/dist/web/session.js +346 -0
  319. package/docs/AGENTS.default.md +116 -0
  320. package/docs/CNAME +1 -0
  321. package/docs/RELEASING.md +64 -0
  322. package/docs/_config.yml +51 -0
  323. package/docs/_layouts/default.html +145 -0
  324. package/docs/agent-send.md +21 -0
  325. package/docs/agent.md +104 -0
  326. package/docs/android/connect.md +131 -0
  327. package/docs/architecture.md +89 -0
  328. package/docs/assets/markdown.css +130 -0
  329. package/docs/assets/pixel-lobster.svg +60 -0
  330. package/docs/assets/terminal.css +497 -0
  331. package/docs/assets/theme.js +55 -0
  332. package/docs/audio.md +50 -0
  333. package/docs/background-process.md +74 -0
  334. package/docs/bash.md +32 -0
  335. package/docs/bonjour.md +159 -0
  336. package/docs/browser.md +289 -0
  337. package/docs/camera.md +152 -0
  338. package/docs/clawd.md +199 -0
  339. package/docs/clawdbot-mac.md +104 -0
  340. package/docs/configuration.md +1177 -0
  341. package/docs/control-api.md +49 -0
  342. package/docs/control-ui.md +83 -0
  343. package/docs/cron.md +374 -0
  344. package/docs/dashboard.md +17 -0
  345. package/docs/device-models.md +46 -0
  346. package/docs/discord.md +293 -0
  347. package/docs/discovery.md +112 -0
  348. package/docs/docker.md +251 -0
  349. package/docs/docs.json +86 -0
  350. package/docs/doctor.md +47 -0
  351. package/docs/elevated.md +31 -0
  352. package/docs/faq.md +640 -0
  353. package/docs/gateway/pairing.md +109 -0
  354. package/docs/gateway-lock.md +28 -0
  355. package/docs/gateway.md +174 -0
  356. package/docs/gmail-pubsub.md +191 -0
  357. package/docs/grammy.md +27 -0
  358. package/docs/group-messages.md +71 -0
  359. package/docs/groups.md +78 -0
  360. package/docs/health.md +28 -0
  361. package/docs/heartbeat.md +64 -0
  362. package/docs/images.md +52 -0
  363. package/docs/imessage.md +63 -0
  364. package/docs/index.md +182 -0
  365. package/docs/ios/connect.md +177 -0
  366. package/docs/ios/spec.md +236 -0
  367. package/docs/location-command.md +95 -0
  368. package/docs/logging.md +99 -0
  369. package/docs/lore.md +131 -0
  370. package/docs/mac/bun.md +133 -0
  371. package/docs/mac/canvas.md +161 -0
  372. package/docs/mac/child-process.md +72 -0
  373. package/docs/mac/dev-setup.md +81 -0
  374. package/docs/mac/health.md +28 -0
  375. package/docs/mac/icon.md +26 -0
  376. package/docs/mac/logging.md +51 -0
  377. package/docs/mac/menu-bar.md +69 -0
  378. package/docs/mac/peekaboo.md +170 -0
  379. package/docs/mac/permissions.md +40 -0
  380. package/docs/mac/release.md +76 -0
  381. package/docs/mac/remote.md +57 -0
  382. package/docs/mac/signing.md +41 -0
  383. package/docs/mac/skills.md +27 -0
  384. package/docs/mac/voice-overlay.md +52 -0
  385. package/docs/mac/voicewake.md +56 -0
  386. package/docs/mac/webchat.md +27 -0
  387. package/docs/mac/xpc.md +40 -0
  388. package/docs/models.md +90 -0
  389. package/docs/nix.md +49 -0
  390. package/docs/nodes.md +157 -0
  391. package/docs/onboarding-config-protocol.md +29 -0
  392. package/docs/onboarding.md +185 -0
  393. package/docs/presence.md +133 -0
  394. package/docs/queue.md +78 -0
  395. package/docs/refactor/browser-control-simplification.md +58 -0
  396. package/docs/refactor/canvas-a2ui.md +93 -0
  397. package/docs/refactor/cli-unification.md +64 -0
  398. package/docs/refactor/gateway-client.md +31 -0
  399. package/docs/refactor/gateway.md +99 -0
  400. package/docs/refactor/new-arch.md +171 -0
  401. package/docs/refactor/tui.md +26 -0
  402. package/docs/refactor/web-gateway-troubleshooting.md +37 -0
  403. package/docs/refactor/webagent-session.md +46 -0
  404. package/docs/remote-gateway-readme.md +148 -0
  405. package/docs/remote.md +66 -0
  406. package/docs/research/memory.md +227 -0
  407. package/docs/rpc.md +35 -0
  408. package/docs/security.md +168 -0
  409. package/docs/session-tool.md +119 -0
  410. package/docs/session.md +84 -0
  411. package/docs/sessions.md +8 -0
  412. package/docs/setup.md +118 -0
  413. package/docs/signal.md +113 -0
  414. package/docs/skills-config.md +58 -0
  415. package/docs/skills.md +149 -0
  416. package/docs/slack.md +158 -0
  417. package/docs/surface.md +20 -0
  418. package/docs/tailscale.md +71 -0
  419. package/docs/talk.md +79 -0
  420. package/docs/telegram.md +90 -0
  421. package/docs/templates/AGENTS.md +126 -0
  422. package/docs/templates/BOOTSTRAP.md +53 -0
  423. package/docs/templates/IDENTITY.md +17 -0
  424. package/docs/templates/SOUL.md +41 -0
  425. package/docs/templates/TOOLS.md +41 -0
  426. package/docs/templates/USER.md +22 -0
  427. package/docs/test.md +35 -0
  428. package/docs/thinking.md +46 -0
  429. package/docs/tools.md +248 -0
  430. package/docs/troubleshooting.md +227 -0
  431. package/docs/tui.md +69 -0
  432. package/docs/typebox.md +42 -0
  433. package/docs/voicewake.md +61 -0
  434. package/docs/web.md +115 -0
  435. package/docs/webchat.md +34 -0
  436. package/docs/webhook.md +132 -0
  437. package/docs/whatsapp-clawd.jpg +0 -0
  438. package/docs/whatsapp.md +142 -0
  439. package/docs/wizard.md +158 -0
  440. package/package.json +178 -0
  441. package/skills/apple-notes/SKILL.md +50 -0
  442. package/skills/apple-reminders/SKILL.md +67 -0
  443. package/skills/bear-notes/SKILL.md +79 -0
  444. package/skills/bird/SKILL.md +25 -0
  445. package/skills/blogwatcher/SKILL.md +46 -0
  446. package/skills/blucli/SKILL.md +27 -0
  447. package/skills/brave-search/SKILL.md +30 -0
  448. package/skills/brave-search/scripts/content.mjs +53 -0
  449. package/skills/brave-search/scripts/search.mjs +79 -0
  450. package/skills/camsnap/SKILL.md +25 -0
  451. package/skills/clawdhub/SKILL.md +53 -0
  452. package/skills/coding-agent/SKILL.md +275 -0
  453. package/skills/discord/SKILL.md +369 -0
  454. package/skills/eightctl/SKILL.md +29 -0
  455. package/skills/food-order/SKILL.md +41 -0
  456. package/skills/gemini/SKILL.md +23 -0
  457. package/skills/gifgrep/SKILL.md +47 -0
  458. package/skills/github/SKILL.md +47 -0
  459. package/skills/gog/SKILL.md +36 -0
  460. package/skills/goplaces/SKILL.md +30 -0
  461. package/skills/imsg/SKILL.md +25 -0
  462. package/skills/local-places/SERVER_README.md +101 -0
  463. package/skills/local-places/SKILL.md +91 -0
  464. package/skills/local-places/pyproject.toml +27 -0
  465. package/skills/local-places/src/local_places/__init__.py +2 -0
  466. package/skills/local-places/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
  467. package/skills/local-places/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
  468. package/skills/local-places/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
  469. package/skills/local-places/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
  470. package/skills/local-places/src/local_places/google_places.py +314 -0
  471. package/skills/local-places/src/local_places/main.py +65 -0
  472. package/skills/local-places/src/local_places/schemas.py +107 -0
  473. package/skills/mcporter/SKILL.md +38 -0
  474. package/skills/nano-banana-pro/SKILL.md +29 -0
  475. package/skills/nano-banana-pro/scripts/generate_image.py +167 -0
  476. package/skills/nano-pdf/SKILL.md +20 -0
  477. package/skills/notion/SKILL.md +156 -0
  478. package/skills/obsidian/SKILL.md +55 -0
  479. package/skills/openai-image-gen/SKILL.md +31 -0
  480. package/skills/openai-image-gen/scripts/gen.py +173 -0
  481. package/skills/openai-whisper/SKILL.md +19 -0
  482. package/skills/openai-whisper-api/SKILL.md +43 -0
  483. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  484. package/skills/openhue/SKILL.md +30 -0
  485. package/skills/oracle/SKILL.md +105 -0
  486. package/skills/ordercli/SKILL.md +47 -0
  487. package/skills/peekaboo/SKILL.md +153 -0
  488. package/skills/qmd/SKILL.md +26 -0
  489. package/skills/sag/SKILL.md +62 -0
  490. package/skills/slack/SKILL.md +143 -0
  491. package/skills/songsee/SKILL.md +29 -0
  492. package/skills/sonoscli/SKILL.md +26 -0
  493. package/skills/spotify-player/SKILL.md +34 -0
  494. package/skills/summarize/SKILL.md +49 -0
  495. package/skills/things-mac/SKILL.md +61 -0
  496. package/skills/tmux/SKILL.md +121 -0
  497. package/skills/tmux/scripts/find-sessions.sh +112 -0
  498. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  499. package/skills/trello/SKILL.md +84 -0
  500. package/skills/video-frames/SKILL.md +29 -0
  501. package/skills/video-frames/scripts/frame.sh +81 -0
  502. package/skills/wacli/SKILL.md +42 -0
  503. package/skills/weather/SKILL.md +49 -0
@@ -0,0 +1,177 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { DEFAULT_GATEWAY_PORT, resolveGatewayPort, } from "../config/config.js";
3
+ export const DEFAULT_GMAIL_LABEL = "INBOX";
4
+ export const DEFAULT_GMAIL_TOPIC = "gog-gmail-watch";
5
+ export const DEFAULT_GMAIL_SUBSCRIPTION = "gog-gmail-watch-push";
6
+ export const DEFAULT_GMAIL_SERVE_BIND = "127.0.0.1";
7
+ export const DEFAULT_GMAIL_SERVE_PORT = 8788;
8
+ export const DEFAULT_GMAIL_SERVE_PATH = "/gmail-pubsub";
9
+ export const DEFAULT_GMAIL_MAX_BYTES = 20_000;
10
+ export const DEFAULT_GMAIL_RENEW_MINUTES = 12 * 60;
11
+ export const DEFAULT_HOOKS_PATH = "/hooks";
12
+ export function generateHookToken(bytes = 24) {
13
+ return randomBytes(bytes).toString("hex");
14
+ }
15
+ export function mergeHookPresets(existing, preset) {
16
+ const next = new Set((existing ?? []).map((item) => item.trim()).filter(Boolean));
17
+ next.add(preset);
18
+ return Array.from(next);
19
+ }
20
+ export function normalizeHooksPath(raw) {
21
+ const base = raw?.trim() || DEFAULT_HOOKS_PATH;
22
+ if (base === "/")
23
+ return DEFAULT_HOOKS_PATH;
24
+ const withSlash = base.startsWith("/") ? base : `/${base}`;
25
+ return withSlash.replace(/\/+$/, "");
26
+ }
27
+ export function normalizeServePath(raw) {
28
+ const base = raw?.trim() || DEFAULT_GMAIL_SERVE_PATH;
29
+ // Tailscale funnel/serve strips the set-path prefix before proxying.
30
+ // To accept requests at /<path> externally, gog must listen on "/".
31
+ if (base === "/")
32
+ return "/";
33
+ const withSlash = base.startsWith("/") ? base : `/${base}`;
34
+ return withSlash.replace(/\/+$/, "");
35
+ }
36
+ export function buildDefaultHookUrl(hooksPath, port = DEFAULT_GATEWAY_PORT) {
37
+ const basePath = normalizeHooksPath(hooksPath);
38
+ const baseUrl = `http://127.0.0.1:${port}`;
39
+ return joinUrl(baseUrl, `${basePath}/gmail`);
40
+ }
41
+ export function resolveGmailHookRuntimeConfig(cfg, overrides) {
42
+ const hooks = cfg.hooks;
43
+ const gmail = hooks?.gmail;
44
+ const hookToken = overrides.hookToken ?? hooks?.token ?? "";
45
+ if (!hookToken) {
46
+ return { ok: false, error: "hooks.token missing (needed for gmail hook)" };
47
+ }
48
+ const account = overrides.account ?? gmail?.account ?? "";
49
+ if (!account) {
50
+ return { ok: false, error: "gmail account required" };
51
+ }
52
+ const topic = overrides.topic ?? gmail?.topic ?? "";
53
+ if (!topic) {
54
+ return { ok: false, error: "gmail topic required" };
55
+ }
56
+ const subscription = overrides.subscription ?? gmail?.subscription ?? DEFAULT_GMAIL_SUBSCRIPTION;
57
+ const pushToken = overrides.pushToken ?? gmail?.pushToken ?? "";
58
+ if (!pushToken) {
59
+ return { ok: false, error: "gmail push token required" };
60
+ }
61
+ const hookUrl = overrides.hookUrl ??
62
+ gmail?.hookUrl ??
63
+ buildDefaultHookUrl(hooks?.path, resolveGatewayPort(cfg));
64
+ const includeBody = overrides.includeBody ?? gmail?.includeBody ?? true;
65
+ const maxBytesRaw = overrides.maxBytes ?? gmail?.maxBytes;
66
+ const maxBytes = typeof maxBytesRaw === "number" &&
67
+ Number.isFinite(maxBytesRaw) &&
68
+ maxBytesRaw > 0
69
+ ? Math.floor(maxBytesRaw)
70
+ : DEFAULT_GMAIL_MAX_BYTES;
71
+ const renewEveryMinutesRaw = overrides.renewEveryMinutes ?? gmail?.renewEveryMinutes;
72
+ const renewEveryMinutes = typeof renewEveryMinutesRaw === "number" &&
73
+ Number.isFinite(renewEveryMinutesRaw) &&
74
+ renewEveryMinutesRaw > 0
75
+ ? Math.floor(renewEveryMinutesRaw)
76
+ : DEFAULT_GMAIL_RENEW_MINUTES;
77
+ const serveBind = overrides.serveBind ?? gmail?.serve?.bind ?? DEFAULT_GMAIL_SERVE_BIND;
78
+ const servePortRaw = overrides.servePort ?? gmail?.serve?.port;
79
+ const servePort = typeof servePortRaw === "number" &&
80
+ Number.isFinite(servePortRaw) &&
81
+ servePortRaw > 0
82
+ ? Math.floor(servePortRaw)
83
+ : DEFAULT_GMAIL_SERVE_PORT;
84
+ const servePathRaw = overrides.servePath ?? gmail?.serve?.path;
85
+ const hasExplicitServePath = typeof servePathRaw === "string" && servePathRaw.trim().length > 0;
86
+ const tailscaleMode = overrides.tailscaleMode ?? gmail?.tailscale?.mode ?? "off";
87
+ // When exposing the push endpoint via Tailscale, the public path is stripped
88
+ // before proxying; use "/" internally unless the user set a path explicitly.
89
+ const servePath = normalizeServePath(tailscaleMode !== "off" && !hasExplicitServePath ? "/" : servePathRaw);
90
+ const tailscalePathRaw = overrides.tailscalePath ?? gmail?.tailscale?.path;
91
+ const tailscalePath = normalizeServePath(tailscaleMode !== "off" && !tailscalePathRaw
92
+ ? hasExplicitServePath
93
+ ? servePathRaw
94
+ : DEFAULT_GMAIL_SERVE_PATH
95
+ : (tailscalePathRaw ?? servePath));
96
+ return {
97
+ ok: true,
98
+ value: {
99
+ account,
100
+ label: overrides.label ?? gmail?.label ?? DEFAULT_GMAIL_LABEL,
101
+ topic,
102
+ subscription,
103
+ pushToken,
104
+ hookToken,
105
+ hookUrl,
106
+ includeBody,
107
+ maxBytes,
108
+ renewEveryMinutes,
109
+ serve: {
110
+ bind: serveBind,
111
+ port: servePort,
112
+ path: servePath,
113
+ },
114
+ tailscale: {
115
+ mode: tailscaleMode,
116
+ path: tailscalePath,
117
+ },
118
+ },
119
+ };
120
+ }
121
+ export function buildGogWatchStartArgs(cfg) {
122
+ return [
123
+ "gmail",
124
+ "watch",
125
+ "start",
126
+ "--account",
127
+ cfg.account,
128
+ "--label",
129
+ cfg.label,
130
+ "--topic",
131
+ cfg.topic,
132
+ ];
133
+ }
134
+ export function buildGogWatchServeArgs(cfg) {
135
+ const args = [
136
+ "gmail",
137
+ "watch",
138
+ "serve",
139
+ "--account",
140
+ cfg.account,
141
+ "--bind",
142
+ cfg.serve.bind,
143
+ "--port",
144
+ String(cfg.serve.port),
145
+ "--path",
146
+ cfg.serve.path,
147
+ "--token",
148
+ cfg.pushToken,
149
+ "--hook-url",
150
+ cfg.hookUrl,
151
+ "--hook-token",
152
+ cfg.hookToken,
153
+ ];
154
+ if (cfg.includeBody) {
155
+ args.push("--include-body");
156
+ }
157
+ if (cfg.maxBytes > 0) {
158
+ args.push("--max-bytes", String(cfg.maxBytes));
159
+ }
160
+ return args;
161
+ }
162
+ export function buildTopicPath(projectId, topicName) {
163
+ return `projects/${projectId}/topics/${topicName}`;
164
+ }
165
+ export function parseTopicPath(topic) {
166
+ const match = topic.trim().match(/^projects\/([^/]+)\/topics\/([^/]+)$/i);
167
+ if (!match)
168
+ return null;
169
+ return { projectId: match[1] ?? "", topicName: match[2] ?? "" };
170
+ }
171
+ function joinUrl(base, path) {
172
+ const url = new URL(base);
173
+ const basePath = url.pathname.replace(/\/+$/, "");
174
+ const extra = path.startsWith("/") ? path : `/${path}`;
175
+ url.pathname = `${basePath}${extra}`;
176
+ return url.toString();
177
+ }
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { fileURLToPath } from "node:url";
4
+ import dotenv from "dotenv";
5
+ import { getReplyFromConfig } from "./auto-reply/reply.js";
6
+ import { applyTemplate } from "./auto-reply/templating.js";
7
+ import { createDefaultDeps } from "./cli/deps.js";
8
+ import { promptYesNo } from "./cli/prompt.js";
9
+ import { waitForever } from "./cli/wait.js";
10
+ import { loadConfig } from "./config/config.js";
11
+ import { deriveSessionKey, loadSessionStore, resolveSessionKey, resolveStorePath, saveSessionStore, } from "./config/sessions.js";
12
+ import { ensureBinary } from "./infra/binaries.js";
13
+ import { normalizeEnv } from "./infra/env.js";
14
+ import { isMainModule } from "./infra/is-main.js";
15
+ import { ensureClawdbotCliOnPath } from "./infra/path-env.js";
16
+ import { describePortOwner, ensurePortAvailable, handlePortError, PortInUseError, } from "./infra/ports.js";
17
+ import { assertSupportedRuntime } from "./infra/runtime-guard.js";
18
+ import { enableConsoleCapture } from "./logging.js";
19
+ import { runCommandWithTimeout, runExec } from "./process/exec.js";
20
+ import { monitorWebProvider } from "./provider-web.js";
21
+ import { assertProvider, normalizeE164, toWhatsappJid } from "./utils.js";
22
+ dotenv.config({ quiet: true });
23
+ normalizeEnv();
24
+ ensureClawdbotCliOnPath();
25
+ // Capture all console output into structured logs while keeping stdout/stderr behavior.
26
+ enableConsoleCapture();
27
+ // Enforce the minimum supported runtime before doing any work.
28
+ assertSupportedRuntime();
29
+ import { buildProgram } from "./cli/program.js";
30
+ const program = buildProgram();
31
+ export { assertProvider, applyTemplate, createDefaultDeps, deriveSessionKey, describePortOwner, ensureBinary, ensurePortAvailable, getReplyFromConfig, handlePortError, loadConfig, loadSessionStore, monitorWebProvider, normalizeE164, PortInUseError, promptYesNo, resolveSessionKey, resolveStorePath, runCommandWithTimeout, runExec, saveSessionStore, toWhatsappJid, waitForever, };
32
+ const isMain = isMainModule({
33
+ currentFile: fileURLToPath(import.meta.url),
34
+ });
35
+ if (isMain) {
36
+ // Global error handlers to prevent silent crashes from unhandled rejections/exceptions.
37
+ // These log the error and exit gracefully instead of crashing without trace.
38
+ process.on("unhandledRejection", (reason, _promise) => {
39
+ console.error("[clawdbot] Unhandled promise rejection:", reason instanceof Error ? (reason.stack ?? reason.message) : reason);
40
+ process.exit(1);
41
+ });
42
+ process.on("uncaughtException", (error) => {
43
+ console.error("[clawdbot] Uncaught exception:", error.stack ?? error.message);
44
+ process.exit(1);
45
+ });
46
+ void program.parseAsync(process.argv).catch((err) => {
47
+ console.error("[clawdbot] CLI failed:", err instanceof Error ? (err.stack ?? err.message) : err);
48
+ process.exit(1);
49
+ });
50
+ }
@@ -0,0 +1,46 @@
1
+ // Keep per-run counters so streams stay strictly monotonic per runId.
2
+ const seqByRun = new Map();
3
+ const listeners = new Set();
4
+ const runContextById = new Map();
5
+ export function registerAgentRunContext(runId, context) {
6
+ if (!runId)
7
+ return;
8
+ const existing = runContextById.get(runId);
9
+ if (!existing) {
10
+ runContextById.set(runId, { ...context });
11
+ return;
12
+ }
13
+ if (context.sessionKey && existing.sessionKey !== context.sessionKey) {
14
+ existing.sessionKey = context.sessionKey;
15
+ }
16
+ }
17
+ export function getAgentRunContext(runId) {
18
+ return runContextById.get(runId);
19
+ }
20
+ export function clearAgentRunContext(runId) {
21
+ runContextById.delete(runId);
22
+ }
23
+ export function resetAgentRunContextForTest() {
24
+ runContextById.clear();
25
+ }
26
+ export function emitAgentEvent(event) {
27
+ const nextSeq = (seqByRun.get(event.runId) ?? 0) + 1;
28
+ seqByRun.set(event.runId, nextSeq);
29
+ const enriched = {
30
+ ...event,
31
+ seq: nextSeq,
32
+ ts: Date.now(),
33
+ };
34
+ for (const listener of listeners) {
35
+ try {
36
+ listener(enriched);
37
+ }
38
+ catch {
39
+ /* ignore */
40
+ }
41
+ }
42
+ }
43
+ export function onAgentEvent(listener) {
44
+ listeners.add(listener);
45
+ return () => listeners.delete(listener);
46
+ }
@@ -0,0 +1,9 @@
1
+ import { runExec } from "../process/exec.js";
2
+ import { defaultRuntime } from "../runtime.js";
3
+ export async function ensureBinary(name, exec = runExec, runtime = defaultRuntime) {
4
+ // Abort early if a required CLI tool is missing.
5
+ await exec("which", [name]).catch(() => {
6
+ runtime.error(`Missing required binary: ${name}. Please install it.`);
7
+ runtime.exit(1);
8
+ });
9
+ }
@@ -0,0 +1,163 @@
1
+ import { runCommandWithTimeout } from "../process/exec.js";
2
+ const DEFAULT_TIMEOUT_MS = 2000;
3
+ function parseIntOrNull(value) {
4
+ if (!value)
5
+ return undefined;
6
+ const parsed = Number.parseInt(value, 10);
7
+ return Number.isFinite(parsed) ? parsed : undefined;
8
+ }
9
+ function parseTxtTokens(tokens) {
10
+ const txt = {};
11
+ for (const token of tokens) {
12
+ const idx = token.indexOf("=");
13
+ if (idx <= 0)
14
+ continue;
15
+ const key = token.slice(0, idx).trim();
16
+ const value = token.slice(idx + 1).trim();
17
+ if (!key)
18
+ continue;
19
+ txt[key] = value;
20
+ }
21
+ return txt;
22
+ }
23
+ function parseDnsSdBrowse(stdout) {
24
+ const instances = new Set();
25
+ for (const raw of stdout.split("\n")) {
26
+ const line = raw.trim();
27
+ if (!line || !line.includes("_clawdbot-bridge._tcp"))
28
+ continue;
29
+ if (!line.includes("Add"))
30
+ continue;
31
+ const match = line.match(/_clawdbot-bridge\._tcp\.?\s+(.+)$/);
32
+ if (match?.[1]) {
33
+ instances.add(match[1].trim());
34
+ }
35
+ }
36
+ return Array.from(instances.values());
37
+ }
38
+ function parseDnsSdResolve(stdout, instanceName) {
39
+ const beacon = { instanceName };
40
+ let txt = {};
41
+ for (const raw of stdout.split("\n")) {
42
+ const line = raw.trim();
43
+ if (!line)
44
+ continue;
45
+ if (line.includes("can be reached at")) {
46
+ const match = line.match(/can be reached at\s+([^\s:]+):(\d+)/i);
47
+ if (match?.[1]) {
48
+ beacon.host = match[1].replace(/\.$/, "");
49
+ }
50
+ if (match?.[2]) {
51
+ beacon.port = parseIntOrNull(match[2]);
52
+ }
53
+ continue;
54
+ }
55
+ if (line.startsWith("txt") || line.includes("txtvers=")) {
56
+ const tokens = line.split(/\s+/).filter(Boolean);
57
+ txt = parseTxtTokens(tokens);
58
+ }
59
+ }
60
+ beacon.txt = Object.keys(txt).length ? txt : undefined;
61
+ if (txt.displayName)
62
+ beacon.displayName = txt.displayName;
63
+ if (txt.lanHost)
64
+ beacon.lanHost = txt.lanHost;
65
+ if (txt.tailnetDns)
66
+ beacon.tailnetDns = txt.tailnetDns;
67
+ if (txt.cliPath)
68
+ beacon.cliPath = txt.cliPath;
69
+ beacon.bridgePort = parseIntOrNull(txt.bridgePort);
70
+ beacon.gatewayPort = parseIntOrNull(txt.gatewayPort);
71
+ beacon.sshPort = parseIntOrNull(txt.sshPort);
72
+ if (!beacon.displayName)
73
+ beacon.displayName = instanceName;
74
+ return beacon;
75
+ }
76
+ async function discoverViaDnsSd(timeoutMs) {
77
+ const browse = await runCommandWithTimeout(["dns-sd", "-B", "_clawdbot-bridge._tcp", "local."], { timeoutMs });
78
+ const instances = parseDnsSdBrowse(browse.stdout);
79
+ const results = [];
80
+ for (const instance of instances) {
81
+ const resolved = await runCommandWithTimeout(["dns-sd", "-L", instance, "_clawdbot-bridge._tcp", "local."], { timeoutMs });
82
+ const parsed = parseDnsSdResolve(resolved.stdout, instance);
83
+ if (parsed)
84
+ results.push(parsed);
85
+ }
86
+ return results;
87
+ }
88
+ function parseAvahiBrowse(stdout) {
89
+ const results = [];
90
+ let current = null;
91
+ for (const raw of stdout.split("\n")) {
92
+ const line = raw.trimEnd();
93
+ if (!line)
94
+ continue;
95
+ if (line.startsWith("=") && line.includes("_clawdbot-bridge._tcp")) {
96
+ if (current)
97
+ results.push(current);
98
+ const marker = " _clawdbot-bridge._tcp";
99
+ const idx = line.indexOf(marker);
100
+ const left = idx >= 0 ? line.slice(0, idx).trim() : line;
101
+ const parts = left.split(/\s+/);
102
+ const instanceName = parts.length > 3 ? parts.slice(3).join(" ") : left;
103
+ current = {
104
+ instanceName,
105
+ displayName: instanceName,
106
+ };
107
+ continue;
108
+ }
109
+ if (!current)
110
+ continue;
111
+ const trimmed = line.trim();
112
+ if (trimmed.startsWith("hostname =")) {
113
+ const match = trimmed.match(/hostname\s*=\s*\[([^\]]+)\]/);
114
+ if (match?.[1])
115
+ current.host = match[1];
116
+ continue;
117
+ }
118
+ if (trimmed.startsWith("port =")) {
119
+ const match = trimmed.match(/port\s*=\s*\[(\d+)\]/);
120
+ if (match?.[1])
121
+ current.port = parseIntOrNull(match[1]);
122
+ continue;
123
+ }
124
+ if (trimmed.startsWith("txt =")) {
125
+ const tokens = Array.from(trimmed.matchAll(/"([^"]*)"/g), (m) => m[1]);
126
+ const txt = parseTxtTokens(tokens);
127
+ current.txt = Object.keys(txt).length ? txt : undefined;
128
+ if (txt.displayName)
129
+ current.displayName = txt.displayName;
130
+ if (txt.lanHost)
131
+ current.lanHost = txt.lanHost;
132
+ if (txt.tailnetDns)
133
+ current.tailnetDns = txt.tailnetDns;
134
+ if (txt.cliPath)
135
+ current.cliPath = txt.cliPath;
136
+ current.bridgePort = parseIntOrNull(txt.bridgePort);
137
+ current.gatewayPort = parseIntOrNull(txt.gatewayPort);
138
+ current.sshPort = parseIntOrNull(txt.sshPort);
139
+ }
140
+ }
141
+ if (current)
142
+ results.push(current);
143
+ return results;
144
+ }
145
+ async function discoverViaAvahi(timeoutMs) {
146
+ const browse = await runCommandWithTimeout(["avahi-browse", "-rt", "_clawdbot-bridge._tcp"], { timeoutMs });
147
+ return parseAvahiBrowse(browse.stdout);
148
+ }
149
+ export async function discoverGatewayBeacons(opts = {}) {
150
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
151
+ try {
152
+ if (process.platform === "darwin") {
153
+ return await discoverViaDnsSd(timeoutMs);
154
+ }
155
+ if (process.platform === "linux") {
156
+ return await discoverViaAvahi(timeoutMs);
157
+ }
158
+ }
159
+ catch {
160
+ return [];
161
+ }
162
+ return [];
163
+ }
@@ -0,0 +1,200 @@
1
+ import os from "node:os";
2
+ import { logDebug, logWarn } from "../logger.js";
3
+ import { getLogger } from "../logging.js";
4
+ function isDisabledByEnv() {
5
+ if (process.env.CLAWDBOT_DISABLE_BONJOUR === "1")
6
+ return true;
7
+ if (process.env.NODE_ENV === "test")
8
+ return true;
9
+ if (process.env.VITEST)
10
+ return true;
11
+ return false;
12
+ }
13
+ function safeServiceName(name) {
14
+ const trimmed = name.trim();
15
+ return trimmed.length > 0 ? trimmed : "Clawdbot";
16
+ }
17
+ function prettifyInstanceName(name) {
18
+ const normalized = name.trim().replace(/\s+/g, " ");
19
+ return normalized.replace(/\s+\(Clawdbot\)\s*$/i, "").trim() || normalized;
20
+ }
21
+ function formatBonjourError(err) {
22
+ if (err instanceof Error) {
23
+ const msg = err.message || String(err);
24
+ return err.name && err.name !== "Error" ? `${err.name}: ${msg}` : msg;
25
+ }
26
+ return String(err);
27
+ }
28
+ function serviceSummary(label, svc) {
29
+ let fqdn = "unknown";
30
+ let hostname = "unknown";
31
+ let port = -1;
32
+ try {
33
+ fqdn = svc.getFQDN();
34
+ }
35
+ catch {
36
+ // ignore
37
+ }
38
+ try {
39
+ hostname = svc.getHostname();
40
+ }
41
+ catch {
42
+ // ignore
43
+ }
44
+ try {
45
+ port = svc.getPort();
46
+ }
47
+ catch {
48
+ // ignore
49
+ }
50
+ const state = typeof svc.serviceState === "string" ? svc.serviceState : "unknown";
51
+ return `${label} fqdn=${fqdn} host=${hostname} port=${port} state=${state}`;
52
+ }
53
+ export async function startGatewayBonjourAdvertiser(opts) {
54
+ if (isDisabledByEnv()) {
55
+ return { stop: async () => { } };
56
+ }
57
+ const { getResponder, Protocol } = await import("@homebridge/ciao");
58
+ const responder = getResponder();
59
+ // mDNS service instance names are single DNS labels; dots in hostnames (like
60
+ // `Mac.localdomain`) can confuse some resolvers/browsers and break discovery.
61
+ // Keep only the first label and normalize away a trailing `.local`.
62
+ const hostname = os
63
+ .hostname()
64
+ .replace(/\.local$/i, "")
65
+ .split(".")[0]
66
+ .trim() || "clawdbot";
67
+ const instanceName = typeof opts.instanceName === "string" && opts.instanceName.trim()
68
+ ? opts.instanceName.trim()
69
+ : `${hostname} (Clawdbot)`;
70
+ const displayName = prettifyInstanceName(instanceName);
71
+ const txtBase = {
72
+ role: "gateway",
73
+ gatewayPort: String(opts.gatewayPort),
74
+ lanHost: `${hostname}.local`,
75
+ displayName,
76
+ };
77
+ if (typeof opts.bridgePort === "number" && opts.bridgePort > 0) {
78
+ txtBase.bridgePort = String(opts.bridgePort);
79
+ }
80
+ if (typeof opts.canvasPort === "number" && opts.canvasPort > 0) {
81
+ txtBase.canvasPort = String(opts.canvasPort);
82
+ }
83
+ if (typeof opts.tailnetDns === "string" && opts.tailnetDns.trim()) {
84
+ txtBase.tailnetDns = opts.tailnetDns.trim();
85
+ }
86
+ if (typeof opts.cliPath === "string" && opts.cliPath.trim()) {
87
+ txtBase.cliPath = opts.cliPath.trim();
88
+ }
89
+ const services = [];
90
+ // Bridge beacon (used by macOS/iOS/Android nodes and the mac app onboarding flow).
91
+ if (typeof opts.bridgePort === "number" && opts.bridgePort > 0) {
92
+ const bridge = responder.createService({
93
+ name: safeServiceName(instanceName),
94
+ type: "clawdbot-bridge",
95
+ protocol: "tcp" /* Protocol.TCP */,
96
+ port: opts.bridgePort,
97
+ domain: "local",
98
+ hostname,
99
+ txt: {
100
+ ...txtBase,
101
+ sshPort: String(opts.sshPort ?? 22),
102
+ transport: "bridge",
103
+ },
104
+ });
105
+ services.push({
106
+ label: "bridge",
107
+ svc: bridge,
108
+ });
109
+ }
110
+ logDebug(`bonjour: starting (hostname=${hostname}, instance=${JSON.stringify(safeServiceName(instanceName))}, gatewayPort=${opts.gatewayPort}, bridgePort=${opts.bridgePort ?? 0}, sshPort=${opts.sshPort ?? 22})`);
111
+ for (const { label, svc } of services) {
112
+ try {
113
+ svc.on("name-change", (name) => {
114
+ const next = typeof name === "string" ? name : String(name);
115
+ logWarn(`bonjour: ${label} name conflict resolved; newName=${JSON.stringify(next)}`);
116
+ });
117
+ svc.on("hostname-change", (nextHostname) => {
118
+ const next = typeof nextHostname === "string"
119
+ ? nextHostname
120
+ : String(nextHostname);
121
+ logWarn(`bonjour: ${label} hostname conflict resolved; newHostname=${JSON.stringify(next)}`);
122
+ });
123
+ }
124
+ catch (err) {
125
+ logDebug(`bonjour: failed to attach listeners for ${label}: ${String(err)}`);
126
+ }
127
+ }
128
+ // Do not block gateway startup on mDNS probing/announce. Advertising can take
129
+ // multiple seconds depending on network state; the gateway should come up even
130
+ // if Bonjour is slow or fails.
131
+ for (const { label, svc } of services) {
132
+ try {
133
+ void svc
134
+ .advertise()
135
+ .then(() => {
136
+ // Keep this out of stdout/stderr (menubar + tests) but capture in the rolling log.
137
+ getLogger().info(`bonjour: advertised ${serviceSummary(label, svc)}`);
138
+ })
139
+ .catch((err) => {
140
+ logWarn(`bonjour: advertise failed (${serviceSummary(label, svc)}): ${formatBonjourError(err)}`);
141
+ });
142
+ }
143
+ catch (err) {
144
+ logWarn(`bonjour: advertise threw (${serviceSummary(label, svc)}): ${formatBonjourError(err)}`);
145
+ }
146
+ }
147
+ // Watchdog: if we ever end up in an unannounced state (e.g. after sleep/wake or
148
+ // interface churn), try to re-advertise instead of requiring a full gateway restart.
149
+ const lastRepairAttempt = new Map();
150
+ const watchdog = setInterval(() => {
151
+ for (const { label, svc } of services) {
152
+ const stateUnknown = svc.serviceState;
153
+ if (typeof stateUnknown !== "string")
154
+ continue;
155
+ if (stateUnknown === "announced" || stateUnknown === "announcing")
156
+ continue;
157
+ let key = label;
158
+ try {
159
+ key = `${label}:${svc.getFQDN()}`;
160
+ }
161
+ catch {
162
+ // ignore
163
+ }
164
+ const now = Date.now();
165
+ const last = lastRepairAttempt.get(key) ?? 0;
166
+ if (now - last < 30_000)
167
+ continue;
168
+ lastRepairAttempt.set(key, now);
169
+ logWarn(`bonjour: watchdog detected non-announced service; attempting re-advertise (${serviceSummary(label, svc)})`);
170
+ try {
171
+ void svc.advertise().catch((err) => {
172
+ logWarn(`bonjour: watchdog advertise failed (${serviceSummary(label, svc)}): ${formatBonjourError(err)}`);
173
+ });
174
+ }
175
+ catch (err) {
176
+ logWarn(`bonjour: watchdog advertise threw (${serviceSummary(label, svc)}): ${formatBonjourError(err)}`);
177
+ }
178
+ }
179
+ }, 60_000);
180
+ watchdog.unref?.();
181
+ return {
182
+ stop: async () => {
183
+ clearInterval(watchdog);
184
+ for (const { svc } of services) {
185
+ try {
186
+ await svc.destroy();
187
+ }
188
+ catch {
189
+ /* ignore */
190
+ }
191
+ }
192
+ try {
193
+ await responder.shutdown();
194
+ }
195
+ catch {
196
+ /* ignore */
197
+ }
198
+ },
199
+ };
200
+ }