@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
package/dist/hooks/workspace.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { MANIFEST_KEY } from "../compat/legacy-names.js";
|
|
4
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
|
+
import { isPathInsideWithRealpath } from "../security/scan-paths.js";
|
|
4
6
|
import { CONFIG_DIR, resolveUserPath } from "../utils.js";
|
|
5
7
|
import { resolveBundledHooksDir } from "./bundled-dir.js";
|
|
6
8
|
import { shouldIncludeHook } from "./config.js";
|
|
7
9
|
import { parseFrontmatter, resolvePoolbotMetadata, resolveHookInvocationPolicy, } from "./frontmatter.js";
|
|
10
|
+
const log = createSubsystemLogger("hooks/workspace");
|
|
8
11
|
function filterHookEntries(entries, config, eligibility) {
|
|
9
12
|
return entries.filter((entry) => shouldIncludeHook({ entry, config, eligibility }));
|
|
10
13
|
}
|
|
11
14
|
function readHookPackageManifest(dir) {
|
|
12
15
|
const manifestPath = path.join(dir, "package.json");
|
|
13
|
-
if (!fs.existsSync(manifestPath))
|
|
16
|
+
if (!fs.existsSync(manifestPath)) {
|
|
14
17
|
return null;
|
|
18
|
+
}
|
|
15
19
|
try {
|
|
16
20
|
const raw = fs.readFileSync(manifestPath, "utf-8");
|
|
17
21
|
return JSON.parse(raw);
|
|
@@ -21,15 +25,27 @@ function readHookPackageManifest(dir) {
|
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
function resolvePackageHooks(manifest) {
|
|
24
|
-
const raw = manifest
|
|
25
|
-
if (!Array.isArray(raw))
|
|
28
|
+
const raw = manifest[MANIFEST_KEY]?.hooks;
|
|
29
|
+
if (!Array.isArray(raw)) {
|
|
26
30
|
return [];
|
|
31
|
+
}
|
|
27
32
|
return raw.map((entry) => (typeof entry === "string" ? entry.trim() : "")).filter(Boolean);
|
|
28
33
|
}
|
|
34
|
+
function resolveContainedDir(baseDir, targetDir) {
|
|
35
|
+
const base = path.resolve(baseDir);
|
|
36
|
+
const resolved = path.resolve(baseDir, targetDir);
|
|
37
|
+
if (!isPathInsideWithRealpath(base, resolved, {
|
|
38
|
+
requireRealpath: true,
|
|
39
|
+
})) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return resolved;
|
|
43
|
+
}
|
|
29
44
|
function loadHookFromDir(params) {
|
|
30
45
|
const hookMdPath = path.join(params.hookDir, "HOOK.md");
|
|
31
|
-
if (!fs.existsSync(hookMdPath))
|
|
46
|
+
if (!fs.existsSync(hookMdPath)) {
|
|
32
47
|
return null;
|
|
48
|
+
}
|
|
33
49
|
try {
|
|
34
50
|
const content = fs.readFileSync(hookMdPath, "utf-8");
|
|
35
51
|
const frontmatter = parseFrontmatter(content);
|
|
@@ -45,7 +61,7 @@ function loadHookFromDir(params) {
|
|
|
45
61
|
}
|
|
46
62
|
}
|
|
47
63
|
if (!handlerPath) {
|
|
48
|
-
|
|
64
|
+
log.warn(`Hook "${name}" has HOOK.md but no handler file in ${params.hookDir}`);
|
|
49
65
|
return null;
|
|
50
66
|
}
|
|
51
67
|
return {
|
|
@@ -59,7 +75,8 @@ function loadHookFromDir(params) {
|
|
|
59
75
|
};
|
|
60
76
|
}
|
|
61
77
|
catch (err) {
|
|
62
|
-
|
|
78
|
+
const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
79
|
+
log.warn(`Failed to load hook from ${params.hookDir}: ${message}`);
|
|
63
80
|
return null;
|
|
64
81
|
}
|
|
65
82
|
}
|
|
@@ -68,30 +85,38 @@ function loadHookFromDir(params) {
|
|
|
68
85
|
*/
|
|
69
86
|
function loadHooksFromDir(params) {
|
|
70
87
|
const { dir, source, pluginId } = params;
|
|
71
|
-
if (!fs.existsSync(dir))
|
|
88
|
+
if (!fs.existsSync(dir)) {
|
|
72
89
|
return [];
|
|
90
|
+
}
|
|
73
91
|
const stat = fs.statSync(dir);
|
|
74
|
-
if (!stat.isDirectory())
|
|
92
|
+
if (!stat.isDirectory()) {
|
|
75
93
|
return [];
|
|
94
|
+
}
|
|
76
95
|
const hooks = [];
|
|
77
96
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
78
97
|
for (const entry of entries) {
|
|
79
|
-
if (!entry.isDirectory())
|
|
98
|
+
if (!entry.isDirectory()) {
|
|
80
99
|
continue;
|
|
100
|
+
}
|
|
81
101
|
const hookDir = path.join(dir, entry.name);
|
|
82
102
|
const manifest = readHookPackageManifest(hookDir);
|
|
83
103
|
const packageHooks = manifest ? resolvePackageHooks(manifest) : [];
|
|
84
104
|
if (packageHooks.length > 0) {
|
|
85
105
|
for (const hookPath of packageHooks) {
|
|
86
|
-
const resolvedHookDir =
|
|
106
|
+
const resolvedHookDir = resolveContainedDir(hookDir, hookPath);
|
|
107
|
+
if (!resolvedHookDir) {
|
|
108
|
+
log.warn(`Ignoring out-of-package hook path "${hookPath}" in ${hookDir} (must be within package directory)`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
87
111
|
const hook = loadHookFromDir({
|
|
88
112
|
hookDir: resolvedHookDir,
|
|
89
113
|
source,
|
|
90
114
|
pluginId,
|
|
91
115
|
nameHint: path.basename(resolvedHookDir),
|
|
92
116
|
});
|
|
93
|
-
if (hook)
|
|
117
|
+
if (hook) {
|
|
94
118
|
hooks.push(hook);
|
|
119
|
+
}
|
|
95
120
|
}
|
|
96
121
|
continue;
|
|
97
122
|
}
|
|
@@ -101,8 +126,9 @@ function loadHooksFromDir(params) {
|
|
|
101
126
|
pluginId,
|
|
102
127
|
nameHint: entry.name,
|
|
103
128
|
});
|
|
104
|
-
if (hook)
|
|
129
|
+
if (hook) {
|
|
105
130
|
hooks.push(hook);
|
|
131
|
+
}
|
|
106
132
|
}
|
|
107
133
|
return hooks;
|
|
108
134
|
}
|
|
@@ -165,14 +191,18 @@ function loadHookEntries(workspaceDir, opts) {
|
|
|
165
191
|
});
|
|
166
192
|
const merged = new Map();
|
|
167
193
|
// Precedence: extra < bundled < managed < workspace (workspace wins)
|
|
168
|
-
for (const hook of extraHooks)
|
|
194
|
+
for (const hook of extraHooks) {
|
|
169
195
|
merged.set(hook.name, hook);
|
|
170
|
-
|
|
196
|
+
}
|
|
197
|
+
for (const hook of bundledHooks) {
|
|
171
198
|
merged.set(hook.name, hook);
|
|
172
|
-
|
|
199
|
+
}
|
|
200
|
+
for (const hook of managedHooks) {
|
|
173
201
|
merged.set(hook.name, hook);
|
|
174
|
-
|
|
202
|
+
}
|
|
203
|
+
for (const hook of workspaceHooks) {
|
|
175
204
|
merged.set(hook.name, hook);
|
|
205
|
+
}
|
|
176
206
|
return Array.from(merged.values()).map((hook) => {
|
|
177
207
|
let frontmatter = {};
|
|
178
208
|
try {
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return Object.keys(accounts).filter(Boolean);
|
|
7
|
-
}
|
|
8
|
-
export function listIMessageAccountIds(cfg) {
|
|
9
|
-
const ids = listConfiguredAccountIds(cfg);
|
|
10
|
-
if (ids.length === 0)
|
|
11
|
-
return [DEFAULT_ACCOUNT_ID];
|
|
12
|
-
return ids.sort((a, b) => a.localeCompare(b));
|
|
13
|
-
}
|
|
14
|
-
export function resolveDefaultIMessageAccountId(cfg) {
|
|
15
|
-
const ids = listIMessageAccountIds(cfg);
|
|
16
|
-
if (ids.includes(DEFAULT_ACCOUNT_ID))
|
|
17
|
-
return DEFAULT_ACCOUNT_ID;
|
|
18
|
-
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
19
|
-
}
|
|
1
|
+
import { createAccountListHelpers } from "../channels/plugins/account-helpers.js";
|
|
2
|
+
import { normalizeAccountId } from "../routing/session-key.js";
|
|
3
|
+
const { listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("imessage");
|
|
4
|
+
export const listIMessageAccountIds = listAccountIds;
|
|
5
|
+
export const resolveDefaultIMessageAccountId = resolveDefaultAccountId;
|
|
20
6
|
function resolveAccountConfig(cfg, accountId) {
|
|
21
7
|
const accounts = cfg.channels?.imessage?.accounts;
|
|
22
|
-
if (!accounts || typeof accounts !== "object")
|
|
8
|
+
if (!accounts || typeof accounts !== "object") {
|
|
23
9
|
return undefined;
|
|
10
|
+
}
|
|
24
11
|
return accounts[accountId];
|
|
25
12
|
}
|
|
26
13
|
function mergeIMessageAccountConfig(cfg, accountId) {
|
|
@@ -43,6 +30,8 @@ export function resolveIMessageAccount(params) {
|
|
|
43
30
|
merged.dmPolicy ||
|
|
44
31
|
merged.groupPolicy ||
|
|
45
32
|
typeof merged.includeAttachments === "boolean" ||
|
|
33
|
+
(merged.attachmentRoots && merged.attachmentRoots.length > 0) ||
|
|
34
|
+
(merged.remoteAttachmentRoots && merged.remoteAttachmentRoots.length > 0) ||
|
|
46
35
|
typeof merged.mediaMaxMb === "number" ||
|
|
47
36
|
typeof merged.textChunkLimit === "number" ||
|
|
48
37
|
(merged.groups && Object.keys(merged.groups).length > 0));
|
|
@@ -63,7 +63,8 @@ export function resolveIMessageInboundDecision(params) {
|
|
|
63
63
|
return { kind: "drop", reason: "group without chat_id" };
|
|
64
64
|
}
|
|
65
65
|
const groupId = isGroup ? groupIdCandidate : undefined;
|
|
66
|
-
const
|
|
66
|
+
const storeAllowFrom = params.dmPolicy === "allowlist" ? [] : params.storeAllowFrom;
|
|
67
|
+
const effectiveDmAllowFrom = Array.from(new Set([...params.allowFrom, ...storeAllowFrom]))
|
|
67
68
|
.map((v) => String(v).trim())
|
|
68
69
|
.filter(Boolean);
|
|
69
70
|
// Keep DM pairing-store authorization scoped to DMs; group access must come from explicit group allowlist config.
|
package/dist/infra/archive.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { Readable, Transform } from "node:stream";
|
|
5
5
|
import { pipeline } from "node:stream/promises";
|
|
6
6
|
import JSZip from "jszip";
|
|
7
7
|
import * as tar from "tar";
|
|
8
|
-
import {
|
|
8
|
+
import { resolveArchiveOutputPath, stripArchivePath, validateArchiveEntryPath, } from "./archive-path.js";
|
|
9
|
+
import { isNotFoundPathError, isPathInside, isSymlinkOpenError } from "./path-guards.js";
|
|
10
|
+
export class ArchiveSecurityError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message, options) {
|
|
13
|
+
super(message, options);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = "ArchiveSecurityError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
9
18
|
/** @internal */
|
|
10
19
|
export const DEFAULT_MAX_ARCHIVE_BYTES_ZIP = 256 * 1024 * 1024;
|
|
11
20
|
/** @internal */
|
|
@@ -18,7 +27,12 @@ const ERROR_ARCHIVE_SIZE_EXCEEDS_LIMIT = "archive size exceeds limit";
|
|
|
18
27
|
const ERROR_ARCHIVE_ENTRY_COUNT_EXCEEDS_LIMIT = "archive entry count exceeds limit";
|
|
19
28
|
const ERROR_ARCHIVE_ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT = "archive entry extracted size exceeds limit";
|
|
20
29
|
const ERROR_ARCHIVE_EXTRACTED_SIZE_EXCEEDS_LIMIT = "archive extracted size exceeds limit";
|
|
30
|
+
const ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK = "archive entry traverses symlink in destination";
|
|
21
31
|
const TAR_SUFFIXES = [".tgz", ".tar.gz", ".tar"];
|
|
32
|
+
const OPEN_WRITE_FLAGS = fsConstants.O_WRONLY |
|
|
33
|
+
fsConstants.O_CREAT |
|
|
34
|
+
fsConstants.O_TRUNC |
|
|
35
|
+
(process.platform !== "win32" && "O_NOFOLLOW" in fsConstants ? fsConstants.O_NOFOLLOW : 0);
|
|
22
36
|
export function resolveArchiveKind(filePath) {
|
|
23
37
|
const lower = filePath.toLowerCase();
|
|
24
38
|
if (lower.endsWith(".zip")) {
|
|
@@ -67,53 +81,6 @@ export async function withTimeout(promise, timeoutMs, label) {
|
|
|
67
81
|
}
|
|
68
82
|
}
|
|
69
83
|
}
|
|
70
|
-
// Path hygiene.
|
|
71
|
-
function normalizeArchivePath(raw) {
|
|
72
|
-
// Archives may contain Windows separators; treat them as separators.
|
|
73
|
-
return raw.replaceAll("\\", "/");
|
|
74
|
-
}
|
|
75
|
-
function isWindowsDrivePath(p) {
|
|
76
|
-
return /^[a-zA-Z]:[\\/]/.test(p);
|
|
77
|
-
}
|
|
78
|
-
function validateArchiveEntryPath(entryPath) {
|
|
79
|
-
if (!entryPath || entryPath === "." || entryPath === "./") {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (isWindowsDrivePath(entryPath)) {
|
|
83
|
-
throw new Error(`archive entry uses a drive path: ${entryPath}`);
|
|
84
|
-
}
|
|
85
|
-
const normalized = path.posix.normalize(normalizeArchivePath(entryPath));
|
|
86
|
-
if (normalized === ".." || normalized.startsWith("../")) {
|
|
87
|
-
throw new Error(`archive entry escapes destination: ${entryPath}`);
|
|
88
|
-
}
|
|
89
|
-
if (path.posix.isAbsolute(normalized) || normalized.startsWith("//")) {
|
|
90
|
-
throw new Error(`archive entry is absolute: ${entryPath}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function stripArchivePath(entryPath, stripComponents) {
|
|
94
|
-
const raw = normalizeArchivePath(entryPath);
|
|
95
|
-
if (!raw || raw === "." || raw === "./") {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
// Important: mimic tar --strip-components semantics (raw segments before
|
|
99
|
-
// normalization) so strip-induced escapes like "a/../b" are not hidden.
|
|
100
|
-
const parts = raw.split("/").filter((part) => part.length > 0 && part !== ".");
|
|
101
|
-
const strip = Math.max(0, Math.floor(stripComponents));
|
|
102
|
-
const stripped = strip === 0 ? parts.join("/") : parts.slice(strip).join("/");
|
|
103
|
-
const result = path.posix.normalize(stripped);
|
|
104
|
-
if (!result || result === "." || result === "./") {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return result;
|
|
108
|
-
}
|
|
109
|
-
function resolveCheckedOutPath(destDir, relPath, original) {
|
|
110
|
-
const safeBase = resolveSafeBaseDir(destDir);
|
|
111
|
-
const outPath = path.resolve(destDir, relPath);
|
|
112
|
-
if (!outPath.startsWith(safeBase)) {
|
|
113
|
-
throw new Error(`archive entry escapes destination: ${original}`);
|
|
114
|
-
}
|
|
115
|
-
return outPath;
|
|
116
|
-
}
|
|
117
84
|
function clampLimit(value) {
|
|
118
85
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
119
86
|
return undefined;
|
|
@@ -181,6 +148,80 @@ function createExtractBudgetTransform(params) {
|
|
|
181
148
|
},
|
|
182
149
|
});
|
|
183
150
|
}
|
|
151
|
+
function symlinkTraversalError(originalPath) {
|
|
152
|
+
return new ArchiveSecurityError("destination-symlink-traversal", `${ERROR_ARCHIVE_ENTRY_TRAVERSES_SYMLINK}: ${originalPath}`);
|
|
153
|
+
}
|
|
154
|
+
async function assertDestinationDirReady(destDir) {
|
|
155
|
+
const stat = await fs.lstat(destDir);
|
|
156
|
+
if (stat.isSymbolicLink()) {
|
|
157
|
+
throw new ArchiveSecurityError("destination-symlink", "archive destination is a symlink");
|
|
158
|
+
}
|
|
159
|
+
if (!stat.isDirectory()) {
|
|
160
|
+
throw new ArchiveSecurityError("destination-not-directory", "archive destination is not a directory");
|
|
161
|
+
}
|
|
162
|
+
return await fs.realpath(destDir);
|
|
163
|
+
}
|
|
164
|
+
async function assertNoSymlinkTraversal(params) {
|
|
165
|
+
const parts = params.relPath.split("/").filter(Boolean);
|
|
166
|
+
let current = path.resolve(params.rootDir);
|
|
167
|
+
for (const part of parts) {
|
|
168
|
+
current = path.join(current, part);
|
|
169
|
+
let stat;
|
|
170
|
+
try {
|
|
171
|
+
stat = await fs.lstat(current);
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
if (isNotFoundPathError(err)) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
if (stat.isSymbolicLink()) {
|
|
180
|
+
throw symlinkTraversalError(params.originalPath);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function assertResolvedInsideDestination(params) {
|
|
185
|
+
let resolved;
|
|
186
|
+
try {
|
|
187
|
+
resolved = await fs.realpath(params.targetPath);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
if (isNotFoundPathError(err)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
if (!isPathInside(params.destinationRealDir, resolved)) {
|
|
196
|
+
throw symlinkTraversalError(params.originalPath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function openZipOutputFile(outPath, originalPath) {
|
|
200
|
+
try {
|
|
201
|
+
return await fs.open(outPath, OPEN_WRITE_FLAGS, 0o666);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
if (isSymlinkOpenError(err)) {
|
|
205
|
+
throw symlinkTraversalError(originalPath);
|
|
206
|
+
}
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function cleanupPartialRegularFile(filePath) {
|
|
211
|
+
let stat;
|
|
212
|
+
try {
|
|
213
|
+
stat = await fs.lstat(filePath);
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
if (isNotFoundPathError(err)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
if (stat.isFile()) {
|
|
222
|
+
await fs.unlink(filePath).catch(() => undefined);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
184
225
|
async function readZipEntryStream(entry) {
|
|
185
226
|
if (typeof entry.nodeStream === "function") {
|
|
186
227
|
return entry.nodeStream();
|
|
@@ -189,8 +230,68 @@ async function readZipEntryStream(entry) {
|
|
|
189
230
|
const buf = await entry.async("nodebuffer");
|
|
190
231
|
return Readable.from(buf);
|
|
191
232
|
}
|
|
233
|
+
function resolveZipOutputPath(params) {
|
|
234
|
+
validateArchiveEntryPath(params.entryPath);
|
|
235
|
+
const relPath = stripArchivePath(params.entryPath, params.strip);
|
|
236
|
+
if (!relPath) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
validateArchiveEntryPath(relPath);
|
|
240
|
+
return {
|
|
241
|
+
relPath,
|
|
242
|
+
outPath: resolveArchiveOutputPath({
|
|
243
|
+
rootDir: params.destinationDir,
|
|
244
|
+
relPath,
|
|
245
|
+
originalPath: params.entryPath,
|
|
246
|
+
}),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function prepareZipOutputPath(params) {
|
|
250
|
+
await assertNoSymlinkTraversal({
|
|
251
|
+
rootDir: params.destinationDir,
|
|
252
|
+
relPath: params.relPath,
|
|
253
|
+
originalPath: params.originalPath,
|
|
254
|
+
});
|
|
255
|
+
if (params.isDirectory) {
|
|
256
|
+
await fs.mkdir(params.outPath, { recursive: true });
|
|
257
|
+
await assertResolvedInsideDestination({
|
|
258
|
+
destinationRealDir: params.destinationRealDir,
|
|
259
|
+
targetPath: params.outPath,
|
|
260
|
+
originalPath: params.originalPath,
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const parentDir = path.dirname(params.outPath);
|
|
265
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
266
|
+
await assertResolvedInsideDestination({
|
|
267
|
+
destinationRealDir: params.destinationRealDir,
|
|
268
|
+
targetPath: parentDir,
|
|
269
|
+
originalPath: params.originalPath,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async function writeZipFileEntry(params) {
|
|
273
|
+
const handle = await openZipOutputFile(params.outPath, params.entry.name);
|
|
274
|
+
params.budget.startEntry();
|
|
275
|
+
const readable = await readZipEntryStream(params.entry);
|
|
276
|
+
const writable = handle.createWriteStream();
|
|
277
|
+
try {
|
|
278
|
+
await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: params.budget.addBytes }), writable);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
await cleanupPartialRegularFile(params.outPath).catch(() => undefined);
|
|
282
|
+
throw err;
|
|
283
|
+
}
|
|
284
|
+
// Best-effort permission restore for zip entries created on unix.
|
|
285
|
+
if (typeof params.entry.unixPermissions === "number") {
|
|
286
|
+
const mode = params.entry.unixPermissions & 0o777;
|
|
287
|
+
if (mode !== 0) {
|
|
288
|
+
await fs.chmod(params.outPath, mode).catch(() => undefined);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
192
292
|
async function extractZip(params) {
|
|
193
293
|
const limits = resolveExtractLimits(params.limits);
|
|
294
|
+
const destinationRealDir = await assertDestinationDirReady(params.destDir);
|
|
194
295
|
const stat = await fs.stat(params.archivePath);
|
|
195
296
|
if (stat.size > limits.maxArchiveBytes) {
|
|
196
297
|
throw new Error(ERROR_ARCHIVE_SIZE_EXCEEDS_LIMIT);
|
|
@@ -202,34 +303,30 @@ async function extractZip(params) {
|
|
|
202
303
|
assertArchiveEntryCountWithinLimit(entries.length, limits);
|
|
203
304
|
const budget = createByteBudgetTracker(limits);
|
|
204
305
|
for (const entry of entries) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
306
|
+
const output = resolveZipOutputPath({
|
|
307
|
+
entryPath: entry.name,
|
|
308
|
+
strip,
|
|
309
|
+
destinationDir: params.destDir,
|
|
310
|
+
});
|
|
311
|
+
if (!output) {
|
|
208
312
|
continue;
|
|
209
313
|
}
|
|
210
|
-
|
|
211
|
-
|
|
314
|
+
await prepareZipOutputPath({
|
|
315
|
+
destinationDir: params.destDir,
|
|
316
|
+
destinationRealDir,
|
|
317
|
+
relPath: output.relPath,
|
|
318
|
+
outPath: output.outPath,
|
|
319
|
+
originalPath: entry.name,
|
|
320
|
+
isDirectory: entry.dir,
|
|
321
|
+
});
|
|
212
322
|
if (entry.dir) {
|
|
213
|
-
await fs.mkdir(outPath, { recursive: true });
|
|
214
323
|
continue;
|
|
215
324
|
}
|
|
216
|
-
await
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
catch (err) {
|
|
223
|
-
await fs.unlink(outPath).catch(() => undefined);
|
|
224
|
-
throw err;
|
|
225
|
-
}
|
|
226
|
-
// Best-effort permission restore for zip entries created on unix.
|
|
227
|
-
if (typeof entry.unixPermissions === "number") {
|
|
228
|
-
const mode = entry.unixPermissions & 0o777;
|
|
229
|
-
if (mode !== 0) {
|
|
230
|
-
await fs.chmod(outPath, mode).catch(() => undefined);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
325
|
+
await writeZipFileEntry({
|
|
326
|
+
entry,
|
|
327
|
+
outPath: output.outPath,
|
|
328
|
+
budget,
|
|
329
|
+
});
|
|
233
330
|
}
|
|
234
331
|
}
|
|
235
332
|
function readTarEntryInfo(entry) {
|
|
@@ -275,7 +372,11 @@ export async function extractArchive(params) {
|
|
|
275
372
|
return;
|
|
276
373
|
}
|
|
277
374
|
validateArchiveEntryPath(relPath);
|
|
278
|
-
|
|
375
|
+
resolveArchiveOutputPath({
|
|
376
|
+
rootDir: params.destDir,
|
|
377
|
+
relPath,
|
|
378
|
+
originalPath: info.path,
|
|
379
|
+
});
|
|
279
380
|
if (info.type === "SymbolicLink" ||
|
|
280
381
|
info.type === "Link" ||
|
|
281
382
|
info.type === "BlockDevice" ||
|
|
@@ -11,7 +11,10 @@ export function resolveControlUiDistIndexPathForRoot(root) {
|
|
|
11
11
|
export async function resolveControlUiDistIndexHealth(opts = {}) {
|
|
12
12
|
const indexPath = opts.root
|
|
13
13
|
? resolveControlUiDistIndexPathForRoot(opts.root)
|
|
14
|
-
: await resolveControlUiDistIndexPath(
|
|
14
|
+
: await resolveControlUiDistIndexPath({
|
|
15
|
+
argv1: opts.argv1 ?? process.argv[1],
|
|
16
|
+
moduleUrl: opts.moduleUrl,
|
|
17
|
+
});
|
|
15
18
|
return {
|
|
16
19
|
indexPath,
|
|
17
20
|
exists: Boolean(indexPath && fs.existsSync(indexPath)),
|
|
@@ -44,7 +47,9 @@ export function resolveControlUiRepoRoot(argv1 = process.argv[1]) {
|
|
|
44
47
|
}
|
|
45
48
|
return null;
|
|
46
49
|
}
|
|
47
|
-
export async function resolveControlUiDistIndexPath(
|
|
50
|
+
export async function resolveControlUiDistIndexPath(argv1OrOpts) {
|
|
51
|
+
const argv1 = typeof argv1OrOpts === "string" ? argv1OrOpts : (argv1OrOpts?.argv1 ?? process.argv[1]);
|
|
52
|
+
const moduleUrl = typeof argv1OrOpts === "object" ? argv1OrOpts?.moduleUrl : undefined;
|
|
48
53
|
if (!argv1) {
|
|
49
54
|
return null;
|
|
50
55
|
}
|
|
@@ -54,7 +59,7 @@ export async function resolveControlUiDistIndexPath(argv1 = process.argv[1]) {
|
|
|
54
59
|
if (path.basename(distDir) === "dist") {
|
|
55
60
|
return path.join(distDir, "control-ui", "index.html");
|
|
56
61
|
}
|
|
57
|
-
const packageRoot = await resolvePoolBotPackageRoot({ argv1: normalized });
|
|
62
|
+
const packageRoot = await resolvePoolBotPackageRoot({ argv1: normalized, moduleUrl });
|
|
58
63
|
if (packageRoot) {
|
|
59
64
|
return path.join(packageRoot, "dist", "control-ui", "index.html");
|
|
60
65
|
}
|
|
@@ -64,16 +69,19 @@ export async function resolveControlUiDistIndexPath(argv1 = process.argv[1]) {
|
|
|
64
69
|
for (let i = 0; i < 8; i++) {
|
|
65
70
|
const pkgJsonPath = path.join(dir, "package.json");
|
|
66
71
|
const indexPath = path.join(dir, "dist", "control-ui", "index.html");
|
|
67
|
-
if (fs.existsSync(pkgJsonPath)
|
|
72
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
68
73
|
try {
|
|
69
74
|
const raw = fs.readFileSync(pkgJsonPath, "utf-8");
|
|
70
75
|
const parsed = JSON.parse(raw);
|
|
71
76
|
if (parsed.name === "poolbot") {
|
|
72
|
-
return indexPath;
|
|
77
|
+
return fs.existsSync(indexPath) ? indexPath : null;
|
|
73
78
|
}
|
|
79
|
+
// Stop at the first package boundary to avoid resolving through unrelated ancestors.
|
|
80
|
+
return null;
|
|
74
81
|
}
|
|
75
82
|
catch {
|
|
76
|
-
// Invalid package.json
|
|
83
|
+
// Invalid package.json at package boundary; abort fallback resolution.
|
|
84
|
+
return null;
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
87
|
const parent = path.dirname(dir);
|