agent-device 0.21.23 → 0.21.25
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/README.md +6 -6
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.23.apk → agent-device-android-ime-helper-0.21.25.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.25.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.23.manifest.json → agent-device-android-ime-helper-0.21.25.manifest.json} +4 -4
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.manifest.json +16 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +12 -6
- package/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/RectResponseTests.swift +21 -0
- package/apple/snapshot-bridge/README.md +9 -7
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +159 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerHttpRequestFraming.swift +139 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerPeer.swift +41 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerToken.swift +25 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +56 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +14 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +18 -11
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +8 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +12 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +25 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +1 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +29 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +0 -3
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +35 -13
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryConfirmation.swift +59 -26
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +19 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +50 -106
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +142 -54
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +4 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +21 -19
- package/dist/src/absence-observation-errors.js +1 -0
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +2 -2
- package/dist/src/adb-failure.js +2 -2
- package/dist/src/adb.js +1 -1
- package/dist/src/agent-browser-lifecycle.js +1 -1
- package/dist/src/agent-browser-process-record.js +1 -1
- package/dist/src/agent-browser-provider.js +1 -1
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/alert.js +1 -1
- package/dist/src/android-adb.d.ts +3 -2
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/android-system-surface-disclosure.js +1 -0
- package/dist/src/android-text-entry.js +1 -0
- package/dist/src/android.js +1 -1
- package/dist/src/app-backend.js +1 -1
- package/dist/src/app-helpers.js +1 -1
- package/dist/src/app-inventory.d.ts +4 -0
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-lifecycle.js +4 -3
- package/dist/src/app-lifecycle2.js +2 -2
- package/dist/src/app-log-files.js +1 -1
- package/dist/src/app-log-reconnect.js +1 -1
- package/dist/src/app-log-runtime2.js +3 -3
- package/dist/src/app-resolution-facade.js +1 -1
- package/dist/src/app-resolution.js +2 -2
- package/dist/src/app-state-runtime.d.ts +15 -0
- package/dist/src/apple-runner-platform.js +1 -1
- package/dist/src/application-lifecycle-interaction.js +1 -1
- package/dist/src/artifact-download.js +1 -1
- package/dist/src/auth.js +1 -1
- package/dist/src/aws-device-farm-connection.js +1 -0
- package/dist/src/back-mode.js +1 -1
- package/dist/src/backend-snapshot-options.js +1 -1
- package/dist/src/boot-diagnostics.js +1 -1
- package/dist/src/browserstack-connection.js +1 -0
- package/dist/src/capture-disclosure.js +1 -0
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +36 -17
- package/dist/src/cli-output.js +1 -1
- package/dist/src/cli-runner.js +3 -1
- package/dist/src/cli.js +3 -4
- package/dist/src/client-companion-tunnel.js +1 -0
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-types.d.ts +15 -7
- package/dist/src/cloud-artifacts.d.ts +3 -2
- package/dist/src/code-signature-cache.js +1 -1
- package/dist/src/code-signature.js +1 -1
- package/dist/src/command-metadata.js +73 -1
- package/dist/src/command-policy.js +1 -0
- package/dist/src/command-schema.js +3 -3
- package/dist/src/command-surface.js +1 -1
- package/dist/src/command-tools.js +2 -2
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/connection-verification.js +1 -0
- package/dist/src/connection.js +5 -5
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-client-lease-beat.js +1 -1
- package/dist/src/daemon-client-lifecycle.js +1 -1
- package/dist/src/daemon-client-liveness-probe.js +1 -1
- package/dist/src/daemon-policy-file.js +1 -1
- package/dist/src/daemon-registration-owner.js +1 -1
- package/dist/src/daemon-registration.js +1 -1
- package/dist/src/daemon-resolution.js +3 -1
- package/dist/src/debug-symbols-facade.js +1 -1
- package/dist/src/device-claim-inspection.js +1 -1
- package/dist/src/device-input-state.d.ts +6 -17
- package/dist/src/device-inventory-context.js +1 -1
- package/dist/src/device-readiness-runtime.js +1 -1
- package/dist/src/device-release.js +1 -1
- package/dist/src/device-selection-resolver.js +1 -1
- package/dist/src/device-shell.js +1 -1
- package/dist/src/device.js +3 -1
- package/dist/src/devicectl.js +1 -1
- package/dist/src/diagnostics.js +1 -1
- package/dist/src/doctor-facade.js +1 -1
- package/dist/src/durable-capture-resource.js +1 -1
- package/dist/src/durable-resource.js +1 -1
- package/dist/src/engine-eval-script.js +1 -1
- package/dist/src/error.js +1 -1
- package/dist/src/exec.js +1 -1
- package/dist/src/expo-dev-client-launch.js +1 -0
- package/dist/src/file.js +3 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic-settle.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/gesture-admission.js +1 -1
- package/dist/src/gesture-plan-types.d.ts +2 -5
- package/dist/src/gesture-plan.js +1 -1
- package/dist/src/gesture-runtime.js +1 -1
- package/dist/src/harmonyos.js +1 -1
- package/dist/src/hdc.js +1 -1
- package/dist/src/helper.js +2 -2
- package/dist/src/human-control.js +1 -1
- package/dist/src/ime-rebind.js +1 -1
- package/dist/src/ime-state.js +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions2.js +2 -2
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-error.js +1 -0
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-catalog.js +1 -1
- package/dist/src/interactor.js +5 -5
- package/dist/src/interactor2.js +1 -1
- package/dist/src/interactors.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +5 -1
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/inventory.js +2 -2
- package/dist/src/inventory2.js +1 -1
- package/dist/src/inventory6.js +1 -1
- package/dist/src/io.d.ts +2 -2
- package/dist/src/ios-client.js +1 -1
- package/dist/src/ios-settings.js +1 -1
- package/dist/src/ios-snapshot-acquisition.js +1 -1
- package/dist/src/ios-snapshot-adapter.js +1 -1
- package/dist/src/ios-snapshot-engine.js +1 -1
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/keyed-lock.js +1 -1
- package/dist/src/launch-confirmation.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun-runtime-dependencies.js +1 -1
- package/dist/src/limrun.d.ts +5 -564
- package/dist/src/limrun.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/load.js +1 -1
- package/dist/src/local-interactor-operation-set.js +1 -1
- package/dist/src/log-stream.js +9 -9
- package/dist/src/mechanics.js +16 -16
- package/dist/src/metro.js +1 -1
- package/dist/src/metro2.js +1 -1
- package/dist/src/mobile-snapshot-semantics.js +1 -1
- package/dist/src/observability.js +1 -1
- package/dist/src/observation.js +1 -2
- package/dist/src/open-target-resolution.js +1 -1
- package/dist/src/output-path.js +1 -1
- package/dist/src/overlay.js +1 -3
- package/dist/src/perf-facade.js +1 -1
- package/dist/src/perf-runtime-operation-builder.js +1 -1
- package/dist/src/perf-runtime.js +1 -1
- package/dist/src/perf-target.js +1 -1
- package/dist/src/physical-device-control.js +1 -1
- package/dist/src/platform-request-scope.js +1 -1
- package/dist/src/platform-runtime-android-adb-host.js +1 -1
- package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
- package/dist/src/platform-runtime-apple-resources.js +1 -0
- package/dist/src/platform-runtime-claim-recovery.js +1 -0
- package/dist/src/platform-runtime-daemon-services.js +1 -0
- package/dist/src/platform-runtime-host.js +1 -1
- package/dist/src/platform-runtime-network-web-transport.js +1 -1
- package/dist/src/platform-runtime-open-target.js +1 -1
- package/dist/src/platform-runtime-operation-host.js +4 -4
- package/dist/src/platform-runtime-runtime-hints.js +2 -2
- package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
- package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
- package/dist/src/platform-runtime-unavailable.js +1 -1
- package/dist/src/platform-runtime.js +1 -1
- package/dist/src/platform-runtime2.js +1 -1
- package/dist/src/plugin.js +1 -0
- package/dist/src/plugins/webdriver.d.ts +111 -0
- package/dist/src/plugins/webdriver.js +1 -0
- package/dist/src/plugins.d.ts +2 -1
- package/dist/src/plugins2.js +1 -1
- package/dist/src/png-crop-bytes.js +1 -1
- package/dist/src/png-resize.js +1 -0
- package/dist/src/png-worker-client.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/process-entry.js +1 -1
- package/dist/src/process-exit.js +1 -1
- package/dist/src/process.js +1 -1
- package/dist/src/provider-credential-fingerprint.js +1 -1
- package/dist/src/provider-device-admission.js +1 -1
- package/dist/src/provider-device-runtime.d.ts +556 -0
- package/dist/src/provider-policy.js +1 -0
- package/dist/src/provider-profile-fields.js +1 -1
- package/dist/src/provider-webdriver.js +1 -2
- package/dist/src/proxy.js +2 -2
- package/dist/src/quality-warnings.js +1 -1
- package/dist/src/react-devtools.js +2 -0
- package/dist/src/react-native-overlay2.js +3 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-runtime.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/recording.js +2 -2
- package/dist/src/register-builtins.js +1 -1
- package/dist/src/remote-bridge.js +1 -0
- package/dist/src/remote-config-core.js +1 -0
- package/dist/src/remote-config.js +1 -1
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/replay-backend-registry.js +1 -0
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/replay-maestro-backend.js +117 -0
- package/dist/src/replay-script-selection.js +1 -0
- package/dist/src/replay.js +1 -3
- package/dist/src/replay2.js +1 -0
- package/dist/src/reporting.js +3 -3
- package/dist/src/request-cancel.js +1 -1
- package/dist/src/request-providers.js +1 -1
- package/dist/src/request-runtime-binding.js +1 -1
- package/dist/src/result-serialization.js +1 -1
- package/dist/src/retry.js +1 -1
- package/dist/src/retry2.js +1 -0
- package/dist/src/runner-artifact-manifest.js +1 -1
- package/dist/src/runner-cache-retention.js +1 -0
- package/dist/src/runner-cache-trim.js +1 -0
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-dev-tools-security.js +1 -1
- package/dist/src/runner-device-readiness.js +1 -1
- package/dist/src/runner-device-set.js +1 -0
- package/dist/src/runner-error-classification.js +3 -3
- package/dist/src/runner-exchange.js +1 -1
- package/dist/src/runner-lease.js +1 -0
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner-retention.js +1 -0
- package/dist/src/runner-start-budget.js +1 -1
- package/dist/src/runner-startup-transport.js +1 -0
- package/dist/src/runner-toolchain-probe.js +2 -0
- package/dist/src/runner.js +2 -1
- package/dist/src/runtime-command-surface.js +1 -0
- package/dist/src/runtime-deployment.js +1 -0
- package/dist/src/runtime-factory.js +1 -0
- package/dist/src/runtime-session.js +1 -0
- package/dist/src/runtime-transport-hints.js +1 -1
- package/dist/src/runtime.js +2 -1
- package/dist/src/runtime10.js +2 -2
- package/dist/src/runtime2.js +4 -4
- package/dist/src/runtime3.js +1 -1
- package/dist/src/runtime4.js +5 -5
- package/dist/src/runtime5.js +1 -1
- package/dist/src/runtime6.js +1 -1
- package/dist/src/runtime7.js +1 -1
- package/dist/src/runtime8.js +1 -1
- package/dist/src/runtime9.js +1 -1
- package/dist/src/screen-recording-transport.js +1 -1
- package/dist/src/screenshot-density.js +1 -1
- package/dist/src/screenshot-image.js +1 -1
- package/dist/src/screenshot-runtime.js +1 -1
- package/dist/src/screenshot3.js +5 -5
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.d.ts +20 -29
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.d.ts +1 -1
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.d.ts +4 -2
- package/dist/src/sdk-remote-config.d.ts +4 -0
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.d.ts +5 -6
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selection.js +0 -0
- package/dist/src/selector-pipeline-policy.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/session-allocation.js +1 -1
- package/dist/src/session-replay-runtime-failure-response.js +1 -0
- package/dist/src/session-runtime-admission.js +1 -1
- package/dist/src/session.js +1 -1
- package/dist/src/session2.js +5 -5
- package/dist/src/settings.js +1 -1
- package/dist/src/settings2.js +1 -1
- package/dist/src/simctl-settings.js +1 -1
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator-boot.js +1 -1
- package/dist/src/simulator-state.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-capture-annotations.js +1 -1
- package/dist/src/snapshot-chrome.js +1 -0
- package/dist/src/snapshot-desktop-projection.js +1 -1
- package/dist/src/snapshot-evidence.js +1 -0
- package/dist/src/snapshot-node-lookup.js +1 -0
- package/dist/src/snapshot-occlusion.js +1 -0
- package/dist/src/snapshot-quality-verdict.js +1 -0
- package/dist/src/snapshot-runtime-binding.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -1
- package/dist/src/snapshot-state.js +1 -1
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/snapshot3.js +1 -1
- package/dist/src/src.js +5 -1
- package/dist/src/src2.js +1 -1
- package/dist/src/src3.js +2 -1
- package/dist/src/store.js +1 -1
- package/dist/src/surface-snapshot2.js +1 -1
- package/dist/src/target-evidence-tree.js +1 -0
- package/dist/src/target-evidence.js +1 -1
- package/dist/src/text-entry-focus.js +1 -1
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/touch-runtime.js +1 -1
- package/dist/src/touch-runtime2.js +1 -1
- package/dist/src/tree.js +1 -1
- package/dist/src/tv-remote.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/undici.js +8 -8
- package/dist/src/update-check.js +1 -1
- package/dist/src/vars.js +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/video.js +2 -2
- package/dist/src/wait.js +1 -0
- package/dist/src/web.js +1 -1
- package/dist/src/web2.js +1 -1
- package/package.json +22 -19
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.23.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.23.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.23.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.23.manifest.json +0 -16
- package/dist/src/adb-transport.js +0 -1
- package/dist/src/alert-runtime.js +0 -1
- package/dist/src/app-deployment-resolution.js +0 -2
- package/dist/src/app-inventory-contract.js +0 -1
- package/dist/src/app-log-session-resource.js +0 -1
- package/dist/src/application-lifecycle-runtime.js +0 -1
- package/dist/src/auth-session.js +0 -1
- package/dist/src/cache.js +0 -1
- package/dist/src/capabilities.js +0 -1
- package/dist/src/capture.js +0 -1
- package/dist/src/catalog.js +0 -1
- package/dist/src/click-button.js +0 -1
- package/dist/src/client-metro-companion.js +0 -1
- package/dist/src/client-options.js +0 -3
- package/dist/src/compatibility-policy.js +0 -1
- package/dist/src/daemon-artifacts.js +0 -1
- package/dist/src/daemon-client-transport.js +0 -1
- package/dist/src/daemon-http.js +0 -1
- package/dist/src/device-claim-rule.js +0 -1
- package/dist/src/device-rotation.js +0 -1
- package/dist/src/device-session.js +0 -3
- package/dist/src/device2.js +0 -1
- package/dist/src/device3.js +0 -3
- package/dist/src/dispatch-resolve.js +0 -4
- package/dist/src/format.js +0 -1
- package/dist/src/gesture-plan-types.js +0 -1
- package/dist/src/host.js +0 -1
- package/dist/src/human-control-contract.js +0 -1
- package/dist/src/interaction-ref-policy.js +0 -1
- package/dist/src/interactor-operation-binding.js +0 -1
- package/dist/src/keyboard-actions.js +0 -1
- package/dist/src/lease-context.js +0 -1
- package/dist/src/lease-registry-scope.js +0 -1
- package/dist/src/lease-scope.js +0 -1
- package/dist/src/managed-device-scope.js +0 -1
- package/dist/src/observe-until.js +0 -1
- package/dist/src/output-common.js +0 -5
- package/dist/src/owner-identity.js +0 -3
- package/dist/src/path-resolution.js +0 -1
- package/dist/src/perf-runtime-plan.js +0 -1
- package/dist/src/platform-runtime-app-log-process.js +0 -1
- package/dist/src/platform-runtime-managed-owner.js +0 -1
- package/dist/src/platform-runtime-screen-recording-process-host.js +0 -1
- package/dist/src/png.js +0 -1
- package/dist/src/prepare-kind.js +0 -1
- package/dist/src/provider-device-runtimes.js +0 -1
- package/dist/src/provider-session-ownership.js +0 -2
- package/dist/src/providers.js +0 -1
- package/dist/src/registry.js +0 -68
- package/dist/src/request-admission.js +0 -1
- package/dist/src/request-cancellation.js +0 -1
- package/dist/src/request-progress-protocol.js +0 -1
- package/dist/src/runner-cache-metadata.js +0 -3
- package/dist/src/runner-disposal.js +0 -1
- package/dist/src/runner-session-types.js +0 -1
- package/dist/src/runtime-admission.js +0 -1
- package/dist/src/screen-recording-admission-ledger.js +0 -1
- package/dist/src/screen-recording-resource-recovery.js +0 -1
- package/dist/src/screenshot-result.js +0 -1
- package/dist/src/script.js +0 -1
- package/dist/src/scroll-command.js +0 -1
- package/dist/src/scroll-gesture.js +0 -1
- package/dist/src/scroll-indicator.js +0 -1
- package/dist/src/session-artifact-paths.js +0 -1
- package/dist/src/session-paths.js +0 -1
- package/dist/src/session-repair-tombstone.js +0 -1
- package/dist/src/session-routing.js +0 -1
- package/dist/src/session-snapshot.js +0 -1
- package/dist/src/snapshot4.js +0 -1
- package/dist/src/src4.js +0 -1
- package/dist/src/src5.js +0 -1
- package/dist/src/src6.js +0 -1
- package/dist/src/src7.js +0 -2
- package/dist/src/src8.js +0 -115
- package/dist/src/string-enum.js +0 -1
- package/dist/src/success-text.js +0 -1
- package/dist/src/timeouts.js +0 -1
- package/dist/src/touch-reference-frame.js +0 -1
- package/dist/src/transport.js +0 -3
- package/dist/src/ttl-memo.js +0 -1
- package/dist/src/verified-file.js +0 -1
- package/dist/src/viewport.js +0 -1
- package/dist/src/window-state.js +0 -1
- /package/dist/src/{doctor-output.js → doctor-lines.js} +0 -0
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ agent-device doctor
|
|
|
31
31
|
agent-device help workflow
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Run `doctor` yourself before
|
|
34
|
+
Run `doctor` yourself before you hand the CLI to an agent. `help workflow` links to the guides for debugging, replay, and profiling.
|
|
35
35
|
|
|
36
36
|
### Drive an app from the CLI
|
|
37
37
|
|
|
@@ -61,7 +61,7 @@ agent-device screenshot ./contact-form.png
|
|
|
61
61
|
agent-device close
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Refs are only
|
|
64
|
+
Refs are valid only in the latest output: after a `--settle` command, use the refs in its diff, and take a new snapshot only if the diff omits what you need. Snapshots come from the app's accessibility tree, so clear labels, roles, and test IDs make agent runs more reliable; use screenshots and video as evidence or when accessibility data is poor.
|
|
65
65
|
|
|
66
66
|

