@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,840 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import CoreGraphics
|
|
3
|
+
import Darwin
|
|
4
|
+
import Foundation
|
|
5
|
+
import QuartzCore
|
|
6
|
+
|
|
7
|
+
private struct CursorOverlayCommand {
|
|
8
|
+
let operation: String
|
|
9
|
+
let point: CGPoint?
|
|
10
|
+
let durationMs: Int?
|
|
11
|
+
let speedPxPerSecond: Double?
|
|
12
|
+
let accelerationPxPerSecondSquared: Double?
|
|
13
|
+
let autoHideMs: Int
|
|
14
|
+
let targetPid: pid_t?
|
|
15
|
+
let targetWindowNumber: Int64?
|
|
16
|
+
let targetWindowFrame: CGRect?
|
|
17
|
+
let sustainedPress: Bool
|
|
18
|
+
|
|
19
|
+
private static let maximumCoordinateMagnitude = 100_000.0
|
|
20
|
+
private static let maximumDurationMs = 2_000
|
|
21
|
+
private static let maximumSpeedPxPerSecond = 50_000.0
|
|
22
|
+
private static let maximumAccelerationPxPerSecondSquared = 500_000.0
|
|
23
|
+
private static let maximumAutoHideMs = 30_000
|
|
24
|
+
|
|
25
|
+
private static func number(_ object: Any?, field: String) throws -> NSNumber? {
|
|
26
|
+
guard let object else { return nil }
|
|
27
|
+
guard let value = object as? NSNumber,
|
|
28
|
+
CFGetTypeID(value) != CFBooleanGetTypeID() else {
|
|
29
|
+
throw CursorOverlayError(message: "cursor overlay \(field) must be numeric")
|
|
30
|
+
}
|
|
31
|
+
return value
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private static func integer(_ object: Any?, field: String, fallback: Int) throws -> Int {
|
|
35
|
+
guard let value = try number(object, field: field) else { return fallback }
|
|
36
|
+
let doubleValue = value.doubleValue
|
|
37
|
+
guard doubleValue.isFinite, doubleValue.rounded(.towardZero) == doubleValue,
|
|
38
|
+
doubleValue >= Double(Int.min), doubleValue <= Double(Int.max) else {
|
|
39
|
+
throw CursorOverlayError(message: "cursor overlay \(field) must be an integer")
|
|
40
|
+
}
|
|
41
|
+
return Int(doubleValue)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
init(_ object: [String: Any]) throws {
|
|
45
|
+
guard let operation = object["op"] as? String else {
|
|
46
|
+
throw CursorOverlayError(message: "cursor overlay command is missing op")
|
|
47
|
+
}
|
|
48
|
+
self.operation = operation
|
|
49
|
+
let x = try Self.number(object["x"], field: "x")
|
|
50
|
+
let y = try Self.number(object["y"], field: "y")
|
|
51
|
+
if let x, let y {
|
|
52
|
+
let xValue = x.doubleValue
|
|
53
|
+
let yValue = y.doubleValue
|
|
54
|
+
guard xValue.isFinite, yValue.isFinite,
|
|
55
|
+
abs(xValue) <= Self.maximumCoordinateMagnitude,
|
|
56
|
+
abs(yValue) <= Self.maximumCoordinateMagnitude else {
|
|
57
|
+
throw CursorOverlayError(message: "cursor overlay coordinates are outside the supported range")
|
|
58
|
+
}
|
|
59
|
+
self.point = CGPoint(x: xValue, y: yValue)
|
|
60
|
+
} else {
|
|
61
|
+
self.point = nil
|
|
62
|
+
}
|
|
63
|
+
let durationMs = object["durationMs"] == nil
|
|
64
|
+
? nil
|
|
65
|
+
: try Self.integer(object["durationMs"], field: "durationMs", fallback: 180)
|
|
66
|
+
let autoHideMs = try Self.integer(object["autoHideMs"], field: "autoHideMs", fallback: 0)
|
|
67
|
+
guard durationMs == nil || (durationMs! >= 0 && durationMs! <= Self.maximumDurationMs),
|
|
68
|
+
autoHideMs >= 0, autoHideMs <= Self.maximumAutoHideMs else {
|
|
69
|
+
throw CursorOverlayError(message: "cursor overlay timing is outside the supported range")
|
|
70
|
+
}
|
|
71
|
+
self.durationMs = durationMs
|
|
72
|
+
self.autoHideMs = autoHideMs
|
|
73
|
+
let speed = try Self.number(object["speedPxPerSecond"], field: "speedPxPerSecond")?.doubleValue
|
|
74
|
+
let acceleration = try Self.number(
|
|
75
|
+
object["accelerationPxPerSecondSquared"],
|
|
76
|
+
field: "accelerationPxPerSecondSquared"
|
|
77
|
+
)?.doubleValue
|
|
78
|
+
guard (speed == nil) == (acceleration == nil) else {
|
|
79
|
+
throw CursorOverlayError(message: "cursor overlay speed and acceleration must be provided together")
|
|
80
|
+
}
|
|
81
|
+
guard speed == nil || (speed!.isFinite && speed! >= 100 && speed! <= Self.maximumSpeedPxPerSecond),
|
|
82
|
+
acceleration == nil || (acceleration!.isFinite && acceleration! >= 100
|
|
83
|
+
&& acceleration! <= Self.maximumAccelerationPxPerSecondSquared) else {
|
|
84
|
+
throw CursorOverlayError(message: "cursor overlay physical motion is outside the supported range")
|
|
85
|
+
}
|
|
86
|
+
self.speedPxPerSecond = speed
|
|
87
|
+
self.accelerationPxPerSecondSquared = acceleration
|
|
88
|
+
|
|
89
|
+
if let rawPid = try Self.number(object["targetPid"], field: "targetPid") {
|
|
90
|
+
let pidValue = rawPid.doubleValue
|
|
91
|
+
guard pidValue.isFinite, pidValue.rounded(.towardZero) == pidValue,
|
|
92
|
+
pidValue > 0, pidValue <= Double(Int32.max) else {
|
|
93
|
+
throw CursorOverlayError(message: "cursor overlay targetPid is invalid")
|
|
94
|
+
}
|
|
95
|
+
self.targetPid = pid_t(pidValue)
|
|
96
|
+
} else {
|
|
97
|
+
self.targetPid = nil
|
|
98
|
+
}
|
|
99
|
+
if let rawWindowNumber = try Self.number(object["targetWindowNumber"], field: "targetWindowNumber") {
|
|
100
|
+
let windowValue = rawWindowNumber.doubleValue
|
|
101
|
+
guard windowValue.isFinite, windowValue.rounded(.towardZero) == windowValue,
|
|
102
|
+
windowValue > 0, windowValue <= Double(Int64.max) else {
|
|
103
|
+
throw CursorOverlayError(message: "cursor overlay targetWindowNumber is invalid")
|
|
104
|
+
}
|
|
105
|
+
self.targetWindowNumber = Int64(windowValue)
|
|
106
|
+
} else {
|
|
107
|
+
self.targetWindowNumber = nil
|
|
108
|
+
}
|
|
109
|
+
if let frame = object["targetWindowFrame"] as? [String: Any] {
|
|
110
|
+
guard let x = try Self.number(frame["x"], field: "targetWindowFrame.x"),
|
|
111
|
+
let y = try Self.number(frame["y"], field: "targetWindowFrame.y"),
|
|
112
|
+
let width = try Self.number(frame["width"], field: "targetWindowFrame.width"),
|
|
113
|
+
let height = try Self.number(frame["height"], field: "targetWindowFrame.height") else {
|
|
114
|
+
throw CursorOverlayError(message: "cursor overlay targetWindowFrame is incomplete")
|
|
115
|
+
}
|
|
116
|
+
let values = [x.doubleValue, y.doubleValue, width.doubleValue, height.doubleValue]
|
|
117
|
+
guard values.allSatisfy(\.isFinite), width.doubleValue > 0, height.doubleValue > 0,
|
|
118
|
+
values.allSatisfy({ abs($0) <= Self.maximumCoordinateMagnitude }) else {
|
|
119
|
+
throw CursorOverlayError(message: "cursor overlay targetWindowFrame is invalid")
|
|
120
|
+
}
|
|
121
|
+
self.targetWindowFrame = CGRect(
|
|
122
|
+
x: x.doubleValue,
|
|
123
|
+
y: y.doubleValue,
|
|
124
|
+
width: width.doubleValue,
|
|
125
|
+
height: height.doubleValue
|
|
126
|
+
)
|
|
127
|
+
} else {
|
|
128
|
+
self.targetWindowFrame = nil
|
|
129
|
+
}
|
|
130
|
+
if let sustainedPress = object["sustainedPress"] {
|
|
131
|
+
guard let value = sustainedPress as? Bool else {
|
|
132
|
+
throw CursorOverlayError(message: "cursor overlay sustainedPress must be boolean")
|
|
133
|
+
}
|
|
134
|
+
self.sustainedPress = value
|
|
135
|
+
} else {
|
|
136
|
+
self.sustainedPress = false
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private struct CursorOverlayError: Error {
|
|
142
|
+
let message: String
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private final class CursorPanel: NSPanel {
|
|
146
|
+
override var canBecomeKey: Bool { false }
|
|
147
|
+
override var canBecomeMain: Bool { false }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private final class CursorView: NSView {
|
|
151
|
+
override var isFlipped: Bool { true }
|
|
152
|
+
|
|
153
|
+
private let image: NSImage? = EmbeddedCursorImage.image
|
|
154
|
+
|
|
155
|
+
var pressed = false {
|
|
156
|
+
didSet { needsDisplay = true }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
override var isOpaque: Bool { false }
|
|
160
|
+
|
|
161
|
+
override func draw(_ dirtyRect: NSRect) {
|
|
162
|
+
super.draw(dirtyRect)
|
|
163
|
+
guard let image else { return }
|
|
164
|
+
let imageRect = pressed ? bounds.insetBy(dx: 2, dy: 2) : bounds
|
|
165
|
+
image.draw(
|
|
166
|
+
in: imageRect,
|
|
167
|
+
from: .zero,
|
|
168
|
+
operation: .sourceOver,
|
|
169
|
+
fraction: pressed ? 0.85 : 1,
|
|
170
|
+
respectFlipped: true,
|
|
171
|
+
hints: nil
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@MainActor
|
|
177
|
+
private final class CursorOverlayController: NSObject {
|
|
178
|
+
private static let size = NSSize(width: 28, height: 28)
|
|
179
|
+
|
|
180
|
+
private let window: CursorPanel
|
|
181
|
+
private let cursorView: CursorView
|
|
182
|
+
private var hideWork: DispatchWorkItem?
|
|
183
|
+
private var releaseWork: DispatchWorkItem?
|
|
184
|
+
private var targetCheckTimer: Timer?
|
|
185
|
+
private var glideTimer: Timer?
|
|
186
|
+
private var glideCompletion: ((Bool) -> Void)?
|
|
187
|
+
private var hasPosition = false
|
|
188
|
+
private var currentQuartzPoint: CGPoint?
|
|
189
|
+
private var targetPid: pid_t?
|
|
190
|
+
private var targetWindowNumber: Int64?
|
|
191
|
+
private var targetWindowFrame: CGRect?
|
|
192
|
+
|
|
193
|
+
override init() {
|
|
194
|
+
cursorView = CursorView(frame: NSRect(origin: .zero, size: Self.size))
|
|
195
|
+
window = CursorPanel(
|
|
196
|
+
contentRect: NSRect(origin: .zero, size: Self.size),
|
|
197
|
+
styleMask: [.borderless, .nonactivatingPanel],
|
|
198
|
+
backing: .buffered,
|
|
199
|
+
defer: false
|
|
200
|
+
)
|
|
201
|
+
super.init()
|
|
202
|
+
window.title = "DSH Computer Use Cursor"
|
|
203
|
+
window.contentView = cursorView
|
|
204
|
+
window.isOpaque = false
|
|
205
|
+
window.backgroundColor = .clear
|
|
206
|
+
window.hasShadow = false
|
|
207
|
+
// Target loss must remove the indicator immediately, without AppKit's
|
|
208
|
+
// panel fade leaving it visible over an unrelated foreground app.
|
|
209
|
+
window.animationBehavior = .none
|
|
210
|
+
window.ignoresMouseEvents = true
|
|
211
|
+
window.hidesOnDeactivate = false
|
|
212
|
+
window.becomesKeyOnlyIfNeeded = true
|
|
213
|
+
window.level = .floating
|
|
214
|
+
window.collectionBehavior = [.fullScreenAuxiliary, .stationary, .ignoresCycle]
|
|
215
|
+
window.isReleasedWhenClosed = false
|
|
216
|
+
window.sharingType = .readOnly
|
|
217
|
+
NSWorkspace.shared.notificationCenter.addObserver(
|
|
218
|
+
self,
|
|
219
|
+
selector: #selector(frontmostApplicationChanged(_:)),
|
|
220
|
+
name: NSWorkspace.didActivateApplicationNotification,
|
|
221
|
+
object: nil
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
deinit {
|
|
226
|
+
NSWorkspace.shared.notificationCenter.removeObserver(self)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// Outcome of a placement request, so the caller can distinguish a cursor
|
|
230
|
+
/// that moved from one that was hidden because its window binding no
|
|
231
|
+
/// longer holds. Reporting success for the second is how a live session
|
|
232
|
+
/// ends up with a frozen agent cursor and no error anywhere.
|
|
233
|
+
enum Placement {
|
|
234
|
+
case shown
|
|
235
|
+
case targetNotFrontmost
|
|
236
|
+
case targetUnavailable
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
func show(
|
|
240
|
+
at quartzPoint: CGPoint,
|
|
241
|
+
durationMs: Int?,
|
|
242
|
+
speedPxPerSecond: Double?,
|
|
243
|
+
accelerationPxPerSecondSquared: Double?,
|
|
244
|
+
autoHideMs: Int,
|
|
245
|
+
targetPid: pid_t?,
|
|
246
|
+
targetWindowNumber: Int64?,
|
|
247
|
+
targetWindowFrame: CGRect?,
|
|
248
|
+
completion: @escaping (Placement) -> Void
|
|
249
|
+
) {
|
|
250
|
+
hideWork?.cancel()
|
|
251
|
+
let placement = targetPlacement(pid: targetPid, windowNumber: targetWindowNumber, expectedFrame: targetWindowFrame)
|
|
252
|
+
guard placement == .shown else {
|
|
253
|
+
hide()
|
|
254
|
+
completion(placement)
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
self.targetPid = targetPid
|
|
258
|
+
self.targetWindowNumber = targetWindowNumber
|
|
259
|
+
self.targetWindowFrame = targetWindowFrame
|
|
260
|
+
scheduleTargetChecks()
|
|
261
|
+
if !hasPosition {
|
|
262
|
+
let mouse = NSEvent.mouseLocation
|
|
263
|
+
window.setFrameOrigin(NSPoint(x: mouse.x, y: mouse.y - Self.size.height))
|
|
264
|
+
currentQuartzPoint = self.quartzPoint(fromAppKit: mouse)
|
|
265
|
+
hasPosition = true
|
|
266
|
+
}
|
|
267
|
+
window.orderFrontRegardless()
|
|
268
|
+
let shownPlacement = targetPlacement(pid: targetPid, windowNumber: targetWindowNumber, expectedFrame: targetWindowFrame)
|
|
269
|
+
guard shownPlacement == .shown else {
|
|
270
|
+
hide()
|
|
271
|
+
completion(shownPlacement)
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
glide(
|
|
275
|
+
from: currentQuartzPoint ?? quartzPoint,
|
|
276
|
+
to: quartzPoint,
|
|
277
|
+
durationMs: durationMs,
|
|
278
|
+
speedPxPerSecond: speedPxPerSecond,
|
|
279
|
+
accelerationPxPerSecondSquared: accelerationPxPerSecondSquared
|
|
280
|
+
) { [weak self] reached in
|
|
281
|
+
guard let self else {
|
|
282
|
+
completion(.targetUnavailable)
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
let finalPlacement = self.targetPlacement(
|
|
286
|
+
pid: targetPid,
|
|
287
|
+
windowNumber: targetWindowNumber,
|
|
288
|
+
expectedFrame: targetWindowFrame
|
|
289
|
+
)
|
|
290
|
+
guard reached, finalPlacement == .shown else {
|
|
291
|
+
self.hide()
|
|
292
|
+
completion(finalPlacement == .shown ? .targetUnavailable : finalPlacement)
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
self.scheduleHide(after: autoHideMs)
|
|
296
|
+
completion(.shown)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// Travel to `targetOrigin`, interpolated on the main run loop.
|
|
301
|
+
///
|
|
302
|
+
/// `window.animator().setFrameOrigin` does not move this panel: the overlay
|
|
303
|
+
/// runs with `.prohibited` activation policy, and the implicit animator
|
|
304
|
+
/// silently does nothing there. Because the animated branch only ran once
|
|
305
|
+
/// the panel was already visible, the first placement worked and every
|
|
306
|
+
/// later move was a no-op — a cursor that appears once and then never
|
|
307
|
+
/// follows the agent again.
|
|
308
|
+
///
|
|
309
|
+
/// Stepping the origin directly is the same call the first placement
|
|
310
|
+
/// already proved works, so the glide is both visible and correct.
|
|
311
|
+
private func glide(
|
|
312
|
+
from origin: CGPoint,
|
|
313
|
+
to target: CGPoint,
|
|
314
|
+
durationMs: Int?,
|
|
315
|
+
speedPxPerSecond: Double?,
|
|
316
|
+
accelerationPxPerSecondSquared: Double?,
|
|
317
|
+
completion: @escaping (Bool) -> Void
|
|
318
|
+
) {
|
|
319
|
+
finishGlide(reached: false)
|
|
320
|
+
guard origin != target else {
|
|
321
|
+
currentQuartzPoint = target
|
|
322
|
+
window.setFrameOrigin(panelOrigin(fromQuartz: target))
|
|
323
|
+
window.orderFrontRegardless()
|
|
324
|
+
completion(true)
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
let distance = hypot(target.x - origin.x, target.y - origin.y)
|
|
328
|
+
let duration = motionDuration(
|
|
329
|
+
distance: distance,
|
|
330
|
+
explicitDurationMs: durationMs,
|
|
331
|
+
speedPxPerSecond: speedPxPerSecond,
|
|
332
|
+
accelerationPxPerSecondSquared: accelerationPxPerSecondSquared
|
|
333
|
+
)
|
|
334
|
+
guard duration > 0 else {
|
|
335
|
+
currentQuartzPoint = target
|
|
336
|
+
window.setFrameOrigin(panelOrigin(fromQuartz: target))
|
|
337
|
+
window.orderFrontRegardless()
|
|
338
|
+
completion(true)
|
|
339
|
+
return
|
|
340
|
+
}
|
|
341
|
+
let started = CACurrentMediaTime()
|
|
342
|
+
glideCompletion = completion
|
|
343
|
+
let timer = Timer(timeInterval: 1.0 / 120.0, repeats: true) { [weak self] timer in
|
|
344
|
+
MainActor.assumeIsolated {
|
|
345
|
+
guard let self else { timer.invalidate(); return }
|
|
346
|
+
if let targetPid = self.targetPid,
|
|
347
|
+
NSWorkspace.shared.frontmostApplication?.processIdentifier != targetPid {
|
|
348
|
+
self.hide()
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
let elapsed = CACurrentMediaTime() - started
|
|
352
|
+
if elapsed >= duration {
|
|
353
|
+
self.currentQuartzPoint = target
|
|
354
|
+
self.window.setFrameOrigin(self.panelOrigin(fromQuartz: target))
|
|
355
|
+
self.hasPosition = true
|
|
356
|
+
self.finishGlide(reached: true)
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
let linear = elapsed / duration
|
|
360
|
+
let fraction: Double
|
|
361
|
+
if let speedPxPerSecond, let accelerationPxPerSecondSquared {
|
|
362
|
+
fraction = cursorMotionFraction(
|
|
363
|
+
progress: linear,
|
|
364
|
+
distance: distance,
|
|
365
|
+
speed: speedPxPerSecond,
|
|
366
|
+
acceleration: accelerationPxPerSecondSquared
|
|
367
|
+
)
|
|
368
|
+
} else {
|
|
369
|
+
fraction = linear * linear * (3 - 2 * linear)
|
|
370
|
+
}
|
|
371
|
+
let point = cursorMotionPoint(from: origin, to: target, fraction: fraction)
|
|
372
|
+
self.currentQuartzPoint = point
|
|
373
|
+
self.window.setFrameOrigin(self.panelOrigin(fromQuartz: point))
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
glideTimer = timer
|
|
377
|
+
RunLoop.main.add(timer, forMode: .common)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private func motionDuration(
|
|
381
|
+
distance: Double,
|
|
382
|
+
explicitDurationMs: Int?,
|
|
383
|
+
speedPxPerSecond: Double?,
|
|
384
|
+
accelerationPxPerSecondSquared: Double?
|
|
385
|
+
) -> Double {
|
|
386
|
+
if let explicitDurationMs { return Double(explicitDurationMs) / 1000 }
|
|
387
|
+
guard let speedPxPerSecond, let accelerationPxPerSecondSquared else { return 0.18 }
|
|
388
|
+
return cursorMotionDuration(
|
|
389
|
+
distance: distance,
|
|
390
|
+
speed: speedPxPerSecond,
|
|
391
|
+
acceleration: accelerationPxPerSecondSquared
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private func finishGlide(reached: Bool) {
|
|
396
|
+
glideTimer?.invalidate()
|
|
397
|
+
glideTimer = nil
|
|
398
|
+
let completion = glideCompletion
|
|
399
|
+
glideCompletion = nil
|
|
400
|
+
completion?(reached)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
func press(autoHideMs: Int, sustained: Bool) {
|
|
404
|
+
guard window.isVisible else { return }
|
|
405
|
+
releaseWork?.cancel()
|
|
406
|
+
releaseWork = nil
|
|
407
|
+
cursorView.pressed = true
|
|
408
|
+
if !sustained {
|
|
409
|
+
let work = DispatchWorkItem { [weak self] in
|
|
410
|
+
self?.cursorView.pressed = false
|
|
411
|
+
}
|
|
412
|
+
releaseWork = work
|
|
413
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150), execute: work)
|
|
414
|
+
}
|
|
415
|
+
scheduleHide(after: autoHideMs)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
func release(autoHideMs: Int) {
|
|
419
|
+
releaseWork?.cancel()
|
|
420
|
+
releaseWork = nil
|
|
421
|
+
cursorView.pressed = false
|
|
422
|
+
scheduleHide(after: autoHideMs)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
var isVisible: Bool { window.isVisible }
|
|
426
|
+
|
|
427
|
+
@discardableResult
|
|
428
|
+
func validateTarget(pid: pid_t?, windowNumber: Int64?, expectedFrame: CGRect?) -> Placement {
|
|
429
|
+
guard window.isVisible else { return .targetUnavailable }
|
|
430
|
+
let placement = targetPlacement(pid: pid, windowNumber: windowNumber, expectedFrame: expectedFrame)
|
|
431
|
+
if placement != .shown {
|
|
432
|
+
hide()
|
|
433
|
+
return placement
|
|
434
|
+
}
|
|
435
|
+
return .shown
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
func hide() {
|
|
439
|
+
finishGlide(reached: false)
|
|
440
|
+
hideWork?.cancel()
|
|
441
|
+
hideWork = nil
|
|
442
|
+
releaseWork?.cancel()
|
|
443
|
+
releaseWork = nil
|
|
444
|
+
targetCheckTimer?.invalidate()
|
|
445
|
+
targetCheckTimer = nil
|
|
446
|
+
targetPid = nil
|
|
447
|
+
targetWindowNumber = nil
|
|
448
|
+
targetWindowFrame = nil
|
|
449
|
+
cursorView.pressed = false
|
|
450
|
+
window.orderOut(nil)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
func stop() {
|
|
454
|
+
hide()
|
|
455
|
+
NSApp.stop(nil)
|
|
456
|
+
if let wakeEvent = NSEvent.otherEvent(
|
|
457
|
+
with: .applicationDefined,
|
|
458
|
+
location: .zero,
|
|
459
|
+
modifierFlags: [],
|
|
460
|
+
timestamp: ProcessInfo.processInfo.systemUptime,
|
|
461
|
+
windowNumber: 0,
|
|
462
|
+
context: nil,
|
|
463
|
+
subtype: 0,
|
|
464
|
+
data1: 0,
|
|
465
|
+
data2: 0
|
|
466
|
+
) {
|
|
467
|
+
NSApp.postEvent(wakeEvent, atStart: true)
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private func scheduleHide(after milliseconds: Int) {
|
|
472
|
+
hideWork?.cancel()
|
|
473
|
+
guard milliseconds > 0 else { return }
|
|
474
|
+
let work = DispatchWorkItem { [weak self] in self?.hide() }
|
|
475
|
+
hideWork = work
|
|
476
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(milliseconds), execute: work)
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
private func scheduleTargetChecks() {
|
|
480
|
+
guard targetCheckTimer == nil else { return }
|
|
481
|
+
targetCheckTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { [weak self] _ in
|
|
482
|
+
Task { @MainActor [weak self] in
|
|
483
|
+
guard let self, self.window.isVisible else { return }
|
|
484
|
+
if self.targetPlacement(
|
|
485
|
+
pid: self.targetPid,
|
|
486
|
+
windowNumber: self.targetWindowNumber,
|
|
487
|
+
expectedFrame: self.targetWindowFrame
|
|
488
|
+
) != .shown {
|
|
489
|
+
self.hide()
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
@objc private func frontmostApplicationChanged(_ notification: Notification) {
|
|
496
|
+
guard window.isVisible, let targetPid else { return }
|
|
497
|
+
if NSWorkspace.shared.frontmostApplication?.processIdentifier != targetPid {
|
|
498
|
+
hide()
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
private func appKitPoint(fromQuartz point: CGPoint) -> NSPoint {
|
|
503
|
+
for screen in NSScreen.screens {
|
|
504
|
+
guard let screenNumber = (screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value else {
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
let quartzFrame = CGDisplayBounds(CGDirectDisplayID(screenNumber))
|
|
508
|
+
if quartzFrame.contains(point) {
|
|
509
|
+
return NSPoint(
|
|
510
|
+
x: screen.frame.origin.x + (point.x - quartzFrame.origin.x),
|
|
511
|
+
y: screen.frame.maxY - (point.y - quartzFrame.origin.y)
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
let mainDisplay = CGMainDisplayID()
|
|
516
|
+
let quartzFrame = CGDisplayBounds(mainDisplay)
|
|
517
|
+
let screen = NSScreen.screens.first { candidate in
|
|
518
|
+
(candidate.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value == mainDisplay
|
|
519
|
+
} ?? NSScreen.main
|
|
520
|
+
let appKitFrame = screen?.frame ?? .zero
|
|
521
|
+
return NSPoint(
|
|
522
|
+
x: appKitFrame.origin.x + (point.x - quartzFrame.origin.x),
|
|
523
|
+
y: appKitFrame.maxY - (point.y - quartzFrame.origin.y)
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private func quartzPoint(fromAppKit point: NSPoint) -> CGPoint {
|
|
528
|
+
for screen in NSScreen.screens {
|
|
529
|
+
guard screen.frame.contains(point),
|
|
530
|
+
let screenNumber = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
|
|
531
|
+
continue
|
|
532
|
+
}
|
|
533
|
+
let quartzFrame = CGDisplayBounds(CGDirectDisplayID(screenNumber.uint32Value))
|
|
534
|
+
return CGPoint(
|
|
535
|
+
x: quartzFrame.origin.x + (point.x - screen.frame.origin.x),
|
|
536
|
+
y: quartzFrame.origin.y + (screen.frame.maxY - point.y)
|
|
537
|
+
)
|
|
538
|
+
}
|
|
539
|
+
let mainDisplay = CGMainDisplayID()
|
|
540
|
+
let quartzFrame = CGDisplayBounds(mainDisplay)
|
|
541
|
+
let appKitFrame = NSScreen.main?.frame ?? NSRect(
|
|
542
|
+
x: quartzFrame.origin.x,
|
|
543
|
+
y: quartzFrame.origin.y,
|
|
544
|
+
width: quartzFrame.width,
|
|
545
|
+
height: quartzFrame.height
|
|
546
|
+
)
|
|
547
|
+
return CGPoint(
|
|
548
|
+
x: quartzFrame.origin.x + (point.x - appKitFrame.origin.x),
|
|
549
|
+
y: quartzFrame.origin.y + (appKitFrame.maxY - point.y)
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private func panelOrigin(fromQuartz point: CGPoint) -> NSPoint {
|
|
554
|
+
let appKit = appKitPoint(fromQuartz: point)
|
|
555
|
+
return NSPoint(x: appKit.x, y: appKit.y - Self.size.height)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
private func targetPlacement(pid: pid_t?, windowNumber: Int64?, expectedFrame: CGRect?) -> Placement {
|
|
559
|
+
guard let pid, let windowNumber, let expectedFrame else { return .targetUnavailable }
|
|
560
|
+
guard NSWorkspace.shared.frontmostApplication?.processIdentifier == pid else { return .targetNotFrontmost }
|
|
561
|
+
guard let rawWindows = CGWindowListCopyWindowInfo(
|
|
562
|
+
[.optionOnScreenOnly, .excludeDesktopElements],
|
|
563
|
+
kCGNullWindowID
|
|
564
|
+
) as? [[String: Any]] else {
|
|
565
|
+
return .targetUnavailable
|
|
566
|
+
}
|
|
567
|
+
let matches = rawWindows.contains { window in
|
|
568
|
+
guard (window[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == pid,
|
|
569
|
+
(window[kCGWindowNumber as String] as? NSNumber)?.int64Value == windowNumber,
|
|
570
|
+
let bounds = window[kCGWindowBounds as String] as? [String: Any],
|
|
571
|
+
let currentFrame = CGRect(dictionaryRepresentation: bounds as CFDictionary) else { return false }
|
|
572
|
+
let tolerance: CGFloat = 2
|
|
573
|
+
return abs(currentFrame.minX - expectedFrame.minX) <= tolerance
|
|
574
|
+
&& abs(currentFrame.minY - expectedFrame.minY) <= tolerance
|
|
575
|
+
&& abs(currentFrame.width - expectedFrame.width) <= tolerance
|
|
576
|
+
&& abs(currentFrame.height - expectedFrame.height) <= tolerance
|
|
577
|
+
}
|
|
578
|
+
return matches ? .shown : .targetUnavailable
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
@MainActor
|
|
583
|
+
enum CursorOverlayRuntime {
|
|
584
|
+
static func run() {
|
|
585
|
+
let app = NSApplication.shared
|
|
586
|
+
app.setActivationPolicy(.prohibited)
|
|
587
|
+
let controller = CursorOverlayController()
|
|
588
|
+
let dispatcher = CursorCommandDispatcher(controller: controller)
|
|
589
|
+
let input = FileHandle.standardInput
|
|
590
|
+
let parser = CursorCommandParser { object in
|
|
591
|
+
DispatchQueue.main.async {
|
|
592
|
+
dispatcher.enqueue(object)
|
|
593
|
+
}
|
|
594
|
+
} onEnd: {
|
|
595
|
+
DispatchQueue.main.async { dispatcher.endInput() }
|
|
596
|
+
} onInvalid: { message in
|
|
597
|
+
DispatchQueue.main.async {
|
|
598
|
+
dispatcher.invalidate(message)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
input.readabilityHandler = { handle in parser.consume(handle.availableData) }
|
|
602
|
+
emitCursorResponse(["ok": true, "ready": true, "pid": ProcessInfo.processInfo.processIdentifier])
|
|
603
|
+
app.run()
|
|
604
|
+
input.readabilityHandler = nil
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
@MainActor
|
|
609
|
+
private final class CursorCommandDispatcher {
|
|
610
|
+
private let controller: CursorOverlayController
|
|
611
|
+
private var commands: [[String: Any]] = []
|
|
612
|
+
private var processing = false
|
|
613
|
+
private var inputEnded = false
|
|
614
|
+
private var terminated = false
|
|
615
|
+
|
|
616
|
+
init(controller: CursorOverlayController) {
|
|
617
|
+
self.controller = controller
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
func enqueue(_ object: [String: Any]) {
|
|
621
|
+
guard !inputEnded, !terminated else { return }
|
|
622
|
+
commands.append(object)
|
|
623
|
+
if object["op"] as? String == "stop", processing {
|
|
624
|
+
controller.hide()
|
|
625
|
+
}
|
|
626
|
+
drain()
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
func endInput() {
|
|
630
|
+
guard !inputEnded, !terminated else { return }
|
|
631
|
+
inputEnded = true
|
|
632
|
+
if commands.contains(where: { $0["op"] as? String == "stop" }) {
|
|
633
|
+
if processing { controller.hide() }
|
|
634
|
+
drain()
|
|
635
|
+
} else {
|
|
636
|
+
terminated = true
|
|
637
|
+
commands.removeAll()
|
|
638
|
+
controller.stop()
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
func invalidate(_ message: String) {
|
|
643
|
+
guard !terminated else { return }
|
|
644
|
+
inputEnded = true
|
|
645
|
+
terminated = true
|
|
646
|
+
commands.removeAll()
|
|
647
|
+
controller.hide()
|
|
648
|
+
emitCursorResponse(["ok": false, "error": message])
|
|
649
|
+
controller.stop()
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
private func drain() {
|
|
653
|
+
guard !terminated, !processing, !commands.isEmpty else { return }
|
|
654
|
+
processing = true
|
|
655
|
+
let command = commands.removeFirst()
|
|
656
|
+
let stopsRuntime = command["op"] as? String == "stop"
|
|
657
|
+
handleCursorCommand(command, controller: controller) { [weak self] in
|
|
658
|
+
guard let self else { return }
|
|
659
|
+
self.processing = false
|
|
660
|
+
if stopsRuntime {
|
|
661
|
+
self.terminated = true
|
|
662
|
+
self.commands.removeAll()
|
|
663
|
+
return
|
|
664
|
+
}
|
|
665
|
+
DispatchQueue.main.async { [weak self] in self?.drain() }
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private final class CursorCommandParser: @unchecked Sendable {
|
|
671
|
+
private static let maximumLineBytes = 16 * 1024
|
|
672
|
+
private let queue = DispatchQueue(label: "dsh-computer-use.cursor-protocol")
|
|
673
|
+
private let onObject: @Sendable ([String: Any]) -> Void
|
|
674
|
+
private let onEnd: @Sendable () -> Void
|
|
675
|
+
private let onInvalid: @Sendable (String) -> Void
|
|
676
|
+
private var buffer = Data()
|
|
677
|
+
private var ended = false
|
|
678
|
+
|
|
679
|
+
init(
|
|
680
|
+
onObject: @escaping @Sendable ([String: Any]) -> Void,
|
|
681
|
+
onEnd: @escaping @Sendable () -> Void,
|
|
682
|
+
onInvalid: @escaping @Sendable (String) -> Void
|
|
683
|
+
) {
|
|
684
|
+
self.onObject = onObject
|
|
685
|
+
self.onEnd = onEnd
|
|
686
|
+
self.onInvalid = onInvalid
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
func consume(_ data: Data) {
|
|
690
|
+
queue.async { [self] in
|
|
691
|
+
guard !ended, !data.isEmpty else {
|
|
692
|
+
guard !ended else { return }
|
|
693
|
+
ended = true
|
|
694
|
+
onEnd()
|
|
695
|
+
return
|
|
696
|
+
}
|
|
697
|
+
buffer.append(data)
|
|
698
|
+
if buffer.count > Self.maximumLineBytes, buffer.firstIndex(of: 0x0a) == nil {
|
|
699
|
+
ended = true
|
|
700
|
+
buffer.removeAll()
|
|
701
|
+
onInvalid("cursor overlay command exceeded the protocol limit")
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
while let newline = buffer.firstIndex(of: 0x0a) {
|
|
705
|
+
let line = buffer[..<newline]
|
|
706
|
+
buffer.removeSubrange(...newline)
|
|
707
|
+
if line.count > Self.maximumLineBytes {
|
|
708
|
+
ended = true
|
|
709
|
+
onInvalid("cursor overlay command exceeded the protocol limit")
|
|
710
|
+
return
|
|
711
|
+
}
|
|
712
|
+
guard !line.isEmpty else { continue }
|
|
713
|
+
guard let object = try? JSONSerialization.jsonObject(with: Data(line)) as? [String: Any] else {
|
|
714
|
+
ended = true
|
|
715
|
+
onInvalid("cursor overlay command is not valid JSON")
|
|
716
|
+
return
|
|
717
|
+
}
|
|
718
|
+
onObject(object)
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
@MainActor
|
|
725
|
+
private func handleCursorCommand(
|
|
726
|
+
_ object: [String: Any],
|
|
727
|
+
controller: CursorOverlayController,
|
|
728
|
+
completion: @escaping () -> Void
|
|
729
|
+
) {
|
|
730
|
+
do {
|
|
731
|
+
let command = try CursorOverlayCommand(object)
|
|
732
|
+
// Whether the panel is actually on screen after this command. A hidden
|
|
733
|
+
// overlay still leaves native input working, so the operation is not an
|
|
734
|
+
// error -- but the caller must be able to tell, or the user silently
|
|
735
|
+
// loses sight of where the agent is acting.
|
|
736
|
+
var visible = true
|
|
737
|
+
var reasonCode: String?
|
|
738
|
+
switch command.operation {
|
|
739
|
+
case "show", "move":
|
|
740
|
+
guard let point = command.point else {
|
|
741
|
+
throw CursorOverlayError(message: "cursor overlay command needs x and y")
|
|
742
|
+
}
|
|
743
|
+
controller.show(
|
|
744
|
+
at: point,
|
|
745
|
+
durationMs: command.durationMs,
|
|
746
|
+
speedPxPerSecond: command.speedPxPerSecond,
|
|
747
|
+
accelerationPxPerSecondSquared: command.accelerationPxPerSecondSquared,
|
|
748
|
+
autoHideMs: command.autoHideMs,
|
|
749
|
+
targetPid: command.targetPid,
|
|
750
|
+
targetWindowNumber: command.targetWindowNumber,
|
|
751
|
+
targetWindowFrame: command.targetWindowFrame
|
|
752
|
+
) { placement in
|
|
753
|
+
let shown = placement == .shown
|
|
754
|
+
var response: [String: Any] = ["ok": true, "op": command.operation, "visible": shown]
|
|
755
|
+
if !shown {
|
|
756
|
+
response["reasonCode"] = cursorReasonCode(placement)
|
|
757
|
+
response["reason"] = cursorReason(placement)
|
|
758
|
+
}
|
|
759
|
+
emitCursorResponse(response)
|
|
760
|
+
completion()
|
|
761
|
+
}
|
|
762
|
+
return
|
|
763
|
+
case "press":
|
|
764
|
+
let placement = controller.validateTarget(
|
|
765
|
+
pid: command.targetPid,
|
|
766
|
+
windowNumber: command.targetWindowNumber,
|
|
767
|
+
expectedFrame: command.targetWindowFrame
|
|
768
|
+
)
|
|
769
|
+
visible = placement == .shown
|
|
770
|
+
reasonCode = cursorReasonCode(placement)
|
|
771
|
+
controller.press(autoHideMs: command.autoHideMs, sustained: command.sustainedPress)
|
|
772
|
+
case "release":
|
|
773
|
+
let placement = controller.validateTarget(
|
|
774
|
+
pid: command.targetPid,
|
|
775
|
+
windowNumber: command.targetWindowNumber,
|
|
776
|
+
expectedFrame: command.targetWindowFrame
|
|
777
|
+
)
|
|
778
|
+
visible = placement == .shown
|
|
779
|
+
reasonCode = cursorReasonCode(placement)
|
|
780
|
+
controller.release(autoHideMs: command.autoHideMs)
|
|
781
|
+
case "validate":
|
|
782
|
+
let placement = controller.validateTarget(
|
|
783
|
+
pid: command.targetPid,
|
|
784
|
+
windowNumber: command.targetWindowNumber,
|
|
785
|
+
expectedFrame: command.targetWindowFrame
|
|
786
|
+
)
|
|
787
|
+
visible = placement == .shown
|
|
788
|
+
reasonCode = cursorReasonCode(placement)
|
|
789
|
+
case "hide":
|
|
790
|
+
controller.hide()
|
|
791
|
+
visible = false
|
|
792
|
+
case "stop":
|
|
793
|
+
controller.stop()
|
|
794
|
+
visible = false
|
|
795
|
+
case "ping":
|
|
796
|
+
visible = controller.isVisible
|
|
797
|
+
default:
|
|
798
|
+
throw CursorOverlayError(message: "unknown cursor overlay operation")
|
|
799
|
+
}
|
|
800
|
+
var response: [String: Any] = ["ok": true, "op": command.operation, "visible": visible]
|
|
801
|
+
if !visible && (command.operation == "show" || command.operation == "move"
|
|
802
|
+
|| command.operation == "press" || command.operation == "release"
|
|
803
|
+
|| command.operation == "validate") {
|
|
804
|
+
response["reasonCode"] = reasonCode
|
|
805
|
+
response["reason"] = reasonCode == "target-not-frontmost"
|
|
806
|
+
? "the bound target application is not frontmost; the agent cursor is hidden"
|
|
807
|
+
: "the bound target window no longer matches; the agent cursor is hidden"
|
|
808
|
+
}
|
|
809
|
+
emitCursorResponse(response)
|
|
810
|
+
completion()
|
|
811
|
+
} catch let error as CursorOverlayError {
|
|
812
|
+
emitCursorResponse(["ok": false, "error": error.message])
|
|
813
|
+
completion()
|
|
814
|
+
} catch {
|
|
815
|
+
emitCursorResponse(["ok": false, "error": String(describing: error)])
|
|
816
|
+
completion()
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
private func cursorReasonCode(_ placement: CursorOverlayController.Placement) -> String? {
|
|
821
|
+
switch placement {
|
|
822
|
+
case .shown: return nil
|
|
823
|
+
case .targetNotFrontmost: return "target-not-frontmost"
|
|
824
|
+
case .targetUnavailable: return "target-invalid"
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
private func cursorReason(_ placement: CursorOverlayController.Placement) -> String? {
|
|
829
|
+
switch placement {
|
|
830
|
+
case .shown: return nil
|
|
831
|
+
case .targetNotFrontmost: return "the bound target application is not frontmost; the agent cursor is hidden"
|
|
832
|
+
case .targetUnavailable: return "the bound target window no longer matches; the agent cursor is hidden"
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
private func emitCursorResponse(_ payload: [String: Any]) {
|
|
837
|
+
guard let data = try? JSONSerialization.data(withJSONObject: payload, options: [.sortedKeys]) else { return }
|
|
838
|
+
FileHandle.standardOutput.write(data)
|
|
839
|
+
FileHandle.standardOutput.write(Data("\n".utf8))
|
|
840
|
+
}
|