@poolzin/pool-bot 2026.2.17 → 2026.2.19
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 +25 -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/device-pair/poolbot.plugin.json +20 -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/package.json +37 -0
- package/extensions/feishu/poolbot.plugin.json +10 -0
- package/extensions/feishu/skills/feishu-doc/SKILL.md +105 -0
- package/extensions/feishu/skills/feishu-doc/references/block-types.md +103 -0
- package/extensions/feishu/skills/feishu-drive/SKILL.md +97 -0
- package/extensions/feishu/skills/feishu-perm/SKILL.md +119 -0
- package/extensions/feishu/skills/feishu-wiki/SKILL.md +111 -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/package.json +14 -0
- package/extensions/irc/poolbot.plugin.json +9 -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/README.md +33 -0
- package/extensions/minimax-portal-auth/index.ts +161 -0
- package/extensions/minimax-portal-auth/oauth.ts +247 -0
- package/extensions/minimax-portal-auth/package.json +15 -0
- package/extensions/minimax-portal-auth/poolbot.plugin.json +9 -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/README.md +82 -0
- package/extensions/openai-codex-auth/index.ts +177 -0
- package/extensions/openai-codex-auth/package.json +15 -0
- package/extensions/openai-codex-auth/poolbot.plugin.json +9 -0
- package/extensions/phone-control/index.ts +421 -0
- package/extensions/phone-control/poolbot.plugin.json +10 -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/talk-voice/poolbot.plugin.json +10 -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/thread-ownership/poolbot.plugin.json +28 -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,90 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
import { subscribeEmbeddedPiSession } from "./pi-embedded-subscribe.js";
|
|
3
|
+
export function createStubSessionHarness() {
|
|
4
|
+
let handler;
|
|
5
|
+
const session = {
|
|
6
|
+
subscribe: (fn) => {
|
|
7
|
+
handler = fn;
|
|
8
|
+
return () => { };
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
return { session, emit: (evt) => handler?.(evt) };
|
|
12
|
+
}
|
|
13
|
+
export function createSubscribedSessionHarness(params) {
|
|
14
|
+
const { sessionExtras, ...subscribeParams } = params;
|
|
15
|
+
const { session, emit } = createStubSessionHarness();
|
|
16
|
+
const mergedSession = Object.assign(session, sessionExtras ?? {});
|
|
17
|
+
const subscription = subscribeEmbeddedPiSession({
|
|
18
|
+
...subscribeParams,
|
|
19
|
+
session: mergedSession,
|
|
20
|
+
});
|
|
21
|
+
return { emit, session: mergedSession, subscription };
|
|
22
|
+
}
|
|
23
|
+
export function createParagraphChunkedBlockReplyHarness(params) {
|
|
24
|
+
const onBlockReply = params.onBlockReply ?? (() => { });
|
|
25
|
+
const { emit, subscription } = createSubscribedSessionHarness({
|
|
26
|
+
runId: params.runId ?? "run",
|
|
27
|
+
onBlockReply,
|
|
28
|
+
blockReplyBreak: "message_end",
|
|
29
|
+
blockReplyChunking: {
|
|
30
|
+
...params.chunking,
|
|
31
|
+
breakPreference: "paragraph",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
return { emit, onBlockReply, subscription };
|
|
35
|
+
}
|
|
36
|
+
export function extractAgentEventPayloads(calls) {
|
|
37
|
+
return calls
|
|
38
|
+
.map((call) => {
|
|
39
|
+
const first = call?.[0];
|
|
40
|
+
const data = first?.data;
|
|
41
|
+
return data && typeof data === "object" ? data : undefined;
|
|
42
|
+
})
|
|
43
|
+
.filter((value) => Boolean(value));
|
|
44
|
+
}
|
|
45
|
+
export function extractTextPayloads(calls) {
|
|
46
|
+
return calls
|
|
47
|
+
.map((call) => {
|
|
48
|
+
const payload = call?.[0];
|
|
49
|
+
return typeof payload?.text === "string" ? payload.text : undefined;
|
|
50
|
+
})
|
|
51
|
+
.filter((text) => Boolean(text));
|
|
52
|
+
}
|
|
53
|
+
export function emitMessageStartAndEndForAssistantText(params) {
|
|
54
|
+
const assistantMessage = {
|
|
55
|
+
role: "assistant",
|
|
56
|
+
content: [{ type: "text", text: params.text }],
|
|
57
|
+
};
|
|
58
|
+
params.emit({ type: "message_start", message: assistantMessage });
|
|
59
|
+
params.emit({ type: "message_end", message: assistantMessage });
|
|
60
|
+
}
|
|
61
|
+
export function emitAssistantTextDeltaAndEnd(params) {
|
|
62
|
+
params.emit({
|
|
63
|
+
type: "message_update",
|
|
64
|
+
message: { role: "assistant" },
|
|
65
|
+
assistantMessageEvent: {
|
|
66
|
+
type: "text_delta",
|
|
67
|
+
delta: params.text,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const assistantMessage = {
|
|
71
|
+
role: "assistant",
|
|
72
|
+
content: [{ type: "text", text: params.text }],
|
|
73
|
+
};
|
|
74
|
+
params.emit({ type: "message_end", message: assistantMessage });
|
|
75
|
+
}
|
|
76
|
+
export function expectFencedChunks(calls, expectedPrefix) {
|
|
77
|
+
expect(calls.length).toBeGreaterThan(1);
|
|
78
|
+
for (const call of calls) {
|
|
79
|
+
const chunk = call[0]?.text;
|
|
80
|
+
expect(typeof chunk === "string" && chunk.startsWith(expectedPrefix)).toBe(true);
|
|
81
|
+
const fenceCount = typeof chunk === "string" ? (chunk.match(/```/g)?.length ?? 0) : 0;
|
|
82
|
+
expect(fenceCount).toBeGreaterThanOrEqual(2);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function expectSingleAgentEventText(calls, text) {
|
|
86
|
+
const payloads = extractAgentEventPayloads(calls);
|
|
87
|
+
expect(payloads).toHaveLength(1);
|
|
88
|
+
expect(payloads[0]?.text).toBe(text);
|
|
89
|
+
expect(payloads[0]?.delta).toBe(text);
|
|
90
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { emitAgentEvent } from "../infra/agent-events.js";
|
|
2
|
+
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
|
|
3
|
+
export function handleAutoCompactionStart(ctx) {
|
|
4
|
+
ctx.state.compactionInFlight = true;
|
|
5
|
+
ctx.incrementCompactionCount();
|
|
6
|
+
ctx.ensureCompactionPromise();
|
|
7
|
+
ctx.log.debug(`embedded run compaction start: runId=${ctx.params.runId}`);
|
|
8
|
+
emitAgentEvent({
|
|
9
|
+
runId: ctx.params.runId,
|
|
10
|
+
stream: "compaction",
|
|
11
|
+
data: { phase: "start" },
|
|
12
|
+
});
|
|
13
|
+
void ctx.params.onAgentEvent?.({
|
|
14
|
+
stream: "compaction",
|
|
15
|
+
data: { phase: "start" },
|
|
16
|
+
});
|
|
17
|
+
// Run before_compaction plugin hook (fire-and-forget)
|
|
18
|
+
const hookRunner = getGlobalHookRunner();
|
|
19
|
+
if (hookRunner?.hasHooks("before_compaction")) {
|
|
20
|
+
void hookRunner
|
|
21
|
+
.runBeforeCompaction({
|
|
22
|
+
messageCount: ctx.params.session.messages?.length ?? 0,
|
|
23
|
+
}, {})
|
|
24
|
+
.catch((err) => {
|
|
25
|
+
ctx.log.warn(`before_compaction hook failed: ${String(err)}`);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function handleAutoCompactionEnd(ctx, evt) {
|
|
30
|
+
ctx.state.compactionInFlight = false;
|
|
31
|
+
const willRetry = Boolean(evt.willRetry);
|
|
32
|
+
if (willRetry) {
|
|
33
|
+
ctx.noteCompactionRetry();
|
|
34
|
+
ctx.resetForCompactionRetry();
|
|
35
|
+
ctx.log.debug(`embedded run compaction retry: runId=${ctx.params.runId}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
ctx.maybeResolveCompactionWait();
|
|
39
|
+
}
|
|
40
|
+
emitAgentEvent({
|
|
41
|
+
runId: ctx.params.runId,
|
|
42
|
+
stream: "compaction",
|
|
43
|
+
data: { phase: "end", willRetry },
|
|
44
|
+
});
|
|
45
|
+
void ctx.params.onAgentEvent?.({
|
|
46
|
+
stream: "compaction",
|
|
47
|
+
data: { phase: "end", willRetry },
|
|
48
|
+
});
|
|
49
|
+
// Run after_compaction plugin hook (fire-and-forget)
|
|
50
|
+
if (!willRetry) {
|
|
51
|
+
const hookRunnerEnd = getGlobalHookRunner();
|
|
52
|
+
if (hookRunnerEnd?.hasHooks("after_compaction")) {
|
|
53
|
+
void hookRunnerEnd
|
|
54
|
+
.runAfterCompaction({
|
|
55
|
+
messageCount: ctx.params.session.messages?.length ?? 0,
|
|
56
|
+
compactedCount: ctx.getCompactionCount(),
|
|
57
|
+
}, {})
|
|
58
|
+
.catch((err) => {
|
|
59
|
+
ctx.log.warn(`after_compaction hook failed: ${String(err)}`);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { handleToolExecutionEnd, handleToolExecutionStart, } from "./pi-embedded-subscribe.handlers.tools.js";
|
|
2
|
+
/** Type-safe bridge: narrows parameter type so callers avoid assertions. */
|
|
3
|
+
function asFullContext(ctx) {
|
|
4
|
+
return ctx;
|
|
5
|
+
}
|
|
6
|
+
/** Typed wrapper around {@link handleToolExecutionStart}. */
|
|
7
|
+
export function callToolExecutionStart(ctx, evt) {
|
|
8
|
+
return handleToolExecutionStart(asFullContext(ctx), evt);
|
|
9
|
+
}
|
|
10
|
+
/** Typed wrapper around {@link handleToolExecutionEnd}. */
|
|
11
|
+
export async function callToolExecutionEnd(ctx, evt) {
|
|
12
|
+
return handleToolExecutionEnd(asFullContext(ctx), evt);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Check whether a mock-call argument is an object containing `mediaUrls`
|
|
16
|
+
* but NOT `text` (i.e. a "direct media" emission).
|
|
17
|
+
*/
|
|
18
|
+
export function isDirectMediaCall(call) {
|
|
19
|
+
const arg = call[0];
|
|
20
|
+
if (!arg || typeof arg !== "object") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return "mediaUrls" in arg && !("text" in arg);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Filter a vi.fn() mock's call log to only direct-media emissions.
|
|
27
|
+
*/
|
|
28
|
+
export function filterDirectMediaCalls(mock) {
|
|
29
|
+
return mock.mock.calls.filter(isDirectMediaCall);
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function createSessionManagerRuntimeRegistry() {
|
|
2
|
+
// Session-scoped runtime registry keyed by object identity.
|
|
3
|
+
// The SessionManager instance must stay stable across set/get calls.
|
|
4
|
+
const registry = new WeakMap();
|
|
5
|
+
const set = (sessionManager, value) => {
|
|
6
|
+
if (!sessionManager || typeof sessionManager !== "object") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const key = sessionManager;
|
|
10
|
+
if (value === null) {
|
|
11
|
+
registry.delete(key);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
registry.set(key, value);
|
|
15
|
+
};
|
|
16
|
+
const get = (sessionManager) => {
|
|
17
|
+
if (!sessionManager || typeof sessionManager !== "object") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return registry.get(sessionManager) ?? null;
|
|
21
|
+
};
|
|
22
|
+
return { set, get };
|
|
23
|
+
}
|
package/dist/agents/pi-tools.js
CHANGED
|
@@ -81,6 +81,8 @@ export const __testing = {
|
|
|
81
81
|
wrapToolParamNormalization,
|
|
82
82
|
assertRequiredParams,
|
|
83
83
|
};
|
|
84
|
+
/** Alias for upstream compatibility — callers importing the upstream name get the pool-bot version. */
|
|
85
|
+
export const createOpenClawCodingTools = createPoolbotCodingTools;
|
|
84
86
|
export function createPoolbotCodingTools(options) {
|
|
85
87
|
const execToolName = "exec";
|
|
86
88
|
const sandbox = options?.sandbox?.enabled ? options.sandbox : undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function getQueuedFileWriter(writers, filePath) {
|
|
4
|
+
const existing = writers.get(filePath);
|
|
5
|
+
if (existing) {
|
|
6
|
+
return existing;
|
|
7
|
+
}
|
|
8
|
+
const dir = path.dirname(filePath);
|
|
9
|
+
const ready = fs.mkdir(dir, { recursive: true }).catch(() => undefined);
|
|
10
|
+
let queue = Promise.resolve();
|
|
11
|
+
const writer = {
|
|
12
|
+
filePath,
|
|
13
|
+
write: (line) => {
|
|
14
|
+
queue = queue
|
|
15
|
+
.then(() => ready)
|
|
16
|
+
.then(() => fs.appendFile(filePath, line, "utf8"))
|
|
17
|
+
.catch(() => undefined);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
writers.set(filePath, writer);
|
|
21
|
+
return writer;
|
|
22
|
+
}
|
|
@@ -1,44 +1,118 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const HOT_CONTAINER_WINDOW_MS = 5 * 60 * 1000;
|
|
9
|
-
export function execDocker(args, opts) {
|
|
2
|
+
function createAbortError() {
|
|
3
|
+
const err = new Error("Aborted");
|
|
4
|
+
err.name = "AbortError";
|
|
5
|
+
return err;
|
|
6
|
+
}
|
|
7
|
+
export function execDockerRaw(args, opts) {
|
|
10
8
|
return new Promise((resolve, reject) => {
|
|
11
9
|
const child = spawn("docker", args, {
|
|
12
|
-
stdio: ["
|
|
10
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13
11
|
});
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const stdoutChunks = [];
|
|
13
|
+
const stderrChunks = [];
|
|
14
|
+
let aborted = false;
|
|
15
|
+
const signal = opts?.signal;
|
|
16
|
+
const handleAbort = () => {
|
|
17
|
+
if (aborted) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
aborted = true;
|
|
21
|
+
child.kill("SIGTERM");
|
|
22
|
+
};
|
|
23
|
+
if (signal) {
|
|
24
|
+
if (signal.aborted) {
|
|
25
|
+
handleAbort();
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
signal.addEventListener("abort", handleAbort);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
16
31
|
child.stdout?.on("data", (chunk) => {
|
|
17
|
-
|
|
32
|
+
stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
18
33
|
});
|
|
19
34
|
child.stderr?.on("data", (chunk) => {
|
|
20
|
-
|
|
35
|
+
stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
36
|
+
});
|
|
37
|
+
child.on("error", (error) => {
|
|
38
|
+
if (signal) {
|
|
39
|
+
signal.removeEventListener("abort", handleAbort);
|
|
40
|
+
}
|
|
41
|
+
reject(error);
|
|
21
42
|
});
|
|
22
43
|
child.on("close", (code) => {
|
|
44
|
+
if (signal) {
|
|
45
|
+
signal.removeEventListener("abort", handleAbort);
|
|
46
|
+
}
|
|
47
|
+
const stdout = Buffer.concat(stdoutChunks);
|
|
48
|
+
const stderr = Buffer.concat(stderrChunks);
|
|
49
|
+
if (aborted || signal?.aborted) {
|
|
50
|
+
reject(createAbortError());
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
23
53
|
const exitCode = code ?? 0;
|
|
24
54
|
if (exitCode !== 0 && !opts?.allowFailure) {
|
|
25
|
-
|
|
55
|
+
const message = stderr.length > 0 ? stderr.toString("utf8").trim() : "";
|
|
56
|
+
const error = Object.assign(new Error(message || `docker ${args.join(" ")} failed`), {
|
|
57
|
+
code: exitCode,
|
|
58
|
+
stdout,
|
|
59
|
+
stderr,
|
|
60
|
+
});
|
|
61
|
+
reject(error);
|
|
26
62
|
return;
|
|
27
63
|
}
|
|
28
64
|
resolve({ stdout, stderr, code: exitCode });
|
|
29
65
|
});
|
|
66
|
+
const stdin = child.stdin;
|
|
67
|
+
if (stdin) {
|
|
68
|
+
if (opts?.input !== undefined) {
|
|
69
|
+
stdin.end(opts.input);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
stdin.end();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
30
75
|
});
|
|
31
76
|
}
|
|
77
|
+
import { formatCliCommand } from "../../cli/command-format.js";
|
|
78
|
+
import { defaultRuntime } from "../../runtime.js";
|
|
79
|
+
import { computeSandboxConfigHash } from "./config-hash.js";
|
|
80
|
+
import { DEFAULT_SANDBOX_IMAGE, SANDBOX_AGENT_WORKSPACE_MOUNT } from "./constants.js";
|
|
81
|
+
import { readRegistry, updateRegistry } from "./registry.js";
|
|
82
|
+
import { resolveSandboxAgentId, resolveSandboxScopeKey, slugifySessionKey } from "./shared.js";
|
|
83
|
+
import { validateSandboxSecurity } from "./validate-sandbox-security.js";
|
|
84
|
+
const HOT_CONTAINER_WINDOW_MS = 5 * 60 * 1000;
|
|
85
|
+
export async function execDocker(args, opts) {
|
|
86
|
+
const result = await execDockerRaw(args, opts);
|
|
87
|
+
return {
|
|
88
|
+
stdout: result.stdout.toString("utf8"),
|
|
89
|
+
stderr: result.stderr.toString("utf8"),
|
|
90
|
+
code: result.code,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function readDockerContainerLabel(containerName, label) {
|
|
94
|
+
const result = await execDocker(["inspect", "-f", `{{ index .Config.Labels "${label}" }}`, containerName], { allowFailure: true });
|
|
95
|
+
if (result.code !== 0) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const raw = result.stdout.trim();
|
|
99
|
+
if (!raw || raw === "<no value>") {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return raw;
|
|
103
|
+
}
|
|
32
104
|
export async function readDockerPort(containerName, port) {
|
|
33
105
|
const result = await execDocker(["port", containerName, `${port}/tcp`], {
|
|
34
106
|
allowFailure: true,
|
|
35
107
|
});
|
|
36
|
-
if (result.code !== 0)
|
|
108
|
+
if (result.code !== 0) {
|
|
37
109
|
return null;
|
|
110
|
+
}
|
|
38
111
|
const line = result.stdout.trim().split(/\r?\n/)[0] ?? "";
|
|
39
112
|
const match = line.match(/:(\d+)\s*$/);
|
|
40
|
-
if (!match)
|
|
113
|
+
if (!match) {
|
|
41
114
|
return null;
|
|
115
|
+
}
|
|
42
116
|
const mapped = Number.parseInt(match[1] ?? "", 10);
|
|
43
117
|
return Number.isFinite(mapped) ? mapped : null;
|
|
44
118
|
}
|
|
@@ -46,8 +120,9 @@ async function dockerImageExists(image) {
|
|
|
46
120
|
const result = await execDocker(["image", "inspect", image], {
|
|
47
121
|
allowFailure: true,
|
|
48
122
|
});
|
|
49
|
-
if (result.code === 0)
|
|
123
|
+
if (result.code === 0) {
|
|
50
124
|
return true;
|
|
125
|
+
}
|
|
51
126
|
const stderr = result.stderr.trim();
|
|
52
127
|
if (stderr.includes("No such image")) {
|
|
53
128
|
return false;
|
|
@@ -56,8 +131,9 @@ async function dockerImageExists(image) {
|
|
|
56
131
|
}
|
|
57
132
|
export async function ensureDockerImage(image) {
|
|
58
133
|
const exists = await dockerImageExists(image);
|
|
59
|
-
if (exists)
|
|
134
|
+
if (exists) {
|
|
60
135
|
return;
|
|
136
|
+
}
|
|
61
137
|
if (image === DEFAULT_SANDBOX_IMAGE) {
|
|
62
138
|
await execDocker(["pull", "debian:bookworm-slim"]);
|
|
63
139
|
await execDocker(["tag", "debian:bookworm-slim", DEFAULT_SANDBOX_IMAGE]);
|
|
@@ -69,13 +145,15 @@ export async function dockerContainerState(name) {
|
|
|
69
145
|
const result = await execDocker(["inspect", "-f", "{{.State.Running}}", name], {
|
|
70
146
|
allowFailure: true,
|
|
71
147
|
});
|
|
72
|
-
if (result.code !== 0)
|
|
148
|
+
if (result.code !== 0) {
|
|
73
149
|
return { exists: false, running: false };
|
|
150
|
+
}
|
|
74
151
|
return { exists: true, running: result.stdout.trim() === "true" };
|
|
75
152
|
}
|
|
76
153
|
function normalizeDockerLimit(value) {
|
|
77
|
-
if (value === undefined || value === null)
|
|
154
|
+
if (value === undefined || value === null) {
|
|
78
155
|
return undefined;
|
|
156
|
+
}
|
|
79
157
|
if (typeof value === "number") {
|
|
80
158
|
return Number.isFinite(value) ? String(value) : undefined;
|
|
81
159
|
}
|
|
@@ -83,23 +161,29 @@ function normalizeDockerLimit(value) {
|
|
|
83
161
|
return trimmed ? trimmed : undefined;
|
|
84
162
|
}
|
|
85
163
|
function formatUlimitValue(name, value) {
|
|
86
|
-
if (!name.trim())
|
|
164
|
+
if (!name.trim()) {
|
|
87
165
|
return null;
|
|
166
|
+
}
|
|
88
167
|
if (typeof value === "number" || typeof value === "string") {
|
|
89
168
|
const raw = String(value).trim();
|
|
90
169
|
return raw ? `${name}=${raw}` : null;
|
|
91
170
|
}
|
|
92
171
|
const soft = typeof value.soft === "number" ? Math.max(0, value.soft) : undefined;
|
|
93
172
|
const hard = typeof value.hard === "number" ? Math.max(0, value.hard) : undefined;
|
|
94
|
-
if (soft === undefined && hard === undefined)
|
|
173
|
+
if (soft === undefined && hard === undefined) {
|
|
95
174
|
return null;
|
|
96
|
-
|
|
175
|
+
}
|
|
176
|
+
if (soft === undefined) {
|
|
97
177
|
return `${name}=${hard}`;
|
|
98
|
-
|
|
178
|
+
}
|
|
179
|
+
if (hard === undefined) {
|
|
99
180
|
return `${name}=${soft}`;
|
|
181
|
+
}
|
|
100
182
|
return `${name}=${soft}:${hard}`;
|
|
101
183
|
}
|
|
102
184
|
export function buildSandboxCreateArgs(params) {
|
|
185
|
+
// Runtime security validation: blocks dangerous bind mounts, network modes, and profiles.
|
|
186
|
+
validateSandboxSecurity(params.cfg);
|
|
103
187
|
const createdAtMs = params.createdAtMs ?? Date.now();
|
|
104
188
|
const args = ["create", "--name", params.name];
|
|
105
189
|
args.push("--label", "poolbot.sandbox=1");
|
|
@@ -109,18 +193,28 @@ export function buildSandboxCreateArgs(params) {
|
|
|
109
193
|
args.push("--label", `poolbot.configHash=${params.configHash}`);
|
|
110
194
|
}
|
|
111
195
|
for (const [key, value] of Object.entries(params.labels ?? {})) {
|
|
112
|
-
if (key && value)
|
|
196
|
+
if (key && value) {
|
|
113
197
|
args.push("--label", `${key}=${value}`);
|
|
198
|
+
}
|
|
114
199
|
}
|
|
115
|
-
if (params.cfg.readOnlyRoot)
|
|
200
|
+
if (params.cfg.readOnlyRoot) {
|
|
116
201
|
args.push("--read-only");
|
|
202
|
+
}
|
|
117
203
|
for (const entry of params.cfg.tmpfs) {
|
|
118
204
|
args.push("--tmpfs", entry);
|
|
119
205
|
}
|
|
120
|
-
if (params.cfg.network)
|
|
206
|
+
if (params.cfg.network) {
|
|
121
207
|
args.push("--network", params.cfg.network);
|
|
122
|
-
|
|
208
|
+
}
|
|
209
|
+
if (params.cfg.user) {
|
|
123
210
|
args.push("--user", params.cfg.user);
|
|
211
|
+
}
|
|
212
|
+
for (const [key, value] of Object.entries(params.cfg.env ?? {})) {
|
|
213
|
+
if (!key.trim()) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
args.push("--env", key + "=" + value);
|
|
217
|
+
}
|
|
124
218
|
for (const cap of params.cfg.capDrop) {
|
|
125
219
|
args.push("--cap-drop", cap);
|
|
126
220
|
}
|
|
@@ -132,29 +226,34 @@ export function buildSandboxCreateArgs(params) {
|
|
|
132
226
|
args.push("--security-opt", `apparmor=${params.cfg.apparmorProfile}`);
|
|
133
227
|
}
|
|
134
228
|
for (const entry of params.cfg.dns ?? []) {
|
|
135
|
-
if (entry.trim())
|
|
229
|
+
if (entry.trim()) {
|
|
136
230
|
args.push("--dns", entry);
|
|
231
|
+
}
|
|
137
232
|
}
|
|
138
233
|
for (const entry of params.cfg.extraHosts ?? []) {
|
|
139
|
-
if (entry.trim())
|
|
234
|
+
if (entry.trim()) {
|
|
140
235
|
args.push("--add-host", entry);
|
|
236
|
+
}
|
|
141
237
|
}
|
|
142
238
|
if (typeof params.cfg.pidsLimit === "number" && params.cfg.pidsLimit > 0) {
|
|
143
239
|
args.push("--pids-limit", String(params.cfg.pidsLimit));
|
|
144
240
|
}
|
|
145
241
|
const memory = normalizeDockerLimit(params.cfg.memory);
|
|
146
|
-
if (memory)
|
|
242
|
+
if (memory) {
|
|
147
243
|
args.push("--memory", memory);
|
|
244
|
+
}
|
|
148
245
|
const memorySwap = normalizeDockerLimit(params.cfg.memorySwap);
|
|
149
|
-
if (memorySwap)
|
|
246
|
+
if (memorySwap) {
|
|
150
247
|
args.push("--memory-swap", memorySwap);
|
|
248
|
+
}
|
|
151
249
|
if (typeof params.cfg.cpus === "number" && params.cfg.cpus > 0) {
|
|
152
250
|
args.push("--cpus", String(params.cfg.cpus));
|
|
153
251
|
}
|
|
154
252
|
for (const [name, value] of Object.entries(params.cfg.ulimits ?? {})) {
|
|
155
253
|
const formatted = formatUlimitValue(name, value);
|
|
156
|
-
if (formatted)
|
|
254
|
+
if (formatted) {
|
|
157
255
|
args.push("--ulimit", formatted);
|
|
256
|
+
}
|
|
158
257
|
}
|
|
159
258
|
if (params.cfg.binds?.length) {
|
|
160
259
|
for (const bind of params.cfg.binds) {
|
|
@@ -187,13 +286,7 @@ async function createSandboxContainer(params) {
|
|
|
187
286
|
}
|
|
188
287
|
}
|
|
189
288
|
async function readContainerConfigHash(containerName) {
|
|
190
|
-
|
|
191
|
-
if (result.code !== 0)
|
|
192
|
-
return null;
|
|
193
|
-
const raw = result.stdout.trim();
|
|
194
|
-
if (!raw || raw === "<no value>")
|
|
195
|
-
return null;
|
|
196
|
-
return raw;
|
|
289
|
+
return await readDockerContainerLabel(containerName, "poolbot.configHash");
|
|
197
290
|
}
|
|
198
291
|
function formatSandboxRecreateHint(params) {
|
|
199
292
|
if (params.scope === "session") {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { execDockerRaw } from "./docker.js";
|
|
2
|
+
import { buildSandboxFsMounts, resolveSandboxFsPathWithMounts, } from "./fs-paths.js";
|
|
3
|
+
export function createSandboxFsBridge(params) {
|
|
4
|
+
return new SandboxFsBridgeImpl(params.sandbox);
|
|
5
|
+
}
|
|
6
|
+
class SandboxFsBridgeImpl {
|
|
7
|
+
sandbox;
|
|
8
|
+
mounts;
|
|
9
|
+
constructor(sandbox) {
|
|
10
|
+
this.sandbox = sandbox;
|
|
11
|
+
this.mounts = buildSandboxFsMounts(sandbox);
|
|
12
|
+
}
|
|
13
|
+
resolvePath(params) {
|
|
14
|
+
const target = this.resolveResolvedPath(params);
|
|
15
|
+
return {
|
|
16
|
+
hostPath: target.hostPath,
|
|
17
|
+
relativePath: target.relativePath,
|
|
18
|
+
containerPath: target.containerPath,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async readFile(params) {
|
|
22
|
+
const target = this.resolveResolvedPath(params);
|
|
23
|
+
const result = await this.runCommand('set -eu; cat -- "$1"', {
|
|
24
|
+
args: [target.containerPath],
|
|
25
|
+
signal: params.signal,
|
|
26
|
+
});
|
|
27
|
+
return result.stdout;
|
|
28
|
+
}
|
|
29
|
+
async writeFile(params) {
|
|
30
|
+
const target = this.resolveResolvedPath(params);
|
|
31
|
+
this.ensureWriteAccess(target, "write files");
|
|
32
|
+
const buffer = Buffer.isBuffer(params.data)
|
|
33
|
+
? params.data
|
|
34
|
+
: Buffer.from(params.data, params.encoding ?? "utf8");
|
|
35
|
+
const script = params.mkdir === false
|
|
36
|
+
? 'set -eu; cat >"$1"'
|
|
37
|
+
: 'set -eu; dir=$(dirname -- "$1"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; cat >"$1"';
|
|
38
|
+
await this.runCommand(script, {
|
|
39
|
+
args: [target.containerPath],
|
|
40
|
+
stdin: buffer,
|
|
41
|
+
signal: params.signal,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async mkdirp(params) {
|
|
45
|
+
const target = this.resolveResolvedPath(params);
|
|
46
|
+
this.ensureWriteAccess(target, "create directories");
|
|
47
|
+
await this.runCommand('set -eu; mkdir -p -- "$1"', {
|
|
48
|
+
args: [target.containerPath],
|
|
49
|
+
signal: params.signal,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async remove(params) {
|
|
53
|
+
const target = this.resolveResolvedPath(params);
|
|
54
|
+
this.ensureWriteAccess(target, "remove files");
|
|
55
|
+
const flags = [params.force === false ? "" : "-f", params.recursive ? "-r" : ""].filter(Boolean);
|
|
56
|
+
const rmCommand = flags.length > 0 ? `rm ${flags.join(" ")}` : "rm";
|
|
57
|
+
await this.runCommand(`set -eu; ${rmCommand} -- "$1"`, {
|
|
58
|
+
args: [target.containerPath],
|
|
59
|
+
signal: params.signal,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async rename(params) {
|
|
63
|
+
const from = this.resolveResolvedPath({ filePath: params.from, cwd: params.cwd });
|
|
64
|
+
const to = this.resolveResolvedPath({ filePath: params.to, cwd: params.cwd });
|
|
65
|
+
this.ensureWriteAccess(from, "rename files");
|
|
66
|
+
this.ensureWriteAccess(to, "rename files");
|
|
67
|
+
await this.runCommand('set -eu; dir=$(dirname -- "$2"); if [ "$dir" != "." ]; then mkdir -p -- "$dir"; fi; mv -- "$1" "$2"', {
|
|
68
|
+
args: [from.containerPath, to.containerPath],
|
|
69
|
+
signal: params.signal,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async stat(params) {
|
|
73
|
+
const target = this.resolveResolvedPath(params);
|
|
74
|
+
const result = await this.runCommand('set -eu; stat -c "%F|%s|%Y" -- "$1"', {
|
|
75
|
+
args: [target.containerPath],
|
|
76
|
+
signal: params.signal,
|
|
77
|
+
allowFailure: true,
|
|
78
|
+
});
|
|
79
|
+
if (result.code !== 0) {
|
|
80
|
+
const stderr = result.stderr.toString("utf8");
|
|
81
|
+
if (stderr.includes("No such file or directory")) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const message = stderr.trim() || `stat failed with code ${result.code}`;
|
|
85
|
+
throw new Error(`stat failed for ${target.containerPath}: ${message}`);
|
|
86
|
+
}
|
|
87
|
+
const text = result.stdout.toString("utf8").trim();
|
|
88
|
+
const [typeRaw, sizeRaw, mtimeRaw] = text.split("|");
|
|
89
|
+
const size = Number.parseInt(sizeRaw ?? "0", 10);
|
|
90
|
+
const mtime = Number.parseInt(mtimeRaw ?? "0", 10) * 1000;
|
|
91
|
+
return {
|
|
92
|
+
type: coerceStatType(typeRaw),
|
|
93
|
+
size: Number.isFinite(size) ? size : 0,
|
|
94
|
+
mtimeMs: Number.isFinite(mtime) ? mtime : 0,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async runCommand(script, options = {}) {
|
|
98
|
+
const dockerArgs = [
|
|
99
|
+
"exec",
|
|
100
|
+
"-i",
|
|
101
|
+
this.sandbox.containerName,
|
|
102
|
+
"sh",
|
|
103
|
+
"-c",
|
|
104
|
+
script,
|
|
105
|
+
"moltbot-sandbox-fs",
|
|
106
|
+
];
|
|
107
|
+
if (options.args?.length) {
|
|
108
|
+
dockerArgs.push(...options.args);
|
|
109
|
+
}
|
|
110
|
+
return execDockerRaw(dockerArgs, {
|
|
111
|
+
input: options.stdin,
|
|
112
|
+
allowFailure: options.allowFailure,
|
|
113
|
+
signal: options.signal,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
ensureWriteAccess(target, action) {
|
|
117
|
+
if (!allowsWrites(this.sandbox.workspaceAccess) || !target.writable) {
|
|
118
|
+
throw new Error(`Sandbox path is read-only; cannot ${action}: ${target.containerPath}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
resolveResolvedPath(params) {
|
|
122
|
+
return resolveSandboxFsPathWithMounts({
|
|
123
|
+
filePath: params.filePath,
|
|
124
|
+
cwd: params.cwd ?? this.sandbox.workspaceDir,
|
|
125
|
+
defaultWorkspaceRoot: this.sandbox.workspaceDir,
|
|
126
|
+
defaultContainerRoot: this.sandbox.containerWorkdir,
|
|
127
|
+
mounts: this.mounts,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function allowsWrites(access) {
|
|
132
|
+
return access === "rw";
|
|
133
|
+
}
|
|
134
|
+
function coerceStatType(typeRaw) {
|
|
135
|
+
if (!typeRaw) {
|
|
136
|
+
return "other";
|
|
137
|
+
}
|
|
138
|
+
const normalized = typeRaw.trim().toLowerCase();
|
|
139
|
+
if (normalized.includes("directory")) {
|
|
140
|
+
return "directory";
|
|
141
|
+
}
|
|
142
|
+
if (normalized.includes("file")) {
|
|
143
|
+
return "file";
|
|
144
|
+
}
|
|
145
|
+
return "other";
|
|
146
|
+
}
|