|
|
67
67
|
|
|
@@ -126,7 +126,7 @@ With the CLI installed, prompts like these work end to end:
|
|
|
126
126
|
## Next steps
|
|
127
127
|
|
|
128
128
|
- [AI Agent Setup](https://oss.callstack.com/agent-device/docs/agent-setup): skills, project rules, and per-client setup for Cursor, Codex, Claude Code, Windsurf, and others.
|
|
129
|
-
- [Quick Start](https://oss.callstack.com/agent-device/docs/quick-start):
|
|
129
|
+
- [Quick Start](https://oss.callstack.com/agent-device/docs/quick-start): open an app, read its UI, and interact with it from the command line.
|
|
130
130
|
- [Replay & E2E](https://oss.callstack.com/agent-device/docs/replay-e2e) and [Debugging & Profiling](https://oss.callstack.com/agent-device/docs/debugging-profiling): repeatable tests and bug hunting.
|
|
131
131
|
|
|
132
132
|
## Where to run agent-device
|
|
@@ -137,15 +137,15 @@ The same session and evidence model works at every step: the agent explores the
|
|
|
137
137
|
| --- | --- | --- |
|
|
138
138
|
| Local | Trying commands and debugging apps on simulators, emulators, physical devices, macOS, and Linux. | Follow the Quick Start. |
|
|
139
139
|
| CI/CD | Automated pull request and merge validation with replay scripts and captured artifacts. | Try the [EAS workflow template](https://github.com/callstackincubator/eas-agent-device/blob/main/.eas/workflows/agent-qa-mobile.yml). |
|
|
140
|
-
| Cloud / remote | Linux runners, managed devices, and remote jobs. | Set up a [remote proxy](https://oss.callstack.com/agent-device/docs/remote-proxy), connect a [device cloud](https://oss.callstack.com/agent-device/docs/device-clouds) (BrowserStack, AWS Device Farm, Limrun), or [contact Callstack](mailto:hello@callstack.com) for team QA. |
|
|
140
|
+
| Cloud / remote | Linux runners, managed devices, and remote jobs. | Set up a [remote proxy](https://oss.callstack.com/agent-device/docs/remote-proxy), connect a [device cloud](https://oss.callstack.com/agent-device/docs/device-clouds) (BrowserStack, AWS Device Farm, TestMu AI, Limrun), or [contact Callstack](mailto:hello@callstack.com) for team QA. |
|
|
141
141
|
|
|
142
142
|
## How it works
|
|
143
143
|
|
|
144
144
|
`agent-device` keeps device state in sessions. It uses a local accessibility bridge for iOS Simulator snapshots and XCTest for iOS interactions, physical iOS, and tvOS; ADB and the snapshot helper on Android; HDC and ArkUI `uitest` on HarmonyOS; Vega CLI/VDA on the Vega Virtual Device; a local helper on macOS; and AT-SPI on Linux.
|
|
145
145
|
|
|
146
|
-
Support depth varies by target.
|
|
146
|
+
Support depth varies by target. Some backends, such as HarmonyOS and Vega OS, cover a subset of commands; run `agent-device capabilities --platform <platform>` to see what a target supports.
|
|
147
147
|
|
|
148
|
-
Sessions are scoped to the caller's git worktree, and host-local device claims stop parallel agents from taking over each other's simulators and emulators.
|
|
148
|
+
Sessions are scoped to the caller's git worktree, and host-local device claims stop parallel agents from taking over each other's simulators and emulators. Run `agent-device device status` to inspect ownership without a daemon, and `agent-device device release --stale` to release claims whose owner is provably no longer running. The same commands drive hosted devices on [BrowserStack, AWS Device Farm, TestMu AI, and Limrun](https://oss.callstack.com/agent-device/docs/device-clouds).
|
|
149
149
|
|
|
150
150
|
`agent-device` uses the inspect-act-verify process from Vercel's [agent-browser](https://github.com/vercel-labs/agent-browser) for mobile, TV, and desktop apps. Basic `--platform web` support runs `agent-browser` in the same session and replay system.
|
|
151
151
|
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2b1c6a028b28e426c4bed80449b6dd1eff69985f1741a58d033e30f8b9fa5bf1 agent-device-android-ime-helper-0.21.25.apk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-ime-helper",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"assetName": "agent-device-android-ime-helper-0.21.
|
|
5
|
-
"sha256": "
|
|
3
|
+
"version": "0.21.25",
|
|
4
|
+
"assetName": "agent-device-android-ime-helper-0.21.25.apk",
|
|
5
|
+
"sha256": "2b1c6a028b28e426c4bed80449b6dd1eff69985f1741a58d033e30f8b9fa5bf1",
|
|
6
6
|
"packageName": "com.callstack.agentdevice.imehelper",
|
|
7
|
-
"versionCode":
|
|
7
|
+
"versionCode": 21025,
|
|
8
8
|
"serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
|
|
9
9
|
"broadcastProtocol": "android-ime-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
79175080eb71f444224c844a599e75e839d6456790eb96d175fc887f85237a12 agent-device-android-snapshot-helper-0.21.25.apk
|
package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.manifest.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "android-snapshot-helper",
|
|
3
|
+
"version": "0.21.25",
|
|
4
|
+
"releaseTag": "v0.21.25",
|
|
5
|
+
"assetName": "agent-device-android-snapshot-helper-0.21.25.apk",
|
|
6
|
+
"apkUrl": "https://github.com/callstack/agent-device/releases/download/v0.21.25/agent-device-android-snapshot-helper-0.21.25.apk",
|
|
7
|
+
"sha256": "79175080eb71f444224c844a599e75e839d6456790eb96d175fc887f85237a12",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.21.25.apk.sha256",
|
|
9
|
+
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
+
"versionCode": 21025,
|
|
11
|
+
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
|
+
"minSdk": 23,
|
|
13
|
+
"targetSdk": 36,
|
|
14
|
+
"outputFormat": "uiautomator-xml",
|
|
15
|
+
"statusProtocol": "android-snapshot-helper-v1"
|
|
16
|
+
}
|
|
@@ -795,12 +795,18 @@ func rectAttribute(_ element: AXUIElement) -> RectResponse? {
|
|
|
795
795
|
return nil
|
|
796
796
|
}
|
|
797
797
|
|
|
798
|
-
return
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
798
|
+
return finiteRectResponse(position: position, size: size)
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/// JSONEncoder throws on inf/NaN, which fails the whole snapshot. SwiftUI reports lazy
|
|
802
|
+
/// containers scrolled out of view as an AXOpaqueProviderGroup at (inf, inf, 0, 0).
|
|
803
|
+
func finiteRectResponse(position: CGPoint, size: CGSize) -> RectResponse? {
|
|
804
|
+
let x = Double(position.x)
|
|
805
|
+
let y = Double(position.y)
|
|
806
|
+
let width = Double(size.width)
|
|
807
|
+
let height = Double(size.height)
|
|
808
|
+
guard x.isFinite, y.isFinite, width.isFinite, height.isFinite else { return nil }
|
|
809
|
+
return RectResponse(x: x, y: y, width: width, height: height)
|
|
804
810
|
}
|
|
805
811
|
|
|
806
812
|
private func accessibilityAxValue(_ value: CFTypeRef?) -> AXValue? {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
|
|
3
|
+
@testable import AgentDeviceMacOSHelper
|
|
4
|
+
|
|
5
|
+
final class RectResponseTests: XCTestCase {
|
|
6
|
+
func testFiniteRectKeepsItsValues() throws {
|
|
7
|
+
let rect = try XCTUnwrap(
|
|
8
|
+
finiteRectResponse(position: CGPoint(x: 1, y: 2), size: CGSize(width: 3, height: 4)))
|
|
9
|
+
XCTAssertEqual([rect.x, rect.y, rect.width, rect.height], [1, 2, 3, 4])
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func testNonFiniteComponentYieldsNoRect() {
|
|
13
|
+
let nonFinite: [CGFloat] = [.infinity, -.infinity, .nan]
|
|
14
|
+
for value in nonFinite {
|
|
15
|
+
XCTAssertNil(finiteRectResponse(position: CGPoint(x: value, y: 0), size: .zero))
|
|
16
|
+
XCTAssertNil(finiteRectResponse(position: CGPoint(x: 0, y: value), size: .zero))
|
|
17
|
+
XCTAssertNil(finiteRectResponse(position: .zero, size: CGSize(width: value, height: 0)))
|
|
18
|
+
XCTAssertNil(finiteRectResponse(position: .zero, size: CGSize(width: 0, height: value)))
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -50,13 +50,15 @@ disabled after fallback until that app relaunches.
|
|
|
50
50
|
|
|
51
51
|
## Remote content
|
|
52
52
|
|
|
53
|
-
The reader snapshots one process.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
The reader snapshots one process. Two hosts put another process's UI into that
|
|
54
|
+
tree as an `AXRemoteElement` leaf: a WebKit page — Safari's or a `WKWebView`'s —
|
|
55
|
+
lives in a WebContent process under the web view, and a scene-hosted remote view
|
|
56
|
+
controller, such as a share extension presented over the app, lives in the
|
|
57
|
+
extension's process under a `_UISceneHostingView`. The guest returns the leaf as
|
|
58
|
+
delivered; the host refuses a tree in which such a leaf sits under either host
|
|
59
|
+
and reaches the viewport, or reports no frame (`remote-content-boundary`), and
|
|
60
|
+
routes that app generation to XCTest, which resolves remote elements (#2484). A
|
|
61
|
+
zero-area or off-screen leaf is published: it hosts nothing the capture can miss.
|
|
60
62
|
|
|
61
63
|
## Bounded depth recovery
|
|
62
64
|
|
|
@@ -71,6 +71,7 @@ int main(int argc, const char *argv[]) {
|
|
|
71
71
|
// took focus from an input that has stopped answering the accessibility query that found it.
|
|
72
72
|
@property(nonatomic, strong) UILabel *textEntryFocusWitness;
|
|
73
73
|
@property(nonatomic, assign) NSUInteger textEntryWriteBacks;
|
|
74
|
+
@property(nonatomic, assign) NSUInteger textEntryTotalEdits;
|
|
74
75
|
@property(nonatomic, copy, nullable) NSString *textEntryRenderedValue;
|
|
75
76
|
@property(nonatomic, assign) NSTimeInterval textEntryLastEditTime;
|
|
76
77
|
@property(nonatomic, assign) NSTimeInterval textEntryBurstStartTime;
|
|
@@ -97,6 +98,122 @@ int main(int argc, const char *argv[]) {
|
|
|
97
98
|
return [NSString stringWithFormat:@"%lu of 6 digits", (unsigned long)self.text.length];
|
|
98
99
|
}
|
|
99
100
|
@end
|
|
101
|
+
|
|
102
|
+
// A field that reformats its own text on every edit into `## ### ## ##` digit groups, the way a
|
|
103
|
+
// digit-grouping formatter in a Flutter or React Native input does. The accessibility value is
|
|
104
|
+
// what the user sees — the formatted string — so it never equals the digits a `fill` sent (#2634).
|
|
105
|
+
// The field owns the delegate it installs, keeping the formatter out of the view controller's
|
|
106
|
+
// own delegate callbacks.
|
|
107
|
+
@interface AgentDeviceDigitGroupingTextField : UITextField <UITextFieldDelegate>
|
|
108
|
+
@end
|
|
109
|
+
|
|
110
|
+
@interface AgentDeviceCurrencyTextField : UITextField <UITextFieldDelegate>
|
|
111
|
+
@property(nonatomic, strong) UILabel *entryStatus;
|
|
112
|
+
@property(nonatomic, assign) NSUInteger insertedDigits;
|
|
113
|
+
@end
|
|
114
|
+
|
|
115
|
+
@interface AgentDeviceSubmitClearingTextField : UITextField <UITextFieldDelegate>
|
|
116
|
+
@property(nonatomic, strong) UILabel *entryStatus;
|
|
117
|
+
@property(nonatomic, assign) NSUInteger submits;
|
|
118
|
+
@end
|
|
119
|
+
|
|
120
|
+
static NSString *AgentDeviceFormatGroupedDigits(NSString *raw) {
|
|
121
|
+
NSMutableString *digits = [NSMutableString string];
|
|
122
|
+
NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
|
|
123
|
+
for (NSString *chunk in [raw componentsSeparatedByCharactersInSet:nonDigits]) {
|
|
124
|
+
[digits appendString:chunk];
|
|
125
|
+
}
|
|
126
|
+
NSMutableArray<NSNumber *> *groupWidths = [NSMutableArray arrayWithArray:@[@2, @3, @2, @2]];
|
|
127
|
+
NSMutableString *formatted = [NSMutableString string];
|
|
128
|
+
NSUInteger cursor = 0;
|
|
129
|
+
for (NSNumber *width in groupWidths) {
|
|
130
|
+
if (cursor >= digits.length) {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (formatted.length > 0) {
|
|
134
|
+
[formatted appendString:@" "];
|
|
135
|
+
}
|
|
136
|
+
NSUInteger end = MIN(cursor + width.unsignedIntegerValue, digits.length);
|
|
137
|
+
[formatted appendString:[digits substringWithRange:NSMakeRange(cursor, end - cursor)]];
|
|
138
|
+
cursor = end;
|
|
139
|
+
}
|
|
140
|
+
// Digits past the last group keep grouping by threes rather than vanishing from the field.
|
|
141
|
+
while (cursor < digits.length) {
|
|
142
|
+
NSUInteger end = MIN(cursor + 3, digits.length);
|
|
143
|
+
[formatted appendString:[NSString stringWithFormat:@" %@", [digits substringWithRange:NSMakeRange(cursor, end - cursor)]]];
|
|
144
|
+
cursor = end;
|
|
145
|
+
}
|
|
146
|
+
return formatted;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static NSUInteger AgentDeviceDigitCount(NSString *text, NSUInteger limit) {
|
|
150
|
+
NSUInteger count = 0;
|
|
151
|
+
for (NSUInteger index = 0; index < limit && index < text.length; index++) {
|
|
152
|
+
if ([NSCharacterSet.decimalDigitCharacterSet characterIsMember:[text characterAtIndex:index]]) {
|
|
153
|
+
count += 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return count;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@implementation AgentDeviceDigitGroupingTextField
|
|
160
|
+
- (BOOL)textField:(UITextField *)textField
|
|
161
|
+
shouldChangeCharactersInRange:(NSRange)range
|
|
162
|
+
replacementString:(NSString *)string {
|
|
163
|
+
NSString *combined = [textField.text stringByReplacingCharactersInRange:range withString:string];
|
|
164
|
+
NSString *formatted = AgentDeviceFormatGroupedDigits(combined);
|
|
165
|
+
textField.text = formatted;
|
|
166
|
+
// Assigning `text` drops the caret at the end, which a real grouping formatter does not do:
|
|
167
|
+
// a mid-string edit must keep editing where the user put the caret. The digits before the
|
|
168
|
+
// caret survive the reformat unchanged, so the caret belongs before the digit that follows
|
|
169
|
+
// them — past the separators the grouping inserted behind them — and at the end when it
|
|
170
|
+
// followed all of them. Typed digits count as before the caret; a delete does not move it.
|
|
171
|
+
NSUInteger digitsBeforeCaret =
|
|
172
|
+
AgentDeviceDigitCount(textField.text, range.location) + AgentDeviceDigitCount(string, string.length);
|
|
173
|
+
NSUInteger caretOffset = formatted.length;
|
|
174
|
+
NSUInteger seen = 0;
|
|
175
|
+
for (NSUInteger index = 0; index < formatted.length; index++) {
|
|
176
|
+
if (![NSCharacterSet.decimalDigitCharacterSet characterIsMember:[formatted characterAtIndex:index]]) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
seen += 1;
|
|
180
|
+
if (seen == digitsBeforeCaret + 1) {
|
|
181
|
+
caretOffset = index;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
textField.selectedTextRange = [textField textRangeFromPosition:[textField positionFromPosition:textField.beginningOfDocument offset:caretOffset]
|
|
186
|
+
toPosition:[textField positionFromPosition:textField.beginningOfDocument offset:caretOffset]];
|
|
187
|
+
return NO;
|
|
188
|
+
}
|
|
189
|
+
@end
|
|
190
|
+
|
|
191
|
+
@implementation AgentDeviceCurrencyTextField
|
|
192
|
+
- (BOOL)textField:(UITextField *)textField
|
|
193
|
+
shouldChangeCharactersInRange:(NSRange)range
|
|
194
|
+
replacementString:(NSString *)string {
|
|
195
|
+
NSString *combined = [textField.text stringByReplacingCharactersInRange:range withString:string];
|
|
196
|
+
NSMutableString *digits = [NSMutableString string];
|
|
197
|
+
for (NSString *part in [combined componentsSeparatedByCharactersInSet:NSCharacterSet.decimalDigitCharacterSet.invertedSet]) {
|
|
198
|
+
[digits appendString:part];
|
|
199
|
+
}
|
|
200
|
+
unsigned long long cents = digits.longLongValue;
|
|
201
|
+
textField.text = [NSString stringWithFormat:@"$%llu.%02llu", cents / 100, cents % 100];
|
|
202
|
+
self.insertedDigits += AgentDeviceDigitCount(string, string.length);
|
|
203
|
+
self.entryStatus.text = [NSString stringWithFormat:@"%lu", (unsigned long)self.insertedDigits];
|
|
204
|
+
textField.selectedTextRange = [textField textRangeFromPosition:textField.endOfDocument toPosition:textField.endOfDocument];
|
|
205
|
+
return NO;
|
|
206
|
+
}
|
|
207
|
+
@end
|
|
208
|
+
|
|
209
|
+
@implementation AgentDeviceSubmitClearingTextField
|
|
210
|
+
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
|
211
|
+
self.submits += 1;
|
|
212
|
+
self.entryStatus.text = [NSString stringWithFormat:@"%lu", (unsigned long)self.submits];
|
|
213
|
+
textField.text = @"";
|
|
214
|
+
return YES;
|
|
215
|
+
}
|
|
216
|
+
@end
|
|
100
217
|
#endif
|
|
101
218
|
|
|
102
219
|
@implementation AgentDeviceRunnerViewController
|
|
@@ -202,11 +319,12 @@ static NSTimeInterval AgentDeviceAlertActivationBusyWindow(void) {
|
|
|
202
319
|
- (void)updateTextEntryWriteBackStatus {
|
|
203
320
|
NSTimeInterval burstSpan = self.textEntryLastEditTime - self.textEntryBurstStartTime;
|
|
204
321
|
self.textEntryWriteBackStatus.text = [NSString
|
|
205
|
-
stringWithFormat:@"write-backs=%lu burst-edits=%lu burst-ms=%lu min-gap-ms=%lu",
|
|
322
|
+
stringWithFormat:@"write-backs=%lu burst-edits=%lu burst-ms=%lu min-gap-ms=%lu total-edits=%lu",
|
|
206
323
|
(unsigned long)self.textEntryWriteBacks,
|
|
207
324
|
(unsigned long)self.textEntryBurstEdits,
|
|
208
325
|
(unsigned long)llround(burstSpan * 1000),
|
|
209
|
-
(unsigned long)llround(self.textEntryBurstMinGap * 1000)
|
|
326
|
+
(unsigned long)llround(self.textEntryBurstMinGap * 1000),
|
|
327
|
+
(unsigned long)self.textEntryTotalEdits];
|
|
210
328
|
}
|
|
211
329
|
|
|
212
330
|
- (void)presentAlertFixtureReplacement:(BOOL)replacement {
|
|
@@ -292,6 +410,7 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
292
410
|
// typed faster than the app renders loses the characters that arrived while a render was in
|
|
293
411
|
// flight, and the field settles stable short of the request.
|
|
294
412
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-app-owned-value"]) {
|
|
413
|
+
self.textEntryTotalEdits += 1;
|
|
295
414
|
NSTimeInterval now = NSProcessInfo.processInfo.systemUptime;
|
|
296
415
|
NSTimeInterval gap = now - self.textEntryLastEditTime;
|
|
297
416
|
BOOL overtookARender = self.textEntryRenderedValue != nil && gap < self.textEntryAcknowledgeWindowSeconds;
|
|
@@ -408,8 +527,28 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
408
527
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-regression"]) {
|
|
409
528
|
BOOL digitCountValue =
|
|
410
529
|
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-digit-count-value"];
|
|
411
|
-
|
|
412
|
-
|
|
530
|
+
BOOL digitGroupingValue =
|
|
531
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-digit-grouping-value"];
|
|
532
|
+
BOOL currencyValue =
|
|
533
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-currency-value"];
|
|
534
|
+
BOOL submitClears =
|
|
535
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-submit-clears"];
|
|
536
|
+
UITextField *textField;
|
|
537
|
+
if (digitCountValue) {
|
|
538
|
+
textField = [[AgentDeviceDigitCountTextField alloc] init];
|
|
539
|
+
} else if (digitGroupingValue) {
|
|
540
|
+
textField = [[AgentDeviceDigitGroupingTextField alloc] init];
|
|
541
|
+
} else if (currencyValue) {
|
|
542
|
+
textField = [[AgentDeviceCurrencyTextField alloc] init];
|
|
543
|
+
textField.text = @"$0.00";
|
|
544
|
+
} else if (submitClears) {
|
|
545
|
+
textField = [[AgentDeviceSubmitClearingTextField alloc] init];
|
|
546
|
+
} else {
|
|
547
|
+
textField = [[UITextField alloc] init];
|
|
548
|
+
}
|
|
549
|
+
if (digitGroupingValue || currencyValue || submitClears) {
|
|
550
|
+
textField.delegate = (id<UITextFieldDelegate>)textField;
|
|
551
|
+
}
|
|
413
552
|
if (![NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-unnamed-input"]) {
|
|
414
553
|
textField.accessibilityIdentifier = @"agent-device-hardware-keyboard-input";
|
|
415
554
|
}
|
|
@@ -434,6 +573,22 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
434
573
|
[textField.widthAnchor constraintEqualToConstant:240],
|
|
435
574
|
[textField.heightAnchor constraintEqualToConstant:AgentDeviceTextEntryFieldHeight],
|
|
436
575
|
]];
|
|
576
|
+
if (currencyValue || submitClears) {
|
|
577
|
+
UILabel *entryStatus = [[UILabel alloc] init];
|
|
578
|
+
entryStatus.accessibilityIdentifier = @"agent-device-text-entry-events";
|
|
579
|
+
entryStatus.text = @"0";
|
|
580
|
+
entryStatus.translatesAutoresizingMaskIntoConstraints = NO;
|
|
581
|
+
[self.view addSubview:entryStatus];
|
|
582
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
583
|
+
[entryStatus.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
|
|
584
|
+
[entryStatus.topAnchor constraintEqualToAnchor:textField.bottomAnchor constant:12],
|
|
585
|
+
]];
|
|
586
|
+
if (currencyValue) {
|
|
587
|
+
((AgentDeviceCurrencyTextField *)textField).entryStatus = entryStatus;
|
|
588
|
+
} else {
|
|
589
|
+
((AgentDeviceSubmitClearingTextField *)textField).entryStatus = entryStatus;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
437
592
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-unqueryable-on-focus"]) {
|
|
438
593
|
self.textEntryFocusWitness = [[UILabel alloc] init];
|
|
439
594
|
self.textEntryFocusWitness.accessibilityIdentifier = @"agent-device-text-entry-focus";
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerHttpRequestFraming.swift
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
enum RunnerHttpRequestRejection: Equatable {
|
|
6
|
+
case malformed(String)
|
|
7
|
+
case headTooLarge
|
|
8
|
+
case unauthorized
|
|
9
|
+
case transferEncodingUnsupported
|
|
10
|
+
case lengthRequired
|
|
11
|
+
case bodyTooLarge
|
|
12
|
+
case methodNotAllowed
|
|
13
|
+
case notFound
|
|
14
|
+
case timedOut
|
|
15
|
+
|
|
16
|
+
var status: Int {
|
|
17
|
+
switch self {
|
|
18
|
+
case .malformed, .transferEncodingUnsupported: return 400
|
|
19
|
+
case .unauthorized: return 401
|
|
20
|
+
case .notFound: return 404
|
|
21
|
+
case .methodNotAllowed: return 405
|
|
22
|
+
case .timedOut: return 408
|
|
23
|
+
case .lengthRequired: return 411
|
|
24
|
+
case .bodyTooLarge: return 413
|
|
25
|
+
case .headTooLarge: return 431
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var code: String {
|
|
30
|
+
self == .unauthorized ? "UNAUTHORIZED" : "INVALID_ARGS"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var message: String {
|
|
34
|
+
switch self {
|
|
35
|
+
case .malformed(let part): return "runner request is malformed: \(part)"
|
|
36
|
+
case .headTooLarge:
|
|
37
|
+
return "runner request head exceeds \(RunnerHttpRequestFraming.maxHeadBytes) bytes"
|
|
38
|
+
case .unauthorized: return "runner request is missing a valid listener token"
|
|
39
|
+
case .transferEncodingUnsupported: return "runner requests must not use Transfer-Encoding"
|
|
40
|
+
case .lengthRequired: return "runner requests must declare one Content-Length"
|
|
41
|
+
case .bodyTooLarge:
|
|
42
|
+
return "runner request body exceeds \(RunnerHttpRequestFraming.maxBodyBytes) bytes"
|
|
43
|
+
case .methodNotAllowed: return "runner accepts only POST"
|
|
44
|
+
case .notFound: return "runner serves only \(RunnerHttpRequestFraming.commandPath)"
|
|
45
|
+
case .timedOut: return "runner request did not arrive before its deadline"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
struct RunnerHttpAdmittedHead: Equatable {
|
|
52
|
+
let bodyOffset: Int
|
|
53
|
+
let contentLength: Int
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
enum RunnerHttpHeadVerdict: Equatable {
|
|
57
|
+
case incomplete
|
|
58
|
+
case admitted(RunnerHttpAdmittedHead)
|
|
59
|
+
case rejected(RunnerHttpRequestRejection)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
enum RunnerHttpRequestFraming {
|
|
65
|
+
static let commandPath = "/command"
|
|
66
|
+
static let maxHeadBytes = 16 * 1024
|
|
67
|
+
static let maxBodyBytes = 2 * 1024 * 1024
|
|
68
|
+
private static let headTerminator = Data("\r\n\r\n".utf8)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
static func admitHead(
|
|
73
|
+
_ buffer: Data,
|
|
74
|
+
token: RunnerListenerToken,
|
|
75
|
+
alreadyScanned: Int = 0
|
|
76
|
+
) -> RunnerHttpHeadVerdict {
|
|
77
|
+
let searchStart = buffer.startIndex + max(0, alreadyScanned - (headTerminator.count - 1))
|
|
78
|
+
guard let terminator = buffer.range(of: headTerminator, in: searchStart..<buffer.endIndex) else {
|
|
79
|
+
return buffer.count > maxHeadBytes ? .rejected(.headTooLarge) : .incomplete
|
|
80
|
+
}
|
|
81
|
+
if terminator.lowerBound - buffer.startIndex > maxHeadBytes { return .rejected(.headTooLarge) }
|
|
82
|
+
guard
|
|
83
|
+
let head = String(data: buffer[buffer.startIndex..<terminator.lowerBound], encoding: .utf8)
|
|
84
|
+
else {
|
|
85
|
+
return .rejected(.malformed("head is not UTF-8"))
|
|
86
|
+
}
|
|
87
|
+
var lines = head.components(separatedBy: "\r\n")
|
|
88
|
+
let requestLine = lines.removeFirst().split(separator: " ", omittingEmptySubsequences: false)
|
|
89
|
+
guard requestLine.count == 3, requestLine[2] == "HTTP/1.1" || requestLine[2] == "HTTP/1.0" else {
|
|
90
|
+
return .rejected(.malformed("request line"))
|
|
91
|
+
}
|
|
92
|
+
var headers: [String: [String]] = [:]
|
|
93
|
+
for line in lines {
|
|
94
|
+
guard let colon = line.firstIndex(of: ":") else {
|
|
95
|
+
return .rejected(.malformed("header line"))
|
|
96
|
+
}
|
|
97
|
+
let name = line[line.startIndex..<colon]
|
|
98
|
+
guard !name.isEmpty, name.allSatisfy(isHeaderNameCharacter) else {
|
|
99
|
+
return .rejected(.malformed("header name"))
|
|
100
|
+
}
|
|
101
|
+
let value = line[line.index(after: colon)...].trimmingCharacters(in: CharacterSet(charactersIn: " \t"))
|
|
102
|
+
headers[name.lowercased(), default: []].append(value)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
guard let presented = headers[RunnerListenerToken.requestHeaderName], presented.count == 1,
|
|
106
|
+
token.matches(presented[0])
|
|
107
|
+
else {
|
|
108
|
+
return .rejected(.unauthorized)
|
|
109
|
+
}
|
|
110
|
+
guard requestLine[0] == "POST" else { return .rejected(.methodNotAllowed) }
|
|
111
|
+
guard requestLine[1] == Substring(commandPath) else { return .rejected(.notFound) }
|
|
112
|
+
if headers["transfer-encoding"] != nil { return .rejected(.transferEncodingUnsupported) }
|
|
113
|
+
guard let lengths = headers["content-length"], lengths.count == 1 else {
|
|
114
|
+
return .rejected(.lengthRequired)
|
|
115
|
+
}
|
|
116
|
+
let declared = lengths[0]
|
|
117
|
+
guard !declared.isEmpty, declared.utf8.allSatisfy({ $0 >= 0x30 && $0 <= 0x39 }) else {
|
|
118
|
+
return .rejected(.malformed("Content-Length"))
|
|
119
|
+
}
|
|
120
|
+
guard declared.utf8.count <= 10, let contentLength = Int(declared), contentLength <= maxBodyBytes
|
|
121
|
+
else {
|
|
122
|
+
return .rejected(.bodyTooLarge)
|
|
123
|
+
}
|
|
124
|
+
return .admitted(
|
|
125
|
+
RunnerHttpAdmittedHead(
|
|
126
|
+
bodyOffset: terminator.upperBound - buffer.startIndex,
|
|
127
|
+
contentLength: contentLength
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static func isHeaderNameCharacter(_ character: Character) -> Bool {
|
|
133
|
+
guard let ascii = character.asciiValue else { return false }
|
|
134
|
+
switch ascii {
|
|
135
|
+
case 0x30...0x39, 0x41...0x5A, 0x61...0x7A: return true
|
|
136
|
+
default: return "!#$%&'*+-.^_`|~".utf8.contains(ascii)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Network
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
enum RunnerListenerPeer: Equatable {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
case loopback
|
|
12
|
+
case tunnel
|
|
13
|
+
case directNetwork
|
|
14
|
+
|
|
15
|
+
static func of(_ connection: NWConnection) -> RunnerListenerPeer {
|
|
16
|
+
classify(
|
|
17
|
+
isLoopbackAddress: isLoopback(connection.endpoint),
|
|
18
|
+
interfaceTypes: connection.currentPath?.availableInterfaces.map(\.type) ?? []
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static func classify(
|
|
23
|
+
isLoopbackAddress: Bool,
|
|
24
|
+
interfaceTypes: [NWInterface.InterfaceType]
|
|
25
|
+
) -> RunnerListenerPeer {
|
|
26
|
+
if isLoopbackAddress { return .loopback }
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if !interfaceTypes.isEmpty && interfaceTypes.allSatisfy({ $0 == .other }) { return .tunnel }
|
|
30
|
+
return .directNetwork
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static func isLoopback(_ endpoint: NWEndpoint) -> Bool {
|
|
34
|
+
guard case .hostPort(let host, _) = endpoint else { return false }
|
|
35
|
+
switch host {
|
|
36
|
+
case .ipv4(let address): return address.isLoopback
|
|
37
|
+
case .ipv6(let address): return address.isLoopback || (address.asIPv4?.isLoopback ?? false)
|
|
38
|
+
default: return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerToken.swift
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
struct RunnerListenerToken {
|
|
5
|
+
static let requestHeaderName = "x-agent-device-runner-token"
|
|
6
|
+
static let minimumByteCount = 32
|
|
7
|
+
|
|
8
|
+
private let expected: [UInt8]
|
|
9
|
+
|
|
10
|
+
init?(_ token: String?) {
|
|
11
|
+
guard let token, token.utf8.count >= Self.minimumByteCount else { return nil }
|
|
12
|
+
expected = Array(token.utf8)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
func matches(_ presented: String) -> Bool {
|
|
17
|
+
let candidate = Array(presented.utf8)
|
|
18
|
+
var difference = candidate.count ^ expected.count
|
|
19
|
+
for index in 0..<expected.count {
|
|
20
|
+
let byte = index < candidate.count ? candidate[index] : 0
|
|
21
|
+
difference |= Int(byte ^ expected[index])
|
|
22
|
+
}
|
|
23
|
+
return difference == 0
|
|
24
|
+
}
|
|
25
|
+
}
|