@toddzheng024/dscode-desktop 0.0.0-stage → 0.7.33
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 +21 -0
- package/README.md +120 -2
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/bin/apply_patch +9 -0
- package/cordis.patch.yml +433 -0
- package/docs/CHANGELOG.md +641 -0
- package/docs/account-login.md +44 -0
- package/docs/auto-review.md +128 -0
- package/docs/browser-use.md +783 -0
- package/docs/computer-use.md +81 -0
- package/docs/custom-providers.md +88 -0
- package/docs/demo.md +76 -0
- package/docs/distribution.md +58 -0
- package/docs/dscode-ultra.md +75 -0
- package/docs/email.md +296 -0
- package/docs/exec.md +40 -0
- package/docs/hub-distribution.md +190 -0
- package/docs/memory.md +121 -0
- package/docs/opencode-go.md +61 -0
- package/docs/plugin-hub.md +68 -0
- package/docs/releases/0.2.0.md +28 -0
- package/docs/releases/0.3.0.md +21 -0
- package/docs/releases/0.4.0.md +20 -0
- package/docs/releases/0.5.0.md +19 -0
- package/docs/releases/0.6.0.md +20 -0
- package/docs/releases/0.7.0.md +22 -0
- package/docs/releases/0.7.1.md +20 -0
- package/docs/releases/0.7.10.md +10 -0
- package/docs/releases/0.7.11.md +17 -0
- package/docs/releases/0.7.12.md +11 -0
- package/docs/releases/0.7.13.md +12 -0
- package/docs/releases/0.7.14.md +11 -0
- package/docs/releases/0.7.15.md +11 -0
- package/docs/releases/0.7.16.md +10 -0
- package/docs/releases/0.7.17.md +12 -0
- package/docs/releases/0.7.18.md +13 -0
- package/docs/releases/0.7.19.md +8 -0
- package/docs/releases/0.7.2.md +19 -0
- package/docs/releases/0.7.20.md +10 -0
- package/docs/releases/0.7.21.md +9 -0
- package/docs/releases/0.7.22.md +11 -0
- package/docs/releases/0.7.23.md +14 -0
- package/docs/releases/0.7.24.md +11 -0
- package/docs/releases/0.7.25.md +8 -0
- package/docs/releases/0.7.26.md +18 -0
- package/docs/releases/0.7.27.md +8 -0
- package/docs/releases/0.7.28.md +22 -0
- package/docs/releases/0.7.29.md +12 -0
- package/docs/releases/0.7.3.md +23 -0
- package/docs/releases/0.7.30.md +19 -0
- package/docs/releases/0.7.31.md +14 -0
- package/docs/releases/0.7.32.md +14 -0
- package/docs/releases/0.7.33.md +30 -0
- package/docs/releases/0.7.4.md +16 -0
- package/docs/releases/0.7.5.md +18 -0
- package/docs/releases/0.7.6.md +29 -0
- package/docs/releases/0.7.7.md +36 -0
- package/docs/releases/0.7.8.md +14 -0
- package/docs/releases/0.7.9.md +15 -0
- package/docs/session-bridge.md +80 -0
- package/docs/session-cards.md +53 -0
- package/docs/session-communication.md +186 -0
- package/docs/session-metrics.md +41 -0
- package/docs/skills.md +81 -0
- package/docs/triggers.md +338 -0
- package/docs/tui-commands.md +204 -0
- package/extensions/browser/manifest.json +22 -0
- package/extensions/browser/popup.css +6 -0
- package/extensions/browser/popup.html +11 -0
- package/extensions/browser/popup.mjs +47 -0
- package/extensions/browser/protocol.mjs +168 -0
- package/extensions/browser/worker.mjs +94 -0
- package/icon.svg +7 -0
- package/locale/en.json +6 -0
- package/locale/zh-cn.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system/LICENSE +21 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.md +15 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.zh.md +15 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/flock.d.ts +13 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/flock.js +51 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/index.d.ts +73 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/index.js +98 -0
- package/node_modules/@deepseek-ai/node-addon-system/package.json +45 -0
- package/node_modules/@deepseek-ai/node-addon-system/src/flock.c +164 -0
- package/node_modules/@deepseek-ai/node-addon-system/src/main.c +298 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.zh.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/bin/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/prebuilds.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.zh.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/bin/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/prebuilds.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.zh.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/glibc/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/landlock-run +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/musl/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/prebuilds.json +24 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.zh.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/glibc/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/landlock-run +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/musl/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/prebuilds.json +24 -0
- package/package.json +156 -4
- package/plugins/account/index.mjs +178 -0
- package/plugins/account/state.mjs +123 -0
- package/plugins/auto-review/audit.mjs +24 -0
- package/plugins/auto-review/index.mjs +267 -0
- package/plugins/auto-review/policy.mjs +92 -0
- package/plugins/browser/SKILL.md +158 -0
- package/plugins/browser/access.mjs +143 -0
- package/plugins/browser/config.mjs +60 -0
- package/plugins/browser/connection.mjs +385 -0
- package/plugins/browser/desktop-client.mjs +282 -0
- package/plugins/browser/desktop-host.mjs +74 -0
- package/plugins/browser/document-identity.mjs +57 -0
- package/plugins/browser/extension-relay.mjs +66 -0
- package/plugins/browser/files.mjs +32 -0
- package/plugins/browser/index.mjs +235 -0
- package/plugins/browser/mcp-entry.mjs +18 -0
- package/plugins/browser/mcp-tools.mjs +21 -0
- package/plugins/browser/presentation.mjs +45 -0
- package/plugins/browser/preview.mjs +77 -0
- package/plugins/browser/review.mjs +20 -0
- package/plugins/browser/screenshots.mjs +35 -0
- package/plugins/clipboard-image/clipboard-image.swift +56 -0
- package/plugins/clipboard-image/index.mjs +70 -0
- package/plugins/code-review/baseline.mjs +143 -0
- package/plugins/code-review/git.mjs +163 -0
- package/plugins/code-review/index.mjs +236 -0
- package/plugins/compaction/engine.mjs +304 -0
- package/plugins/compaction/tetris.mjs +65 -0
- package/plugins/compaction/threshold.mjs +98 -0
- package/plugins/computer-use/DESKTOP-SKILL.md +64 -0
- package/plugins/computer-use/desktop-host.mjs +75 -0
- package/plugins/computer-use/desktop-skill.mjs +28 -0
- package/plugins/computer-use/desktop-upstream.mjs +3 -0
- package/plugins/computer-use/index.mjs +37 -0
- package/plugins/cordis-config/plain.mjs +31 -0
- package/plugins/credentials/index.mjs +73 -0
- package/plugins/custom/adapter.mjs +110 -0
- package/plugins/custom/config.mjs +85 -0
- package/plugins/custom/desktop-client.mjs +150 -0
- package/plugins/custom/desktop-host.mjs +78 -0
- package/plugins/custom/images.mjs +32 -0
- package/plugins/custom/index.mjs +145 -0
- package/plugins/custom/wire.mjs +179 -0
- package/plugins/desktop/client.mjs +1041 -0
- package/plugins/desktop/index.mjs +10 -0
- package/plugins/desktop/shell-environment.mjs +9 -0
- package/plugins/desktop-workspace/hooks.mjs +29 -0
- package/plugins/desktop-workspace/index.mjs +37 -0
- package/plugins/dscode/board.mjs +260 -0
- package/plugins/dscode/child-target.mjs +11 -0
- package/plugins/dscode/control.mjs +17 -0
- package/plugins/dscode/desktop-client.mjs +55 -0
- package/plugins/dscode/desktop-host.mjs +37 -0
- package/plugins/dscode/index.mjs +270 -0
- package/plugins/email/cli.mjs +32 -0
- package/plugins/email/contacts.mjs +36 -0
- package/plugins/email/desktop-client.mjs +117 -0
- package/plugins/email/desktop-host.mjs +54 -0
- package/plugins/email/desktop-service.mjs +106 -0
- package/plugins/email/gmail-oauth.mjs +69 -0
- package/plugins/email/gmail-store.mjs +2 -0
- package/plugins/email/gmail.mjs +194 -0
- package/plugins/email/imap.mjs +136 -0
- package/plugins/email/inbox.d.mts +25 -0
- package/plugins/email/inbox.mjs +76 -0
- package/plugins/email/smtp.mjs +70 -0
- package/plugins/email/store.mjs +32 -0
- package/plugins/email-tools/index.mjs +46 -0
- package/plugins/exec/cli.mjs +64 -0
- package/plugins/exec/index.mjs +112 -0
- package/plugins/grok/adapter.mjs +114 -0
- package/plugins/grok/auth.mjs +76 -0
- package/plugins/grok/billing.mjs +146 -0
- package/plugins/grok/index.mjs +122 -0
- package/plugins/grok/models.mjs +129 -0
- package/plugins/grok/status.mjs +28 -0
- package/plugins/grok/wire.mjs +268 -0
- package/plugins/hub/catalog.mjs +82 -0
- package/plugins/hub/desktop-client.mjs +114 -0
- package/plugins/hub/desktop-host.mjs +129 -0
- package/plugins/i18n/messages.d.mts +21 -0
- package/plugins/i18n/messages.mjs +537 -0
- package/plugins/jev/approval.mjs +131 -0
- package/plugins/jev/client.mjs +74 -0
- package/plugins/jev/index.mjs +99 -0
- package/plugins/memory/content.mjs +57 -0
- package/plugins/memory/desktop-host.mjs +3 -0
- package/plugins/memory/index.mjs +168 -0
- package/plugins/memory/pipeline.mjs +79 -0
- package/plugins/memory/store.mjs +93 -0
- package/plugins/message-source/kind.mjs +20 -0
- package/plugins/opencode-go/adapter.mjs +96 -0
- package/plugins/opencode-go/command.mjs +128 -0
- package/plugins/opencode-go/index.mjs +120 -0
- package/plugins/opencode-go/models.mjs +58 -0
- package/plugins/opencode-go/oauth.mjs +194 -0
- package/plugins/opencode-go/search.mjs +86 -0
- package/plugins/opencode-go/usage.mjs +119 -0
- package/plugins/opencode-go/version.mjs +10 -0
- package/plugins/opencode-go/wire.mjs +73 -0
- package/plugins/openrouter/adapter.mjs +232 -0
- package/plugins/openrouter/index.mjs +123 -0
- package/plugins/openrouter/models.mjs +157 -0
- package/plugins/openrouter/search.mjs +113 -0
- package/plugins/openrouter/wire.mjs +101 -0
- package/plugins/providers/catalog.mjs +160 -0
- package/plugins/providers/chat-messages.mjs +120 -0
- package/plugins/providers/chat-stream.mjs +174 -0
- package/plugins/providers/desktop-client.mjs +84 -0
- package/plugins/providers/desktop-host.mjs +63 -0
- package/plugins/providers/effort.mjs +35 -0
- package/plugins/providers/http-errors.mjs +50 -0
- package/plugins/providers/openrouter-account.mjs +171 -0
- package/plugins/session-bridge/client.mjs +106 -0
- package/plugins/session-bridge/communication.mjs +260 -0
- package/plugins/session-bridge/index.mjs +68 -0
- package/plugins/session-bridge/mailbox.mjs +267 -0
- package/plugins/session-bridge/paths.mjs +21 -0
- package/plugins/session-bridge/server.mjs +172 -0
- package/plugins/session-bridge/tasks.mjs +229 -0
- package/plugins/session-cards/content.mjs +64 -0
- package/plugins/session-cards/index.mjs +38 -0
- package/plugins/session-cards/manager.mjs +140 -0
- package/plugins/session-metrics/attribution.mjs +30 -0
- package/plugins/session-metrics/balance.mjs +81 -0
- package/plugins/session-metrics/desktop-client.mjs +62 -0
- package/plugins/session-metrics/desktop-host.mjs +46 -0
- package/plugins/session-metrics/index.mjs +96 -0
- package/plugins/session-metrics/pricing.mjs +84 -0
- package/plugins/session-metrics/rate.mjs +59 -0
- package/plugins/session-metrics/store.mjs +75 -0
- package/plugins/session-metrics/turns.mjs +134 -0
- package/plugins/session-metrics/view.mjs +271 -0
- package/plugins/time-marks/desktop-host.mjs +2 -0
- package/plugins/time-marks/index.mjs +116 -0
- package/plugins/time-marks/marks.mjs +152 -0
- package/plugins/triggers/cli.mjs +558 -0
- package/plugins/triggers/commands.mjs +143 -0
- package/plugins/triggers/config.mjs +264 -0
- package/plugins/triggers/desktop-client.mjs +155 -0
- package/plugins/triggers/desktop-host.mjs +88 -0
- package/plugins/triggers/desktop-management.mjs +97 -0
- package/plugins/triggers/desktop-scheduler.mjs +125 -0
- package/plugins/triggers/host.mjs +24 -0
- package/plugins/triggers/index.mjs +27 -0
- package/plugins/triggers/job-cli.mjs +110 -0
- package/plugins/triggers/jobs.mjs +156 -0
- package/plugins/triggers/launchd.mjs +101 -0
- package/plugins/triggers/lease.mjs +24 -0
- package/plugins/triggers/log.mjs +104 -0
- package/plugins/triggers/management.mjs +181 -0
- package/plugins/triggers/options.mjs +117 -0
- package/plugins/triggers/overlay.mjs +7 -0
- package/plugins/triggers/poll.mjs +65 -0
- package/plugins/triggers/run.mjs +157 -0
- package/plugins/triggers/schedule.mjs +40 -0
- package/plugins/triggers/scheduler-service.mjs +46 -0
- package/plugins/triggers/scheduler.mjs +92 -0
- package/plugins/triggers/session-run.mjs +125 -0
- package/plugins/triggers/session.mjs +48 -0
- package/plugins/triggers/source-emit.mjs +19 -0
- package/plugins/triggers/source-host.mjs +78 -0
- package/plugins/triggers/source-ingress.mjs +70 -0
- package/plugins/triggers/source-sandbox.mjs +35 -0
- package/plugins/triggers/sources.mjs +45 -0
- package/plugins/triggers/spool.mjs +145 -0
- package/plugins/triggers/tools.mjs +67 -0
- package/plugins/triggers/unattended.mjs +18 -0
- package/plugins/tui-tools/composition.mjs +28 -0
- package/plugins/tui-tools/desktop-host.mjs +2 -0
- package/plugins/tui-tools/doctor-cli.mjs +22 -0
- package/plugins/tui-tools/doctor.mjs +148 -0
- package/plugins/tui-tools/hook-sources.mjs +113 -0
- package/plugins/tui-tools/hooks.mjs +23 -0
- package/plugins/tui-tools/index.mjs +268 -0
- package/plugins/tui-tools/sandbox-runner.mjs +123 -0
- package/plugins/tui-tools/shell.mjs +27 -0
- package/plugins/tui-tools/update.mjs +111 -0
- package/plugins/tui-tools/workspace-discovery.mjs +129 -0
- package/plugins/ultra/policy.mjs +42 -0
- package/plugins/worktree-subagent/worktree.mjs +56 -0
- package/runtime-sources.json +94 -0
- package/vendor/bash/LICENSE +21 -0
- package/vendor/bash/index.js +717 -0
- package/vendor/bash/types/background.d.ts +50 -0
- package/vendor/bash/types/index.d.ts +38 -0
- package/vendor/bash/types/render.d.ts +57 -0
- package/vendor/command-goal/LICENSE +21 -0
- package/vendor/command-goal/index.js +210 -0
- package/vendor/command-goal/types/index.d.ts +10 -0
- package/vendor/computer-use/LICENSE +21 -0
- package/vendor/computer-use/lib/approval-policy.js +12 -0
- package/vendor/computer-use/lib/approval-policy.js.map +1 -0
- package/vendor/computer-use/lib/artifacts.js +79 -0
- package/vendor/computer-use/lib/artifacts.js.map +1 -0
- package/vendor/computer-use/lib/backend.js +3 -0
- package/vendor/computer-use/lib/backend.js.map +1 -0
- package/vendor/computer-use/lib/client.js +369 -0
- package/vendor/computer-use/lib/client.js.map +1 -0
- package/vendor/computer-use/lib/config.js +159 -0
- package/vendor/computer-use/lib/config.js.map +1 -0
- package/vendor/computer-use/lib/confirmations.js +99 -0
- package/vendor/computer-use/lib/confirmations.js.map +1 -0
- package/vendor/computer-use/lib/diff.js +58 -0
- package/vendor/computer-use/lib/diff.js.map +1 -0
- package/vendor/computer-use/lib/errors.js +22 -0
- package/vendor/computer-use/lib/errors.js.map +1 -0
- package/vendor/computer-use/lib/exposure.js +244 -0
- package/vendor/computer-use/lib/exposure.js.map +1 -0
- package/vendor/computer-use/lib/index.js +66 -0
- package/vendor/computer-use/lib/index.js.map +1 -0
- package/vendor/computer-use/lib/leases.js +262 -0
- package/vendor/computer-use/lib/leases.js.map +1 -0
- package/vendor/computer-use/lib/providers/macos.js +243 -0
- package/vendor/computer-use/lib/providers/macos.js.map +1 -0
- package/vendor/computer-use/lib/providers/native-helper.js +726 -0
- package/vendor/computer-use/lib/providers/native-helper.js.map +1 -0
- package/vendor/computer-use/lib/providers/unsupported.js +51 -0
- package/vendor/computer-use/lib/providers/unsupported.js.map +1 -0
- package/vendor/computer-use/lib/service.js +608 -0
- package/vendor/computer-use/lib/service.js.map +1 -0
- package/vendor/computer-use/lib/skill.js +136 -0
- package/vendor/computer-use/lib/skill.js.map +1 -0
- package/vendor/computer-use/lib/target-resolver.js +180 -0
- package/vendor/computer-use/lib/target-resolver.js.map +1 -0
- package/vendor/computer-use/lib/tools.js +511 -0
- package/vendor/computer-use/lib/tools.js.map +1 -0
- package/vendor/computer-use/lib/types/approval-policy.d.ts +13 -0
- package/vendor/computer-use/lib/types/approval-policy.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/artifacts.d.ts +10 -0
- package/vendor/computer-use/lib/types/artifacts.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/backend.d.ts +124 -0
- package/vendor/computer-use/lib/types/backend.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/client/index.d.ts +83 -0
- package/vendor/computer-use/lib/types/client/index.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/config.d.ts +94 -0
- package/vendor/computer-use/lib/types/config.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/confirmations.d.ts +23 -0
- package/vendor/computer-use/lib/types/confirmations.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/diff.d.ts +5 -0
- package/vendor/computer-use/lib/types/diff.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/errors.d.ts +15 -0
- package/vendor/computer-use/lib/types/errors.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/exposure.d.ts +30 -0
- package/vendor/computer-use/lib/types/exposure.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/index.d.ts +21 -0
- package/vendor/computer-use/lib/types/index.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/leases.d.ts +81 -0
- package/vendor/computer-use/lib/types/leases.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/macos.d.ts +39 -0
- package/vendor/computer-use/lib/types/providers/macos.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/native-helper.d.ts +48 -0
- package/vendor/computer-use/lib/types/providers/native-helper.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/unsupported.d.ts +23 -0
- package/vendor/computer-use/lib/types/providers/unsupported.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/service.d.ts +58 -0
- package/vendor/computer-use/lib/types/service.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/skill.d.ts +14 -0
- package/vendor/computer-use/lib/types/skill.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/target-resolver.d.ts +38 -0
- package/vendor/computer-use/lib/types/target-resolver.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/tools.d.ts +6 -0
- package/vendor/computer-use/lib/types/tools.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/types.d.ts +270 -0
- package/vendor/computer-use/lib/types/types.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/web.d.ts +33 -0
- package/vendor/computer-use/lib/types/web.d.ts.map +1 -0
- package/vendor/computer-use/lib/types.js +9 -0
- package/vendor/computer-use/lib/types.js.map +1 -0
- package/vendor/computer-use/lib/web.js +188 -0
- package/vendor/computer-use/lib/web.js.map +1 -0
- package/vendor/computer-use/native/macos/Sources/Fixture/main.swift +412 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorImage.swift +11 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorMotion.swift +67 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorOverlay.swift +840 -0
- package/vendor/computer-use/native/macos/Sources/Helper/TargetedPointer.swift +327 -0
- package/vendor/computer-use/native/macos/Sources/Helper/WindowNumberMatcher.swift +29 -0
- package/vendor/computer-use/native/macos/Sources/Helper/main.swift +1244 -0
- package/vendor/computer-use/native/macos/Sources/Monitor/main.swift +153 -0
- package/vendor/computer-use/native/macos/bin/dsh-computer-use-helper +0 -0
- package/vendor/computer-use/native/macos/manifest.json +14 -0
- package/vendor/persistent/LICENSE +21 -0
- package/vendor/persistent/index.js +411 -0
- package/vendor/persistent/types/index.d.ts +24 -0
- package/vendor/subagent/LICENSE +21 -0
- package/vendor/subagent/index.js +694 -0
- package/vendor/subagent/invariant.js +52 -0
- package/vendor/subagent/model-selection-settings.js +68 -0
- package/vendor/subagent/types/index.d.ts +82 -0
- package/vendor/subagent/types/invariant.d.ts +16 -0
- package/vendor/subagent/types/list-models.d.ts +10 -0
- package/vendor/subagent/types/model-selection-settings.d.ts +45 -0
- package/vendor/subagent/types/model-selection-state.d.ts +48 -0
- package/vendor/subagent/types/model-selection.d.ts +81 -0
- package/vendor/subagent-core/LICENSE +21 -0
- package/vendor/subagent-core/index.js +3202 -0
- package/vendor/subagent-core/invariant.js +76 -0
- package/vendor/subagent-core/typert.host.d.ts +3 -0
- package/vendor/subagent-core/typert.host.js +978 -0
- package/vendor/subagent-core/typert.remote-client.d.ts +25 -0
- package/vendor/subagent-core/typert.remote-client.js +114 -0
- package/vendor/subagent-core/types/archive-admission.d.ts +16 -0
- package/vendor/subagent-core/types/archive-admission.js +151 -0
- package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
- package/vendor/subagent-core/types/assistant-output.js +73 -0
- package/vendor/subagent-core/types/catalog.d.ts +84 -0
- package/vendor/subagent-core/types/catalog.js +109 -0
- package/vendor/subagent-core/types/child-agent.d.ts +156 -0
- package/vendor/subagent-core/types/child-agent.js +213 -0
- package/vendor/subagent-core/types/client.d.ts +8 -0
- package/vendor/subagent-core/types/client.js +7 -0
- package/vendor/subagent-core/types/continuation-activation.d.ts +267 -0
- package/vendor/subagent-core/types/continuation-activation.js +700 -0
- package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
- package/vendor/subagent-core/types/continuation-messages.js +106 -0
- package/vendor/subagent-core/types/continuation.d.ts +124 -0
- package/vendor/subagent-core/types/continuation.js +455 -0
- package/vendor/subagent-core/types/control-types.d.ts +135 -0
- package/vendor/subagent-core/types/control-types.js +8 -0
- package/vendor/subagent-core/types/control.d.ts +45 -0
- package/vendor/subagent-core/types/control.js +79 -0
- package/vendor/subagent-core/types/depth.d.ts +31 -0
- package/vendor/subagent-core/types/depth.js +39 -0
- package/vendor/subagent-core/types/descriptor.d.ts +144 -0
- package/vendor/subagent-core/types/descriptor.js +193 -0
- package/vendor/subagent-core/types/error.d.ts +11 -0
- package/vendor/subagent-core/types/error.js +14 -0
- package/vendor/subagent-core/types/inbox.d.ts +43 -0
- package/vendor/subagent-core/types/inbox.js +61 -0
- package/vendor/subagent-core/types/index.d.ts +320 -0
- package/vendor/subagent-core/types/index.js +475 -0
- package/vendor/subagent-core/types/internal.d.ts +58 -0
- package/vendor/subagent-core/types/internal.js +58 -0
- package/vendor/subagent-core/types/invariant.d.ts +13 -0
- package/vendor/subagent-core/types/invariant.js +91 -0
- package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
- package/vendor/subagent-core/types/lifecycle.js +180 -0
- package/vendor/subagent-core/types/list-children.d.ts +36 -0
- package/vendor/subagent-core/types/list-children.js +166 -0
- package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
- package/vendor/subagent-core/types/out-of-process.js +219 -0
- package/vendor/subagent-core/types/projection-types.d.ts +82 -0
- package/vendor/subagent-core/types/projection-types.js +7 -0
- package/vendor/subagent-core/types/projection.d.ts +100 -0
- package/vendor/subagent-core/types/projection.js +157 -0
- package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
- package/vendor/subagent-core/types/run-settlement.js +70 -0
- package/vendor/subagent-core/types/types.d.ts +377 -0
- package/vendor/subagent-core/types/types.js +19 -0
- package/vendor/subagent-driver/LICENSE +21 -0
- package/vendor/subagent-driver/index.js +255 -0
- package/vendor/subagent-driver/types/index.d.ts +33 -0
- package/vendor/subagent-driver/types/structured.d.ts +42 -0
- package/vendor/subagent-fork/LICENSE +21 -0
- package/vendor/subagent-fork/index.js +61 -0
- package/vendor/subagent-fork/types/index.d.ts +20 -0
- package/vendor/subagent-spawn/LICENSE +21 -0
- package/vendor/subagent-spawn/index.js +45 -0
- package/vendor/subagent-spawn/types/index.d.ts +19 -0
- package/vendor/terminal/LICENSE +21 -0
- package/vendor/terminal/index.js +1093 -0
- package/vendor/terminal/types/config.d.ts +82 -0
- package/vendor/terminal/types/index.d.ts +36 -0
- package/vendor/terminal/types/sanitize.d.ts +47 -0
- package/vendor/terminal/types/session.d.ts +87 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
5
|
+
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
6
|
+
import { launchOptions } from './config.mjs';
|
|
7
|
+
import { BrowserAccess, needsDeveloper } from './access.mjs';
|
|
8
|
+
import { checkFileArguments } from './files.mjs';
|
|
9
|
+
import { BROWSER_STDIO_MAX_BYTES } from './screenshots.mjs';
|
|
10
|
+
|
|
11
|
+
const entrypoint = () => fileURLToPath(new URL('./mcp-entry.mjs', import.meta.url));
|
|
12
|
+
const pagesOf = result => result.structuredContent?.pages;
|
|
13
|
+
|
|
14
|
+
/** One browser per agent session. All calls are ordered, including disposal. */
|
|
15
|
+
export class BrowserConnection {
|
|
16
|
+
constructor({ home, sessionId, config, connect = connectChrome, access = new BrowserAccess(home), relay, fileRoots }) {
|
|
17
|
+
Object.assign(this, launchOptions(home, sessionId, config));
|
|
18
|
+
this.connect = connect;
|
|
19
|
+
this.access = access;
|
|
20
|
+
this.relay = relay;
|
|
21
|
+
this.fileRoots = fileRoots;
|
|
22
|
+
this.siteTools = new Map();
|
|
23
|
+
this.owned = new Set();
|
|
24
|
+
this.kept = new Map();
|
|
25
|
+
this.pages = [];
|
|
26
|
+
this.tail = Promise.resolve();
|
|
27
|
+
this.closed = false;
|
|
28
|
+
this.failure = undefined;
|
|
29
|
+
this.handoff = null;
|
|
30
|
+
this.observedAt = null;
|
|
31
|
+
this.generation = 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async start(signal) {
|
|
35
|
+
if (this.closed) throw Error('Browser connection is closed. Use browser_start to reconnect.');
|
|
36
|
+
if (!this.starting) this.starting = (async () => {
|
|
37
|
+
if (this.config.mode === 'extension') {
|
|
38
|
+
if (!this.relay?.ready) throw Error('Use /browser pair, connect the DSCODE extension and share a tab, then start the browser.');
|
|
39
|
+
this.args.push('--ws-endpoint', this.relay.endpoint);
|
|
40
|
+
this.releaseRelayListener = this.relay.onClose(() => {
|
|
41
|
+
this.failure = 'Browser extension disconnected or sharing was revoked. Use /browser stop, then /browser pair to share again. Do not replay an uncertain action.';
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (this.profile) await mkdir(this.profile, { recursive: true, mode: 0o700 });
|
|
45
|
+
const connection = await this.connect(this.args, signal);
|
|
46
|
+
this.client = connection.client;
|
|
47
|
+
this.transport = connection.transport;
|
|
48
|
+
this.client.onclose = () => { if (!this.closed) this.failure = 'Browser connection ended. Call browser_stop then browser_start (or /browser stop and /browser start). Do not replay an uncertain action.'; };
|
|
49
|
+
this.tools = (await this.client.listTools({}, { signal, timeout: 30000 })).tools;
|
|
50
|
+
const result = await this.raw('list_pages', {}, signal);
|
|
51
|
+
if (result.isError) throw Error(`Browser startup failed: ${result.content.filter(block => block.type === 'text').map(block => block.text).join('\n').slice(0, 4000)}`);
|
|
52
|
+
this.observe(result);
|
|
53
|
+
return this;
|
|
54
|
+
})().catch(async error => { await this.close(); throw error; });
|
|
55
|
+
return this.starting;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
enqueue(action, signal) {
|
|
59
|
+
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
60
|
+
let abort;
|
|
61
|
+
const cancelled = signal && new Promise((_resolve, reject) => {
|
|
62
|
+
abort = () => reject(signal.reason);
|
|
63
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
64
|
+
});
|
|
65
|
+
const detach = () => { if (abort) signal.removeEventListener('abort', abort); };
|
|
66
|
+
const result = this.tail.then(() => {
|
|
67
|
+
// Cancel queued work promptly, but retain its place until earlier work
|
|
68
|
+
// settles. Once dispatched, the action and MCP own cancellation handling.
|
|
69
|
+
detach();
|
|
70
|
+
signal?.throwIfAborted();
|
|
71
|
+
return action();
|
|
72
|
+
});
|
|
73
|
+
this.tail = result.catch(() => {});
|
|
74
|
+
return cancelled ? Promise.race([result, cancelled]).finally(detach) : result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async raw(name, args, signal) {
|
|
78
|
+
if (this.closed || this.failure) throw Error(this.failure ?? 'Browser connection is closed.');
|
|
79
|
+
signal?.throwIfAborted();
|
|
80
|
+
const result = await this.client.callTool({ name, arguments: args }, undefined, { signal, timeout: 60000 });
|
|
81
|
+
// A successful refresh must replace cached observations. Other tools may
|
|
82
|
+
// omit pages, but list_pages cannot authorize actions with stale metadata.
|
|
83
|
+
if (name === 'list_pages' && !result.isError && !Array.isArray(pagesOf(result))) {
|
|
84
|
+
// A restart invalidates page IDs even when its new list is unavailable.
|
|
85
|
+
// Do not lose that generation signal by rejecting before observation.
|
|
86
|
+
if (result.structuredContent?.reconnected) this.observe(result);
|
|
87
|
+
throw Error('Browser page list is unavailable or malformed. Refresh browser status before continuing.');
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
observe(result) {
|
|
93
|
+
if (result.structuredContent?.reconnected) {
|
|
94
|
+
this.generation++;
|
|
95
|
+
// IDs are generation-local; never transfer ownership to a newly numbered tab.
|
|
96
|
+
this.owned.clear(); this.kept.clear();
|
|
97
|
+
this.siteTools.clear();
|
|
98
|
+
if (this.handoff) this.handoff.invalidated = true;
|
|
99
|
+
}
|
|
100
|
+
const pages = pagesOf(result);
|
|
101
|
+
if (Array.isArray(pages)) {
|
|
102
|
+
for (const [id, entry] of this.siteTools) {
|
|
103
|
+
if (!pages.some(p => p.id === id && p.url === entry.url && p.documentId && p.documentId === entry.documentId)) this.siteTools.delete(id);
|
|
104
|
+
}
|
|
105
|
+
this.pages = pages;
|
|
106
|
+
this.observedAt = new Date().toISOString();
|
|
107
|
+
const live = new Set(pages.map(p => p.id));
|
|
108
|
+
for (const id of this.owned) if (!live.has(id)) { this.owned.delete(id); this.kept.delete(id); }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
call(name, args, signal, reviewed) {
|
|
113
|
+
return this.enqueue(async () => {
|
|
114
|
+
if (this.closed || this.failure) throw Error(this.failure ?? 'Browser connection is closed.');
|
|
115
|
+
if (name !== 'list_pages') this.assertAgentControl();
|
|
116
|
+
if (!this.tools?.some(tool => tool.name === name)) throw Error(`Unknown browser tool: ${name}`);
|
|
117
|
+
if (name.includes('webmcp') && !this.config.webmcp) throw Error('WebMCP is disabled. The user can enable /browser webmcp on after stopping session browsers.');
|
|
118
|
+
// A refresh replaces the previous discovery even when it fails. Otherwise
|
|
119
|
+
// a later execution could silently reuse definitions the agent could not
|
|
120
|
+
// confirm in its most recent observation.
|
|
121
|
+
if (name === 'list_webmcp_tools') this.siteTools.delete(args.pageId);
|
|
122
|
+
if (args.pageId !== undefined) {
|
|
123
|
+
// Chrome may restart while the MCP transport stays alive. Detect that
|
|
124
|
+
// generation before forwarding an action that carries an old page ID.
|
|
125
|
+
const listing = await this.raw('list_pages', {}, signal);
|
|
126
|
+
this.observe(listing);
|
|
127
|
+
if (listing.isError) return listing;
|
|
128
|
+
if (listing.structuredContent?.reconnected) throw Error('Chrome restarted; previous page IDs are invalid. List pages and observe the intended page before acting. No action was replayed.');
|
|
129
|
+
if (!this.pages.some(page => page.id === args.pageId)) throw Error('That browser page is closed or unavailable. List pages and choose a live page.');
|
|
130
|
+
}
|
|
131
|
+
await this.access.check(name, args, this.pages);
|
|
132
|
+
const sourcePage = this.pages.find(p => p.id === args.pageId);
|
|
133
|
+
const sourceUrl = sourcePage?.url, sourceDocument = sourcePage?.documentId, sourceGeneration = this.generation;
|
|
134
|
+
if (reviewed && this.pages.find(p => p.id === args.pageId)?.url !== reviewed.url) throw Error('Browser page changed after the tool was proposed. Inspect it and submit a new action for review; nothing was executed.');
|
|
135
|
+
if (name === 'execute_webmcp_tool') {
|
|
136
|
+
const discovered = this.siteTools.get(args.pageId);
|
|
137
|
+
if (!discovered || discovered.url !== this.pages.find(p => p.id === args.pageId)?.url) throw Error('Discover WebMCP tools on this page before executing one.');
|
|
138
|
+
if (reviewed && reviewed.documentId !== discovered.documentId) throw Error('WebMCP document changed after review. Discover and review the action again.');
|
|
139
|
+
if (reviewed && reviewed.definition !== discovered.definitions.get(args.toolName)) throw Error('WebMCP definition changed after review. Discover and review the action again.');
|
|
140
|
+
try {
|
|
141
|
+
const listed = await this.raw('list_webmcp_tools', { pageId: args.pageId }, signal);
|
|
142
|
+
this.observe(listed);
|
|
143
|
+
// Native WebMCP discovery omits page metadata. Re-observe after its
|
|
144
|
+
// result, before dispatching the action, to bind it to this document.
|
|
145
|
+
const verified = await this.raw('list_pages', {}, signal);
|
|
146
|
+
this.observe(verified);
|
|
147
|
+
await this.access.check(name, args, this.pages);
|
|
148
|
+
const definition = listed.structuredContent?.webmcpTools?.find(t => t.name === args.toolName);
|
|
149
|
+
if (listed.isError || listed.structuredContent?.reconnected || verified.isError || verified.structuredContent?.reconnected || !Array.isArray(pagesOf(verified)) || !definition ||
|
|
150
|
+
this.siteTools.get(args.pageId) !== discovered ||
|
|
151
|
+
JSON.stringify(definition) !== discovered.definitions.get(args.toolName)) {
|
|
152
|
+
throw Error('WebMCP registration or page changed. Discover tools again and review the new definition; no action was executed.');
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
this.siteTools.delete(args.pageId);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
const input = args.input === undefined ? {} : JSON.parse(args.input);
|
|
159
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw Error('WebMCP input must be a JSON object.');
|
|
160
|
+
}
|
|
161
|
+
// Only the selected page returned by new_page is ours; a user's concurrent
|
|
162
|
+
// new tab must never be inferred to be agent-owned from a list difference.
|
|
163
|
+
// Files may change while this action waits in the queue or observes Chrome.
|
|
164
|
+
// Recheck at dispatch as well as at tool admission.
|
|
165
|
+
if (this.fileRoots) await checkFileArguments(args, this.fileRoots);
|
|
166
|
+
// Admission and WebMCP/file validation can yield while another session
|
|
167
|
+
// revokes access. Withholding output afterward cannot undo a dispatched
|
|
168
|
+
// action, so authorize again after those awaited preparations.
|
|
169
|
+
if (name !== 'list_pages') {
|
|
170
|
+
await this.access.check(name, args, this.pages);
|
|
171
|
+
this.assertAgentControl();
|
|
172
|
+
}
|
|
173
|
+
if (name === 'resize_page' && this.config.headless) {
|
|
174
|
+
// Background headless tabs retain old layout dimensions until activated.
|
|
175
|
+
// Use the supported tool rather than mutating upstream tool definitions.
|
|
176
|
+
const selected = await this.raw('select_page', { pageId: args.pageId, bringToFront: true }, signal);
|
|
177
|
+
this.observe(selected);
|
|
178
|
+
if (selected.isError) return selected;
|
|
179
|
+
if (this.generation !== sourceGeneration) throw Error('Chrome restarted before resizing. List pages and choose the intended page; resize was not sent.');
|
|
180
|
+
await this.access.check(name, args, this.pages);
|
|
181
|
+
this.assertAgentControl();
|
|
182
|
+
}
|
|
183
|
+
let result;
|
|
184
|
+
try { result = await this.raw(name, args, signal); }
|
|
185
|
+
catch (error) {
|
|
186
|
+
if (name !== 'execute_webmcp_tool') throw error;
|
|
187
|
+
this.siteTools.delete(args.pageId);
|
|
188
|
+
if (this.owned.has(args.pageId)) this.keep(args.pageId, 'uncertain-webmcp');
|
|
189
|
+
throw Error(`WebMCP execution response was not received. ${error.message} The action may already have completed; inspect the page and do not replay it. Discover tools again before requesting another action.`, { cause: error });
|
|
190
|
+
}
|
|
191
|
+
this.observe(result);
|
|
192
|
+
if (!result.isError && name === 'new_page') {
|
|
193
|
+
const page = pagesOf(result)?.find(p => p.selected);
|
|
194
|
+
if (page) this.owned.add(page.id);
|
|
195
|
+
}
|
|
196
|
+
// Re-observe after the call as a navigation can finish while the tool is
|
|
197
|
+
// producing its response. The response may still contain source-page
|
|
198
|
+
// data: authorize every observed origin, not just the final destination.
|
|
199
|
+
// This does not undo requests already made by the browser.
|
|
200
|
+
if (name !== 'list_pages') {
|
|
201
|
+
const target = name === 'new_page' ? pagesOf(result)?.find(p => p.selected)?.id : args.pageId;
|
|
202
|
+
try {
|
|
203
|
+
const listing = await this.raw('list_pages', {}, signal);
|
|
204
|
+
this.observe(listing);
|
|
205
|
+
if (listing.isError || listing.structuredContent?.reconnected) throw Error('Page list unavailable or Chrome restarted.');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (name === 'execute_webmcp_tool') this.siteTools.delete(args.pageId);
|
|
208
|
+
// new_page has no input pageId for the generic result hook to retain.
|
|
209
|
+
// Preserve confirmed ownership before cleanup can erase this evidence.
|
|
210
|
+
// observe() clears ownership on restart, so recycled IDs stay unowned.
|
|
211
|
+
if (this.owned.has(target)) this.keep(target, 'unverified-action');
|
|
212
|
+
throw Error(`Could not verify the page after the action. ${error.message} The action may already have completed; inspect browser status and do not replay it.`, { cause: error });
|
|
213
|
+
}
|
|
214
|
+
const page = this.pages.find(p => p.id === target);
|
|
215
|
+
try {
|
|
216
|
+
if (!page && name !== 'close_page') throw Error('Browser page unavailable after the action.');
|
|
217
|
+
const resultUrl = pagesOf(result)?.find(p => p.id === target)?.url;
|
|
218
|
+
const urls = new Set([sourceUrl, name === 'new_page' ? args.url : undefined, resultUrl, page?.url]);
|
|
219
|
+
for (const url of urls) if (url !== undefined) await this.access.checkUrl(url, needsDeveloper(name, args));
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
if (name === 'execute_webmcp_tool') this.siteTools.delete(args.pageId);
|
|
223
|
+
if (this.owned.has(target)) this.keep(target, 'site-permission');
|
|
224
|
+
throw Error(`Page output withheld after navigation, closure or permission change. ${error.message} The action may already have completed; do not replay it.`, { cause: error });
|
|
225
|
+
}
|
|
226
|
+
if (name === 'list_webmcp_tools' && !result.isError && page) {
|
|
227
|
+
const returnedPage = pagesOf(result)?.find(p => p.id === target);
|
|
228
|
+
if (!sourceDocument || page.url !== sourceUrl || page.documentId !== sourceDocument ||
|
|
229
|
+
(returnedPage && (returnedPage.url !== sourceUrl || returnedPage.documentId !== sourceDocument)) || this.generation !== sourceGeneration) {
|
|
230
|
+
throw Error('Page changed or its document could not be verified during WebMCP discovery. Inspect the intended page and discover its tools again.');
|
|
231
|
+
}
|
|
232
|
+
const definitions = result.structuredContent?.webmcpTools;
|
|
233
|
+
if (!Array.isArray(definitions)) throw Error('WebMCP discovery is unavailable. Use Chrome 150+ with WebMCP enabled, or ordinary DOM tools.');
|
|
234
|
+
this.siteTools.set(target, { url: page.url, documentId: sourceDocument, definitions: new Map(definitions.map(t => [t.name, JSON.stringify(t)])) });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}, signal);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
keep(pageId, reason = 'deliverable') {
|
|
242
|
+
if (!this.owned.has(pageId)) throw Error('Only a tab created by this session can be marked for retention.');
|
|
243
|
+
if (!this.kept.has(pageId)) this.kept.set(pageId, reason);
|
|
244
|
+
return this.status();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
waitForPage({ urlContains, excludePageIds = [], timeoutMs = 10000 }, signal) {
|
|
248
|
+
if (typeof urlContains !== 'string' || !urlContains || urlContains.length > 2048) return Promise.reject(Error('Specify a URL substring of 1–2048 characters to identify the expected tab.'));
|
|
249
|
+
if (!Array.isArray(excludePageIds) || !excludePageIds.every(Number.isSafeInteger)) return Promise.reject(Error('excludePageIds must contain live integer page IDs from before the action.'));
|
|
250
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30000) return Promise.reject(Error('Tab wait timeout must be 1–30000 milliseconds.'));
|
|
251
|
+
return this.enqueue(async () => {
|
|
252
|
+
this.assertAgentControl();
|
|
253
|
+
const deadline = new AbortController();
|
|
254
|
+
const timer = setTimeout(() => deadline.abort(), timeoutMs);
|
|
255
|
+
const activeSignal = AbortSignal.any([deadline.signal, ...(signal ? [signal] : [])]);
|
|
256
|
+
try {
|
|
257
|
+
while (true) {
|
|
258
|
+
const listing = await this.raw('list_pages', {}, activeSignal);
|
|
259
|
+
this.observe(listing);
|
|
260
|
+
if (listing.isError) throw Error('Cannot refresh pages while waiting for a tab.');
|
|
261
|
+
if (listing.structuredContent?.reconnected) throw Error('Chrome restarted while waiting; list pages again before choosing a target.');
|
|
262
|
+
const matches = this.pages.filter(p => p.url.includes(urlContains) && !excludePageIds.includes(p.id));
|
|
263
|
+
if (matches.length > 1) throw Error('Several tabs match. List pages and choose the intended page ID.');
|
|
264
|
+
if (matches.length === 1) return { page: matches[0], ...this.status() };
|
|
265
|
+
await delay(200, undefined, { signal: activeSignal });
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (signal?.aborted) signal.throwIfAborted();
|
|
269
|
+
if (deadline.signal.aborted) throw Error('Timed out waiting for the expected tab. Inspect the page list and original action before retrying; do not repeat an uncertain click.', { cause: error });
|
|
270
|
+
throw error;
|
|
271
|
+
} finally { clearTimeout(timer); }
|
|
272
|
+
}, signal);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
assertAgentControl({ allowDisconnected = false } = {}) {
|
|
276
|
+
if (!allowDisconnected && (this.closed || this.failure)) throw Error(this.failure ?? 'Browser connection is closed.');
|
|
277
|
+
if (this.handoff) throw Error('Browser is handed to the user. Wait for the user to run /browser resume; do not operate or stop it through another tool.');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
takeHandoff(pageId, reason, signal) {
|
|
281
|
+
return this.enqueue(async () => {
|
|
282
|
+
if (!Number.isSafeInteger(pageId)) throw Error('Handoff requires a live integer page ID.');
|
|
283
|
+
if (typeof reason !== 'string' || !reason.trim() || reason.length > 500) throw Error('Describe the human step in 1–500 characters.');
|
|
284
|
+
if (this.handoff) return this.status();
|
|
285
|
+
const listing = await this.raw('list_pages', {}, signal);
|
|
286
|
+
this.observe(listing);
|
|
287
|
+
if (listing.isError) throw Error('Cannot verify the handoff page. List pages before trying again.');
|
|
288
|
+
if (listing.structuredContent?.reconnected) throw Error('Chrome restarted; list pages and choose the handoff page again.');
|
|
289
|
+
const page = this.pages.find(p => p.id === pageId);
|
|
290
|
+
if (!page) throw Error('The handoff page is closed or unavailable.');
|
|
291
|
+
// Pause before focusing: a failed focus must not allow further actions
|
|
292
|
+
// while the user may already be interacting with the page.
|
|
293
|
+
this.handoff = { pageId, url: page.url, reason: reason.trim(), invalidated: false, focusError: null };
|
|
294
|
+
if (this.owned.has(pageId)) this.kept.set(pageId, 'handoff');
|
|
295
|
+
try {
|
|
296
|
+
const focused = await this.raw('select_page', { pageId, bringToFront: true }, signal);
|
|
297
|
+
this.observe(focused);
|
|
298
|
+
if (focused.isError) this.handoff.focusError = 'Could not focus the page. Select it manually in Chrome.';
|
|
299
|
+
} catch { this.handoff.focusError = 'Could not focus the page. Select it manually in Chrome.'; }
|
|
300
|
+
return this.status();
|
|
301
|
+
}, signal);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Only the user command exposes this transition; it is not an agent tool.
|
|
305
|
+
resume(signal) {
|
|
306
|
+
return this.enqueue(async () => {
|
|
307
|
+
if (!this.handoff) throw Error('There is no browser handoff to resume.');
|
|
308
|
+
const listing = await this.raw('list_pages', {}, signal);
|
|
309
|
+
this.observe(listing);
|
|
310
|
+
if (listing.isError) throw Error('Cannot refresh browser pages; handoff remains paused.');
|
|
311
|
+
const pageId = this.handoff.pageId;
|
|
312
|
+
const resumedPageId = !this.handoff.invalidated && this.pages.some(p => p.id === pageId) ? pageId : null;
|
|
313
|
+
this.handoff = null;
|
|
314
|
+
if (resumedPageId !== null && this.kept.get(pageId) === 'handoff') this.kept.set(pageId, 'resumed-handoff');
|
|
315
|
+
return { ...this.status(), resumedPageId, message: resumedPageId === null
|
|
316
|
+
? 'Browser control resumed. The original page is unavailable; choose a live page and take a new snapshot before acting.'
|
|
317
|
+
: `Browser control resumed on page ${pageId}. Take a new snapshot before acting; the user may have changed the page.` };
|
|
318
|
+
}, signal);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
cleanup(signal) {
|
|
322
|
+
return this.enqueue(async () => {
|
|
323
|
+
if (this.handoff) return { closed: [], skipped: 'user-handoff', ...this.status() };
|
|
324
|
+
const listing = await this.raw('list_pages', {}, signal);
|
|
325
|
+
if (listing.isError) throw Error('Cannot verify live tabs; cleanup stopped.');
|
|
326
|
+
this.observe(listing);
|
|
327
|
+
const closed = [];
|
|
328
|
+
for (const id of [...this.owned]) {
|
|
329
|
+
if (this.kept.has(id)) continue;
|
|
330
|
+
// Recheck between closes: Chrome can restart in the middle of a cleanup
|
|
331
|
+
// batch and reuse IDs from the array captured by this loop.
|
|
332
|
+
const live = await this.raw('list_pages', {}, signal);
|
|
333
|
+
this.observe(live);
|
|
334
|
+
if (live.isError) throw Error('Cannot verify live tabs; cleanup stopped.');
|
|
335
|
+
if (live.structuredContent?.reconnected) break;
|
|
336
|
+
if (!this.owned.has(id) || this.kept.has(id)) continue;
|
|
337
|
+
try { await this.access.checkUrl(this.pages.find(p => p.id === id)?.url); }
|
|
338
|
+
catch { this.kept.set(id, 'site-permission'); continue; }
|
|
339
|
+
// User commands and completed previews can retain a tab while either
|
|
340
|
+
// observation awaits I/O. Honor them before dispatching its close.
|
|
341
|
+
if (!this.owned.has(id) || this.kept.has(id)) continue;
|
|
342
|
+
// A timeout may have closed the tab. Preserve any remaining page rather
|
|
343
|
+
// than silently retrying that close on the next turn's cleanup.
|
|
344
|
+
this.kept.set(id, 'uncertain-close');
|
|
345
|
+
const result = await this.raw('close_page', { pageId: id }, signal);
|
|
346
|
+
this.observe(result);
|
|
347
|
+
if (result.structuredContent?.reconnected) break;
|
|
348
|
+
if (result.isError) throw Error('Tab cleanup failed; inspect browser status before retrying.');
|
|
349
|
+
if (!this.pages.some(p => p.id === id)) closed.push(id);
|
|
350
|
+
else this.kept.set(id, 'close-not-confirmed');
|
|
351
|
+
}
|
|
352
|
+
return { closed, ...this.status() };
|
|
353
|
+
}, signal);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
status() {
|
|
357
|
+
return { mode: this.config.mode, profile: this.profile ?? null, connected: !!this.client && !this.closed && !this.failure, error: this.failure ?? null,
|
|
358
|
+
headless: this.config.headless, webmcp: !!this.config.webmcp, observedAt: this.observedAt, handoff: this.handoff ? { ...this.handoff } : null,
|
|
359
|
+
pages: this.pages.map(page => ({ ...page, owned: this.owned.has(page.id), kept: this.kept.has(page.id), retention: this.kept.get(page.id) ?? null })) };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async close() {
|
|
363
|
+
this.closed = true;
|
|
364
|
+
this.releaseRelayListener?.();
|
|
365
|
+
this.relay?.close();
|
|
366
|
+
// Closing the transport also interrupts an in-flight request. No replay.
|
|
367
|
+
await this.client?.close().catch(() => {});
|
|
368
|
+
await this.transport?.close().catch(() => {});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function browserProcessEnvironment(versions = process.versions) {
|
|
373
|
+
// Desktop Hosts run under Electron's Node mode. The SDK's minimal environment
|
|
374
|
+
// omits this flag; without it the child opens the desktop app instead of MCP.
|
|
375
|
+
return { ...getDefaultEnvironment(), ...(versions.electron ? { ELECTRON_RUN_AS_NODE: '1' } : {}) };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async function connectChrome(args, signal) {
|
|
379
|
+
const transport = new StdioClientTransport({ command: process.execPath, args: [entrypoint(), ...args], env: browserProcessEnvironment(), stderr: 'pipe', maxBufferSize: BROWSER_STDIO_MAX_BYTES });
|
|
380
|
+
const client = new Client({ name: 'dscode-browser', version: '1.0.0' });
|
|
381
|
+
// Drain diagnostics without sending local browser state or paths into logs.
|
|
382
|
+
transport.stderr?.on('data', () => {});
|
|
383
|
+
try { await client.connect(transport, { signal, timeout: 30000 }); return { client, transport }; }
|
|
384
|
+
catch (error) { await transport.close(); throw error; }
|
|
385
|
+
}
|