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