@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
|
@@ -1,85 +1,28 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
2
|
+
import { normalizeDeviceAuthScopes } from "../shared/device-auth.js";
|
|
3
|
+
import { roleScopesAllow } from "../shared/operator-scope-compat.js";
|
|
4
|
+
import { createAsyncLock, pruneExpiredPending, readJsonFile, resolvePairingPaths, writeJsonAtomic, } from "./pairing-files.js";
|
|
5
|
+
import { generatePairingToken, verifyPairingToken } from "./pairing-token.js";
|
|
5
6
|
const PENDING_TTL_MS = 5 * 60 * 1000;
|
|
6
|
-
|
|
7
|
-
const root = baseDir ?? resolveStateDir();
|
|
8
|
-
const dir = path.join(root, "devices");
|
|
9
|
-
return {
|
|
10
|
-
dir,
|
|
11
|
-
pendingPath: path.join(dir, "pending.json"),
|
|
12
|
-
pairedPath: path.join(dir, "paired.json"),
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
async function readJSON(filePath) {
|
|
16
|
-
try {
|
|
17
|
-
const raw = await fs.readFile(filePath, "utf8");
|
|
18
|
-
return JSON.parse(raw);
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function writeJSONAtomic(filePath, value) {
|
|
25
|
-
const dir = path.dirname(filePath);
|
|
26
|
-
await fs.mkdir(dir, { recursive: true });
|
|
27
|
-
const tmp = `${filePath}.${randomUUID()}.tmp`;
|
|
28
|
-
await fs.writeFile(tmp, JSON.stringify(value, null, 2), "utf8");
|
|
29
|
-
try {
|
|
30
|
-
await fs.chmod(tmp, 0o600);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// best-effort
|
|
34
|
-
}
|
|
35
|
-
await fs.rename(tmp, filePath);
|
|
36
|
-
try {
|
|
37
|
-
await fs.chmod(filePath, 0o600);
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// best-effort
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function pruneExpiredPending(pendingById, nowMs) {
|
|
44
|
-
for (const [id, req] of Object.entries(pendingById)) {
|
|
45
|
-
if (nowMs - req.ts > PENDING_TTL_MS) {
|
|
46
|
-
delete pendingById[id];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
let lock = Promise.resolve();
|
|
51
|
-
async function withLock(fn) {
|
|
52
|
-
const prev = lock;
|
|
53
|
-
let release;
|
|
54
|
-
lock = new Promise((resolve) => {
|
|
55
|
-
release = resolve;
|
|
56
|
-
});
|
|
57
|
-
await prev;
|
|
58
|
-
try {
|
|
59
|
-
return await fn();
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
release?.();
|
|
63
|
-
}
|
|
64
|
-
}
|
|
7
|
+
const withLock = createAsyncLock();
|
|
65
8
|
async function loadState(baseDir) {
|
|
66
|
-
const { pendingPath, pairedPath } =
|
|
9
|
+
const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "devices");
|
|
67
10
|
const [pending, paired] = await Promise.all([
|
|
68
|
-
|
|
69
|
-
|
|
11
|
+
readJsonFile(pendingPath),
|
|
12
|
+
readJsonFile(pairedPath),
|
|
70
13
|
]);
|
|
71
14
|
const state = {
|
|
72
15
|
pendingById: pending ?? {},
|
|
73
16
|
pairedByDeviceId: paired ?? {},
|
|
74
17
|
};
|
|
75
|
-
pruneExpiredPending(state.pendingById, Date.now());
|
|
18
|
+
pruneExpiredPending(state.pendingById, Date.now(), PENDING_TTL_MS);
|
|
76
19
|
return state;
|
|
77
20
|
}
|
|
78
21
|
async function persistState(state, baseDir) {
|
|
79
|
-
const { pendingPath, pairedPath } =
|
|
22
|
+
const { pendingPath, pairedPath } = resolvePairingPaths(baseDir, "devices");
|
|
80
23
|
await Promise.all([
|
|
81
|
-
|
|
82
|
-
|
|
24
|
+
writeJsonAtomic(pendingPath, state.pendingById),
|
|
25
|
+
writeJsonAtomic(pairedPath, state.pairedByDeviceId),
|
|
83
26
|
]);
|
|
84
27
|
}
|
|
85
28
|
function normalizeDeviceId(deviceId) {
|
|
@@ -92,66 +35,124 @@ function normalizeRole(role) {
|
|
|
92
35
|
function mergeRoles(...items) {
|
|
93
36
|
const roles = new Set();
|
|
94
37
|
for (const item of items) {
|
|
95
|
-
if (!item)
|
|
38
|
+
if (!item) {
|
|
96
39
|
continue;
|
|
40
|
+
}
|
|
97
41
|
if (Array.isArray(item)) {
|
|
98
42
|
for (const role of item) {
|
|
99
43
|
const trimmed = role.trim();
|
|
100
|
-
if (trimmed)
|
|
44
|
+
if (trimmed) {
|
|
101
45
|
roles.add(trimmed);
|
|
46
|
+
}
|
|
102
47
|
}
|
|
103
48
|
}
|
|
104
49
|
else {
|
|
105
50
|
const trimmed = item.trim();
|
|
106
|
-
if (trimmed)
|
|
51
|
+
if (trimmed) {
|
|
107
52
|
roles.add(trimmed);
|
|
53
|
+
}
|
|
108
54
|
}
|
|
109
55
|
}
|
|
110
|
-
if (roles.size === 0)
|
|
56
|
+
if (roles.size === 0) {
|
|
111
57
|
return undefined;
|
|
58
|
+
}
|
|
112
59
|
return [...roles];
|
|
113
60
|
}
|
|
114
61
|
function mergeScopes(...items) {
|
|
115
62
|
const scopes = new Set();
|
|
116
63
|
for (const item of items) {
|
|
117
|
-
if (!item)
|
|
64
|
+
if (!item) {
|
|
118
65
|
continue;
|
|
66
|
+
}
|
|
119
67
|
for (const scope of item) {
|
|
120
68
|
const trimmed = scope.trim();
|
|
121
|
-
if (trimmed)
|
|
69
|
+
if (trimmed) {
|
|
122
70
|
scopes.add(trimmed);
|
|
71
|
+
}
|
|
123
72
|
}
|
|
124
73
|
}
|
|
125
|
-
if (scopes.size === 0)
|
|
74
|
+
if (scopes.size === 0) {
|
|
126
75
|
return undefined;
|
|
76
|
+
}
|
|
127
77
|
return [...scopes];
|
|
128
78
|
}
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
79
|
+
function mergePendingDevicePairingRequest(existing, incoming, isRepair) {
|
|
80
|
+
const existingRole = normalizeRole(existing.role);
|
|
81
|
+
const incomingRole = normalizeRole(incoming.role);
|
|
82
|
+
return {
|
|
83
|
+
...existing,
|
|
84
|
+
displayName: incoming.displayName ?? existing.displayName,
|
|
85
|
+
platform: incoming.platform ?? existing.platform,
|
|
86
|
+
clientId: incoming.clientId ?? existing.clientId,
|
|
87
|
+
clientMode: incoming.clientMode ?? existing.clientMode,
|
|
88
|
+
role: existingRole ?? incomingRole ?? undefined,
|
|
89
|
+
roles: mergeRoles(existing.roles, existing.role, incoming.role),
|
|
90
|
+
scopes: mergeScopes(existing.scopes, incoming.scopes),
|
|
91
|
+
remoteIp: incoming.remoteIp ?? existing.remoteIp,
|
|
92
|
+
// If either request is interactive, keep the pending request visible for approval.
|
|
93
|
+
silent: Boolean(existing.silent && incoming.silent),
|
|
94
|
+
isRepair: existing.isRepair || isRepair,
|
|
95
|
+
ts: Date.now(),
|
|
96
|
+
};
|
|
139
97
|
}
|
|
140
98
|
function scopesAllow(requested, allowed) {
|
|
141
|
-
if (requested.length === 0)
|
|
99
|
+
if (requested.length === 0) {
|
|
142
100
|
return true;
|
|
143
|
-
|
|
101
|
+
}
|
|
102
|
+
if (allowed.length === 0) {
|
|
144
103
|
return false;
|
|
104
|
+
}
|
|
145
105
|
const allowedSet = new Set(allowed);
|
|
146
106
|
return requested.every((scope) => allowedSet.has(scope));
|
|
147
107
|
}
|
|
108
|
+
const DEVICE_SCOPE_IMPLICATIONS = {
|
|
109
|
+
"operator.admin": ["operator.read", "operator.write", "operator.approvals", "operator.pairing"],
|
|
110
|
+
"operator.write": ["operator.read"],
|
|
111
|
+
};
|
|
112
|
+
function expandScopeImplications(scopes) {
|
|
113
|
+
const expanded = new Set(scopes);
|
|
114
|
+
const queue = [...scopes];
|
|
115
|
+
while (queue.length > 0) {
|
|
116
|
+
const scope = queue.pop();
|
|
117
|
+
if (!scope) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
for (const impliedScope of DEVICE_SCOPE_IMPLICATIONS[scope] ?? []) {
|
|
121
|
+
if (!expanded.has(impliedScope)) {
|
|
122
|
+
expanded.add(impliedScope);
|
|
123
|
+
queue.push(impliedScope);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return [...expanded];
|
|
128
|
+
}
|
|
129
|
+
function scopesAllowWithImplications(requested, allowed) {
|
|
130
|
+
return scopesAllow(expandScopeImplications(requested), expandScopeImplications(allowed));
|
|
131
|
+
}
|
|
148
132
|
function newToken() {
|
|
149
|
-
return
|
|
133
|
+
return generatePairingToken();
|
|
134
|
+
}
|
|
135
|
+
function getPairedDeviceFromState(state, deviceId) {
|
|
136
|
+
return state.pairedByDeviceId[normalizeDeviceId(deviceId)] ?? null;
|
|
137
|
+
}
|
|
138
|
+
function cloneDeviceTokens(device) {
|
|
139
|
+
return device.tokens ? { ...device.tokens } : {};
|
|
140
|
+
}
|
|
141
|
+
function buildDeviceAuthToken(params) {
|
|
142
|
+
return {
|
|
143
|
+
token: newToken(),
|
|
144
|
+
role: params.role,
|
|
145
|
+
scopes: params.scopes,
|
|
146
|
+
createdAtMs: params.existing?.createdAtMs ?? params.now,
|
|
147
|
+
rotatedAtMs: params.rotatedAtMs,
|
|
148
|
+
revokedAtMs: undefined,
|
|
149
|
+
lastUsedAtMs: params.existing?.lastUsedAtMs,
|
|
150
|
+
};
|
|
150
151
|
}
|
|
151
152
|
export async function listDevicePairing(baseDir) {
|
|
152
153
|
const state = await loadState(baseDir);
|
|
153
|
-
const pending = Object.values(state.pendingById).
|
|
154
|
-
const paired = Object.values(state.pairedByDeviceId).
|
|
154
|
+
const pending = Object.values(state.pendingById).toSorted((a, b) => b.ts - a.ts);
|
|
155
|
+
const paired = Object.values(state.pairedByDeviceId).toSorted((a, b) => b.approvedAtMs - a.approvedAtMs);
|
|
155
156
|
return { pending, paired };
|
|
156
157
|
}
|
|
157
158
|
export async function getPairedDevice(deviceId, baseDir) {
|
|
@@ -165,11 +166,14 @@ export async function requestDevicePairing(req, baseDir) {
|
|
|
165
166
|
if (!deviceId) {
|
|
166
167
|
throw new Error("deviceId required");
|
|
167
168
|
}
|
|
168
|
-
const
|
|
169
|
+
const isRepair = Boolean(state.pairedByDeviceId[deviceId]);
|
|
170
|
+
const existing = Object.values(state.pendingById).find((pending) => pending.deviceId === deviceId);
|
|
169
171
|
if (existing) {
|
|
170
|
-
|
|
172
|
+
const merged = mergePendingDevicePairingRequest(existing, req, isRepair);
|
|
173
|
+
state.pendingById[existing.requestId] = merged;
|
|
174
|
+
await persistState(state, baseDir);
|
|
175
|
+
return { status: "pending", request: merged, created: false };
|
|
171
176
|
}
|
|
172
|
-
const isRepair = Boolean(state.pairedByDeviceId[deviceId]);
|
|
173
177
|
const request = {
|
|
174
178
|
requestId: randomUUID(),
|
|
175
179
|
deviceId,
|
|
@@ -195,17 +199,24 @@ export async function approveDevicePairing(requestId, baseDir) {
|
|
|
195
199
|
return await withLock(async () => {
|
|
196
200
|
const state = await loadState(baseDir);
|
|
197
201
|
const pending = state.pendingById[requestId];
|
|
198
|
-
if (!pending)
|
|
202
|
+
if (!pending) {
|
|
199
203
|
return null;
|
|
204
|
+
}
|
|
200
205
|
const now = Date.now();
|
|
201
206
|
const existing = state.pairedByDeviceId[pending.deviceId];
|
|
202
207
|
const roles = mergeRoles(existing?.roles, existing?.role, pending.roles, pending.role);
|
|
203
|
-
const
|
|
208
|
+
const approvedScopes = mergeScopes(existing?.approvedScopes ?? existing?.scopes, pending.scopes);
|
|
204
209
|
const tokens = existing?.tokens ? { ...existing.tokens } : {};
|
|
205
210
|
const roleForToken = normalizeRole(pending.role);
|
|
206
211
|
if (roleForToken) {
|
|
207
|
-
const nextScopes = normalizeScopes(pending.scopes);
|
|
208
212
|
const existingToken = tokens[roleForToken];
|
|
213
|
+
const requestedScopes = normalizeDeviceAuthScopes(pending.scopes);
|
|
214
|
+
const nextScopes = requestedScopes.length > 0
|
|
215
|
+
? requestedScopes
|
|
216
|
+
: normalizeDeviceAuthScopes(existingToken?.scopes ??
|
|
217
|
+
approvedScopes ??
|
|
218
|
+
existing?.approvedScopes ??
|
|
219
|
+
existing?.scopes);
|
|
209
220
|
const now = Date.now();
|
|
210
221
|
tokens[roleForToken] = {
|
|
211
222
|
token: newToken(),
|
|
@@ -226,7 +237,8 @@ export async function approveDevicePairing(requestId, baseDir) {
|
|
|
226
237
|
clientMode: pending.clientMode,
|
|
227
238
|
role: pending.role,
|
|
228
239
|
roles,
|
|
229
|
-
scopes,
|
|
240
|
+
scopes: approvedScopes,
|
|
241
|
+
approvedScopes,
|
|
230
242
|
remoteIp: pending.remoteIp,
|
|
231
243
|
tokens,
|
|
232
244
|
createdAtMs: existing?.createdAtMs ?? now,
|
|
@@ -242,19 +254,33 @@ export async function rejectDevicePairing(requestId, baseDir) {
|
|
|
242
254
|
return await withLock(async () => {
|
|
243
255
|
const state = await loadState(baseDir);
|
|
244
256
|
const pending = state.pendingById[requestId];
|
|
245
|
-
if (!pending)
|
|
257
|
+
if (!pending) {
|
|
246
258
|
return null;
|
|
259
|
+
}
|
|
247
260
|
delete state.pendingById[requestId];
|
|
248
261
|
await persistState(state, baseDir);
|
|
249
262
|
return { requestId, deviceId: pending.deviceId };
|
|
250
263
|
});
|
|
251
264
|
}
|
|
265
|
+
export async function removePairedDevice(deviceId, baseDir) {
|
|
266
|
+
return await withLock(async () => {
|
|
267
|
+
const state = await loadState(baseDir);
|
|
268
|
+
const normalized = normalizeDeviceId(deviceId);
|
|
269
|
+
if (!normalized || !state.pairedByDeviceId[normalized]) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
delete state.pairedByDeviceId[normalized];
|
|
273
|
+
await persistState(state, baseDir);
|
|
274
|
+
return { deviceId: normalized };
|
|
275
|
+
});
|
|
276
|
+
}
|
|
252
277
|
export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
|
|
253
278
|
return await withLock(async () => {
|
|
254
279
|
const state = await loadState(baseDir);
|
|
255
280
|
const existing = state.pairedByDeviceId[normalizeDeviceId(deviceId)];
|
|
256
|
-
if (!existing)
|
|
281
|
+
if (!existing) {
|
|
257
282
|
return;
|
|
283
|
+
}
|
|
258
284
|
const roles = mergeRoles(existing.roles, existing.role, patch.role);
|
|
259
285
|
const scopes = mergeScopes(existing.scopes, patch.scopes);
|
|
260
286
|
state.pairedByDeviceId[deviceId] = {
|
|
@@ -263,6 +289,7 @@ export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
|
|
|
263
289
|
deviceId: existing.deviceId,
|
|
264
290
|
createdAtMs: existing.createdAtMs,
|
|
265
291
|
approvedAtMs: existing.approvedAtMs,
|
|
292
|
+
approvedScopes: existing.approvedScopes,
|
|
266
293
|
role: patch.role ?? existing.role,
|
|
267
294
|
roles,
|
|
268
295
|
scopes,
|
|
@@ -271,8 +298,9 @@ export async function updatePairedDeviceMetadata(deviceId, patch, baseDir) {
|
|
|
271
298
|
});
|
|
272
299
|
}
|
|
273
300
|
export function summarizeDeviceTokens(tokens) {
|
|
274
|
-
if (!tokens)
|
|
301
|
+
if (!tokens) {
|
|
275
302
|
return undefined;
|
|
303
|
+
}
|
|
276
304
|
const summaries = Object.values(tokens)
|
|
277
305
|
.map((token) => ({
|
|
278
306
|
role: token.role,
|
|
@@ -282,27 +310,32 @@ export function summarizeDeviceTokens(tokens) {
|
|
|
282
310
|
revokedAtMs: token.revokedAtMs,
|
|
283
311
|
lastUsedAtMs: token.lastUsedAtMs,
|
|
284
312
|
}))
|
|
285
|
-
.
|
|
313
|
+
.toSorted((a, b) => a.role.localeCompare(b.role));
|
|
286
314
|
return summaries.length > 0 ? summaries : undefined;
|
|
287
315
|
}
|
|
288
316
|
export async function verifyDeviceToken(params) {
|
|
289
317
|
return await withLock(async () => {
|
|
290
318
|
const state = await loadState(params.baseDir);
|
|
291
|
-
const device = state
|
|
292
|
-
if (!device)
|
|
319
|
+
const device = getPairedDeviceFromState(state, params.deviceId);
|
|
320
|
+
if (!device) {
|
|
293
321
|
return { ok: false, reason: "device-not-paired" };
|
|
322
|
+
}
|
|
294
323
|
const role = normalizeRole(params.role);
|
|
295
|
-
if (!role)
|
|
324
|
+
if (!role) {
|
|
296
325
|
return { ok: false, reason: "role-missing" };
|
|
326
|
+
}
|
|
297
327
|
const entry = device.tokens?.[role];
|
|
298
|
-
if (!entry)
|
|
328
|
+
if (!entry) {
|
|
299
329
|
return { ok: false, reason: "token-missing" };
|
|
300
|
-
|
|
330
|
+
}
|
|
331
|
+
if (entry.revokedAtMs) {
|
|
301
332
|
return { ok: false, reason: "token-revoked" };
|
|
302
|
-
|
|
333
|
+
}
|
|
334
|
+
if (!verifyPairingToken(params.token, entry.token)) {
|
|
303
335
|
return { ok: false, reason: "token-mismatch" };
|
|
304
|
-
|
|
305
|
-
|
|
336
|
+
}
|
|
337
|
+
const requestedScopes = normalizeDeviceAuthScopes(params.scopes);
|
|
338
|
+
if (!roleScopesAllow({ role, requestedScopes, allowedScopes: entry.scopes })) {
|
|
306
339
|
return { ok: false, reason: "scope-mismatch" };
|
|
307
340
|
}
|
|
308
341
|
entry.lastUsedAtMs = Date.now();
|
|
@@ -316,30 +349,29 @@ export async function verifyDeviceToken(params) {
|
|
|
316
349
|
export async function ensureDeviceToken(params) {
|
|
317
350
|
return await withLock(async () => {
|
|
318
351
|
const state = await loadState(params.baseDir);
|
|
319
|
-
const
|
|
320
|
-
|
|
352
|
+
const requestedScopes = normalizeDeviceAuthScopes(params.scopes);
|
|
353
|
+
const context = resolveDeviceTokenUpdateContext({
|
|
354
|
+
state,
|
|
355
|
+
deviceId: params.deviceId,
|
|
356
|
+
role: params.role,
|
|
357
|
+
});
|
|
358
|
+
if (!context) {
|
|
321
359
|
return null;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return null;
|
|
325
|
-
const requestedScopes = normalizeScopes(params.scopes);
|
|
326
|
-
const tokens = device.tokens ? { ...device.tokens } : {};
|
|
327
|
-
const existing = tokens[role];
|
|
360
|
+
}
|
|
361
|
+
const { device, role, tokens, existing } = context;
|
|
328
362
|
if (existing && !existing.revokedAtMs) {
|
|
329
|
-
if (
|
|
363
|
+
if (roleScopesAllow({ role, requestedScopes, allowedScopes: existing.scopes })) {
|
|
330
364
|
return existing;
|
|
331
365
|
}
|
|
332
366
|
}
|
|
333
367
|
const now = Date.now();
|
|
334
|
-
const next = {
|
|
335
|
-
token: newToken(),
|
|
368
|
+
const next = buildDeviceAuthToken({
|
|
336
369
|
role,
|
|
337
370
|
scopes: requestedScopes,
|
|
338
|
-
|
|
371
|
+
existing,
|
|
372
|
+
now,
|
|
339
373
|
rotatedAtMs: existing ? now : undefined,
|
|
340
|
-
|
|
341
|
-
lastUsedAtMs: existing?.lastUsedAtMs,
|
|
342
|
-
};
|
|
374
|
+
});
|
|
343
375
|
tokens[role] = next;
|
|
344
376
|
device.tokens = tokens;
|
|
345
377
|
state.pairedByDeviceId[device.deviceId] = device;
|
|
@@ -347,33 +379,46 @@ export async function ensureDeviceToken(params) {
|
|
|
347
379
|
return next;
|
|
348
380
|
});
|
|
349
381
|
}
|
|
382
|
+
function resolveDeviceTokenUpdateContext(params) {
|
|
383
|
+
const device = getPairedDeviceFromState(params.state, params.deviceId);
|
|
384
|
+
if (!device) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const role = normalizeRole(params.role);
|
|
388
|
+
if (!role) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
const tokens = cloneDeviceTokens(device);
|
|
392
|
+
const existing = tokens[role];
|
|
393
|
+
return { device, role, tokens, existing };
|
|
394
|
+
}
|
|
350
395
|
export async function rotateDeviceToken(params) {
|
|
351
396
|
return await withLock(async () => {
|
|
352
397
|
const state = await loadState(params.baseDir);
|
|
353
|
-
const
|
|
354
|
-
|
|
398
|
+
const context = resolveDeviceTokenUpdateContext({
|
|
399
|
+
state,
|
|
400
|
+
deviceId: params.deviceId,
|
|
401
|
+
role: params.role,
|
|
402
|
+
});
|
|
403
|
+
if (!context) {
|
|
355
404
|
return null;
|
|
356
|
-
|
|
357
|
-
|
|
405
|
+
}
|
|
406
|
+
const { device, role, tokens, existing } = context;
|
|
407
|
+
const requestedScopes = normalizeDeviceAuthScopes(params.scopes ?? existing?.scopes ?? device.scopes);
|
|
408
|
+
const approvedScopes = normalizeDeviceAuthScopes(device.approvedScopes ?? device.scopes ?? existing?.scopes);
|
|
409
|
+
if (!scopesAllowWithImplications(requestedScopes, approvedScopes)) {
|
|
358
410
|
return null;
|
|
359
|
-
|
|
360
|
-
const existing = tokens[role];
|
|
361
|
-
const requestedScopes = normalizeScopes(params.scopes ?? existing?.scopes ?? device.scopes);
|
|
411
|
+
}
|
|
362
412
|
const now = Date.now();
|
|
363
|
-
const next = {
|
|
364
|
-
token: newToken(),
|
|
413
|
+
const next = buildDeviceAuthToken({
|
|
365
414
|
role,
|
|
366
415
|
scopes: requestedScopes,
|
|
367
|
-
|
|
416
|
+
existing,
|
|
417
|
+
now,
|
|
368
418
|
rotatedAtMs: now,
|
|
369
|
-
|
|
370
|
-
lastUsedAtMs: existing?.lastUsedAtMs,
|
|
371
|
-
};
|
|
419
|
+
});
|
|
372
420
|
tokens[role] = next;
|
|
373
421
|
device.tokens = tokens;
|
|
374
|
-
if (params.scopes !== undefined) {
|
|
375
|
-
device.scopes = requestedScopes;
|
|
376
|
-
}
|
|
377
422
|
state.pairedByDeviceId[device.deviceId] = device;
|
|
378
423
|
await persistState(state, params.baseDir);
|
|
379
424
|
return next;
|
|
@@ -383,13 +428,16 @@ export async function revokeDeviceToken(params) {
|
|
|
383
428
|
return await withLock(async () => {
|
|
384
429
|
const state = await loadState(params.baseDir);
|
|
385
430
|
const device = state.pairedByDeviceId[normalizeDeviceId(params.deviceId)];
|
|
386
|
-
if (!device)
|
|
431
|
+
if (!device) {
|
|
387
432
|
return null;
|
|
433
|
+
}
|
|
388
434
|
const role = normalizeRole(params.role);
|
|
389
|
-
if (!role)
|
|
435
|
+
if (!role) {
|
|
390
436
|
return null;
|
|
391
|
-
|
|
437
|
+
}
|
|
438
|
+
if (!device.tokens?.[role]) {
|
|
392
439
|
return null;
|
|
440
|
+
}
|
|
393
441
|
const tokens = { ...device.tokens };
|
|
394
442
|
const entry = { ...tokens[role], revokedAtMs: Date.now() };
|
|
395
443
|
tokens[role] = entry;
|
|
@@ -399,3 +447,15 @@ export async function revokeDeviceToken(params) {
|
|
|
399
447
|
return entry;
|
|
400
448
|
});
|
|
401
449
|
}
|
|
450
|
+
export async function clearDevicePairing(deviceId, baseDir) {
|
|
451
|
+
return await withLock(async () => {
|
|
452
|
+
const state = await loadState(baseDir);
|
|
453
|
+
const normalizedId = normalizeDeviceId(deviceId);
|
|
454
|
+
if (!state.pairedByDeviceId[normalizedId]) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
delete state.pairedByDeviceId[normalizedId];
|
|
458
|
+
await persistState(state, baseDir);
|
|
459
|
+
return true;
|
|
460
|
+
});
|
|
461
|
+
}
|
package/dist/infra/env.js
CHANGED
|
@@ -3,21 +3,26 @@ import { parseBooleanValue } from "../utils/boolean.js";
|
|
|
3
3
|
const log = createSubsystemLogger("env");
|
|
4
4
|
const loggedEnv = new Set();
|
|
5
5
|
function formatEnvValue(value, redact) {
|
|
6
|
-
if (redact)
|
|
6
|
+
if (redact) {
|
|
7
7
|
return "<redacted>";
|
|
8
|
+
}
|
|
8
9
|
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
9
|
-
if (singleLine.length <= 160)
|
|
10
|
+
if (singleLine.length <= 160) {
|
|
10
11
|
return singleLine;
|
|
12
|
+
}
|
|
11
13
|
return `${singleLine.slice(0, 160)}…`;
|
|
12
14
|
}
|
|
13
15
|
export function logAcceptedEnvOption(option) {
|
|
14
|
-
if (process.env.VITEST || process.env.NODE_ENV === "test")
|
|
16
|
+
if (process.env.VITEST || process.env.NODE_ENV === "test") {
|
|
15
17
|
return;
|
|
16
|
-
|
|
18
|
+
}
|
|
19
|
+
if (loggedEnv.has(option.key)) {
|
|
17
20
|
return;
|
|
21
|
+
}
|
|
18
22
|
const rawValue = option.value ?? process.env[option.key];
|
|
19
|
-
if (!rawValue || !rawValue.trim())
|
|
23
|
+
if (!rawValue || !rawValue.trim()) {
|
|
20
24
|
return;
|
|
25
|
+
}
|
|
21
26
|
loggedEnv.add(option.key);
|
|
22
27
|
log.info(`env: ${option.key}=${formatEnvValue(rawValue, option.redact)} (${option.description})`);
|
|
23
28
|
}
|