sim-mirror 2.0.0__tar.gz → 2.1.0__tar.gz
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.
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/PKG-INFO +46 -25
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/README.md +44 -23
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/claude-code-cli/README.md +2 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/codex/README.md +1 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/cursor/README.md +1 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/fastapi-embed/README.md +1 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Accessibility.swift +23 -7
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Arguments.swift +2 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Input.swift +1 -1
- sim_mirror-2.1.0/helper/Sources/HelperCore/Orientation.swift +117 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Recording.swift +48 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Requests.swift +36 -3
- sim_mirror-2.1.0/helper/Sources/HelperCore/Rotation.swift +58 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Screen.swift +2 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Server.swift +15 -3
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Wire.swift +1 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/AccessibilityReader.swift +56 -6
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/CaptureDevice.swift +9 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/RecordingRenderer.swift +4 -3
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/SimulatorDevice.swift +33 -10
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/Fixtures/wire-vectors.json +8 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/AccessibilityTests.swift +8 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/ArgumentsTests.swift +1 -1
- sim_mirror-2.1.0/helper/Tests/HelperCoreTests/OrientationTests.swift +113 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/RecordingTests.swift +17 -0
- sim_mirror-2.1.0/helper/Tests/HelperCoreTests/RotationTests.swift +41 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/ServerTests.swift +47 -6
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/README.md +6 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/hierarchy-alert.json +3 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/hierarchy.schema.json +2 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/common.schema.json +7 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/constants.json +2 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/http.schema.json +22 -2
- sim_mirror-2.1.0/protocol/v1/input-refused.schema.json +12 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/pyproject.toml +4 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/_version.py +1 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/api.py +5 -0
- sim_mirror-2.1.0/src/sim_mirror/build/checkouts.py +259 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/provider.py +85 -17
- sim_mirror-2.1.0/src/sim_mirror/build/schemes.py +129 -0
- sim_mirror-2.1.0/src/sim_mirror/build/source.py +59 -0
- sim_mirror-2.1.0/src/sim_mirror/build/stamp.py +152 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/xcodebuild.py +208 -34
- sim_mirror-2.1.0/src/sim_mirror/build/xctestplan.py +105 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/config_command.py +33 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/context.py +12 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/doctor.py +2 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/main.py +3 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/mcp.py +3 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/record.py +35 -5
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/serve.py +3 -1
- sim_mirror-2.1.0/src/sim_mirror/cli/stop.py +128 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/model.py +10 -0
- sim_mirror-2.1.0/src/sim_mirror/config/observe_languages.py +91 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/schema.py +117 -5
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/document.py +8 -6
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/base.py +28 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/companion.py +3 -3
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/connector.py +4 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/engine.py +56 -3
- sim_mirror-2.1.0/src/sim_mirror/connectors/idb/orientation.py +157 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/connector.py +7 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/wda_roles.py +9 -3
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/native/client.py +24 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/native/connector.py +18 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/actions.py +327 -44
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/backends.py +24 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/control.py +47 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/device_changes.py +16 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/instance.py +27 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/manager.py +170 -15
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/recordings.py +143 -8
- sim_mirror-2.1.0/src/sim_mirror/core/refusals.py +255 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/runtime.py +24 -5
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/screen_input.py +17 -9
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/screen_relay.py +38 -5
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/signing.py +4 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/app.py +75 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/auth.py +21 -4
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/lease.py +5 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/lifecycle.py +28 -0
- sim_mirror-2.1.0/src/sim_mirror/daemon/pauses.py +46 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/policy.py +3 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/checks.py +10 -2
- sim_mirror-2.1.0/src/sim_mirror/doctor/worktree.py +89 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/host_copy.py +33 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/hosting.py +32 -5
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/mcp/launcher.py +157 -35
- sim_mirror-2.1.0/src/sim_mirror/mcp/relay.py +625 -0
- sim_mirror-2.1.0/src/sim_mirror/perception/bar_names.py +49 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/imagehash.py +12 -7
- sim_mirror-2.1.0/src/sim_mirror/perception/in_front.py +116 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/model.py +20 -1
- sim_mirror-2.1.0/src/sim_mirror/perception/occlusion.py +346 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/ocr.py +66 -20
- sim_mirror-2.1.0/src/sim_mirror/perception/orientation.py +177 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/readers.py +71 -13
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/settle.py +52 -23
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/snapshot.py +72 -15
- sim_mirror-2.1.0/src/sim_mirror/perception/system_alert.py +87 -0
- sim_mirror-2.1.0/src/sim_mirror/platform/codesign.py +38 -0
- sim_mirror-2.1.0/src/sim_mirror/platform/git.py +63 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/simctl.py +16 -3
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/protocol/__init__.py +2 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/protocol/_generated.py +27 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/protocol/messages.py +7 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/seams.py +15 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/http_routes.py +22 -6
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/static/viewer/embed.html +1 -6
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/static/viewer/index.html +1 -6
- sim_mirror-2.1.0/src/sim_mirror/server/static/viewer/start.js +35 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/checkouts.py +72 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/contract.py +2 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/daemon.py +3 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fakes.py +63 -4
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-alert-ipad-landscape-left-ios26.json +551 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-alert-ipad-upside-down-ios26.json +491 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-alert-turned-left-ios26.json +457 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-alert-turned-right-ios26.json +491 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-alert-upright-ios26.json +469 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-files-navigation-bar-de-ios26.json +551 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-files-tab-bar-de-ios26.json +495 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-floating-button-ios26.json +541 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-health-tab-bar-ios26.json +488 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-native-files-navigation-bar-de-ios26.json +427 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-probe-ipad-upside-down-ios26.json +133 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-probe-rotated-left-ios26.json +133 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-settings-back-button-ios26.json +395 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-settings-search-field-ios26.json +426 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-settings-toolbar-ja-ios26.json +397 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-settings-under-navigation-bar-ios26.json +533 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-landscape-left-ios26.json +363 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-landscape-right-ios26.json +363 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-maps-alert-ios26.json +382 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-portrait-ios26.json +370 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-reminders-portrait-ios26.json +345 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-safari-landscape-left-ios26.json +1166 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/ax-turned-safari-landscape-right-ios26.json +1166 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/xcodebuild-settings-test.json +55 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/xcscheme-staging.xcscheme +91 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/xcscheme-test-plans.xcscheme +86 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/xctestplan-everything.xctestplan +38 -0
- sim_mirror-2.1.0/src/sim_mirror/testing/fixtures/xctestplan-units-only.xctestplan +42 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/native.py +6 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/rig.py +18 -1
- sim_mirror-2.1.0/src/sim_mirror/tools/act.py +50 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/app.py +31 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/context.py +10 -3
- sim_mirror-2.1.0/src/sim_mirror/tools/device.py +240 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/record.py +6 -1
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/registry.py +9 -2
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/results.py +2 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/schemas.py +79 -17
- sim_mirror-2.0.0/src/sim_mirror/mcp/relay.py +0 -316
- sim_mirror-2.0.0/src/sim_mirror/server/static/viewer/start.js +0 -32
- sim_mirror-2.0.0/src/sim_mirror/tools/act.py +0 -31
- sim_mirror-2.0.0/src/sim_mirror/tools/device.py +0 -128
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/.gitignore +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/LICENSE +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/NOTICE +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/THIRD_PARTY_LICENSES.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/docs/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/docs/media/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/custom-connector/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/embed-host/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/iframe/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/token-budget/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/examples/web-component/README.md +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Package.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Capture.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/FrameScheduler.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/JSON.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperCore/Video.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/Disclaimed.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/Framebuffer.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/Frameworks.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/H264Stream.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/HIDTransports.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/Pictures.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/PixelSource.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/ScreenFeed.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/SimDeviceHandle.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/UnixSocket.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/UsbCapture.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/HelperPlatform/Warming.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/ObjCGuard/ObjCGuard.m +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/ObjCGuard/include/ObjCGuard.h +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Sources/sim-mirror-helper/main.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Support/Info.plist +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/CaptureTests.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/InputTests.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/PictureTests.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/helper/Tests/HelperCoreTests/WireTests.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/error-inactive.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/error-unauthorized.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/hierarchy-keyboard.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/hierarchy-swiftui.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/hierarchy-tagged.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/hierarchy-uikit.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/examples/listing.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/app-sdk/v1/listing.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/agent-event.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/client-input.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/close-codes.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/hello.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/screen-text.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/settings.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/status.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/protocol/v1/stream.schema.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/scripts/hatch_build.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/__main__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/destination.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/wda.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/build/xcresult.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/app_command.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/device_command.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/devices.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/helper.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/open_viewer.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/settings_command.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/tokens.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/tools.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/version.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/wda.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/cli/xcode.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/discovery.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/env.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/provenance.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/settings_store.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/toml_source.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/config/writer.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/client.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/discovery.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/errors.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/merge.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/reader.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/app/wire.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/helper_process.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/frameworks.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/proto/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/proto/idb.proto +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/proto/idb_grpc.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/idb/proto/idb_pb2.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/cable.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/capture.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/screen.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/wda.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/wda_client.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/wda_setup.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/iphone/wda_source.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/client.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/connector.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/hierarchy.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/merge.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/mcpbridge/reader.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/native/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/native/helper.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/native/wire.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/registry.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/simctl/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/simctl/capture.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/connectors/simctl/connector.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/availability.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/device_logs.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/device_settings.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/devices.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/events.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/frames.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/gestures.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/reaper.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/recording_sources.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/render.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/status.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/text_entry.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/text_overlay.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/tickets.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/core/touch.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/confirmations.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/health.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/passes.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/daemon/tokens.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/macos.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/real_devices.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/report.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/doctor/tap.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/mcp/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/budget.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/vision/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/vision/helper.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/vision/probe.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/vision/text_recognizer.swift +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/perception/wait.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/developer_dir.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/device_data.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/devicectl.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/errors.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/identifiers.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/images.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/json_lines.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/keyboard.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/keychain.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/lockdown.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/process.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/swift.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/swiftpm.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/usbmux.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/xcode.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/platform/xcrun.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/py.typed +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/scope.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/agent_routes.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/envelope.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/log_redaction.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/pages.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/security.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/settings_routes.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/settings_view.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/server/socket_routes.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/storage/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/storage/app_support.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/storage/claims.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/storage/private.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/app_sdk.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/asgi.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-alert-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-app-sdk-alert.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-app-sdk-keyboard.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-app-sdk-swiftui.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-app-sdk-tagged.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-app-sdk-uikit.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-home-complete.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-home-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-keyboard-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-probe-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-safari-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-safari-typed.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-settings-interactable.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/ax-settings-ios27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/development-certificate.pem +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-appearance.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-apps.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-devices.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-displays.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-launch-missing.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-launch.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-lock-state.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-processes.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/devicectl-screenshot.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-answers.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-alert.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-home.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-keyboard.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-probe.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-safari.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-settings.txt +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/simctl-devices.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/simctl-runtimes.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcodebuild-list-names.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcodebuild-list.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcodebuild-settings.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcodebuild-test-plans-none.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcodebuild-test-plans.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-build-broken.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-build-ok.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-build-results-unbuilt.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-summary-retries.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-summary-swift-testing-xcode27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-summary-swift-testing.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-summary-unbuilt.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-summary.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-tests-retries.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-tests-swift-testing-xcode27.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-tests-swift-testing.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/fixtures/xcresult-test-tests.json +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/guards.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/pictures.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/recording.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/usbmux.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/vision.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/testing/wda.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/__init__.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/screenshot.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/tools/snapshot.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/src/sim_mirror/validation.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/tests/unit/protocol/test_messages.py +0 -0
- {sim_mirror-2.0.0 → sim_mirror-2.1.0}/viewer/README.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sim-mirror
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: Mirror and drive
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser.
|
|
5
5
|
Project-URL: Homepage, https://github.com/AndrewKochulab/sim-mirror
|
|
6
6
|
Project-URL: Documentation, https://github.com/AndrewKochulab/sim-mirror/tree/main/docs
|
|
7
7
|
Project-URL: Issues, https://github.com/AndrewKochulab/sim-mirror/issues
|
|
@@ -39,7 +39,7 @@ Description-Content-Type: text/markdown
|
|
|
39
39
|
|
|
40
40
|
# SimMirror
|
|
41
41
|
|
|
42
|
-
**Mirror and drive
|
|
42
|
+
**Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser.**
|
|
43
43
|
|
|
44
44
|
[](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml)
|
|
45
45
|
[](LICENSE)
|
|
@@ -52,14 +52,16 @@ through its notes, then fills in and saves a new one, the agent's cursor gliding
|
|
|
52
52
|
*A real session, sped up four times. [The whole recording](https://github.com/AndrewKochulab/sim-mirror/releases/download/v1.2.0/sim-mirror-plugin-demo.mp4)
|
|
53
53
|
runs 1m 39s.*
|
|
54
54
|
|
|
55
|
-
A live iOS Simulator in any browser tab or web page, an animated cursor that
|
|
56
|
-
to tap, and token-efficient UI snapshots so agents read the screen as compact
|
|
57
|
-
|
|
55
|
+
A live iOS Simulator -- or your own iPhone, over its cable -- in any browser tab or web page, an animated cursor that
|
|
56
|
+
shows exactly what your AI agent is about to tap, and token-efficient UI snapshots so agents read the screen as compact
|
|
57
|
+
text instead of screenshots. Record any screen as an MP4 or a GIF for a pull request. Works with Claude Code, Codex,
|
|
58
|
+
Cursor and any other MCP client.
|
|
58
59
|
|
|
59
|
-
> **Status: 2.
|
|
60
|
-
> release to [what SimMirror promises not to break](docs/stability.md). 2.0
|
|
61
|
-
> simulators -- live over the cable, driven through WebDriverAgent -- and records any screen as an MP4 or a GIF.
|
|
62
|
-
>
|
|
60
|
+
> **Status: 2.1.** The agent tools, the viewer, the protocol and the embedding API are stable, and a check holds every
|
|
61
|
+
> release to [what SimMirror promises not to break](docs/stability.md). 2.0 brought real iPhones and iPads beside the
|
|
62
|
+
> simulators -- live over the cable, driven through WebDriverAgent -- and records any screen as an MP4 or a GIF. 2.1
|
|
63
|
+
> reads and touches apps turned to landscape, keeps taps off what a tab bar covers, builds the worktree the agent works
|
|
64
|
+
> in, and adds observe mode for walkthroughs on accounts an agent must not change. It removes nothing 1.x promised.
|
|
63
65
|
|
|
64
66
|
## Why SimMirror
|
|
65
67
|
|
|
@@ -71,6 +73,9 @@ Claude Code, Codex, Cursor and any other MCP client.
|
|
|
71
73
|
in batches with waits, and reach for a screenshot only when a question is visual.
|
|
72
74
|
- **Read screens accessibility cannot.** A game or a canvas reads from its pixels, and an app under development can
|
|
73
75
|
share its own views, so icon-only buttons and hand-drawn controls read by name.
|
|
76
|
+
- **Test on a real iPhone.** Plug it in and it sits beside the simulators: live over the cable at up to 60 fps, touched
|
|
77
|
+
and typed on from the browser or by an agent, and given back everything SimMirror changed on it.
|
|
78
|
+
- **Show what happened.** Record a flow as an MP4 or a GIF with every touch drawn in, and put it in a pull request.
|
|
74
79
|
|
|
75
80
|
## Install
|
|
76
81
|
|
|
@@ -84,7 +89,7 @@ Claude Code, Codex, Cursor and any other MCP client.
|
|
|
84
89
|
**Any MCP client**, as a stdio server:
|
|
85
90
|
|
|
86
91
|
```sh
|
|
87
|
-
claude mcp add sim-mirror -- uvx --from sim-mirror==2.
|
|
92
|
+
claude mcp add sim-mirror -- uvx --from sim-mirror==2.1.0 sim-mirror mcp
|
|
88
93
|
```
|
|
89
94
|
|
|
90
95
|
Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.md) and
|
|
@@ -93,7 +98,7 @@ Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.
|
|
|
93
98
|
**The command**, for `sim-mirror open`, `doctor` and the rest:
|
|
94
99
|
|
|
95
100
|
```sh
|
|
96
|
-
uv tool install sim-mirror==2.
|
|
101
|
+
uv tool install sim-mirror==2.1.0
|
|
97
102
|
# or
|
|
98
103
|
brew install andrewkochulab/tap/sim-mirror
|
|
99
104
|
```
|
|
@@ -103,8 +108,8 @@ brew install andrewkochulab/tap/sim-mirror
|
|
|
103
108
|
[on PyPI](https://pypi.org/project/sim-mirror/) and in a [Homebrew tap](https://github.com/AndrewKochulab/homebrew-tap).
|
|
104
109
|
|
|
105
110
|
**Requirements:** an Apple Silicon Mac, Xcode 26 or later, and [uv](https://docs.astral.sh/uv/) or Homebrew. Nothing
|
|
106
|
-
else: SimMirror drives a simulator with its own helper, which comes in the package.
|
|
107
|
-
[Installation](docs/installation.md).
|
|
111
|
+
else: SimMirror drives a simulator with its own helper, which comes in the package. A real iPhone needs Developer Mode,
|
|
112
|
+
and an Apple development team to be touched. Details in [Installation](docs/installation.md).
|
|
108
113
|
|
|
109
114
|
## Quickstart
|
|
110
115
|
|
|
@@ -118,6 +123,12 @@ Then ask your agent:
|
|
|
118
123
|
> Open Settings → General → About and tell me which iOS version this simulator runs. Use sim_snapshot rather than
|
|
119
124
|
> screenshots.
|
|
120
125
|
|
|
126
|
+
With an iPhone plugged in, unlocked and in Developer Mode:
|
|
127
|
+
|
|
128
|
+
> Switch to my iPhone, build and run the app on it, walk through sign-up and record it as a GIF.
|
|
129
|
+
|
|
130
|
+
The first time, press **Set up touch** in the viewer: it builds WebDriverAgent with your project's own signing team.
|
|
131
|
+
|
|
121
132
|
[Getting started](docs/getting-started.md) explains what happens along the way.
|
|
122
133
|
|
|
123
134
|
## Features
|
|
@@ -131,8 +142,13 @@ the viewer's own pointer glides there first (`agent.cursor_lead_ms`). Never the
|
|
|
131
142
|
|
|
132
143
|
**Screen understanding.** A snapshot is a few lines -- `e2 button "General" (201,319)` -- with refs that stay with their
|
|
133
144
|
elements across scrolls, a digest, and diffs. `sim_act` plays up to 20 steps in one call and waits for text, its
|
|
134
|
-
absence, or the screen to settle -- past a spinner or a pulsing dot that never stops.
|
|
135
|
-
|
|
145
|
+
absence, or the screen to settle -- past a spinner or a pulsing dot that never stops. An app turned to landscape is
|
|
146
|
+
read and touched in its own points, `sim_device rotate` turns a simulator, and a tap on what a tab bar, a toolbar or
|
|
147
|
+
the keyboard covers is refused with how to reach it. See [Screen understanding](docs/screen-understanding.md).
|
|
148
|
+
|
|
149
|
+
**Observe mode.** For a walkthrough on an account the agent must not change, `agent.observe` refuses a tap on what
|
|
150
|
+
reads like Delete, Save, Send or Sign out -- in English and a dozen other languages, or styled destructive by its app --
|
|
151
|
+
unless the call confirms it. See [Security](docs/security.md).
|
|
136
152
|
|
|
137
153
|
**Reading pixels.** A screen whose accessibility says nothing -- a game, a canvas, an app still loading -- reads as the
|
|
138
154
|
text macOS's Vision finds in it, with refs to tap and text to wait for, and the viewer can outline what was read
|
|
@@ -143,13 +159,15 @@ in a Debug build on the simulator it shares its own UIKit and SwiftUI views, so
|
|
|
143
159
|
gesture or a hand-drawn control reads by name, and `.simMirror("Settings")` names a SwiftUI view. Optional, and nothing
|
|
144
160
|
of it is in a Release build. See [The app SDK](docs/app-sdk.md).
|
|
145
161
|
|
|
146
|
-
**Real devices.** A cabled iPhone or iPad shows up beside the simulators
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
**Real devices.** A cabled iPhone or iPad shows up beside the simulators, and an agent can switch to it
|
|
163
|
+
(`sim_device list` and `choose`): its live screen over the cable, at about 55 frames a second; its apps, logs, look and
|
|
164
|
+
location through Xcode's `devicectl`; and touching, typing and its element tree through WebDriverAgent, set up once
|
|
165
|
+
with a click and signed by your project's own team -- a tap lands in about a third of a second. It is reached only over
|
|
166
|
+
the cable, and what SimMirror changes on it is put back when it is let go. See [Real devices](docs/real-devices.md).
|
|
150
167
|
|
|
151
168
|
**Recording.** The viewer's Record button, `sim_record` or `sim-mirror record` keep a device's screen as an MP4, a GIF
|
|
152
|
-
or both, with each touch drawn where it landed and a 9:41 status bar
|
|
169
|
+
or both, with each touch drawn where it landed and a 9:41 status bar, and `preset: "pr"` keeps one small enough for
|
|
170
|
+
a pull request. See [Recording](docs/recording.md).
|
|
153
171
|
|
|
154
172
|
**Connectors.** SimMirror's own native helper gives full control with nothing to install; idb_companion still can,
|
|
155
173
|
and the simctl connector mirrors the screen view-only when neither is there, and says why. `auto` uses the fastest that
|
|
@@ -172,7 +190,9 @@ scoped tokens, and one-shot codes in URL fragments. See [Security](docs/security
|
|
|
172
190
|
|
|
173
191
|
**Build and test.** `sim_build_run` and `sim_test` build with xcodebuild for the agent's simulator, install and
|
|
174
192
|
launch, and answer with only what failed and where -- each failing test named so it can be run again alone, and on
|
|
175
|
-
another simulator when asked
|
|
193
|
+
another simulator when asked -- and with the folder, branch and commit they built. They build the git worktree the
|
|
194
|
+
agent works in, with the configuration its scheme runs. Off by default, since they run commands: `sim-mirror config
|
|
195
|
+
set build.tools true`.
|
|
176
196
|
|
|
177
197
|
## Measure the token cost
|
|
178
198
|
|
|
@@ -206,8 +226,8 @@ an image at about 750 pixels a token).
|
|
|
206
226
|
Rows checked on real hardware are marked verified, with the date, in [Compatibility](docs/compatibility.md).
|
|
207
227
|
|
|
208
228
|
From 1.0 the tools, the protocol, the embedding API, the viewer package, settings and the command line are held to
|
|
209
|
-
[what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
|
|
210
|
-
exception, and pin a minor version.
|
|
229
|
+
[what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
|
|
230
|
+
Connectors are the exception, and pin a minor version.
|
|
211
231
|
|
|
212
232
|
## Documentation
|
|
213
233
|
|
|
@@ -223,7 +243,8 @@ the [command line](docs/reference/cli.md) and the [protocol](docs/reference/prot
|
|
|
223
243
|
| **v0.2** | Build, run and test tools, a settings panel, Xcode 27's UI hierarchy through `mcpbridge`, one shared daemon for many hosts |
|
|
224
244
|
| **v1.0** | Stable protocol and embedding API, held by a check; PyPI, npm and the MCP Registry; a cursor that stays while the agent works |
|
|
225
245
|
| **v1.2** | SimMirror's own native helper instead of idb_companion, screens read from their pixels and settling past endless animations, an optional in-app debug SDK, and a Homebrew tap |
|
|
226
|
-
| **v2.0**
|
|
246
|
+
| **v2.0** | Real iPhones and iPads: the live screen over the cable, apps, logs, look and location without signing; full control through WebDriverAgent; recording a screen as an MP4 or GIF |
|
|
247
|
+
| **v2.1** (now) | Apps turned to landscape and `sim_device rotate`, taps kept off what bars cover, builds of the agent's worktree, observe mode, `sim-mirror stop`, and recordings sized for a pull request |
|
|
227
248
|
| **Android** | Android emulator support with the same viewer, cursor and tools |
|
|
228
249
|
| **Website & launch** | Landing page, video tutorials and guides |
|
|
229
250
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# SimMirror
|
|
4
4
|
|
|
5
|
-
**Mirror and drive
|
|
5
|
+
**Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser.**
|
|
6
6
|
|
|
7
7
|
[](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml)
|
|
8
8
|
[](LICENSE)
|
|
@@ -15,14 +15,16 @@ through its notes, then fills in and saves a new one, the agent's cursor gliding
|
|
|
15
15
|
*A real session, sped up four times. [The whole recording](https://github.com/AndrewKochulab/sim-mirror/releases/download/v1.2.0/sim-mirror-plugin-demo.mp4)
|
|
16
16
|
runs 1m 39s.*
|
|
17
17
|
|
|
18
|
-
A live iOS Simulator in any browser tab or web page, an animated cursor that
|
|
19
|
-
to tap, and token-efficient UI snapshots so agents read the screen as compact
|
|
20
|
-
|
|
18
|
+
A live iOS Simulator -- or your own iPhone, over its cable -- in any browser tab or web page, an animated cursor that
|
|
19
|
+
shows exactly what your AI agent is about to tap, and token-efficient UI snapshots so agents read the screen as compact
|
|
20
|
+
text instead of screenshots. Record any screen as an MP4 or a GIF for a pull request. Works with Claude Code, Codex,
|
|
21
|
+
Cursor and any other MCP client.
|
|
21
22
|
|
|
22
|
-
> **Status: 2.
|
|
23
|
-
> release to [what SimMirror promises not to break](docs/stability.md). 2.0
|
|
24
|
-
> simulators -- live over the cable, driven through WebDriverAgent -- and records any screen as an MP4 or a GIF.
|
|
25
|
-
>
|
|
23
|
+
> **Status: 2.1.** The agent tools, the viewer, the protocol and the embedding API are stable, and a check holds every
|
|
24
|
+
> release to [what SimMirror promises not to break](docs/stability.md). 2.0 brought real iPhones and iPads beside the
|
|
25
|
+
> simulators -- live over the cable, driven through WebDriverAgent -- and records any screen as an MP4 or a GIF. 2.1
|
|
26
|
+
> reads and touches apps turned to landscape, keeps taps off what a tab bar covers, builds the worktree the agent works
|
|
27
|
+
> in, and adds observe mode for walkthroughs on accounts an agent must not change. It removes nothing 1.x promised.
|
|
26
28
|
|
|
27
29
|
## Why SimMirror
|
|
28
30
|
|
|
@@ -34,6 +36,9 @@ Claude Code, Codex, Cursor and any other MCP client.
|
|
|
34
36
|
in batches with waits, and reach for a screenshot only when a question is visual.
|
|
35
37
|
- **Read screens accessibility cannot.** A game or a canvas reads from its pixels, and an app under development can
|
|
36
38
|
share its own views, so icon-only buttons and hand-drawn controls read by name.
|
|
39
|
+
- **Test on a real iPhone.** Plug it in and it sits beside the simulators: live over the cable at up to 60 fps, touched
|
|
40
|
+
and typed on from the browser or by an agent, and given back everything SimMirror changed on it.
|
|
41
|
+
- **Show what happened.** Record a flow as an MP4 or a GIF with every touch drawn in, and put it in a pull request.
|
|
37
42
|
|
|
38
43
|
## Install
|
|
39
44
|
|
|
@@ -47,7 +52,7 @@ Claude Code, Codex, Cursor and any other MCP client.
|
|
|
47
52
|
**Any MCP client**, as a stdio server:
|
|
48
53
|
|
|
49
54
|
```sh
|
|
50
|
-
claude mcp add sim-mirror -- uvx --from sim-mirror==2.
|
|
55
|
+
claude mcp add sim-mirror -- uvx --from sim-mirror==2.1.0 sim-mirror mcp
|
|
51
56
|
```
|
|
52
57
|
|
|
53
58
|
Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.md) and
|
|
@@ -56,7 +61,7 @@ Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.
|
|
|
56
61
|
**The command**, for `sim-mirror open`, `doctor` and the rest:
|
|
57
62
|
|
|
58
63
|
```sh
|
|
59
|
-
uv tool install sim-mirror==2.
|
|
64
|
+
uv tool install sim-mirror==2.1.0
|
|
60
65
|
# or
|
|
61
66
|
brew install andrewkochulab/tap/sim-mirror
|
|
62
67
|
```
|
|
@@ -66,8 +71,8 @@ brew install andrewkochulab/tap/sim-mirror
|
|
|
66
71
|
[on PyPI](https://pypi.org/project/sim-mirror/) and in a [Homebrew tap](https://github.com/AndrewKochulab/homebrew-tap).
|
|
67
72
|
|
|
68
73
|
**Requirements:** an Apple Silicon Mac, Xcode 26 or later, and [uv](https://docs.astral.sh/uv/) or Homebrew. Nothing
|
|
69
|
-
else: SimMirror drives a simulator with its own helper, which comes in the package.
|
|
70
|
-
[Installation](docs/installation.md).
|
|
74
|
+
else: SimMirror drives a simulator with its own helper, which comes in the package. A real iPhone needs Developer Mode,
|
|
75
|
+
and an Apple development team to be touched. Details in [Installation](docs/installation.md).
|
|
71
76
|
|
|
72
77
|
## Quickstart
|
|
73
78
|
|
|
@@ -81,6 +86,12 @@ Then ask your agent:
|
|
|
81
86
|
> Open Settings → General → About and tell me which iOS version this simulator runs. Use sim_snapshot rather than
|
|
82
87
|
> screenshots.
|
|
83
88
|
|
|
89
|
+
With an iPhone plugged in, unlocked and in Developer Mode:
|
|
90
|
+
|
|
91
|
+
> Switch to my iPhone, build and run the app on it, walk through sign-up and record it as a GIF.
|
|
92
|
+
|
|
93
|
+
The first time, press **Set up touch** in the viewer: it builds WebDriverAgent with your project's own signing team.
|
|
94
|
+
|
|
84
95
|
[Getting started](docs/getting-started.md) explains what happens along the way.
|
|
85
96
|
|
|
86
97
|
## Features
|
|
@@ -94,8 +105,13 @@ the viewer's own pointer glides there first (`agent.cursor_lead_ms`). Never the
|
|
|
94
105
|
|
|
95
106
|
**Screen understanding.** A snapshot is a few lines -- `e2 button "General" (201,319)` -- with refs that stay with their
|
|
96
107
|
elements across scrolls, a digest, and diffs. `sim_act` plays up to 20 steps in one call and waits for text, its
|
|
97
|
-
absence, or the screen to settle -- past a spinner or a pulsing dot that never stops.
|
|
98
|
-
|
|
108
|
+
absence, or the screen to settle -- past a spinner or a pulsing dot that never stops. An app turned to landscape is
|
|
109
|
+
read and touched in its own points, `sim_device rotate` turns a simulator, and a tap on what a tab bar, a toolbar or
|
|
110
|
+
the keyboard covers is refused with how to reach it. See [Screen understanding](docs/screen-understanding.md).
|
|
111
|
+
|
|
112
|
+
**Observe mode.** For a walkthrough on an account the agent must not change, `agent.observe` refuses a tap on what
|
|
113
|
+
reads like Delete, Save, Send or Sign out -- in English and a dozen other languages, or styled destructive by its app --
|
|
114
|
+
unless the call confirms it. See [Security](docs/security.md).
|
|
99
115
|
|
|
100
116
|
**Reading pixels.** A screen whose accessibility says nothing -- a game, a canvas, an app still loading -- reads as the
|
|
101
117
|
text macOS's Vision finds in it, with refs to tap and text to wait for, and the viewer can outline what was read
|
|
@@ -106,13 +122,15 @@ in a Debug build on the simulator it shares its own UIKit and SwiftUI views, so
|
|
|
106
122
|
gesture or a hand-drawn control reads by name, and `.simMirror("Settings")` names a SwiftUI view. Optional, and nothing
|
|
107
123
|
of it is in a Release build. See [The app SDK](docs/app-sdk.md).
|
|
108
124
|
|
|
109
|
-
**Real devices.** A cabled iPhone or iPad shows up beside the simulators
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
125
|
+
**Real devices.** A cabled iPhone or iPad shows up beside the simulators, and an agent can switch to it
|
|
126
|
+
(`sim_device list` and `choose`): its live screen over the cable, at about 55 frames a second; its apps, logs, look and
|
|
127
|
+
location through Xcode's `devicectl`; and touching, typing and its element tree through WebDriverAgent, set up once
|
|
128
|
+
with a click and signed by your project's own team -- a tap lands in about a third of a second. It is reached only over
|
|
129
|
+
the cable, and what SimMirror changes on it is put back when it is let go. See [Real devices](docs/real-devices.md).
|
|
113
130
|
|
|
114
131
|
**Recording.** The viewer's Record button, `sim_record` or `sim-mirror record` keep a device's screen as an MP4, a GIF
|
|
115
|
-
or both, with each touch drawn where it landed and a 9:41 status bar
|
|
132
|
+
or both, with each touch drawn where it landed and a 9:41 status bar, and `preset: "pr"` keeps one small enough for
|
|
133
|
+
a pull request. See [Recording](docs/recording.md).
|
|
116
134
|
|
|
117
135
|
**Connectors.** SimMirror's own native helper gives full control with nothing to install; idb_companion still can,
|
|
118
136
|
and the simctl connector mirrors the screen view-only when neither is there, and says why. `auto` uses the fastest that
|
|
@@ -135,7 +153,9 @@ scoped tokens, and one-shot codes in URL fragments. See [Security](docs/security
|
|
|
135
153
|
|
|
136
154
|
**Build and test.** `sim_build_run` and `sim_test` build with xcodebuild for the agent's simulator, install and
|
|
137
155
|
launch, and answer with only what failed and where -- each failing test named so it can be run again alone, and on
|
|
138
|
-
another simulator when asked
|
|
156
|
+
another simulator when asked -- and with the folder, branch and commit they built. They build the git worktree the
|
|
157
|
+
agent works in, with the configuration its scheme runs. Off by default, since they run commands: `sim-mirror config
|
|
158
|
+
set build.tools true`.
|
|
139
159
|
|
|
140
160
|
## Measure the token cost
|
|
141
161
|
|
|
@@ -169,8 +189,8 @@ an image at about 750 pixels a token).
|
|
|
169
189
|
Rows checked on real hardware are marked verified, with the date, in [Compatibility](docs/compatibility.md).
|
|
170
190
|
|
|
171
191
|
From 1.0 the tools, the protocol, the embedding API, the viewer package, settings and the command line are held to
|
|
172
|
-
[what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
|
|
173
|
-
exception, and pin a minor version.
|
|
192
|
+
[what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
|
|
193
|
+
Connectors are the exception, and pin a minor version.
|
|
174
194
|
|
|
175
195
|
## Documentation
|
|
176
196
|
|
|
@@ -186,7 +206,8 @@ the [command line](docs/reference/cli.md) and the [protocol](docs/reference/prot
|
|
|
186
206
|
| **v0.2** | Build, run and test tools, a settings panel, Xcode 27's UI hierarchy through `mcpbridge`, one shared daemon for many hosts |
|
|
187
207
|
| **v1.0** | Stable protocol and embedding API, held by a check; PyPI, npm and the MCP Registry; a cursor that stays while the agent works |
|
|
188
208
|
| **v1.2** | SimMirror's own native helper instead of idb_companion, screens read from their pixels and settling past endless animations, an optional in-app debug SDK, and a Homebrew tap |
|
|
189
|
-
| **v2.0**
|
|
209
|
+
| **v2.0** | Real iPhones and iPads: the live screen over the cable, apps, logs, look and location without signing; full control through WebDriverAgent; recording a screen as an MP4 or GIF |
|
|
210
|
+
| **v2.1** (now) | Apps turned to landscape and `sim_device rotate`, taps kept off what bars cover, builds of the agent's worktree, observe mode, `sim-mirror stop`, and recordings sized for a pull request |
|
|
190
211
|
| **Android** | Android emulator support with the same viewer, cursor and tools |
|
|
191
212
|
| **Website & launch** | Landing page, video tutorials and guides |
|
|
192
213
|
|
|
@@ -16,7 +16,7 @@ Its tools are named `mcp__plugin_sim-mirror_sim-mirror__sim_snapshot` and so on.
|
|
|
16
16
|
## Without the plugin
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
claude mcp add sim-mirror -- uvx --from sim-mirror==2.
|
|
19
|
+
claude mcp add sim-mirror -- uvx --from sim-mirror==2.1.0 sim-mirror mcp
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Add `--scope project` before the name to write it to the project's `.mcp.json` for everyone working on it. Its tools
|
|
@@ -30,7 +30,7 @@ simulator (its scope) and this folder as the only place builds and installs may
|
|
|
30
30
|
## Watch it
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
uvx --from sim-mirror==2.
|
|
33
|
+
uvx --from sim-mirror==2.1.0 sim-mirror open
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
opens this folder's viewer in your browser. Every gesture the agent makes is drawn there as a cursor just before it
|
|
@@ -6,7 +6,7 @@ Add [config.toml](config.toml)'s table to `~/.codex/config.toml`, then start Cod
|
|
|
6
6
|
and installs may reach. The commented `args` pin a project by name and path instead.
|
|
7
7
|
- `tool_timeout_sec` is raised because `sim_build_run` and `sim_test` can take minutes (they are there only when build
|
|
8
8
|
tools are switched on).
|
|
9
|
-
- Watch and take over in a browser: `uvx --from sim-mirror==2.
|
|
9
|
+
- Watch and take over in a browser: `uvx --from sim-mirror==2.1.0 sim-mirror open`
|
|
10
10
|
in the same folder.
|
|
11
11
|
|
|
12
12
|
The tools are the same for every client; see [claude-code-cli](../claude-code-cli/) for a first prompt to try.
|
|
@@ -6,7 +6,7 @@ one), and enable the server in Cursor's MCP settings.
|
|
|
6
6
|
- `sim-mirror mcp` gives the agent the simulator of the folder it starts in, and that folder as the only place builds
|
|
7
7
|
and installs may reach. If the tools act on the wrong project, append `"--scope", "my-app", "--root",
|
|
8
8
|
"/absolute/path/to/my-app"` to `args`.
|
|
9
|
-
- Watch and take over in a browser: `uvx --from sim-mirror==2.
|
|
9
|
+
- Watch and take over in a browser: `uvx --from sim-mirror==2.1.0 sim-mirror open`
|
|
10
10
|
in the same folder.
|
|
11
11
|
|
|
12
12
|
The tools are the same for every client; see [claude-code-cli](../claude-code-cli/) for a first prompt to try.
|
|
@@ -5,7 +5,7 @@ A Python application with SimMirror inside it: [app.py](app.py). No daemon runs;
|
|
|
5
5
|
|
|
6
6
|
```sh
|
|
7
7
|
cd examples/fastapi-embed
|
|
8
|
-
EXAMPLE_KEY=change-me uv run --with "sim-mirror==2.
|
|
8
|
+
EXAMPLE_KEY=change-me uv run --with "sim-mirror==2.1.0" \
|
|
9
9
|
uvicorn --factory app:create_app --port 7484
|
|
10
10
|
```
|
|
11
11
|
|
|
@@ -36,12 +36,17 @@ public enum AccessibilityValue: Equatable, Sendable {
|
|
|
36
36
|
/// One element of a screen's accessibility tree, read from the device.
|
|
37
37
|
///
|
|
38
38
|
/// Only what SimMirror reads of an element is kept. Each attribute is a round trip to the simulator, and a snapshot
|
|
39
|
-
/// reads every element, so an attribute nothing reads --
|
|
40
|
-
/// asked
|
|
39
|
+
/// reads every element, so an attribute nothing reads -- help text, custom actions -- is not asked for at all. A role's
|
|
40
|
+
/// description is asked of a group only: it is how a navigation bar says what it is.
|
|
41
41
|
public struct AXNode: Equatable, Sendable {
|
|
42
|
+
/// The role of an element that only holds others, as a navigation bar, a tab bar and a toolbar are listed.
|
|
43
|
+
public static let groupRole = "AXGroup"
|
|
44
|
+
|
|
42
45
|
/// Its role as the accessibility API says it, ``AXButton``.
|
|
43
46
|
public var role: String?
|
|
44
47
|
public var subrole: String?
|
|
48
|
+
/// What its role is called, in the device's language: ``Nav bar`` for a navigation bar UIKit lists as a group.
|
|
49
|
+
public var roleDescription: String?
|
|
45
50
|
public var label: String?
|
|
46
51
|
public var title: String?
|
|
47
52
|
public var identifier: String?
|
|
@@ -53,12 +58,14 @@ public struct AXNode: Equatable, Sendable {
|
|
|
53
58
|
public var children: [AXNode]
|
|
54
59
|
|
|
55
60
|
public init(
|
|
56
|
-
role: String? = nil, subrole: String? = nil,
|
|
57
|
-
|
|
58
|
-
traits: UInt64? = nil, enabled: Bool = true,
|
|
61
|
+
role: String? = nil, subrole: String? = nil, roleDescription: String? = nil, label: String? = nil,
|
|
62
|
+
title: String? = nil, identifier: String? = nil, value: AccessibilityValue? = nil,
|
|
63
|
+
frame: Rect = Rect(x: 0, y: 0, width: 0, height: 0), traits: UInt64? = nil, enabled: Bool = true,
|
|
64
|
+
children: [AXNode] = []
|
|
59
65
|
) {
|
|
60
66
|
self.role = role
|
|
61
67
|
self.subrole = subrole
|
|
68
|
+
self.roleDescription = roleDescription
|
|
62
69
|
self.label = label
|
|
63
70
|
self.title = title
|
|
64
71
|
self.identifier = identifier
|
|
@@ -111,12 +118,15 @@ public enum AXDocument {
|
|
|
111
118
|
"Tab", "Key", "DisclosureTriangle",
|
|
112
119
|
]
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
/// The document of a tree. Its frames are the interface's points, and its ``orientation`` says how that interface
|
|
122
|
+
/// lies on the screen, whose portrait size ``screen`` is.
|
|
123
|
+
public static func make(_ tree: AXTree, screen: ScreenGeometry) -> JSONValue {
|
|
124
|
+
let elements = kept(tree.root)
|
|
116
125
|
return .object([
|
|
117
126
|
"backend": .string("native"),
|
|
118
127
|
"elements": .array(elements),
|
|
119
128
|
"modal": .null,
|
|
129
|
+
"orientation": .string(tree.orientation.rawValue),
|
|
120
130
|
"screen": .object([
|
|
121
131
|
"coordinate_space": .string("screen"),
|
|
122
132
|
"width": .int(screen.widthPt),
|
|
@@ -126,6 +136,11 @@ public enum AXDocument {
|
|
|
126
136
|
])
|
|
127
137
|
}
|
|
128
138
|
|
|
139
|
+
/// What was found at points of the screen: each element alone, as the document writes one, or null.
|
|
140
|
+
public static func found(_ nodes: [AXNode?]) -> JSONValue {
|
|
141
|
+
.object(["elements": .array(nodes.map { $0.map { element($0, children: []) } ?? .null })])
|
|
142
|
+
}
|
|
143
|
+
|
|
129
144
|
/// A role without its ``AX`` prefix: ``AXButton`` is ``Button``.
|
|
130
145
|
public static func type(of role: String?) -> String? {
|
|
131
146
|
guard let role else { return nil }
|
|
@@ -148,6 +163,7 @@ public enum AXDocument {
|
|
|
148
163
|
.object([
|
|
149
164
|
"type": .text(type(of: node.role)),
|
|
150
165
|
"subrole": .text(node.subrole),
|
|
166
|
+
"role_description": .text(node.roleDescription),
|
|
151
167
|
"label": .text(node.label),
|
|
152
168
|
"title": .text(node.title),
|
|
153
169
|
"identifier": .text(node.identifier),
|
|
@@ -142,7 +142,7 @@ public struct ServeOptions: Equatable, Sendable {
|
|
|
142
142
|
/// What `sim-mirror-helper version` prints.
|
|
143
143
|
public struct VersionReport: Equatable, Sendable, Encodable {
|
|
144
144
|
/// What this helper can do besides serving a simulator, so SimMirror asks only a helper that can.
|
|
145
|
-
public static let features = ["render", "capture"]
|
|
145
|
+
public static let features = ["render", "capture", "rotate", "at"]
|
|
146
146
|
|
|
147
147
|
public let version: String
|
|
148
148
|
public let wire: Int
|
|
@@ -217,7 +217,7 @@ public struct SelfCheckReport: Equatable, Sendable, Encodable {
|
|
|
217
217
|
return "\(jpeg.width)x\(jpeg.height), \(jpeg.data.count) bytes"
|
|
218
218
|
}
|
|
219
219
|
await check("input") { "through \(try device.input().transport.name)" }
|
|
220
|
-
await check("element tree") { "\(count(try await device.tree())) elements" }
|
|
220
|
+
await check("element tree") { "\(count(try await device.tree().root)) elements" }
|
|
221
221
|
return SelfCheckReport(parts: parts)
|
|
222
222
|
}
|
|
223
223
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
4
|
/// One input event, as SimMirror sends it: a finger at a point, a hardware button or a keyboard key, down or up.
|
|
5
|
-
/// Points are the
|
|
5
|
+
/// Points are the screen's own portrait points, whichever way an app's interface is turned: so is a simulator's digitizer.
|
|
6
6
|
public struct HIDEvent: Equatable, Sendable, Codable {
|
|
7
7
|
public enum Kind: String, Sendable, Codable { case touch, button, key }
|
|
8
8
|
public enum Phase: String, Sendable, Codable { case down, up }
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// How an app's interface lies on the device's screen, as UIKit names the four ways.
|
|
5
|
+
///
|
|
6
|
+
/// A simulator's screen never turns: its framebuffer, its touches and `ScreenGeometry` are the device's portrait points
|
|
7
|
+
/// whichever way the interface is drawn. Accessibility answers in the interface's own points, so a tree read from an app
|
|
8
|
+
/// turned to landscape is 874 wide on a screen 402 wide. The orientation is what turns one into the other. The device
|
|
9
|
+
/// turned in Simulator.app is no different: measured on iOS 26.5, its framebuffer stays portrait too.
|
|
10
|
+
public enum InterfaceOrientation: String, Sendable, CaseIterable {
|
|
11
|
+
case portrait
|
|
12
|
+
case portraitUpsideDown = "portrait_upside_down"
|
|
13
|
+
case landscapeLeft = "landscape_left"
|
|
14
|
+
case landscapeRight = "landscape_right"
|
|
15
|
+
|
|
16
|
+
/// Whether the interface is the screen turned a quarter, so its width is the screen's height.
|
|
17
|
+
public var sideways: Bool {
|
|
18
|
+
self == .landscapeLeft || self == .landscapeRight
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Where a point on the screen, in the screen's portrait points, is in the interface's points.
|
|
22
|
+
///
|
|
23
|
+
/// Measured on an iPhone 17 Pro simulator: an app turned to landscape right is drawn with its top along the
|
|
24
|
+
/// screen's right edge, so the screen's top right corner is the interface's top left.
|
|
25
|
+
public func interfacePoint(x: Double, y: Double, on screen: ScreenGeometry) -> (x: Double, y: Double) {
|
|
26
|
+
let width = Double(screen.widthPt)
|
|
27
|
+
let height = Double(screen.heightPt)
|
|
28
|
+
switch self {
|
|
29
|
+
case .portrait: return (x, y)
|
|
30
|
+
case .portraitUpsideDown: return (width - x, height - y)
|
|
31
|
+
case .landscapeRight: return (y, width - x)
|
|
32
|
+
case .landscapeLeft: return (height - y, x)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Where a point of the interface, in its points, is on the screen, in the screen's portrait points.
|
|
37
|
+
public func screenPoint(x: Double, y: Double, on screen: ScreenGeometry) -> (x: Double, y: Double) {
|
|
38
|
+
let width = Double(screen.widthPt)
|
|
39
|
+
let height = Double(screen.heightPt)
|
|
40
|
+
switch self {
|
|
41
|
+
case .portrait: return (x, y)
|
|
42
|
+
case .portraitUpsideDown: return (width - x, height - y)
|
|
43
|
+
case .landscapeRight: return (width - y, x)
|
|
44
|
+
case .landscapeLeft: return (y, height - x)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// How many of a tree's elements are looked for on the screen before points of it are.
|
|
49
|
+
static let elementsAsked = 4
|
|
50
|
+
|
|
51
|
+
/// The share of the screen probed for which way the interface is turned, across and down: inside the edges, and
|
|
52
|
+
/// nowhere two orientations put the same interface point.
|
|
53
|
+
static let probes: [(Double, Double)] = [
|
|
54
|
+
(0.3, 0.25), (0.7, 0.25), (0.3, 0.75), (0.7, 0.75), (0.5, 0.15), (0.5, 0.85), (0.2, 0.5), (0.8, 0.5),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
/// Which way a tree's interface is turned, from the size of its root -- the frontmost application -- and what is
|
|
58
|
+
/// found at points of the screen.
|
|
59
|
+
///
|
|
60
|
+
/// An interface as wide as the screen is portrait or upside down, and one as wide as the screen is tall is one of the
|
|
61
|
+
/// landscapes. Which of the two is told by asking what is at a point of the screen (`hit`, answering the frame of
|
|
62
|
+
/// the element there in the interface's points, as the tree's are). The middle of one of the tree's small elements
|
|
63
|
+
/// is asked for where each of the two would put it on the screen: only the right one finds an element there. Then
|
|
64
|
+
/// points spread over the screen are asked: only the right one puts the point inside what is found. A point that
|
|
65
|
+
/// finds nothing, or an element both would put it in, tells nothing, and the next is asked. When none tells, the
|
|
66
|
+
/// first of the two: portrait, or landscape right -- where UIKit puts an app that takes either landscape.
|
|
67
|
+
public static func find(
|
|
68
|
+
_ root: AXNode, screen: ScreenGeometry, hit: (_ x: Double, _ y: Double) -> Rect?
|
|
69
|
+
) -> InterfaceOrientation {
|
|
70
|
+
let interface = root.frame
|
|
71
|
+
let wideScreen = screen.widthPt > screen.heightPt
|
|
72
|
+
let quarter = interface.width > 0 && interface.height > 0 && (interface.width > interface.height) != wideScreen
|
|
73
|
+
let candidates: [InterfaceOrientation] = quarter ? [.landscapeRight, .landscapeLeft] : [.portrait, .portraitUpsideDown]
|
|
74
|
+
let most = Double(screen.widthPt * screen.heightPt) / 4
|
|
75
|
+
let small = leaves(root).filter { $0.width >= 4 && $0.height >= 4 && $0.width * $0.height <= most }
|
|
76
|
+
for frame in small.prefix(elementsAsked) {
|
|
77
|
+
let middle = (x: frame.x + frame.width / 2, y: frame.y + frame.height / 2)
|
|
78
|
+
let fitting = candidates.filter { candidate in
|
|
79
|
+
let point = candidate.screenPoint(x: middle.x, y: middle.y, on: screen)
|
|
80
|
+
return hit(point.x, point.y)?.contains(point: middle) ?? false
|
|
81
|
+
}
|
|
82
|
+
if fitting.count == 1 { return fitting[0] }
|
|
83
|
+
}
|
|
84
|
+
for (across, down) in probes {
|
|
85
|
+
let x = across * Double(screen.widthPt)
|
|
86
|
+
let y = down * Double(screen.heightPt)
|
|
87
|
+
guard let found = hit(x, y), found.width > 0, found.height > 0 else { continue }
|
|
88
|
+
let fitting = candidates.filter { found.contains(point: $0.interfacePoint(x: x, y: y, on: screen)) }
|
|
89
|
+
if fitting.count == 1 { return fitting[0] }
|
|
90
|
+
}
|
|
91
|
+
return candidates[0]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// The frames of the elements with nothing inside them, in the tree's order.
|
|
95
|
+
static func leaves(_ node: AXNode) -> [Rect] {
|
|
96
|
+
node.children.isEmpty ? [node.frame] : node.children.flatMap(leaves)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
extension Rect {
|
|
101
|
+
/// Whether a point is inside, edges included, give or take half a point.
|
|
102
|
+
func contains(point: (x: Double, y: Double)) -> Bool {
|
|
103
|
+
let slack = 0.5
|
|
104
|
+
return point.x >= x - slack && point.x <= x + width + slack && point.y >= y - slack && point.y <= y + height + slack
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// A screen's accessibility tree and the way its interface is turned, which its frames are measured in.
|
|
109
|
+
public struct AXTree: Equatable, Sendable {
|
|
110
|
+
public let root: AXNode
|
|
111
|
+
public let orientation: InterfaceOrientation
|
|
112
|
+
|
|
113
|
+
public init(root: AXNode, orientation: InterfaceOrientation = .portrait) {
|
|
114
|
+
self.root = root
|
|
115
|
+
self.orientation = orientation
|
|
116
|
+
}
|
|
117
|
+
}
|