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,59 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { DisconnectReason } from "@whiskeysockets/baileys";
|
|
3
|
+
import { danger, info, success } from "../globals.js";
|
|
4
|
+
import { logInfo } from "../logger.js";
|
|
5
|
+
import { defaultRuntime } from "../runtime.js";
|
|
6
|
+
import { createWaSocket, formatError, resolveWebAuthDir, waitForWaConnection, } from "./session.js";
|
|
7
|
+
export async function loginWeb(verbose, provider = "whatsapp", waitForConnection = waitForWaConnection, runtime = defaultRuntime) {
|
|
8
|
+
if (provider !== "whatsapp" && provider !== "web") {
|
|
9
|
+
throw new Error(`Unsupported provider: ${provider}`);
|
|
10
|
+
}
|
|
11
|
+
const sock = await createWaSocket(true, verbose);
|
|
12
|
+
logInfo("Waiting for WhatsApp connection...", runtime);
|
|
13
|
+
try {
|
|
14
|
+
await waitForConnection(sock);
|
|
15
|
+
console.log(success("✅ Linked! Credentials saved for future sends."));
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
const code = err?.error?.output
|
|
19
|
+
?.statusCode ??
|
|
20
|
+
err?.output?.statusCode;
|
|
21
|
+
if (code === 515) {
|
|
22
|
+
console.log(info("WhatsApp asked for a restart after pairing (code 515); creds are saved. Restarting connection once…"));
|
|
23
|
+
try {
|
|
24
|
+
sock.ws?.close();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// ignore
|
|
28
|
+
}
|
|
29
|
+
const retry = await createWaSocket(false, verbose);
|
|
30
|
+
try {
|
|
31
|
+
await waitForConnection(retry);
|
|
32
|
+
console.log(success("✅ Linked after restart; web session ready. You can now send with provider=web."));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
setTimeout(() => retry.ws?.close(), 500);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (code === DisconnectReason.loggedOut) {
|
|
40
|
+
await fs.rm(resolveWebAuthDir(), { recursive: true, force: true });
|
|
41
|
+
console.error(danger("WhatsApp reported the session is logged out. Cleared cached web session; please rerun clawdbot login and scan the QR again."));
|
|
42
|
+
throw new Error("Session logged out; cache cleared. Re-run login.");
|
|
43
|
+
}
|
|
44
|
+
const formatted = formatError(err);
|
|
45
|
+
console.error(danger(`WhatsApp Web connection ended before fully opening. ${formatted}`));
|
|
46
|
+
throw new Error(formatted);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
// Let Baileys flush any final events before closing the socket.
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
try {
|
|
52
|
+
sock.ws?.close();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// ignore
|
|
56
|
+
}
|
|
57
|
+
}, 500);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { logVerbose, shouldLogVerbose } from "../globals.js";
|
|
4
|
+
import { maxBytesForKind, mediaKindFromMime, } from "../media/constants.js";
|
|
5
|
+
import { resizeToJpeg } from "../media/image-ops.js";
|
|
6
|
+
import { detectMime, extensionForMime } from "../media/mime.js";
|
|
7
|
+
async function loadWebMediaInternal(mediaUrl, options = {}) {
|
|
8
|
+
const { maxBytes, optimizeImages = true } = options;
|
|
9
|
+
if (mediaUrl.startsWith("file://")) {
|
|
10
|
+
mediaUrl = mediaUrl.replace("file://", "");
|
|
11
|
+
}
|
|
12
|
+
const optimizeAndClampImage = async (buffer, cap) => {
|
|
13
|
+
const originalSize = buffer.length;
|
|
14
|
+
const optimized = await optimizeImageToJpeg(buffer, cap);
|
|
15
|
+
if (optimized.optimizedSize < originalSize && shouldLogVerbose()) {
|
|
16
|
+
logVerbose(`Optimized media from ${(originalSize / (1024 * 1024)).toFixed(2)}MB to ${(optimized.optimizedSize / (1024 * 1024)).toFixed(2)}MB (side≤${optimized.resizeSide}px, q=${optimized.quality})`);
|
|
17
|
+
}
|
|
18
|
+
if (optimized.buffer.length > cap) {
|
|
19
|
+
throw new Error(`Media could not be reduced below ${(cap / (1024 * 1024)).toFixed(0)}MB (got ${(optimized.buffer.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
buffer: optimized.buffer,
|
|
23
|
+
contentType: "image/jpeg",
|
|
24
|
+
kind: "image",
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
if (/^https?:\/\//i.test(mediaUrl)) {
|
|
28
|
+
let fileName;
|
|
29
|
+
try {
|
|
30
|
+
const url = new URL(mediaUrl);
|
|
31
|
+
const base = path.basename(url.pathname);
|
|
32
|
+
fileName = base || undefined;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// ignore parse errors; leave undefined
|
|
36
|
+
}
|
|
37
|
+
const res = await fetch(mediaUrl);
|
|
38
|
+
if (!res.ok || !res.body) {
|
|
39
|
+
throw new Error(`Failed to fetch media: HTTP ${res.status}`);
|
|
40
|
+
}
|
|
41
|
+
const array = Buffer.from(await res.arrayBuffer());
|
|
42
|
+
const contentType = await detectMime({
|
|
43
|
+
buffer: array,
|
|
44
|
+
headerMime: res.headers.get("content-type"),
|
|
45
|
+
filePath: mediaUrl,
|
|
46
|
+
});
|
|
47
|
+
if (fileName && !path.extname(fileName) && contentType) {
|
|
48
|
+
const ext = extensionForMime(contentType);
|
|
49
|
+
if (ext)
|
|
50
|
+
fileName = `${fileName}${ext}`;
|
|
51
|
+
}
|
|
52
|
+
const kind = mediaKindFromMime(contentType);
|
|
53
|
+
const cap = Math.min(maxBytes ?? maxBytesForKind(kind), maxBytesForKind(kind));
|
|
54
|
+
if (kind === "image") {
|
|
55
|
+
// Skip optimization for GIFs to preserve animation.
|
|
56
|
+
if (contentType === "image/gif" || !optimizeImages) {
|
|
57
|
+
if (array.length > cap) {
|
|
58
|
+
throw new Error(`${contentType === "image/gif" ? "GIF" : "Media"} exceeds ${(cap / (1024 * 1024)).toFixed(0)}MB limit (got ${(array.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
59
|
+
}
|
|
60
|
+
return { buffer: array, contentType, kind, fileName };
|
|
61
|
+
}
|
|
62
|
+
return { ...(await optimizeAndClampImage(array, cap)), fileName };
|
|
63
|
+
}
|
|
64
|
+
if (array.length > cap) {
|
|
65
|
+
throw new Error(`Media exceeds ${(cap / (1024 * 1024)).toFixed(0)}MB limit (got ${(array.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
buffer: array,
|
|
69
|
+
contentType: contentType ?? undefined,
|
|
70
|
+
kind,
|
|
71
|
+
fileName,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Local path
|
|
75
|
+
const data = await fs.readFile(mediaUrl);
|
|
76
|
+
const mime = await detectMime({ buffer: data, filePath: mediaUrl });
|
|
77
|
+
const kind = mediaKindFromMime(mime);
|
|
78
|
+
let fileName = path.basename(mediaUrl) || undefined;
|
|
79
|
+
if (fileName && !path.extname(fileName) && mime) {
|
|
80
|
+
const ext = extensionForMime(mime);
|
|
81
|
+
if (ext)
|
|
82
|
+
fileName = `${fileName}${ext}`;
|
|
83
|
+
}
|
|
84
|
+
const cap = Math.min(maxBytes ?? maxBytesForKind(kind), maxBytesForKind(kind));
|
|
85
|
+
if (kind === "image") {
|
|
86
|
+
// Skip optimization for GIFs to preserve animation.
|
|
87
|
+
if (mime === "image/gif" || !optimizeImages) {
|
|
88
|
+
if (data.length > cap) {
|
|
89
|
+
throw new Error(`${mime === "image/gif" ? "GIF" : "Media"} exceeds ${(cap / (1024 * 1024)).toFixed(0)}MB limit (got ${(data.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
90
|
+
}
|
|
91
|
+
return { buffer: data, contentType: mime, kind, fileName };
|
|
92
|
+
}
|
|
93
|
+
return { ...(await optimizeAndClampImage(data, cap)), fileName };
|
|
94
|
+
}
|
|
95
|
+
if (data.length > cap) {
|
|
96
|
+
throw new Error(`Media exceeds ${(cap / (1024 * 1024)).toFixed(0)}MB limit (got ${(data.length / (1024 * 1024)).toFixed(2)}MB)`);
|
|
97
|
+
}
|
|
98
|
+
return { buffer: data, contentType: mime, kind, fileName };
|
|
99
|
+
}
|
|
100
|
+
export async function loadWebMedia(mediaUrl, maxBytes) {
|
|
101
|
+
return await loadWebMediaInternal(mediaUrl, {
|
|
102
|
+
maxBytes,
|
|
103
|
+
optimizeImages: true,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export async function loadWebMediaRaw(mediaUrl, maxBytes) {
|
|
107
|
+
return await loadWebMediaInternal(mediaUrl, {
|
|
108
|
+
maxBytes,
|
|
109
|
+
optimizeImages: false,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export async function optimizeImageToJpeg(buffer, maxBytes) {
|
|
113
|
+
// Try a grid of sizes/qualities until under the limit.
|
|
114
|
+
const sides = [2048, 1536, 1280, 1024, 800];
|
|
115
|
+
const qualities = [80, 70, 60, 50, 40];
|
|
116
|
+
let smallest = null;
|
|
117
|
+
for (const side of sides) {
|
|
118
|
+
for (const quality of qualities) {
|
|
119
|
+
const out = await resizeToJpeg({
|
|
120
|
+
buffer,
|
|
121
|
+
maxSide: side,
|
|
122
|
+
quality,
|
|
123
|
+
withoutEnlargement: true,
|
|
124
|
+
});
|
|
125
|
+
const size = out.length;
|
|
126
|
+
if (!smallest || size < smallest.size) {
|
|
127
|
+
smallest = { buffer: out, size, resizeSide: side, quality };
|
|
128
|
+
}
|
|
129
|
+
if (size <= maxBytes) {
|
|
130
|
+
return {
|
|
131
|
+
buffer: out,
|
|
132
|
+
optimizedSize: size,
|
|
133
|
+
resizeSide: side,
|
|
134
|
+
quality,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (smallest) {
|
|
140
|
+
return {
|
|
141
|
+
buffer: smallest.buffer,
|
|
142
|
+
optimizedSize: smallest.size,
|
|
143
|
+
resizeSide: smallest.resizeSide,
|
|
144
|
+
quality: smallest.quality,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
throw new Error("Failed to optimize image");
|
|
148
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createSubsystemLogger, getChildLogger } from "../logging.js";
|
|
3
|
+
import { toWhatsappJid } from "../utils.js";
|
|
4
|
+
import { getActiveWebListener, } from "./active-listener.js";
|
|
5
|
+
import { loadWebMedia } from "./media.js";
|
|
6
|
+
const outboundLog = createSubsystemLogger("gateway/providers/whatsapp").child("outbound");
|
|
7
|
+
export async function sendMessageWhatsApp(to, body, options) {
|
|
8
|
+
let text = body;
|
|
9
|
+
const correlationId = randomUUID();
|
|
10
|
+
const startedAt = Date.now();
|
|
11
|
+
const active = getActiveWebListener();
|
|
12
|
+
if (!active) {
|
|
13
|
+
throw new Error("No active gateway listener. Start the gateway before sending WhatsApp messages.");
|
|
14
|
+
}
|
|
15
|
+
const logger = getChildLogger({
|
|
16
|
+
module: "web-outbound",
|
|
17
|
+
correlationId,
|
|
18
|
+
to,
|
|
19
|
+
});
|
|
20
|
+
try {
|
|
21
|
+
const jid = toWhatsappJid(to);
|
|
22
|
+
let mediaBuffer;
|
|
23
|
+
let mediaType;
|
|
24
|
+
if (options.mediaUrl) {
|
|
25
|
+
const media = await loadWebMedia(options.mediaUrl);
|
|
26
|
+
const caption = text || undefined;
|
|
27
|
+
mediaBuffer = media.buffer;
|
|
28
|
+
mediaType = media.contentType;
|
|
29
|
+
if (media.kind === "audio") {
|
|
30
|
+
// WhatsApp expects explicit opus codec for PTT voice notes.
|
|
31
|
+
mediaType =
|
|
32
|
+
media.contentType === "audio/ogg"
|
|
33
|
+
? "audio/ogg; codecs=opus"
|
|
34
|
+
: (media.contentType ?? "application/octet-stream");
|
|
35
|
+
}
|
|
36
|
+
else if (media.kind === "video") {
|
|
37
|
+
text = caption ?? "";
|
|
38
|
+
}
|
|
39
|
+
else if (media.kind === "image") {
|
|
40
|
+
text = caption ?? "";
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
text = caption ?? "";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
outboundLog.info(`Sending message -> ${jid}${options.mediaUrl ? " (media)" : ""}`);
|
|
47
|
+
logger.info({ jid, hasMedia: Boolean(options.mediaUrl) }, "sending message");
|
|
48
|
+
if (!active)
|
|
49
|
+
throw new Error("Active web listener missing");
|
|
50
|
+
await active.sendComposingTo(to);
|
|
51
|
+
const sendOptions = options.gifPlayback
|
|
52
|
+
? { gifPlayback: true }
|
|
53
|
+
: undefined;
|
|
54
|
+
const result = sendOptions
|
|
55
|
+
? await active.sendMessage(to, text, mediaBuffer, mediaType, sendOptions)
|
|
56
|
+
: await active.sendMessage(to, text, mediaBuffer, mediaType);
|
|
57
|
+
const messageId = result?.messageId ?? "unknown";
|
|
58
|
+
const durationMs = Date.now() - startedAt;
|
|
59
|
+
outboundLog.info(`Sent message ${messageId} -> ${jid}${options.mediaUrl ? " (media)" : ""} (${durationMs}ms)`);
|
|
60
|
+
logger.info({ jid, messageId }, "sent message");
|
|
61
|
+
return { messageId, toJid: jid };
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger.error({ err: String(err), to, hasMedia: Boolean(options.mediaUrl) }, "failed to send via web session");
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { deflateSync } from "node:zlib";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const QRCode = require("qrcode-terminal/vendor/QRCode");
|
|
5
|
+
const QRErrorCorrectLevel = require("qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel");
|
|
6
|
+
function createQrMatrix(input) {
|
|
7
|
+
const qr = new QRCode(-1, QRErrorCorrectLevel.L);
|
|
8
|
+
qr.addData(input);
|
|
9
|
+
qr.make();
|
|
10
|
+
return qr;
|
|
11
|
+
}
|
|
12
|
+
function fillPixel(buf, x, y, width, r, g, b, a = 255) {
|
|
13
|
+
const idx = (y * width + x) * 4;
|
|
14
|
+
buf[idx] = r;
|
|
15
|
+
buf[idx + 1] = g;
|
|
16
|
+
buf[idx + 2] = b;
|
|
17
|
+
buf[idx + 3] = a;
|
|
18
|
+
}
|
|
19
|
+
function crcTable() {
|
|
20
|
+
const table = new Uint32Array(256);
|
|
21
|
+
for (let i = 0; i < 256; i += 1) {
|
|
22
|
+
let c = i;
|
|
23
|
+
for (let k = 0; k < 8; k += 1) {
|
|
24
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
25
|
+
}
|
|
26
|
+
table[i] = c >>> 0;
|
|
27
|
+
}
|
|
28
|
+
return table;
|
|
29
|
+
}
|
|
30
|
+
const CRC_TABLE = crcTable();
|
|
31
|
+
function crc32(buf) {
|
|
32
|
+
let crc = 0xffffffff;
|
|
33
|
+
for (let i = 0; i < buf.length; i += 1) {
|
|
34
|
+
crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
35
|
+
}
|
|
36
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
37
|
+
}
|
|
38
|
+
function pngChunk(type, data) {
|
|
39
|
+
const typeBuf = Buffer.from(type, "ascii");
|
|
40
|
+
const len = Buffer.alloc(4);
|
|
41
|
+
len.writeUInt32BE(data.length, 0);
|
|
42
|
+
const crc = crc32(Buffer.concat([typeBuf, data]));
|
|
43
|
+
const crcBuf = Buffer.alloc(4);
|
|
44
|
+
crcBuf.writeUInt32BE(crc, 0);
|
|
45
|
+
return Buffer.concat([len, typeBuf, data, crcBuf]);
|
|
46
|
+
}
|
|
47
|
+
function encodePngRgba(buffer, width, height) {
|
|
48
|
+
const stride = width * 4;
|
|
49
|
+
const raw = Buffer.alloc((stride + 1) * height);
|
|
50
|
+
for (let row = 0; row < height; row += 1) {
|
|
51
|
+
const rawOffset = row * (stride + 1);
|
|
52
|
+
raw[rawOffset] = 0; // filter: none
|
|
53
|
+
buffer.copy(raw, rawOffset + 1, row * stride, row * stride + stride);
|
|
54
|
+
}
|
|
55
|
+
const compressed = deflateSync(raw);
|
|
56
|
+
const signature = Buffer.from([
|
|
57
|
+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
|
58
|
+
]);
|
|
59
|
+
const ihdr = Buffer.alloc(13);
|
|
60
|
+
ihdr.writeUInt32BE(width, 0);
|
|
61
|
+
ihdr.writeUInt32BE(height, 4);
|
|
62
|
+
ihdr[8] = 8; // bit depth
|
|
63
|
+
ihdr[9] = 6; // color type RGBA
|
|
64
|
+
ihdr[10] = 0; // compression
|
|
65
|
+
ihdr[11] = 0; // filter
|
|
66
|
+
ihdr[12] = 0; // interlace
|
|
67
|
+
return Buffer.concat([
|
|
68
|
+
signature,
|
|
69
|
+
pngChunk("IHDR", ihdr),
|
|
70
|
+
pngChunk("IDAT", compressed),
|
|
71
|
+
pngChunk("IEND", Buffer.alloc(0)),
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
export async function renderQrPngBase64(input, opts = {}) {
|
|
75
|
+
const { scale = 6, marginModules = 4 } = opts;
|
|
76
|
+
const qr = createQrMatrix(input);
|
|
77
|
+
const modules = qr.getModuleCount();
|
|
78
|
+
const size = (modules + marginModules * 2) * scale;
|
|
79
|
+
const buf = Buffer.alloc(size * size * 4, 255);
|
|
80
|
+
for (let row = 0; row < modules; row += 1) {
|
|
81
|
+
for (let col = 0; col < modules; col += 1) {
|
|
82
|
+
if (!qr.isDark(row, col))
|
|
83
|
+
continue;
|
|
84
|
+
const startX = (col + marginModules) * scale;
|
|
85
|
+
const startY = (row + marginModules) * scale;
|
|
86
|
+
for (let y = 0; y < scale; y += 1) {
|
|
87
|
+
const pixelY = startY + y;
|
|
88
|
+
for (let x = 0; x < scale; x += 1) {
|
|
89
|
+
const pixelX = startX + x;
|
|
90
|
+
fillPixel(buf, pixelX, pixelY, size, 0, 0, 0, 255);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const png = encodePngRgba(buf, size, size);
|
|
96
|
+
return png.toString("base64");
|
|
97
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
export const DEFAULT_HEARTBEAT_SECONDS = 60;
|
|
3
|
+
export const DEFAULT_RECONNECT_POLICY = {
|
|
4
|
+
initialMs: 2_000,
|
|
5
|
+
maxMs: 30_000,
|
|
6
|
+
factor: 1.8,
|
|
7
|
+
jitter: 0.25,
|
|
8
|
+
maxAttempts: 12,
|
|
9
|
+
};
|
|
10
|
+
const clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
|
11
|
+
export function resolveHeartbeatSeconds(cfg, overrideSeconds) {
|
|
12
|
+
const candidate = overrideSeconds ?? cfg.web?.heartbeatSeconds;
|
|
13
|
+
if (typeof candidate === "number" && candidate > 0)
|
|
14
|
+
return candidate;
|
|
15
|
+
return DEFAULT_HEARTBEAT_SECONDS;
|
|
16
|
+
}
|
|
17
|
+
export function resolveReconnectPolicy(cfg, overrides) {
|
|
18
|
+
const reconnectOverrides = cfg.web?.reconnect ?? {};
|
|
19
|
+
const overrideConfig = overrides ?? {};
|
|
20
|
+
const merged = {
|
|
21
|
+
...DEFAULT_RECONNECT_POLICY,
|
|
22
|
+
...reconnectOverrides,
|
|
23
|
+
...overrideConfig,
|
|
24
|
+
};
|
|
25
|
+
merged.initialMs = Math.max(250, merged.initialMs);
|
|
26
|
+
merged.maxMs = Math.max(merged.initialMs, merged.maxMs);
|
|
27
|
+
merged.factor = clamp(merged.factor, 1.1, 10);
|
|
28
|
+
merged.jitter = clamp(merged.jitter, 0, 1);
|
|
29
|
+
merged.maxAttempts = Math.max(0, Math.floor(merged.maxAttempts));
|
|
30
|
+
return merged;
|
|
31
|
+
}
|
|
32
|
+
export function computeBackoff(policy, attempt) {
|
|
33
|
+
const base = policy.initialMs * policy.factor ** Math.max(attempt - 1, 0);
|
|
34
|
+
const jitter = base * policy.jitter * Math.random();
|
|
35
|
+
return Math.min(policy.maxMs, Math.round(base + jitter));
|
|
36
|
+
}
|
|
37
|
+
export function sleepWithAbort(ms, abortSignal) {
|
|
38
|
+
if (ms <= 0)
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const timer = setTimeout(() => {
|
|
42
|
+
cleanup();
|
|
43
|
+
resolve();
|
|
44
|
+
}, ms);
|
|
45
|
+
const onAbort = () => {
|
|
46
|
+
cleanup();
|
|
47
|
+
reject(new Error("aborted"));
|
|
48
|
+
};
|
|
49
|
+
const cleanup = () => {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
52
|
+
};
|
|
53
|
+
if (abortSignal) {
|
|
54
|
+
abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export function newConnectionId() {
|
|
59
|
+
return randomUUID();
|
|
60
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
let handler = null;
|
|
2
|
+
let pendingReason = null;
|
|
3
|
+
let scheduled = false;
|
|
4
|
+
let running = false;
|
|
5
|
+
let timer = null;
|
|
6
|
+
const DEFAULT_COALESCE_MS = 250;
|
|
7
|
+
const DEFAULT_RETRY_MS = 1_000;
|
|
8
|
+
function schedule(coalesceMs) {
|
|
9
|
+
if (timer)
|
|
10
|
+
return;
|
|
11
|
+
timer = setTimeout(async () => {
|
|
12
|
+
timer = null;
|
|
13
|
+
scheduled = false;
|
|
14
|
+
const active = handler;
|
|
15
|
+
if (!active)
|
|
16
|
+
return;
|
|
17
|
+
if (running) {
|
|
18
|
+
scheduled = true;
|
|
19
|
+
schedule(coalesceMs);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const reason = pendingReason;
|
|
23
|
+
pendingReason = null;
|
|
24
|
+
running = true;
|
|
25
|
+
try {
|
|
26
|
+
const res = await active({ reason: reason ?? undefined });
|
|
27
|
+
if (res.status === "skipped" && res.reason === "requests-in-flight") {
|
|
28
|
+
// The main lane is busy; retry soon.
|
|
29
|
+
pendingReason = reason ?? "retry";
|
|
30
|
+
schedule(DEFAULT_RETRY_MS);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
pendingReason = reason ?? "retry";
|
|
35
|
+
schedule(DEFAULT_RETRY_MS);
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
running = false;
|
|
40
|
+
if (pendingReason || scheduled)
|
|
41
|
+
schedule(coalesceMs);
|
|
42
|
+
}
|
|
43
|
+
}, coalesceMs);
|
|
44
|
+
timer.unref?.();
|
|
45
|
+
}
|
|
46
|
+
export function setReplyHeartbeatWakeHandler(next) {
|
|
47
|
+
handler = next;
|
|
48
|
+
if (handler && pendingReason) {
|
|
49
|
+
schedule(DEFAULT_COALESCE_MS);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function requestReplyHeartbeatNow(opts) {
|
|
53
|
+
pendingReason = opts?.reason ?? pendingReason ?? "requested";
|
|
54
|
+
schedule(opts?.coalesceMs ?? DEFAULT_COALESCE_MS);
|
|
55
|
+
}
|
|
56
|
+
export function hasReplyHeartbeatWakeHandler() {
|
|
57
|
+
return handler !== null;
|
|
58
|
+
}
|
|
59
|
+
export function hasPendingReplyHeartbeatWake() {
|
|
60
|
+
return pendingReason !== null || Boolean(timer) || scheduled;
|
|
61
|
+
}
|