@poolzin/pool-bot 2026.2.17 → 2026.2.18
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 +17 -0
- package/dist/agents/agent-scope.js +4 -0
- package/dist/agents/announce-idempotency.js +14 -0
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
- package/dist/agents/bash-tools.exec-runtime.js +438 -0
- package/dist/agents/bash-tools.shared.js +6 -0
- package/dist/agents/cli-runner/reliability.js +61 -0
- package/dist/agents/cli-watchdog-defaults.js +11 -0
- package/dist/agents/command-poll-backoff.js +63 -0
- package/dist/agents/current-time.js +16 -0
- package/dist/agents/model-alias-lines.js +18 -0
- package/dist/agents/model-auth-label.js +61 -0
- package/dist/agents/models-config.e2e-harness.js +115 -0
- package/dist/agents/ollama-stream.js +11 -3
- package/dist/agents/openclaw-tools.js +135 -0
- package/dist/agents/pi-auth-json.js +118 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
- package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
- package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
- package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
- package/dist/agents/pi-tools.js +2 -0
- package/dist/agents/queued-file-writer.js +22 -0
- package/dist/agents/sandbox/docker.js +133 -40
- package/dist/agents/sandbox/fs-bridge.js +146 -0
- package/dist/agents/sandbox/fs-paths.js +205 -0
- package/dist/agents/sandbox/hash.js +4 -0
- package/dist/agents/sandbox-paths.js +3 -0
- package/dist/agents/session-dirs.js +20 -0
- package/dist/agents/skills/filter.js +24 -0
- package/dist/agents/skills/tools-dir.js +9 -0
- package/dist/agents/skills-install-download.js +290 -0
- package/dist/agents/skills-install-output.js +30 -0
- package/dist/agents/skills-install.download-test-utils.js +36 -0
- package/dist/agents/skills.e2e-test-helpers.js +13 -0
- package/dist/agents/subagent-announce-queue.js +59 -15
- package/dist/agents/subagent-depth.js +137 -0
- package/dist/agents/subagent-registry.js +448 -96
- package/dist/agents/subagent-spawn.js +262 -0
- package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
- package/dist/agents/tool-display-common.js +782 -0
- package/dist/agents/tools/image-tool.js +1 -1
- package/dist/agents/tools/sessions-access.js +178 -0
- package/dist/agents/tools/sessions-resolution.js +206 -0
- package/dist/agents/tools/subagents-tool.js +616 -0
- package/dist/agents/workspace-dir.js +18 -0
- package/dist/agents/workspace-dirs.js +14 -0
- package/dist/agents/workspace.js +70 -0
- package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
- package/dist/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/reply/commands-mesh.js +245 -0
- package/dist/auto-reply/reply/commands-setunset.js +28 -0
- package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
- package/dist/auto-reply/reply/directive-handling.params.js +1 -0
- package/dist/auto-reply/reply/directive-parsing.js +36 -0
- package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
- package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
- package/dist/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
- package/dist/auto-reply/reply/session-run-accounting.js +33 -0
- package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
- package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
- package/dist/browser/bridge-auth-registry.js +26 -0
- package/dist/browser/client-actions-url.js +10 -0
- package/dist/browser/control-auth.js +73 -0
- package/dist/browser/csrf.js +64 -0
- package/dist/browser/http-auth.js +52 -0
- package/dist/browser/paths.js +37 -0
- package/dist/browser/proxy-files.js +32 -0
- package/dist/browser/pw-ai-state.js +7 -0
- package/dist/browser/resolved-config-refresh.js +42 -0
- package/dist/browser/routes/path-output.js +1 -0
- package/dist/browser/server-context.chrome-test-harness.js +20 -0
- package/dist/browser/server-middleware.js +31 -0
- package/dist/browser/test-port.js +16 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/file-resolver.js +43 -0
- package/dist/channels/account-summary.js +19 -0
- package/dist/channels/draft-stream-loop.js +77 -0
- package/dist/channels/plugins/account-helpers.js +26 -0
- package/dist/channels/telegram/allow-from.js +10 -0
- package/dist/cli/browser-cli-resize.js +22 -0
- package/dist/cli/browser-cli-shared.js +8 -0
- package/dist/cli/clawbot-cli.js +5 -0
- package/dist/cli/completion-cli.js +566 -0
- package/dist/cli/config-cli.js +63 -5
- package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
- package/dist/cli/daemon-cli/register-service-commands.js +60 -0
- package/dist/cli/daemon-cli-compat.js +80 -0
- package/dist/cli/nodes-cli/pairing-render.js +26 -0
- package/dist/cli/program/action-reparse.js +17 -0
- package/dist/cli/program/command-registry.js +17 -0
- package/dist/cli/program/program-context.js +8 -0
- package/dist/cli/program/register.subclis.js +7 -0
- package/dist/cli/program/routes.js +233 -0
- package/dist/cli/qr-cli.js +132 -0
- package/dist/cli/requirements-test-fixtures.js +17 -0
- package/dist/cli/respawn-policy.js +4 -0
- package/dist/cli/shared/parse-port.js +18 -0
- package/dist/cli/skills-cli.format.js +241 -0
- package/dist/cli/update-cli/progress.js +121 -0
- package/dist/cli/update-cli/restart-helper.js +108 -0
- package/dist/cli/update-cli/shared.js +196 -0
- package/dist/cli/update-cli/status.js +97 -0
- package/dist/cli/update-cli/suppress-deprecations.js +17 -0
- package/dist/cli/update-cli/update-command.js +506 -0
- package/dist/cli/update-cli/wizard.js +130 -0
- package/dist/cli/update-cli.js +3 -9
- package/dist/cli/windows-argv.js +69 -0
- package/dist/commands/auth-choice-legacy.js +20 -0
- package/dist/commands/auth-choice.apply-helpers.js +8 -0
- package/dist/commands/channel-test-helpers.js +19 -0
- package/dist/commands/cleanup-plan.js +10 -0
- package/dist/commands/cleanup-utils.js +7 -0
- package/dist/commands/config-validation.js +15 -0
- package/dist/commands/doctor-completion.js +112 -0
- package/dist/commands/doctor-memory-search.js +119 -0
- package/dist/commands/doctor-session-locks.js +73 -0
- package/dist/commands/doctor.e2e-harness.js +364 -0
- package/dist/commands/gateway-presence.js +19 -0
- package/dist/commands/model-default.js +35 -0
- package/dist/commands/models/fallbacks-shared.js +102 -0
- package/dist/commands/models/shared.js +24 -0
- package/dist/commands/onboard-auth.config-gateways.js +64 -0
- package/dist/commands/onboard-auth.config-litellm.js +45 -0
- package/dist/commands/onboard-auth.config-shared.js +116 -0
- package/dist/commands/onboard-config.js +16 -0
- package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
- package/dist/commands/onboard-provider-auth-flags.js +136 -0
- package/dist/commands/openai-codex-oauth.js +40 -0
- package/dist/commands/test-runtime-config-helpers.js +21 -0
- package/dist/commands/test-wizard-helpers.js +68 -0
- package/dist/commands/vllm-setup.js +66 -0
- package/dist/compat/legacy-names.js +2 -0
- package/dist/config/backup-rotation.js +19 -0
- package/dist/config/env-preserve.js +122 -0
- package/dist/config/includes-scan.js +78 -0
- package/dist/config/plugins-allowlist.js +13 -0
- package/dist/config/schema.help.js +256 -0
- package/dist/config/schema.hints.js +189 -0
- package/dist/config/schema.irc.js +20 -0
- package/dist/config/schema.labels.js +317 -0
- package/dist/config/sessions/delivery-info.js +40 -0
- package/dist/config/types.irc.js +1 -0
- package/dist/config/zod-schema.agent-model.js +10 -0
- package/dist/config/zod-schema.allowdeny.js +35 -0
- package/dist/config/zod-schema.sensitive.js +4 -0
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
- package/dist/cron/isolated-agent/subagent-followup.js +127 -0
- package/dist/cron/isolated-agent.mocks.js +12 -0
- package/dist/cron/isolated-agent.test-setup.js +22 -0
- package/dist/cron/legacy-delivery.js +43 -0
- package/dist/cron/webhook-url.js +22 -0
- package/dist/daemon/arg-split.js +40 -0
- package/dist/daemon/exec-file.js +23 -0
- package/dist/daemon/output.js +6 -0
- package/dist/daemon/runtime-format.js +31 -0
- package/dist/daemon/schtasks-exec.js +4 -0
- package/dist/daemon/service-audit.js +22 -0
- package/dist/discord/client.js +41 -0
- package/dist/discord/components-registry.js +57 -0
- package/dist/discord/components.js +816 -0
- package/dist/discord/guilds.js +12 -0
- package/dist/discord/monitor/gateway-plugin.js +48 -0
- package/dist/discord/monitor/presence.js +30 -0
- package/dist/discord/send.components.js +115 -0
- package/dist/discord/send.shared.js +4 -0
- package/dist/discord/ui.js +26 -0
- package/dist/discord/voice-message.js +254 -0
- package/dist/gateway/agent-event-assistant-text.js +5 -0
- package/dist/gateway/agent-prompt.js +33 -0
- package/dist/gateway/auth-rate-limit.js +136 -0
- package/dist/gateway/channel-health-monitor.js +114 -0
- package/dist/gateway/control-ui-contract.js +1 -0
- package/dist/gateway/control-ui-csp.js +15 -0
- package/dist/gateway/gateway-config-prompts.shared.js +25 -0
- package/dist/gateway/http-auth-helpers.js +18 -0
- package/dist/gateway/http-common.js +18 -0
- package/dist/gateway/http-endpoint-helpers.js +27 -0
- package/dist/gateway/node-invoke-sanitize.js +11 -0
- package/dist/gateway/node-invoke-system-run-approval.js +205 -0
- package/dist/gateway/probe-auth.js +21 -0
- package/dist/gateway/protocol/index.js +7 -2
- package/dist/gateway/protocol/schema/mesh.js +54 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
- package/dist/gateway/server-channels.js +11 -0
- package/dist/gateway/server-methods/attachment-normalize.js +16 -0
- package/dist/gateway/server-methods/base-hash.js +8 -0
- package/dist/gateway/server-methods/mesh.js +700 -0
- package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
- package/dist/gateway/server-methods/restart-request.js +13 -0
- package/dist/gateway/server-methods/validation.js +8 -0
- package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
- package/dist/gateway/server.e2e-registry-helpers.js +1 -0
- package/dist/gateway/server.e2e-ws-harness.js +20 -0
- package/dist/gateway/test-helpers.js +2 -0
- package/dist/gateway/test-helpers.server.js +3 -1
- package/dist/gateway/test-http-response.js +12 -0
- package/dist/gateway/test-openai-responses-model.js +20 -0
- package/dist/gateway/test-temp-config.js +30 -0
- package/dist/gateway/test-with-server.js +32 -0
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
- package/dist/imessage/monitor/abort-handler.js +23 -0
- package/dist/imessage/monitor/inbound-processing.js +346 -0
- package/dist/imessage/monitor/parse-notification.js +64 -0
- package/dist/imessage/target-parsing-helpers.js +92 -0
- package/dist/infra/archive.js +244 -20
- package/dist/infra/detect-package-manager.js +26 -0
- package/dist/infra/exec-approvals-allowlist.js +257 -0
- package/dist/infra/exec-approvals-analysis.js +770 -0
- package/dist/infra/exec-approvals.js +13 -0
- package/dist/infra/file-lock.js +1 -0
- package/dist/infra/gemini-auth.js +39 -0
- package/dist/infra/heartbeat-active-hours.js +85 -0
- package/dist/infra/heartbeat-events-filter.js +50 -0
- package/dist/infra/heartbeat-runner.test-utils.js +39 -0
- package/dist/infra/http-body.js +265 -0
- package/dist/infra/install-package-dir.js +50 -0
- package/dist/infra/install-safe-path.js +49 -0
- package/dist/infra/json-files.js +49 -0
- package/dist/infra/jsonl-socket.js +52 -0
- package/dist/infra/map-size.js +14 -0
- package/dist/infra/net/hostname.js +7 -0
- package/dist/infra/npm-registry-spec.js +39 -0
- package/dist/infra/openclaw-root.js +109 -0
- package/dist/infra/outbound/delivery-queue.js +214 -0
- package/dist/infra/outbound/identity.js +23 -0
- package/dist/infra/outbound/message-action-params.js +307 -0
- package/dist/infra/outbound/tool-payload.js +21 -0
- package/dist/infra/package-json.js +23 -0
- package/dist/infra/pairing-files.js +19 -0
- package/dist/infra/pairing-token.js +9 -0
- package/dist/infra/path-prepend.js +51 -0
- package/dist/infra/process-respawn.js +49 -0
- package/dist/infra/runtime-status.js +16 -0
- package/dist/infra/session-cost-usage.types.js +1 -0
- package/dist/infra/session-maintenance-warning.js +89 -0
- package/dist/infra/system-run-command.js +78 -0
- package/dist/infra/tmp-openclaw-dir.js +81 -0
- package/dist/infra/tmp-poolbot-dir.js +2 -0
- package/dist/infra/update-channels.js +19 -0
- package/dist/line/actions.js +45 -0
- package/dist/line/channel-access-token.js +9 -0
- package/dist/line/flex-templates/basic-cards.js +332 -0
- package/dist/line/flex-templates/common.js +18 -0
- package/dist/line/flex-templates/media-control-cards.js +453 -0
- package/dist/line/flex-templates/message.js +10 -0
- package/dist/line/flex-templates/schedule-cards.js +399 -0
- package/dist/line/flex-templates/types.js +1 -0
- package/dist/line/webhook-node.js +100 -0
- package/dist/line/webhook-utils.js +11 -0
- package/dist/logging/timestamps.js +14 -0
- package/dist/markdown/whatsapp.js +62 -0
- package/dist/media/base64.js +34 -0
- package/dist/media/local-roots.js +32 -0
- package/dist/media/outbound-attachment.js +10 -0
- package/dist/media/read-response-with-limit.js +41 -0
- package/dist/media/sniff-mime-from-base64.js +19 -0
- package/dist/media-understanding/audio-preflight.js +67 -0
- package/dist/media-understanding/fs.js +13 -0
- package/dist/media-understanding/output-extract.js +26 -0
- package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
- package/dist/media-understanding/providers/google/inline-data.js +64 -0
- package/dist/media-understanding/providers/shared.js +7 -0
- package/dist/media-understanding/runner.entries.js +459 -0
- package/dist/memory/batch-error-utils.js +11 -0
- package/dist/memory/batch-http.js +27 -0
- package/dist/memory/batch-output.js +29 -0
- package/dist/memory/batch-runner.js +22 -0
- package/dist/memory/batch-upload.js +23 -0
- package/dist/memory/batch-utils.js +26 -0
- package/dist/memory/embeddings-debug.js +11 -0
- package/dist/memory/embeddings-remote-client.js +22 -0
- package/dist/memory/embeddings-remote-fetch.js +14 -0
- package/dist/memory/manager-embedding-ops.js +616 -0
- package/dist/memory/manager-sync-ops.js +953 -0
- package/dist/memory/qmd-manager.js +1061 -0
- package/dist/memory/qmd-query-parser.js +107 -0
- package/dist/memory/qmd-scope.js +93 -0
- package/dist/memory/search-manager.js +0 -1
- package/dist/memory/sync-index.js +21 -0
- package/dist/memory/sync-progress.js +22 -0
- package/dist/memory/sync-stale.js +30 -0
- package/dist/memory/test-embeddings-mock.js +16 -0
- package/dist/memory/test-manager-helpers.js +14 -0
- package/dist/memory/test-runtime-mocks.js +11 -0
- package/dist/node-host/invoke-browser.js +177 -0
- package/dist/node-host/invoke.js +685 -0
- package/dist/pairing/setup-code.js +285 -0
- package/dist/plugin-sdk/account-id.js +1 -0
- package/dist/plugin-sdk/agent-media-payload.js +13 -0
- package/dist/plugin-sdk/allow-from.js +47 -0
- package/dist/plugin-sdk/command-auth.js +23 -0
- package/dist/plugin-sdk/config-paths.js +9 -0
- package/dist/plugin-sdk/file-lock.js +116 -0
- package/dist/plugin-sdk/json-store.js +31 -0
- package/dist/plugin-sdk/onboarding.js +28 -0
- package/dist/plugin-sdk/provider-auth-result.js +29 -0
- package/dist/plugin-sdk/slack-message-actions.js +133 -0
- package/dist/plugin-sdk/status-helpers.js +35 -0
- package/dist/plugin-sdk/text-chunking.js +31 -0
- package/dist/plugin-sdk/tool-send.js +12 -0
- package/dist/plugin-sdk/webhook-path.js +27 -0
- package/dist/plugin-sdk/webhook-targets.js +34 -0
- package/dist/plugins/hooks.test-helpers.js +21 -0
- package/dist/plugins/uninstall.js +171 -0
- package/dist/process/supervisor/adapters/child.js +143 -0
- package/dist/process/supervisor/adapters/env.js +13 -0
- package/dist/process/supervisor/adapters/pty.js +148 -0
- package/dist/process/supervisor/index.js +10 -0
- package/dist/process/supervisor/registry.js +117 -0
- package/dist/process/supervisor/supervisor.js +244 -0
- package/dist/process/supervisor/types.js +1 -0
- package/dist/providers/google-shared.test-helpers.js +75 -0
- package/dist/security/audit-channel.js +419 -0
- package/dist/security/audit-tool-policy.js +1 -0
- package/dist/security/scan-paths.js +12 -0
- package/dist/sessions/input-provenance.js +55 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/shared/chat-content.js +31 -0
- package/dist/shared/chat-envelope.js +45 -0
- package/dist/shared/config-eval.js +117 -0
- package/dist/shared/device-auth.js +16 -0
- package/dist/shared/entry-metadata.js +9 -0
- package/dist/shared/entry-status.js +25 -0
- package/dist/shared/frontmatter.js +98 -0
- package/dist/shared/model-param-b.js +19 -0
- package/dist/shared/net/ipv4.js +17 -0
- package/dist/shared/node-match.js +53 -0
- package/dist/shared/requirements.js +128 -0
- package/dist/shared/subagents-format.js +84 -0
- package/dist/shared/usage-aggregates.js +28 -0
- package/dist/signal/monitor/mentions.js +45 -0
- package/dist/signal/rpc-context.js +19 -0
- package/dist/slack/blocks-fallback.js +76 -0
- package/dist/slack/blocks-input.js +40 -0
- package/dist/slack/draft-stream.js +106 -0
- package/dist/slack/message-actions.js +51 -0
- package/dist/slack/modal-metadata.js +32 -0
- package/dist/slack/monitor/events/interactions.js +462 -0
- package/dist/slack/monitor/room-context.js +17 -0
- package/dist/slack/stream-mode.js +41 -0
- package/dist/telegram/bot-native-command-menu.js +64 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/button-types.js +1 -0
- package/dist/telegram/group-access.js +65 -0
- package/dist/telegram/outbound-params.js +21 -0
- package/dist/telegram/poll-vote-cache.js +21 -0
- package/dist/terminal/health-style.js +36 -0
- package/dist/test-utils/chunk-test-helpers.js +21 -0
- package/dist/test-utils/env.js +72 -0
- package/dist/test-utils/exec-assertions.js +12 -0
- package/dist/test-utils/imessage-test-plugin.js +54 -0
- package/dist/test-utils/mock-http-response.js +17 -0
- package/dist/test-utils/vitest-mock-fn.js +1 -0
- package/dist/tts/tts-core.js +550 -0
- package/dist/utils/chunk-items.js +10 -0
- package/dist/utils/reaction-level.js +52 -0
- package/dist/utils/safe-json.js +22 -0
- package/dist/utils/with-timeout.js +14 -0
- package/dist/web/media.js +17 -5
- package/dist/whatsapp/resolve-outbound-target.js +42 -0
- package/dist/wizard/onboarding.completion.js +74 -0
- package/extensions/bluebubbles/src/account-resolve.ts +29 -0
- package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
- package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
- package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
- package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
- package/extensions/bluebubbles/src/multipart.ts +32 -0
- package/extensions/bluebubbles/src/send-helpers.ts +53 -0
- package/extensions/bluebubbles/src/test-harness.ts +50 -0
- package/extensions/bluebubbles/src/test-mocks.ts +11 -0
- package/extensions/device-pair/index.ts +554 -0
- package/extensions/discord/src/channel.js +366 -0
- package/extensions/discord/src/runtime.js +10 -0
- package/extensions/feishu/index.ts +63 -0
- package/extensions/feishu/src/accounts.ts +114 -0
- package/extensions/feishu/src/bitable.ts +739 -0
- package/extensions/feishu/src/bot.ts +965 -0
- package/extensions/feishu/src/channel.ts +351 -0
- package/extensions/feishu/src/client.ts +118 -0
- package/extensions/feishu/src/config-schema.ts +206 -0
- package/extensions/feishu/src/dedup.ts +33 -0
- package/extensions/feishu/src/directory.ts +177 -0
- package/extensions/feishu/src/doc-schema.ts +47 -0
- package/extensions/feishu/src/docx.ts +536 -0
- package/extensions/feishu/src/drive-schema.ts +46 -0
- package/extensions/feishu/src/drive.ts +227 -0
- package/extensions/feishu/src/dynamic-agent.ts +131 -0
- package/extensions/feishu/src/media.ts +449 -0
- package/extensions/feishu/src/mention.ts +126 -0
- package/extensions/feishu/src/monitor.ts +330 -0
- package/extensions/feishu/src/onboarding.ts +359 -0
- package/extensions/feishu/src/outbound.ts +55 -0
- package/extensions/feishu/src/perm-schema.ts +52 -0
- package/extensions/feishu/src/perm.ts +173 -0
- package/extensions/feishu/src/policy.ts +84 -0
- package/extensions/feishu/src/probe.ts +44 -0
- package/extensions/feishu/src/reactions.ts +160 -0
- package/extensions/feishu/src/reply-dispatcher.ts +239 -0
- package/extensions/feishu/src/runtime.ts +14 -0
- package/extensions/feishu/src/send-result.ts +29 -0
- package/extensions/feishu/src/send.ts +335 -0
- package/extensions/feishu/src/streaming-card.ts +223 -0
- package/extensions/feishu/src/targets.ts +78 -0
- package/extensions/feishu/src/tools-config.ts +21 -0
- package/extensions/feishu/src/types.ts +81 -0
- package/extensions/feishu/src/typing.ts +80 -0
- package/extensions/feishu/src/wiki-schema.ts +55 -0
- package/extensions/feishu/src/wiki.ts +232 -0
- package/extensions/imessage/src/channel.js +253 -0
- package/extensions/imessage/src/runtime.js +10 -0
- package/extensions/irc/index.ts +17 -0
- package/extensions/irc/src/accounts.ts +268 -0
- package/extensions/irc/src/channel.ts +367 -0
- package/extensions/irc/src/client.ts +439 -0
- package/extensions/irc/src/config-schema.ts +97 -0
- package/extensions/irc/src/connect-options.ts +30 -0
- package/extensions/irc/src/control-chars.ts +22 -0
- package/extensions/irc/src/inbound.ts +334 -0
- package/extensions/irc/src/monitor.ts +147 -0
- package/extensions/irc/src/normalize.ts +117 -0
- package/extensions/irc/src/onboarding.ts +479 -0
- package/extensions/irc/src/policy.ts +157 -0
- package/extensions/irc/src/probe.ts +53 -0
- package/extensions/irc/src/protocol.ts +169 -0
- package/extensions/irc/src/runtime.ts +14 -0
- package/extensions/irc/src/send.ts +88 -0
- package/extensions/irc/src/types.ts +93 -0
- package/extensions/matrix/src/matrix/client-bootstrap.ts +39 -0
- package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
- package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
- package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
- package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
- package/extensions/minimax-portal-auth/index.ts +161 -0
- package/extensions/minimax-portal-auth/oauth.ts +247 -0
- package/extensions/msteams/src/file-lock.ts +1 -0
- package/extensions/msteams/src/graph.ts +92 -0
- package/extensions/msteams/src/mentions.ts +114 -0
- package/extensions/msteams/src/test-runtime.ts +16 -0
- package/extensions/openai-codex-auth/index.ts +177 -0
- package/extensions/phone-control/index.ts +421 -0
- package/extensions/shared/resolve-target-test-helpers.ts +66 -0
- package/extensions/signal/src/channel.js +273 -0
- package/extensions/signal/src/runtime.js +10 -0
- package/extensions/slack/src/channel.js +489 -0
- package/extensions/slack/src/runtime.js +10 -0
- package/extensions/talk-voice/index.ts +150 -0
- package/extensions/telegram/src/channel.js +424 -0
- package/extensions/telegram/src/runtime.js +10 -0
- package/extensions/thread-ownership/index.ts +133 -0
- package/extensions/tlon/src/account-fields.ts +25 -0
- package/extensions/tlon/src/urbit/base-url.ts +57 -0
- package/extensions/tlon/src/urbit/channel-client.ts +157 -0
- package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
- package/extensions/tlon/src/urbit/context.ts +47 -0
- package/extensions/tlon/src/urbit/errors.ts +51 -0
- package/extensions/tlon/src/urbit/fetch.ts +39 -0
- package/extensions/twitch/src/test-fixtures.ts +30 -0
- package/extensions/voice-call/src/allowlist.ts +19 -0
- package/extensions/whatsapp/src/channel.js +429 -0
- package/extensions/whatsapp/src/runtime.js +10 -0
- package/package.json +1 -1
package/dist/infra/archive.js
CHANGED
|
@@ -1,22 +1,41 @@
|
|
|
1
|
+
import { createWriteStream } from "node:fs";
|
|
1
2
|
import fs from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
|
-
import
|
|
4
|
+
import { Readable, Transform } from "node:stream";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
4
6
|
import JSZip from "jszip";
|
|
7
|
+
import * as tar from "tar";
|
|
8
|
+
import { resolveSafeBaseDir } from "./path-safety.js";
|
|
9
|
+
/** @internal */
|
|
10
|
+
export const DEFAULT_MAX_ARCHIVE_BYTES_ZIP = 256 * 1024 * 1024;
|
|
11
|
+
/** @internal */
|
|
12
|
+
export const DEFAULT_MAX_ENTRIES = 50_000;
|
|
13
|
+
/** @internal */
|
|
14
|
+
export const DEFAULT_MAX_EXTRACTED_BYTES = 512 * 1024 * 1024;
|
|
15
|
+
/** @internal */
|
|
16
|
+
export const DEFAULT_MAX_ENTRY_BYTES = 256 * 1024 * 1024;
|
|
17
|
+
const ERROR_ARCHIVE_SIZE_EXCEEDS_LIMIT = "archive size exceeds limit";
|
|
18
|
+
const ERROR_ARCHIVE_ENTRY_COUNT_EXCEEDS_LIMIT = "archive entry count exceeds limit";
|
|
19
|
+
const ERROR_ARCHIVE_ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT = "archive entry extracted size exceeds limit";
|
|
20
|
+
const ERROR_ARCHIVE_EXTRACTED_SIZE_EXCEEDS_LIMIT = "archive extracted size exceeds limit";
|
|
5
21
|
const TAR_SUFFIXES = [".tgz", ".tar.gz", ".tar"];
|
|
6
22
|
export function resolveArchiveKind(filePath) {
|
|
7
23
|
const lower = filePath.toLowerCase();
|
|
8
|
-
if (lower.endsWith(".zip"))
|
|
24
|
+
if (lower.endsWith(".zip")) {
|
|
9
25
|
return "zip";
|
|
10
|
-
|
|
26
|
+
}
|
|
27
|
+
if (TAR_SUFFIXES.some((suffix) => lower.endsWith(suffix))) {
|
|
11
28
|
return "tar";
|
|
29
|
+
}
|
|
12
30
|
return null;
|
|
13
31
|
}
|
|
14
32
|
export async function resolvePackedRootDir(extractDir) {
|
|
15
33
|
const direct = path.join(extractDir, "package");
|
|
16
34
|
try {
|
|
17
35
|
const stat = await fs.stat(direct);
|
|
18
|
-
if (stat.isDirectory())
|
|
36
|
+
if (stat.isDirectory()) {
|
|
19
37
|
return direct;
|
|
38
|
+
}
|
|
20
39
|
}
|
|
21
40
|
catch {
|
|
22
41
|
// ignore
|
|
@@ -43,44 +62,249 @@ export async function withTimeout(promise, timeoutMs, label) {
|
|
|
43
62
|
]);
|
|
44
63
|
}
|
|
45
64
|
finally {
|
|
46
|
-
if (timeoutId)
|
|
65
|
+
if (timeoutId) {
|
|
47
66
|
clearTimeout(timeoutId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
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
|
+
function clampLimit(value) {
|
|
118
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
119
|
+
return undefined;
|
|
48
120
|
}
|
|
121
|
+
const v = Math.floor(value);
|
|
122
|
+
return v > 0 ? v : undefined;
|
|
123
|
+
}
|
|
124
|
+
function resolveExtractLimits(limits) {
|
|
125
|
+
// Defaults: defensive, but should not break normal installs.
|
|
126
|
+
return {
|
|
127
|
+
maxArchiveBytes: clampLimit(limits?.maxArchiveBytes) ?? DEFAULT_MAX_ARCHIVE_BYTES_ZIP,
|
|
128
|
+
maxEntries: clampLimit(limits?.maxEntries) ?? DEFAULT_MAX_ENTRIES,
|
|
129
|
+
maxExtractedBytes: clampLimit(limits?.maxExtractedBytes) ?? DEFAULT_MAX_EXTRACTED_BYTES,
|
|
130
|
+
maxEntryBytes: clampLimit(limits?.maxEntryBytes) ?? DEFAULT_MAX_ENTRY_BYTES,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function assertArchiveEntryCountWithinLimit(entryCount, limits) {
|
|
134
|
+
if (entryCount > limits.maxEntries) {
|
|
135
|
+
throw new Error(ERROR_ARCHIVE_ENTRY_COUNT_EXCEEDS_LIMIT);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function createByteBudgetTracker(limits) {
|
|
139
|
+
let entryBytes = 0;
|
|
140
|
+
let extractedBytes = 0;
|
|
141
|
+
const addBytes = (bytes) => {
|
|
142
|
+
const b = Math.max(0, Math.floor(bytes));
|
|
143
|
+
if (b === 0) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
entryBytes += b;
|
|
147
|
+
if (entryBytes > limits.maxEntryBytes) {
|
|
148
|
+
throw new Error(ERROR_ARCHIVE_ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
|
|
149
|
+
}
|
|
150
|
+
extractedBytes += b;
|
|
151
|
+
if (extractedBytes > limits.maxExtractedBytes) {
|
|
152
|
+
throw new Error(ERROR_ARCHIVE_EXTRACTED_SIZE_EXCEEDS_LIMIT);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
startEntry() {
|
|
157
|
+
entryBytes = 0;
|
|
158
|
+
},
|
|
159
|
+
addBytes,
|
|
160
|
+
addEntrySize(size) {
|
|
161
|
+
const s = Math.max(0, Math.floor(size));
|
|
162
|
+
if (s > limits.maxEntryBytes) {
|
|
163
|
+
throw new Error(ERROR_ARCHIVE_ENTRY_EXTRACTED_SIZE_EXCEEDS_LIMIT);
|
|
164
|
+
}
|
|
165
|
+
// Note: tar budgets are based on the header-declared size.
|
|
166
|
+
addBytes(s);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createExtractBudgetTransform(params) {
|
|
171
|
+
return new Transform({
|
|
172
|
+
transform(chunk, _encoding, callback) {
|
|
173
|
+
try {
|
|
174
|
+
const buf = chunk instanceof Buffer ? chunk : Buffer.from(chunk);
|
|
175
|
+
params.onChunkBytes(buf.byteLength);
|
|
176
|
+
callback(null, buf);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
callback(err instanceof Error ? err : new Error(String(err)));
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async function readZipEntryStream(entry) {
|
|
185
|
+
if (typeof entry.nodeStream === "function") {
|
|
186
|
+
return entry.nodeStream();
|
|
187
|
+
}
|
|
188
|
+
// Old JSZip: fall back to buffering, but still extract via a stream.
|
|
189
|
+
const buf = await entry.async("nodebuffer");
|
|
190
|
+
return Readable.from(buf);
|
|
49
191
|
}
|
|
50
192
|
async function extractZip(params) {
|
|
193
|
+
const limits = resolveExtractLimits(params.limits);
|
|
194
|
+
const stat = await fs.stat(params.archivePath);
|
|
195
|
+
if (stat.size > limits.maxArchiveBytes) {
|
|
196
|
+
throw new Error(ERROR_ARCHIVE_SIZE_EXCEEDS_LIMIT);
|
|
197
|
+
}
|
|
51
198
|
const buffer = await fs.readFile(params.archivePath);
|
|
52
199
|
const zip = await JSZip.loadAsync(buffer);
|
|
53
200
|
const entries = Object.values(zip.files);
|
|
201
|
+
const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
|
|
202
|
+
assertArchiveEntryCountWithinLimit(entries.length, limits);
|
|
203
|
+
const budget = createByteBudgetTracker(limits);
|
|
54
204
|
for (const entry of entries) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!dirPath.startsWith(params.destDir)) {
|
|
59
|
-
throw new Error(`zip entry escapes destination: ${entry.name}`);
|
|
60
|
-
}
|
|
61
|
-
await fs.mkdir(dirPath, { recursive: true });
|
|
205
|
+
validateArchiveEntryPath(entry.name);
|
|
206
|
+
const relPath = stripArchivePath(entry.name, strip);
|
|
207
|
+
if (!relPath) {
|
|
62
208
|
continue;
|
|
63
209
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
210
|
+
validateArchiveEntryPath(relPath);
|
|
211
|
+
const outPath = resolveCheckedOutPath(params.destDir, relPath, entry.name);
|
|
212
|
+
if (entry.dir) {
|
|
213
|
+
await fs.mkdir(outPath, { recursive: true });
|
|
214
|
+
continue;
|
|
67
215
|
}
|
|
68
216
|
await fs.mkdir(path.dirname(outPath), { recursive: true });
|
|
69
|
-
|
|
70
|
-
await
|
|
217
|
+
budget.startEntry();
|
|
218
|
+
const readable = await readZipEntryStream(entry);
|
|
219
|
+
try {
|
|
220
|
+
await pipeline(readable, createExtractBudgetTransform({ onChunkBytes: budget.addBytes }), createWriteStream(outPath));
|
|
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
|
+
}
|
|
71
233
|
}
|
|
72
234
|
}
|
|
235
|
+
function readTarEntryInfo(entry) {
|
|
236
|
+
const p = typeof entry === "object" && entry !== null && "path" in entry
|
|
237
|
+
? String(entry.path)
|
|
238
|
+
: "";
|
|
239
|
+
const t = typeof entry === "object" && entry !== null && "type" in entry
|
|
240
|
+
? String(entry.type)
|
|
241
|
+
: "";
|
|
242
|
+
const s = typeof entry === "object" &&
|
|
243
|
+
entry !== null &&
|
|
244
|
+
"size" in entry &&
|
|
245
|
+
typeof entry.size === "number" &&
|
|
246
|
+
Number.isFinite(entry.size)
|
|
247
|
+
? Math.max(0, Math.floor(entry.size))
|
|
248
|
+
: 0;
|
|
249
|
+
return { path: p, type: t, size: s };
|
|
250
|
+
}
|
|
73
251
|
export async function extractArchive(params) {
|
|
74
|
-
const kind = resolveArchiveKind(params.archivePath);
|
|
252
|
+
const kind = params.kind ?? resolveArchiveKind(params.archivePath);
|
|
75
253
|
if (!kind) {
|
|
76
254
|
throw new Error(`unsupported archive: ${params.archivePath}`);
|
|
77
255
|
}
|
|
78
256
|
const label = kind === "zip" ? "extract zip" : "extract tar";
|
|
79
257
|
if (kind === "tar") {
|
|
80
|
-
|
|
258
|
+
const strip = Math.max(0, Math.floor(params.stripComponents ?? 0));
|
|
259
|
+
const limits = resolveExtractLimits(params.limits);
|
|
260
|
+
let entryCount = 0;
|
|
261
|
+
const budget = createByteBudgetTracker(limits);
|
|
262
|
+
await withTimeout(tar.x({
|
|
263
|
+
file: params.archivePath,
|
|
264
|
+
cwd: params.destDir,
|
|
265
|
+
strip,
|
|
266
|
+
gzip: params.tarGzip,
|
|
267
|
+
preservePaths: false,
|
|
268
|
+
strict: true,
|
|
269
|
+
onReadEntry(entry) {
|
|
270
|
+
const info = readTarEntryInfo(entry);
|
|
271
|
+
try {
|
|
272
|
+
validateArchiveEntryPath(info.path);
|
|
273
|
+
const relPath = stripArchivePath(info.path, strip);
|
|
274
|
+
if (!relPath) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
validateArchiveEntryPath(relPath);
|
|
278
|
+
resolveCheckedOutPath(params.destDir, relPath, info.path);
|
|
279
|
+
if (info.type === "SymbolicLink" ||
|
|
280
|
+
info.type === "Link" ||
|
|
281
|
+
info.type === "BlockDevice" ||
|
|
282
|
+
info.type === "CharacterDevice" ||
|
|
283
|
+
info.type === "FIFO" ||
|
|
284
|
+
info.type === "Socket") {
|
|
285
|
+
throw new Error(`tar entry is a link: ${info.path}`);
|
|
286
|
+
}
|
|
287
|
+
entryCount += 1;
|
|
288
|
+
assertArchiveEntryCountWithinLimit(entryCount, limits);
|
|
289
|
+
budget.addEntrySize(info.size);
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
293
|
+
// Node's EventEmitter calls listeners with `this` bound to the
|
|
294
|
+
// emitter (tar.Unpack), which exposes Parser.abort().
|
|
295
|
+
const emitter = this;
|
|
296
|
+
emitter.abort?.(error);
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
}), params.timeoutMs, label);
|
|
81
300
|
return;
|
|
82
301
|
}
|
|
83
|
-
await withTimeout(extractZip(
|
|
302
|
+
await withTimeout(extractZip({
|
|
303
|
+
archivePath: params.archivePath,
|
|
304
|
+
destDir: params.destDir,
|
|
305
|
+
stripComponents: params.stripComponents,
|
|
306
|
+
limits: params.limits,
|
|
307
|
+
}), params.timeoutMs, label);
|
|
84
308
|
}
|
|
85
309
|
export async function fileExists(filePath) {
|
|
86
310
|
try {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function detectPackageManager(root) {
|
|
4
|
+
try {
|
|
5
|
+
const raw = await fs.readFile(path.join(root, "package.json"), "utf-8");
|
|
6
|
+
const parsed = JSON.parse(raw);
|
|
7
|
+
const pm = parsed?.packageManager?.split("@")[0]?.trim();
|
|
8
|
+
if (pm === "pnpm" || pm === "bun" || pm === "npm") {
|
|
9
|
+
return pm;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// ignore
|
|
14
|
+
}
|
|
15
|
+
const files = await fs.readdir(root).catch(() => []);
|
|
16
|
+
if (files.includes("pnpm-lock.yaml")) {
|
|
17
|
+
return "pnpm";
|
|
18
|
+
}
|
|
19
|
+
if (files.includes("bun.lockb")) {
|
|
20
|
+
return "bun";
|
|
21
|
+
}
|
|
22
|
+
if (files.includes("package-lock.json")) {
|
|
23
|
+
return "npm";
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_SAFE_BINS, analyzeShellCommand, isWindowsPlatform, matchAllowlist, resolveAllowlistCandidatePath, splitCommandChain, } from "./exec-approvals-analysis.js";
|
|
4
|
+
function isPathLikeToken(value) {
|
|
5
|
+
const trimmed = value.trim();
|
|
6
|
+
if (!trimmed) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
if (trimmed === "-") {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~")) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
if (trimmed.startsWith("/")) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return /^[A-Za-z]:[\\/]/.test(trimmed);
|
|
19
|
+
}
|
|
20
|
+
function defaultFileExists(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.existsSync(filePath);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function normalizeSafeBins(entries) {
|
|
29
|
+
if (!Array.isArray(entries)) {
|
|
30
|
+
return new Set();
|
|
31
|
+
}
|
|
32
|
+
const normalized = entries
|
|
33
|
+
.map((entry) => entry.trim().toLowerCase())
|
|
34
|
+
.filter((entry) => entry.length > 0);
|
|
35
|
+
return new Set(normalized);
|
|
36
|
+
}
|
|
37
|
+
export function resolveSafeBins(entries) {
|
|
38
|
+
if (entries === undefined) {
|
|
39
|
+
return normalizeSafeBins(DEFAULT_SAFE_BINS);
|
|
40
|
+
}
|
|
41
|
+
return normalizeSafeBins(entries ?? []);
|
|
42
|
+
}
|
|
43
|
+
function hasGlobToken(value) {
|
|
44
|
+
// Safe bins are stdin-only; globbing is both surprising and a historical bypass vector.
|
|
45
|
+
// Note: we still harden execution-time expansion separately.
|
|
46
|
+
return /[*?[\]]/.test(value);
|
|
47
|
+
}
|
|
48
|
+
export function isSafeBinUsage(params) {
|
|
49
|
+
// Windows host exec uses PowerShell, which has different parsing/expansion rules.
|
|
50
|
+
// Keep safeBins conservative there (require explicit allowlist entries).
|
|
51
|
+
if (isWindowsPlatform(process.platform)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (params.safeBins.size === 0) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const resolution = params.resolution;
|
|
58
|
+
const execName = resolution?.executableName?.toLowerCase();
|
|
59
|
+
if (!execName) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const matchesSafeBin = params.safeBins.has(execName) ||
|
|
63
|
+
(process.platform === "win32" && params.safeBins.has(path.parse(execName).name));
|
|
64
|
+
if (!matchesSafeBin) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (!resolution?.resolvedPath) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const cwd = params.cwd ?? process.cwd();
|
|
71
|
+
const exists = params.fileExists ?? defaultFileExists;
|
|
72
|
+
const argv = params.argv.slice(1);
|
|
73
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
74
|
+
const token = argv[i];
|
|
75
|
+
if (!token) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (token === "-") {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (token.startsWith("-")) {
|
|
82
|
+
const eqIndex = token.indexOf("=");
|
|
83
|
+
if (eqIndex > 0) {
|
|
84
|
+
const value = token.slice(eqIndex + 1);
|
|
85
|
+
if (value && hasGlobToken(value)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (value && (isPathLikeToken(value) || exists(path.resolve(cwd, value)))) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (hasGlobToken(token)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (isPathLikeToken(token)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (exists(path.resolve(cwd, token))) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
function evaluateSegments(segments, params) {
|
|
107
|
+
const matches = [];
|
|
108
|
+
const allowSkills = params.autoAllowSkills === true && (params.skillBins?.size ?? 0) > 0;
|
|
109
|
+
const segmentSatisfiedBy = [];
|
|
110
|
+
const satisfied = segments.every((segment) => {
|
|
111
|
+
const candidatePath = resolveAllowlistCandidatePath(segment.resolution, params.cwd);
|
|
112
|
+
const candidateResolution = candidatePath && segment.resolution
|
|
113
|
+
? { ...segment.resolution, resolvedPath: candidatePath }
|
|
114
|
+
: segment.resolution;
|
|
115
|
+
const match = matchAllowlist(params.allowlist, candidateResolution);
|
|
116
|
+
if (match) {
|
|
117
|
+
matches.push(match);
|
|
118
|
+
}
|
|
119
|
+
const safe = isSafeBinUsage({
|
|
120
|
+
argv: segment.argv,
|
|
121
|
+
resolution: segment.resolution,
|
|
122
|
+
safeBins: params.safeBins,
|
|
123
|
+
cwd: params.cwd,
|
|
124
|
+
});
|
|
125
|
+
const skillAllow = allowSkills && segment.resolution?.executableName
|
|
126
|
+
? params.skillBins?.has(segment.resolution.executableName)
|
|
127
|
+
: false;
|
|
128
|
+
const by = match
|
|
129
|
+
? "allowlist"
|
|
130
|
+
: safe
|
|
131
|
+
? "safeBins"
|
|
132
|
+
: skillAllow
|
|
133
|
+
? "skills"
|
|
134
|
+
: null;
|
|
135
|
+
segmentSatisfiedBy.push(by);
|
|
136
|
+
return Boolean(by);
|
|
137
|
+
});
|
|
138
|
+
return { satisfied, matches, segmentSatisfiedBy };
|
|
139
|
+
}
|
|
140
|
+
export function evaluateExecAllowlist(params) {
|
|
141
|
+
const allowlistMatches = [];
|
|
142
|
+
const segmentSatisfiedBy = [];
|
|
143
|
+
if (!params.analysis.ok || params.analysis.segments.length === 0) {
|
|
144
|
+
return { allowlistSatisfied: false, allowlistMatches, segmentSatisfiedBy };
|
|
145
|
+
}
|
|
146
|
+
// If the analysis contains chains, evaluate each chain part separately
|
|
147
|
+
if (params.analysis.chains) {
|
|
148
|
+
for (const chainSegments of params.analysis.chains) {
|
|
149
|
+
const result = evaluateSegments(chainSegments, {
|
|
150
|
+
allowlist: params.allowlist,
|
|
151
|
+
safeBins: params.safeBins,
|
|
152
|
+
cwd: params.cwd,
|
|
153
|
+
skillBins: params.skillBins,
|
|
154
|
+
autoAllowSkills: params.autoAllowSkills,
|
|
155
|
+
});
|
|
156
|
+
if (!result.satisfied) {
|
|
157
|
+
return { allowlistSatisfied: false, allowlistMatches: [], segmentSatisfiedBy: [] };
|
|
158
|
+
}
|
|
159
|
+
allowlistMatches.push(...result.matches);
|
|
160
|
+
segmentSatisfiedBy.push(...result.segmentSatisfiedBy);
|
|
161
|
+
}
|
|
162
|
+
return { allowlistSatisfied: true, allowlistMatches, segmentSatisfiedBy };
|
|
163
|
+
}
|
|
164
|
+
// No chains, evaluate all segments together
|
|
165
|
+
const result = evaluateSegments(params.analysis.segments, {
|
|
166
|
+
allowlist: params.allowlist,
|
|
167
|
+
safeBins: params.safeBins,
|
|
168
|
+
cwd: params.cwd,
|
|
169
|
+
skillBins: params.skillBins,
|
|
170
|
+
autoAllowSkills: params.autoAllowSkills,
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
allowlistSatisfied: result.satisfied,
|
|
174
|
+
allowlistMatches: result.matches,
|
|
175
|
+
segmentSatisfiedBy: result.segmentSatisfiedBy,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Evaluates allowlist for shell commands (including &&, ||, ;) and returns analysis metadata.
|
|
180
|
+
*/
|
|
181
|
+
export function evaluateShellAllowlist(params) {
|
|
182
|
+
const analysisFailure = () => ({
|
|
183
|
+
analysisOk: false,
|
|
184
|
+
allowlistSatisfied: false,
|
|
185
|
+
allowlistMatches: [],
|
|
186
|
+
segments: [],
|
|
187
|
+
segmentSatisfiedBy: [],
|
|
188
|
+
});
|
|
189
|
+
const chainParts = isWindowsPlatform(params.platform) ? null : splitCommandChain(params.command);
|
|
190
|
+
if (!chainParts) {
|
|
191
|
+
const analysis = analyzeShellCommand({
|
|
192
|
+
command: params.command,
|
|
193
|
+
cwd: params.cwd,
|
|
194
|
+
env: params.env,
|
|
195
|
+
platform: params.platform,
|
|
196
|
+
});
|
|
197
|
+
if (!analysis.ok) {
|
|
198
|
+
return analysisFailure();
|
|
199
|
+
}
|
|
200
|
+
const evaluation = evaluateExecAllowlist({
|
|
201
|
+
analysis,
|
|
202
|
+
allowlist: params.allowlist,
|
|
203
|
+
safeBins: params.safeBins,
|
|
204
|
+
cwd: params.cwd,
|
|
205
|
+
skillBins: params.skillBins,
|
|
206
|
+
autoAllowSkills: params.autoAllowSkills,
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
analysisOk: true,
|
|
210
|
+
allowlistSatisfied: evaluation.allowlistSatisfied,
|
|
211
|
+
allowlistMatches: evaluation.allowlistMatches,
|
|
212
|
+
segments: analysis.segments,
|
|
213
|
+
segmentSatisfiedBy: evaluation.segmentSatisfiedBy,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const allowlistMatches = [];
|
|
217
|
+
const segments = [];
|
|
218
|
+
const segmentSatisfiedBy = [];
|
|
219
|
+
for (const part of chainParts) {
|
|
220
|
+
const analysis = analyzeShellCommand({
|
|
221
|
+
command: part,
|
|
222
|
+
cwd: params.cwd,
|
|
223
|
+
env: params.env,
|
|
224
|
+
platform: params.platform,
|
|
225
|
+
});
|
|
226
|
+
if (!analysis.ok) {
|
|
227
|
+
return analysisFailure();
|
|
228
|
+
}
|
|
229
|
+
segments.push(...analysis.segments);
|
|
230
|
+
const evaluation = evaluateExecAllowlist({
|
|
231
|
+
analysis,
|
|
232
|
+
allowlist: params.allowlist,
|
|
233
|
+
safeBins: params.safeBins,
|
|
234
|
+
cwd: params.cwd,
|
|
235
|
+
skillBins: params.skillBins,
|
|
236
|
+
autoAllowSkills: params.autoAllowSkills,
|
|
237
|
+
});
|
|
238
|
+
allowlistMatches.push(...evaluation.allowlistMatches);
|
|
239
|
+
segmentSatisfiedBy.push(...evaluation.segmentSatisfiedBy);
|
|
240
|
+
if (!evaluation.allowlistSatisfied) {
|
|
241
|
+
return {
|
|
242
|
+
analysisOk: true,
|
|
243
|
+
allowlistSatisfied: false,
|
|
244
|
+
allowlistMatches,
|
|
245
|
+
segments,
|
|
246
|
+
segmentSatisfiedBy,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
analysisOk: true,
|
|
252
|
+
allowlistSatisfied: true,
|
|
253
|
+
allowlistMatches,
|
|
254
|
+
segments,
|
|
255
|
+
segmentSatisfiedBy,
|
|
256
|
+
};
|
|
257
|
+
}
|