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.
- package/CHANGELOG.md +120 -0
- package/LICENSE +21 -0
- package/README-header.png +0 -0
- package/README.md +297 -0
- package/dist/agents/agent-paths.js +17 -0
- package/dist/agents/bash-process-registry.js +126 -0
- package/dist/agents/bash-tools.js +837 -0
- package/dist/agents/clawdbot-tools.js +30 -0
- package/dist/agents/clawdis-tools.js +27 -0
- package/dist/agents/context.js +34 -0
- package/dist/agents/defaults.js +6 -0
- package/dist/agents/model-auth.js +112 -0
- package/dist/agents/model-catalog.js +55 -0
- package/dist/agents/model-fallback.js +191 -0
- package/dist/agents/model-scan.js +263 -0
- package/dist/agents/model-selection.js +116 -0
- package/dist/agents/models-config.js +49 -0
- package/dist/agents/pi-embedded-helpers.js +74 -0
- package/dist/agents/pi-embedded-runner.js +407 -0
- package/dist/agents/pi-embedded-subscribe.js +568 -0
- package/dist/agents/pi-embedded-utils.js +20 -0
- package/dist/agents/pi-embedded.js +1 -0
- package/dist/agents/pi-oauth.js +88 -0
- package/dist/agents/pi-tools.js +433 -0
- package/dist/agents/sandbox-paths.js +68 -0
- package/dist/agents/sandbox.js +644 -0
- package/dist/agents/shell-utils.js +53 -0
- package/dist/agents/skills-install.js +244 -0
- package/dist/agents/skills-status.js +157 -0
- package/dist/agents/skills.js +470 -0
- package/dist/agents/steerable-agent-loop.js +338 -0
- package/dist/agents/steerable-provider-transport.js +48 -0
- package/dist/agents/system-prompt.js +104 -0
- package/dist/agents/tool-display.js +162 -0
- package/dist/agents/tool-images.js +138 -0
- package/dist/agents/tools/browser-tool.js +339 -0
- package/dist/agents/tools/canvas-tool.js +193 -0
- package/dist/agents/tools/common.js +88 -0
- package/dist/agents/tools/cron-tool.js +124 -0
- package/dist/agents/tools/discord-actions-guild.js +186 -0
- package/dist/agents/tools/discord-actions-messaging.js +285 -0
- package/dist/agents/tools/discord-actions-moderation.js +70 -0
- package/dist/agents/tools/discord-actions.js +56 -0
- package/dist/agents/tools/discord-schema.js +199 -0
- package/dist/agents/tools/discord-tool.js +16 -0
- package/dist/agents/tools/gateway-tool.js +46 -0
- package/dist/agents/tools/gateway.js +27 -0
- package/dist/agents/tools/image-tool.js +132 -0
- package/dist/agents/tools/nodes-tool.js +413 -0
- package/dist/agents/tools/nodes-utils.js +92 -0
- package/dist/agents/tools/sessions-helpers.js +88 -0
- package/dist/agents/tools/sessions-history-tool.js +53 -0
- package/dist/agents/tools/sessions-list-tool.js +143 -0
- package/dist/agents/tools/sessions-send-helpers.js +100 -0
- package/dist/agents/tools/sessions-send-tool.js +347 -0
- package/dist/agents/tools/slack-actions.js +129 -0
- package/dist/agents/tools/slack-schema.js +59 -0
- package/dist/agents/tools/slack-tool.js +16 -0
- package/dist/agents/usage.js +39 -0
- package/dist/agents/workspace.js +241 -0
- package/dist/auto-reply/chunk.js +76 -0
- package/dist/auto-reply/envelope.js +38 -0
- package/dist/auto-reply/group-activation.js +20 -0
- package/dist/auto-reply/heartbeat.js +57 -0
- package/dist/auto-reply/model.js +14 -0
- package/dist/auto-reply/reply/abort.js +14 -0
- package/dist/auto-reply/reply/agent-runner.js +371 -0
- package/dist/auto-reply/reply/block-streaming.js +34 -0
- package/dist/auto-reply/reply/body.js +29 -0
- package/dist/auto-reply/reply/commands.js +207 -0
- package/dist/auto-reply/reply/directive-handling.js +361 -0
- package/dist/auto-reply/reply/directives.js +47 -0
- package/dist/auto-reply/reply/followup-runner.js +149 -0
- package/dist/auto-reply/reply/groups.js +91 -0
- package/dist/auto-reply/reply/mentions.js +38 -0
- package/dist/auto-reply/reply/model-selection.js +114 -0
- package/dist/auto-reply/reply/queue.js +399 -0
- package/dist/auto-reply/reply/reply-tags.js +26 -0
- package/dist/auto-reply/reply/session-updates.js +87 -0
- package/dist/auto-reply/reply/session.js +160 -0
- package/dist/auto-reply/reply/typing.js +75 -0
- package/dist/auto-reply/reply.js +535 -0
- package/dist/auto-reply/send-policy.js +28 -0
- package/dist/auto-reply/status.js +158 -0
- package/dist/auto-reply/templating.js +9 -0
- package/dist/auto-reply/thinking.js +49 -0
- package/dist/auto-reply/tokens.js +2 -0
- package/dist/auto-reply/tool-meta.js +74 -0
- package/dist/auto-reply/transcription.js +57 -0
- package/dist/auto-reply/types.js +1 -0
- package/dist/browser/bridge-server.js +37 -0
- package/dist/browser/cdp.js +382 -0
- package/dist/browser/chrome.js +432 -0
- package/dist/browser/client-actions-core.js +67 -0
- package/dist/browser/client-actions-observe.js +24 -0
- package/dist/browser/client-actions-types.js +1 -0
- package/dist/browser/client-actions.js +3 -0
- package/dist/browser/client-fetch.js +43 -0
- package/dist/browser/client.js +105 -0
- package/dist/browser/config.js +140 -0
- package/dist/browser/constants.js +4 -0
- package/dist/browser/profiles-service.js +122 -0
- package/dist/browser/profiles.js +85 -0
- package/dist/browser/pw-ai.js +2 -0
- package/dist/browser/pw-session.js +144 -0
- package/dist/browser/pw-tools-core.js +363 -0
- package/dist/browser/routes/agent.js +535 -0
- package/dist/browser/routes/basic.js +155 -0
- package/dist/browser/routes/index.js +8 -0
- package/dist/browser/routes/tabs.js +105 -0
- package/dist/browser/routes/utils.js +62 -0
- package/dist/browser/screenshot.js +40 -0
- package/dist/browser/server-context.js +377 -0
- package/dist/browser/server.js +81 -0
- package/dist/browser/target-id.js +18 -0
- package/dist/browser/trash.js +21 -0
- package/dist/canvas-host/a2ui/.bundle.hash +1 -0
- package/dist/canvas-host/a2ui/a2ui.bundle.js +17768 -0
- package/dist/canvas-host/a2ui/index.html +246 -0
- package/dist/canvas-host/a2ui.js +187 -0
- package/dist/canvas-host/server.js +382 -0
- package/dist/cli/browser-cli-actions-input.js +459 -0
- package/dist/cli/browser-cli-actions-observe.js +56 -0
- package/dist/cli/browser-cli-examples.js +31 -0
- package/dist/cli/browser-cli-inspect.js +97 -0
- package/dist/cli/browser-cli-manage.js +286 -0
- package/dist/cli/browser-cli-shared.js +1 -0
- package/dist/cli/browser-cli.js +26 -0
- package/dist/cli/canvas-cli.js +416 -0
- package/dist/cli/cron-cli.js +454 -0
- package/dist/cli/deps.js +17 -0
- package/dist/cli/dns-cli.js +180 -0
- package/dist/cli/gateway-cli.js +489 -0
- package/dist/cli/gateway-rpc.js +20 -0
- package/dist/cli/hooks-cli.js +135 -0
- package/dist/cli/models-cli.js +248 -0
- package/dist/cli/nodes-camera.js +57 -0
- package/dist/cli/nodes-canvas.js +26 -0
- package/dist/cli/nodes-cli.js +946 -0
- package/dist/cli/nodes-screen.js +37 -0
- package/dist/cli/parse-duration.js +20 -0
- package/dist/cli/ports.js +97 -0
- package/dist/cli/program.js +406 -0
- package/dist/cli/prompt.js +19 -0
- package/dist/cli/tui-cli.js +35 -0
- package/dist/cli/wait.js +8 -0
- package/dist/commands/agent.js +645 -0
- package/dist/commands/antigravity-oauth.js +327 -0
- package/dist/commands/configure.js +480 -0
- package/dist/commands/doctor.js +484 -0
- package/dist/commands/health.js +108 -0
- package/dist/commands/models/aliases.js +64 -0
- package/dist/commands/models/fallbacks.js +99 -0
- package/dist/commands/models/image-fallbacks.js +99 -0
- package/dist/commands/models/list.js +323 -0
- package/dist/commands/models/scan.js +266 -0
- package/dist/commands/models/set-image.js +23 -0
- package/dist/commands/models/set.js +23 -0
- package/dist/commands/models/shared.js +72 -0
- package/dist/commands/models.js +7 -0
- package/dist/commands/onboard-auth.js +70 -0
- package/dist/commands/onboard-helpers.js +295 -0
- package/dist/commands/onboard-interactive.js +17 -0
- package/dist/commands/onboard-non-interactive.js +202 -0
- package/dist/commands/onboard-providers.js +634 -0
- package/dist/commands/onboard-remote.js +120 -0
- package/dist/commands/onboard-skills.js +148 -0
- package/dist/commands/onboard-types.js +1 -0
- package/dist/commands/onboard.js +12 -0
- package/dist/commands/send.js +124 -0
- package/dist/commands/sessions.js +212 -0
- package/dist/commands/setup.js +58 -0
- package/dist/commands/signal-install.js +135 -0
- package/dist/commands/status.js +207 -0
- package/dist/commands/update.js +16 -0
- package/dist/config/config.js +6 -0
- package/dist/config/defaults.js +61 -0
- package/dist/config/io.js +147 -0
- package/dist/config/legacy-migrate.js +13 -0
- package/dist/config/legacy.js +159 -0
- package/dist/config/paths.js +71 -0
- package/dist/config/schema.js +150 -0
- package/dist/config/sessions.js +282 -0
- package/dist/config/talk.js +31 -0
- package/dist/config/types.js +1 -0
- package/dist/config/validation.js +29 -0
- package/dist/config/zod-schema.js +831 -0
- package/dist/control-ui/assets/index-BFID3yAA.css +1 -0
- package/dist/control-ui/assets/index-CE_axlTS.js +2235 -0
- package/dist/control-ui/assets/index-CE_axlTS.js.map +1 -0
- package/dist/control-ui/index.html +15 -0
- package/dist/cron/isolated-agent.js +499 -0
- package/dist/cron/run-log.js +72 -0
- package/dist/cron/schedule.js +24 -0
- package/dist/cron/service.js +471 -0
- package/dist/cron/store.js +43 -0
- package/dist/cron/types.js +1 -0
- package/dist/daemon/constants.js +10 -0
- package/dist/daemon/launchd.js +276 -0
- package/dist/daemon/legacy.js +63 -0
- package/dist/daemon/program-args.js +76 -0
- package/dist/daemon/schtasks.js +257 -0
- package/dist/daemon/service.js +60 -0
- package/dist/daemon/systemd.js +266 -0
- package/dist/discord/index.js +2 -0
- package/dist/discord/monitor.js +1188 -0
- package/dist/discord/probe.js +54 -0
- package/dist/discord/send.js +577 -0
- package/dist/discord/token.js +8 -0
- package/dist/gateway/auth.js +121 -0
- package/dist/gateway/call.js +94 -0
- package/dist/gateway/chat-attachments.js +41 -0
- package/dist/gateway/client.js +180 -0
- package/dist/gateway/config-reload.js +274 -0
- package/dist/gateway/control-ui.js +184 -0
- package/dist/gateway/hooks-mapping.js +282 -0
- package/dist/gateway/hooks.js +168 -0
- package/dist/gateway/net.js +29 -0
- package/dist/gateway/protocol/index.js +61 -0
- package/dist/gateway/protocol/schema.js +560 -0
- package/dist/gateway/server-bridge-subscriptions.js +93 -0
- package/dist/gateway/server-bridge.js +1013 -0
- package/dist/gateway/server-browser.js +12 -0
- package/dist/gateway/server-chat.js +159 -0
- package/dist/gateway/server-constants.js +8 -0
- package/dist/gateway/server-discovery.js +62 -0
- package/dist/gateway/server-http.js +165 -0
- package/dist/gateway/server-methods/agent-job.js +125 -0
- package/dist/gateway/server-methods/agent.js +250 -0
- package/dist/gateway/server-methods/chat.js +200 -0
- package/dist/gateway/server-methods/config.js +50 -0
- package/dist/gateway/server-methods/connect.js +6 -0
- package/dist/gateway/server-methods/cron.js +83 -0
- package/dist/gateway/server-methods/health.js +28 -0
- package/dist/gateway/server-methods/models.js +16 -0
- package/dist/gateway/server-methods/nodes.js +294 -0
- package/dist/gateway/server-methods/providers.js +217 -0
- package/dist/gateway/server-methods/send.js +166 -0
- package/dist/gateway/server-methods/sessions.js +305 -0
- package/dist/gateway/server-methods/skills.js +83 -0
- package/dist/gateway/server-methods/system.js +118 -0
- package/dist/gateway/server-methods/talk.js +22 -0
- package/dist/gateway/server-methods/types.js +1 -0
- package/dist/gateway/server-methods/voicewake.js +30 -0
- package/dist/gateway/server-methods/web.js +58 -0
- package/dist/gateway/server-methods/wizard.js +100 -0
- package/dist/gateway/server-methods.js +53 -0
- package/dist/gateway/server-providers.js +644 -0
- package/dist/gateway/server-shared.js +1 -0
- package/dist/gateway/server-utils.js +35 -0
- package/dist/gateway/server.js +1437 -0
- package/dist/gateway/session-utils.js +216 -0
- package/dist/gateway/ws-log.js +349 -0
- package/dist/gateway/ws-logging.js +8 -0
- package/dist/globals.js +41 -0
- package/dist/hooks/gmail-ops.js +236 -0
- package/dist/hooks/gmail-setup-utils.js +278 -0
- package/dist/hooks/gmail-watcher.js +175 -0
- package/dist/hooks/gmail.js +177 -0
- package/dist/imessage/client.js +165 -0
- package/dist/imessage/index.js +3 -0
- package/dist/imessage/monitor.js +272 -0
- package/dist/imessage/probe.js +26 -0
- package/dist/imessage/send.js +83 -0
- package/dist/imessage/targets.js +176 -0
- package/dist/index.js +50 -0
- package/dist/infra/agent-events.js +46 -0
- package/dist/infra/binaries.js +9 -0
- package/dist/infra/bonjour-discovery.js +163 -0
- package/dist/infra/bonjour.js +200 -0
- package/dist/infra/bridge/server.js +562 -0
- package/dist/infra/canvas-host-url.js +54 -0
- package/dist/infra/env.js +8 -0
- package/dist/infra/errors.js +28 -0
- package/dist/infra/gateway-lock.js +8 -0
- package/dist/infra/heartbeat-events.js +21 -0
- package/dist/infra/heartbeat-runner.js +453 -0
- package/dist/infra/heartbeat-wake.js +61 -0
- package/dist/infra/is-main.js +37 -0
- package/dist/infra/machine-name.js +40 -0
- package/dist/infra/node-pairing.js +211 -0
- package/dist/infra/pam.js +42 -0
- package/dist/infra/path-env.js +92 -0
- package/dist/infra/ports.js +87 -0
- package/dist/infra/provider-summary.js +80 -0
- package/dist/infra/restart.js +29 -0
- package/dist/infra/retry.js +16 -0
- package/dist/infra/runtime-guard.js +59 -0
- package/dist/infra/system-events.js +44 -0
- package/dist/infra/system-presence.js +216 -0
- package/dist/infra/tailnet.js +46 -0
- package/dist/infra/tailscale.js +149 -0
- package/dist/infra/voicewake.js +77 -0
- package/dist/infra/widearea-dns.js +123 -0
- package/dist/infra/ws.js +13 -0
- package/dist/logger.js +52 -0
- package/dist/logging.js +490 -0
- package/dist/macos/gateway-daemon.js +141 -0
- package/dist/macos/relay.js +46 -0
- package/dist/media/constants.js +33 -0
- package/dist/media/host.js +42 -0
- package/dist/media/image-ops.js +121 -0
- package/dist/media/mime.js +115 -0
- package/dist/media/parse.js +81 -0
- package/dist/media/server.js +64 -0
- package/dist/media/store.js +139 -0
- package/dist/process/command-queue.js +97 -0
- package/dist/process/exec.js +75 -0
- package/dist/protocol.schema.json +2918 -0
- package/dist/provider-web.js +8 -0
- package/dist/providers/web/index.js +2 -0
- package/dist/runtime.js +8 -0
- package/dist/sessions/send-policy.js +68 -0
- package/dist/signal/client.js +134 -0
- package/dist/signal/daemon.js +69 -0
- package/dist/signal/index.js +3 -0
- package/dist/signal/monitor.js +336 -0
- package/dist/signal/probe.js +46 -0
- package/dist/signal/send.js +91 -0
- package/dist/slack/actions.js +97 -0
- package/dist/slack/index.js +5 -0
- package/dist/slack/monitor.js +1029 -0
- package/dist/slack/probe.js +47 -0
- package/dist/slack/send.js +131 -0
- package/dist/slack/token.js +10 -0
- package/dist/telegram/bot.js +394 -0
- package/dist/telegram/download.js +34 -0
- package/dist/telegram/index.js +4 -0
- package/dist/telegram/monitor.js +47 -0
- package/dist/telegram/probe.js +63 -0
- package/dist/telegram/proxy.js +9 -0
- package/dist/telegram/send.js +138 -0
- package/dist/telegram/token.js +30 -0
- package/dist/telegram/webhook-set.js +12 -0
- package/dist/telegram/webhook.js +56 -0
- package/dist/tui/commands.js +74 -0
- package/dist/tui/components/assistant-message.js +16 -0
- package/dist/tui/components/chat-log.js +92 -0
- package/dist/tui/components/custom-editor.js +53 -0
- package/dist/tui/components/selectors.js +8 -0
- package/dist/tui/components/tool-execution.js +111 -0
- package/dist/tui/components/user-message.js +17 -0
- package/dist/tui/gateway-chat.js +140 -0
- package/dist/tui/layout.js +41 -0
- package/dist/tui/message-list.js +57 -0
- package/dist/tui/theme/theme.js +80 -0
- package/dist/tui/theme.js +25 -0
- package/dist/tui/tui.js +708 -0
- package/dist/utils.js +133 -0
- package/dist/version.js +18 -0
- package/dist/web/active-listener.js +7 -0
- package/dist/web/auto-reply.js +1203 -0
- package/dist/web/inbound.js +481 -0
- package/dist/web/login-qr.js +204 -0
- package/dist/web/login.js +59 -0
- package/dist/web/media.js +148 -0
- package/dist/web/outbound.js +67 -0
- package/dist/web/qr-image.js +97 -0
- package/dist/web/reconnect.js +60 -0
- package/dist/web/reply-heartbeat-wake.js +61 -0
- package/dist/web/session.js +346 -0
- package/dist/wizard/clack-prompter.js +56 -0
- package/dist/wizard/onboarding.js +452 -0
- package/dist/wizard/prompts.js +6 -0
- package/dist/wizard/session.js +203 -0
- package/docs/AGENTS.default.md +116 -0
- package/docs/CNAME +1 -0
- package/docs/RELEASING.md +64 -0
- package/docs/_config.yml +51 -0
- package/docs/_layouts/default.html +145 -0
- package/docs/agent-send.md +21 -0
- package/docs/agent.md +104 -0
- package/docs/android/connect.md +131 -0
- package/docs/architecture.md +89 -0
- package/docs/assets/markdown.css +130 -0
- package/docs/assets/pixel-lobster.svg +60 -0
- package/docs/assets/terminal.css +497 -0
- package/docs/assets/theme.js +55 -0
- package/docs/audio.md +50 -0
- package/docs/background-process.md +74 -0
- package/docs/bash.md +32 -0
- package/docs/bonjour.md +159 -0
- package/docs/browser.md +289 -0
- package/docs/camera.md +152 -0
- package/docs/clawd.md +199 -0
- package/docs/clawdbot-mac.md +104 -0
- package/docs/configuration.md +1177 -0
- package/docs/control-api.md +49 -0
- package/docs/control-ui.md +83 -0
- package/docs/cron.md +374 -0
- package/docs/dashboard.md +17 -0
- package/docs/device-models.md +46 -0
- package/docs/discord.md +293 -0
- package/docs/discovery.md +112 -0
- package/docs/docker.md +251 -0
- package/docs/docs.json +86 -0
- package/docs/doctor.md +47 -0
- package/docs/elevated.md +31 -0
- package/docs/faq.md +640 -0
- package/docs/gateway/pairing.md +109 -0
- package/docs/gateway-lock.md +28 -0
- package/docs/gateway.md +174 -0
- package/docs/gmail-pubsub.md +191 -0
- package/docs/grammy.md +27 -0
- package/docs/group-messages.md +71 -0
- package/docs/groups.md +78 -0
- package/docs/health.md +28 -0
- package/docs/heartbeat.md +64 -0
- package/docs/images.md +52 -0
- package/docs/imessage.md +63 -0
- package/docs/index.md +182 -0
- package/docs/ios/connect.md +177 -0
- package/docs/ios/spec.md +236 -0
- package/docs/location-command.md +95 -0
- package/docs/logging.md +99 -0
- package/docs/lore.md +131 -0
- package/docs/mac/bun.md +133 -0
- package/docs/mac/canvas.md +161 -0
- package/docs/mac/child-process.md +72 -0
- package/docs/mac/dev-setup.md +81 -0
- package/docs/mac/health.md +28 -0
- package/docs/mac/icon.md +26 -0
- package/docs/mac/logging.md +51 -0
- package/docs/mac/menu-bar.md +69 -0
- package/docs/mac/peekaboo.md +170 -0
- package/docs/mac/permissions.md +40 -0
- package/docs/mac/release.md +76 -0
- package/docs/mac/remote.md +57 -0
- package/docs/mac/signing.md +41 -0
- package/docs/mac/skills.md +27 -0
- package/docs/mac/voice-overlay.md +52 -0
- package/docs/mac/voicewake.md +56 -0
- package/docs/mac/webchat.md +27 -0
- package/docs/mac/xpc.md +40 -0
- package/docs/models.md +90 -0
- package/docs/nix.md +49 -0
- package/docs/nodes.md +157 -0
- package/docs/onboarding-config-protocol.md +29 -0
- package/docs/onboarding.md +185 -0
- package/docs/presence.md +133 -0
- package/docs/queue.md +78 -0
- package/docs/refactor/browser-control-simplification.md +58 -0
- package/docs/refactor/canvas-a2ui.md +93 -0
- package/docs/refactor/cli-unification.md +64 -0
- package/docs/refactor/gateway-client.md +31 -0
- package/docs/refactor/gateway.md +99 -0
- package/docs/refactor/new-arch.md +171 -0
- package/docs/refactor/tui.md +26 -0
- package/docs/refactor/web-gateway-troubleshooting.md +37 -0
- package/docs/refactor/webagent-session.md +46 -0
- package/docs/remote-gateway-readme.md +148 -0
- package/docs/remote.md +66 -0
- package/docs/research/memory.md +227 -0
- package/docs/rpc.md +35 -0
- package/docs/security.md +168 -0
- package/docs/session-tool.md +119 -0
- package/docs/session.md +84 -0
- package/docs/sessions.md +8 -0
- package/docs/setup.md +118 -0
- package/docs/signal.md +113 -0
- package/docs/skills-config.md +58 -0
- package/docs/skills.md +149 -0
- package/docs/slack.md +158 -0
- package/docs/surface.md +20 -0
- package/docs/tailscale.md +71 -0
- package/docs/talk.md +79 -0
- package/docs/telegram.md +90 -0
- package/docs/templates/AGENTS.md +126 -0
- package/docs/templates/BOOTSTRAP.md +53 -0
- package/docs/templates/IDENTITY.md +17 -0
- package/docs/templates/SOUL.md +41 -0
- package/docs/templates/TOOLS.md +41 -0
- package/docs/templates/USER.md +22 -0
- package/docs/test.md +35 -0
- package/docs/thinking.md +46 -0
- package/docs/tools.md +248 -0
- package/docs/troubleshooting.md +227 -0
- package/docs/tui.md +69 -0
- package/docs/typebox.md +42 -0
- package/docs/voicewake.md +61 -0
- package/docs/web.md +115 -0
- package/docs/webchat.md +34 -0
- package/docs/webhook.md +132 -0
- package/docs/whatsapp-clawd.jpg +0 -0
- package/docs/whatsapp.md +142 -0
- package/docs/wizard.md +158 -0
- package/package.json +186 -0
- package/skills/apple-notes/SKILL.md +50 -0
- package/skills/apple-reminders/SKILL.md +67 -0
- package/skills/bear-notes/SKILL.md +79 -0
- package/skills/bird/SKILL.md +25 -0
- package/skills/blogwatcher/SKILL.md +46 -0
- package/skills/blucli/SKILL.md +27 -0
- package/skills/brave-search/SKILL.md +30 -0
- package/skills/brave-search/scripts/content.mjs +53 -0
- package/skills/brave-search/scripts/search.mjs +79 -0
- package/skills/camsnap/SKILL.md +25 -0
- package/skills/clawdhub/SKILL.md +53 -0
- package/skills/coding-agent/SKILL.md +275 -0
- package/skills/discord/SKILL.md +369 -0
- package/skills/eightctl/SKILL.md +29 -0
- package/skills/food-order/SKILL.md +41 -0
- package/skills/gemini/SKILL.md +23 -0
- package/skills/gifgrep/SKILL.md +47 -0
- package/skills/github/SKILL.md +47 -0
- package/skills/gog/SKILL.md +36 -0
- package/skills/goplaces/SKILL.md +30 -0
- package/skills/imsg/SKILL.md +25 -0
- package/skills/local-places/SERVER_README.md +101 -0
- package/skills/local-places/SKILL.md +91 -0
- package/skills/local-places/pyproject.toml +27 -0
- package/skills/local-places/src/local_places/__init__.py +2 -0
- package/skills/local-places/src/local_places/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/local-places/src/local_places/__pycache__/google_places.cpython-314.pyc +0 -0
- package/skills/local-places/src/local_places/__pycache__/main.cpython-314.pyc +0 -0
- package/skills/local-places/src/local_places/__pycache__/schemas.cpython-314.pyc +0 -0
- package/skills/local-places/src/local_places/google_places.py +314 -0
- package/skills/local-places/src/local_places/main.py +65 -0
- package/skills/local-places/src/local_places/schemas.py +107 -0
- package/skills/mcporter/SKILL.md +38 -0
- package/skills/nano-banana-pro/SKILL.md +29 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +167 -0
- package/skills/nano-pdf/SKILL.md +20 -0
- package/skills/notion/SKILL.md +156 -0
- package/skills/obsidian/SKILL.md +55 -0
- package/skills/openai-image-gen/SKILL.md +31 -0
- package/skills/openai-image-gen/scripts/gen.py +173 -0
- package/skills/openai-whisper/SKILL.md +19 -0
- package/skills/openai-whisper-api/SKILL.md +43 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +30 -0
- package/skills/oracle/SKILL.md +105 -0
- package/skills/ordercli/SKILL.md +47 -0
- package/skills/peekaboo/SKILL.md +153 -0
- package/skills/qmd/SKILL.md +26 -0
- package/skills/sag/SKILL.md +62 -0
- package/skills/slack/SKILL.md +143 -0
- package/skills/songsee/SKILL.md +29 -0
- package/skills/sonoscli/SKILL.md +26 -0
- package/skills/spotify-player/SKILL.md +34 -0
- package/skills/summarize/SKILL.md +49 -0
- package/skills/things-mac/SKILL.md +61 -0
- package/skills/tmux/SKILL.md +121 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +84 -0
- package/skills/video-frames/SKILL.md +29 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/wacli/SKILL.md +42 -0
- package/skills/weather/SKILL.md +49 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
const PENDING_TTL_MS = 5 * 60 * 1000;
|
|
6
|
+
function defaultBaseDir() {
|
|
7
|
+
return path.join(os.homedir(), ".clawdbot");
|
|
8
|
+
}
|
|
9
|
+
function resolvePaths(baseDir) {
|
|
10
|
+
const root = baseDir ?? defaultBaseDir();
|
|
11
|
+
const dir = path.join(root, "nodes");
|
|
12
|
+
return {
|
|
13
|
+
dir,
|
|
14
|
+
pendingPath: path.join(dir, "pending.json"),
|
|
15
|
+
pairedPath: path.join(dir, "paired.json"),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function readJSON(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function writeJSONAtomic(filePath, value) {
|
|
28
|
+
const dir = path.dirname(filePath);
|
|
29
|
+
await fs.mkdir(dir, { recursive: true });
|
|
30
|
+
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
31
|
+
await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
|
|
32
|
+
await fs.rename(tmp, filePath);
|
|
33
|
+
}
|
|
34
|
+
function pruneExpiredPending(pendingById, nowMs) {
|
|
35
|
+
for (const [id, req] of Object.entries(pendingById)) {
|
|
36
|
+
if (nowMs - req.ts > PENDING_TTL_MS) {
|
|
37
|
+
delete pendingById[id];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let lock = Promise.resolve();
|
|
42
|
+
async function withLock(fn) {
|
|
43
|
+
const prev = lock;
|
|
44
|
+
let release;
|
|
45
|
+
lock = new Promise((resolve) => {
|
|
46
|
+
release = resolve;
|
|
47
|
+
});
|
|
48
|
+
await prev;
|
|
49
|
+
try {
|
|
50
|
+
return await fn();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
release?.();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function loadState(baseDir) {
|
|
57
|
+
const { pendingPath, pairedPath } = resolvePaths(baseDir);
|
|
58
|
+
const [pending, paired] = await Promise.all([
|
|
59
|
+
readJSON(pendingPath),
|
|
60
|
+
readJSON(pairedPath),
|
|
61
|
+
]);
|
|
62
|
+
const state = {
|
|
63
|
+
pendingById: pending ?? {},
|
|
64
|
+
pairedByNodeId: paired ?? {},
|
|
65
|
+
};
|
|
66
|
+
pruneExpiredPending(state.pendingById, Date.now());
|
|
67
|
+
return state;
|
|
68
|
+
}
|
|
69
|
+
async function persistState(state, baseDir) {
|
|
70
|
+
const { pendingPath, pairedPath } = resolvePaths(baseDir);
|
|
71
|
+
await Promise.all([
|
|
72
|
+
writeJSONAtomic(pendingPath, state.pendingById),
|
|
73
|
+
writeJSONAtomic(pairedPath, state.pairedByNodeId),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
function normalizeNodeId(nodeId) {
|
|
77
|
+
return nodeId.trim();
|
|
78
|
+
}
|
|
79
|
+
function newToken() {
|
|
80
|
+
return randomUUID().replaceAll("-", "");
|
|
81
|
+
}
|
|
82
|
+
export async function listNodePairing(baseDir) {
|
|
83
|
+
const state = await loadState(baseDir);
|
|
84
|
+
const pending = Object.values(state.pendingById).sort((a, b) => b.ts - a.ts);
|
|
85
|
+
const paired = Object.values(state.pairedByNodeId).sort((a, b) => b.approvedAtMs - a.approvedAtMs);
|
|
86
|
+
return { pending, paired };
|
|
87
|
+
}
|
|
88
|
+
export async function getPairedNode(nodeId, baseDir) {
|
|
89
|
+
const state = await loadState(baseDir);
|
|
90
|
+
return state.pairedByNodeId[normalizeNodeId(nodeId)] ?? null;
|
|
91
|
+
}
|
|
92
|
+
export async function requestNodePairing(req, baseDir) {
|
|
93
|
+
return await withLock(async () => {
|
|
94
|
+
const state = await loadState(baseDir);
|
|
95
|
+
const nodeId = normalizeNodeId(req.nodeId);
|
|
96
|
+
if (!nodeId) {
|
|
97
|
+
throw new Error("nodeId required");
|
|
98
|
+
}
|
|
99
|
+
const existing = Object.values(state.pendingById).find((p) => p.nodeId === nodeId);
|
|
100
|
+
if (existing) {
|
|
101
|
+
return { status: "pending", request: existing, created: false };
|
|
102
|
+
}
|
|
103
|
+
const isRepair = Boolean(state.pairedByNodeId[nodeId]);
|
|
104
|
+
const request = {
|
|
105
|
+
requestId: randomUUID(),
|
|
106
|
+
nodeId,
|
|
107
|
+
displayName: req.displayName,
|
|
108
|
+
platform: req.platform,
|
|
109
|
+
version: req.version,
|
|
110
|
+
deviceFamily: req.deviceFamily,
|
|
111
|
+
modelIdentifier: req.modelIdentifier,
|
|
112
|
+
caps: req.caps,
|
|
113
|
+
commands: req.commands,
|
|
114
|
+
permissions: req.permissions,
|
|
115
|
+
remoteIp: req.remoteIp,
|
|
116
|
+
silent: req.silent,
|
|
117
|
+
isRepair,
|
|
118
|
+
ts: Date.now(),
|
|
119
|
+
};
|
|
120
|
+
state.pendingById[request.requestId] = request;
|
|
121
|
+
await persistState(state, baseDir);
|
|
122
|
+
return { status: "pending", request, created: true };
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
export async function approveNodePairing(requestId, baseDir) {
|
|
126
|
+
return await withLock(async () => {
|
|
127
|
+
const state = await loadState(baseDir);
|
|
128
|
+
const pending = state.pendingById[requestId];
|
|
129
|
+
if (!pending)
|
|
130
|
+
return null;
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
const existing = state.pairedByNodeId[pending.nodeId];
|
|
133
|
+
const node = {
|
|
134
|
+
nodeId: pending.nodeId,
|
|
135
|
+
token: newToken(),
|
|
136
|
+
displayName: pending.displayName,
|
|
137
|
+
platform: pending.platform,
|
|
138
|
+
version: pending.version,
|
|
139
|
+
deviceFamily: pending.deviceFamily,
|
|
140
|
+
modelIdentifier: pending.modelIdentifier,
|
|
141
|
+
caps: pending.caps,
|
|
142
|
+
commands: pending.commands,
|
|
143
|
+
permissions: pending.permissions,
|
|
144
|
+
remoteIp: pending.remoteIp,
|
|
145
|
+
createdAtMs: existing?.createdAtMs ?? now,
|
|
146
|
+
approvedAtMs: now,
|
|
147
|
+
};
|
|
148
|
+
delete state.pendingById[requestId];
|
|
149
|
+
state.pairedByNodeId[pending.nodeId] = node;
|
|
150
|
+
await persistState(state, baseDir);
|
|
151
|
+
return { requestId, node };
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
export async function rejectNodePairing(requestId, baseDir) {
|
|
155
|
+
return await withLock(async () => {
|
|
156
|
+
const state = await loadState(baseDir);
|
|
157
|
+
const pending = state.pendingById[requestId];
|
|
158
|
+
if (!pending)
|
|
159
|
+
return null;
|
|
160
|
+
delete state.pendingById[requestId];
|
|
161
|
+
await persistState(state, baseDir);
|
|
162
|
+
return { requestId, nodeId: pending.nodeId };
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
export async function verifyNodeToken(nodeId, token, baseDir) {
|
|
166
|
+
const state = await loadState(baseDir);
|
|
167
|
+
const normalized = normalizeNodeId(nodeId);
|
|
168
|
+
const node = state.pairedByNodeId[normalized];
|
|
169
|
+
if (!node)
|
|
170
|
+
return { ok: false };
|
|
171
|
+
return node.token === token ? { ok: true, node } : { ok: false };
|
|
172
|
+
}
|
|
173
|
+
export async function updatePairedNodeMetadata(nodeId, patch, baseDir) {
|
|
174
|
+
await withLock(async () => {
|
|
175
|
+
const state = await loadState(baseDir);
|
|
176
|
+
const normalized = normalizeNodeId(nodeId);
|
|
177
|
+
const existing = state.pairedByNodeId[normalized];
|
|
178
|
+
if (!existing)
|
|
179
|
+
return;
|
|
180
|
+
const next = {
|
|
181
|
+
...existing,
|
|
182
|
+
displayName: patch.displayName ?? existing.displayName,
|
|
183
|
+
platform: patch.platform ?? existing.platform,
|
|
184
|
+
version: patch.version ?? existing.version,
|
|
185
|
+
deviceFamily: patch.deviceFamily ?? existing.deviceFamily,
|
|
186
|
+
modelIdentifier: patch.modelIdentifier ?? existing.modelIdentifier,
|
|
187
|
+
remoteIp: patch.remoteIp ?? existing.remoteIp,
|
|
188
|
+
caps: patch.caps ?? existing.caps,
|
|
189
|
+
commands: patch.commands ?? existing.commands,
|
|
190
|
+
permissions: patch.permissions ?? existing.permissions,
|
|
191
|
+
};
|
|
192
|
+
state.pairedByNodeId[normalized] = next;
|
|
193
|
+
await persistState(state, baseDir);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
export async function renamePairedNode(nodeId, displayName, baseDir) {
|
|
197
|
+
return await withLock(async () => {
|
|
198
|
+
const state = await loadState(baseDir);
|
|
199
|
+
const normalized = normalizeNodeId(nodeId);
|
|
200
|
+
const existing = state.pairedByNodeId[normalized];
|
|
201
|
+
if (!existing)
|
|
202
|
+
return null;
|
|
203
|
+
const trimmed = displayName.trim();
|
|
204
|
+
if (!trimmed)
|
|
205
|
+
throw new Error("displayName required");
|
|
206
|
+
const next = { ...existing, displayName: trimmed };
|
|
207
|
+
state.pairedByNodeId[normalized] = next;
|
|
208
|
+
await persistState(state, baseDir);
|
|
209
|
+
return next;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
let pamAuth;
|
|
2
|
+
let pamError = null;
|
|
3
|
+
async function loadPam() {
|
|
4
|
+
if (pamAuth !== undefined)
|
|
5
|
+
return;
|
|
6
|
+
try {
|
|
7
|
+
// Vite/Vitest: avoid static analysis/bundling for optional native deps.
|
|
8
|
+
const pkgName = "authenticate-pam";
|
|
9
|
+
const mod = (await import(pkgName));
|
|
10
|
+
const candidate = typeof mod === "function"
|
|
11
|
+
? mod
|
|
12
|
+
: typeof mod.authenticate ===
|
|
13
|
+
"function"
|
|
14
|
+
? mod.authenticate
|
|
15
|
+
: typeof mod.default === "function"
|
|
16
|
+
? mod.default
|
|
17
|
+
: null;
|
|
18
|
+
if (!candidate) {
|
|
19
|
+
throw new Error("authenticate-pam did not export an authenticate function");
|
|
20
|
+
}
|
|
21
|
+
pamAuth = candidate;
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
pamAuth = null;
|
|
25
|
+
pamError = err instanceof Error ? err.message : String(err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function getPamAvailability() {
|
|
29
|
+
await loadPam();
|
|
30
|
+
return pamAuth
|
|
31
|
+
? { available: true }
|
|
32
|
+
: { available: false, error: pamError ?? undefined };
|
|
33
|
+
}
|
|
34
|
+
export async function verifyPamCredentials(username, password) {
|
|
35
|
+
await loadPam();
|
|
36
|
+
const auth = pamAuth;
|
|
37
|
+
if (!auth)
|
|
38
|
+
return false;
|
|
39
|
+
return await new Promise((resolve) => {
|
|
40
|
+
auth(username, password, (err) => resolve(!err));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function isExecutable(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function isDirectory(dirPath) {
|
|
14
|
+
try {
|
|
15
|
+
return fs.statSync(dirPath).isDirectory();
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function mergePath(params) {
|
|
22
|
+
const partsExisting = params.existing
|
|
23
|
+
.split(path.delimiter)
|
|
24
|
+
.map((part) => part.trim())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
const partsPrepend = params.prepend
|
|
27
|
+
.map((part) => part.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
const merged = [];
|
|
31
|
+
for (const part of [...partsPrepend, ...partsExisting]) {
|
|
32
|
+
if (!seen.has(part)) {
|
|
33
|
+
seen.add(part);
|
|
34
|
+
merged.push(part);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return merged.join(path.delimiter);
|
|
38
|
+
}
|
|
39
|
+
function candidateBinDirs(opts) {
|
|
40
|
+
const execPath = opts.execPath ?? process.execPath;
|
|
41
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
42
|
+
const homeDir = opts.homeDir ?? os.homedir();
|
|
43
|
+
const platform = opts.platform ?? process.platform;
|
|
44
|
+
const candidates = [];
|
|
45
|
+
// Bun bundled (macOS app): `clawdbot` lives in the Relay dir (process.execPath).
|
|
46
|
+
try {
|
|
47
|
+
const execDir = path.dirname(execPath);
|
|
48
|
+
const siblingClawdbot = path.join(execDir, "clawdbot");
|
|
49
|
+
if (isExecutable(siblingClawdbot))
|
|
50
|
+
candidates.push(execDir);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// ignore
|
|
54
|
+
}
|
|
55
|
+
// Project-local installs (best effort): if a `node_modules/.bin/clawdbot` exists near cwd,
|
|
56
|
+
// include it. This helps when running under launchd or other minimal PATH environments.
|
|
57
|
+
const localBinDir = path.join(cwd, "node_modules", ".bin");
|
|
58
|
+
if (isExecutable(path.join(localBinDir, "clawdbot")))
|
|
59
|
+
candidates.push(localBinDir);
|
|
60
|
+
const miseDataDir = process.env.MISE_DATA_DIR ?? path.join(homeDir, ".local", "share", "mise");
|
|
61
|
+
const miseShims = path.join(miseDataDir, "shims");
|
|
62
|
+
if (isDirectory(miseShims))
|
|
63
|
+
candidates.push(miseShims);
|
|
64
|
+
// Common global install locations (macOS first).
|
|
65
|
+
if (platform === "darwin") {
|
|
66
|
+
candidates.push(path.join(homeDir, "Library", "pnpm"));
|
|
67
|
+
}
|
|
68
|
+
if (process.env.XDG_BIN_HOME)
|
|
69
|
+
candidates.push(process.env.XDG_BIN_HOME);
|
|
70
|
+
candidates.push(path.join(homeDir, ".local", "bin"));
|
|
71
|
+
candidates.push(path.join(homeDir, ".local", "share", "pnpm"));
|
|
72
|
+
candidates.push(path.join(homeDir, ".bun", "bin"));
|
|
73
|
+
candidates.push(path.join(homeDir, ".yarn", "bin"));
|
|
74
|
+
candidates.push("/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin");
|
|
75
|
+
return candidates.filter(isDirectory);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort PATH bootstrap so skills that require the `clawdbot` CLI can run
|
|
79
|
+
* under launchd/minimal environments (and inside the macOS bun bundle).
|
|
80
|
+
*/
|
|
81
|
+
export function ensureClawdbotCliOnPath(opts = {}) {
|
|
82
|
+
if (process.env.CLAWDBOT_PATH_BOOTSTRAPPED === "1")
|
|
83
|
+
return;
|
|
84
|
+
process.env.CLAWDBOT_PATH_BOOTSTRAPPED = "1";
|
|
85
|
+
const existing = opts.pathEnv ?? process.env.PATH ?? "";
|
|
86
|
+
const prepend = candidateBinDirs(opts);
|
|
87
|
+
if (prepend.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
const merged = mergePath({ existing, prepend });
|
|
90
|
+
if (merged)
|
|
91
|
+
process.env.PATH = merged;
|
|
92
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import { danger, info, logVerbose, shouldLogVerbose, warn, } from "../globals.js";
|
|
3
|
+
import { logDebug } from "../logger.js";
|
|
4
|
+
import { runExec } from "../process/exec.js";
|
|
5
|
+
import { defaultRuntime } from "../runtime.js";
|
|
6
|
+
class PortInUseError extends Error {
|
|
7
|
+
port;
|
|
8
|
+
details;
|
|
9
|
+
constructor(port, details) {
|
|
10
|
+
super(`Port ${port} is already in use.`);
|
|
11
|
+
this.name = "PortInUseError";
|
|
12
|
+
this.port = port;
|
|
13
|
+
this.details = details;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function isErrno(err) {
|
|
17
|
+
return Boolean(err && typeof err === "object" && "code" in err);
|
|
18
|
+
}
|
|
19
|
+
export async function describePortOwner(port) {
|
|
20
|
+
// Best-effort process info for a listening port (macOS/Linux).
|
|
21
|
+
try {
|
|
22
|
+
const { stdout } = await runExec("lsof", [
|
|
23
|
+
"-i",
|
|
24
|
+
`tcp:${port}`,
|
|
25
|
+
"-sTCP:LISTEN",
|
|
26
|
+
"-nP",
|
|
27
|
+
]);
|
|
28
|
+
const trimmed = stdout.trim();
|
|
29
|
+
if (trimmed)
|
|
30
|
+
return trimmed;
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
logVerbose(`lsof unavailable: ${String(err)}`);
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
export async function ensurePortAvailable(port) {
|
|
38
|
+
// Detect EADDRINUSE early with a friendly message.
|
|
39
|
+
try {
|
|
40
|
+
await new Promise((resolve, reject) => {
|
|
41
|
+
const tester = net
|
|
42
|
+
.createServer()
|
|
43
|
+
.once("error", (err) => reject(err))
|
|
44
|
+
.once("listening", () => {
|
|
45
|
+
tester.close(() => resolve());
|
|
46
|
+
})
|
|
47
|
+
.listen(port);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
if (isErrno(err) && err.code === "EADDRINUSE") {
|
|
52
|
+
const details = await describePortOwner(port);
|
|
53
|
+
throw new PortInUseError(port, details);
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function handlePortError(err, port, context, runtime = defaultRuntime) {
|
|
59
|
+
// Uniform messaging for EADDRINUSE with optional owner details.
|
|
60
|
+
if (err instanceof PortInUseError ||
|
|
61
|
+
(isErrno(err) && err.code === "EADDRINUSE")) {
|
|
62
|
+
const details = err instanceof PortInUseError
|
|
63
|
+
? err.details
|
|
64
|
+
: await describePortOwner(port);
|
|
65
|
+
runtime.error(danger(`${context} failed: port ${port} is already in use.`));
|
|
66
|
+
if (details) {
|
|
67
|
+
runtime.error(info("Port listener details:"));
|
|
68
|
+
runtime.error(details);
|
|
69
|
+
if (/clawdbot|src\/index\.ts|dist\/index\.js/.test(details)) {
|
|
70
|
+
runtime.error(warn("It looks like another clawdbot instance is already running. Stop it or pick a different port."));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
runtime.error(info("Resolve by stopping the process using the port or passing --port <free-port>."));
|
|
74
|
+
runtime.exit(1);
|
|
75
|
+
}
|
|
76
|
+
runtime.error(danger(`${context} failed: ${String(err)}`));
|
|
77
|
+
if (shouldLogVerbose()) {
|
|
78
|
+
const stdout = err?.stdout;
|
|
79
|
+
const stderr = err?.stderr;
|
|
80
|
+
if (stdout?.trim())
|
|
81
|
+
logDebug(`stdout: ${stdout.trim()}`);
|
|
82
|
+
if (stderr?.trim())
|
|
83
|
+
logDebug(`stderr: ${stderr.trim()}`);
|
|
84
|
+
}
|
|
85
|
+
return runtime.exit(1);
|
|
86
|
+
}
|
|
87
|
+
export { PortInUseError };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { loadConfig } from "../config/config.js";
|
|
3
|
+
import { resolveTelegramToken } from "../telegram/token.js";
|
|
4
|
+
import { normalizeE164 } from "../utils.js";
|
|
5
|
+
import { getWebAuthAgeMs, readWebSelfId, webAuthExists, } from "../web/session.js";
|
|
6
|
+
export async function buildProviderSummary(cfg) {
|
|
7
|
+
const effective = cfg ?? loadConfig();
|
|
8
|
+
const lines = [];
|
|
9
|
+
const webEnabled = effective.web?.enabled !== false;
|
|
10
|
+
if (!webEnabled) {
|
|
11
|
+
lines.push(chalk.cyan("WhatsApp: disabled"));
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const webLinked = await webAuthExists();
|
|
15
|
+
const authAgeMs = getWebAuthAgeMs();
|
|
16
|
+
const authAge = authAgeMs === null ? "unknown" : formatAge(authAgeMs);
|
|
17
|
+
const { e164 } = readWebSelfId();
|
|
18
|
+
lines.push(webLinked
|
|
19
|
+
? chalk.green(`WhatsApp: linked${e164 ? ` as ${e164}` : ""} (auth ${authAge})`)
|
|
20
|
+
: chalk.red("WhatsApp: not linked"));
|
|
21
|
+
}
|
|
22
|
+
const telegramEnabled = effective.telegram?.enabled !== false;
|
|
23
|
+
if (!telegramEnabled) {
|
|
24
|
+
lines.push(chalk.cyan("Telegram: disabled"));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const { token: telegramToken } = resolveTelegramToken(effective);
|
|
28
|
+
const telegramConfigured = Boolean(telegramToken?.trim());
|
|
29
|
+
lines.push(telegramConfigured
|
|
30
|
+
? chalk.green("Telegram: configured")
|
|
31
|
+
: chalk.cyan("Telegram: not configured"));
|
|
32
|
+
}
|
|
33
|
+
const signalEnabled = effective.signal?.enabled !== false;
|
|
34
|
+
if (!signalEnabled) {
|
|
35
|
+
lines.push(chalk.cyan("Signal: disabled"));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const signalConfigured = Boolean(effective.signal) &&
|
|
39
|
+
Boolean(effective.signal?.account?.trim() ||
|
|
40
|
+
effective.signal?.httpUrl?.trim() ||
|
|
41
|
+
effective.signal?.cliPath?.trim() ||
|
|
42
|
+
effective.signal?.httpHost?.trim() ||
|
|
43
|
+
typeof effective.signal?.httpPort === "number" ||
|
|
44
|
+
typeof effective.signal?.autoStart === "boolean");
|
|
45
|
+
lines.push(signalConfigured
|
|
46
|
+
? chalk.green("Signal: configured")
|
|
47
|
+
: chalk.cyan("Signal: not configured"));
|
|
48
|
+
}
|
|
49
|
+
const imessageEnabled = effective.imessage?.enabled !== false;
|
|
50
|
+
if (!imessageEnabled) {
|
|
51
|
+
lines.push(chalk.cyan("iMessage: disabled"));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const imessageConfigured = Boolean(effective.imessage);
|
|
55
|
+
lines.push(imessageConfigured
|
|
56
|
+
? chalk.green("iMessage: configured")
|
|
57
|
+
: chalk.cyan("iMessage: not configured"));
|
|
58
|
+
}
|
|
59
|
+
const allowFrom = effective.whatsapp?.allowFrom?.length
|
|
60
|
+
? effective.whatsapp.allowFrom.map(normalizeE164).filter(Boolean)
|
|
61
|
+
: [];
|
|
62
|
+
if (allowFrom.length) {
|
|
63
|
+
lines.push(chalk.cyan(`AllowFrom: ${allowFrom.join(", ")}`));
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
export function formatAge(ms) {
|
|
68
|
+
if (ms < 0)
|
|
69
|
+
return "unknown";
|
|
70
|
+
const minutes = Math.round(ms / 60_000);
|
|
71
|
+
if (minutes < 1)
|
|
72
|
+
return "just now";
|
|
73
|
+
if (minutes < 60)
|
|
74
|
+
return `${minutes}m ago`;
|
|
75
|
+
const hours = Math.round(minutes / 60);
|
|
76
|
+
if (hours < 48)
|
|
77
|
+
return `${hours}h ago`;
|
|
78
|
+
const days = Math.round(hours / 24);
|
|
79
|
+
return `${days}d ago`;
|
|
80
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
const DEFAULT_LAUNCHD_LABEL = "com.clawdbot.mac";
|
|
3
|
+
const DEFAULT_SYSTEMD_UNIT = "clawdbot-gateway.service";
|
|
4
|
+
export function triggerClawdbotRestart() {
|
|
5
|
+
if (process.platform !== "darwin") {
|
|
6
|
+
if (process.platform === "linux") {
|
|
7
|
+
const unit = process.env.CLAWDBOT_SYSTEMD_UNIT || DEFAULT_SYSTEMD_UNIT;
|
|
8
|
+
const userRestart = spawnSync("systemctl", ["--user", "restart", unit], {
|
|
9
|
+
stdio: "ignore",
|
|
10
|
+
});
|
|
11
|
+
if (!userRestart.error && userRestart.status === 0) {
|
|
12
|
+
return "systemd";
|
|
13
|
+
}
|
|
14
|
+
const systemRestart = spawnSync("systemctl", ["restart", unit], {
|
|
15
|
+
stdio: "ignore",
|
|
16
|
+
});
|
|
17
|
+
if (!systemRestart.error && systemRestart.status === 0) {
|
|
18
|
+
return "systemd";
|
|
19
|
+
}
|
|
20
|
+
return "systemd";
|
|
21
|
+
}
|
|
22
|
+
return "supervisor";
|
|
23
|
+
}
|
|
24
|
+
const label = process.env.CLAWDBOT_LAUNCHD_LABEL || DEFAULT_LAUNCHD_LABEL;
|
|
25
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
26
|
+
const target = uid !== undefined ? `gui/${uid}/${label}` : label;
|
|
27
|
+
spawnSync("launchctl", ["kickstart", "-k", target], { stdio: "ignore" });
|
|
28
|
+
return "launchctl";
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export async function retryAsync(fn, attempts = 3, initialDelayMs = 300) {
|
|
2
|
+
let lastErr;
|
|
3
|
+
for (let i = 0; i < attempts; i += 1) {
|
|
4
|
+
try {
|
|
5
|
+
return await fn();
|
|
6
|
+
}
|
|
7
|
+
catch (err) {
|
|
8
|
+
lastErr = err;
|
|
9
|
+
if (i === attempts - 1)
|
|
10
|
+
break;
|
|
11
|
+
const delay = initialDelayMs * 2 ** i;
|
|
12
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
throw lastErr;
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { defaultRuntime } from "../runtime.js";
|
|
3
|
+
const MIN_NODE = { major: 22, minor: 0, patch: 0 };
|
|
4
|
+
const SEMVER_RE = /(\d+)\.(\d+)\.(\d+)/;
|
|
5
|
+
export function parseSemver(version) {
|
|
6
|
+
if (!version)
|
|
7
|
+
return null;
|
|
8
|
+
const match = version.match(SEMVER_RE);
|
|
9
|
+
if (!match)
|
|
10
|
+
return null;
|
|
11
|
+
const [, major, minor, patch] = match;
|
|
12
|
+
return {
|
|
13
|
+
major: Number.parseInt(major, 10),
|
|
14
|
+
minor: Number.parseInt(minor, 10),
|
|
15
|
+
patch: Number.parseInt(patch, 10),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function isAtLeast(version, minimum) {
|
|
19
|
+
if (!version)
|
|
20
|
+
return false;
|
|
21
|
+
if (version.major !== minimum.major)
|
|
22
|
+
return version.major > minimum.major;
|
|
23
|
+
if (version.minor !== minimum.minor)
|
|
24
|
+
return version.minor > minimum.minor;
|
|
25
|
+
return version.patch >= minimum.patch;
|
|
26
|
+
}
|
|
27
|
+
export function detectRuntime() {
|
|
28
|
+
const kind = process.versions?.node ? "node" : "unknown";
|
|
29
|
+
const version = process.versions?.node ?? null;
|
|
30
|
+
return {
|
|
31
|
+
kind,
|
|
32
|
+
version,
|
|
33
|
+
execPath: process.execPath ?? null,
|
|
34
|
+
pathEnv: process.env.PATH ?? "(not set)",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function runtimeSatisfies(details) {
|
|
38
|
+
const parsed = parseSemver(details.version);
|
|
39
|
+
if (details.kind === "node")
|
|
40
|
+
return isAtLeast(parsed, MIN_NODE);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
export function assertSupportedRuntime(runtime = defaultRuntime, details = detectRuntime()) {
|
|
44
|
+
if (runtimeSatisfies(details))
|
|
45
|
+
return;
|
|
46
|
+
const versionLabel = details.version ?? "unknown";
|
|
47
|
+
const runtimeLabel = details.kind === "unknown"
|
|
48
|
+
? "unknown runtime"
|
|
49
|
+
: `${details.kind} ${versionLabel}`;
|
|
50
|
+
const execLabel = details.execPath ?? "unknown";
|
|
51
|
+
runtime.error([
|
|
52
|
+
"clawdbot requires Node >=22.0.0.",
|
|
53
|
+
`Detected: ${runtimeLabel} (exec: ${execLabel}).`,
|
|
54
|
+
`PATH searched: ${details.pathEnv}`,
|
|
55
|
+
"Install Node: https://nodejs.org/en/download",
|
|
56
|
+
"Upgrade Node and re-run clawdbot.",
|
|
57
|
+
].join("\n"));
|
|
58
|
+
runtime.exit(1);
|
|
59
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Lightweight in-memory queue for human-readable system events that should be
|
|
2
|
+
// prefixed to the next main-session prompt/heartbeat. We intentionally avoid
|
|
3
|
+
// persistence to keep events ephemeral.
|
|
4
|
+
const MAX_EVENTS = 20;
|
|
5
|
+
const queue = [];
|
|
6
|
+
let lastText = null;
|
|
7
|
+
let lastContextKey = null;
|
|
8
|
+
function normalizeContextKey(key) {
|
|
9
|
+
if (!key)
|
|
10
|
+
return null;
|
|
11
|
+
const trimmed = key.trim();
|
|
12
|
+
if (!trimmed)
|
|
13
|
+
return null;
|
|
14
|
+
return trimmed.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
export function isSystemEventContextChanged(contextKey) {
|
|
17
|
+
const normalized = normalizeContextKey(contextKey);
|
|
18
|
+
return normalized !== lastContextKey;
|
|
19
|
+
}
|
|
20
|
+
export function enqueueSystemEvent(text, options) {
|
|
21
|
+
const cleaned = text.trim();
|
|
22
|
+
if (!cleaned)
|
|
23
|
+
return;
|
|
24
|
+
lastContextKey = normalizeContextKey(options?.contextKey);
|
|
25
|
+
if (lastText === cleaned)
|
|
26
|
+
return; // skip consecutive duplicates
|
|
27
|
+
lastText = cleaned;
|
|
28
|
+
queue.push({ text: cleaned, ts: Date.now() });
|
|
29
|
+
if (queue.length > MAX_EVENTS)
|
|
30
|
+
queue.shift();
|
|
31
|
+
}
|
|
32
|
+
export function drainSystemEvents() {
|
|
33
|
+
const out = queue.map((e) => e.text);
|
|
34
|
+
queue.length = 0;
|
|
35
|
+
lastText = null;
|
|
36
|
+
lastContextKey = null;
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
export function peekSystemEvents() {
|
|
40
|
+
return queue.map((e) => e.text);
|
|
41
|
+
}
|
|
42
|
+
export function hasSystemEvents() {
|
|
43
|
+
return queue.length > 0;
|
|
44
|
+
}
|