machine-bridge-mcp 2.0.0 → 3.0.0-beta.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1142 -0
- package/CONTRIBUTING.md +23 -25
- package/GOVERNANCE.md +2 -2
- package/README.md +58 -20
- package/SECURITY.md +161 -120
- package/SUPPORT.md +1 -1
- package/browser-extension/broker-auth.js +83 -0
- package/browser-extension/broker-liveness.js +35 -0
- package/browser-extension/browser-error-boundary.js +96 -0
- package/browser-extension/browser-operations.js +1456 -101
- package/browser-extension/devtools-input.js +195 -34
- package/browser-extension/devtools-observation.js +429 -0
- package/browser-extension/devtools-session.js +48 -0
- package/browser-extension/manifest.json +4 -3
- package/browser-extension/page-automation.js +578 -118
- package/browser-extension/pairing-bootstrap.js +42 -0
- package/browser-extension/pairing.js +28 -12
- package/browser-extension/service-worker.js +126 -106
- package/docs/AGENT_CONTEXT.md +13 -10
- package/docs/ARCHITECTURE.md +159 -67
- package/docs/AUDIT.md +1302 -4
- package/docs/CLIENTS.md +19 -6
- package/docs/COMPUTER_USE.md +297 -0
- package/docs/ENGINEERING.md +72 -13
- package/docs/GETTING_STARTED.md +18 -20
- package/docs/LOCAL_AUTHORIZATION.md +115 -72
- package/docs/LOCAL_AUTOMATION.md +27 -21
- package/docs/LOGGING.md +40 -19
- package/docs/MANAGED_JOBS.md +37 -34
- package/docs/MULTI_ACCOUNT.md +98 -37
- package/docs/OPERATIONS.md +145 -51
- package/docs/OVERVIEW.md +14 -11
- package/docs/POLICY_REFERENCE.md +71 -3
- package/docs/PRIVACY.md +41 -2
- package/docs/PROJECT_STANDARDS.md +29 -6
- package/docs/RELEASING.md +238 -64
- package/docs/TESTING.md +150 -39
- package/docs/THREAT_MODEL.md +164 -67
- package/docs/TOOL_REFERENCE.md +653 -60
- package/docs/UPGRADING.md +78 -18
- package/native/macos/MachineBridgeBackgroundInput.swift +1051 -0
- package/native/macos/MachineBridgeBackgroundInputSmokeFixture.swift +295 -0
- package/native/macos/MachineBridgeTrustBroker.swift +233 -0
- package/package.json +94 -16
- package/scripts/accepted-candidate-tarball.mjs +77 -0
- package/scripts/candidate-runtime-store.mjs +93 -0
- package/scripts/check-plan.mjs +73 -3
- package/scripts/check-runner.mjs +129 -17
- package/scripts/commit-message-check.mjs +5 -2
- package/scripts/consumer-package-security.mjs +293 -0
- package/scripts/coverage-check.mjs +251 -15
- package/scripts/coverage-generation.mjs +41 -0
- package/scripts/coverage-range-merge.mjs +41 -0
- package/scripts/foreground-daemon-recovery.mjs +88 -0
- package/scripts/generate-policy-reference.mjs +28 -5
- package/scripts/generate-tool-reference.mjs +1 -1
- package/scripts/generate-worker-types.mjs +21 -11
- package/scripts/github-backlog.mjs +11 -10
- package/scripts/github-push.mjs +62 -16
- package/scripts/github-release-asset.mjs +58 -0
- package/scripts/github-release.mjs +241 -132
- package/scripts/global-package-installation.mjs +68 -0
- package/scripts/hardened-npm-session.mjs +42 -0
- package/scripts/install-published-prerelease.mjs +198 -0
- package/scripts/local-release-acceptance.mjs +103 -45
- package/scripts/macos-background-input-smoke.mjs +515 -0
- package/scripts/network-retry.mjs +8 -1
- package/scripts/npm-global-prefix.mjs +27 -0
- package/scripts/npm-publication-policy.mjs +17 -0
- package/scripts/official-mcp-conformance.mjs +267 -0
- package/scripts/persistent-activation-process.mjs +101 -0
- package/scripts/prepare-pinned-npm.mjs +12 -69
- package/scripts/prerelease-activation.mjs +160 -0
- package/scripts/privacy-check.mjs +14 -6
- package/scripts/promotion-digest.mjs +128 -0
- package/scripts/publish-npm.mjs +260 -0
- package/scripts/published-release.mjs +132 -0
- package/scripts/release-acceptance.mjs +62 -54
- package/scripts/release-browser-extension-store.mjs +172 -0
- package/scripts/release-candidate-manifest.mjs +52 -0
- package/scripts/release-channel.mjs +106 -0
- package/scripts/release-diagnostic.mjs +41 -0
- package/scripts/release-impact-check.mjs +12 -22
- package/scripts/release-oauth-canary-core.mjs +325 -0
- package/scripts/release-oauth-canary-evidence.mjs +100 -0
- package/scripts/release-oauth-canary.mjs +127 -0
- package/scripts/release-publication-guard.mjs +67 -0
- package/scripts/release-soak.mjs +284 -0
- package/scripts/release-state.mjs +1 -1
- package/scripts/run-checks.mjs +44 -7
- package/scripts/run-worker-dry-run.mjs +21 -0
- package/scripts/sarif-security-gate.mjs +2 -1
- package/scripts/sbom-check.mjs +106 -0
- package/scripts/start-release-candidate.mjs +267 -44
- package/scripts/syntax-check.mjs +36 -14
- package/scripts/verification-generation-guard.mjs +15 -0
- package/scripts/verification-idle-sleep-guard.mjs +37 -0
- package/scripts/verification-state.mjs +108 -0
- package/scripts/wrangler-command-lifecycle.mjs +115 -0
- package/src/local/account-access.mjs +13 -1
- package/src/local/account-admin-response.mjs +92 -0
- package/src/local/account-admin.mjs +52 -19
- package/src/local/agent-context-projection.mjs +26 -7
- package/src/local/agent-context.mjs +88 -49
- package/src/local/agent-skill-discovery.mjs +16 -5
- package/src/local/app-automation-macos-jxa.mjs +350 -0
- package/src/local/app-automation.mjs +873 -163
- package/src/local/application-capability-projection.mjs +24 -0
- package/src/local/authority-context.mjs +106 -0
- package/src/local/autostart-log-maintenance.mjs +36 -0
- package/src/local/browser-bridge-http.mjs +5 -3
- package/src/local/browser-bridge.mjs +109 -31
- package/src/local/browser-broker-auth-http.mjs +67 -0
- package/src/local/browser-broker-auth.mjs +98 -0
- package/src/local/browser-broker-routes.mjs +63 -20
- package/src/local/browser-broker-server.mjs +17 -7
- package/src/local/browser-command.mjs +79 -30
- package/src/local/browser-computer-observation-service.mjs +266 -0
- package/src/local/browser-extension-identity.mjs +50 -0
- package/src/local/browser-extension-path.mjs +22 -0
- package/src/local/browser-extension-protocol.mjs +50 -11
- package/src/local/browser-operation-service.mjs +147 -117
- package/src/local/browser-pairing-grant.mjs +109 -0
- package/src/local/browser-pairing-http.mjs +36 -0
- package/src/local/browser-pairing-launch.mjs +58 -0
- package/src/local/browser-pairing-store.mjs +85 -48
- package/src/local/browser-request-registry.mjs +40 -14
- package/src/local/browser-request-settlement.mjs +78 -0
- package/src/local/browser-resource-input.mjs +74 -0
- package/src/local/browser-trusted-input-health.mjs +63 -0
- package/src/local/call-authority.mjs +21 -0
- package/src/local/call-capacity.mjs +45 -0
- package/src/local/call-registry-drain.mjs +54 -0
- package/src/local/call-registry.mjs +30 -15
- package/src/local/capability-observer.mjs +5 -0
- package/src/local/capability-ranking.mjs +3 -2
- package/src/local/child-process-settlement.mjs +132 -0
- package/src/local/cli-account-admin.mjs +40 -6
- package/src/local/cli-activate.mjs +162 -0
- package/src/local/cli-local-admin.mjs +20 -45
- package/src/local/cli-options.mjs +12 -13
- package/src/local/cli-ready-output.mjs +52 -0
- package/src/local/cli-service.mjs +91 -11
- package/src/local/cli.mjs +232 -150
- package/src/local/computer-use-application-observation.mjs +224 -0
- package/src/local/computer-use-arguments.mjs +270 -0
- package/src/local/computer-use-deadline.mjs +50 -0
- package/src/local/computer-use-dispatch-settlement.mjs +40 -0
- package/src/local/computer-use-expectation.mjs +171 -0
- package/src/local/computer-use-observation.mjs +769 -0
- package/src/local/computer-use-recovery.mjs +82 -0
- package/src/local/computer-use-result-budget.mjs +145 -0
- package/src/local/computer-use-snapshot-store.mjs +67 -0
- package/src/local/computer-use.mjs +2255 -0
- package/src/local/daemon-http-relay-auth.mjs +26 -0
- package/src/local/daemon-http-relay-connection.mjs +298 -0
- package/src/local/daemon-http-relay-request.mjs +64 -0
- package/src/local/daemon-http-relay-sequence.mjs +49 -0
- package/src/local/daemon-process.mjs +40 -7
- package/src/local/delegated-process-sandbox.mjs +148 -0
- package/src/local/device-identity.mjs +156 -49
- package/src/local/device-root-provider.mjs +81 -0
- package/src/local/directory-metadata.mjs +47 -0
- package/src/local/doctor-reporting.mjs +23 -0
- package/src/local/durable-process-spec.mjs +80 -0
- package/src/local/errors.mjs +25 -15
- package/src/local/exclusive-file.mjs +74 -26
- package/src/local/execution-limits.mjs +9 -0
- package/src/local/execution-routing.mjs +276 -0
- package/src/local/execution-surface.mjs +23 -0
- package/src/local/file-mutation-coordinator.mjs +47 -0
- package/src/local/file-snapshot-preservation.mjs +24 -0
- package/src/local/filesystem-identity.mjs +54 -0
- package/src/local/fixed-process-environment.mjs +21 -0
- package/src/local/full-access-test.mjs +31 -13
- package/src/local/git-commit.mjs +69 -0
- package/src/local/git-config-safety.mjs +55 -0
- package/src/local/git-log-parser.mjs +41 -0
- package/src/local/git-metadata-boundary.mjs +77 -0
- package/src/local/git-metadata-tree-safety.mjs +59 -0
- package/src/local/git-operation-state.mjs +19 -0
- package/src/local/git-service.mjs +165 -30
- package/src/local/hardened-npm-download-timeout.mjs +46 -0
- package/src/local/hardened-npm-download.mjs +109 -0
- package/src/local/hardened-npm-extract.mjs +20 -0
- package/src/local/hardened-npm-verification.mjs +104 -0
- package/src/local/hardened-npm.mjs +199 -0
- package/src/local/job-runner.mjs +244 -89
- package/src/local/lifecycle.mjs +6 -0
- package/src/local/log.mjs +19 -45
- package/src/local/macos-background-input.mjs +398 -0
- package/src/local/macos-idle-sleep-assertion.mjs +65 -0
- package/src/local/macos-trust-broker.mjs +404 -0
- package/src/local/managed-job-cancellation.mjs +42 -0
- package/src/local/managed-job-capacity.mjs +16 -0
- package/src/local/managed-job-directory-generation.mjs +60 -0
- package/src/local/managed-job-directory.mjs +75 -0
- package/src/local/managed-job-durable-process.mjs +49 -0
- package/src/local/managed-job-lock.mjs +46 -14
- package/src/local/managed-job-plan.mjs +38 -14
- package/src/local/managed-job-projection.mjs +23 -15
- package/src/local/managed-job-retention.mjs +128 -0
- package/src/local/managed-job-runner-claim.mjs +70 -0
- package/src/local/managed-job-runner.mjs +35 -10
- package/src/local/managed-job-storage.mjs +22 -6
- package/src/local/managed-job-terminal-maintenance.mjs +40 -0
- package/src/local/managed-job-terminal.mjs +150 -0
- package/src/local/managed-jobs.mjs +424 -287
- package/src/local/npm-cli.mjs +42 -0
- package/src/local/npm-environment.mjs +37 -0
- package/src/local/operation-authorization.mjs +68 -324
- package/src/local/operation-risk.mjs +51 -4
- package/src/local/owner-state-lock.mjs +118 -0
- package/src/local/package-identity.mjs +9 -0
- package/src/local/patch.mjs +20 -18
- package/src/local/path-inspection.mjs +23 -0
- package/src/local/policy.mjs +19 -0
- package/src/local/private-toolchain-integrity.mjs +54 -0
- package/src/local/process-execution.mjs +87 -75
- package/src/local/process-foreground-timeout.mjs +50 -0
- package/src/local/process-identity.mjs +41 -7
- package/src/local/process-nonreplayable-settlement.mjs +50 -0
- package/src/local/process-result-projection.mjs +22 -0
- package/src/local/process-session-events.mjs +47 -0
- package/src/local/process-session-remote-activity.mjs +10 -0
- package/src/local/process-session-termination.mjs +73 -0
- package/src/local/process-sessions.mjs +144 -112
- package/src/local/process-tracker.mjs +140 -13
- package/src/local/process-tree-ownership-types.d.ts +37 -0
- package/src/local/process-tree-ownership.mjs +57 -0
- package/src/local/process-tree-signal.mjs +34 -0
- package/src/local/process-tree-snapshot.mjs +77 -0
- package/src/local/process-tree-supervisor.mjs +69 -0
- package/src/local/process-tree.mjs +11 -53
- package/src/local/project-metadata.mjs +19 -6
- package/src/local/relay-call-recovery.mjs +58 -38
- package/src/local/relay-connection-classification.mjs +131 -0
- package/src/local/relay-connection-support.mjs +91 -0
- package/src/local/relay-connection.mjs +179 -272
- package/src/local/relay-diagnostics.mjs +91 -0
- package/src/local/relay-heartbeat.mjs +119 -0
- package/src/local/relay-liveness.mjs +109 -0
- package/src/local/relay-peer-diagnostics.mjs +29 -0
- package/src/local/release-runtime-lock.mjs +18 -0
- package/src/local/remote-activity-idle-sleep-guard.mjs +77 -0
- package/src/local/remote-configuration.mjs +48 -0
- package/src/local/resilient-relay-connection.mjs +178 -0
- package/src/local/resource-admission-diagnostics.mjs +52 -0
- package/src/local/resource-admission-policy.mjs +115 -0
- package/src/local/resource-admission.mjs +339 -0
- package/src/local/resource-build-root.mjs +83 -0
- package/src/local/resource-cargo-concurrency.mjs +38 -0
- package/src/local/resource-cmake-concurrency.mjs +42 -0
- package/src/local/resource-command-concurrency.mjs +37 -0
- package/src/local/resource-command-profile.mjs +176 -0
- package/src/local/resource-coordinator-accounting.mjs +21 -0
- package/src/local/resource-cpu-window.mjs +20 -0
- package/src/local/resource-disk-headroom.mjs +13 -0
- package/src/local/resource-elastic-memory.mjs +19 -0
- package/src/local/resource-elastic-request.mjs +36 -0
- package/src/local/resource-foreground-wait.mjs +29 -0
- package/src/local/resource-go-concurrency.mjs +43 -0
- package/src/local/resource-gradle-concurrency.mjs +57 -0
- package/src/local/resource-host-cache.mjs +39 -0
- package/src/local/resource-host-darwin.mjs +54 -0
- package/src/local/resource-host-linux.mjs +35 -0
- package/src/local/resource-host-sample-file.mjs +23 -0
- package/src/local/resource-host-snapshot.mjs +99 -0
- package/src/local/resource-lease-accounting.mjs +119 -0
- package/src/local/resource-light-command.mjs +6 -0
- package/src/local/resource-make-concurrency.mjs +42 -0
- package/src/local/resource-maven-concurrency.mjs +38 -0
- package/src/local/resource-ninja-command-concurrency.mjs +20 -0
- package/src/local/resource-ninja-concurrency.mjs +28 -0
- package/src/local/resource-operations.mjs +26 -10
- package/src/local/resource-pressure.mjs +67 -0
- package/src/local/resource-probe-command.mjs +35 -0
- package/src/local/resource-process-admission.mjs +59 -0
- package/src/local/resource-process-ancestry-cache.mjs +43 -0
- package/src/local/resource-process-ancestry.mjs +32 -0
- package/src/local/resource-process-priority.mjs +15 -0
- package/src/local/resource-project-key.mjs +53 -0
- package/src/local/resource-pytest-concurrency.mjs +33 -0
- package/src/local/resource-release-control-classification.mjs +14 -0
- package/src/local/resource-release-control-executable.mjs +70 -0
- package/src/local/resource-release-control-workspace.mjs +41 -0
- package/src/local/resource-request-contract.mjs +30 -0
- package/src/local/resource-script-classification.mjs +44 -0
- package/src/local/resource-shell-analysis.mjs +58 -0
- package/src/local/resource-staging-recovery.mjs +97 -0
- package/src/local/resource-swift-concurrency.mjs +31 -0
- package/src/local/resource-transaction-lock.mjs +189 -0
- package/src/local/resource-wait.mjs +48 -0
- package/src/local/resource-waiters.mjs +139 -0
- package/src/local/resource-xcode-command.mjs +7 -0
- package/src/local/resource-xcode-concurrency.mjs +34 -0
- package/src/local/resource-xcode-non-build.mjs +19 -0
- package/src/local/runtime-activation.mjs +700 -0
- package/src/local/runtime-activity-projection.mjs +48 -0
- package/src/local/runtime-capabilities.mjs +48 -15
- package/src/local/runtime-diagnostic-state.mjs +14 -0
- package/src/local/runtime-diagnostics.mjs +40 -13
- package/src/local/runtime-info-projection.mjs +60 -0
- package/src/local/runtime-info-relay-projection.mjs +21 -0
- package/src/local/runtime-paths.mjs +2 -1
- package/src/local/runtime-process-routing.mjs +33 -0
- package/src/local/runtime-relay-connection-options.mjs +44 -0
- package/src/local/runtime-relay-control.mjs +98 -0
- package/src/local/runtime-relay.mjs +26 -50
- package/src/local/runtime-reporting.mjs +37 -21
- package/src/local/runtime-resource-service.mjs +77 -0
- package/src/local/runtime-tool-handlers.mjs +13 -10
- package/src/local/runtime.mjs +244 -208
- package/src/local/secure-file.mjs +73 -20
- package/src/local/security-audit-dispatch.mjs +35 -0
- package/src/local/security-audit-log.mjs +215 -0
- package/src/local/security-audit-state.mjs +139 -0
- package/src/local/security-audit-storage.mjs +135 -0
- package/src/local/security-audit-warning.mjs +31 -0
- package/src/local/security-audit-worker.mjs +87 -0
- package/src/local/service-convergence.mjs +42 -1
- package/src/local/service-definition.mjs +24 -0
- package/src/local/service-environment.mjs +18 -10
- package/src/local/service-owner.mjs +146 -0
- package/src/local/service-ownership.mjs +17 -0
- package/src/local/service-restart-handoff.mjs +64 -0
- package/src/local/service-restart-scheduler.mjs +49 -0
- package/src/local/service-runtime-convergence.mjs +52 -0
- package/src/local/service-runtime.mjs +105 -0
- package/src/local/service-status.mjs +60 -0
- package/src/local/service.mjs +385 -92
- package/src/local/shell.mjs +39 -19
- package/src/local/ssh-key.mjs +158 -42
- package/src/local/state-inventory.mjs +50 -24
- package/src/local/state-owner-lock-inventory.mjs +23 -0
- package/src/local/state-root-owned-namespaces.mjs +83 -0
- package/src/local/state-root-retirement.mjs +89 -0
- package/src/local/state.mjs +386 -96
- package/src/local/stdio.mjs +96 -66
- package/src/local/support-state-projection.mjs +30 -0
- package/src/local/system-network-route.mjs +76 -0
- package/src/local/systemd-removal.mjs +20 -0
- package/src/local/tool-executor.mjs +85 -20
- package/src/local/tool-result-boundary.mjs +51 -0
- package/src/local/toolchain-operation-lock.mjs +41 -0
- package/src/local/tools.mjs +7 -5
- package/src/local/trusted-executable.mjs +53 -0
- package/src/local/trusted-git-executable.mjs +34 -0
- package/src/local/trusted-github-cli.mjs +24 -0
- package/src/local/windows-service-convergence.mjs +49 -0
- package/src/local/windows-service.mjs +52 -45
- package/src/local/worker-deployment-fingerprint.mjs +102 -0
- package/src/local/worker-deployment.mjs +48 -60
- package/src/local/worker-health.mjs +12 -4
- package/src/local/worker-secret-file.mjs +37 -17
- package/src/local/workspace-file-service.mjs +156 -239
- package/src/local/workspace-file-transaction.mjs +189 -0
- package/src/local/workspace-search.mjs +55 -0
- package/src/local/wrangler-toolchain/package-lock.json +1505 -0
- package/src/local/wrangler-toolchain/package.json +23 -0
- package/src/local/wrangler-toolchain-verification.mjs +135 -0
- package/src/local/wrangler-toolchain.mjs +188 -0
- package/src/shared/access-contract.json +7 -1
- package/src/shared/activation-recovery.mjs +49 -0
- package/src/shared/admin-auth.d.mts +2 -1
- package/src/shared/admin-auth.mjs +3 -3
- package/src/shared/authority-revocation.d.mts +9 -0
- package/src/shared/authority-revocation.mjs +28 -0
- package/src/shared/daemon-auth.d.mts +11 -0
- package/src/shared/daemon-auth.mjs +17 -0
- package/src/shared/device-session-auth.d.mts +20 -0
- package/src/shared/device-session-auth.mjs +51 -0
- package/src/shared/foreground-timeout.d.mts +15 -0
- package/src/shared/foreground-timeout.mjs +70 -0
- package/src/shared/log-redaction.d.mts +9 -0
- package/src/shared/log-redaction.mjs +51 -0
- package/src/shared/mcp-protocol.d.mts +27 -0
- package/src/shared/mcp-protocol.mjs +275 -0
- package/src/shared/project-overview-projection.d.mts +4 -0
- package/src/shared/project-overview-projection.mjs +96 -0
- package/src/shared/relay-contract.json +28 -0
- package/src/shared/result-projection.d.mts +2 -1
- package/src/shared/result-projection.mjs +13 -2
- package/src/shared/server-metadata.json +15 -9
- package/src/shared/tool-argument-validation.d.mts +17 -0
- package/src/shared/tool-argument-validation.mjs +325 -0
- package/src/shared/tool-call-capacity.d.mts +41 -0
- package/src/shared/tool-call-capacity.mjs +59 -0
- package/src/shared/tool-catalog.json +626 -57
- package/src/worker/access.ts +12 -0
- package/src/worker/account-admin.ts +55 -33
- package/src/worker/authority-revocations.ts +174 -0
- package/src/worker/authority.ts +52 -6
- package/src/worker/daemon-auth.ts +49 -68
- package/src/worker/daemon-channel.ts +36 -0
- package/src/worker/daemon-http-auth.ts +63 -0
- package/src/worker/daemon-http-channel.ts +120 -0
- package/src/worker/daemon-http-controller.ts +163 -0
- package/src/worker/daemon-http-protocol.ts +62 -0
- package/src/worker/daemon-http-registry.ts +82 -0
- package/src/worker/daemon-last-observation.ts +30 -0
- package/src/worker/daemon-liveness.ts +8 -6
- package/src/worker/daemon-ready-dispatch.ts +17 -0
- package/src/worker/daemon-ready-messages.ts +73 -0
- package/src/worker/daemon-ready-waiters.ts +79 -0
- package/src/worker/daemon-recovery-budget.ts +13 -0
- package/src/worker/daemon-registry.ts +58 -0
- package/src/worker/daemon-relay-diagnostics.ts +127 -0
- package/src/worker/daemon-socket-attachment.ts +55 -0
- package/src/worker/daemon-sockets.ts +72 -62
- package/src/worker/daemon-status.ts +38 -0
- package/src/worker/device-session-verifier.ts +129 -0
- package/src/worker/dpop.ts +157 -0
- package/src/worker/durable-process-timeout.ts +29 -0
- package/src/worker/errors.ts +57 -0
- package/src/worker/http.ts +112 -26
- package/src/worker/index.ts +446 -440
- package/src/worker/mcp-access.ts +57 -0
- package/src/worker/mcp-controller.ts +188 -0
- package/src/worker/mcp-http-accept.ts +11 -0
- package/src/worker/mcp-http-contract.ts +322 -0
- package/src/worker/mcp-initialization-compat.ts +141 -0
- package/src/worker/mcp-jsonrpc.ts +11 -29
- package/src/worker/mcp-removed-protocol.ts +17 -0
- package/src/worker/mcp-response-proxy.ts +135 -0
- package/src/worker/mcp-response-stream.ts +65 -0
- package/src/worker/mcp-stale-schema-compat.ts +57 -0
- package/src/worker/mcp-stream-proxy-contract.ts +48 -0
- package/src/worker/mcp-tool-call-input.ts +23 -0
- package/src/worker/nonce-store.ts +33 -35
- package/src/worker/oauth-authorization-page.ts +21 -1
- package/src/worker/oauth-client-admin.ts +52 -0
- package/src/worker/oauth-client-contract.ts +38 -0
- package/src/worker/oauth-controller.ts +107 -47
- package/src/worker/oauth-field-contract.ts +31 -0
- package/src/worker/oauth-record-contract.ts +7 -0
- package/src/worker/oauth-refresh-authority.ts +27 -0
- package/src/worker/oauth-refresh-exchange.ts +162 -0
- package/src/worker/oauth-refresh-families.ts +130 -27
- package/src/worker/oauth-refresh-persistence.ts +116 -0
- package/src/worker/oauth-state.ts +33 -18
- package/src/worker/oauth-store-validation.ts +131 -0
- package/src/worker/oauth-token-derivation.ts +33 -0
- package/src/worker/oauth-token-issuance.ts +119 -0
- package/src/worker/oauth-tokens.ts +56 -190
- package/src/worker/observability.ts +38 -5
- package/src/worker/pending-admission.ts +15 -0
- package/src/worker/pending-call-capacity.ts +108 -0
- package/src/worker/pending-call-contract.ts +42 -5
- package/src/worker/pending-call-deadlines.ts +75 -0
- package/src/worker/pending-calls.ts +131 -125
- package/src/worker/runtime-alarm-storage.ts +37 -0
- package/src/worker/runtime-alarm.ts +136 -0
- package/src/worker/server-info-activity.ts +36 -0
- package/src/worker/server-info-tool-delivery.ts +15 -0
- package/src/worker/server-info.ts +168 -0
- package/src/worker/tool-catalog.ts +47 -1
- package/src/worker/tool-timeout.ts +75 -14
- package/src/worker/websocket-protocol.ts +26 -2
- package/src/worker/worker-edge-guard.ts +90 -0
- package/src/worker/worker-edge-log.ts +65 -0
- package/src/worker/worker-entry.ts +52 -0
- package/src/worker/worker-mcp-config.ts +23 -0
- package/src/worker/worker-metadata.ts +48 -0
- package/src/worker/worker-rate-limit-key.ts +30 -0
- package/src/worker/worker-runtime-config.ts +20 -0
- package/src/worker/worker-static-routes.ts +54 -0
- package/src/worker/worker-task-lifetime.ts +18 -0
- package/tsconfig.local.json +17 -1
- package/wrangler.jsonc +13 -0
- package/src/local/cli-approval.mjs +0 -117
- package/src/local/operation-state-lock.mjs +0 -92
- package/src/worker/mcp-session.ts +0 -72
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
import AppKit
|
|
2
|
+
import CoreGraphics
|
|
3
|
+
import Darwin
|
|
4
|
+
import Foundation
|
|
5
|
+
|
|
6
|
+
private let skyLightPath = "/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight"
|
|
7
|
+
private let hiServicesPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/HIServices.framework/HIServices"
|
|
8
|
+
|
|
9
|
+
private typealias PostToPidFn = @convention(c) (pid_t, UnsafeMutableRawPointer?) -> Void
|
|
10
|
+
private typealias SetWindowLocationFn = @convention(c) (UnsafeMutableRawPointer?, Double, Double) -> Void
|
|
11
|
+
private typealias SetIntegerFieldFn = @convention(c) (UnsafeMutableRawPointer?, UInt32, Int64) -> Void
|
|
12
|
+
private typealias PostEventRecordFn = @convention(c) (UnsafeRawPointer?, UnsafeRawPointer?) -> Int32
|
|
13
|
+
private typealias GetFrontProcessFn = @convention(c) (UnsafeMutableRawPointer?) -> Int32
|
|
14
|
+
private typealias GetProcessForPidFn = @convention(c) (pid_t, UnsafeMutableRawPointer?) -> Int32
|
|
15
|
+
|
|
16
|
+
private struct Request: Decodable {
|
|
17
|
+
let operation: String
|
|
18
|
+
let pid: Int32?
|
|
19
|
+
let process_generation: String?
|
|
20
|
+
let window_id: UInt32?
|
|
21
|
+
let screen_x: Double?
|
|
22
|
+
let screen_y: Double?
|
|
23
|
+
let local_x: Double?
|
|
24
|
+
let local_y: Double?
|
|
25
|
+
let destination_screen_x: Double?
|
|
26
|
+
let destination_screen_y: Double?
|
|
27
|
+
let destination_local_x: Double?
|
|
28
|
+
let destination_local_y: Double?
|
|
29
|
+
let delta_x: Double?
|
|
30
|
+
let delta_y: Double?
|
|
31
|
+
let click_count: Int?
|
|
32
|
+
let text: String?
|
|
33
|
+
let key: String?
|
|
34
|
+
let window_x: Double?
|
|
35
|
+
let window_y: Double?
|
|
36
|
+
let window_width: Double?
|
|
37
|
+
let window_height: Double?
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private struct Response: Encodable {
|
|
41
|
+
let ok: Bool
|
|
42
|
+
let backend: String
|
|
43
|
+
let focus_without_raise: Bool?
|
|
44
|
+
let frontmost_restored: Bool?
|
|
45
|
+
let front_window_validated: Bool?
|
|
46
|
+
let cursor_preserved: Bool?
|
|
47
|
+
let dispatch_started: Bool?
|
|
48
|
+
let error: String?
|
|
49
|
+
let input_transport: String?
|
|
50
|
+
|
|
51
|
+
init(
|
|
52
|
+
ok: Bool,
|
|
53
|
+
backend: String,
|
|
54
|
+
focus_without_raise: Bool?,
|
|
55
|
+
frontmost_restored: Bool?,
|
|
56
|
+
front_window_validated: Bool?,
|
|
57
|
+
cursor_preserved: Bool?,
|
|
58
|
+
dispatch_started: Bool?,
|
|
59
|
+
error: String?,
|
|
60
|
+
input_transport: String? = nil
|
|
61
|
+
) {
|
|
62
|
+
self.ok = ok
|
|
63
|
+
self.backend = backend
|
|
64
|
+
self.focus_without_raise = focus_without_raise
|
|
65
|
+
self.frontmost_restored = frontmost_restored
|
|
66
|
+
self.front_window_validated = front_window_validated
|
|
67
|
+
self.cursor_preserved = cursor_preserved
|
|
68
|
+
self.dispatch_started = dispatch_started
|
|
69
|
+
self.error = error
|
|
70
|
+
self.input_transport = input_transport
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private final class SkyLightSymbols {
|
|
75
|
+
let sky: UnsafeMutableRawPointer
|
|
76
|
+
let hi: UnsafeMutableRawPointer
|
|
77
|
+
let postToPid: PostToPidFn
|
|
78
|
+
let setWindowLocation: SetWindowLocationFn
|
|
79
|
+
let setIntegerField: SetIntegerFieldFn
|
|
80
|
+
let postEventRecord: PostEventRecordFn
|
|
81
|
+
let getFrontProcess: GetFrontProcessFn
|
|
82
|
+
let getProcessForPid: GetProcessForPidFn
|
|
83
|
+
|
|
84
|
+
init?() {
|
|
85
|
+
guard let sky = dlopen(skyLightPath, RTLD_LAZY | RTLD_GLOBAL),
|
|
86
|
+
let hi = dlopen(hiServicesPath, RTLD_LAZY | RTLD_GLOBAL) else { return nil }
|
|
87
|
+
func load<T>(_ handle: UnsafeMutableRawPointer, _ name: String, _: T.Type) -> T? {
|
|
88
|
+
guard let pointer = dlsym(handle, name) else { return nil }
|
|
89
|
+
return unsafeBitCast(pointer, to: T.self)
|
|
90
|
+
}
|
|
91
|
+
guard let postToPid = load(sky, "SLEventPostToPid", PostToPidFn.self),
|
|
92
|
+
let setWindowLocation = load(sky, "CGEventSetWindowLocation", SetWindowLocationFn.self),
|
|
93
|
+
let setIntegerField = load(sky, "SLEventSetIntegerValueField", SetIntegerFieldFn.self),
|
|
94
|
+
let postEventRecord = load(sky, "SLPSPostEventRecordTo", PostEventRecordFn.self),
|
|
95
|
+
let getFrontProcess = load(sky, "_SLPSGetFrontProcess", GetFrontProcessFn.self),
|
|
96
|
+
let getProcessForPid = load(hi, "GetProcessForPID", GetProcessForPidFn.self) else { return nil }
|
|
97
|
+
self.sky = sky
|
|
98
|
+
self.hi = hi
|
|
99
|
+
self.postToPid = postToPid
|
|
100
|
+
self.setWindowLocation = setWindowLocation
|
|
101
|
+
self.setIntegerField = setIntegerField
|
|
102
|
+
self.postEventRecord = postEventRecord
|
|
103
|
+
self.getFrontProcess = getFrontProcess
|
|
104
|
+
self.getProcessForPid = getProcessForPid
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
deinit {
|
|
108
|
+
dlclose(hi)
|
|
109
|
+
dlclose(sky)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private func encode(_ response: Response) {
|
|
114
|
+
let encoder = JSONEncoder()
|
|
115
|
+
guard let data = try? encoder.encode(response), let text = String(data: data, encoding: .utf8) else {
|
|
116
|
+
print("{\"ok\":false,\"backend\":\"skylight-experimental\",\"error\":\"encoding_failed\"}")
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
print(text)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private struct FocusPreparationResult {
|
|
123
|
+
let ok: Bool
|
|
124
|
+
let dispatchStarted: Bool
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private enum PointerTransport {
|
|
128
|
+
case skyLight
|
|
129
|
+
case publicCGEvent
|
|
130
|
+
|
|
131
|
+
var label: String {
|
|
132
|
+
switch self {
|
|
133
|
+
case .skyLight: "skylight-pid"
|
|
134
|
+
case .publicCGEvent: "public-cgevent-pid"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private func pointerTransport(application: NSRunningApplication) -> PointerTransport {
|
|
140
|
+
let bundleId = String(application.bundleIdentifier ?? "").lowercased()
|
|
141
|
+
let skyLightPrefixes = [
|
|
142
|
+
"com.google.chrome", "org.chromium.chromium", "com.microsoft.edgemac",
|
|
143
|
+
"company.thebrowser.browser", "com.brave.browser", "com.vivaldi.vivaldi",
|
|
144
|
+
]
|
|
145
|
+
if skyLightPrefixes.contains(where: { bundleId.hasPrefix($0) }) { return .skyLight }
|
|
146
|
+
if let bundleURL = application.bundleURL,
|
|
147
|
+
let info = Bundle(url: bundleURL)?.infoDictionary {
|
|
148
|
+
let principal = String(info["NSPrincipalClass"] as? String ?? "")
|
|
149
|
+
if principal.contains("AtomApplication") || info["ElectronAsarIntegrity"] != nil { return .skyLight }
|
|
150
|
+
if info["UIApplicationSceneManifest"] != nil || info["UIDeviceFamily"] != nil { return .skyLight }
|
|
151
|
+
}
|
|
152
|
+
return .publicCGEvent
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private func postPointerEvent(_ event: CGEvent, symbols: SkyLightSymbols, pid: pid_t, transport: PointerTransport) {
|
|
156
|
+
switch transport {
|
|
157
|
+
case .skyLight:
|
|
158
|
+
symbols.postToPid(pid, Unmanaged.passUnretained(event).toOpaque())
|
|
159
|
+
case .publicCGEvent:
|
|
160
|
+
event.postToPid(pid)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private func frontmostInstanceStillMatches(_ expected: NSRunningApplication?) -> Bool {
|
|
165
|
+
guard let expected else { return NSWorkspace.shared.frontmostApplication == nil }
|
|
166
|
+
guard !expected.isTerminated, let current = NSWorkspace.shared.frontmostApplication else { return false }
|
|
167
|
+
return current.isEqual(expected)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private func focusContinuationStillAuthorized(
|
|
171
|
+
priorFront: NSRunningApplication,
|
|
172
|
+
targetApplication: NSRunningApplication
|
|
173
|
+
) -> Bool {
|
|
174
|
+
guard !priorFront.isTerminated, !targetApplication.isTerminated else { return false }
|
|
175
|
+
guard let current = NSWorkspace.shared.frontmostApplication else { return true }
|
|
176
|
+
return current.isEqual(priorFront) || current.isEqual(targetApplication)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func skyLightInputStillAuthorized(
|
|
180
|
+
frameworkTransport: PointerTransport,
|
|
181
|
+
priorFront: NSRunningApplication?,
|
|
182
|
+
targetApplication: NSRunningApplication,
|
|
183
|
+
focusDispatched: Bool
|
|
184
|
+
) -> Bool {
|
|
185
|
+
guard frameworkTransport == .skyLight else { return true }
|
|
186
|
+
guard !targetApplication.isTerminated, let current = NSWorkspace.shared.frontmostApplication else { return false }
|
|
187
|
+
if !focusDispatched { return current.isEqual(targetApplication) }
|
|
188
|
+
guard let priorFront, !priorFront.isTerminated else { return current.isEqual(targetApplication) }
|
|
189
|
+
return current.isEqual(priorFront) || current.isEqual(targetApplication)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private func inputBoundaryStillValid(
|
|
193
|
+
pid: pid_t,
|
|
194
|
+
application: NSRunningApplication,
|
|
195
|
+
windowId: UInt32,
|
|
196
|
+
expected: CGRect,
|
|
197
|
+
frameworkTransport: PointerTransport,
|
|
198
|
+
priorFront: NSRunningApplication?,
|
|
199
|
+
focusDispatched: Bool
|
|
200
|
+
) -> Bool {
|
|
201
|
+
frontWindowStillMatches(pid: pid, application: application, windowId: windowId, expected: expected)
|
|
202
|
+
&& skyLightInputStillAuthorized(
|
|
203
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
204
|
+
targetApplication: application, focusDispatched: focusDispatched
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private func prepareFocusIfNeeded(
|
|
209
|
+
_ symbols: SkyLightSymbols,
|
|
210
|
+
pid: pid_t,
|
|
211
|
+
windowId: UInt32,
|
|
212
|
+
frameworkTransport: PointerTransport,
|
|
213
|
+
priorFront: NSRunningApplication?,
|
|
214
|
+
targetApplication: NSRunningApplication
|
|
215
|
+
) -> FocusPreparationResult {
|
|
216
|
+
guard frameworkTransport == .skyLight else { return FocusPreparationResult(ok: true, dispatchStarted: false) }
|
|
217
|
+
guard frontmostInstanceStillMatches(priorFront) else {
|
|
218
|
+
return FocusPreparationResult(ok: false, dispatchStarted: false)
|
|
219
|
+
}
|
|
220
|
+
if let priorFront, priorFront.isEqual(targetApplication) {
|
|
221
|
+
return FocusPreparationResult(ok: true, dispatchStarted: false)
|
|
222
|
+
}
|
|
223
|
+
guard let priorFront, !priorFront.isTerminated else {
|
|
224
|
+
return FocusPreparationResult(ok: false, dispatchStarted: false)
|
|
225
|
+
}
|
|
226
|
+
return focusWithoutRaise(
|
|
227
|
+
symbols, pid: pid, windowId: windowId, priorFront: priorFront, targetApplication: targetApplication
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private func focusWithoutRaise(
|
|
232
|
+
_ symbols: SkyLightSymbols,
|
|
233
|
+
pid: pid_t,
|
|
234
|
+
windowId: UInt32,
|
|
235
|
+
priorFront: NSRunningApplication,
|
|
236
|
+
targetApplication: NSRunningApplication
|
|
237
|
+
) -> FocusPreparationResult {
|
|
238
|
+
guard processInstanceStillMatches(pid: pid, application: targetApplication),
|
|
239
|
+
frontmostInstanceStillMatches(priorFront), !priorFront.isTerminated else {
|
|
240
|
+
return FocusPreparationResult(ok: false, dispatchStarted: false)
|
|
241
|
+
}
|
|
242
|
+
var previous = [UInt8](repeating: 0, count: 8)
|
|
243
|
+
var expectedPrevious = [UInt8](repeating: 0, count: 8)
|
|
244
|
+
var target = [UInt8](repeating: 0, count: 8)
|
|
245
|
+
let previousResolved = previous.withUnsafeMutableBytes { symbols.getFrontProcess($0.baseAddress) == 0 }
|
|
246
|
+
let expectedPreviousResolved = expectedPrevious.withUnsafeMutableBytes {
|
|
247
|
+
symbols.getProcessForPid(priorFront.processIdentifier, $0.baseAddress) == 0
|
|
248
|
+
}
|
|
249
|
+
let targetResolved = target.withUnsafeMutableBytes { symbols.getProcessForPid(pid, $0.baseAddress) == 0 }
|
|
250
|
+
guard previousResolved && expectedPreviousResolved && targetResolved,
|
|
251
|
+
previous == expectedPrevious,
|
|
252
|
+
processInstanceStillMatches(pid: pid, application: targetApplication),
|
|
253
|
+
frontmostInstanceStillMatches(priorFront), !priorFront.isTerminated else {
|
|
254
|
+
return FocusPreparationResult(ok: false, dispatchStarted: false)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
var record = [UInt8](repeating: 0, count: 0xF8)
|
|
258
|
+
record[0x04] = 0xF8
|
|
259
|
+
record[0x08] = 0x0D
|
|
260
|
+
let littleEndian = windowId.littleEndian
|
|
261
|
+
withUnsafeBytes(of: littleEndian) { bytes in
|
|
262
|
+
for index in 0..<4 { record[0x3C + index] = bytes[index] }
|
|
263
|
+
}
|
|
264
|
+
var lastHopPrevious = [UInt8](repeating: 0, count: 8)
|
|
265
|
+
let lastHopPreviousResolved = lastHopPrevious.withUnsafeMutableBytes { symbols.getFrontProcess($0.baseAddress) == 0 }
|
|
266
|
+
guard lastHopPreviousResolved, lastHopPrevious == expectedPrevious,
|
|
267
|
+
processInstanceStillMatches(pid: pid, application: targetApplication),
|
|
268
|
+
frontmostInstanceStillMatches(priorFront), !priorFront.isTerminated else {
|
|
269
|
+
return FocusPreparationResult(ok: false, dispatchStarted: false)
|
|
270
|
+
}
|
|
271
|
+
record[0x8A] = 0x02
|
|
272
|
+
let defocused = lastHopPrevious.withUnsafeBytes { psn in
|
|
273
|
+
record.withUnsafeBytes { body in symbols.postEventRecord(psn.baseAddress, body.baseAddress) == 0 }
|
|
274
|
+
}
|
|
275
|
+
guard defocused else { return FocusPreparationResult(ok: false, dispatchStarted: true) }
|
|
276
|
+
|
|
277
|
+
var lastHopTarget = [UInt8](repeating: 0, count: 8)
|
|
278
|
+
let lastHopTargetResolved = lastHopTarget.withUnsafeMutableBytes { symbols.getProcessForPid(pid, $0.baseAddress) == 0 }
|
|
279
|
+
guard lastHopTargetResolved, lastHopTarget == target,
|
|
280
|
+
processInstanceStillMatches(pid: pid, application: targetApplication),
|
|
281
|
+
focusContinuationStillAuthorized(priorFront: priorFront, targetApplication: targetApplication) else {
|
|
282
|
+
return FocusPreparationResult(ok: false, dispatchStarted: true)
|
|
283
|
+
}
|
|
284
|
+
record[0x8A] = 0x01
|
|
285
|
+
let focused = lastHopTarget.withUnsafeBytes { psn in
|
|
286
|
+
record.withUnsafeBytes { body in symbols.postEventRecord(psn.baseAddress, body.baseAddress) == 0 }
|
|
287
|
+
}
|
|
288
|
+
return FocusPreparationResult(ok: focused, dispatchStarted: true)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private func stamp(
|
|
292
|
+
_ event: CGEvent,
|
|
293
|
+
symbols: SkyLightSymbols,
|
|
294
|
+
pid: pid_t,
|
|
295
|
+
windowId: UInt32,
|
|
296
|
+
localX: Double,
|
|
297
|
+
localY: Double,
|
|
298
|
+
clickState: Int64,
|
|
299
|
+
phase: Int64,
|
|
300
|
+
clickGroup: Int64,
|
|
301
|
+
subtype: Int64 = 3
|
|
302
|
+
) {
|
|
303
|
+
let pointer = Unmanaged.passUnretained(event).toOpaque()
|
|
304
|
+
symbols.setIntegerField(pointer, 0, phase)
|
|
305
|
+
symbols.setIntegerField(pointer, 1, clickState)
|
|
306
|
+
symbols.setIntegerField(pointer, 3, 0)
|
|
307
|
+
symbols.setIntegerField(pointer, 7, subtype)
|
|
308
|
+
symbols.setIntegerField(pointer, 40, Int64(pid))
|
|
309
|
+
symbols.setIntegerField(pointer, 51, Int64(windowId))
|
|
310
|
+
symbols.setIntegerField(pointer, 91, Int64(windowId))
|
|
311
|
+
symbols.setIntegerField(pointer, 92, Int64(windowId))
|
|
312
|
+
symbols.setIntegerField(pointer, 58, clickGroup)
|
|
313
|
+
symbols.setWindowLocation(pointer, localX, localY)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private func sendMouseEvent(
|
|
317
|
+
_ type: CGEventType,
|
|
318
|
+
symbols: SkyLightSymbols,
|
|
319
|
+
source: CGEventSource,
|
|
320
|
+
pid: pid_t,
|
|
321
|
+
windowId: UInt32,
|
|
322
|
+
screenX: Double,
|
|
323
|
+
screenY: Double,
|
|
324
|
+
localX: Double,
|
|
325
|
+
localY: Double,
|
|
326
|
+
clickState: Int64,
|
|
327
|
+
phase: Int64,
|
|
328
|
+
clickGroup: Int64,
|
|
329
|
+
transport: PointerTransport,
|
|
330
|
+
subtype: Int64 = 3
|
|
331
|
+
) throws {
|
|
332
|
+
guard let event = CGEvent(
|
|
333
|
+
mouseEventSource: source,
|
|
334
|
+
mouseType: type,
|
|
335
|
+
mouseCursorPosition: CGPoint(x: screenX, y: screenY),
|
|
336
|
+
mouseButton: .left
|
|
337
|
+
) else { throw NSError(domain: "MachineBridgeBackgroundInput", code: 1) }
|
|
338
|
+
stamp(event, symbols: symbols, pid: pid, windowId: windowId, localX: localX, localY: localY,
|
|
339
|
+
clickState: clickState, phase: phase, clickGroup: clickGroup, subtype: subtype)
|
|
340
|
+
postPointerEvent(event, symbols: symbols, pid: pid, transport: transport)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private func sendScrollEvent(
|
|
344
|
+
symbols: SkyLightSymbols,
|
|
345
|
+
source: CGEventSource,
|
|
346
|
+
pid: pid_t,
|
|
347
|
+
windowId: UInt32,
|
|
348
|
+
screenX: Double,
|
|
349
|
+
screenY: Double,
|
|
350
|
+
localX: Double,
|
|
351
|
+
localY: Double,
|
|
352
|
+
deltaX: Int32,
|
|
353
|
+
deltaY: Int32,
|
|
354
|
+
transport: PointerTransport
|
|
355
|
+
) throws {
|
|
356
|
+
guard let event = CGEvent(
|
|
357
|
+
scrollWheelEvent2Source: source,
|
|
358
|
+
units: .pixel,
|
|
359
|
+
wheelCount: 2,
|
|
360
|
+
wheel1: deltaY,
|
|
361
|
+
wheel2: deltaX,
|
|
362
|
+
wheel3: 0
|
|
363
|
+
) else { throw NSError(domain: "MachineBridgeBackgroundInput", code: 6) }
|
|
364
|
+
event.location = CGPoint(x: screenX, y: screenY)
|
|
365
|
+
let pointer = Unmanaged.passUnretained(event).toOpaque()
|
|
366
|
+
symbols.setIntegerField(pointer, 40, Int64(pid))
|
|
367
|
+
symbols.setIntegerField(pointer, 51, Int64(windowId))
|
|
368
|
+
symbols.setIntegerField(pointer, 91, Int64(windowId))
|
|
369
|
+
symbols.setIntegerField(pointer, 92, Int64(windowId))
|
|
370
|
+
symbols.setWindowLocation(pointer, localX, localY)
|
|
371
|
+
postPointerEvent(event, symbols: symbols, pid: pid, transport: transport)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private func near(_ left: Double, _ right: Double, tolerance: Double = 1.0) -> Bool {
|
|
375
|
+
left.isFinite && right.isFinite && abs(left - right) <= tolerance
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private func processStartSignature(pid: pid_t) -> String? {
|
|
379
|
+
var info = proc_bsdinfo()
|
|
380
|
+
let size = MemoryLayout<proc_bsdinfo>.size
|
|
381
|
+
let copied = withUnsafeMutablePointer(to: &info) { pointer in
|
|
382
|
+
proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, pointer, Int32(size))
|
|
383
|
+
}
|
|
384
|
+
guard copied == size, info.pbi_pid == UInt32(pid) else { return nil }
|
|
385
|
+
var start = Data()
|
|
386
|
+
withUnsafeBytes(of: info.pbi_start_tvsec) { start.append(contentsOf: $0) }
|
|
387
|
+
withUnsafeBytes(of: info.pbi_start_tvusec) { start.append(contentsOf: $0) }
|
|
388
|
+
guard start.count == 16 else { return nil }
|
|
389
|
+
return start.base64EncodedString()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private func processGenerationToken(pid: pid_t) -> String? {
|
|
393
|
+
guard let signature = processStartSignature(pid: pid) else { return nil }
|
|
394
|
+
return "proc:\(signature)"
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private func processInstanceStillMatches(pid: pid_t, application: NSRunningApplication) -> Bool {
|
|
398
|
+
guard !application.isTerminated,
|
|
399
|
+
let current = NSRunningApplication(processIdentifier: pid), !current.isTerminated else { return false }
|
|
400
|
+
return application.isEqual(current)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private func runningApplication(pid: pid_t, expectedGeneration: String) -> NSRunningApplication? {
|
|
404
|
+
guard !expectedGeneration.isEmpty, expectedGeneration.count <= 2048,
|
|
405
|
+
!expectedGeneration.contains("\n"), !expectedGeneration.contains("\r"), !expectedGeneration.contains("\0"),
|
|
406
|
+
let application = NSRunningApplication(processIdentifier: pid), !application.isTerminated,
|
|
407
|
+
processGenerationToken(pid: pid) == expectedGeneration,
|
|
408
|
+
processInstanceStillMatches(pid: pid, application: application) else { return nil }
|
|
409
|
+
return application
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private func keyboardFailure(dispatchStarted: Bool, error: String) -> Response {
|
|
413
|
+
Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
414
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
415
|
+
dispatch_started: dispatchStarted,
|
|
416
|
+
error: dispatchStarted ? "dispatch_outcome_unknown" : error,
|
|
417
|
+
input_transport: "public-cgevent-pid")
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
private func unicodeKeystroke(_ request: Request) -> Response {
|
|
421
|
+
guard let rawPid = request.pid, rawPid > 0,
|
|
422
|
+
let processGeneration = request.process_generation,
|
|
423
|
+
let text = request.text, !text.isEmpty, !text.contains("\0") else {
|
|
424
|
+
return keyboardFailure(dispatchStarted: false, error: "invalid_request_before_dispatch")
|
|
425
|
+
}
|
|
426
|
+
let units = Array(text.utf16)
|
|
427
|
+
guard units.count <= 4_000 else {
|
|
428
|
+
return keyboardFailure(dispatchStarted: false, error: "invalid_request_before_dispatch")
|
|
429
|
+
}
|
|
430
|
+
let pid = pid_t(rawPid)
|
|
431
|
+
guard let application = runningApplication(pid: pid, expectedGeneration: processGeneration) else {
|
|
432
|
+
return keyboardFailure(dispatchStarted: false, error: "process_generation_changed_before_dispatch")
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
var start = 0
|
|
436
|
+
var dispatchStarted = false
|
|
437
|
+
while start < units.count {
|
|
438
|
+
var end = min(start + 20, units.count)
|
|
439
|
+
if end < units.count,
|
|
440
|
+
units[end - 1] >= 0xD800, units[end - 1] <= 0xDBFF,
|
|
441
|
+
units[end] >= 0xDC00, units[end] <= 0xDFFF {
|
|
442
|
+
end -= 1
|
|
443
|
+
}
|
|
444
|
+
guard end > start, processInstanceStillMatches(pid: pid, application: application),
|
|
445
|
+
let down = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: true),
|
|
446
|
+
let up = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: false) else {
|
|
447
|
+
return keyboardFailure(dispatchStarted: dispatchStarted, error: "input_unavailable_before_dispatch")
|
|
448
|
+
}
|
|
449
|
+
let chunk = Array(units[start..<end])
|
|
450
|
+
chunk.withUnsafeBufferPointer { buffer in
|
|
451
|
+
guard let base = buffer.baseAddress else { return }
|
|
452
|
+
down.keyboardSetUnicodeString(stringLength: buffer.count, unicodeString: base)
|
|
453
|
+
up.keyboardSetUnicodeString(stringLength: buffer.count, unicodeString: base)
|
|
454
|
+
}
|
|
455
|
+
down.postToPid(pid)
|
|
456
|
+
dispatchStarted = true
|
|
457
|
+
usleep(5_000)
|
|
458
|
+
guard processInstanceStillMatches(pid: pid, application: application) else {
|
|
459
|
+
return keyboardFailure(dispatchStarted: true, error: "dispatch_outcome_unknown")
|
|
460
|
+
}
|
|
461
|
+
up.postToPid(pid)
|
|
462
|
+
usleep(5_000)
|
|
463
|
+
start = end
|
|
464
|
+
}
|
|
465
|
+
return Response(ok: true, backend: "skylight-experimental", focus_without_raise: nil,
|
|
466
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
467
|
+
dispatch_started: true, error: nil, input_transport: "public-cgevent-pid")
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private func specialKeySpec(_ value: String) -> (keyCode: CGKeyCode, shifted: Bool)? {
|
|
471
|
+
let shifted = value.hasPrefix("Shift+")
|
|
472
|
+
let key = shifted ? String(value.dropFirst(6)) : value
|
|
473
|
+
let keyCodes: [String: CGKeyCode] = [
|
|
474
|
+
"Enter": 0x24, "Tab": 0x30, "Space": 0x31, "Backspace": 0x33, "Escape": 0x35,
|
|
475
|
+
"Home": 0x73, "PageUp": 0x74, "Delete": 0x75, "End": 0x77, "PageDown": 0x79,
|
|
476
|
+
"ArrowLeft": 0x7B, "ArrowRight": 0x7C, "ArrowDown": 0x7D, "ArrowUp": 0x7E,
|
|
477
|
+
]
|
|
478
|
+
guard let keyCode = keyCodes[key] else { return nil }
|
|
479
|
+
return (keyCode, shifted)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private func specialKeyPress(_ request: Request) -> Response {
|
|
483
|
+
guard let rawPid = request.pid, rawPid > 0,
|
|
484
|
+
let processGeneration = request.process_generation,
|
|
485
|
+
let key = request.key, let spec = specialKeySpec(key) else {
|
|
486
|
+
return keyboardFailure(dispatchStarted: false, error: "invalid_request_before_dispatch")
|
|
487
|
+
}
|
|
488
|
+
let pid = pid_t(rawPid)
|
|
489
|
+
guard let application = runningApplication(pid: pid, expectedGeneration: processGeneration) else {
|
|
490
|
+
return keyboardFailure(dispatchStarted: false, error: "process_generation_changed_before_dispatch")
|
|
491
|
+
}
|
|
492
|
+
guard processInstanceStillMatches(pid: pid, application: application),
|
|
493
|
+
let down = CGEvent(keyboardEventSource: nil, virtualKey: spec.keyCode, keyDown: true),
|
|
494
|
+
let up = CGEvent(keyboardEventSource: nil, virtualKey: spec.keyCode, keyDown: false) else {
|
|
495
|
+
return keyboardFailure(dispatchStarted: false, error: "input_unavailable_before_dispatch")
|
|
496
|
+
}
|
|
497
|
+
if spec.shifted {
|
|
498
|
+
down.flags = .maskShift
|
|
499
|
+
up.flags = .maskShift
|
|
500
|
+
}
|
|
501
|
+
down.postToPid(pid)
|
|
502
|
+
usleep(5_000)
|
|
503
|
+
guard processInstanceStillMatches(pid: pid, application: application) else {
|
|
504
|
+
return keyboardFailure(dispatchStarted: true, error: "dispatch_outcome_unknown")
|
|
505
|
+
}
|
|
506
|
+
up.postToPid(pid)
|
|
507
|
+
usleep(5_000)
|
|
508
|
+
return Response(ok: true, backend: "skylight-experimental", focus_without_raise: nil,
|
|
509
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
510
|
+
dispatch_started: true, error: nil, input_transport: "public-cgevent-pid")
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private func frontWindowStillMatches(
|
|
514
|
+
pid: pid_t,
|
|
515
|
+
application: NSRunningApplication,
|
|
516
|
+
windowId: UInt32,
|
|
517
|
+
expected: CGRect
|
|
518
|
+
) -> Bool {
|
|
519
|
+
guard processInstanceStillMatches(pid: pid, application: application),
|
|
520
|
+
let raw = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID),
|
|
521
|
+
let windows = raw as? [[String: Any]] else { return false }
|
|
522
|
+
for info in windows {
|
|
523
|
+
let ownerPid = (info[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value ?? 0
|
|
524
|
+
let layer = (info[kCGWindowLayer as String] as? NSNumber)?.intValue ?? -1
|
|
525
|
+
let alpha = (info[kCGWindowAlpha as String] as? NSNumber)?.doubleValue ?? 1
|
|
526
|
+
guard ownerPid == pid, layer == 0, alpha > 0 else { continue }
|
|
527
|
+
let candidateId = (info[kCGWindowNumber as String] as? NSNumber)?.uint32Value ?? 0
|
|
528
|
+
guard let boundsObject = info[kCGWindowBounds as String] as? NSDictionary,
|
|
529
|
+
let candidateBounds = CGRect(dictionaryRepresentation: boundsObject) else { return false }
|
|
530
|
+
return candidateId == windowId
|
|
531
|
+
&& near(candidateBounds.origin.x, expected.origin.x)
|
|
532
|
+
&& near(candidateBounds.origin.y, expected.origin.y)
|
|
533
|
+
&& near(candidateBounds.size.width, expected.size.width)
|
|
534
|
+
&& near(candidateBounds.size.height, expected.size.height)
|
|
535
|
+
}
|
|
536
|
+
return false
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
private func cursorLocation() -> CGPoint? {
|
|
540
|
+
NSEvent.mouseLocation
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private func restoreFrontmostIfNeeded(
|
|
544
|
+
priorFront: NSRunningApplication?, targetApplication: NSRunningApplication, focusDispatched: Bool
|
|
545
|
+
) -> Bool {
|
|
546
|
+
guard focusDispatched,
|
|
547
|
+
let priorFront, !priorFront.isEqual(targetApplication), !priorFront.isTerminated,
|
|
548
|
+
let current = NSWorkspace.shared.frontmostApplication, current.isEqual(targetApplication) else { return false }
|
|
549
|
+
return priorFront.activate(options: [])
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private func click(_ request: Request, symbols: SkyLightSymbols) -> Response {
|
|
553
|
+
guard let rawPid = request.pid, rawPid > 0,
|
|
554
|
+
let processGeneration = request.process_generation,
|
|
555
|
+
let windowId = request.window_id, windowId > 0,
|
|
556
|
+
let screenX = request.screen_x, screenX.isFinite,
|
|
557
|
+
let screenY = request.screen_y, screenY.isFinite,
|
|
558
|
+
let localX = request.local_x, localX.isFinite,
|
|
559
|
+
let localY = request.local_y, localY.isFinite,
|
|
560
|
+
let windowX = request.window_x, windowX.isFinite,
|
|
561
|
+
let windowY = request.window_y, windowY.isFinite,
|
|
562
|
+
let windowWidth = request.window_width, windowWidth > 0, windowWidth.isFinite,
|
|
563
|
+
let windowHeight = request.window_height, windowHeight > 0, windowHeight.isFinite,
|
|
564
|
+
localX >= 0, localY >= 0, localX < windowWidth, localY < windowHeight,
|
|
565
|
+
(request.click_count ?? 1) >= 1, (request.click_count ?? 1) <= 2 else {
|
|
566
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
567
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
568
|
+
dispatch_started: false, error: "invalid_request_before_dispatch")
|
|
569
|
+
}
|
|
570
|
+
let pid = pid_t(rawPid)
|
|
571
|
+
guard let targetApplication = runningApplication(pid: pid, expectedGeneration: processGeneration) else {
|
|
572
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
573
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
574
|
+
dispatch_started: false, error: "process_generation_changed_before_dispatch")
|
|
575
|
+
}
|
|
576
|
+
let clickCount = request.click_count ?? 1
|
|
577
|
+
let transport = pointerTransport(application: targetApplication)
|
|
578
|
+
let frameworkTransport = transport
|
|
579
|
+
let clickSubtype: Int64 = switch transport {
|
|
580
|
+
case .skyLight: 3
|
|
581
|
+
case .publicCGEvent: 0
|
|
582
|
+
}
|
|
583
|
+
let expectedBounds = CGRect(x: windowX, y: windowY, width: windowWidth, height: windowHeight)
|
|
584
|
+
guard frontWindowStillMatches(pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds) else {
|
|
585
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
586
|
+
frontmost_restored: nil, front_window_validated: false, cursor_preserved: nil,
|
|
587
|
+
dispatch_started: false, error: "front_window_changed_before_dispatch")
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
let cursorBefore = cursorLocation()
|
|
591
|
+
let priorFront = NSWorkspace.shared.frontmostApplication
|
|
592
|
+
let focusPreparation = prepareFocusIfNeeded(
|
|
593
|
+
symbols, pid: pid, windowId: windowId, frameworkTransport: transport,
|
|
594
|
+
priorFront: priorFront, targetApplication: targetApplication
|
|
595
|
+
)
|
|
596
|
+
guard focusPreparation.ok else {
|
|
597
|
+
let restored = restoreFrontmostIfNeeded(
|
|
598
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
599
|
+
)
|
|
600
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
601
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
602
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
603
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "focus_without_raise_unavailable_before_dispatch")
|
|
604
|
+
}
|
|
605
|
+
if focusPreparation.dispatchStarted { usleep(50_000) }
|
|
606
|
+
guard skyLightInputStillAuthorized(
|
|
607
|
+
frameworkTransport: transport, priorFront: priorFront,
|
|
608
|
+
targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
609
|
+
) else {
|
|
610
|
+
let restored = restoreFrontmostIfNeeded(
|
|
611
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
612
|
+
)
|
|
613
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
614
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
615
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
616
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "frontmost_changed_before_input")
|
|
617
|
+
}
|
|
618
|
+
guard let source = CGEventSource(stateID: .hidSystemState) else {
|
|
619
|
+
let restored = restoreFrontmostIfNeeded(
|
|
620
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
621
|
+
)
|
|
622
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
623
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
624
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
625
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "event_source_unavailable_before_dispatch")
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
let clickGroup = Int64(UInt32.random(in: 1...UInt32.max))
|
|
629
|
+
var pointerDispatchStarted = false
|
|
630
|
+
var downSent = false
|
|
631
|
+
var upSent = true
|
|
632
|
+
var activeClickState: Int64 = 0
|
|
633
|
+
do {
|
|
634
|
+
guard inputBoundaryStillValid(
|
|
635
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
636
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
637
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
638
|
+
) else {
|
|
639
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 11)
|
|
640
|
+
}
|
|
641
|
+
try sendMouseEvent(.mouseMoved, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
642
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
643
|
+
clickState: 0, phase: 2, clickGroup: clickGroup, transport: transport, subtype: clickSubtype)
|
|
644
|
+
pointerDispatchStarted = true
|
|
645
|
+
usleep(15_000)
|
|
646
|
+
for clickState in 1...clickCount {
|
|
647
|
+
activeClickState = Int64(clickState)
|
|
648
|
+
guard inputBoundaryStillValid(
|
|
649
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
650
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
651
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
652
|
+
) else {
|
|
653
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 12)
|
|
654
|
+
}
|
|
655
|
+
try sendMouseEvent(.leftMouseDown, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
656
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
657
|
+
clickState: activeClickState, phase: 3, clickGroup: clickGroup, transport: transport, subtype: clickSubtype)
|
|
658
|
+
pointerDispatchStarted = true
|
|
659
|
+
downSent = true
|
|
660
|
+
upSent = false
|
|
661
|
+
usleep(28_000)
|
|
662
|
+
guard inputBoundaryStillValid(
|
|
663
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
664
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
665
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
666
|
+
) else {
|
|
667
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 13)
|
|
668
|
+
}
|
|
669
|
+
try sendMouseEvent(.leftMouseUp, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
670
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
671
|
+
clickState: activeClickState, phase: 3, clickGroup: clickGroup, transport: transport, subtype: clickSubtype)
|
|
672
|
+
pointerDispatchStarted = true
|
|
673
|
+
upSent = true
|
|
674
|
+
downSent = false
|
|
675
|
+
if clickState < clickCount { usleep(55_000) }
|
|
676
|
+
}
|
|
677
|
+
} catch {
|
|
678
|
+
if downSent && !upSent && processInstanceStillMatches(pid: pid, application: targetApplication) {
|
|
679
|
+
try? sendMouseEvent(.leftMouseUp, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
680
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
681
|
+
clickState: activeClickState, phase: 3, clickGroup: clickGroup, transport: transport, subtype: clickSubtype)
|
|
682
|
+
}
|
|
683
|
+
let dispatchStarted = focusPreparation.dispatchStarted || pointerDispatchStarted
|
|
684
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
685
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
686
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
687
|
+
dispatch_started: dispatchStarted,
|
|
688
|
+
error: dispatchStarted ? "dispatch_outcome_unknown" : "input_unavailable_before_dispatch")
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
usleep(50_000)
|
|
692
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
693
|
+
let cursorAfter = cursorLocation()
|
|
694
|
+
let cursorPreserved: Bool? = {
|
|
695
|
+
guard let cursorBefore, let cursorAfter else { return nil }
|
|
696
|
+
return hypot(cursorBefore.x - cursorAfter.x, cursorBefore.y - cursorAfter.y) <= 0.5
|
|
697
|
+
}()
|
|
698
|
+
return Response(ok: true, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
699
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: cursorPreserved,
|
|
700
|
+
dispatch_started: true, error: nil, input_transport: transport.label)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
private func drag(_ request: Request, symbols: SkyLightSymbols) -> Response {
|
|
704
|
+
guard let rawPid = request.pid, rawPid > 0,
|
|
705
|
+
let processGeneration = request.process_generation,
|
|
706
|
+
let windowId = request.window_id, windowId > 0,
|
|
707
|
+
let screenX = request.screen_x, screenX.isFinite,
|
|
708
|
+
let screenY = request.screen_y, screenY.isFinite,
|
|
709
|
+
let localX = request.local_x, localX.isFinite,
|
|
710
|
+
let localY = request.local_y, localY.isFinite,
|
|
711
|
+
let destinationScreenX = request.destination_screen_x, destinationScreenX.isFinite,
|
|
712
|
+
let destinationScreenY = request.destination_screen_y, destinationScreenY.isFinite,
|
|
713
|
+
let destinationLocalX = request.destination_local_x, destinationLocalX.isFinite,
|
|
714
|
+
let destinationLocalY = request.destination_local_y, destinationLocalY.isFinite,
|
|
715
|
+
let windowX = request.window_x, windowX.isFinite,
|
|
716
|
+
let windowY = request.window_y, windowY.isFinite,
|
|
717
|
+
let windowWidth = request.window_width, windowWidth > 0, windowWidth.isFinite,
|
|
718
|
+
let windowHeight = request.window_height, windowHeight > 0, windowHeight.isFinite,
|
|
719
|
+
localX >= 0, localY >= 0, destinationLocalX >= 0, destinationLocalY >= 0,
|
|
720
|
+
localX < windowWidth, localY < windowHeight,
|
|
721
|
+
destinationLocalX < windowWidth, destinationLocalY < windowHeight else {
|
|
722
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
723
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
724
|
+
dispatch_started: false, error: "invalid_request_before_dispatch")
|
|
725
|
+
}
|
|
726
|
+
let pid = pid_t(rawPid)
|
|
727
|
+
guard let targetApplication = runningApplication(pid: pid, expectedGeneration: processGeneration) else {
|
|
728
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
729
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
730
|
+
dispatch_started: false, error: "process_generation_changed_before_dispatch")
|
|
731
|
+
}
|
|
732
|
+
let transport = pointerTransport(application: targetApplication)
|
|
733
|
+
let frameworkTransport = transport
|
|
734
|
+
let expectedBounds = CGRect(x: windowX, y: windowY, width: windowWidth, height: windowHeight)
|
|
735
|
+
guard frontWindowStillMatches(pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds) else {
|
|
736
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
737
|
+
frontmost_restored: nil, front_window_validated: false, cursor_preserved: nil,
|
|
738
|
+
dispatch_started: false, error: "front_window_changed_before_dispatch")
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
let cursorBefore = cursorLocation()
|
|
742
|
+
let priorFront = NSWorkspace.shared.frontmostApplication
|
|
743
|
+
let focusPreparation = prepareFocusIfNeeded(
|
|
744
|
+
symbols, pid: pid, windowId: windowId, frameworkTransport: transport,
|
|
745
|
+
priorFront: priorFront, targetApplication: targetApplication
|
|
746
|
+
)
|
|
747
|
+
guard focusPreparation.ok else {
|
|
748
|
+
let restored = restoreFrontmostIfNeeded(
|
|
749
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
750
|
+
)
|
|
751
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
752
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
753
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
754
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "focus_without_raise_unavailable_before_dispatch")
|
|
755
|
+
}
|
|
756
|
+
if focusPreparation.dispatchStarted { usleep(50_000) }
|
|
757
|
+
guard skyLightInputStillAuthorized(
|
|
758
|
+
frameworkTransport: transport, priorFront: priorFront,
|
|
759
|
+
targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
760
|
+
) else {
|
|
761
|
+
let restored = restoreFrontmostIfNeeded(
|
|
762
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
763
|
+
)
|
|
764
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
765
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
766
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
767
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "frontmost_changed_before_input")
|
|
768
|
+
}
|
|
769
|
+
guard let source = CGEventSource(stateID: .hidSystemState) else {
|
|
770
|
+
let restored = restoreFrontmostIfNeeded(
|
|
771
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
772
|
+
)
|
|
773
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
774
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
775
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
776
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "event_source_unavailable_before_dispatch")
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
let clickGroup = Int64(UInt32.random(in: 1...UInt32.max))
|
|
780
|
+
var pointerDispatchStarted = false
|
|
781
|
+
var downSent = false
|
|
782
|
+
var upSent = false
|
|
783
|
+
var lastScreenX = screenX
|
|
784
|
+
var lastScreenY = screenY
|
|
785
|
+
var lastLocalX = localX
|
|
786
|
+
var lastLocalY = localY
|
|
787
|
+
do {
|
|
788
|
+
guard inputBoundaryStillValid(
|
|
789
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
790
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
791
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
792
|
+
) else {
|
|
793
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 2)
|
|
794
|
+
}
|
|
795
|
+
try sendMouseEvent(.mouseMoved, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
796
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
797
|
+
clickState: 0, phase: 2, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
798
|
+
pointerDispatchStarted = true
|
|
799
|
+
usleep(15_000)
|
|
800
|
+
guard inputBoundaryStillValid(
|
|
801
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
802
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
803
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
804
|
+
) else {
|
|
805
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 3)
|
|
806
|
+
}
|
|
807
|
+
try sendMouseEvent(.leftMouseDown, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
808
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
809
|
+
clickState: 1, phase: 3, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
810
|
+
pointerDispatchStarted = true
|
|
811
|
+
downSent = true
|
|
812
|
+
usleep(16_000)
|
|
813
|
+
|
|
814
|
+
let steps = 8
|
|
815
|
+
for step in 1...steps {
|
|
816
|
+
guard inputBoundaryStillValid(
|
|
817
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
818
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
819
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
820
|
+
) else {
|
|
821
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 4)
|
|
822
|
+
}
|
|
823
|
+
let ratio = Double(step) / Double(steps)
|
|
824
|
+
lastScreenX = screenX + (destinationScreenX - screenX) * ratio
|
|
825
|
+
lastScreenY = screenY + (destinationScreenY - screenY) * ratio
|
|
826
|
+
lastLocalX = localX + (destinationLocalX - localX) * ratio
|
|
827
|
+
lastLocalY = localY + (destinationLocalY - localY) * ratio
|
|
828
|
+
try sendMouseEvent(.leftMouseDragged, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
829
|
+
screenX: lastScreenX, screenY: lastScreenY, localX: lastLocalX, localY: lastLocalY,
|
|
830
|
+
clickState: 1, phase: 3, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
831
|
+
pointerDispatchStarted = true
|
|
832
|
+
usleep(16_000)
|
|
833
|
+
}
|
|
834
|
+
guard inputBoundaryStillValid(
|
|
835
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
836
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
837
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
838
|
+
) else {
|
|
839
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 5)
|
|
840
|
+
}
|
|
841
|
+
usleep(30_000)
|
|
842
|
+
try sendMouseEvent(.leftMouseUp, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
843
|
+
screenX: destinationScreenX, screenY: destinationScreenY,
|
|
844
|
+
localX: destinationLocalX, localY: destinationLocalY,
|
|
845
|
+
clickState: 1, phase: 3, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
846
|
+
pointerDispatchStarted = true
|
|
847
|
+
upSent = true
|
|
848
|
+
} catch {
|
|
849
|
+
if downSent && !upSent && processInstanceStillMatches(pid: pid, application: targetApplication) {
|
|
850
|
+
try? sendMouseEvent(.leftMouseUp, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
851
|
+
screenX: lastScreenX, screenY: lastScreenY, localX: lastLocalX, localY: lastLocalY,
|
|
852
|
+
clickState: 1, phase: 3, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
853
|
+
}
|
|
854
|
+
let dispatchStarted = focusPreparation.dispatchStarted || pointerDispatchStarted
|
|
855
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
856
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
857
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
858
|
+
dispatch_started: dispatchStarted,
|
|
859
|
+
error: dispatchStarted ? "dispatch_outcome_unknown" : "input_unavailable_before_dispatch")
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
usleep(50_000)
|
|
863
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
864
|
+
let cursorAfter = cursorLocation()
|
|
865
|
+
let cursorPreserved: Bool? = {
|
|
866
|
+
guard let cursorBefore, let cursorAfter else { return nil }
|
|
867
|
+
return hypot(cursorBefore.x - cursorAfter.x, cursorBefore.y - cursorAfter.y) <= 0.5
|
|
868
|
+
}()
|
|
869
|
+
return Response(ok: true, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
870
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: cursorPreserved,
|
|
871
|
+
dispatch_started: true, error: nil, input_transport: transport.label)
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
private func scroll(_ request: Request, symbols: SkyLightSymbols) -> Response {
|
|
875
|
+
guard let rawPid = request.pid, rawPid > 0,
|
|
876
|
+
let processGeneration = request.process_generation,
|
|
877
|
+
let windowId = request.window_id, windowId > 0,
|
|
878
|
+
let screenX = request.screen_x, screenX.isFinite,
|
|
879
|
+
let screenY = request.screen_y, screenY.isFinite,
|
|
880
|
+
let localX = request.local_x, localX.isFinite,
|
|
881
|
+
let localY = request.local_y, localY.isFinite,
|
|
882
|
+
let requestedDeltaX = request.delta_x, requestedDeltaX.isFinite, abs(requestedDeltaX) <= 10_000,
|
|
883
|
+
let requestedDeltaY = request.delta_y, requestedDeltaY.isFinite, abs(requestedDeltaY) <= 10_000,
|
|
884
|
+
let windowX = request.window_x, windowX.isFinite,
|
|
885
|
+
let windowY = request.window_y, windowY.isFinite,
|
|
886
|
+
let windowWidth = request.window_width, windowWidth > 0, windowWidth.isFinite,
|
|
887
|
+
let windowHeight = request.window_height, windowHeight > 0, windowHeight.isFinite,
|
|
888
|
+
localX >= 0, localY >= 0, localX < windowWidth, localY < windowHeight else {
|
|
889
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
890
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
891
|
+
dispatch_started: false, error: "invalid_request_before_dispatch")
|
|
892
|
+
}
|
|
893
|
+
let wheelX = Int32((-requestedDeltaX).rounded())
|
|
894
|
+
let wheelY = Int32((-requestedDeltaY).rounded())
|
|
895
|
+
guard wheelX != 0 || wheelY != 0 else {
|
|
896
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
897
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
898
|
+
dispatch_started: false, error: "invalid_request_before_dispatch")
|
|
899
|
+
}
|
|
900
|
+
let pid = pid_t(rawPid)
|
|
901
|
+
guard let targetApplication = runningApplication(pid: pid, expectedGeneration: processGeneration) else {
|
|
902
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
903
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
904
|
+
dispatch_started: false, error: "process_generation_changed_before_dispatch")
|
|
905
|
+
}
|
|
906
|
+
let frameworkTransport = pointerTransport(application: targetApplication)
|
|
907
|
+
let transport: PointerTransport = .skyLight
|
|
908
|
+
let expectedBounds = CGRect(x: windowX, y: windowY, width: windowWidth, height: windowHeight)
|
|
909
|
+
guard frontWindowStillMatches(pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds) else {
|
|
910
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
911
|
+
frontmost_restored: nil, front_window_validated: false, cursor_preserved: nil,
|
|
912
|
+
dispatch_started: false, error: "front_window_changed_before_dispatch")
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
let cursorBefore = cursorLocation()
|
|
916
|
+
let priorFront = NSWorkspace.shared.frontmostApplication
|
|
917
|
+
let focusPreparation = prepareFocusIfNeeded(
|
|
918
|
+
symbols, pid: pid, windowId: windowId, frameworkTransport: frameworkTransport,
|
|
919
|
+
priorFront: priorFront, targetApplication: targetApplication
|
|
920
|
+
)
|
|
921
|
+
guard focusPreparation.ok else {
|
|
922
|
+
let restored = restoreFrontmostIfNeeded(
|
|
923
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
924
|
+
)
|
|
925
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
926
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
927
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
928
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "focus_without_raise_unavailable_before_dispatch")
|
|
929
|
+
}
|
|
930
|
+
if focusPreparation.dispatchStarted { usleep(50_000) }
|
|
931
|
+
guard skyLightInputStillAuthorized(
|
|
932
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
933
|
+
targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
934
|
+
) else {
|
|
935
|
+
let restored = restoreFrontmostIfNeeded(
|
|
936
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
937
|
+
)
|
|
938
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
939
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
940
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
941
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "frontmost_changed_before_input")
|
|
942
|
+
}
|
|
943
|
+
guard let source = CGEventSource(stateID: .hidSystemState) else {
|
|
944
|
+
let restored = restoreFrontmostIfNeeded(
|
|
945
|
+
priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted
|
|
946
|
+
)
|
|
947
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
948
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
949
|
+
dispatch_started: focusPreparation.dispatchStarted,
|
|
950
|
+
error: focusPreparation.dispatchStarted ? "dispatch_outcome_unknown" : "event_source_unavailable_before_dispatch")
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
var pointerDispatchStarted = false
|
|
954
|
+
do {
|
|
955
|
+
let clickGroup = Int64(UInt32.random(in: 1...UInt32.max))
|
|
956
|
+
guard inputBoundaryStillValid(
|
|
957
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
958
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
959
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
960
|
+
) else {
|
|
961
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 7)
|
|
962
|
+
}
|
|
963
|
+
try sendMouseEvent(.mouseMoved, symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
964
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
965
|
+
clickState: 0, phase: 2, clickGroup: clickGroup, transport: transport, subtype: 0)
|
|
966
|
+
pointerDispatchStarted = true
|
|
967
|
+
usleep(12_000)
|
|
968
|
+
guard inputBoundaryStillValid(
|
|
969
|
+
pid: pid, application: targetApplication, windowId: windowId, expected: expectedBounds,
|
|
970
|
+
frameworkTransport: frameworkTransport, priorFront: priorFront,
|
|
971
|
+
focusDispatched: focusPreparation.dispatchStarted
|
|
972
|
+
) else {
|
|
973
|
+
throw NSError(domain: "MachineBridgeBackgroundInput", code: 8)
|
|
974
|
+
}
|
|
975
|
+
try sendScrollEvent(symbols: symbols, source: source, pid: pid, windowId: windowId,
|
|
976
|
+
screenX: screenX, screenY: screenY, localX: localX, localY: localY,
|
|
977
|
+
deltaX: wheelX, deltaY: wheelY, transport: transport)
|
|
978
|
+
pointerDispatchStarted = true
|
|
979
|
+
} catch {
|
|
980
|
+
let dispatchStarted = focusPreparation.dispatchStarted || pointerDispatchStarted
|
|
981
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
982
|
+
return Response(ok: false, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
983
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: nil,
|
|
984
|
+
dispatch_started: dispatchStarted,
|
|
985
|
+
error: dispatchStarted ? "dispatch_outcome_unknown" : "input_unavailable_before_dispatch")
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
usleep(50_000)
|
|
989
|
+
let restored = restoreFrontmostIfNeeded(priorFront: priorFront, targetApplication: targetApplication, focusDispatched: focusPreparation.dispatchStarted)
|
|
990
|
+
let cursorAfter = cursorLocation()
|
|
991
|
+
let cursorPreserved: Bool? = {
|
|
992
|
+
guard let cursorBefore, let cursorAfter else { return nil }
|
|
993
|
+
return hypot(cursorBefore.x - cursorAfter.x, cursorBefore.y - cursorAfter.y) <= 0.5
|
|
994
|
+
}()
|
|
995
|
+
return Response(ok: true, backend: "skylight-experimental", focus_without_raise: focusPreparation.dispatchStarted,
|
|
996
|
+
frontmost_restored: restored, front_window_validated: true, cursor_preserved: cursorPreserved,
|
|
997
|
+
dispatch_started: true, error: nil, input_transport: transport.label)
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
guard let line = readLine(), let data = line.data(using: .utf8), let request = try? JSONDecoder().decode(Request.self, from: data) else {
|
|
1001
|
+
encode(Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
1002
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
1003
|
+
dispatch_started: false, error: "invalid_json_before_dispatch"))
|
|
1004
|
+
exit(2)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
switch request.operation {
|
|
1008
|
+
case "unicode_keystroke":
|
|
1009
|
+
let response = unicodeKeystroke(request)
|
|
1010
|
+
encode(response)
|
|
1011
|
+
exit(response.ok ? 0 : 4)
|
|
1012
|
+
case "key_press":
|
|
1013
|
+
let response = specialKeyPress(request)
|
|
1014
|
+
encode(response)
|
|
1015
|
+
exit(response.ok ? 0 : 4)
|
|
1016
|
+
case "probe", "click", "drag", "scroll":
|
|
1017
|
+
guard let symbols = SkyLightSymbols() else {
|
|
1018
|
+
encode(Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
1019
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
1020
|
+
dispatch_started: false, error: "private_symbols_unavailable_before_dispatch"))
|
|
1021
|
+
exit(3)
|
|
1022
|
+
}
|
|
1023
|
+
switch request.operation {
|
|
1024
|
+
case "probe":
|
|
1025
|
+
encode(Response(ok: true, backend: "skylight-experimental", focus_without_raise: nil,
|
|
1026
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
1027
|
+
dispatch_started: false, error: nil))
|
|
1028
|
+
case "click":
|
|
1029
|
+
let response = click(request, symbols: symbols)
|
|
1030
|
+
encode(response)
|
|
1031
|
+
exit(response.ok ? 0 : 4)
|
|
1032
|
+
case "drag":
|
|
1033
|
+
let response = drag(request, symbols: symbols)
|
|
1034
|
+
encode(response)
|
|
1035
|
+
exit(response.ok ? 0 : 4)
|
|
1036
|
+
case "scroll":
|
|
1037
|
+
let response = scroll(request, symbols: symbols)
|
|
1038
|
+
encode(response)
|
|
1039
|
+
exit(response.ok ? 0 : 4)
|
|
1040
|
+
default:
|
|
1041
|
+
encode(Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
1042
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
1043
|
+
dispatch_started: false, error: "unsupported_operation_before_dispatch"))
|
|
1044
|
+
exit(2)
|
|
1045
|
+
}
|
|
1046
|
+
default:
|
|
1047
|
+
encode(Response(ok: false, backend: "skylight-experimental", focus_without_raise: nil,
|
|
1048
|
+
frontmost_restored: nil, front_window_validated: nil, cursor_preserved: nil,
|
|
1049
|
+
dispatch_started: false, error: "unsupported_operation_before_dispatch"))
|
|
1050
|
+
exit(2)
|
|
1051
|
+
}
|