@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
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { resolveSandboxInputPath, resolveSandboxPath } from "../sandbox-paths.js";
|
|
3
|
+
import { SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js";
|
|
4
|
+
export function parseSandboxBindMount(spec) {
|
|
5
|
+
const trimmed = spec.trim();
|
|
6
|
+
if (!trimmed) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const parsed = splitBindSpec(trimmed);
|
|
10
|
+
if (!parsed) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const hostToken = parsed.host.trim();
|
|
14
|
+
const containerToken = parsed.container.trim();
|
|
15
|
+
if (!hostToken || !containerToken || !path.posix.isAbsolute(containerToken)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const optionsToken = parsed.options.trim().toLowerCase();
|
|
19
|
+
const optionParts = optionsToken
|
|
20
|
+
? optionsToken
|
|
21
|
+
.split(",")
|
|
22
|
+
.map((entry) => entry.trim())
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
: [];
|
|
25
|
+
const writable = !optionParts.includes("ro");
|
|
26
|
+
return {
|
|
27
|
+
hostRoot: path.resolve(hostToken),
|
|
28
|
+
containerRoot: normalizeContainerPath(containerToken),
|
|
29
|
+
writable,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function splitBindSpec(spec) {
|
|
33
|
+
const separator = getHostContainerSeparatorIndex(spec);
|
|
34
|
+
if (separator === -1) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const host = spec.slice(0, separator);
|
|
38
|
+
const rest = spec.slice(separator + 1);
|
|
39
|
+
const optionsStart = rest.indexOf(":");
|
|
40
|
+
if (optionsStart === -1) {
|
|
41
|
+
return { host, container: rest, options: "" };
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
host,
|
|
45
|
+
container: rest.slice(0, optionsStart),
|
|
46
|
+
options: rest.slice(optionsStart + 1),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function getHostContainerSeparatorIndex(spec) {
|
|
50
|
+
const hasDriveLetterPrefix = /^[A-Za-z]:[\\/]/.test(spec);
|
|
51
|
+
for (let i = hasDriveLetterPrefix ? 2 : 0; i < spec.length; i += 1) {
|
|
52
|
+
if (spec[i] === ":") {
|
|
53
|
+
return i;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
export function buildSandboxFsMounts(sandbox) {
|
|
59
|
+
const mounts = [
|
|
60
|
+
{
|
|
61
|
+
hostRoot: path.resolve(sandbox.workspaceDir),
|
|
62
|
+
containerRoot: normalizeContainerPath(sandbox.containerWorkdir),
|
|
63
|
+
writable: sandbox.workspaceAccess === "rw",
|
|
64
|
+
source: "workspace",
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
if (sandbox.workspaceAccess !== "none" &&
|
|
68
|
+
path.resolve(sandbox.agentWorkspaceDir) !== path.resolve(sandbox.workspaceDir)) {
|
|
69
|
+
mounts.push({
|
|
70
|
+
hostRoot: path.resolve(sandbox.agentWorkspaceDir),
|
|
71
|
+
containerRoot: SANDBOX_AGENT_WORKSPACE_MOUNT,
|
|
72
|
+
writable: sandbox.workspaceAccess === "rw",
|
|
73
|
+
source: "agent",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
for (const bind of sandbox.docker.binds ?? []) {
|
|
77
|
+
const parsed = parseSandboxBindMount(bind);
|
|
78
|
+
if (!parsed) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
mounts.push({
|
|
82
|
+
hostRoot: parsed.hostRoot,
|
|
83
|
+
containerRoot: parsed.containerRoot,
|
|
84
|
+
writable: parsed.writable,
|
|
85
|
+
source: "bind",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return dedupeMounts(mounts);
|
|
89
|
+
}
|
|
90
|
+
export function resolveSandboxFsPathWithMounts(params) {
|
|
91
|
+
const mountsByContainer = [...params.mounts].toSorted((a, b) => b.containerRoot.length - a.containerRoot.length);
|
|
92
|
+
const mountsByHost = [...params.mounts].toSorted((a, b) => b.hostRoot.length - a.hostRoot.length);
|
|
93
|
+
const input = params.filePath;
|
|
94
|
+
const inputPosix = normalizePosixInput(input);
|
|
95
|
+
if (path.posix.isAbsolute(inputPosix)) {
|
|
96
|
+
const containerMount = findMountByContainerPath(mountsByContainer, inputPosix);
|
|
97
|
+
if (containerMount) {
|
|
98
|
+
const rel = path.posix.relative(containerMount.containerRoot, inputPosix);
|
|
99
|
+
const hostPath = rel
|
|
100
|
+
? path.resolve(containerMount.hostRoot, ...toHostSegments(rel))
|
|
101
|
+
: containerMount.hostRoot;
|
|
102
|
+
return {
|
|
103
|
+
hostPath,
|
|
104
|
+
containerPath: rel
|
|
105
|
+
? path.posix.join(containerMount.containerRoot, rel)
|
|
106
|
+
: containerMount.containerRoot,
|
|
107
|
+
relativePath: toDisplayRelative({
|
|
108
|
+
containerPath: rel
|
|
109
|
+
? path.posix.join(containerMount.containerRoot, rel)
|
|
110
|
+
: containerMount.containerRoot,
|
|
111
|
+
defaultContainerRoot: params.defaultContainerRoot,
|
|
112
|
+
}),
|
|
113
|
+
writable: containerMount.writable,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const hostResolved = resolveSandboxInputPath(input, params.cwd);
|
|
118
|
+
const hostMount = findMountByHostPath(mountsByHost, hostResolved);
|
|
119
|
+
if (hostMount) {
|
|
120
|
+
const relHost = path.relative(hostMount.hostRoot, hostResolved);
|
|
121
|
+
const relPosix = relHost ? relHost.split(path.sep).join(path.posix.sep) : "";
|
|
122
|
+
const containerPath = relPosix
|
|
123
|
+
? path.posix.join(hostMount.containerRoot, relPosix)
|
|
124
|
+
: hostMount.containerRoot;
|
|
125
|
+
return {
|
|
126
|
+
hostPath: hostResolved,
|
|
127
|
+
containerPath,
|
|
128
|
+
relativePath: toDisplayRelative({
|
|
129
|
+
containerPath,
|
|
130
|
+
defaultContainerRoot: params.defaultContainerRoot,
|
|
131
|
+
}),
|
|
132
|
+
writable: hostMount.writable,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Preserve legacy error wording for out-of-sandbox paths.
|
|
136
|
+
resolveSandboxPath({
|
|
137
|
+
filePath: input,
|
|
138
|
+
cwd: params.cwd,
|
|
139
|
+
root: params.defaultWorkspaceRoot,
|
|
140
|
+
});
|
|
141
|
+
throw new Error(`Path escapes sandbox root (${params.defaultWorkspaceRoot}): ${input}`);
|
|
142
|
+
}
|
|
143
|
+
function dedupeMounts(mounts) {
|
|
144
|
+
const seen = new Set();
|
|
145
|
+
const deduped = [];
|
|
146
|
+
for (const mount of mounts) {
|
|
147
|
+
const key = `${mount.hostRoot}=>${mount.containerRoot}`;
|
|
148
|
+
if (seen.has(key)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
seen.add(key);
|
|
152
|
+
deduped.push(mount);
|
|
153
|
+
}
|
|
154
|
+
return deduped;
|
|
155
|
+
}
|
|
156
|
+
function findMountByContainerPath(mounts, target) {
|
|
157
|
+
for (const mount of mounts) {
|
|
158
|
+
if (isPathInsidePosix(mount.containerRoot, target)) {
|
|
159
|
+
return mount;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function findMountByHostPath(mounts, target) {
|
|
165
|
+
for (const mount of mounts) {
|
|
166
|
+
if (isPathInsideHost(mount.hostRoot, target)) {
|
|
167
|
+
return mount;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function isPathInsidePosix(root, target) {
|
|
173
|
+
const rel = path.posix.relative(root, target);
|
|
174
|
+
if (!rel) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
return !(rel.startsWith("..") || path.posix.isAbsolute(rel));
|
|
178
|
+
}
|
|
179
|
+
function isPathInsideHost(root, target) {
|
|
180
|
+
const rel = path.relative(root, target);
|
|
181
|
+
if (!rel) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
return !(rel.startsWith("..") || path.isAbsolute(rel));
|
|
185
|
+
}
|
|
186
|
+
function toHostSegments(relativePosix) {
|
|
187
|
+
return relativePosix.split("/").filter(Boolean);
|
|
188
|
+
}
|
|
189
|
+
function toDisplayRelative(params) {
|
|
190
|
+
const rel = path.posix.relative(params.defaultContainerRoot, params.containerPath);
|
|
191
|
+
if (!rel) {
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
194
|
+
if (!rel.startsWith("..") && !path.posix.isAbsolute(rel)) {
|
|
195
|
+
return rel;
|
|
196
|
+
}
|
|
197
|
+
return params.containerPath;
|
|
198
|
+
}
|
|
199
|
+
function normalizeContainerPath(value) {
|
|
200
|
+
const normalized = path.posix.normalize(value);
|
|
201
|
+
return normalized === "." ? "/" : normalized;
|
|
202
|
+
}
|
|
203
|
+
function normalizePosixInput(value) {
|
|
204
|
+
return value.replace(/\\/g, "/").trim();
|
|
205
|
+
}
|
|
@@ -24,6 +24,9 @@ function resolveToCwd(filePath, cwd) {
|
|
|
24
24
|
return expanded;
|
|
25
25
|
return path.resolve(cwd, expanded);
|
|
26
26
|
}
|
|
27
|
+
export function resolveSandboxInputPath(filePath, cwd) {
|
|
28
|
+
return resolveToCwd(filePath, cwd);
|
|
29
|
+
}
|
|
27
30
|
export function resolveSandboxPath(params) {
|
|
28
31
|
const resolved = resolveToCwd(params.filePath, params.cwd);
|
|
29
32
|
const rootResolved = path.resolve(params.root);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function resolveAgentSessionDirs(stateDir) {
|
|
4
|
+
const agentsDir = path.join(stateDir, "agents");
|
|
5
|
+
let entries = [];
|
|
6
|
+
try {
|
|
7
|
+
entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
const code = err.code;
|
|
11
|
+
if (code === "ENOENT") {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
return entries
|
|
17
|
+
.filter((entry) => entry.isDirectory())
|
|
18
|
+
.map((entry) => path.join(agentsDir, entry.name, "sessions"))
|
|
19
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function normalizeSkillFilter(skillFilter) {
|
|
2
|
+
if (skillFilter === undefined) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
return skillFilter.map((entry) => String(entry).trim()).filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
export function normalizeSkillFilterForComparison(skillFilter) {
|
|
8
|
+
const normalized = normalizeSkillFilter(skillFilter);
|
|
9
|
+
if (normalized === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return Array.from(new Set(normalized)).toSorted();
|
|
13
|
+
}
|
|
14
|
+
export function matchesSkillFilter(cached, next) {
|
|
15
|
+
const cachedNormalized = normalizeSkillFilterForComparison(cached);
|
|
16
|
+
const nextNormalized = normalizeSkillFilterForComparison(next);
|
|
17
|
+
if (cachedNormalized === undefined || nextNormalized === undefined) {
|
|
18
|
+
return cachedNormalized === nextNormalized;
|
|
19
|
+
}
|
|
20
|
+
if (cachedNormalized.length !== nextNormalized.length) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return cachedNormalized.every((entry, index) => entry === nextNormalized[index]);
|
|
24
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { safePathSegmentHashed } from "../../infra/install-safe-path.js";
|
|
3
|
+
import { resolveConfigDir } from "../../utils.js";
|
|
4
|
+
import { resolveSkillKey } from "./frontmatter.js";
|
|
5
|
+
export function resolveSkillToolsRootDir(entry) {
|
|
6
|
+
const key = resolveSkillKey(entry.skill, entry);
|
|
7
|
+
const safeKey = safePathSegmentHashed(key);
|
|
8
|
+
return path.join(resolveConfigDir(), "tools", safeKey);
|
|
9
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Readable } from "node:stream";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
5
|
+
import { extractArchive as extractArchiveSafe } from "../infra/archive.js";
|
|
6
|
+
import { fetchWithSsrFGuard } from "../infra/net/fetch-guard.js";
|
|
7
|
+
import { isWithinDir, resolveSafeBaseDir } from "../infra/path-safety.js";
|
|
8
|
+
import { runCommandWithTimeout } from "../process/exec.js";
|
|
9
|
+
import { ensureDir, resolveUserPath } from "../utils.js";
|
|
10
|
+
import { formatInstallFailureMessage } from "./skills-install-output.js";
|
|
11
|
+
import { hasBinary } from "./skills.js";
|
|
12
|
+
import { resolveSkillToolsRootDir } from "./skills/tools-dir.js";
|
|
13
|
+
function isNodeReadableStream(value) {
|
|
14
|
+
return Boolean(value && typeof value.pipe === "function");
|
|
15
|
+
}
|
|
16
|
+
function isWindowsDrivePath(p) {
|
|
17
|
+
return /^[a-zA-Z]:[\\/]/.test(p);
|
|
18
|
+
}
|
|
19
|
+
function resolveDownloadTargetDir(entry, spec) {
|
|
20
|
+
const safeRoot = resolveSkillToolsRootDir(entry);
|
|
21
|
+
const raw = spec.targetDir?.trim();
|
|
22
|
+
if (!raw) {
|
|
23
|
+
return safeRoot;
|
|
24
|
+
}
|
|
25
|
+
// Treat non-absolute paths as relative to the per-skill tools root.
|
|
26
|
+
const resolved = raw.startsWith("~") || path.isAbsolute(raw) || isWindowsDrivePath(raw)
|
|
27
|
+
? resolveUserPath(raw)
|
|
28
|
+
: path.resolve(safeRoot, raw);
|
|
29
|
+
if (!isWithinDir(safeRoot, resolved)) {
|
|
30
|
+
throw new Error(`Refusing to install outside the skill tools directory. targetDir="${raw}" resolves to "${resolved}". Allowed root: "${safeRoot}".`);
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
function resolveArchiveType(spec, filename) {
|
|
35
|
+
const explicit = spec.archive?.trim().toLowerCase();
|
|
36
|
+
if (explicit) {
|
|
37
|
+
return explicit;
|
|
38
|
+
}
|
|
39
|
+
const lower = filename.toLowerCase();
|
|
40
|
+
if (lower.endsWith(".tar.gz") || lower.endsWith(".tgz")) {
|
|
41
|
+
return "tar.gz";
|
|
42
|
+
}
|
|
43
|
+
if (lower.endsWith(".tar.bz2") || lower.endsWith(".tbz2")) {
|
|
44
|
+
return "tar.bz2";
|
|
45
|
+
}
|
|
46
|
+
if (lower.endsWith(".zip")) {
|
|
47
|
+
return "zip";
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
function normalizeArchiveEntryPath(raw) {
|
|
52
|
+
return raw.replaceAll("\\", "/");
|
|
53
|
+
}
|
|
54
|
+
function validateArchiveEntryPath(entryPath) {
|
|
55
|
+
if (!entryPath || entryPath === "." || entryPath === "./") {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (isWindowsDrivePath(entryPath)) {
|
|
59
|
+
throw new Error(`archive entry uses a drive path: ${entryPath}`);
|
|
60
|
+
}
|
|
61
|
+
const normalized = path.posix.normalize(normalizeArchiveEntryPath(entryPath));
|
|
62
|
+
if (normalized === ".." || normalized.startsWith("../")) {
|
|
63
|
+
throw new Error(`archive entry escapes targetDir: ${entryPath}`);
|
|
64
|
+
}
|
|
65
|
+
if (path.posix.isAbsolute(normalized) || normalized.startsWith("//")) {
|
|
66
|
+
throw new Error(`archive entry is absolute: ${entryPath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function stripArchivePath(entryPath, stripComponents) {
|
|
70
|
+
const raw = normalizeArchiveEntryPath(entryPath);
|
|
71
|
+
if (!raw || raw === "." || raw === "./") {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
// Important: tar's --strip-components semantics operate on raw path segments,
|
|
75
|
+
// before any normalization that would collapse "..". We mimic that so we
|
|
76
|
+
// can detect strip-induced escapes like "a/../b" with stripComponents=1.
|
|
77
|
+
const parts = raw.split("/").filter((part) => part.length > 0 && part !== ".");
|
|
78
|
+
const strip = Math.max(0, Math.floor(stripComponents));
|
|
79
|
+
const stripped = strip === 0 ? parts.join("/") : parts.slice(strip).join("/");
|
|
80
|
+
const result = path.posix.normalize(stripped);
|
|
81
|
+
if (!result || result === "." || result === "./") {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
function validateExtractedPathWithinRoot(params) {
|
|
87
|
+
const safeBase = resolveSafeBaseDir(params.rootDir);
|
|
88
|
+
const outPath = path.resolve(params.rootDir, params.relPath);
|
|
89
|
+
if (!outPath.startsWith(safeBase)) {
|
|
90
|
+
throw new Error(`archive entry escapes targetDir: ${params.originalPath}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function downloadFile(url, destPath, timeoutMs) {
|
|
94
|
+
const { response, release } = await fetchWithSsrFGuard({
|
|
95
|
+
url,
|
|
96
|
+
timeoutMs: Math.max(1_000, timeoutMs),
|
|
97
|
+
});
|
|
98
|
+
try {
|
|
99
|
+
if (!response.ok || !response.body) {
|
|
100
|
+
throw new Error(`Download failed (${response.status} ${response.statusText})`);
|
|
101
|
+
}
|
|
102
|
+
await ensureDir(path.dirname(destPath));
|
|
103
|
+
const file = fs.createWriteStream(destPath);
|
|
104
|
+
const body = response.body;
|
|
105
|
+
const readable = isNodeReadableStream(body)
|
|
106
|
+
? body
|
|
107
|
+
: Readable.fromWeb(body);
|
|
108
|
+
await pipeline(readable, file);
|
|
109
|
+
const stat = await fs.promises.stat(destPath);
|
|
110
|
+
return { bytes: stat.size };
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await release();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function extractArchive(params) {
|
|
117
|
+
const { archivePath, archiveType, targetDir, stripComponents, timeoutMs } = params;
|
|
118
|
+
const strip = typeof stripComponents === "number" && Number.isFinite(stripComponents)
|
|
119
|
+
? Math.max(0, Math.floor(stripComponents))
|
|
120
|
+
: 0;
|
|
121
|
+
try {
|
|
122
|
+
if (archiveType === "zip") {
|
|
123
|
+
await extractArchiveSafe({
|
|
124
|
+
archivePath,
|
|
125
|
+
destDir: targetDir,
|
|
126
|
+
timeoutMs,
|
|
127
|
+
kind: "zip",
|
|
128
|
+
stripComponents: strip,
|
|
129
|
+
});
|
|
130
|
+
return { stdout: "", stderr: "", code: 0 };
|
|
131
|
+
}
|
|
132
|
+
if (archiveType === "tar.gz") {
|
|
133
|
+
await extractArchiveSafe({
|
|
134
|
+
archivePath,
|
|
135
|
+
destDir: targetDir,
|
|
136
|
+
timeoutMs,
|
|
137
|
+
kind: "tar",
|
|
138
|
+
stripComponents: strip,
|
|
139
|
+
tarGzip: true,
|
|
140
|
+
});
|
|
141
|
+
return { stdout: "", stderr: "", code: 0 };
|
|
142
|
+
}
|
|
143
|
+
if (archiveType === "tar.bz2") {
|
|
144
|
+
if (!hasBinary("tar")) {
|
|
145
|
+
return { stdout: "", stderr: "tar not found on PATH", code: null };
|
|
146
|
+
}
|
|
147
|
+
// Preflight list to prevent zip-slip style traversal before extraction.
|
|
148
|
+
const listResult = await runCommandWithTimeout(["tar", "tf", archivePath], { timeoutMs });
|
|
149
|
+
if (listResult.code !== 0) {
|
|
150
|
+
return {
|
|
151
|
+
stdout: listResult.stdout,
|
|
152
|
+
stderr: listResult.stderr || "tar list failed",
|
|
153
|
+
code: listResult.code,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const entries = listResult.stdout
|
|
157
|
+
.split("\n")
|
|
158
|
+
.map((line) => line.trim())
|
|
159
|
+
.filter(Boolean);
|
|
160
|
+
const verboseResult = await runCommandWithTimeout(["tar", "tvf", archivePath], { timeoutMs });
|
|
161
|
+
if (verboseResult.code !== 0) {
|
|
162
|
+
return {
|
|
163
|
+
stdout: verboseResult.stdout,
|
|
164
|
+
stderr: verboseResult.stderr || "tar verbose list failed",
|
|
165
|
+
code: verboseResult.code,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
for (const line of verboseResult.stdout.split("\n")) {
|
|
169
|
+
const trimmed = line.trim();
|
|
170
|
+
if (!trimmed) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const typeChar = trimmed[0];
|
|
174
|
+
if (typeChar === "l" || typeChar === "h" || trimmed.includes(" -> ")) {
|
|
175
|
+
return {
|
|
176
|
+
stdout: verboseResult.stdout,
|
|
177
|
+
stderr: "tar archive contains link entries; refusing to extract for safety",
|
|
178
|
+
code: 1,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
validateArchiveEntryPath(entry);
|
|
184
|
+
const relPath = stripArchivePath(entry, strip);
|
|
185
|
+
if (!relPath) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
validateArchiveEntryPath(relPath);
|
|
189
|
+
validateExtractedPathWithinRoot({ rootDir: targetDir, relPath, originalPath: entry });
|
|
190
|
+
}
|
|
191
|
+
const argv = ["tar", "xf", archivePath, "-C", targetDir];
|
|
192
|
+
if (strip > 0) {
|
|
193
|
+
argv.push("--strip-components", String(strip));
|
|
194
|
+
}
|
|
195
|
+
return await runCommandWithTimeout(argv, { timeoutMs });
|
|
196
|
+
}
|
|
197
|
+
return { stdout: "", stderr: `unsupported archive type: ${archiveType}`, code: null };
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
201
|
+
return { stdout: "", stderr: message, code: 1 };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export async function installDownloadSpec(params) {
|
|
205
|
+
const { entry, spec, timeoutMs } = params;
|
|
206
|
+
const url = spec.url?.trim();
|
|
207
|
+
if (!url) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
message: "missing download url",
|
|
211
|
+
stdout: "",
|
|
212
|
+
stderr: "",
|
|
213
|
+
code: null,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
let filename = "";
|
|
217
|
+
try {
|
|
218
|
+
const parsed = new URL(url);
|
|
219
|
+
filename = path.basename(parsed.pathname);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
filename = path.basename(url);
|
|
223
|
+
}
|
|
224
|
+
if (!filename) {
|
|
225
|
+
filename = "download";
|
|
226
|
+
}
|
|
227
|
+
let targetDir = "";
|
|
228
|
+
try {
|
|
229
|
+
targetDir = resolveDownloadTargetDir(entry, spec);
|
|
230
|
+
await ensureDir(targetDir);
|
|
231
|
+
const stat = await fs.promises.lstat(targetDir);
|
|
232
|
+
if (stat.isSymbolicLink()) {
|
|
233
|
+
throw new Error(`targetDir is a symlink: ${targetDir}`);
|
|
234
|
+
}
|
|
235
|
+
if (!stat.isDirectory()) {
|
|
236
|
+
throw new Error(`targetDir is not a directory: ${targetDir}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
241
|
+
return { ok: false, message, stdout: "", stderr: message, code: null };
|
|
242
|
+
}
|
|
243
|
+
const archivePath = path.join(targetDir, filename);
|
|
244
|
+
let downloaded = 0;
|
|
245
|
+
try {
|
|
246
|
+
const result = await downloadFile(url, archivePath, timeoutMs);
|
|
247
|
+
downloaded = result.bytes;
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
251
|
+
return { ok: false, message, stdout: "", stderr: message, code: null };
|
|
252
|
+
}
|
|
253
|
+
const archiveType = resolveArchiveType(spec, filename);
|
|
254
|
+
const shouldExtract = spec.extract ?? Boolean(archiveType);
|
|
255
|
+
if (!shouldExtract) {
|
|
256
|
+
return {
|
|
257
|
+
ok: true,
|
|
258
|
+
message: `Downloaded to ${archivePath}`,
|
|
259
|
+
stdout: `downloaded=${downloaded}`,
|
|
260
|
+
stderr: "",
|
|
261
|
+
code: 0,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (!archiveType) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
message: "extract requested but archive type could not be detected",
|
|
268
|
+
stdout: "",
|
|
269
|
+
stderr: "",
|
|
270
|
+
code: null,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
const extractResult = await extractArchive({
|
|
274
|
+
archivePath,
|
|
275
|
+
archiveType,
|
|
276
|
+
targetDir,
|
|
277
|
+
stripComponents: spec.stripComponents,
|
|
278
|
+
timeoutMs,
|
|
279
|
+
});
|
|
280
|
+
const success = extractResult.code === 0;
|
|
281
|
+
return {
|
|
282
|
+
ok: success,
|
|
283
|
+
message: success
|
|
284
|
+
? `Downloaded and extracted to ${targetDir}`
|
|
285
|
+
: formatInstallFailureMessage(extractResult),
|
|
286
|
+
stdout: extractResult.stdout.trim(),
|
|
287
|
+
stderr: extractResult.stderr.trim(),
|
|
288
|
+
code: extractResult.code,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function summarizeInstallOutput(text) {
|
|
2
|
+
const raw = text.trim();
|
|
3
|
+
if (!raw) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const lines = raw
|
|
7
|
+
.split("\n")
|
|
8
|
+
.map((line) => line.trim())
|
|
9
|
+
.filter(Boolean);
|
|
10
|
+
if (lines.length === 0) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const preferred = lines.find((line) => /^error\b/i.test(line)) ??
|
|
14
|
+
lines.find((line) => /\b(err!|error:|failed)\b/i.test(line)) ??
|
|
15
|
+
lines.at(-1);
|
|
16
|
+
if (!preferred) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const normalized = preferred.replace(/\s+/g, " ").trim();
|
|
20
|
+
const maxLen = 200;
|
|
21
|
+
return normalized.length > maxLen ? `${normalized.slice(0, maxLen - 1)}…` : normalized;
|
|
22
|
+
}
|
|
23
|
+
export function formatInstallFailureMessage(result) {
|
|
24
|
+
const code = typeof result.code === "number" ? `exit ${result.code}` : "unknown exit";
|
|
25
|
+
const summary = summarizeInstallOutput(result.stderr) ?? summarizeInstallOutput(result.stdout);
|
|
26
|
+
if (!summary) {
|
|
27
|
+
return `Install failed (${code})`;
|
|
28
|
+
}
|
|
29
|
+
return `Install failed (${code}): ${summary}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function setTempStateDir(workspaceDir) {
|
|
4
|
+
const stateDir = path.join(workspaceDir, "state");
|
|
5
|
+
process.env.POOLBOT_STATE_DIR = stateDir;
|
|
6
|
+
return stateDir;
|
|
7
|
+
}
|
|
8
|
+
export async function writeDownloadSkill(params) {
|
|
9
|
+
const skillDir = path.join(params.workspaceDir, "skills", params.name);
|
|
10
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
11
|
+
const meta = {
|
|
12
|
+
openclaw: {
|
|
13
|
+
install: [
|
|
14
|
+
{
|
|
15
|
+
id: params.installId,
|
|
16
|
+
kind: "download",
|
|
17
|
+
url: params.url,
|
|
18
|
+
archive: params.archive,
|
|
19
|
+
extract: true,
|
|
20
|
+
stripComponents: params.stripComponents,
|
|
21
|
+
targetDir: params.targetDir,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
await fs.writeFile(path.join(skillDir, "SKILL.md"), `---
|
|
27
|
+
name: ${params.name}
|
|
28
|
+
description: test skill
|
|
29
|
+
metadata: ${JSON.stringify(meta)}
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
# ${params.name}
|
|
33
|
+
`, "utf-8");
|
|
34
|
+
await fs.writeFile(path.join(skillDir, "runner.js"), "export {};\n", "utf-8");
|
|
35
|
+
return skillDir;
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function writeSkill(params) {
|
|
4
|
+
const { dir, name, description, metadata, body } = params;
|
|
5
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6
|
+
await fs.writeFile(path.join(dir, "SKILL.md"), `---
|
|
7
|
+
name: ${name}
|
|
8
|
+
description: ${description}${metadata ? `\nmetadata: ${metadata}` : ""}
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
${body ?? `# ${name}\n`}
|
|
12
|
+
`, "utf-8");
|
|
13
|
+
}
|