@elizaos/agent 0.25.8 → 2.0.0-alpha.83
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/LICENSE +1 -1
- package/package.json +994 -34
- package/packages/agent/src/actions/emote.d.ts +14 -0
- package/packages/agent/src/actions/emote.d.ts.map +1 -0
- package/packages/agent/src/actions/emote.js +91 -0
- package/packages/agent/src/actions/restart.d.ts +19 -0
- package/packages/agent/src/actions/restart.d.ts.map +1 -0
- package/packages/agent/src/actions/restart.js +86 -0
- package/packages/agent/src/actions/send-message.d.ts +3 -0
- package/packages/agent/src/actions/send-message.d.ts.map +1 -0
- package/packages/agent/src/actions/send-message.js +144 -0
- package/packages/agent/src/actions/stream-control.d.ts +15 -0
- package/packages/agent/src/actions/stream-control.d.ts.map +1 -0
- package/packages/agent/src/actions/stream-control.js +357 -0
- package/packages/agent/src/actions/switch-stream-source.d.ts +16 -0
- package/packages/agent/src/actions/switch-stream-source.d.ts.map +1 -0
- package/packages/agent/src/actions/switch-stream-source.js +94 -0
- package/packages/agent/src/actions/terminal.d.ts +14 -0
- package/packages/agent/src/actions/terminal.d.ts.map +1 -0
- package/packages/agent/src/actions/terminal.js +154 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts +38 -0
- package/packages/agent/src/api/agent-admin-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-admin-routes.js +93 -0
- package/packages/agent/src/api/agent-lifecycle-routes.d.ts +16 -0
- package/packages/agent/src/api/agent-lifecycle-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-lifecycle-routes.js +80 -0
- package/packages/agent/src/api/agent-model.d.ts +12 -0
- package/packages/agent/src/api/agent-model.d.ts.map +1 -0
- package/packages/agent/src/api/agent-model.js +123 -0
- package/packages/agent/src/api/agent-transfer-routes.d.ts +16 -0
- package/packages/agent/src/api/agent-transfer-routes.d.ts.map +1 -0
- package/packages/agent/src/api/agent-transfer-routes.js +124 -0
- package/packages/agent/src/api/apps-routes.d.ts +19 -0
- package/packages/agent/src/api/apps-routes.d.ts.map +1 -0
- package/packages/agent/src/api/apps-routes.js +128 -0
- package/packages/agent/src/api/auth-routes.d.ts +11 -0
- package/packages/agent/src/api/auth-routes.d.ts.map +1 -0
- package/packages/agent/src/api/auth-routes.js +54 -0
- package/packages/agent/src/api/bsc-trade.d.ts +34 -0
- package/packages/agent/src/api/bsc-trade.d.ts.map +1 -0
- package/packages/agent/src/api/bsc-trade.js +567 -0
- package/packages/agent/src/api/bug-report-routes.d.ts +7 -0
- package/packages/agent/src/api/bug-report-routes.d.ts.map +1 -0
- package/packages/agent/src/api/bug-report-routes.js +124 -0
- package/packages/agent/src/api/character-routes.d.ts +50 -0
- package/packages/agent/src/api/character-routes.d.ts.map +1 -0
- package/packages/agent/src/api/character-routes.js +302 -0
- package/packages/agent/src/api/cloud-billing-routes.d.ts +14 -0
- package/packages/agent/src/api/cloud-billing-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-billing-routes.js +400 -0
- package/packages/agent/src/api/cloud-compat-routes.d.ts +15 -0
- package/packages/agent/src/api/cloud-compat-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-compat-routes.js +131 -0
- package/packages/agent/src/api/cloud-routes.d.ts +62 -0
- package/packages/agent/src/api/cloud-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-routes.js +339 -0
- package/packages/agent/src/api/cloud-status-routes.d.ts +15 -0
- package/packages/agent/src/api/cloud-status-routes.d.ts.map +1 -0
- package/packages/agent/src/api/cloud-status-routes.js +165 -0
- package/packages/agent/src/api/compat-utils.d.ts +49 -0
- package/packages/agent/src/api/compat-utils.d.ts.map +1 -0
- package/packages/agent/src/api/compat-utils.js +126 -0
- package/packages/agent/src/api/connector-health.d.ts +34 -0
- package/packages/agent/src/api/connector-health.d.ts.map +1 -0
- package/packages/agent/src/api/connector-health.js +109 -0
- package/packages/agent/src/api/coordinator-wiring.d.ts +46 -0
- package/packages/agent/src/api/coordinator-wiring.d.ts.map +1 -0
- package/packages/agent/src/api/coordinator-wiring.js +101 -0
- package/packages/agent/src/api/credit-detection.d.ts +9 -0
- package/packages/agent/src/api/credit-detection.d.ts.map +1 -0
- package/packages/agent/src/api/credit-detection.js +41 -0
- package/packages/agent/src/api/database.d.ts +33 -0
- package/packages/agent/src/api/database.d.ts.map +1 -0
- package/packages/agent/src/api/database.js +1019 -0
- package/packages/agent/src/api/diagnostics-routes.d.ts +46 -0
- package/packages/agent/src/api/diagnostics-routes.d.ts.map +1 -0
- package/packages/agent/src/api/diagnostics-routes.js +241 -0
- package/packages/agent/src/api/drop-service.d.ts +26 -0
- package/packages/agent/src/api/drop-service.d.ts.map +1 -0
- package/packages/agent/src/api/drop-service.js +134 -0
- package/packages/agent/src/api/early-logs.d.ts +29 -0
- package/packages/agent/src/api/early-logs.d.ts.map +1 -0
- package/packages/agent/src/api/early-logs.js +96 -0
- package/packages/agent/src/api/http-helpers.d.ts +50 -0
- package/packages/agent/src/api/http-helpers.d.ts.map +1 -0
- package/packages/agent/src/api/http-helpers.js +145 -0
- package/packages/agent/src/api/index.d.ts +61 -0
- package/packages/agent/src/api/index.d.ts.map +1 -0
- package/packages/agent/src/api/index.js +59 -0
- package/packages/agent/src/api/knowledge-routes.d.ts +23 -0
- package/packages/agent/src/api/knowledge-routes.d.ts.map +1 -0
- package/packages/agent/src/api/knowledge-routes.js +931 -0
- package/packages/agent/src/api/knowledge-service-loader.d.ts +51 -0
- package/packages/agent/src/api/knowledge-service-loader.d.ts.map +1 -0
- package/packages/agent/src/api/knowledge-service-loader.js +34 -0
- package/packages/agent/src/api/memory-bounds.d.ts +51 -0
- package/packages/agent/src/api/memory-bounds.d.ts.map +1 -0
- package/packages/agent/src/api/memory-bounds.js +81 -0
- package/packages/agent/src/api/memory-routes.d.ts +9 -0
- package/packages/agent/src/api/memory-routes.d.ts.map +1 -0
- package/packages/agent/src/api/memory-routes.js +241 -0
- package/packages/agent/src/api/merkle-tree.d.ts +90 -0
- package/packages/agent/src/api/merkle-tree.d.ts.map +1 -0
- package/packages/agent/src/api/merkle-tree.js +174 -0
- package/packages/agent/src/api/models-routes.d.ts +14 -0
- package/packages/agent/src/api/models-routes.d.ts.map +1 -0
- package/packages/agent/src/api/models-routes.js +37 -0
- package/packages/agent/src/api/nfa-routes.d.ts +5 -0
- package/packages/agent/src/api/nfa-routes.d.ts.map +1 -0
- package/packages/agent/src/api/nfa-routes.js +125 -0
- package/packages/agent/src/api/og-tracker.d.ts +28 -0
- package/packages/agent/src/api/og-tracker.d.ts.map +1 -0
- package/packages/agent/src/api/og-tracker.js +60 -0
- package/packages/agent/src/api/parse-action-block.d.ts +36 -0
- package/packages/agent/src/api/parse-action-block.d.ts.map +1 -0
- package/packages/agent/src/api/parse-action-block.js +110 -0
- package/packages/agent/src/api/permissions-routes.d.ts +32 -0
- package/packages/agent/src/api/permissions-routes.d.ts.map +1 -0
- package/packages/agent/src/api/permissions-routes.js +149 -0
- package/packages/agent/src/api/plugin-validation.d.ts +86 -0
- package/packages/agent/src/api/plugin-validation.d.ts.map +1 -0
- package/packages/agent/src/api/plugin-validation.js +259 -0
- package/packages/agent/src/api/provider-switch-config.d.ts +37 -0
- package/packages/agent/src/api/provider-switch-config.d.ts.map +1 -0
- package/packages/agent/src/api/provider-switch-config.js +317 -0
- package/packages/agent/src/api/registry-routes.d.ts +26 -0
- package/packages/agent/src/api/registry-routes.d.ts.map +1 -0
- package/packages/agent/src/api/registry-routes.js +90 -0
- package/packages/agent/src/api/registry-service.d.ts +77 -0
- package/packages/agent/src/api/registry-service.d.ts.map +1 -0
- package/packages/agent/src/api/registry-service.js +190 -0
- package/packages/agent/src/api/route-helpers.d.ts +16 -0
- package/packages/agent/src/api/route-helpers.d.ts.map +1 -0
- package/packages/agent/src/api/route-helpers.js +1 -0
- package/packages/agent/src/api/sandbox-routes.d.ts +12 -0
- package/packages/agent/src/api/sandbox-routes.d.ts.map +1 -0
- package/packages/agent/src/api/sandbox-routes.js +1334 -0
- package/packages/agent/src/api/server.d.ts +418 -0
- package/packages/agent/src/api/server.d.ts.map +1 -0
- package/packages/agent/src/api/server.js +13614 -0
- package/packages/agent/src/api/signal-routes.d.ts +39 -0
- package/packages/agent/src/api/signal-routes.d.ts.map +1 -0
- package/packages/agent/src/api/signal-routes.js +168 -0
- package/packages/agent/src/api/stream-persistence.d.ts +64 -0
- package/packages/agent/src/api/stream-persistence.d.ts.map +1 -0
- package/packages/agent/src/api/stream-persistence.js +231 -0
- package/packages/agent/src/api/stream-route-state.d.ts +50 -0
- package/packages/agent/src/api/stream-route-state.d.ts.map +1 -0
- package/packages/agent/src/api/stream-route-state.js +1 -0
- package/packages/agent/src/api/stream-routes.d.ts +45 -0
- package/packages/agent/src/api/stream-routes.d.ts.map +1 -0
- package/packages/agent/src/api/stream-routes.js +809 -0
- package/packages/agent/src/api/stream-voice-routes.d.ts +36 -0
- package/packages/agent/src/api/stream-voice-routes.d.ts.map +1 -0
- package/packages/agent/src/api/stream-voice-routes.js +133 -0
- package/packages/agent/src/api/streaming-text.d.ts +9 -0
- package/packages/agent/src/api/streaming-text.d.ts.map +1 -0
- package/packages/agent/src/api/streaming-text.js +85 -0
- package/packages/agent/src/api/streaming-types.d.ts +30 -0
- package/packages/agent/src/api/streaming-types.d.ts.map +1 -0
- package/packages/agent/src/api/streaming-types.js +1 -0
- package/packages/agent/src/api/subscription-routes.d.ts +20 -0
- package/packages/agent/src/api/subscription-routes.d.ts.map +1 -0
- package/packages/agent/src/api/subscription-routes.js +191 -0
- package/packages/agent/src/api/terminal-run-limits.d.ts +5 -0
- package/packages/agent/src/api/terminal-run-limits.d.ts.map +1 -0
- package/packages/agent/src/api/terminal-run-limits.js +22 -0
- package/packages/agent/src/api/training-backend-check.d.ts +8 -0
- package/packages/agent/src/api/training-backend-check.d.ts.map +1 -0
- package/packages/agent/src/api/training-backend-check.js +28 -0
- package/packages/agent/src/api/training-routes.d.ts +44 -0
- package/packages/agent/src/api/training-routes.d.ts.map +1 -0
- package/packages/agent/src/api/training-routes.js +195 -0
- package/packages/agent/src/api/training-service-like.d.ts +38 -0
- package/packages/agent/src/api/training-service-like.d.ts.map +1 -0
- package/packages/agent/src/api/training-service-like.js +1 -0
- package/packages/agent/src/api/trajectory-routes.d.ts +17 -0
- package/packages/agent/src/api/trajectory-routes.d.ts.map +1 -0
- package/packages/agent/src/api/trajectory-routes.js +405 -0
- package/packages/agent/src/api/trigger-routes.d.ts +72 -0
- package/packages/agent/src/api/trigger-routes.d.ts.map +1 -0
- package/packages/agent/src/api/trigger-routes.js +268 -0
- package/packages/agent/src/api/twitter-verify.d.ts +25 -0
- package/packages/agent/src/api/twitter-verify.d.ts.map +1 -0
- package/packages/agent/src/api/twitter-verify.js +168 -0
- package/packages/agent/src/api/tx-service.d.ts +47 -0
- package/packages/agent/src/api/tx-service.d.ts.map +1 -0
- package/packages/agent/src/api/tx-service.js +156 -0
- package/packages/agent/src/api/wallet-dex-prices.d.ts +43 -0
- package/packages/agent/src/api/wallet-dex-prices.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-dex-prices.js +149 -0
- package/packages/agent/src/api/wallet-evm-balance.d.ts +65 -0
- package/packages/agent/src/api/wallet-evm-balance.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-evm-balance.js +663 -0
- package/packages/agent/src/api/wallet-routes.d.ts +33 -0
- package/packages/agent/src/api/wallet-routes.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-routes.js +292 -0
- package/packages/agent/src/api/wallet-rpc.d.ts +61 -0
- package/packages/agent/src/api/wallet-rpc.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-rpc.js +367 -0
- package/packages/agent/src/api/wallet-trading-profile.d.ts +51 -0
- package/packages/agent/src/api/wallet-trading-profile.d.ts.map +1 -0
- package/packages/agent/src/api/wallet-trading-profile.js +547 -0
- package/packages/agent/src/api/wallet.d.ts +31 -0
- package/packages/agent/src/api/wallet.d.ts.map +1 -0
- package/packages/agent/src/api/wallet.js +513 -0
- package/packages/agent/src/api/whatsapp-routes.d.ts +39 -0
- package/packages/agent/src/api/whatsapp-routes.d.ts.map +1 -0
- package/packages/agent/src/api/whatsapp-routes.js +182 -0
- package/packages/agent/src/api/zip-utils.d.ts +8 -0
- package/packages/agent/src/api/zip-utils.d.ts.map +1 -0
- package/packages/agent/src/api/zip-utils.js +115 -0
- package/packages/agent/src/auth/anthropic.d.ts +25 -0
- package/packages/agent/src/auth/anthropic.d.ts.map +1 -0
- package/packages/agent/src/auth/anthropic.js +40 -0
- package/packages/agent/src/auth/apply-stealth.d.ts +8 -0
- package/packages/agent/src/auth/apply-stealth.d.ts.map +1 -0
- package/packages/agent/src/auth/apply-stealth.js +35 -0
- package/packages/agent/src/auth/claude-code-stealth.d.ts +2 -0
- package/packages/agent/src/auth/claude-code-stealth.d.ts.map +1 -0
- package/packages/agent/src/auth/claude-code-stealth.js +104 -0
- package/packages/agent/src/auth/credentials.d.ts +55 -0
- package/packages/agent/src/auth/credentials.d.ts.map +1 -0
- package/packages/agent/src/auth/credentials.js +182 -0
- package/packages/agent/src/auth/index.d.ts +7 -0
- package/packages/agent/src/auth/index.d.ts.map +1 -0
- package/packages/agent/src/auth/index.js +3 -0
- package/packages/agent/src/auth/openai-codex.d.ts +27 -0
- package/packages/agent/src/auth/openai-codex.d.ts.map +1 -0
- package/packages/agent/src/auth/openai-codex.js +72 -0
- package/packages/agent/src/auth/types.d.ts +18 -0
- package/packages/agent/src/auth/types.d.ts.map +1 -0
- package/packages/agent/src/auth/types.js +8 -0
- package/packages/agent/src/awareness/registry.d.ts +27 -0
- package/packages/agent/src/awareness/registry.d.ts.map +1 -0
- package/packages/agent/src/awareness/registry.js +161 -0
- package/packages/agent/src/benchmark-server.d.ts +2 -0
- package/packages/agent/src/benchmark-server.d.ts.map +1 -0
- package/packages/agent/src/benchmark-server.js +773 -0
- package/packages/agent/src/bin.d.ts +3 -0
- package/packages/agent/src/bin.d.ts.map +1 -0
- package/packages/agent/src/bin.js +6 -0
- package/packages/agent/src/cli/index.d.ts +2 -0
- package/packages/agent/src/cli/index.d.ts.map +1 -0
- package/packages/agent/src/cli/index.js +40 -0
- package/packages/agent/src/cli/parse-duration.d.ts +5 -0
- package/packages/agent/src/cli/parse-duration.d.ts.map +1 -0
- package/packages/agent/src/cli/parse-duration.js +27 -0
- package/packages/agent/src/cloud/auth.d.ts +19 -0
- package/packages/agent/src/cloud/auth.d.ts.map +1 -0
- package/packages/agent/src/cloud/auth.js +107 -0
- package/packages/agent/src/cloud/backup.d.ts +18 -0
- package/packages/agent/src/cloud/backup.d.ts.map +1 -0
- package/packages/agent/src/cloud/backup.js +42 -0
- package/packages/agent/src/cloud/base-url.d.ts +3 -0
- package/packages/agent/src/cloud/base-url.d.ts.map +1 -0
- package/packages/agent/src/cloud/base-url.js +40 -0
- package/packages/agent/src/cloud/bridge-client.d.ts +56 -0
- package/packages/agent/src/cloud/bridge-client.d.ts.map +1 -0
- package/packages/agent/src/cloud/bridge-client.js +190 -0
- package/packages/agent/src/cloud/cloud-manager.d.ts +32 -0
- package/packages/agent/src/cloud/cloud-manager.d.ts.map +1 -0
- package/packages/agent/src/cloud/cloud-manager.js +119 -0
- package/packages/agent/src/cloud/cloud-proxy.d.ts +20 -0
- package/packages/agent/src/cloud/cloud-proxy.d.ts.map +1 -0
- package/packages/agent/src/cloud/cloud-proxy.js +34 -0
- package/packages/agent/src/cloud/index.d.ts +7 -0
- package/packages/agent/src/cloud/index.d.ts.map +1 -0
- package/packages/agent/src/cloud/index.js +6 -0
- package/packages/agent/src/cloud/reconnect.d.ts +26 -0
- package/packages/agent/src/cloud/reconnect.d.ts.map +1 -0
- package/packages/agent/src/cloud/reconnect.js +86 -0
- package/packages/agent/src/cloud/validate-url.d.ts +2 -0
- package/packages/agent/src/cloud/validate-url.d.ts.map +1 -0
- package/packages/agent/src/cloud/validate-url.js +162 -0
- package/packages/agent/src/config/character-schema.d.ts +25 -0
- package/packages/agent/src/config/character-schema.d.ts.map +1 -0
- package/packages/agent/src/config/character-schema.js +39 -0
- package/packages/agent/src/config/config.d.ts +6 -0
- package/packages/agent/src/config/config.d.ts.map +1 -0
- package/packages/agent/src/config/config.js +118 -0
- package/packages/agent/src/config/env-vars.d.ts +3 -0
- package/packages/agent/src/config/env-vars.d.ts.map +1 -0
- package/packages/agent/src/config/env-vars.js +76 -0
- package/packages/agent/src/config/includes.d.ts +26 -0
- package/packages/agent/src/config/includes.d.ts.map +1 -0
- package/packages/agent/src/config/includes.js +148 -0
- package/packages/agent/src/config/index.d.ts +16 -0
- package/packages/agent/src/config/index.d.ts.map +1 -0
- package/packages/agent/src/config/index.js +15 -0
- package/packages/agent/src/config/object-utils.d.ts +2 -0
- package/packages/agent/src/config/object-utils.d.ts.map +1 -0
- package/packages/agent/src/config/object-utils.js +6 -0
- package/packages/agent/src/config/paths.d.ts +13 -0
- package/packages/agent/src/config/paths.d.ts.map +1 -0
- package/packages/agent/src/config/paths.js +67 -0
- package/packages/agent/src/config/plugin-auto-enable.d.ts +16 -0
- package/packages/agent/src/config/plugin-auto-enable.d.ts.map +1 -0
- package/packages/agent/src/config/plugin-auto-enable.js +384 -0
- package/packages/agent/src/config/schema.d.ts +87 -0
- package/packages/agent/src/config/schema.d.ts.map +1 -0
- package/packages/agent/src/config/schema.js +928 -0
- package/packages/agent/src/config/telegram-custom-commands.d.ts +25 -0
- package/packages/agent/src/config/telegram-custom-commands.d.ts.map +1 -0
- package/packages/agent/src/config/telegram-custom-commands.js +71 -0
- package/packages/agent/src/config/types.agent-defaults.d.ts +331 -0
- package/packages/agent/src/config/types.agent-defaults.d.ts.map +1 -0
- package/packages/agent/src/config/types.agent-defaults.js +1 -0
- package/packages/agent/src/config/types.agents.d.ts +110 -0
- package/packages/agent/src/config/types.agents.d.ts.map +1 -0
- package/packages/agent/src/config/types.agents.js +1 -0
- package/packages/agent/src/config/types.d.ts +8 -0
- package/packages/agent/src/config/types.d.ts.map +1 -0
- package/packages/agent/src/config/types.eliza.d.ts +636 -0
- package/packages/agent/src/config/types.eliza.d.ts.map +1 -0
- package/packages/agent/src/config/types.eliza.js +1 -0
- package/packages/agent/src/config/types.gateway.d.ts +216 -0
- package/packages/agent/src/config/types.gateway.d.ts.map +1 -0
- package/packages/agent/src/config/types.gateway.js +1 -0
- package/packages/agent/src/config/types.hooks.d.ts +107 -0
- package/packages/agent/src/config/types.hooks.d.ts.map +1 -0
- package/packages/agent/src/config/types.hooks.js +1 -0
- package/packages/agent/src/config/types.js +7 -0
- package/packages/agent/src/config/types.messages.d.ts +176 -0
- package/packages/agent/src/config/types.messages.d.ts.map +1 -0
- package/packages/agent/src/config/types.messages.js +1 -0
- package/packages/agent/src/config/types.tools.d.ts +400 -0
- package/packages/agent/src/config/types.tools.d.ts.map +1 -0
- package/packages/agent/src/config/types.tools.js +1 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.d.ts +1062 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.agent-runtime.js +721 -0
- package/packages/agent/src/config/zod-schema.core.d.ts +1021 -0
- package/packages/agent/src/config/zod-schema.core.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.core.js +694 -0
- package/packages/agent/src/config/zod-schema.d.ts +4817 -0
- package/packages/agent/src/config/zod-schema.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.hooks.d.ts +88 -0
- package/packages/agent/src/config/zod-schema.hooks.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.hooks.js +133 -0
- package/packages/agent/src/config/zod-schema.js +778 -0
- package/packages/agent/src/config/zod-schema.providers-core.d.ts +2976 -0
- package/packages/agent/src/config/zod-schema.providers-core.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.providers-core.js +1006 -0
- package/packages/agent/src/config/zod-schema.session.d.ts +183 -0
- package/packages/agent/src/config/zod-schema.session.d.ts.map +1 -0
- package/packages/agent/src/config/zod-schema.session.js +86 -0
- package/packages/agent/src/contracts/apps.d.ts +42 -0
- package/packages/agent/src/contracts/apps.d.ts.map +1 -0
- package/packages/agent/src/contracts/apps.js +4 -0
- package/packages/agent/src/contracts/awareness.d.ts +38 -0
- package/packages/agent/src/contracts/awareness.d.ts.map +1 -0
- package/packages/agent/src/contracts/awareness.js +7 -0
- package/packages/agent/src/contracts/config.d.ts +146 -0
- package/packages/agent/src/contracts/config.d.ts.map +1 -0
- package/packages/agent/src/contracts/config.js +4 -0
- package/packages/agent/src/contracts/drop.d.ts +20 -0
- package/packages/agent/src/contracts/drop.d.ts.map +1 -0
- package/packages/agent/src/contracts/drop.js +4 -0
- package/packages/agent/src/contracts/index.d.ts +9 -0
- package/packages/agent/src/contracts/index.d.ts.map +1 -0
- package/packages/agent/src/contracts/index.js +8 -0
- package/packages/agent/src/contracts/onboarding.d.ts +379 -0
- package/packages/agent/src/contracts/onboarding.d.ts.map +1 -0
- package/packages/agent/src/contracts/onboarding.js +290 -0
- package/packages/agent/src/contracts/permissions.d.ts +35 -0
- package/packages/agent/src/contracts/permissions.d.ts.map +1 -0
- package/packages/agent/src/contracts/permissions.js +4 -0
- package/packages/agent/src/contracts/verification.d.ts +9 -0
- package/packages/agent/src/contracts/verification.d.ts.map +1 -0
- package/packages/agent/src/contracts/verification.js +4 -0
- package/packages/agent/src/contracts/wallet.d.ts +409 -0
- package/packages/agent/src/contracts/wallet.d.ts.map +1 -0
- package/packages/agent/src/contracts/wallet.js +60 -0
- package/packages/agent/src/diagnostics/integration-observability.d.ts +40 -0
- package/packages/agent/src/diagnostics/integration-observability.d.ts.map +1 -0
- package/packages/agent/src/diagnostics/integration-observability.js +68 -0
- package/packages/agent/src/emotes/catalog.d.ts +31 -0
- package/packages/agent/src/emotes/catalog.d.ts.map +1 -0
- package/packages/agent/src/emotes/catalog.js +618 -0
- package/packages/agent/src/hooks/discovery.d.ts +13 -0
- package/packages/agent/src/hooks/discovery.d.ts.map +1 -0
- package/packages/agent/src/hooks/discovery.js +184 -0
- package/packages/agent/src/hooks/eligibility.d.ts +12 -0
- package/packages/agent/src/hooks/eligibility.d.ts.map +1 -0
- package/packages/agent/src/hooks/eligibility.js +100 -0
- package/packages/agent/src/hooks/index.d.ts +3 -0
- package/packages/agent/src/hooks/index.d.ts.map +1 -0
- package/packages/agent/src/hooks/index.js +2 -0
- package/packages/agent/src/hooks/loader.d.ts +34 -0
- package/packages/agent/src/hooks/loader.d.ts.map +1 -0
- package/packages/agent/src/hooks/loader.js +176 -0
- package/packages/agent/src/hooks/registry.d.ts +11 -0
- package/packages/agent/src/hooks/registry.d.ts.map +1 -0
- package/packages/agent/src/hooks/registry.js +58 -0
- package/packages/agent/src/hooks/types.d.ts +104 -0
- package/packages/agent/src/hooks/types.d.ts.map +1 -0
- package/packages/agent/src/hooks/types.js +8 -0
- package/packages/agent/src/index.d.ts +20 -0
- package/packages/agent/src/index.d.ts.map +1 -0
- package/packages/agent/src/index.js +19 -0
- package/packages/agent/src/onboarding-presets.d.ts +78 -0
- package/packages/agent/src/onboarding-presets.d.ts.map +1 -0
- package/packages/agent/src/onboarding-presets.js +1352 -0
- package/packages/agent/src/plugins/custom-rtmp/index.d.ts +12 -0
- package/packages/agent/src/plugins/custom-rtmp/index.d.ts.map +1 -0
- package/packages/agent/src/plugins/custom-rtmp/index.js +26 -0
- package/packages/agent/src/providers/admin-trust.d.ts +4 -0
- package/packages/agent/src/providers/admin-trust.d.ts.map +1 -0
- package/packages/agent/src/providers/admin-trust.js +53 -0
- package/packages/agent/src/providers/session-bridge.d.ts +24 -0
- package/packages/agent/src/providers/session-bridge.d.ts.map +1 -0
- package/packages/agent/src/providers/session-bridge.js +85 -0
- package/packages/agent/src/providers/session-utils.d.ts +20 -0
- package/packages/agent/src/providers/session-utils.d.ts.map +1 -0
- package/packages/agent/src/providers/session-utils.js +33 -0
- package/packages/agent/src/providers/simple-mode.d.ts +4 -0
- package/packages/agent/src/providers/simple-mode.d.ts.map +1 -0
- package/packages/agent/src/providers/simple-mode.js +85 -0
- package/packages/agent/src/providers/ui-catalog.d.ts +3 -0
- package/packages/agent/src/providers/ui-catalog.d.ts.map +1 -0
- package/packages/agent/src/providers/ui-catalog.js +123 -0
- package/packages/agent/src/providers/workspace-provider.d.ts +22 -0
- package/packages/agent/src/providers/workspace-provider.d.ts.map +1 -0
- package/packages/agent/src/providers/workspace-provider.js +167 -0
- package/packages/agent/src/providers/workspace.d.ts +54 -0
- package/packages/agent/src/providers/workspace.d.ts.map +1 -0
- package/packages/agent/src/providers/workspace.js +405 -0
- package/packages/agent/src/runtime/agent-event-service.d.ts +35 -0
- package/packages/agent/src/runtime/agent-event-service.d.ts.map +1 -0
- package/packages/agent/src/runtime/agent-event-service.js +16 -0
- package/packages/agent/src/runtime/cloud-onboarding.d.ts +55 -0
- package/packages/agent/src/runtime/cloud-onboarding.d.ts.map +1 -0
- package/packages/agent/src/runtime/cloud-onboarding.js +279 -0
- package/packages/agent/src/runtime/core-plugins.d.ts +14 -0
- package/packages/agent/src/runtime/core-plugins.d.ts.map +1 -0
- package/packages/agent/src/runtime/core-plugins.js +51 -0
- package/packages/agent/src/runtime/custom-actions.d.ts +40 -0
- package/packages/agent/src/runtime/custom-actions.d.ts.map +1 -0
- package/packages/agent/src/runtime/custom-actions.js +454 -0
- package/packages/agent/src/runtime/eliza-plugin.d.ts +16 -0
- package/packages/agent/src/runtime/eliza-plugin.d.ts.map +1 -0
- package/packages/agent/src/runtime/eliza-plugin.js +108 -0
- package/packages/agent/src/runtime/eliza.d.ts +205 -0
- package/packages/agent/src/runtime/eliza.d.ts.map +1 -0
- package/packages/agent/src/runtime/eliza.js +3935 -0
- package/packages/agent/src/runtime/embedding-presets.d.ts +19 -0
- package/packages/agent/src/runtime/embedding-presets.d.ts.map +1 -0
- package/packages/agent/src/runtime/embedding-presets.js +53 -0
- package/packages/agent/src/runtime/index.d.ts +9 -0
- package/packages/agent/src/runtime/index.d.ts.map +1 -0
- package/packages/agent/src/runtime/index.js +8 -0
- package/packages/agent/src/runtime/onboarding-names.d.ts +11 -0
- package/packages/agent/src/runtime/onboarding-names.d.ts.map +1 -0
- package/packages/agent/src/runtime/onboarding-names.js +74 -0
- package/packages/agent/src/runtime/release-plugin-policy.d.ts +20 -0
- package/packages/agent/src/runtime/release-plugin-policy.d.ts.map +1 -0
- package/packages/agent/src/runtime/release-plugin-policy.js +87 -0
- package/packages/agent/src/runtime/restart.d.ts +45 -0
- package/packages/agent/src/runtime/restart.d.ts.map +1 -0
- package/packages/agent/src/runtime/restart.js +45 -0
- package/packages/agent/src/runtime/trajectory-persistence.d.ts +214 -0
- package/packages/agent/src/runtime/trajectory-persistence.d.ts.map +1 -0
- package/packages/agent/src/runtime/trajectory-persistence.js +1957 -0
- package/packages/agent/src/runtime/version.d.ts +2 -0
- package/packages/agent/src/runtime/version.d.ts.map +1 -0
- package/packages/agent/src/runtime/version.js +5 -0
- package/packages/agent/src/security/audit-log.d.ts +49 -0
- package/packages/agent/src/security/audit-log.d.ts.map +1 -0
- package/packages/agent/src/security/audit-log.js +161 -0
- package/packages/agent/src/security/network-policy.d.ts +6 -0
- package/packages/agent/src/security/network-policy.d.ts.map +1 -0
- package/packages/agent/src/security/network-policy.js +85 -0
- package/packages/agent/src/server/index.d.ts +3 -0
- package/packages/agent/src/server/index.d.ts.map +1 -0
- package/packages/agent/src/server/index.js +1 -0
- package/packages/agent/src/services/agent-export.d.ts +100 -0
- package/packages/agent/src/services/agent-export.d.ts.map +1 -0
- package/packages/agent/src/services/agent-export.js +729 -0
- package/packages/agent/src/services/app-manager.d.ts +34 -0
- package/packages/agent/src/services/app-manager.d.ts.map +1 -0
- package/packages/agent/src/services/app-manager.js +425 -0
- package/packages/agent/src/services/browser-capture.d.ts +39 -0
- package/packages/agent/src/services/browser-capture.d.ts.map +1 -0
- package/packages/agent/src/services/browser-capture.js +162 -0
- package/packages/agent/src/services/coding-agent-context.d.ts +310 -0
- package/packages/agent/src/services/coding-agent-context.d.ts.map +1 -0
- package/packages/agent/src/services/coding-agent-context.js +281 -0
- package/packages/agent/src/services/fallback-training-service.d.ts +78 -0
- package/packages/agent/src/services/fallback-training-service.d.ts.map +1 -0
- package/packages/agent/src/services/fallback-training-service.js +126 -0
- package/packages/agent/src/services/index.d.ts +18 -0
- package/packages/agent/src/services/index.d.ts.map +1 -0
- package/packages/agent/src/services/index.js +17 -0
- package/packages/agent/src/services/mcp-marketplace.d.ts +89 -0
- package/packages/agent/src/services/mcp-marketplace.d.ts.map +1 -0
- package/packages/agent/src/services/mcp-marketplace.js +200 -0
- package/packages/agent/src/services/plugin-manager-types.d.ts +139 -0
- package/packages/agent/src/services/plugin-manager-types.d.ts.map +1 -0
- package/packages/agent/src/services/plugin-manager-types.js +18 -0
- package/packages/agent/src/services/privy-wallets.d.ts +18 -0
- package/packages/agent/src/services/privy-wallets.d.ts.map +1 -0
- package/packages/agent/src/services/privy-wallets.js +225 -0
- package/packages/agent/src/services/registry-client-app-meta.d.ts +6 -0
- package/packages/agent/src/services/registry-client-app-meta.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-app-meta.js +147 -0
- package/packages/agent/src/services/registry-client-endpoints.d.ts +7 -0
- package/packages/agent/src/services/registry-client-endpoints.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-endpoints.js +183 -0
- package/packages/agent/src/services/registry-client-local.d.ts +4 -0
- package/packages/agent/src/services/registry-client-local.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-local.js +377 -0
- package/packages/agent/src/services/registry-client-network.d.ts +9 -0
- package/packages/agent/src/services/registry-client-network.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-network.js +109 -0
- package/packages/agent/src/services/registry-client-queries.d.ts +15 -0
- package/packages/agent/src/services/registry-client-queries.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-queries.js +150 -0
- package/packages/agent/src/services/registry-client-types.d.ts +115 -0
- package/packages/agent/src/services/registry-client-types.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client-types.js +1 -0
- package/packages/agent/src/services/registry-client.d.ts +39 -0
- package/packages/agent/src/services/registry-client.d.ts.map +1 -0
- package/packages/agent/src/services/registry-client.js +249 -0
- package/packages/agent/src/services/remote-signing-service.d.ts +58 -0
- package/packages/agent/src/services/remote-signing-service.d.ts.map +1 -0
- package/packages/agent/src/services/remote-signing-service.js +185 -0
- package/packages/agent/src/services/sandbox-engine.d.ts +96 -0
- package/packages/agent/src/services/sandbox-engine.d.ts.map +1 -0
- package/packages/agent/src/services/sandbox-engine.js +604 -0
- package/packages/agent/src/services/sandbox-manager.d.ts +104 -0
- package/packages/agent/src/services/sandbox-manager.d.ts.map +1 -0
- package/packages/agent/src/services/sandbox-manager.js +353 -0
- package/packages/agent/src/services/self-updater.d.ts +21 -0
- package/packages/agent/src/services/self-updater.d.ts.map +1 -0
- package/packages/agent/src/services/self-updater.js +162 -0
- package/packages/agent/src/services/signal-pairing.d.ts +37 -0
- package/packages/agent/src/services/signal-pairing.d.ts.map +1 -0
- package/packages/agent/src/services/signal-pairing.js +124 -0
- package/packages/agent/src/services/signing-policy.d.ts +44 -0
- package/packages/agent/src/services/signing-policy.d.ts.map +1 -0
- package/packages/agent/src/services/signing-policy.js +165 -0
- package/packages/agent/src/services/skill-catalog-client.d.ts +47 -0
- package/packages/agent/src/services/skill-catalog-client.d.ts.map +1 -0
- package/packages/agent/src/services/skill-catalog-client.js +130 -0
- package/packages/agent/src/services/skill-marketplace.d.ts +42 -0
- package/packages/agent/src/services/skill-marketplace.d.ts.map +1 -0
- package/packages/agent/src/services/skill-marketplace.js +680 -0
- package/packages/agent/src/services/stream-manager.d.ts +121 -0
- package/packages/agent/src/services/stream-manager.d.ts.map +1 -0
- package/packages/agent/src/services/stream-manager.js +604 -0
- package/packages/agent/src/services/tts-stream-bridge.d.ts +83 -0
- package/packages/agent/src/services/tts-stream-bridge.d.ts.map +1 -0
- package/packages/agent/src/services/tts-stream-bridge.js +349 -0
- package/packages/agent/src/services/update-checker.d.ts +29 -0
- package/packages/agent/src/services/update-checker.d.ts.map +1 -0
- package/packages/agent/src/services/update-checker.js +134 -0
- package/packages/agent/src/services/version-compat.d.ts +99 -0
- package/packages/agent/src/services/version-compat.d.ts.map +1 -0
- package/packages/agent/src/services/version-compat.js +195 -0
- package/packages/agent/src/services/whatsapp-pairing.d.ts +41 -0
- package/packages/agent/src/services/whatsapp-pairing.d.ts.map +1 -0
- package/packages/agent/src/services/whatsapp-pairing.js +209 -0
- package/packages/agent/src/shared/ui-catalog-prompt.d.ts +52 -0
- package/packages/agent/src/shared/ui-catalog-prompt.d.ts.map +1 -0
- package/packages/agent/src/shared/ui-catalog-prompt.js +1028 -0
- package/packages/agent/src/test-support/process-helpers.d.ts +13 -0
- package/packages/agent/src/test-support/process-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/process-helpers.js +23 -0
- package/packages/agent/src/test-support/route-test-helpers.d.ts +37 -0
- package/packages/agent/src/test-support/route-test-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/route-test-helpers.js +54 -0
- package/packages/agent/src/test-support/test-helpers.d.ts +77 -0
- package/packages/agent/src/test-support/test-helpers.d.ts.map +1 -0
- package/packages/agent/src/test-support/test-helpers.js +210 -0
- package/packages/agent/src/testing/index.d.ts +4 -0
- package/packages/agent/src/testing/index.d.ts.map +1 -0
- package/packages/agent/src/testing/index.js +3 -0
- package/packages/agent/src/triggers/action.d.ts +3 -0
- package/packages/agent/src/triggers/action.d.ts.map +1 -0
- package/packages/agent/src/triggers/action.js +267 -0
- package/packages/agent/src/triggers/runtime.d.ts +24 -0
- package/packages/agent/src/triggers/runtime.d.ts.map +1 -0
- package/packages/agent/src/triggers/runtime.js +322 -0
- package/packages/agent/src/triggers/scheduling.d.ts +70 -0
- package/packages/agent/src/triggers/scheduling.d.ts.map +1 -0
- package/packages/agent/src/triggers/scheduling.js +355 -0
- package/packages/agent/src/triggers/types.d.ts +115 -0
- package/packages/agent/src/triggers/types.d.ts.map +1 -0
- package/packages/agent/src/triggers/types.js +1 -0
- package/packages/agent/src/utils/exec-safety.d.ts +2 -0
- package/packages/agent/src/utils/exec-safety.d.ts.map +1 -0
- package/packages/agent/src/utils/exec-safety.js +21 -0
- package/packages/agent/src/utils/number-parsing.d.ts +26 -0
- package/packages/agent/src/utils/number-parsing.d.ts.map +1 -0
- package/packages/agent/src/utils/number-parsing.js +52 -0
- package/packages/agent/src/utils/spoken-text.d.ts +2 -0
- package/packages/agent/src/utils/spoken-text.d.ts.map +1 -0
- package/packages/agent/src/utils/spoken-text.js +56 -0
- package/packages/agent/src/version-resolver.d.ts +3 -0
- package/packages/agent/src/version-resolver.d.ts.map +1 -0
- package/packages/agent/src/version-resolver.js +51 -0
- package/jest.config.js +0 -17
- package/src/__tests__/client-type-identification.test.ts +0 -59
- package/src/defaultCharacter.ts +0 -530
- package/src/index.ts +0 -865
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,1334 @@
|
|
|
1
|
+
/** Sandbox capability API routes: status, exec, browser, screen, audio, computer use. */
|
|
2
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
3
|
+
import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { platform, tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { readJsonBody as parseJsonBody, readRequestBody, sendJson as sendJsonResponse, } from "./http-helpers";
|
|
7
|
+
const MAX_COMPUTER_INPUT_LENGTH = 4096;
|
|
8
|
+
const MAX_KEYPRESS_LENGTH = 128;
|
|
9
|
+
const SAFE_KEYPRESS_PATTERN = /^[A-Za-z0-9+_.,: -]+$/;
|
|
10
|
+
const ALLOWED_AUDIO_FORMATS = new Set(["wav", "mp3", "ogg", "flac", "m4a"]);
|
|
11
|
+
const MIN_AUDIO_RECORD_DURATION_MS = 250;
|
|
12
|
+
const MAX_AUDIO_RECORD_DURATION_MS = 30_000;
|
|
13
|
+
// ── Route handler ────────────────────────────────────────────────────────────
|
|
14
|
+
/** Returns `true` if handled, `false` to fall through. */
|
|
15
|
+
export async function handleSandboxRoute(req, res, pathname, method, state) {
|
|
16
|
+
if (!pathname.startsWith("/api/sandbox")) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const mgr = state.sandboxManager;
|
|
20
|
+
// Platform info doesn't require a running manager
|
|
21
|
+
if (method === "GET" && pathname === "/api/sandbox/platform") {
|
|
22
|
+
sendJson(res, 200, getPlatformInfo());
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
// ── POST /api/sandbox/docker/start ────────────────────────────────
|
|
26
|
+
// Attempt to start Docker Desktop (works on macOS/Windows desktop builds)
|
|
27
|
+
if (method === "POST" && pathname === "/api/sandbox/docker/start") {
|
|
28
|
+
try {
|
|
29
|
+
const result = attemptDockerStart();
|
|
30
|
+
sendJson(res, 200, result);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
sendJson(res, 500, {
|
|
34
|
+
success: false,
|
|
35
|
+
error: `Failed to start Docker: ${err instanceof Error ? err.message : String(err)}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (!mgr) {
|
|
41
|
+
sendJson(res, 503, {
|
|
42
|
+
error: "Sandbox manager not initialized",
|
|
43
|
+
});
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
// ── GET /api/sandbox/status ─────────────────────────────────────────
|
|
47
|
+
if (method === "GET" && pathname === "/api/sandbox/status") {
|
|
48
|
+
sendJson(res, 200, mgr.getStatus());
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// ── GET /api/sandbox/events ─────────────────────────────────────────
|
|
52
|
+
if (method === "GET" && pathname === "/api/sandbox/events") {
|
|
53
|
+
const events = mgr.getEventLog();
|
|
54
|
+
sendJson(res, 200, { events: events.slice(-100) });
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
// ── POST /api/sandbox/start ─────────────────────────────────────────
|
|
58
|
+
if (method === "POST" && pathname === "/api/sandbox/start") {
|
|
59
|
+
try {
|
|
60
|
+
await mgr.start();
|
|
61
|
+
sendJson(res, 200, mgr.getStatus());
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
sendJson(res, 500, {
|
|
65
|
+
error: `Failed to start sandbox: ${err instanceof Error ? err.message : String(err)}`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
// ── POST /api/sandbox/stop ──────────────────────────────────────────
|
|
71
|
+
if (method === "POST" && pathname === "/api/sandbox/stop") {
|
|
72
|
+
try {
|
|
73
|
+
await mgr.stop();
|
|
74
|
+
sendJson(res, 200, mgr.getStatus());
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
sendJson(res, 500, {
|
|
78
|
+
error: `Failed to stop sandbox: ${err instanceof Error ? err.message : String(err)}`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// ── POST /api/sandbox/recover ───────────────────────────────────────
|
|
84
|
+
if (method === "POST" && pathname === "/api/sandbox/recover") {
|
|
85
|
+
try {
|
|
86
|
+
await mgr.recover();
|
|
87
|
+
sendJson(res, 200, mgr.getStatus());
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
sendJson(res, 500, {
|
|
91
|
+
error: `Recovery failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// ── POST /api/sandbox/exec ──────────────────────────────────────────
|
|
97
|
+
if (method === "POST" && pathname === "/api/sandbox/exec") {
|
|
98
|
+
const parsed = await readJsonBody(req, res);
|
|
99
|
+
if (!parsed)
|
|
100
|
+
return true;
|
|
101
|
+
if (!parsed.command || typeof parsed.command !== "string") {
|
|
102
|
+
sendJson(res, 400, { error: "Missing 'command' field" });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
const result = await mgr.exec({
|
|
106
|
+
command: parsed.command,
|
|
107
|
+
workdir: parsed.workdir,
|
|
108
|
+
timeoutMs: parsed.timeoutMs,
|
|
109
|
+
});
|
|
110
|
+
sendJson(res, result.exitCode === 0 ? 200 : 422, result);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
// ── GET /api/sandbox/browser ────────────────────────────────────────
|
|
114
|
+
if (method === "GET" && pathname === "/api/sandbox/browser") {
|
|
115
|
+
sendJson(res, 200, {
|
|
116
|
+
cdpEndpoint: mgr.getBrowserCdpEndpoint(),
|
|
117
|
+
wsEndpoint: mgr.getBrowserWsEndpoint(),
|
|
118
|
+
noVncEndpoint: mgr.getBrowserNoVncEndpoint(),
|
|
119
|
+
});
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
// ── Capability bridges ──────────────────────────────────────────────
|
|
123
|
+
if (method === "GET" && pathname === "/api/sandbox/screen/screenshot") {
|
|
124
|
+
try {
|
|
125
|
+
const screenshot = captureScreenshot();
|
|
126
|
+
res.writeHead(200, {
|
|
127
|
+
"Content-Type": "image/png",
|
|
128
|
+
"Content-Length": screenshot.length,
|
|
129
|
+
});
|
|
130
|
+
res.end(screenshot);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
sendJson(res, 500, {
|
|
134
|
+
error: `Screenshot failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
// ── POST /api/sandbox/screen/screenshot ─────────────────────────────
|
|
140
|
+
// Returns base64-encoded screenshot for easy consumption by agents
|
|
141
|
+
if (method === "POST" && pathname === "/api/sandbox/screen/screenshot") {
|
|
142
|
+
const rawBody = await readBody(req);
|
|
143
|
+
if (!rawBody || !rawBody.trim()) {
|
|
144
|
+
sendJson(res, 200, {
|
|
145
|
+
format: "png",
|
|
146
|
+
encoding: "base64",
|
|
147
|
+
width: null,
|
|
148
|
+
height: null,
|
|
149
|
+
data: captureScreenshot().toString("base64"),
|
|
150
|
+
});
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
let regionInput;
|
|
154
|
+
try {
|
|
155
|
+
regionInput = JSON.parse(rawBody);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
sendJson(res, 400, { error: "Invalid JSON body" });
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
const region = resolveScreenshotRegion(regionInput);
|
|
162
|
+
if (region.error) {
|
|
163
|
+
sendJson(res, 400, { error: region.error });
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const screenshot = captureScreenshot(region.region);
|
|
168
|
+
const base64 = screenshot.toString("base64");
|
|
169
|
+
sendJson(res, 200, {
|
|
170
|
+
format: "png",
|
|
171
|
+
encoding: "base64",
|
|
172
|
+
width: null, // platform-dependent
|
|
173
|
+
height: null,
|
|
174
|
+
data: base64,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
sendJson(res, 500, {
|
|
179
|
+
error: `Screenshot failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
// ── GET /api/sandbox/screen/windows ─────────────────────────────────
|
|
185
|
+
if (method === "GET" && pathname === "/api/sandbox/screen/windows") {
|
|
186
|
+
try {
|
|
187
|
+
const windows = listWindows();
|
|
188
|
+
sendJson(res, 200, { windows });
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
sendJson(res, 200, { windows: [], error: String(err) });
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
// ── POST /api/sandbox/audio/record ──────────────────────────────────
|
|
196
|
+
if (method === "POST" && pathname === "/api/sandbox/audio/record") {
|
|
197
|
+
const body = await readBody(req);
|
|
198
|
+
let durationMs = 5000;
|
|
199
|
+
if (body) {
|
|
200
|
+
let parsed;
|
|
201
|
+
try {
|
|
202
|
+
parsed = JSON.parse(body);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
sendJson(res, 400, {
|
|
206
|
+
error: "Invalid JSON in request body",
|
|
207
|
+
});
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
if (parsed === null ||
|
|
211
|
+
typeof parsed !== "object" ||
|
|
212
|
+
Array.isArray(parsed)) {
|
|
213
|
+
sendJson(res, 400, { error: "Request body must be a JSON object" });
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
const bodyValues = parsed;
|
|
217
|
+
if (Object.hasOwn(bodyValues, "durationMs")) {
|
|
218
|
+
const durationValue = bodyValues.durationMs;
|
|
219
|
+
if (typeof durationValue !== "number") {
|
|
220
|
+
sendJson(res, 400, {
|
|
221
|
+
error: "durationMs must be a finite number",
|
|
222
|
+
});
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
// Defense in depth: JSON.parse only produces finite numbers, but this guard
|
|
226
|
+
// keeps behavior explicit against future parser/runtime changes.
|
|
227
|
+
if (!Number.isFinite(durationValue)) {
|
|
228
|
+
sendJson(res, 400, {
|
|
229
|
+
error: "durationMs must be a finite number",
|
|
230
|
+
});
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
if (!Number.isInteger(durationValue)) {
|
|
234
|
+
sendJson(res, 400, {
|
|
235
|
+
error: "durationMs must be an integer number of milliseconds",
|
|
236
|
+
});
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
if (durationValue < MIN_AUDIO_RECORD_DURATION_MS ||
|
|
240
|
+
durationValue > MAX_AUDIO_RECORD_DURATION_MS) {
|
|
241
|
+
sendJson(res, 400, {
|
|
242
|
+
error: `durationMs must be between ${MIN_AUDIO_RECORD_DURATION_MS} and ${MAX_AUDIO_RECORD_DURATION_MS} milliseconds`,
|
|
243
|
+
});
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
durationMs = durationValue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const audio = await recordAudio(durationMs);
|
|
251
|
+
sendJson(res, 200, {
|
|
252
|
+
format: "wav",
|
|
253
|
+
encoding: "base64",
|
|
254
|
+
durationMs,
|
|
255
|
+
data: audio.toString("base64"),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
catch (err) {
|
|
259
|
+
sendJson(res, 500, {
|
|
260
|
+
error: `Audio recording failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
// ── POST /api/sandbox/audio/play ────────────────────────────────────
|
|
266
|
+
if (method === "POST" && pathname === "/api/sandbox/audio/play") {
|
|
267
|
+
const parsed = await readJsonBody(req, res);
|
|
268
|
+
if (!parsed)
|
|
269
|
+
return true;
|
|
270
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
271
|
+
sendJson(res, 400, { error: "Body must be a JSON object" });
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
const payload = parsed;
|
|
275
|
+
if (typeof payload.data !== "string" || !payload.data.trim()) {
|
|
276
|
+
sendJson(res, 400, { error: "Missing 'data' field (base64 audio)" });
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
const formatResult = resolveAudioFormat(payload.format);
|
|
280
|
+
if (formatResult.error) {
|
|
281
|
+
sendJson(res, 400, { error: formatResult.error });
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
await playAudio(Buffer.from(payload.data, "base64"), formatResult.format);
|
|
286
|
+
sendJson(res, 200, { success: true });
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
sendJson(res, 500, {
|
|
290
|
+
error: `Audio playback failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
// ── POST /api/sandbox/computer/click ────────────────────────────────
|
|
296
|
+
if (method === "POST" && pathname === "/api/sandbox/computer/click") {
|
|
297
|
+
const parsed = await readJsonBody(req, res);
|
|
298
|
+
if (!parsed)
|
|
299
|
+
return true;
|
|
300
|
+
const clickPayload = resolveClickPayload(parsed);
|
|
301
|
+
if (clickPayload.error) {
|
|
302
|
+
sendJson(res, 400, { error: clickPayload.error });
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const { x, y, button } = clickPayload;
|
|
307
|
+
performClick(x, y, button);
|
|
308
|
+
sendJson(res, 200, { success: true, x, y, button });
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
sendJson(res, 500, {
|
|
312
|
+
error: `Click failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
// ── POST /api/sandbox/computer/type ─────────────────────────────────
|
|
318
|
+
if (method === "POST" && pathname === "/api/sandbox/computer/type") {
|
|
319
|
+
const parsed = await readJsonBody(req, res);
|
|
320
|
+
if (!parsed)
|
|
321
|
+
return true;
|
|
322
|
+
const typePayload = resolveTypePayload(parsed);
|
|
323
|
+
if (typePayload.error) {
|
|
324
|
+
sendJson(res, 400, { error: typePayload.error });
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const { text } = typePayload;
|
|
329
|
+
performType(text);
|
|
330
|
+
sendJson(res, 200, { success: true, length: text.length });
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
sendJson(res, 500, {
|
|
334
|
+
error: `Type failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
// ── POST /api/sandbox/computer/keypress ─────────────────────────────
|
|
340
|
+
if (method === "POST" && pathname === "/api/sandbox/computer/keypress") {
|
|
341
|
+
const parsed = await readJsonBody(req, res);
|
|
342
|
+
if (!parsed)
|
|
343
|
+
return true;
|
|
344
|
+
const keypressPayload = resolveKeypressPayload(parsed);
|
|
345
|
+
if (keypressPayload.error) {
|
|
346
|
+
sendJson(res, 400, { error: keypressPayload.error });
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const { keys } = keypressPayload;
|
|
351
|
+
performKeypress(keys);
|
|
352
|
+
sendJson(res, 200, { success: true, keys });
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
sendJson(res, 500, {
|
|
356
|
+
error: `Keypress failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
// ── Signing routes ─────────────────────────────────────────────────
|
|
362
|
+
if (method === "POST" && pathname === "/api/sandbox/sign") {
|
|
363
|
+
const signer = state.signingService;
|
|
364
|
+
if (!signer) {
|
|
365
|
+
sendJson(res, 503, { error: "Signing service not configured" });
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
const body = await readJsonBody(req, res);
|
|
369
|
+
if (body === null)
|
|
370
|
+
return true;
|
|
371
|
+
const parsed = resolveSigningRequestPayload(body);
|
|
372
|
+
if ("error" in parsed) {
|
|
373
|
+
sendJson(res, 400, { error: parsed.error });
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const result = await signer.submitSigningRequest(parsed.request);
|
|
378
|
+
sendJson(res, result.success ? 200 : 403, result);
|
|
379
|
+
}
|
|
380
|
+
catch (err) {
|
|
381
|
+
sendJson(res, 400, {
|
|
382
|
+
error: `Invalid request: ${err instanceof Error ? err.message : String(err)}`,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
if (method === "POST" && pathname === "/api/sandbox/sign/approve") {
|
|
388
|
+
const signer = state.signingService;
|
|
389
|
+
if (!signer) {
|
|
390
|
+
sendJson(res, 503, { error: "Signing service not configured" });
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
const body = await readJsonBody(req, res);
|
|
394
|
+
if (!body)
|
|
395
|
+
return true;
|
|
396
|
+
try {
|
|
397
|
+
const { requestId } = body;
|
|
398
|
+
const result = await signer.approveRequest(requestId);
|
|
399
|
+
sendJson(res, result.success ? 200 : 403, result);
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
sendJson(res, 400, { error: String(err) });
|
|
403
|
+
}
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
if (method === "POST" && pathname === "/api/sandbox/sign/reject") {
|
|
407
|
+
const signer = state.signingService;
|
|
408
|
+
if (!signer) {
|
|
409
|
+
sendJson(res, 503, { error: "Signing service not configured" });
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
const body = await readJsonBody(req, res);
|
|
413
|
+
if (!body)
|
|
414
|
+
return true;
|
|
415
|
+
try {
|
|
416
|
+
const { requestId } = body;
|
|
417
|
+
const rejected = signer.rejectRequest(requestId);
|
|
418
|
+
sendJson(res, 200, { rejected });
|
|
419
|
+
}
|
|
420
|
+
catch (err) {
|
|
421
|
+
sendJson(res, 400, { error: String(err) });
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
if (method === "GET" && pathname === "/api/sandbox/sign/pending") {
|
|
426
|
+
const signer = state.signingService;
|
|
427
|
+
if (!signer) {
|
|
428
|
+
sendJson(res, 503, { error: "Signing service not configured" });
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
sendJson(res, 200, { pending: signer.getPendingApprovals() });
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
if (method === "GET" && pathname === "/api/sandbox/sign/address") {
|
|
435
|
+
const signer = state.signingService;
|
|
436
|
+
if (!signer) {
|
|
437
|
+
sendJson(res, 503, { error: "Signing service not configured" });
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
const address = await signer.getAddress();
|
|
442
|
+
sendJson(res, 200, { address });
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
sendJson(res, 500, { error: String(err) });
|
|
446
|
+
}
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
// ── GET /api/sandbox/capabilities ───────────────────────────────────
|
|
450
|
+
if (method === "GET" && pathname === "/api/sandbox/capabilities") {
|
|
451
|
+
sendJson(res, 200, detectCapabilities());
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
// ── Fallthrough ─────────────────────────────────────────────────────
|
|
455
|
+
sendJson(res, 404, { error: `Unknown sandbox route: ${method} ${pathname}` });
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
function asObject(value) {
|
|
459
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
460
|
+
return null;
|
|
461
|
+
return value;
|
|
462
|
+
}
|
|
463
|
+
function resolveSigningRequestPayload(input) {
|
|
464
|
+
const obj = asObject(input);
|
|
465
|
+
if (!obj) {
|
|
466
|
+
return { error: "Signing payload must be a JSON object" };
|
|
467
|
+
}
|
|
468
|
+
const requestId = obj.requestId;
|
|
469
|
+
const chainId = parseFiniteInteger(obj.chainId);
|
|
470
|
+
const to = obj.to;
|
|
471
|
+
const value = obj.value;
|
|
472
|
+
const data = obj.data;
|
|
473
|
+
const nonce = obj.nonce === undefined ? undefined : parseFiniteInteger(obj.nonce);
|
|
474
|
+
const rawGasLimit = obj.gasLimit;
|
|
475
|
+
const createdAt = parseFiniteInteger(obj.createdAt);
|
|
476
|
+
if (typeof requestId !== "string" || !requestId.trim()) {
|
|
477
|
+
return { error: "Signing payload requires a non-empty string 'requestId'" };
|
|
478
|
+
}
|
|
479
|
+
if (chainId === null || chainId < 0) {
|
|
480
|
+
return { error: "Signing payload requires an integer 'chainId' >= 0" };
|
|
481
|
+
}
|
|
482
|
+
if (typeof to !== "string" || !/^0x[0-9a-fA-F]{40}$/.test(to.trim())) {
|
|
483
|
+
return {
|
|
484
|
+
error: "Signing payload requires a hex 'to' address (e.g., 0x followed by 40 hex characters)",
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
488
|
+
return { error: "Signing payload requires a non-empty string 'value'" };
|
|
489
|
+
}
|
|
490
|
+
if (typeof data !== "string" || !data.trim()) {
|
|
491
|
+
return { error: "Signing payload requires a non-empty string 'data'" };
|
|
492
|
+
}
|
|
493
|
+
if (nonce === null) {
|
|
494
|
+
return { error: "'nonce' must be a non-negative integer when provided" };
|
|
495
|
+
}
|
|
496
|
+
if (createdAt === null) {
|
|
497
|
+
return { error: "Signing payload requires an integer 'createdAt'" };
|
|
498
|
+
}
|
|
499
|
+
if (rawGasLimit !== undefined && typeof rawGasLimit !== "string") {
|
|
500
|
+
return {
|
|
501
|
+
error: "Signing payload 'gasLimit' must be a string when provided",
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const gasLimit = rawGasLimit?.trim();
|
|
505
|
+
if (gasLimit === "") {
|
|
506
|
+
return {
|
|
507
|
+
error: "Signing payload 'gasLimit' cannot be empty when provided",
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
request: {
|
|
512
|
+
requestId: requestId.trim(),
|
|
513
|
+
chainId,
|
|
514
|
+
to: to.trim(),
|
|
515
|
+
value: value.trim(),
|
|
516
|
+
data,
|
|
517
|
+
...(nonce === undefined ? {} : { nonce }),
|
|
518
|
+
...(gasLimit === undefined ? {} : { gasLimit }),
|
|
519
|
+
createdAt,
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function parseFiniteInteger(value) {
|
|
524
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
525
|
+
return null;
|
|
526
|
+
if (!Number.isInteger(value))
|
|
527
|
+
return null;
|
|
528
|
+
return value;
|
|
529
|
+
}
|
|
530
|
+
function resolveScreenshotRegion(input) {
|
|
531
|
+
if (input === undefined || input === null)
|
|
532
|
+
return {};
|
|
533
|
+
const obj = asObject(input);
|
|
534
|
+
if (!obj)
|
|
535
|
+
return { error: "Screenshot region payload must be a JSON object" };
|
|
536
|
+
const hasRegionField = "x" in obj || "y" in obj || "width" in obj || "height" in obj;
|
|
537
|
+
if (!hasRegionField)
|
|
538
|
+
return {};
|
|
539
|
+
const x = parseFiniteInteger(obj.x);
|
|
540
|
+
const y = parseFiniteInteger(obj.y);
|
|
541
|
+
const width = parseFiniteInteger(obj.width);
|
|
542
|
+
const height = parseFiniteInteger(obj.height);
|
|
543
|
+
if (x === null || y === null || width === null || height === null) {
|
|
544
|
+
return {
|
|
545
|
+
error: "Region requires integer x, y, width, and height values",
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
if (width <= 0 || height <= 0) {
|
|
549
|
+
return { error: "Region width and height must be greater than 0" };
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
region: { x, y, width, height },
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function resolveClickPayload(input) {
|
|
556
|
+
const obj = asObject(input);
|
|
557
|
+
if (!obj) {
|
|
558
|
+
return {
|
|
559
|
+
x: 0,
|
|
560
|
+
y: 0,
|
|
561
|
+
button: "left",
|
|
562
|
+
error: "Click payload must be a JSON object",
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const x = parseFiniteInteger(obj.x);
|
|
566
|
+
const y = parseFiniteInteger(obj.y);
|
|
567
|
+
if (x === null || y === null) {
|
|
568
|
+
return {
|
|
569
|
+
x: 0,
|
|
570
|
+
y: 0,
|
|
571
|
+
button: "left",
|
|
572
|
+
error: "Click payload requires integer x and y coordinates",
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
const rawButton = obj.button;
|
|
576
|
+
let button = "left";
|
|
577
|
+
if (rawButton !== undefined) {
|
|
578
|
+
if (rawButton !== "left" && rawButton !== "right") {
|
|
579
|
+
return {
|
|
580
|
+
x,
|
|
581
|
+
y,
|
|
582
|
+
button,
|
|
583
|
+
error: "button must be either 'left' or 'right'",
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
button = rawButton;
|
|
587
|
+
}
|
|
588
|
+
return { x, y, button };
|
|
589
|
+
}
|
|
590
|
+
function resolveTypePayload(input) {
|
|
591
|
+
const obj = asObject(input);
|
|
592
|
+
if (!obj)
|
|
593
|
+
return { text: "", error: "Type payload must be a JSON object" };
|
|
594
|
+
if (typeof obj.text !== "string") {
|
|
595
|
+
return { text: "", error: "Type payload requires a string 'text' field" };
|
|
596
|
+
}
|
|
597
|
+
if (obj.text.length === 0) {
|
|
598
|
+
return { text: "", error: "text cannot be empty" };
|
|
599
|
+
}
|
|
600
|
+
if (obj.text.length > MAX_COMPUTER_INPUT_LENGTH) {
|
|
601
|
+
return {
|
|
602
|
+
text: "",
|
|
603
|
+
error: `text exceeds maximum length (${MAX_COMPUTER_INPUT_LENGTH})`,
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return { text: obj.text };
|
|
607
|
+
}
|
|
608
|
+
function resolveKeypressPayload(input) {
|
|
609
|
+
const obj = asObject(input);
|
|
610
|
+
if (!obj) {
|
|
611
|
+
return { keys: "", error: "Keypress payload must be a JSON object" };
|
|
612
|
+
}
|
|
613
|
+
if (typeof obj.keys !== "string") {
|
|
614
|
+
return {
|
|
615
|
+
keys: "",
|
|
616
|
+
error: "Keypress payload requires a string 'keys' field",
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const keys = obj.keys.trim();
|
|
620
|
+
if (!keys)
|
|
621
|
+
return { keys: "", error: "keys cannot be empty" };
|
|
622
|
+
if (keys.length > MAX_KEYPRESS_LENGTH) {
|
|
623
|
+
return {
|
|
624
|
+
keys: "",
|
|
625
|
+
error: `keys exceeds maximum length (${MAX_KEYPRESS_LENGTH})`,
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
if (!SAFE_KEYPRESS_PATTERN.test(keys)) {
|
|
629
|
+
return {
|
|
630
|
+
keys: "",
|
|
631
|
+
error: "keys contains unsupported characters; allowed: letters, numbers, space, +, _, ., ,, :, -",
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return { keys };
|
|
635
|
+
}
|
|
636
|
+
function resolveAudioFormat(input) {
|
|
637
|
+
if (input === undefined || input === null)
|
|
638
|
+
return { format: "wav" };
|
|
639
|
+
if (typeof input !== "string") {
|
|
640
|
+
return { format: "wav", error: "format must be a string" };
|
|
641
|
+
}
|
|
642
|
+
const normalized = input.trim().toLowerCase();
|
|
643
|
+
if (!normalized)
|
|
644
|
+
return { format: "wav" };
|
|
645
|
+
if (!/^[a-z0-9]+$/.test(normalized)) {
|
|
646
|
+
return {
|
|
647
|
+
format: "wav",
|
|
648
|
+
error: "format contains unsupported characters; use one of: wav, mp3, ogg, flac, m4a",
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
if (!ALLOWED_AUDIO_FORMATS.has(normalized)) {
|
|
652
|
+
return {
|
|
653
|
+
format: "wav",
|
|
654
|
+
error: "format must be one of: wav, mp3, ogg, flac, m4a",
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
return { format: normalized };
|
|
658
|
+
}
|
|
659
|
+
function runCommand(command, args, timeout) {
|
|
660
|
+
execFileSync(command, args, {
|
|
661
|
+
timeout,
|
|
662
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
function captureScreenshot(region) {
|
|
666
|
+
const os = platform();
|
|
667
|
+
const tmpFile = join(tmpdir(), `sandbox-screenshot-${Date.now()}.png`);
|
|
668
|
+
try {
|
|
669
|
+
if (os === "darwin") {
|
|
670
|
+
if (region) {
|
|
671
|
+
runCommand("screencapture", [
|
|
672
|
+
`-R${region.x},${region.y},${region.width},${region.height}`,
|
|
673
|
+
"-x",
|
|
674
|
+
tmpFile,
|
|
675
|
+
], 10000);
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
runCommand("screencapture", ["-x", tmpFile], 10000);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
else if (os === "linux") {
|
|
682
|
+
// Try tools in preference order
|
|
683
|
+
if (commandExists("import")) {
|
|
684
|
+
if (region) {
|
|
685
|
+
runCommand("import", [
|
|
686
|
+
"-window",
|
|
687
|
+
"root",
|
|
688
|
+
"-crop",
|
|
689
|
+
`${region.width}x${region.height}+${region.x}+${region.y}`,
|
|
690
|
+
tmpFile,
|
|
691
|
+
], 10000);
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
runCommand("import", ["-window", "root", tmpFile], 10000);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else if (commandExists("scrot")) {
|
|
698
|
+
runCommand("scrot", [tmpFile], 10000);
|
|
699
|
+
}
|
|
700
|
+
else if (commandExists("gnome-screenshot")) {
|
|
701
|
+
runCommand("gnome-screenshot", ["-f", tmpFile], 10000);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
throw new Error("No screenshot tool available. Install ImageMagick, scrot, or gnome-screenshot.");
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
else if (os === "win32") {
|
|
708
|
+
// PowerShell screenshot
|
|
709
|
+
const psCmd = [
|
|
710
|
+
`Add-Type -AssemblyName System.Windows.Forms`,
|
|
711
|
+
`$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds`,
|
|
712
|
+
`$bitmap = New-Object System.Drawing.Bitmap($screen.Width, $screen.Height)`,
|
|
713
|
+
`$graphics = [System.Drawing.Graphics]::FromImage($bitmap)`,
|
|
714
|
+
`$graphics.CopyFromScreen($screen.Location, [System.Drawing.Point]::Empty, $screen.Size)`,
|
|
715
|
+
`$bitmap.Save('${tmpFile.replace(/\//g, "\\")}')`,
|
|
716
|
+
`$graphics.Dispose()`,
|
|
717
|
+
`$bitmap.Dispose()`,
|
|
718
|
+
].join("; ");
|
|
719
|
+
execSync(`powershell -Command "${psCmd}"`, {
|
|
720
|
+
timeout: 15000,
|
|
721
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
throw new Error(`Screenshot not supported on platform: ${os}`);
|
|
726
|
+
}
|
|
727
|
+
const data = readFileSync(tmpFile);
|
|
728
|
+
try {
|
|
729
|
+
unlinkSync(tmpFile);
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
/* cleanup best effort */
|
|
733
|
+
}
|
|
734
|
+
return data;
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
// Clean up temp file on error
|
|
738
|
+
try {
|
|
739
|
+
unlinkSync(tmpFile);
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
/* ignore */
|
|
743
|
+
}
|
|
744
|
+
throw err;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function listWindows() {
|
|
748
|
+
const os = platform();
|
|
749
|
+
if (os === "darwin") {
|
|
750
|
+
try {
|
|
751
|
+
const script = `
|
|
752
|
+
tell application "System Events"
|
|
753
|
+
set windowList to {}
|
|
754
|
+
repeat with proc in (every process whose visible is true)
|
|
755
|
+
try
|
|
756
|
+
repeat with w in (every window of proc)
|
|
757
|
+
set end of windowList to (name of proc) & "|||" & (name of w) & "|||" & (id of w as text)
|
|
758
|
+
end repeat
|
|
759
|
+
end try
|
|
760
|
+
end repeat
|
|
761
|
+
return windowList as text
|
|
762
|
+
end tell`;
|
|
763
|
+
const output = execSync(`osascript -e '${script}'`, {
|
|
764
|
+
encoding: "utf-8",
|
|
765
|
+
timeout: 10000,
|
|
766
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
767
|
+
});
|
|
768
|
+
return output
|
|
769
|
+
.split(", ")
|
|
770
|
+
.filter(Boolean)
|
|
771
|
+
.map((entry) => {
|
|
772
|
+
const parts = entry.split("|||");
|
|
773
|
+
return {
|
|
774
|
+
app: parts[0] ?? "unknown",
|
|
775
|
+
title: parts[1] ?? "unknown",
|
|
776
|
+
id: parts[2] ?? "0",
|
|
777
|
+
};
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
catch {
|
|
781
|
+
return [];
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (os === "linux") {
|
|
785
|
+
try {
|
|
786
|
+
const output = execSync('wmctrl -l 2>/dev/null || xdotool search --name "" getwindowname 2>/dev/null', {
|
|
787
|
+
encoding: "utf-8",
|
|
788
|
+
timeout: 5000,
|
|
789
|
+
});
|
|
790
|
+
return output
|
|
791
|
+
.split("\n")
|
|
792
|
+
.filter(Boolean)
|
|
793
|
+
.map((line, i) => ({
|
|
794
|
+
id: String(i),
|
|
795
|
+
title: line.trim(),
|
|
796
|
+
app: "unknown",
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
799
|
+
catch {
|
|
800
|
+
return [];
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (os === "win32") {
|
|
804
|
+
try {
|
|
805
|
+
const output = execSync(`powershell -Command "Get-Process | Where-Object {$_.MainWindowTitle} | Select-Object Id, MainWindowTitle | ConvertTo-Json"`, { encoding: "utf-8", timeout: 10000 });
|
|
806
|
+
const processes = JSON.parse(output);
|
|
807
|
+
const list = Array.isArray(processes) ? processes : [processes];
|
|
808
|
+
return list.map((p) => ({
|
|
809
|
+
id: String(p.Id),
|
|
810
|
+
title: p.MainWindowTitle,
|
|
811
|
+
app: "unknown",
|
|
812
|
+
}));
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return [];
|
|
819
|
+
}
|
|
820
|
+
async function recordAudio(durationMs) {
|
|
821
|
+
const os = platform();
|
|
822
|
+
const durationSec = Math.ceil(durationMs / 1000);
|
|
823
|
+
const tmpFile = join(tmpdir(), `sandbox-audio-${Date.now()}.wav`);
|
|
824
|
+
if (os === "darwin") {
|
|
825
|
+
// Use sox (rec) on macOS
|
|
826
|
+
if (commandExists("rec")) {
|
|
827
|
+
execSync(`rec -q ${tmpFile} trim 0 ${durationSec}`, {
|
|
828
|
+
timeout: durationMs + 5000,
|
|
829
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
else if (commandExists("ffmpeg")) {
|
|
833
|
+
execSync(`ffmpeg -f avfoundation -i ":0" -t ${durationSec} -y ${tmpFile} 2>/dev/null`, { timeout: durationMs + 10000, stdio: ["ignore", "pipe", "pipe"] });
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
throw new Error("No audio recording tool available. Install sox or ffmpeg.");
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else if (os === "linux") {
|
|
840
|
+
if (commandExists("arecord")) {
|
|
841
|
+
execSync(`arecord -d ${durationSec} -f cd ${tmpFile}`, {
|
|
842
|
+
timeout: durationMs + 5000,
|
|
843
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
else if (commandExists("ffmpeg")) {
|
|
847
|
+
execSync(`ffmpeg -f pulse -i default -t ${durationSec} -y ${tmpFile} 2>/dev/null`, { timeout: durationMs + 10000, stdio: ["ignore", "pipe", "pipe"] });
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
throw new Error("No audio recording tool available. Install alsa-utils or ffmpeg.");
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
else if (os === "win32") {
|
|
854
|
+
// Use ffmpeg on Windows (most portable)
|
|
855
|
+
if (commandExists("ffmpeg")) {
|
|
856
|
+
execSync(`ffmpeg -f dshow -i audio="Microphone" -t ${durationSec} -y "${tmpFile.replace(/\//g, "\\")}" 2>NUL`, { timeout: durationMs + 10000, stdio: ["ignore", "pipe", "pipe"] });
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
throw new Error("No audio recording tool available. Install ffmpeg.");
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
throw new Error(`Audio recording not supported on platform: ${os}`);
|
|
864
|
+
}
|
|
865
|
+
const data = readFileSync(tmpFile);
|
|
866
|
+
try {
|
|
867
|
+
unlinkSync(tmpFile);
|
|
868
|
+
}
|
|
869
|
+
catch {
|
|
870
|
+
/* cleanup */
|
|
871
|
+
}
|
|
872
|
+
return data;
|
|
873
|
+
}
|
|
874
|
+
async function playAudio(data, format) {
|
|
875
|
+
const os = platform();
|
|
876
|
+
const tmpFile = join(tmpdir(), `sandbox-play-${Date.now()}.${format}`);
|
|
877
|
+
writeFileSync(tmpFile, data);
|
|
878
|
+
try {
|
|
879
|
+
if (os === "darwin") {
|
|
880
|
+
runCommand("afplay", [tmpFile], 60000);
|
|
881
|
+
}
|
|
882
|
+
else if (os === "linux") {
|
|
883
|
+
if (commandExists("aplay")) {
|
|
884
|
+
runCommand("aplay", [tmpFile], 60000);
|
|
885
|
+
}
|
|
886
|
+
else if (commandExists("paplay")) {
|
|
887
|
+
runCommand("paplay", [tmpFile], 60000);
|
|
888
|
+
}
|
|
889
|
+
else if (commandExists("ffplay")) {
|
|
890
|
+
runCommand("ffplay", ["-autoexit", "-nodisp", tmpFile], 60000);
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
throw new Error("No audio playback tool available.");
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
else if (os === "win32") {
|
|
897
|
+
const escapedPath = tmpFile.replace(/\//g, "\\").replace(/'/g, "''");
|
|
898
|
+
runCommand("powershell", [
|
|
899
|
+
"-Command",
|
|
900
|
+
`(New-Object Media.SoundPlayer '${escapedPath}').PlaySync()`,
|
|
901
|
+
], 60000);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
try {
|
|
906
|
+
unlinkSync(tmpFile);
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
/* cleanup */
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function toAppleScriptStringLiteral(value) {
|
|
914
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
915
|
+
}
|
|
916
|
+
function performClick(x, y, button) {
|
|
917
|
+
const os = platform();
|
|
918
|
+
if (os === "darwin") {
|
|
919
|
+
// Use cliclick on macOS (brew install cliclick)
|
|
920
|
+
if (commandExists("cliclick")) {
|
|
921
|
+
const btn = button === "right" ? "rc" : "c";
|
|
922
|
+
runCommand("cliclick", [`${btn}:${x},${y}`], 5000);
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
// AppleScript fallback
|
|
926
|
+
runCommand("osascript", ["-e", `tell application "System Events" to click at {${x}, ${y}}`], 5000);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
else if (os === "linux") {
|
|
930
|
+
if (commandExists("xdotool")) {
|
|
931
|
+
const btn = button === "right" ? "3" : "1";
|
|
932
|
+
runCommand("xdotool", ["mousemove", String(x), String(y), "click", btn], 5000);
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
throw new Error("xdotool required for mouse control on Linux.");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
else if (os === "win32") {
|
|
939
|
+
// Use Win32 API via PowerShell to perform an actual mouse click
|
|
940
|
+
const psScript = [
|
|
941
|
+
`Add-Type -AssemblyName System.Windows.Forms`,
|
|
942
|
+
`[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x},${y})`,
|
|
943
|
+
`Add-Type -MemberDefinition '[DllImport("user32.dll")] public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);' -Name Win32Mouse -Namespace Win32`,
|
|
944
|
+
button === "right"
|
|
945
|
+
? `[Win32.Win32Mouse]::mouse_event(0x0008, 0, 0, 0, 0); [Win32.Win32Mouse]::mouse_event(0x0010, 0, 0, 0, 0)` // right down + up
|
|
946
|
+
: `[Win32.Win32Mouse]::mouse_event(0x0002, 0, 0, 0, 0); [Win32.Win32Mouse]::mouse_event(0x0004, 0, 0, 0, 0)`, // left down + up
|
|
947
|
+
].join("; ");
|
|
948
|
+
runCommand("powershell", ["-Command", psScript], 5000);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function performType(text) {
|
|
952
|
+
const os = platform();
|
|
953
|
+
if (os === "darwin") {
|
|
954
|
+
if (commandExists("cliclick")) {
|
|
955
|
+
runCommand("cliclick", [`t:${text}`], 10000);
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
runCommand("osascript", [
|
|
959
|
+
"-e",
|
|
960
|
+
`tell application "System Events" to keystroke ${toAppleScriptStringLiteral(text)}`,
|
|
961
|
+
], 10000);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
else if (os === "linux") {
|
|
965
|
+
if (commandExists("xdotool")) {
|
|
966
|
+
runCommand("xdotool", ["type", "--", text], 10000);
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
throw new Error("xdotool required for keyboard input on Linux.");
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
else if (os === "win32") {
|
|
973
|
+
const escaped = text.replace(/'/g, "''");
|
|
974
|
+
runCommand("powershell", [
|
|
975
|
+
"-Command",
|
|
976
|
+
`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escaped}')`,
|
|
977
|
+
], 10000);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function performKeypress(keys) {
|
|
981
|
+
const os = platform();
|
|
982
|
+
if (os === "darwin") {
|
|
983
|
+
if (commandExists("cliclick")) {
|
|
984
|
+
runCommand("cliclick", [`kp:${keys}`], 5000);
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
const symbolicKeyCodes = {
|
|
988
|
+
return: 36,
|
|
989
|
+
enter: 36,
|
|
990
|
+
tab: 48,
|
|
991
|
+
space: 49,
|
|
992
|
+
escape: 53,
|
|
993
|
+
esc: 53,
|
|
994
|
+
left: 123,
|
|
995
|
+
right: 124,
|
|
996
|
+
down: 125,
|
|
997
|
+
up: 126,
|
|
998
|
+
};
|
|
999
|
+
const normalized = keys.trim().toLowerCase();
|
|
1000
|
+
const mappedCode = symbolicKeyCodes[normalized];
|
|
1001
|
+
const numericCode = mappedCode ??
|
|
1002
|
+
(Number.isInteger(Number(keys.trim())) ? Number(keys.trim()) : null);
|
|
1003
|
+
if (numericCode !== null) {
|
|
1004
|
+
runCommand("osascript", ["-e", `tell application "System Events" to key code ${numericCode}`], 5000);
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
runCommand("osascript", [
|
|
1008
|
+
"-e",
|
|
1009
|
+
`tell application "System Events" to keystroke ${toAppleScriptStringLiteral(keys)}`,
|
|
1010
|
+
], 5000);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
else if (os === "linux") {
|
|
1015
|
+
if (commandExists("xdotool")) {
|
|
1016
|
+
runCommand("xdotool", ["key", keys], 5000);
|
|
1017
|
+
}
|
|
1018
|
+
else {
|
|
1019
|
+
throw new Error("xdotool required for key input on Linux.");
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else if (os === "win32") {
|
|
1023
|
+
const escaped = keys.replace(/'/g, "''");
|
|
1024
|
+
runCommand("powershell", [
|
|
1025
|
+
"-Command",
|
|
1026
|
+
`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${escaped}')`,
|
|
1027
|
+
], 5000);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
function detectCapabilities() {
|
|
1031
|
+
const os = platform();
|
|
1032
|
+
const caps = {};
|
|
1033
|
+
// Screenshot
|
|
1034
|
+
if (os === "darwin") {
|
|
1035
|
+
caps.screenshot = { available: true, tool: "screencapture (built-in)" };
|
|
1036
|
+
}
|
|
1037
|
+
else if (os === "linux") {
|
|
1038
|
+
if (commandExists("import"))
|
|
1039
|
+
caps.screenshot = { available: true, tool: "ImageMagick import" };
|
|
1040
|
+
else if (commandExists("scrot"))
|
|
1041
|
+
caps.screenshot = { available: true, tool: "scrot" };
|
|
1042
|
+
else if (commandExists("gnome-screenshot"))
|
|
1043
|
+
caps.screenshot = { available: true, tool: "gnome-screenshot" };
|
|
1044
|
+
else
|
|
1045
|
+
caps.screenshot = {
|
|
1046
|
+
available: false,
|
|
1047
|
+
tool: "none (install ImageMagick, scrot, or gnome-screenshot)",
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
else if (os === "win32") {
|
|
1051
|
+
caps.screenshot = { available: true, tool: "PowerShell System.Drawing" };
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
caps.screenshot = { available: false, tool: "unsupported platform" };
|
|
1055
|
+
}
|
|
1056
|
+
// Audio record
|
|
1057
|
+
if (os === "darwin") {
|
|
1058
|
+
if (commandExists("rec"))
|
|
1059
|
+
caps.audioRecord = { available: true, tool: "sox rec" };
|
|
1060
|
+
else if (commandExists("ffmpeg"))
|
|
1061
|
+
caps.audioRecord = { available: true, tool: "ffmpeg" };
|
|
1062
|
+
else
|
|
1063
|
+
caps.audioRecord = {
|
|
1064
|
+
available: false,
|
|
1065
|
+
tool: "none (install sox or ffmpeg)",
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
else if (os === "linux") {
|
|
1069
|
+
if (commandExists("arecord"))
|
|
1070
|
+
caps.audioRecord = { available: true, tool: "arecord" };
|
|
1071
|
+
else if (commandExists("ffmpeg"))
|
|
1072
|
+
caps.audioRecord = { available: true, tool: "ffmpeg" };
|
|
1073
|
+
else
|
|
1074
|
+
caps.audioRecord = {
|
|
1075
|
+
available: false,
|
|
1076
|
+
tool: "none (install alsa-utils or ffmpeg)",
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
else if (os === "win32") {
|
|
1080
|
+
if (commandExists("ffmpeg"))
|
|
1081
|
+
caps.audioRecord = { available: true, tool: "ffmpeg" };
|
|
1082
|
+
else
|
|
1083
|
+
caps.audioRecord = { available: false, tool: "none (install ffmpeg)" };
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
caps.audioRecord = { available: false, tool: "unsupported" };
|
|
1087
|
+
}
|
|
1088
|
+
// Audio play
|
|
1089
|
+
if (os === "darwin")
|
|
1090
|
+
caps.audioPlay = { available: true, tool: "afplay (built-in)" };
|
|
1091
|
+
else if (os === "linux") {
|
|
1092
|
+
if (commandExists("aplay"))
|
|
1093
|
+
caps.audioPlay = { available: true, tool: "aplay" };
|
|
1094
|
+
else if (commandExists("paplay"))
|
|
1095
|
+
caps.audioPlay = { available: true, tool: "paplay" };
|
|
1096
|
+
else if (commandExists("ffplay"))
|
|
1097
|
+
caps.audioPlay = { available: true, tool: "ffplay" };
|
|
1098
|
+
else
|
|
1099
|
+
caps.audioPlay = { available: false, tool: "none" };
|
|
1100
|
+
}
|
|
1101
|
+
else if (os === "win32") {
|
|
1102
|
+
caps.audioPlay = { available: true, tool: "PowerShell SoundPlayer" };
|
|
1103
|
+
}
|
|
1104
|
+
else {
|
|
1105
|
+
caps.audioPlay = { available: false, tool: "unsupported" };
|
|
1106
|
+
}
|
|
1107
|
+
// Mouse/keyboard control
|
|
1108
|
+
if (os === "darwin") {
|
|
1109
|
+
if (commandExists("cliclick"))
|
|
1110
|
+
caps.computerUse = { available: true, tool: "cliclick" };
|
|
1111
|
+
else
|
|
1112
|
+
caps.computerUse = { available: true, tool: "AppleScript (limited)" };
|
|
1113
|
+
}
|
|
1114
|
+
else if (os === "linux") {
|
|
1115
|
+
if (commandExists("xdotool"))
|
|
1116
|
+
caps.computerUse = { available: true, tool: "xdotool" };
|
|
1117
|
+
else
|
|
1118
|
+
caps.computerUse = { available: false, tool: "none (install xdotool)" };
|
|
1119
|
+
}
|
|
1120
|
+
else if (os === "win32") {
|
|
1121
|
+
caps.computerUse = { available: true, tool: "PowerShell SendKeys" };
|
|
1122
|
+
}
|
|
1123
|
+
else {
|
|
1124
|
+
caps.computerUse = { available: false, tool: "unsupported" };
|
|
1125
|
+
}
|
|
1126
|
+
// Window listing
|
|
1127
|
+
if (os === "darwin")
|
|
1128
|
+
caps.windowList = { available: true, tool: "AppleScript" };
|
|
1129
|
+
else if (os === "linux") {
|
|
1130
|
+
if (commandExists("wmctrl"))
|
|
1131
|
+
caps.windowList = { available: true, tool: "wmctrl" };
|
|
1132
|
+
else if (commandExists("xdotool"))
|
|
1133
|
+
caps.windowList = { available: true, tool: "xdotool" };
|
|
1134
|
+
else
|
|
1135
|
+
caps.windowList = {
|
|
1136
|
+
available: false,
|
|
1137
|
+
tool: "none (install wmctrl or xdotool)",
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
else if (os === "win32") {
|
|
1141
|
+
caps.windowList = { available: true, tool: "PowerShell Get-Process" };
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
caps.windowList = { available: false, tool: "unsupported" };
|
|
1145
|
+
}
|
|
1146
|
+
// Browser
|
|
1147
|
+
caps.browser = { available: true, tool: "CDP via sandbox browser container" };
|
|
1148
|
+
// Shell
|
|
1149
|
+
caps.shell = { available: true, tool: "docker exec" };
|
|
1150
|
+
return caps;
|
|
1151
|
+
}
|
|
1152
|
+
function getPlatformInfo() {
|
|
1153
|
+
const os = platform();
|
|
1154
|
+
let dockerInstalled = false;
|
|
1155
|
+
let dockerRunning = false;
|
|
1156
|
+
let appleContainerAvailable = false;
|
|
1157
|
+
// Check if docker binary exists (installed)
|
|
1158
|
+
try {
|
|
1159
|
+
const which = os === "win32" ? "where" : "which";
|
|
1160
|
+
execSync(`${which} docker`, { stdio: "ignore", timeout: 3000 });
|
|
1161
|
+
dockerInstalled = true;
|
|
1162
|
+
}
|
|
1163
|
+
catch {
|
|
1164
|
+
/* not installed */
|
|
1165
|
+
}
|
|
1166
|
+
// Check if docker daemon is running (docker info succeeds only when daemon is up)
|
|
1167
|
+
if (dockerInstalled) {
|
|
1168
|
+
try {
|
|
1169
|
+
execSync("docker info", { stdio: "ignore", timeout: 5000 });
|
|
1170
|
+
dockerRunning = true;
|
|
1171
|
+
}
|
|
1172
|
+
catch {
|
|
1173
|
+
/* installed but not running */
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
if (os === "darwin") {
|
|
1177
|
+
try {
|
|
1178
|
+
execSync("which container", { stdio: "ignore", timeout: 3000 });
|
|
1179
|
+
appleContainerAvailable = true;
|
|
1180
|
+
}
|
|
1181
|
+
catch {
|
|
1182
|
+
/* */
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
return {
|
|
1186
|
+
platform: os,
|
|
1187
|
+
arch: require("node:os").arch(),
|
|
1188
|
+
dockerInstalled,
|
|
1189
|
+
dockerRunning,
|
|
1190
|
+
// Legacy compat: dockerAvailable = running (old clients check this)
|
|
1191
|
+
dockerAvailable: dockerRunning,
|
|
1192
|
+
appleContainerAvailable,
|
|
1193
|
+
wsl2: os === "win32" ? isWsl2Available() : false,
|
|
1194
|
+
recommended: os === "darwin" && appleContainerAvailable ? "apple-container" : "docker",
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
function isWsl2Available() {
|
|
1198
|
+
try {
|
|
1199
|
+
execSync("wsl --status", { stdio: "ignore", timeout: 5000 });
|
|
1200
|
+
return true;
|
|
1201
|
+
}
|
|
1202
|
+
catch {
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function attemptDockerStart() {
|
|
1207
|
+
const os = platform();
|
|
1208
|
+
try {
|
|
1209
|
+
if (os === "darwin") {
|
|
1210
|
+
execSync('open -a "Docker"', { timeout: 5000, stdio: "ignore" });
|
|
1211
|
+
return {
|
|
1212
|
+
success: true,
|
|
1213
|
+
message: "Docker Desktop is starting on macOS. Give it a moment~",
|
|
1214
|
+
waitMs: 15000,
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
if (os === "win32") {
|
|
1218
|
+
// Try common install locations
|
|
1219
|
+
const paths = [
|
|
1220
|
+
'"C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"',
|
|
1221
|
+
'"C:\\Program Files (x86)\\Docker\\Docker\\Docker Desktop.exe"',
|
|
1222
|
+
];
|
|
1223
|
+
let started = false;
|
|
1224
|
+
for (const p of paths) {
|
|
1225
|
+
try {
|
|
1226
|
+
execSync(`start "" ${p}`, {
|
|
1227
|
+
timeout: 5000,
|
|
1228
|
+
stdio: "ignore",
|
|
1229
|
+
shell: "cmd.exe",
|
|
1230
|
+
});
|
|
1231
|
+
started = true;
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
catch {
|
|
1235
|
+
/* try next path */
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (!started) {
|
|
1239
|
+
// Try via start menu
|
|
1240
|
+
execSync('start "" "Docker Desktop"', {
|
|
1241
|
+
timeout: 5000,
|
|
1242
|
+
stdio: "ignore",
|
|
1243
|
+
shell: "cmd.exe",
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
return {
|
|
1247
|
+
success: true,
|
|
1248
|
+
message: "Docker Desktop is starting on Windows. This may take 30 seconds~",
|
|
1249
|
+
waitMs: 30000,
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
if (os === "linux") {
|
|
1253
|
+
// Try systemctl first (most common)
|
|
1254
|
+
try {
|
|
1255
|
+
execSync("sudo systemctl start docker", {
|
|
1256
|
+
timeout: 10000,
|
|
1257
|
+
stdio: "ignore",
|
|
1258
|
+
});
|
|
1259
|
+
return {
|
|
1260
|
+
success: true,
|
|
1261
|
+
message: "Docker daemon started via systemctl",
|
|
1262
|
+
waitMs: 5000,
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
catch {
|
|
1266
|
+
/* systemctl may not be available */
|
|
1267
|
+
}
|
|
1268
|
+
// Try service command
|
|
1269
|
+
try {
|
|
1270
|
+
execSync("sudo service docker start", {
|
|
1271
|
+
timeout: 10000,
|
|
1272
|
+
stdio: "ignore",
|
|
1273
|
+
});
|
|
1274
|
+
return {
|
|
1275
|
+
success: true,
|
|
1276
|
+
message: "Docker daemon started via service",
|
|
1277
|
+
waitMs: 5000,
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
catch {
|
|
1281
|
+
/* */
|
|
1282
|
+
}
|
|
1283
|
+
return {
|
|
1284
|
+
success: false,
|
|
1285
|
+
message: "Could not auto-start Docker on Linux. Run: sudo systemctl start docker",
|
|
1286
|
+
waitMs: 0,
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
success: false,
|
|
1291
|
+
message: `Auto-start not supported on ${os}`,
|
|
1292
|
+
waitMs: 0,
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
catch (err) {
|
|
1296
|
+
return {
|
|
1297
|
+
success: false,
|
|
1298
|
+
message: `Failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
1299
|
+
waitMs: 0,
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
1304
|
+
function commandExists(cmd) {
|
|
1305
|
+
try {
|
|
1306
|
+
const which = platform() === "win32" ? "where" : "which";
|
|
1307
|
+
execSync(`${which} ${cmd}`, { stdio: "ignore", timeout: 3000 });
|
|
1308
|
+
return true;
|
|
1309
|
+
}
|
|
1310
|
+
catch {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function sendJson(res, status, data) {
|
|
1315
|
+
sendJsonResponse(res, data, status);
|
|
1316
|
+
}
|
|
1317
|
+
function readBody(req) {
|
|
1318
|
+
return readRequestBody(req, {
|
|
1319
|
+
maxBytes: 10 * 1024 * 1024,
|
|
1320
|
+
returnNullOnTooLarge: true,
|
|
1321
|
+
returnNullOnError: true,
|
|
1322
|
+
destroyOnTooLarge: true,
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
function readJsonBody(req, res) {
|
|
1326
|
+
return parseJsonBody(req, res, {
|
|
1327
|
+
maxBytes: 10 * 1024 * 1024,
|
|
1328
|
+
requireObject: false,
|
|
1329
|
+
readErrorStatus: 400,
|
|
1330
|
+
parseErrorStatus: 400,
|
|
1331
|
+
readErrorMessage: "Missing request body",
|
|
1332
|
+
parseErrorMessage: "Invalid JSON in request body",
|
|
1333
|
+
});
|
|
1334
|
+
}
|