@poolzin/pool-bot 2026.2.24 → 2026.2.26
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 +21 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +17 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/bedrock-discovery.js +3 -1
- package/dist/agents/byteplus-models.js +97 -0
- package/dist/agents/chutes-oauth.js +1 -0
- package/dist/agents/cli-runner/helpers.js +4 -0
- package/dist/agents/compaction.js +41 -14
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/doubao-models.js +121 -0
- package/dist/agents/failover-error.js +2 -0
- package/dist/agents/huggingface-models.js +5 -3
- package/dist/agents/live-model-filter.js +5 -0
- package/dist/agents/minimax-vlm.js +10 -8
- package/dist/agents/model-auth.js +6 -0
- package/dist/agents/model-catalog.js +3 -1
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/model-selection.js +7 -1
- package/dist/agents/models-config.providers.js +364 -165
- package/dist/agents/ollama-stream.js +117 -4
- package/dist/agents/opencode-zen-models.js +22 -11
- package/dist/agents/pi-embedded-helpers/errors.js +55 -33
- package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
- package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/compact.js +29 -7
- package/dist/agents/pi-embedded-runner/extensions.js +28 -26
- package/dist/agents/pi-embedded-runner/google.js +20 -8
- package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
- package/dist/agents/pi-embedded-runner/run.js +71 -12
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
- package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
- package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
- package/dist/agents/pi-embedded-runner/thinking.js +42 -0
- package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
- package/dist/agents/pi-embedded-runner/utils.js +7 -10
- package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
- package/dist/agents/pi-embedded-subscribe.js +9 -4
- package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
- package/dist/agents/pi-embedded-utils.js +3 -0
- package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
- package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
- package/dist/agents/pi-settings.js +40 -0
- package/dist/agents/pi-tools.policy.js +2 -1
- package/dist/agents/provider/config-loader.js +1 -1
- package/dist/agents/sandbox/browser.js +170 -33
- package/dist/agents/sandbox/config-hash.js +14 -27
- package/dist/agents/sandbox/config.js +21 -2
- package/dist/agents/sandbox/constants.js +2 -0
- package/dist/agents/sandbox/docker.js +16 -2
- package/dist/agents/sandbox/novnc-auth.js +62 -0
- package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
- package/dist/agents/sandbox/shared.js +10 -6
- package/dist/agents/sandbox-paths.js +24 -11
- package/dist/agents/schema/clean-for-gemini.js +132 -85
- package/dist/agents/session-slug.js +10 -5
- package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
- package/dist/agents/session-tool-result-guard.js +3 -1
- package/dist/agents/session-transcript-repair.js +40 -6
- package/dist/agents/skills/bundled-dir.js +19 -5
- package/dist/agents/skills/env-overrides.js +124 -43
- package/dist/agents/skills/frontmatter.js +6 -6
- package/dist/agents/skills/plugin-skills.js +14 -7
- package/dist/agents/skills/workspace.js +1 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-announce.js +251 -49
- package/dist/agents/subagent-lifecycle-events.js +19 -0
- package/dist/agents/subagent-registry-cleanup.js +31 -0
- package/dist/agents/subagent-registry-completion.js +68 -0
- package/dist/agents/subagent-registry-queries.js +117 -0
- package/dist/agents/subagent-registry-state.js +46 -0
- package/dist/agents/subagent-registry.js +252 -221
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/subagent-registry.store.js +1 -0
- package/dist/agents/subagent-registry.types.js +1 -0
- package/dist/agents/subagent-spawn.js +195 -7
- package/dist/agents/system-prompt.js +22 -6
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/timeout.js +18 -6
- package/dist/agents/tool-call-id.js +1 -1
- package/dist/agents/tool-display-common.js +162 -29
- package/dist/agents/tool-images.js +82 -9
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tool-policy.js +51 -26
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/nodes-utils.js +1 -10
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/tools/sessions-send-helpers.js +12 -6
- package/dist/agents/tools/sessions-spawn-tool.js +8 -2
- package/dist/agents/tools/subagents-tool.js +2 -1
- package/dist/agents/tools/whatsapp-actions.js +10 -2
- package/dist/agents/tools/whatsapp-target-auth.js +18 -0
- package/dist/agents/transcript-policy.js +22 -8
- package/dist/agents/venice-models.js +11 -3
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +19 -21
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/bash-command.js +41 -39
- package/dist/auto-reply/reply/command-gates.js +25 -0
- package/dist/auto-reply/reply/commands-allowlist.js +111 -72
- package/dist/auto-reply/reply/commands-bash.js +6 -5
- package/dist/auto-reply/reply/commands-config.js +30 -28
- package/dist/auto-reply/reply/commands-core.js +2 -1
- package/dist/auto-reply/reply/commands-info.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +65 -14
- package/dist/auto-reply/reply/commands-session.js +237 -82
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/reply/commands-setunset.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
- package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
- package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
- package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
- package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
- package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
- package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
- package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
- package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
- package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
- package/dist/auto-reply/reply/commands-subagents.js +51 -587
- package/dist/auto-reply/reply/commands-tts.js +10 -5
- package/dist/auto-reply/reply/config-value.js +10 -5
- package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
- package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
- package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
- package/dist/auto-reply/reply/followup-runner.js +1 -0
- package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
- package/dist/auto-reply/reply/get-reply-directives.js +17 -28
- package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
- package/dist/auto-reply/reply/get-reply.js +71 -12
- package/dist/auto-reply/reply/model-selection.js +80 -39
- package/dist/auto-reply/reply/queue/enqueue.js +10 -5
- package/dist/auto-reply/reply/queue/state.js +13 -12
- package/dist/auto-reply/reply/reply-payloads.js +67 -36
- package/dist/auto-reply/reply/reply-reference.js +9 -8
- package/dist/auto-reply/reply/route-reply.js +15 -8
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
- package/dist/auto-reply/reply/session.js +22 -6
- package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
- package/dist/auto-reply/reply/subagents-utils.js +56 -30
- package/dist/auto-reply/reply/typing.js +46 -21
- package/dist/auto-reply/send-policy.js +14 -7
- package/dist/auto-reply/status.js +140 -16
- package/dist/auto-reply/templating.js +10 -5
- package/dist/auto-reply/thinking.js +7 -16
- package/dist/auto-reply/tokens.js +21 -5
- package/dist/browser/bridge-server.js +36 -20
- package/dist/browser/cdp.helpers.js +7 -14
- package/dist/browser/cdp.js +35 -15
- package/dist/browser/chrome.profile-decoration.js +7 -4
- package/dist/browser/config.js +30 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +39 -0
- package/dist/browser/paths.js +77 -0
- package/dist/browser/profiles.js +13 -8
- package/dist/browser/pw-ai-module.js +10 -5
- package/dist/browser/pw-session.js +76 -39
- package/dist/browser/pw-tools-core.interactions.js +14 -7
- package/dist/browser/pw-tools-core.state.js +12 -6
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-context.js +7 -0
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +26 -0
- package/dist/channels/allowlists/resolve-utils.js +43 -19
- package/dist/channels/channel-config.js +14 -7
- package/dist/channels/draft-stream-loop.js +7 -0
- package/dist/channels/model-overrides.js +82 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/plugins/normalize/imessage.js +14 -7
- package/dist/channels/plugins/normalize/slack.js +10 -5
- package/dist/channels/plugins/normalize/telegram.js +14 -7
- package/dist/channels/plugins/outbound/discord.js +80 -8
- package/dist/channels/plugins/outbound/signal.js +11 -11
- package/dist/channels/plugins/setup-helpers.js +10 -5
- package/dist/channels/sender-label.js +14 -7
- package/dist/channels/session.js +4 -2
- package/dist/channels/status-reactions.js +297 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +3 -2
- package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
- package/dist/cli/cli-name.js +11 -11
- package/dist/cli/cli-utils.js +13 -3
- package/dist/cli/command-format.js +1 -1
- package/dist/cli/config-cli.js +1 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
- package/dist/cli/daemon-cli/lifecycle.js +64 -2
- package/dist/cli/daemon-cli/restart-health.js +126 -0
- package/dist/cli/daemon-cli/status.gather.js +9 -13
- package/dist/cli/daemon-cli/status.print.js +2 -10
- package/dist/cli/deps.js +27 -22
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/gateway-cli/run-loop.js +23 -5
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +26 -0
- package/dist/cli/outbound-send-deps.js +2 -9
- package/dist/cli/outbound-send-mapping.js +11 -0
- package/dist/cli/pairing-cli.js +40 -14
- package/dist/cli/plugins-cli.js +250 -73
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +214 -136
- package/dist/cli/program/command-tree.js +16 -0
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/program/preaction.js +13 -9
- package/dist/cli/program/register.configure.js +3 -18
- package/dist/cli/program/register.maintenance.js +2 -2
- package/dist/cli/program/register.onboard.js +2 -0
- package/dist/cli/program/register.status-health-sessions.js +16 -17
- package/dist/cli/program/register.subclis.js +93 -52
- package/dist/cli/route.js +12 -8
- package/dist/cli/system-cli.js +36 -46
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/cli/update-cli/shared.js +22 -9
- package/dist/cli/update-cli/update-command.js +89 -14
- package/dist/cli/update-cli/wizard.js +6 -12
- package/dist/commands/agent/run-context.js +18 -5
- package/dist/commands/agent/session-store.js +17 -4
- package/dist/commands/agent.js +185 -89
- package/dist/commands/agents.bindings.js +14 -7
- package/dist/commands/agents.commands.add.js +13 -9
- package/dist/commands/agents.commands.identity.js +12 -6
- package/dist/commands/agents.commands.list.js +11 -6
- package/dist/commands/agents.config.js +8 -10
- package/dist/commands/agents.providers.js +12 -6
- package/dist/commands/auth-choice-options.js +103 -75
- package/dist/commands/auth-choice.apply.byteplus.js +55 -0
- package/dist/commands/auth-choice.apply.js +4 -0
- package/dist/commands/auth-choice.apply.minimax.js +61 -13
- package/dist/commands/auth-choice.apply.openai.js +3 -1
- package/dist/commands/auth-choice.apply.volcengine.js +55 -0
- package/dist/commands/auth-choice.preferred-provider.js +2 -0
- package/dist/commands/channels/remove.js +13 -6
- package/dist/commands/channels/shared.js +4 -14
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/configure.commands.js +14 -0
- package/dist/commands/configure.gateway.js +2 -4
- package/dist/commands/configure.js +1 -1
- package/dist/commands/configure.shared.js +11 -0
- package/dist/commands/daemon-install-helpers.js +2 -2
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/dashboard.js +12 -10
- package/dist/commands/docs.js +14 -8
- package/dist/commands/doctor-config-flow.js +11 -9
- package/dist/commands/doctor-legacy-config.js +281 -0
- package/dist/commands/doctor-state-integrity.js +99 -23
- package/dist/commands/doctor-update.js +12 -9
- package/dist/commands/models/list.list-command.js +7 -5
- package/dist/commands/models/set-image.js +2 -21
- package/dist/commands/node-daemon-install-helpers.js +10 -8
- package/dist/commands/onboard-auth.config-minimax.js +54 -80
- package/dist/commands/onboard-auth.config-opencode.js +2 -18
- package/dist/commands/onboard-auth.credentials.js +90 -13
- package/dist/commands/onboard-auth.js +1 -1
- package/dist/commands/onboard-auth.models.js +6 -5
- package/dist/commands/onboard-hooks.js +1 -1
- package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
- package/dist/commands/onboard-provider-auth-flags.js +14 -0
- package/dist/commands/onboard-remote.js +14 -7
- package/dist/commands/onboard.js +11 -13
- package/dist/commands/sandbox-display.js +6 -5
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/commands/status-all/diagnosis.js +14 -10
- package/dist/commands/status-all/format.js +1 -0
- package/dist/commands/status.gateway-probe.js +1 -16
- package/dist/commands/systemd-linger.js +12 -6
- package/dist/config/agent-limits.js +2 -0
- package/dist/config/commands.js +32 -15
- package/dist/config/config-paths.js +9 -11
- package/dist/config/config.js +1 -1
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +45 -7
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +656 -171
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +54 -4
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.help.js +44 -7
- package/dist/config/schema.js +58 -570
- package/dist/config/schema.labels.js +38 -6
- package/dist/config/sessions/delivery-info.js +10 -3
- package/dist/config/sessions/main-session.js +10 -5
- package/dist/config/sessions/session-file.js +33 -0
- package/dist/config/sessions/session-key.js +10 -5
- package/dist/config/sessions/store.js +1 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +156 -20
- package/dist/config/zod-schema.providers-core.js +78 -4
- package/dist/config/zod-schema.providers.js +6 -1
- package/dist/config/zod-schema.session.js +41 -2
- package/dist/cron/run-log.js +3 -0
- package/dist/cron/schedule.js +21 -10
- package/dist/cron/service/ops.js +35 -21
- package/dist/cron/service/timer.js +116 -16
- package/dist/cron/stagger.js +3 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/api.js +12 -6
- package/dist/discord/draft-chunking.js +22 -0
- package/dist/discord/draft-stream.js +124 -0
- package/dist/discord/monitor/agent-components.js +1 -1
- package/dist/discord/monitor/commands.js +5 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/discord/monitor/gateway-plugin.js +2 -1
- package/dist/discord/monitor/listeners.js +37 -27
- package/dist/discord/monitor/message-handler.js +4 -1
- package/dist/discord/monitor/message-handler.preflight.js +65 -8
- package/dist/discord/monitor/message-handler.process.js +246 -217
- package/dist/discord/monitor/message-utils.js +143 -6
- package/dist/discord/monitor/model-picker-preferences.js +143 -0
- package/dist/discord/monitor/model-picker.js +651 -0
- package/dist/discord/monitor/native-command.js +573 -16
- package/dist/discord/monitor/provider.allowlist.js +223 -0
- package/dist/discord/monitor/provider.js +275 -347
- package/dist/discord/monitor/provider.lifecycle.js +100 -0
- package/dist/discord/monitor/reply-delivery.js +123 -16
- package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
- package/dist/discord/monitor/thread-bindings.js +4 -0
- package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
- package/dist/discord/monitor/thread-bindings.manager.js +423 -0
- package/dist/discord/monitor/thread-bindings.messages.js +55 -0
- package/dist/discord/monitor/thread-bindings.state.js +358 -0
- package/dist/discord/monitor/thread-bindings.types.js +6 -0
- package/dist/discord/resolve-users.js +33 -21
- package/dist/discord/send.channels.js +15 -0
- package/dist/discord/send.js +3 -2
- package/dist/discord/send.outbound.js +82 -26
- package/dist/discord/send.permissions.js +83 -30
- package/dist/discord/send.reactions.js +8 -4
- package/dist/discord/token.js +10 -5
- package/dist/discord/voice/command.js +263 -0
- package/dist/discord/voice/manager.js +531 -0
- package/dist/gateway/auth.js +72 -13
- package/dist/gateway/call.js +152 -83
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +74 -9
- package/dist/gateway/node-invoke-system-run-approval.js +14 -35
- package/dist/gateway/node-registry.js +10 -5
- package/dist/gateway/openai-http.js +1 -0
- package/dist/gateway/openresponses-http.js +121 -110
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +4 -2
- package/dist/gateway/protocol/schema/cron.js +1 -0
- package/dist/gateway/protocol/schema/devices.js +1 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/role-policy.js +17 -0
- package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
- package/dist/gateway/server/ws-connection/message-handler.js +175 -148
- package/dist/gateway/server-chat.js +83 -25
- package/dist/gateway/server-constants.js +10 -9
- package/dist/gateway/server-cron.js +1 -0
- package/dist/gateway/server-http.js +247 -54
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +465 -130
- package/dist/gateway/server-methods/config.js +193 -152
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-methods/sessions.js +64 -8
- package/dist/gateway/server-methods/usage.js +162 -75
- package/dist/gateway/server-node-events.js +29 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +39 -13
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/gateway/tools-invoke-http.js +21 -10
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
- package/dist/hooks/bundled/command-logger/handler.js +7 -2
- package/dist/hooks/bundled/session-memory/handler.js +170 -38
- package/dist/hooks/frontmatter.js +6 -6
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/hooks/gmail-watcher.js +11 -6
- package/dist/hooks/internal-hooks.js +11 -1
- package/dist/hooks/llm-slug-generator.js +4 -1
- package/dist/hooks/workspace.js +47 -17
- package/dist/imessage/accounts.js +9 -20
- package/dist/imessage/monitor/inbound-processing.js +2 -1
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/archive.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +204 -144
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +141 -70
- package/dist/infra/exec-approvals-analysis.js +78 -20
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +277 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/heartbeat-wake.js +6 -12
- package/dist/infra/host-env-security-policy.json +19 -0
- package/dist/infra/host-env-security.js +66 -0
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/net/ssrf.js +131 -38
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/channel-selection.js +12 -6
- package/dist/infra/outbound/envelope.js +1 -1
- package/dist/infra/outbound/format.js +12 -6
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/outbound/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/path-guards.js +25 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/provider-usage.fetch.codex.js +7 -15
- package/dist/infra/provider-usage.fetch.gemini.js +14 -11
- package/dist/infra/provider-usage.fetch.shared.js +30 -1
- package/dist/infra/provider-usage.fetch.zai.js +10 -9
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/session-cost-usage.js +107 -59
- package/dist/infra/session-maintenance-warning.js +3 -1
- package/dist/infra/shell-env.js +98 -34
- package/dist/infra/ssh-config.js +12 -6
- package/dist/infra/system-run-command.js +49 -4
- package/dist/infra/update-channels.js +10 -5
- package/dist/infra/update-startup.js +86 -9
- package/dist/line/accounts.js +5 -7
- package/dist/line/bot-access.js +8 -20
- package/dist/line/bot-handlers.js +3 -1
- package/dist/link-understanding/detect.js +15 -7
- package/dist/media/constants.js +15 -6
- package/dist/media/image-ops.js +7 -0
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/media/local-roots.js +3 -2
- package/dist/media-understanding/apply.js +4 -1
- package/dist/media-understanding/concurrency.js +8 -20
- package/dist/memory/backend-config.js +45 -6
- package/dist/memory/embeddings.js +10 -4
- package/dist/memory/fs-utils.js +23 -0
- package/dist/memory/manager-search.js +12 -6
- package/dist/memory/manager-sync-ops.js +12 -2
- package/dist/memory/qmd-manager.js +466 -53
- package/dist/memory/query-expansion.js +167 -3
- package/dist/memory/status-format.js +10 -5
- package/dist/memory/sync-memory-files.js +1 -1
- package/dist/memory/test-manager.js +8 -0
- package/dist/node-host/invoke-system-run.js +281 -0
- package/dist/node-host/invoke.js +55 -337
- package/dist/pairing/pairing-store.js +22 -0
- package/dist/plugin-sdk/allow-from.js +1 -1
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/index.js +6 -3
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugin-sdk/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/plugins/runtime.js +3 -17
- package/dist/plugins/update.js +78 -12
- package/dist/process/spawn-utils.js +14 -7
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/providers/github-copilot-token.js +11 -6
- package/dist/providers/qwen-portal-oauth.js +14 -6
- package/dist/routing/account-id.js +30 -0
- package/dist/routing/resolve-route.js +3 -7
- package/dist/routing/session-key.js +2 -16
- package/dist/security/audit-channel.js +100 -20
- package/dist/security/audit-extra.async.js +505 -179
- package/dist/security/audit-extra.js +12 -2
- package/dist/security/audit-extra.sync.js +421 -35
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +180 -370
- package/dist/security/dm-policy-shared.js +68 -0
- package/dist/security/external-content.js +46 -14
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +42 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/signal/accounts.js +7 -20
- package/dist/signal/monitor/event-handler.js +3 -1
- package/dist/slack/accounts.js +6 -19
- package/dist/slack/actions.js +11 -3
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/slack/monitor/replies.js +15 -7
- package/dist/slack/monitor/slash.js +22 -13
- package/dist/slack/resolve-channels.js +10 -5
- package/dist/slack/send.js +102 -12
- package/dist/slack/stream-mode.js +10 -0
- package/dist/slack/streaming.js +4 -2
- package/dist/telegram/accounts.js +19 -14
- package/dist/telegram/bot/helpers.js +3 -5
- package/dist/telegram/bot-access.js +35 -36
- package/dist/telegram/bot-handlers.js +120 -148
- package/dist/telegram/bot-message-context.js +68 -9
- package/dist/telegram/bot-message-dispatch.js +477 -210
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +44 -8
- package/dist/telegram/inline-buttons.js +5 -15
- package/dist/telegram/monitor.js +11 -7
- package/dist/telegram/network-config.js +19 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/telegram/send.js +3 -2
- package/dist/telegram/sent-message-cache.js +5 -6
- package/dist/telegram/status-reaction-variants.js +208 -0
- package/dist/telegram/sticker-cache.js +11 -9
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/terminal/theme.js +12 -12
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +50 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +236 -48
- package/dist/utils/account-id.js +2 -4
- package/dist/utils/boolean.js +10 -5
- package/dist/utils/directive-tags.js +11 -0
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/queue-helpers.js +67 -12
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/auto-reply/deliver-reply.js +8 -4
- package/dist/web/auto-reply/mentions.js +10 -5
- package/dist/web/auto-reply/monitor/group-members.js +14 -7
- package/dist/web/auto-reply/monitor/process-message.js +45 -24
- package/dist/web/inbound/access-control.js +5 -2
- package/dist/web/login-qr.js +12 -6
- package/dist/web/media.js +126 -15
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/package.json +1 -1
package/dist/gateway/call.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { loadConfig, resolveConfigPath, resolveGatewayPort, resolveStateDir, } from "../config/config.js";
|
|
3
3
|
import { loadOrCreateDeviceIdentity } from "../infra/device-identity.js";
|
|
4
|
-
import { pickPrimaryTailnetIPv4 } from "../infra/tailnet.js";
|
|
5
4
|
import { loadGatewayTlsRuntime } from "../infra/tls/gateway.js";
|
|
6
5
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, } from "../utils/message-channel.js";
|
|
7
6
|
import { GatewayClient } from "./client.js";
|
|
8
|
-
import {
|
|
7
|
+
import { CLI_DEFAULT_OPERATOR_SCOPES, resolveLeastPrivilegeOperatorScopesForMethod, } from "./method-scopes.js";
|
|
8
|
+
import { isSecureWebSocketUrl } from "./net.js";
|
|
9
9
|
import { PROTOCOL_VERSION } from "./protocol/index.js";
|
|
10
10
|
export function resolveExplicitGatewayAuth(opts) {
|
|
11
11
|
const token = typeof opts?.token === "string" && opts.token.trim().length > 0 ? opts.token.trim() : undefined;
|
|
@@ -37,17 +37,10 @@ export function buildGatewayConnectionDetails(options = {}) {
|
|
|
37
37
|
const remote = isRemoteMode ? config.gateway?.remote : undefined;
|
|
38
38
|
const tlsEnabled = config.gateway?.tls?.enabled === true;
|
|
39
39
|
const localPort = resolveGatewayPort(config);
|
|
40
|
-
const tailnetIPv4 = pickPrimaryTailnetIPv4();
|
|
41
40
|
const bindMode = config.gateway?.bind ?? "loopback";
|
|
42
|
-
const preferTailnet = bindMode === "tailnet" && !!tailnetIPv4;
|
|
43
|
-
const preferLan = bindMode === "lan";
|
|
44
|
-
const lanIPv4 = preferLan ? pickPrimaryLanIPv4() : undefined;
|
|
45
41
|
const scheme = tlsEnabled ? "wss" : "ws";
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
: preferLan && lanIPv4
|
|
49
|
-
? `${scheme}://${lanIPv4}:${localPort}`
|
|
50
|
-
: `${scheme}://127.0.0.1:${localPort}`;
|
|
42
|
+
// Self-connections should always target loopback; bind mode only controls listener exposure.
|
|
43
|
+
const localUrl = `${scheme}://127.0.0.1:${localPort}`;
|
|
51
44
|
const urlOverride = typeof options.url === "string" && options.url.trim().length > 0
|
|
52
45
|
? options.url.trim()
|
|
53
46
|
: undefined;
|
|
@@ -60,15 +53,23 @@ export function buildGatewayConnectionDetails(options = {}) {
|
|
|
60
53
|
? "config gateway.remote.url"
|
|
61
54
|
: remoteMisconfigured
|
|
62
55
|
? "missing gateway.remote.url (fallback local)"
|
|
63
|
-
:
|
|
64
|
-
? `local tailnet ${tailnetIPv4}`
|
|
65
|
-
: preferLan && lanIPv4
|
|
66
|
-
? `local lan ${lanIPv4}`
|
|
67
|
-
: "local loopback";
|
|
56
|
+
: "local loopback";
|
|
68
57
|
const remoteFallbackNote = remoteMisconfigured
|
|
69
58
|
? "Warn: gateway.mode=remote but gateway.remote.url is missing; set gateway.remote.url or switch gateway.mode=local."
|
|
70
59
|
: undefined;
|
|
71
60
|
const bindDetail = !urlOverride && !remoteUrl ? `Bind: ${bindMode}` : undefined;
|
|
61
|
+
// Security check: block ALL insecure ws:// to non-loopback addresses (CWE-319, CVSS 9.8)
|
|
62
|
+
// This applies to the FINAL resolved URL, regardless of source (config, CLI override, etc).
|
|
63
|
+
// Both credentials and chat/conversation data must not be transmitted over plaintext to remote hosts.
|
|
64
|
+
if (!isSecureWebSocketUrl(url)) {
|
|
65
|
+
throw new Error([
|
|
66
|
+
`SECURITY ERROR: Gateway URL "${url}" uses plaintext ws:// to a non-loopback address.`,
|
|
67
|
+
"Both credentials and chat data would be exposed to network interception.",
|
|
68
|
+
`Source: ${urlSource}`,
|
|
69
|
+
`Config: ${configPath}`,
|
|
70
|
+
"Fix: Use wss:// for the gateway URL, or connect via SSH tunnel to localhost.",
|
|
71
|
+
].join("\n"));
|
|
72
|
+
}
|
|
72
73
|
const message = [
|
|
73
74
|
`Gateway target: ${url}`,
|
|
74
75
|
`Source: ${urlSource}`,
|
|
@@ -86,77 +87,89 @@ export function buildGatewayConnectionDetails(options = {}) {
|
|
|
86
87
|
message,
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
function trimToUndefined(value) {
|
|
91
|
+
if (typeof value !== "string") {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const trimmed = value.trim();
|
|
95
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
96
|
+
}
|
|
97
|
+
function resolveGatewayCallTimeout(timeoutValue) {
|
|
98
|
+
const timeoutMs = typeof timeoutValue === "number" && Number.isFinite(timeoutValue) ? timeoutValue : 10_000;
|
|
91
99
|
const safeTimerTimeoutMs = Math.max(1, Math.min(Math.floor(timeoutMs), 2_147_483_647));
|
|
100
|
+
return { timeoutMs, safeTimerTimeoutMs };
|
|
101
|
+
}
|
|
102
|
+
function resolveGatewayCallContext(opts) {
|
|
92
103
|
const config = opts.config ?? loadConfig();
|
|
104
|
+
const configPath = opts.configPath ?? resolveConfigPath(process.env, resolveStateDir(process.env));
|
|
93
105
|
const isRemoteMode = config.gateway?.mode === "remote";
|
|
94
|
-
const remote = isRemoteMode
|
|
95
|
-
|
|
106
|
+
const remote = isRemoteMode
|
|
107
|
+
? config.gateway?.remote
|
|
108
|
+
: undefined;
|
|
109
|
+
const urlOverride = trimToUndefined(opts.url);
|
|
110
|
+
const remoteUrl = trimToUndefined(remote?.url);
|
|
96
111
|
const explicitAuth = resolveExplicitGatewayAuth({ token: opts.token, password: opts.password });
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
const remoteUrl = typeof remote?.url === "string" && remote.url.trim().length > 0 ? remote.url.trim() : undefined;
|
|
104
|
-
if (isRemoteMode && !urlOverride && !remoteUrl) {
|
|
105
|
-
const configPath = opts.configPath ?? resolveConfigPath(process.env, resolveStateDir(process.env));
|
|
106
|
-
throw new Error([
|
|
107
|
-
"gateway remote mode misconfigured: gateway.remote.url missing",
|
|
108
|
-
`Config: ${configPath}`,
|
|
109
|
-
"Fix: set gateway.remote.url, or set gateway.mode=local.",
|
|
110
|
-
].join("\n"));
|
|
112
|
+
return { config, configPath, isRemoteMode, remote, urlOverride, remoteUrl, explicitAuth };
|
|
113
|
+
}
|
|
114
|
+
function ensureRemoteModeUrlConfigured(context) {
|
|
115
|
+
if (!context.isRemoteMode || context.urlOverride || context.remoteUrl) {
|
|
116
|
+
return;
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
(tlsRuntime?.enabled ? tlsRuntime.fingerprintSha256 : undefined);
|
|
129
|
-
const token = explicitAuth.token ||
|
|
130
|
-
(!urlOverride
|
|
131
|
-
? isRemoteMode
|
|
132
|
-
? typeof remote?.token === "string" && remote.token.trim().length > 0
|
|
133
|
-
? remote.token.trim()
|
|
134
|
-
: undefined
|
|
135
|
-
: process.env.POOLBOT_GATEWAY_TOKEN?.trim() ||
|
|
136
|
-
process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() ||
|
|
137
|
-
(typeof authToken === "string" && authToken.trim().length > 0
|
|
138
|
-
? authToken.trim()
|
|
139
|
-
: undefined)
|
|
118
|
+
throw new Error([
|
|
119
|
+
"gateway remote mode misconfigured: gateway.remote.url missing",
|
|
120
|
+
`Config: ${context.configPath}`,
|
|
121
|
+
"Fix: set gateway.remote.url, or set gateway.mode=local.",
|
|
122
|
+
].join("\n"));
|
|
123
|
+
}
|
|
124
|
+
function resolveGatewayCredentials(context) {
|
|
125
|
+
const authToken = context.config.gateway?.auth?.token;
|
|
126
|
+
const authPassword = context.config.gateway?.auth?.password;
|
|
127
|
+
const token = context.explicitAuth.token ||
|
|
128
|
+
(!context.urlOverride
|
|
129
|
+
? context.isRemoteMode
|
|
130
|
+
? trimToUndefined(context.remote?.token)
|
|
131
|
+
: trimToUndefined(process.env.POOLBOT_GATEWAY_TOKEN) ||
|
|
132
|
+
trimToUndefined(process.env.CLAWDBOT_GATEWAY_TOKEN) ||
|
|
133
|
+
trimToUndefined(authToken)
|
|
140
134
|
: undefined);
|
|
141
|
-
const password = explicitAuth.password ||
|
|
142
|
-
(!urlOverride
|
|
143
|
-
? process.env.POOLBOT_GATEWAY_PASSWORD
|
|
144
|
-
process.env.CLAWDBOT_GATEWAY_PASSWORD
|
|
145
|
-
(isRemoteMode
|
|
146
|
-
?
|
|
147
|
-
|
|
148
|
-
: undefined
|
|
149
|
-
: typeof authPassword === "string" && authPassword.trim().length > 0
|
|
150
|
-
? authPassword.trim()
|
|
151
|
-
: undefined)
|
|
135
|
+
const password = context.explicitAuth.password ||
|
|
136
|
+
(!context.urlOverride
|
|
137
|
+
? trimToUndefined(process.env.POOLBOT_GATEWAY_PASSWORD) ||
|
|
138
|
+
trimToUndefined(process.env.CLAWDBOT_GATEWAY_PASSWORD) ||
|
|
139
|
+
(context.isRemoteMode
|
|
140
|
+
? trimToUndefined(context.remote?.password)
|
|
141
|
+
: trimToUndefined(authPassword))
|
|
152
142
|
: undefined);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
return { token, password };
|
|
144
|
+
}
|
|
145
|
+
async function resolveGatewayTlsFingerprint(params) {
|
|
146
|
+
const { opts, context, url } = params;
|
|
147
|
+
const useLocalTls = context.config.gateway?.tls?.enabled === true &&
|
|
148
|
+
!context.urlOverride &&
|
|
149
|
+
!context.remoteUrl &&
|
|
150
|
+
url.startsWith("wss://");
|
|
151
|
+
const tlsRuntime = useLocalTls
|
|
152
|
+
? await loadGatewayTlsRuntime(context.config.gateway?.tls)
|
|
153
|
+
: undefined;
|
|
154
|
+
const overrideTlsFingerprint = trimToUndefined(opts.tlsFingerprint);
|
|
155
|
+
const remoteTlsFingerprint = context.isRemoteMode && !context.urlOverride && context.remoteUrl
|
|
156
|
+
? trimToUndefined(context.remote?.tlsFingerprint)
|
|
157
|
+
: undefined;
|
|
158
|
+
return (overrideTlsFingerprint ||
|
|
159
|
+
remoteTlsFingerprint ||
|
|
160
|
+
(tlsRuntime?.enabled ? tlsRuntime.fingerprintSha256 : undefined));
|
|
161
|
+
}
|
|
162
|
+
function formatGatewayCloseError(code, reason, connectionDetails) {
|
|
163
|
+
const reasonText = reason?.trim() || "no close reason";
|
|
164
|
+
const hint = code === 1006 ? "abnormal closure (no close frame)" : code === 1000 ? "normal closure" : "";
|
|
165
|
+
const suffix = hint ? ` ${hint}` : "";
|
|
166
|
+
return `gateway closed (${code}${suffix}): ${reasonText}\n${connectionDetails.message}`;
|
|
167
|
+
}
|
|
168
|
+
function formatGatewayTimeoutError(timeoutMs, connectionDetails) {
|
|
169
|
+
return `gateway timeout after ${timeoutMs}ms\n${connectionDetails.message}`;
|
|
170
|
+
}
|
|
171
|
+
async function executeGatewayRequestWithScopes(params) {
|
|
172
|
+
const { opts, scopes, url, token, password, tlsFingerprint, timeoutMs, safeTimerTimeoutMs } = params;
|
|
160
173
|
return await new Promise((resolve, reject) => {
|
|
161
174
|
let settled = false;
|
|
162
175
|
let ignoreClose = false;
|
|
@@ -185,7 +198,7 @@ export async function callGateway(opts) {
|
|
|
185
198
|
platform: opts.platform,
|
|
186
199
|
mode: opts.mode ?? GATEWAY_CLIENT_MODES.CLI,
|
|
187
200
|
role: "operator",
|
|
188
|
-
scopes
|
|
201
|
+
scopes,
|
|
189
202
|
deviceIdentity: loadOrCreateDeviceIdentity(),
|
|
190
203
|
minProtocol: opts.minProtocol ?? PROTOCOL_VERSION,
|
|
191
204
|
maxProtocol: opts.maxProtocol ?? PROTOCOL_VERSION,
|
|
@@ -210,17 +223,73 @@ export async function callGateway(opts) {
|
|
|
210
223
|
}
|
|
211
224
|
ignoreClose = true;
|
|
212
225
|
client.stop();
|
|
213
|
-
stop(new Error(
|
|
226
|
+
stop(new Error(formatGatewayCloseError(code, reason, params.connectionDetails)));
|
|
214
227
|
},
|
|
215
228
|
});
|
|
216
229
|
const timer = setTimeout(() => {
|
|
217
230
|
ignoreClose = true;
|
|
218
231
|
client.stop();
|
|
219
|
-
stop(new Error(
|
|
232
|
+
stop(new Error(formatGatewayTimeoutError(timeoutMs, params.connectionDetails)));
|
|
220
233
|
}, safeTimerTimeoutMs);
|
|
221
234
|
client.start();
|
|
222
235
|
});
|
|
223
236
|
}
|
|
237
|
+
async function callGatewayWithScopes(opts, scopes) {
|
|
238
|
+
const { timeoutMs, safeTimerTimeoutMs } = resolveGatewayCallTimeout(opts.timeoutMs);
|
|
239
|
+
const context = resolveGatewayCallContext(opts);
|
|
240
|
+
ensureExplicitGatewayAuth({
|
|
241
|
+
urlOverride: context.urlOverride,
|
|
242
|
+
auth: context.explicitAuth,
|
|
243
|
+
errorHint: "Fix: pass --token or --password (or gatewayToken in tools).",
|
|
244
|
+
configPath: context.configPath,
|
|
245
|
+
});
|
|
246
|
+
ensureRemoteModeUrlConfigured(context);
|
|
247
|
+
const connectionDetails = buildGatewayConnectionDetails({
|
|
248
|
+
config: context.config,
|
|
249
|
+
url: context.urlOverride,
|
|
250
|
+
...(opts.configPath ? { configPath: opts.configPath } : {}),
|
|
251
|
+
});
|
|
252
|
+
const url = connectionDetails.url;
|
|
253
|
+
const tlsFingerprint = await resolveGatewayTlsFingerprint({ opts, context, url });
|
|
254
|
+
const { token, password } = resolveGatewayCredentials(context);
|
|
255
|
+
return await executeGatewayRequestWithScopes({
|
|
256
|
+
opts,
|
|
257
|
+
scopes,
|
|
258
|
+
url,
|
|
259
|
+
token,
|
|
260
|
+
password,
|
|
261
|
+
tlsFingerprint,
|
|
262
|
+
timeoutMs,
|
|
263
|
+
safeTimerTimeoutMs,
|
|
264
|
+
connectionDetails,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
export async function callGatewayScoped(opts) {
|
|
268
|
+
return await callGatewayWithScopes(opts, opts.scopes);
|
|
269
|
+
}
|
|
270
|
+
export async function callGatewayCli(opts) {
|
|
271
|
+
const scopes = Array.isArray(opts.scopes) ? opts.scopes : CLI_DEFAULT_OPERATOR_SCOPES;
|
|
272
|
+
return await callGatewayWithScopes(opts, scopes);
|
|
273
|
+
}
|
|
274
|
+
export async function callGatewayLeastPrivilege(opts) {
|
|
275
|
+
const scopes = resolveLeastPrivilegeOperatorScopesForMethod(opts.method);
|
|
276
|
+
return await callGatewayWithScopes(opts, scopes);
|
|
277
|
+
}
|
|
278
|
+
export async function callGateway(opts) {
|
|
279
|
+
if (Array.isArray(opts.scopes)) {
|
|
280
|
+
return await callGatewayWithScopes(opts, opts.scopes);
|
|
281
|
+
}
|
|
282
|
+
const callerMode = opts.mode ?? GATEWAY_CLIENT_MODES.BACKEND;
|
|
283
|
+
const callerName = opts.clientName ?? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT;
|
|
284
|
+
if (callerMode === GATEWAY_CLIENT_MODES.CLI || callerName === GATEWAY_CLIENT_NAMES.CLI) {
|
|
285
|
+
return await callGatewayCli(opts);
|
|
286
|
+
}
|
|
287
|
+
return await callGatewayLeastPrivilege({
|
|
288
|
+
...opts,
|
|
289
|
+
mode: callerMode,
|
|
290
|
+
clientName: callerName,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
224
293
|
export function randomIdempotencyKey() {
|
|
225
294
|
return randomUUID();
|
|
226
295
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
export const CANVAS_CAPABILITY_PATH_PREFIX = "/__poolbot__/cap";
|
|
3
|
+
export const CANVAS_CAPABILITY_QUERY_PARAM = "oc_cap";
|
|
4
|
+
export const CANVAS_CAPABILITY_TTL_MS = 10 * 60_000;
|
|
5
|
+
function normalizeCapability(raw) {
|
|
6
|
+
const trimmed = raw?.trim();
|
|
7
|
+
return trimmed ? trimmed : undefined;
|
|
8
|
+
}
|
|
9
|
+
export function mintCanvasCapabilityToken() {
|
|
10
|
+
return randomBytes(18).toString("base64url");
|
|
11
|
+
}
|
|
12
|
+
export function buildCanvasScopedHostUrl(baseUrl, capability) {
|
|
13
|
+
const normalizedCapability = normalizeCapability(capability);
|
|
14
|
+
if (!normalizedCapability) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(baseUrl);
|
|
19
|
+
const trimmedPath = url.pathname.replace(/\/+$/, "");
|
|
20
|
+
const prefix = `${CANVAS_CAPABILITY_PATH_PREFIX}/${encodeURIComponent(normalizedCapability)}`;
|
|
21
|
+
url.pathname = `${trimmedPath}${prefix}`;
|
|
22
|
+
url.search = "";
|
|
23
|
+
url.hash = "";
|
|
24
|
+
return url.toString().replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function normalizeCanvasScopedUrl(rawUrl) {
|
|
31
|
+
const url = new URL(rawUrl, "http://localhost");
|
|
32
|
+
const prefix = `${CANVAS_CAPABILITY_PATH_PREFIX}/`;
|
|
33
|
+
let scopedPath = false;
|
|
34
|
+
let malformedScopedPath = false;
|
|
35
|
+
let capabilityFromPath;
|
|
36
|
+
let rewrittenUrl;
|
|
37
|
+
if (url.pathname.startsWith(prefix)) {
|
|
38
|
+
scopedPath = true;
|
|
39
|
+
const remainder = url.pathname.slice(prefix.length);
|
|
40
|
+
const slashIndex = remainder.indexOf("/");
|
|
41
|
+
if (slashIndex <= 0) {
|
|
42
|
+
malformedScopedPath = true;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const encodedCapability = remainder.slice(0, slashIndex);
|
|
46
|
+
const canonicalPath = remainder.slice(slashIndex) || "/";
|
|
47
|
+
let decoded;
|
|
48
|
+
try {
|
|
49
|
+
decoded = decodeURIComponent(encodedCapability);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
malformedScopedPath = true;
|
|
53
|
+
}
|
|
54
|
+
capabilityFromPath = normalizeCapability(decoded);
|
|
55
|
+
if (!capabilityFromPath || !canonicalPath.startsWith("/")) {
|
|
56
|
+
malformedScopedPath = true;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
url.pathname = canonicalPath;
|
|
60
|
+
if (!url.searchParams.has(CANVAS_CAPABILITY_QUERY_PARAM)) {
|
|
61
|
+
url.searchParams.set(CANVAS_CAPABILITY_QUERY_PARAM, capabilityFromPath);
|
|
62
|
+
}
|
|
63
|
+
rewrittenUrl = `${url.pathname}${url.search}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const capability = capabilityFromPath ?? normalizeCapability(url.searchParams.get(CANVAS_CAPABILITY_QUERY_PARAM));
|
|
68
|
+
return {
|
|
69
|
+
pathname: url.pathname,
|
|
70
|
+
capability,
|
|
71
|
+
rewrittenUrl,
|
|
72
|
+
scopedPath,
|
|
73
|
+
malformedScopedPath,
|
|
74
|
+
};
|
|
75
|
+
}
|
package/dist/gateway/client.js
CHANGED
|
@@ -2,11 +2,13 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { WebSocket } from "ws";
|
|
3
3
|
import { clearDeviceAuthToken, loadDeviceAuthToken, storeDeviceAuthToken, } from "../infra/device-auth-store.js";
|
|
4
4
|
import { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload, } from "../infra/device-identity.js";
|
|
5
|
+
import { clearDevicePairing } from "../infra/device-pairing.js";
|
|
5
6
|
import { normalizeFingerprint } from "../infra/tls/fingerprint.js";
|
|
6
7
|
import { rawDataToString } from "../infra/ws.js";
|
|
7
8
|
import { logDebug, logError } from "../logger.js";
|
|
8
9
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, } from "../utils/message-channel.js";
|
|
9
10
|
import { buildDeviceAuthPayload } from "./device-auth.js";
|
|
11
|
+
import { isSecureWebSocketUrl } from "./net.js";
|
|
10
12
|
import { PROTOCOL_VERSION, validateEventFrame, validateRequestFrame, validateResponseFrame, } from "./protocol/index.js";
|
|
11
13
|
export const GATEWAY_CLOSE_CODE_HINTS = {
|
|
12
14
|
1000: "normal closure",
|
|
@@ -46,6 +48,24 @@ export class GatewayClient {
|
|
|
46
48
|
this.opts.onConnectError?.(new Error("gateway tls fingerprint requires wss:// gateway url"));
|
|
47
49
|
return;
|
|
48
50
|
}
|
|
51
|
+
// Security check: block ALL plaintext ws:// to non-loopback addresses (CWE-319, CVSS 9.8)
|
|
52
|
+
// This protects both credentials AND chat/conversation data from MITM attacks.
|
|
53
|
+
// Device tokens may be loaded later in sendConnect(), so we block regardless of hasCredentials.
|
|
54
|
+
if (!isSecureWebSocketUrl(url)) {
|
|
55
|
+
// Safe hostname extraction - avoid throwing on malformed URLs in error path
|
|
56
|
+
let displayHost = url;
|
|
57
|
+
try {
|
|
58
|
+
displayHost = new URL(url).hostname || url;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Use raw URL if parsing fails
|
|
62
|
+
}
|
|
63
|
+
const error = new Error(`SECURITY ERROR: Cannot connect to "${displayHost}" over plaintext ws://. ` +
|
|
64
|
+
"Both credentials and chat data would be exposed to network interception. " +
|
|
65
|
+
"Use wss:// for the gateway URL, or connect via SSH tunnel to localhost.");
|
|
66
|
+
this.opts.onConnectError?.(error);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
49
69
|
// Allow node screen snapshots and other large responses.
|
|
50
70
|
const wsOptions = {
|
|
51
71
|
maxPayload: 25 * 1024 * 1024,
|
|
@@ -87,17 +107,21 @@ export class GatewayClient {
|
|
|
87
107
|
this.ws.on("close", (code, reason) => {
|
|
88
108
|
const reasonText = rawDataToString(reason);
|
|
89
109
|
this.ws = null;
|
|
90
|
-
// If closed due to device token mismatch, clear the stored token so next attempt can get a fresh one
|
|
110
|
+
// If closed due to device token mismatch, clear the stored token and pairing so next attempt can get a fresh one
|
|
91
111
|
if (code === 1008 &&
|
|
92
112
|
reasonText.toLowerCase().includes("device token mismatch") &&
|
|
93
113
|
this.opts.deviceIdentity) {
|
|
114
|
+
const deviceId = this.opts.deviceIdentity.deviceId;
|
|
94
115
|
const role = this.opts.role ?? "operator";
|
|
95
116
|
try {
|
|
96
|
-
clearDeviceAuthToken({ deviceId
|
|
97
|
-
|
|
117
|
+
clearDeviceAuthToken({ deviceId, role });
|
|
118
|
+
void clearDevicePairing(deviceId).catch((err) => {
|
|
119
|
+
logDebug(`failed clearing stale device pairing for device ${deviceId}: ${String(err)}`);
|
|
120
|
+
});
|
|
121
|
+
logDebug(`cleared stale device-auth token for device ${deviceId}`);
|
|
98
122
|
}
|
|
99
123
|
catch (err) {
|
|
100
|
-
logDebug(`failed clearing stale device-auth token for device ${
|
|
124
|
+
logDebug(`failed clearing stale device-auth token for device ${deviceId}: ${String(err)}`);
|
|
101
125
|
}
|
|
102
126
|
}
|
|
103
127
|
this.flushPendingErrors(new Error(`gateway closed (${code}): ${reasonText}`));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1
2
|
import chokidar from "chokidar";
|
|
2
3
|
import { listChannelPlugins } from "../channels/plugins/index.js";
|
|
3
4
|
import { getActivePluginRegistry } from "../plugins/runtime.js";
|
|
@@ -102,10 +103,8 @@ export function diffConfigPaths(prev, next, prefix = "") {
|
|
|
102
103
|
}
|
|
103
104
|
return paths;
|
|
104
105
|
}
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
return [];
|
|
108
|
-
}
|
|
106
|
+
if (isDeepStrictEqual(prev, next)) {
|
|
107
|
+
return [];
|
|
109
108
|
}
|
|
110
109
|
return [prefix || "<root>"];
|
|
111
110
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function normalizePart(value, fallback) {
|
|
2
|
+
if (typeof value !== "string") {
|
|
3
|
+
return fallback;
|
|
4
|
+
}
|
|
5
|
+
const normalized = value.trim();
|
|
6
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
7
|
+
}
|
|
8
|
+
export function resolveControlPlaneActor(client) {
|
|
9
|
+
return {
|
|
10
|
+
actor: normalizePart(client?.connect?.client?.id, "unknown-actor"),
|
|
11
|
+
deviceId: normalizePart(client?.connect?.device?.id, "unknown-device"),
|
|
12
|
+
clientIp: normalizePart(client?.clientIp, "unknown-ip"),
|
|
13
|
+
connId: normalizePart(client?.connId, "unknown-conn"),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function formatControlPlaneActor(actor) {
|
|
17
|
+
return `actor=${actor.actor} device=${actor.deviceId} ip=${actor.clientIp} conn=${actor.connId}`;
|
|
18
|
+
}
|
|
19
|
+
export function summarizeChangedPaths(paths, maxPaths = 8) {
|
|
20
|
+
if (paths.length === 0) {
|
|
21
|
+
return "<none>";
|
|
22
|
+
}
|
|
23
|
+
if (paths.length <= maxPaths) {
|
|
24
|
+
return paths.join(",");
|
|
25
|
+
}
|
|
26
|
+
const head = paths.slice(0, maxPaths).join(",");
|
|
27
|
+
return `${head},+${paths.length - maxPaths} more`;
|
|
28
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS = 3;
|
|
2
|
+
const CONTROL_PLANE_RATE_LIMIT_WINDOW_MS = 60_000;
|
|
3
|
+
const controlPlaneBuckets = new Map();
|
|
4
|
+
function normalizePart(value, fallback) {
|
|
5
|
+
if (typeof value !== "string") {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
const normalized = value.trim();
|
|
9
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
10
|
+
}
|
|
11
|
+
export function resolveControlPlaneRateLimitKey(client) {
|
|
12
|
+
const deviceId = normalizePart(client?.connect?.device?.id, "unknown-device");
|
|
13
|
+
const clientIp = normalizePart(client?.clientIp, "unknown-ip");
|
|
14
|
+
return `${deviceId}|${clientIp}`;
|
|
15
|
+
}
|
|
16
|
+
export function consumeControlPlaneWriteBudget(params) {
|
|
17
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
18
|
+
const key = resolveControlPlaneRateLimitKey(params.client);
|
|
19
|
+
const bucket = controlPlaneBuckets.get(key);
|
|
20
|
+
if (!bucket || nowMs - bucket.windowStartMs >= CONTROL_PLANE_RATE_LIMIT_WINDOW_MS) {
|
|
21
|
+
controlPlaneBuckets.set(key, {
|
|
22
|
+
count: 1,
|
|
23
|
+
windowStartMs: nowMs,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
allowed: true,
|
|
27
|
+
retryAfterMs: 0,
|
|
28
|
+
remaining: CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS - 1,
|
|
29
|
+
key,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (bucket.count >= CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS) {
|
|
33
|
+
const retryAfterMs = Math.max(0, bucket.windowStartMs + CONTROL_PLANE_RATE_LIMIT_WINDOW_MS - nowMs);
|
|
34
|
+
return {
|
|
35
|
+
allowed: false,
|
|
36
|
+
retryAfterMs,
|
|
37
|
+
remaining: 0,
|
|
38
|
+
key,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
bucket.count += 1;
|
|
42
|
+
return {
|
|
43
|
+
allowed: true,
|
|
44
|
+
retryAfterMs: 0,
|
|
45
|
+
remaining: Math.max(0, CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS - bucket.count),
|
|
46
|
+
key,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export const __testing = {
|
|
50
|
+
resetControlPlaneRateLimitState() {
|
|
51
|
+
controlPlaneBuckets.clear();
|
|
52
|
+
},
|
|
53
|
+
};
|