@poolzin/pool-bot 2026.2.25 → 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/dist/acp/event-mapper.js +87 -22
- package/dist/acp/meta.js +12 -6
- package/dist/agents/agent-paths.js +8 -9
- package/dist/agents/agent-scope.js +7 -5
- package/dist/agents/auth-profiles/oauth.js +148 -64
- package/dist/agents/auth-profiles/session-override.js +13 -7
- package/dist/agents/bash-tools.exec-host-gateway.js +14 -4
- package/dist/agents/bash-tools.exec-runtime.js +2 -25
- 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/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-selection.js +7 -1
- package/dist/agents/models-config.providers.js +93 -11
- 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-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.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/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.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/fast-coding-tools.js +1 -18
- package/dist/agents/test-helpers/fast-core-tools.js +1 -17
- 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.js +51 -26
- package/dist/agents/tools/browser-tool.js +2 -2
- package/dist/agents/tools/canvas-tool.js +27 -1
- package/dist/agents/tools/common.js +45 -0
- package/dist/agents/tools/discord-actions-guild.js +4 -1
- package/dist/agents/tools/gateway-tool.js +3 -1
- package/dist/agents/tools/nodes-utils.js +1 -10
- 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/auto-reply/commands-registry.data.js +51 -0
- package/dist/auto-reply/commands-registry.js +4 -3
- package/dist/auto-reply/group-activation.js +10 -5
- package/dist/auto-reply/inbound-debounce.js +10 -5
- package/dist/auto-reply/reply/abort.js +1 -1
- package/dist/auto-reply/reply/agent-runner-execution.js +4 -1
- 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.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 +4 -0
- package/dist/browser/extension-relay-auth.js +55 -0
- package/dist/browser/extension-relay.js +74 -29
- package/dist/browser/navigation-guard.js +9 -1
- 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 +2 -2
- package/dist/browser/server-context.js +7 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +2 -1
- 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/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/cli/banner.js +1 -1
- 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/gateway-cli/run-loop.js +23 -5
- package/dist/cli/node-cli/register.js +14 -5
- package/dist/cli/nodes-media-utils.js +7 -2
- 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 +34 -41
- package/dist/cli/ports.js +11 -10
- package/dist/cli/program/command-registry.js +2 -11
- package/dist/cli/program/command-tree.js +16 -0
- 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 +11 -7
- package/dist/cli/system-cli.js +36 -46
- 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 +22 -2
- 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/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/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/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 +30 -16
- package/dist/config/config-paths.js +9 -11
- package/dist/config/defaults.js +22 -2
- package/dist/config/discord-preview-streaming.js +104 -0
- package/dist/config/env-vars.js +37 -8
- package/dist/config/includes.js +4 -0
- package/dist/config/io.js +97 -12
- package/dist/config/legacy.migrations.part-1.js +189 -78
- package/dist/config/legacy.shared.js +3 -1
- package/dist/config/merge-patch.js +4 -0
- package/dist/config/prototype-keys.js +4 -0
- package/dist/config/schema.help.js +44 -7
- 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/zod-schema.agent-runtime.js +11 -0
- package/dist/config/zod-schema.js +148 -13
- 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/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/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 +34 -10
- package/dist/gateway/call.js +4 -16
- package/dist/gateway/client.js +28 -4
- package/dist/gateway/config-reload.js +3 -4
- package/dist/gateway/control-ui.js +219 -96
- package/dist/gateway/hooks-mapping.js +88 -38
- package/dist/gateway/http-auth-helpers.js +3 -2
- package/dist/gateway/http-endpoint-helpers.js +1 -0
- package/dist/gateway/net.js +54 -12
- 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 +1 -0
- package/dist/gateway/origin-check.js +1 -18
- package/dist/gateway/protocol/index.js +4 -3
- 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 +2 -1
- package/dist/gateway/protocol/schema/sessions.js +6 -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 +16 -7
- package/dist/gateway/server-maintenance.js +20 -5
- package/dist/gateway/server-methods/chat.js +10 -6
- package/dist/gateway/server-methods/config.js +12 -14
- package/dist/gateway/server-methods/devices.js +17 -3
- package/dist/gateway/server-methods/models.js +11 -1
- 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-runtime-config.js +34 -13
- package/dist/gateway/server-startup-memory.js +17 -11
- package/dist/gateway/session-utils.fs.js +32 -34
- package/dist/gateway/sessions-resolve.js +17 -5
- package/dist/gateway/test-helpers.openai-mock.js +14 -7
- 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 +6 -5
- package/dist/hooks/frontmatter.js +6 -6
- 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.js +174 -73
- package/dist/infra/control-ui-assets.js +14 -6
- package/dist/infra/device-pairing.js +108 -29
- package/dist/infra/env.js +10 -5
- package/dist/infra/exec-approvals-allowlist.js +122 -0
- package/dist/infra/exec-approvals-analysis.js +34 -3
- package/dist/infra/exec-approvals.js +5 -17
- package/dist/infra/exec-safe-bin-policy.js +53 -45
- package/dist/infra/fs-safe.js +71 -39
- package/dist/infra/gateway-lock.js +6 -2
- 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/net/ssrf.js +131 -38
- package/dist/infra/outbound/bound-delivery-router.js +88 -0
- 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/payloads.js +14 -7
- package/dist/infra/outbound/session-binding-service.js +123 -0
- package/dist/infra/path-guards.js +25 -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/retry-policy.js +4 -2
- package/dist/infra/retry.js +9 -5
- 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/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/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/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/webhook-targets.js +32 -0
- package/dist/plugins/bundled-dir.js +9 -6
- package/dist/plugins/hooks.js +50 -0
- package/dist/plugins/install.js +28 -16
- 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-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 +93 -2
- package/dist/security/audit-extra.async.js +159 -5
- package/dist/security/audit-extra.js +1 -1
- package/dist/security/audit-extra.sync.js +85 -6
- package/dist/security/audit.js +40 -4
- package/dist/security/dm-policy-shared.js +44 -0
- package/dist/security/external-content.js +26 -6
- package/dist/shared/entry-status.js +6 -0
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-match.js +11 -4
- package/dist/shared/operator-scope-compat.js +8 -3
- 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/monitor/auth.js +1 -1
- package/dist/slack/monitor/message-handler/dispatch.js +50 -29
- 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 +155 -90
- package/dist/telegram/bot-native-commands.js +16 -0
- package/dist/telegram/draft-stream.js +14 -1
- 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/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/theme.js +12 -12
- package/dist/tts/tts.js +80 -567
- package/dist/tui/components/chat-log.js +41 -8
- package/dist/tui/theme/theme.js +10 -12
- package/dist/tui/tui-local-shell.js +16 -6
- package/dist/tui/tui.js +58 -6
- 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/queue-helpers.js +67 -12
- 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 +123 -16
- package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
- package/extensions/bluebubbles/src/monitor.ts +208 -1950
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isNodeRoleMethod } from "./method-scopes.js";
|
|
2
|
+
export const GATEWAY_ROLES = ["operator", "node"];
|
|
3
|
+
export function parseGatewayRole(roleRaw) {
|
|
4
|
+
if (roleRaw === "operator" || roleRaw === "node") {
|
|
5
|
+
return roleRaw;
|
|
6
|
+
}
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
export function roleCanSkipDeviceIdentity(role, sharedAuthOk) {
|
|
10
|
+
return role === "operator" && sharedAuthOk;
|
|
11
|
+
}
|
|
12
|
+
export function isRoleAuthorizedForMethod(role, method) {
|
|
13
|
+
if (isNodeRoleMethod(method)) {
|
|
14
|
+
return role === "node";
|
|
15
|
+
}
|
|
16
|
+
return role === "operator";
|
|
17
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { roleCanSkipDeviceIdentity } from "../../role-policy.js";
|
|
2
|
+
export function resolveControlUiAuthPolicy(params) {
|
|
3
|
+
const allowInsecureAuthConfigured = params.isControlUi && params.controlUiConfig?.allowInsecureAuth === true;
|
|
4
|
+
const dangerouslyDisableDeviceAuth = params.isControlUi && params.controlUiConfig?.dangerouslyDisableDeviceAuth === true;
|
|
5
|
+
return {
|
|
6
|
+
allowInsecureAuthConfigured,
|
|
7
|
+
dangerouslyDisableDeviceAuth,
|
|
8
|
+
// `allowInsecureAuth` must not bypass secure-context/device-auth requirements.
|
|
9
|
+
allowBypass: dangerouslyDisableDeviceAuth,
|
|
10
|
+
device: dangerouslyDisableDeviceAuth ? null : params.deviceRaw,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function shouldSkipControlUiPairing(policy, sharedAuthOk) {
|
|
14
|
+
return policy.allowBypass && sharedAuthOk;
|
|
15
|
+
}
|
|
16
|
+
export function evaluateMissingDeviceIdentity(params) {
|
|
17
|
+
if (params.hasDeviceIdentity) {
|
|
18
|
+
return { kind: "allow" };
|
|
19
|
+
}
|
|
20
|
+
if (params.isControlUi && !params.controlUiAuthPolicy.allowBypass) {
|
|
21
|
+
// Allow localhost Control UI connections when allowInsecureAuth is configured.
|
|
22
|
+
// Localhost has no network interception risk, and browser SubtleCrypto
|
|
23
|
+
// (needed for device identity) is unavailable in insecure HTTP contexts.
|
|
24
|
+
// Remote connections are still rejected to preserve the MitM protection
|
|
25
|
+
// that the security fix (#20684) intended.
|
|
26
|
+
if (!params.controlUiAuthPolicy.allowInsecureAuthConfigured || !params.isLocalClient) {
|
|
27
|
+
return { kind: "reject-control-ui-insecure-auth" };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (roleCanSkipDeviceIdentity(params.role, params.sharedAuthOk)) {
|
|
31
|
+
return { kind: "allow" };
|
|
32
|
+
}
|
|
33
|
+
if (!params.authOk && params.hasSharedAuth) {
|
|
34
|
+
return { kind: "reject-unauthorized" };
|
|
35
|
+
}
|
|
36
|
+
return { kind: "reject-device-required" };
|
|
37
|
+
}
|
|
@@ -7,17 +7,20 @@ import { recordRemoteNodeInfo, refreshRemoteNodeBins } from "../../../infra/skil
|
|
|
7
7
|
import { upsertPresence } from "../../../infra/system-presence.js";
|
|
8
8
|
import { loadVoiceWakeConfig } from "../../../infra/voicewake.js";
|
|
9
9
|
import { rawDataToString } from "../../../infra/ws.js";
|
|
10
|
+
import { roleScopesAllow } from "../../../shared/operator-scope-compat.js";
|
|
10
11
|
import { isGatewayCliClient, isWebchatClient } from "../../../utils/message-channel.js";
|
|
11
12
|
import { resolveRuntimeServiceVersion } from "../../../version.js";
|
|
12
13
|
import { AUTH_RATE_LIMIT_SCOPE_DEVICE_TOKEN, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET, } from "../../auth-rate-limit.js";
|
|
13
|
-
import {
|
|
14
|
+
import { authorizeHttpGatewayConnect, authorizeWsControlUiGatewayConnect, isLocalDirectRequest, } from "../../auth.js";
|
|
15
|
+
import { buildCanvasScopedHostUrl, CANVAS_CAPABILITY_TTL_MS, mintCanvasCapabilityToken, } from "../../canvas-capability.js";
|
|
14
16
|
import { buildDeviceAuthPayload } from "../../device-auth.js";
|
|
15
|
-
import { isLoopbackAddress, isTrustedProxyAddress,
|
|
17
|
+
import { isLoopbackAddress, isTrustedProxyAddress, resolveClientIp } from "../../net.js";
|
|
16
18
|
import { resolveHostName } from "../../net.js";
|
|
17
19
|
import { resolveNodeCommandAllowlist } from "../../node-command-policy.js";
|
|
18
20
|
import { checkBrowserOrigin } from "../../origin-check.js";
|
|
19
21
|
import { GATEWAY_CLIENT_IDS } from "../../protocol/client-info.js";
|
|
20
22
|
import { ErrorCodes, errorShape, formatValidationErrors, PROTOCOL_VERSION, validateConnectParams, validateRequestFrame, } from "../../protocol/index.js";
|
|
23
|
+
import { parseGatewayRole } from "../../role-policy.js";
|
|
21
24
|
import { MAX_BUFFERED_BYTES, MAX_PAYLOAD_BYTES, TICK_INTERVAL_MS } from "../../server-constants.js";
|
|
22
25
|
import { handleGatewayRequest } from "../../server-methods.js";
|
|
23
26
|
import { formatError } from "../../server-utils.js";
|
|
@@ -25,12 +28,20 @@ import { formatForLog, logWs } from "../../ws-log.js";
|
|
|
25
28
|
import { truncateCloseReason } from "../close-reason.js";
|
|
26
29
|
import { buildGatewaySnapshot, getHealthCache, getHealthVersion, incrementPresenceVersion, refreshGatewayHealthSnapshot, } from "../health-state.js";
|
|
27
30
|
import { formatGatewayAuthFailureMessage } from "./auth-messages.js";
|
|
31
|
+
import { evaluateMissingDeviceIdentity, resolveControlUiAuthPolicy, shouldSkipControlUiPairing, } from "./connect-policy.js";
|
|
28
32
|
const DEVICE_SIGNATURE_SKEW_MS = 10 * 60 * 1000;
|
|
29
33
|
export function attachGatewayWsMessageHandler(params) {
|
|
30
34
|
const { socket, upgradeReq, connId, remoteAddr, forwardedFor, realIp, requestHost, requestOrigin, requestUserAgent, canvasHostUrl, connectNonce, resolvedAuth, rateLimiter, gatewayMethods, events, extraHandlers, buildRequestContext, send, close, isClosed, clearHandshakeTimer, getClient, setClient, setHandshakeState, setCloseCause, setLastFrameMeta, logGateway, logHealth, logWsControl, } = params;
|
|
31
35
|
const configSnapshot = loadConfig();
|
|
32
36
|
const trustedProxies = configSnapshot.gateway?.trustedProxies ?? [];
|
|
33
|
-
const
|
|
37
|
+
const allowRealIpFallback = configSnapshot.gateway?.allowRealIpFallback === true;
|
|
38
|
+
const clientIp = resolveClientIp({
|
|
39
|
+
remoteAddr,
|
|
40
|
+
forwardedFor,
|
|
41
|
+
realIp,
|
|
42
|
+
trustedProxies,
|
|
43
|
+
allowRealIpFallback,
|
|
44
|
+
});
|
|
34
45
|
// If proxy headers are present but the remote address isn't trusted, don't treat
|
|
35
46
|
// the connection as local. This prevents auth bypass when running behind a reverse
|
|
36
47
|
// proxy without proper configuration - the proxy's loopback connection would otherwise
|
|
@@ -42,7 +53,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
42
53
|
const hostIsLocal = hostName === "localhost" || hostName === "127.0.0.1" || hostName === "::1";
|
|
43
54
|
const hostIsTailscaleServe = hostName.endsWith(".ts.net");
|
|
44
55
|
const hostIsLocalish = hostIsLocal || hostIsTailscaleServe;
|
|
45
|
-
const isLocalClient = isLocalDirectRequest(upgradeReq, trustedProxies);
|
|
56
|
+
const isLocalClient = isLocalDirectRequest(upgradeReq, trustedProxies, allowRealIpFallback);
|
|
46
57
|
const reportedClientIp = isLocalClient || hasUntrustedProxyHeaders
|
|
47
58
|
? undefined
|
|
48
59
|
: clientIp && !isLoopbackAddress(clientIp)
|
|
@@ -162,7 +173,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
162
173
|
return;
|
|
163
174
|
}
|
|
164
175
|
const roleRaw = connectParams.role ?? "operator";
|
|
165
|
-
const role = roleRaw
|
|
176
|
+
const role = parseGatewayRole(roleRaw);
|
|
166
177
|
if (!role) {
|
|
167
178
|
markHandshakeFailure("invalid-role", {
|
|
168
179
|
role: roleRaw,
|
|
@@ -202,52 +213,64 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
202
213
|
const hasTokenAuth = Boolean(connectParams.auth?.token);
|
|
203
214
|
const hasPasswordAuth = Boolean(connectParams.auth?.password);
|
|
204
215
|
const hasSharedAuth = hasTokenAuth || hasPasswordAuth;
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
|
|
210
|
-
let authResult = await authorizeGatewayConnect({
|
|
211
|
-
auth: resolvedAuth,
|
|
212
|
-
connectAuth: connectParams.auth,
|
|
213
|
-
req: upgradeReq,
|
|
214
|
-
trustedProxies,
|
|
215
|
-
rateLimiter: hasDeviceTokenCandidate ? undefined : rateLimiter,
|
|
216
|
-
clientIp,
|
|
217
|
-
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET,
|
|
216
|
+
const controlUiAuthPolicy = resolveControlUiAuthPolicy({
|
|
217
|
+
isControlUi,
|
|
218
|
+
controlUiConfig: configSnapshot.gateway?.controlUi,
|
|
219
|
+
deviceRaw,
|
|
218
220
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!sharedRateCheck.allowed) {
|
|
225
|
-
authResult = {
|
|
226
|
-
ok: false,
|
|
227
|
-
reason: "rate_limited",
|
|
228
|
-
rateLimited: true,
|
|
229
|
-
retryAfterMs: sharedRateCheck.retryAfterMs,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
rateLimiter.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
let authOk = authResult.ok;
|
|
237
|
-
let authMethod = authResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
|
|
238
|
-
const sharedAuthResult = hasSharedAuth
|
|
239
|
-
? await authorizeGatewayConnect({
|
|
240
|
-
auth: { ...resolvedAuth, allowTailscale: false },
|
|
221
|
+
const device = controlUiAuthPolicy.device;
|
|
222
|
+
const resolveAuthState = async () => {
|
|
223
|
+
const hasDeviceTokenCandidate = Boolean(connectParams.auth?.token && device);
|
|
224
|
+
let nextAuthResult = await authorizeWsControlUiGatewayConnect({
|
|
225
|
+
auth: resolvedAuth,
|
|
241
226
|
connectAuth: connectParams.auth,
|
|
242
227
|
req: upgradeReq,
|
|
243
228
|
trustedProxies,
|
|
244
|
-
|
|
245
|
-
|
|
229
|
+
allowRealIpFallback,
|
|
230
|
+
rateLimiter: hasDeviceTokenCandidate ? undefined : rateLimiter,
|
|
231
|
+
clientIp,
|
|
246
232
|
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET,
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
233
|
+
});
|
|
234
|
+
if (hasDeviceTokenCandidate &&
|
|
235
|
+
nextAuthResult.ok &&
|
|
236
|
+
rateLimiter &&
|
|
237
|
+
(nextAuthResult.method === "token" || nextAuthResult.method === "password")) {
|
|
238
|
+
const sharedRateCheck = rateLimiter.check(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
|
|
239
|
+
if (!sharedRateCheck.allowed) {
|
|
240
|
+
nextAuthResult = {
|
|
241
|
+
ok: false,
|
|
242
|
+
reason: "rate_limited",
|
|
243
|
+
rateLimited: true,
|
|
244
|
+
retryAfterMs: sharedRateCheck.retryAfterMs,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
rateLimiter.reset(clientIp, AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const nextAuthMethod = nextAuthResult.method ?? (resolvedAuth.mode === "password" ? "password" : "token");
|
|
252
|
+
const sharedAuthResult = hasSharedAuth
|
|
253
|
+
? await authorizeHttpGatewayConnect({
|
|
254
|
+
auth: { ...resolvedAuth, allowTailscale: false },
|
|
255
|
+
connectAuth: connectParams.auth,
|
|
256
|
+
req: upgradeReq,
|
|
257
|
+
trustedProxies,
|
|
258
|
+
allowRealIpFallback,
|
|
259
|
+
// Shared-auth probe only; rate-limit side effects are handled in
|
|
260
|
+
// the primary auth flow (or deferred for device-token candidates).
|
|
261
|
+
rateLimitScope: AUTH_RATE_LIMIT_SCOPE_SHARED_SECRET,
|
|
262
|
+
})
|
|
263
|
+
: null;
|
|
264
|
+
const nextSharedAuthOk = sharedAuthResult?.ok === true &&
|
|
265
|
+
(sharedAuthResult.method === "token" || sharedAuthResult.method === "password");
|
|
266
|
+
return {
|
|
267
|
+
authResult: nextAuthResult,
|
|
268
|
+
authOk: nextAuthResult.ok,
|
|
269
|
+
authMethod: nextAuthMethod,
|
|
270
|
+
sharedAuthOk: nextSharedAuthOk,
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
let { authResult, authOk, authMethod, sharedAuthOk } = await resolveAuthState();
|
|
251
274
|
const rejectUnauthorized = (failedAuth) => {
|
|
252
275
|
markHandshakeFailure("unauthorized", {
|
|
253
276
|
authMode: resolvedAuth.mode,
|
|
@@ -274,37 +297,55 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
274
297
|
sendHandshakeErrorResponse(ErrorCodes.INVALID_REQUEST, authMessage);
|
|
275
298
|
close(1008, truncateCloseReason(authMessage));
|
|
276
299
|
};
|
|
277
|
-
|
|
278
|
-
if (scopes.length > 0 && !
|
|
300
|
+
const clearUnboundScopes = () => {
|
|
301
|
+
if (scopes.length > 0 && !controlUiAuthPolicy.allowBypass) {
|
|
279
302
|
scopes = [];
|
|
280
303
|
connectParams.scopes = scopes;
|
|
281
304
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
305
|
+
};
|
|
306
|
+
const handleMissingDeviceIdentity = () => {
|
|
307
|
+
if (!device) {
|
|
308
|
+
clearUnboundScopes();
|
|
309
|
+
}
|
|
310
|
+
const decision = evaluateMissingDeviceIdentity({
|
|
311
|
+
hasDeviceIdentity: Boolean(device),
|
|
312
|
+
role,
|
|
313
|
+
isControlUi,
|
|
314
|
+
controlUiAuthPolicy,
|
|
315
|
+
sharedAuthOk,
|
|
316
|
+
authOk,
|
|
317
|
+
hasSharedAuth,
|
|
318
|
+
isLocalClient,
|
|
319
|
+
});
|
|
320
|
+
if (decision.kind === "allow") {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
if (decision.kind === "reject-control-ui-insecure-auth") {
|
|
324
|
+
const errorMessage = "control ui requires device identity (use HTTPS or localhost secure context)";
|
|
325
|
+
markHandshakeFailure("control-ui-insecure-auth", {
|
|
326
|
+
insecureAuthConfigured: controlUiAuthPolicy.allowInsecureAuthConfigured,
|
|
327
|
+
});
|
|
286
328
|
sendHandshakeErrorResponse(ErrorCodes.INVALID_REQUEST, errorMessage);
|
|
287
329
|
close(1008, errorMessage);
|
|
288
|
-
return;
|
|
330
|
+
return false;
|
|
289
331
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
rejectUnauthorized(authResult);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
markHandshakeFailure("device-required");
|
|
297
|
-
sendHandshakeErrorResponse(ErrorCodes.NOT_PAIRED, "device identity required");
|
|
298
|
-
close(1008, "device identity required");
|
|
299
|
-
return;
|
|
332
|
+
if (decision.kind === "reject-unauthorized") {
|
|
333
|
+
rejectUnauthorized(authResult);
|
|
334
|
+
return false;
|
|
300
335
|
}
|
|
336
|
+
markHandshakeFailure("device-required");
|
|
337
|
+
sendHandshakeErrorResponse(ErrorCodes.NOT_PAIRED, "device identity required");
|
|
338
|
+
close(1008, "device identity required");
|
|
339
|
+
return false;
|
|
340
|
+
};
|
|
341
|
+
if (!handleMissingDeviceIdentity()) {
|
|
342
|
+
return;
|
|
301
343
|
}
|
|
302
344
|
if (device) {
|
|
303
|
-
const
|
|
304
|
-
if (!derivedId || derivedId !== device.id) {
|
|
345
|
+
const rejectDeviceAuthInvalid = (reason, message) => {
|
|
305
346
|
setHandshakeState("failed");
|
|
306
347
|
setCloseCause("device-auth-invalid", {
|
|
307
|
-
reason
|
|
348
|
+
reason,
|
|
308
349
|
client: connectParams.client.id,
|
|
309
350
|
deviceId: device.id,
|
|
310
351
|
});
|
|
@@ -312,61 +353,29 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
312
353
|
type: "res",
|
|
313
354
|
id: frame.id,
|
|
314
355
|
ok: false,
|
|
315
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST,
|
|
356
|
+
error: errorShape(ErrorCodes.INVALID_REQUEST, message),
|
|
316
357
|
});
|
|
317
|
-
close(1008,
|
|
358
|
+
close(1008, message);
|
|
359
|
+
};
|
|
360
|
+
const derivedId = deriveDeviceIdFromPublicKey(device.publicKey);
|
|
361
|
+
if (!derivedId || derivedId !== device.id) {
|
|
362
|
+
rejectDeviceAuthInvalid("device-id-mismatch", "device identity mismatch");
|
|
318
363
|
return;
|
|
319
364
|
}
|
|
320
365
|
const signedAt = device.signedAt;
|
|
321
366
|
if (typeof signedAt !== "number" ||
|
|
322
367
|
Math.abs(Date.now() - signedAt) > DEVICE_SIGNATURE_SKEW_MS) {
|
|
323
|
-
|
|
324
|
-
setCloseCause("device-auth-invalid", {
|
|
325
|
-
reason: "device-signature-stale",
|
|
326
|
-
client: connectParams.client.id,
|
|
327
|
-
deviceId: device.id,
|
|
328
|
-
});
|
|
329
|
-
send({
|
|
330
|
-
type: "res",
|
|
331
|
-
id: frame.id,
|
|
332
|
-
ok: false,
|
|
333
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST, "device signature expired"),
|
|
334
|
-
});
|
|
335
|
-
close(1008, "device signature expired");
|
|
368
|
+
rejectDeviceAuthInvalid("device-signature-stale", "device signature expired");
|
|
336
369
|
return;
|
|
337
370
|
}
|
|
338
371
|
const nonceRequired = !isLocalClient;
|
|
339
372
|
const providedNonce = typeof device.nonce === "string" ? device.nonce.trim() : "";
|
|
340
373
|
if (nonceRequired && !providedNonce) {
|
|
341
|
-
|
|
342
|
-
setCloseCause("device-auth-invalid", {
|
|
343
|
-
reason: "device-nonce-missing",
|
|
344
|
-
client: connectParams.client.id,
|
|
345
|
-
deviceId: device.id,
|
|
346
|
-
});
|
|
347
|
-
send({
|
|
348
|
-
type: "res",
|
|
349
|
-
id: frame.id,
|
|
350
|
-
ok: false,
|
|
351
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST, "device nonce required"),
|
|
352
|
-
});
|
|
353
|
-
close(1008, "device nonce required");
|
|
374
|
+
rejectDeviceAuthInvalid("device-nonce-missing", "device nonce required");
|
|
354
375
|
return;
|
|
355
376
|
}
|
|
356
377
|
if (providedNonce && providedNonce !== connectNonce) {
|
|
357
|
-
|
|
358
|
-
setCloseCause("device-auth-invalid", {
|
|
359
|
-
reason: "device-nonce-mismatch",
|
|
360
|
-
client: connectParams.client.id,
|
|
361
|
-
deviceId: device.id,
|
|
362
|
-
});
|
|
363
|
-
send({
|
|
364
|
-
type: "res",
|
|
365
|
-
id: frame.id,
|
|
366
|
-
ok: false,
|
|
367
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST, "device nonce mismatch"),
|
|
368
|
-
});
|
|
369
|
-
close(1008, "device nonce mismatch");
|
|
378
|
+
rejectDeviceAuthInvalid("device-nonce-mismatch", "device nonce mismatch");
|
|
370
379
|
return;
|
|
371
380
|
}
|
|
372
381
|
const payload = buildDeviceAuthPayload({
|
|
@@ -380,21 +389,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
380
389
|
nonce: providedNonce || undefined,
|
|
381
390
|
version: providedNonce ? "v2" : "v1",
|
|
382
391
|
});
|
|
383
|
-
const rejectDeviceSignatureInvalid = () =>
|
|
384
|
-
setHandshakeState("failed");
|
|
385
|
-
setCloseCause("device-auth-invalid", {
|
|
386
|
-
reason: "device-signature",
|
|
387
|
-
client: connectParams.client.id,
|
|
388
|
-
deviceId: device.id,
|
|
389
|
-
});
|
|
390
|
-
send({
|
|
391
|
-
type: "res",
|
|
392
|
-
id: frame.id,
|
|
393
|
-
ok: false,
|
|
394
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST, "device signature invalid"),
|
|
395
|
-
});
|
|
396
|
-
close(1008, "device signature invalid");
|
|
397
|
-
};
|
|
392
|
+
const rejectDeviceSignatureInvalid = () => rejectDeviceAuthInvalid("device-signature", "device signature invalid");
|
|
398
393
|
const signatureOk = verifyDeviceSignature(device.publicKey, payload, device.signature);
|
|
399
394
|
const allowLegacy = !nonceRequired && !providedNonce;
|
|
400
395
|
if (!signatureOk && allowLegacy) {
|
|
@@ -422,19 +417,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
422
417
|
}
|
|
423
418
|
devicePublicKey = normalizeDevicePublicKeyBase64Url(device.publicKey);
|
|
424
419
|
if (!devicePublicKey) {
|
|
425
|
-
|
|
426
|
-
setCloseCause("device-auth-invalid", {
|
|
427
|
-
reason: "device-public-key",
|
|
428
|
-
client: connectParams.client.id,
|
|
429
|
-
deviceId: device.id,
|
|
430
|
-
});
|
|
431
|
-
send({
|
|
432
|
-
type: "res",
|
|
433
|
-
id: frame.id,
|
|
434
|
-
ok: false,
|
|
435
|
-
error: errorShape(ErrorCodes.INVALID_REQUEST, "device public key invalid"),
|
|
436
|
-
});
|
|
437
|
-
close(1008, "device public key invalid");
|
|
420
|
+
rejectDeviceAuthInvalid("device-public-key", "device public key invalid");
|
|
438
421
|
return;
|
|
439
422
|
}
|
|
440
423
|
}
|
|
@@ -472,9 +455,28 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
472
455
|
rejectUnauthorized(authResult);
|
|
473
456
|
return;
|
|
474
457
|
}
|
|
475
|
-
const skipPairing =
|
|
458
|
+
const skipPairing = shouldSkipControlUiPairing(controlUiAuthPolicy, sharedAuthOk);
|
|
476
459
|
if (device && devicePublicKey && !skipPairing) {
|
|
477
|
-
const
|
|
460
|
+
const formatAuditList = (items) => {
|
|
461
|
+
if (!items || items.length === 0) {
|
|
462
|
+
return "<none>";
|
|
463
|
+
}
|
|
464
|
+
const out = new Set();
|
|
465
|
+
for (const item of items) {
|
|
466
|
+
const trimmed = item.trim();
|
|
467
|
+
if (trimmed) {
|
|
468
|
+
out.add(trimmed);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (out.size === 0) {
|
|
472
|
+
return "<none>";
|
|
473
|
+
}
|
|
474
|
+
return [...out].toSorted().join(",");
|
|
475
|
+
};
|
|
476
|
+
const logUpgradeAudit = (reason, currentRoles, currentScopes) => {
|
|
477
|
+
logGateway.warn(`security audit: device access upgrade requested reason=${reason} device=${device.id} ip=${reportedClientIp ?? "unknown-ip"} auth=${authMethod} roleFrom=${formatAuditList(currentRoles)} roleTo=${role} scopesFrom=${formatAuditList(currentScopes)} scopesTo=${formatAuditList(scopes)} client=${connectParams.client.id} conn=${connId}`);
|
|
478
|
+
};
|
|
479
|
+
const requirePairing = async (reason) => {
|
|
478
480
|
const pairing = await requestDevicePairing({
|
|
479
481
|
deviceId: device.id,
|
|
480
482
|
publicKey: devicePublicKey,
|
|
@@ -485,7 +487,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
485
487
|
role,
|
|
486
488
|
scopes,
|
|
487
489
|
remoteIp: reportedClientIp,
|
|
488
|
-
silent: isLocalClient,
|
|
490
|
+
silent: isLocalClient && reason === "not-paired",
|
|
489
491
|
});
|
|
490
492
|
const context = buildRequestContext();
|
|
491
493
|
if (pairing.request.silent === true) {
|
|
@@ -532,32 +534,48 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
532
534
|
}
|
|
533
535
|
}
|
|
534
536
|
else {
|
|
535
|
-
const
|
|
537
|
+
const pairedRoles = Array.isArray(paired.roles)
|
|
538
|
+
? paired.roles
|
|
539
|
+
: paired.role
|
|
540
|
+
? [paired.role]
|
|
541
|
+
: [];
|
|
542
|
+
const pairedScopes = Array.isArray(paired.scopes)
|
|
543
|
+
? paired.scopes
|
|
544
|
+
: Array.isArray(paired.approvedScopes)
|
|
545
|
+
? paired.approvedScopes
|
|
546
|
+
: [];
|
|
547
|
+
const allowedRoles = new Set(pairedRoles);
|
|
536
548
|
if (allowedRoles.size === 0) {
|
|
537
|
-
|
|
549
|
+
logUpgradeAudit("role-upgrade", pairedRoles, pairedScopes);
|
|
550
|
+
const ok = await requirePairing("role-upgrade");
|
|
538
551
|
if (!ok) {
|
|
539
552
|
return;
|
|
540
553
|
}
|
|
541
554
|
}
|
|
542
555
|
else if (!allowedRoles.has(role)) {
|
|
543
|
-
|
|
556
|
+
logUpgradeAudit("role-upgrade", pairedRoles, pairedScopes);
|
|
557
|
+
const ok = await requirePairing("role-upgrade");
|
|
544
558
|
if (!ok) {
|
|
545
559
|
return;
|
|
546
560
|
}
|
|
547
561
|
}
|
|
548
|
-
const pairedScopes = Array.isArray(paired.scopes) ? paired.scopes : [];
|
|
549
562
|
if (scopes.length > 0) {
|
|
550
563
|
if (pairedScopes.length === 0) {
|
|
551
|
-
|
|
564
|
+
logUpgradeAudit("scope-upgrade", pairedRoles, pairedScopes);
|
|
565
|
+
const ok = await requirePairing("scope-upgrade");
|
|
552
566
|
if (!ok) {
|
|
553
567
|
return;
|
|
554
568
|
}
|
|
555
569
|
}
|
|
556
570
|
else {
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
571
|
+
const scopesAllowed = roleScopesAllow({
|
|
572
|
+
role,
|
|
573
|
+
requestedScopes: scopes,
|
|
574
|
+
allowedScopes: pairedScopes,
|
|
575
|
+
});
|
|
576
|
+
if (!scopesAllowed) {
|
|
577
|
+
logUpgradeAudit("scope-upgrade", pairedRoles, pairedScopes);
|
|
578
|
+
const ok = await requirePairing("scope-upgrade");
|
|
561
579
|
if (!ok) {
|
|
562
580
|
return;
|
|
563
581
|
}
|
|
@@ -630,6 +648,13 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
630
648
|
snapshot.health = cachedHealth;
|
|
631
649
|
snapshot.stateVersion.health = getHealthVersion();
|
|
632
650
|
}
|
|
651
|
+
const canvasCapability = role === "node" && canvasHostUrl ? mintCanvasCapabilityToken() : undefined;
|
|
652
|
+
const canvasCapabilityExpiresAtMs = canvasCapability
|
|
653
|
+
? Date.now() + CANVAS_CAPABILITY_TTL_MS
|
|
654
|
+
: undefined;
|
|
655
|
+
const scopedCanvasHostUrl = canvasHostUrl && canvasCapability
|
|
656
|
+
? (buildCanvasScopedHostUrl(canvasHostUrl, canvasCapability) ?? canvasHostUrl)
|
|
657
|
+
: canvasHostUrl;
|
|
633
658
|
const helloOk = {
|
|
634
659
|
type: "hello-ok",
|
|
635
660
|
protocol: PROTOCOL_VERSION,
|
|
@@ -641,7 +666,7 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
641
666
|
},
|
|
642
667
|
features: { methods: gatewayMethods, events },
|
|
643
668
|
snapshot,
|
|
644
|
-
canvasHostUrl,
|
|
669
|
+
canvasHostUrl: scopedCanvasHostUrl,
|
|
645
670
|
auth: deviceToken
|
|
646
671
|
? {
|
|
647
672
|
deviceToken: deviceToken.token,
|
|
@@ -663,6 +688,8 @@ export function attachGatewayWsMessageHandler(params) {
|
|
|
663
688
|
connId,
|
|
664
689
|
presenceKey,
|
|
665
690
|
clientIp: reportedClientIp,
|
|
691
|
+
canvasCapability,
|
|
692
|
+
canvasCapabilityExpiresAtMs,
|
|
666
693
|
};
|
|
667
694
|
setClient(nextClient);
|
|
668
695
|
setHandshakeState("connected");
|