sim-mirror 2.0.0__tar.gz → 2.0.1__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.
Files changed (347) hide show
  1. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/PKG-INFO +27 -16
  2. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/README.md +25 -14
  3. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/claude-code-cli/README.md +2 -2
  4. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/codex/README.md +1 -1
  5. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/cursor/README.md +1 -1
  6. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/fastapi-embed/README.md +1 -1
  7. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Wire.swift +1 -1
  8. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/pyproject.toml +1 -1
  9. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/_version.py +1 -1
  10. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/context.py +12 -0
  11. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/mcp.py +1 -0
  12. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/wda_roles.py +5 -1
  13. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/actions.py +9 -1
  14. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/recordings.py +16 -0
  15. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/mcp/launcher.py +41 -3
  16. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/.gitignore +0 -0
  17. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/LICENSE +0 -0
  18. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/NOTICE +0 -0
  19. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/THIRD_PARTY_LICENSES.md +0 -0
  20. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/docs/README.md +0 -0
  21. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/docs/media/README.md +0 -0
  22. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/README.md +0 -0
  23. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/custom-connector/README.md +0 -0
  24. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/embed-host/README.md +0 -0
  25. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/iframe/README.md +0 -0
  26. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/token-budget/README.md +0 -0
  27. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/examples/web-component/README.md +0 -0
  28. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Package.swift +0 -0
  29. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Accessibility.swift +0 -0
  30. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Arguments.swift +0 -0
  31. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Capture.swift +0 -0
  32. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/FrameScheduler.swift +0 -0
  33. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Input.swift +0 -0
  34. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/JSON.swift +0 -0
  35. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Recording.swift +0 -0
  36. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Requests.swift +0 -0
  37. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Screen.swift +0 -0
  38. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Server.swift +0 -0
  39. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperCore/Video.swift +0 -0
  40. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/AccessibilityReader.swift +0 -0
  41. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/CaptureDevice.swift +0 -0
  42. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/Disclaimed.swift +0 -0
  43. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/Framebuffer.swift +0 -0
  44. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/Frameworks.swift +0 -0
  45. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/H264Stream.swift +0 -0
  46. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/HIDTransports.swift +0 -0
  47. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/Pictures.swift +0 -0
  48. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/PixelSource.swift +0 -0
  49. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/RecordingRenderer.swift +0 -0
  50. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/ScreenFeed.swift +0 -0
  51. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/SimDeviceHandle.swift +0 -0
  52. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/SimulatorDevice.swift +0 -0
  53. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/UnixSocket.swift +0 -0
  54. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/UsbCapture.swift +0 -0
  55. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/HelperPlatform/Warming.swift +0 -0
  56. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/ObjCGuard/ObjCGuard.m +0 -0
  57. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/ObjCGuard/include/ObjCGuard.h +0 -0
  58. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Sources/sim-mirror-helper/main.swift +0 -0
  59. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Support/Info.plist +0 -0
  60. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/Fixtures/wire-vectors.json +0 -0
  61. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/AccessibilityTests.swift +0 -0
  62. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/ArgumentsTests.swift +0 -0
  63. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/CaptureTests.swift +0 -0
  64. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/InputTests.swift +0 -0
  65. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/PictureTests.swift +0 -0
  66. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/RecordingTests.swift +0 -0
  67. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/ServerTests.swift +0 -0
  68. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/helper/Tests/HelperCoreTests/WireTests.swift +0 -0
  69. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/README.md +0 -0
  70. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/error-inactive.json +0 -0
  71. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/error-unauthorized.json +0 -0
  72. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/hierarchy-alert.json +0 -0
  73. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/hierarchy-keyboard.json +0 -0
  74. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/hierarchy-swiftui.json +0 -0
  75. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/hierarchy-tagged.json +0 -0
  76. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/hierarchy-uikit.json +0 -0
  77. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/examples/listing.json +0 -0
  78. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/hierarchy.schema.json +0 -0
  79. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/app-sdk/v1/listing.schema.json +0 -0
  80. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/agent-event.schema.json +0 -0
  81. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/client-input.schema.json +0 -0
  82. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/close-codes.json +0 -0
  83. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/common.schema.json +0 -0
  84. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/constants.json +0 -0
  85. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/hello.schema.json +0 -0
  86. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/http.schema.json +0 -0
  87. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/screen-text.schema.json +0 -0
  88. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/settings.schema.json +0 -0
  89. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/status.schema.json +0 -0
  90. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/protocol/v1/stream.schema.json +0 -0
  91. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/scripts/hatch_build.py +0 -0
  92. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/__init__.py +0 -0
  93. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/__main__.py +0 -0
  94. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/api.py +0 -0
  95. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/__init__.py +0 -0
  96. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/destination.py +0 -0
  97. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/provider.py +0 -0
  98. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/wda.py +0 -0
  99. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/xcodebuild.py +0 -0
  100. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/build/xcresult.py +0 -0
  101. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/__init__.py +0 -0
  102. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/app_command.py +0 -0
  103. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/config_command.py +0 -0
  104. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/device_command.py +0 -0
  105. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/devices.py +0 -0
  106. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/doctor.py +0 -0
  107. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/helper.py +0 -0
  108. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/main.py +0 -0
  109. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/open_viewer.py +0 -0
  110. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/record.py +0 -0
  111. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/serve.py +0 -0
  112. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/settings_command.py +0 -0
  113. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/tokens.py +0 -0
  114. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/tools.py +0 -0
  115. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/version.py +0 -0
  116. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/wda.py +0 -0
  117. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/cli/xcode.py +0 -0
  118. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/__init__.py +0 -0
  119. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/discovery.py +0 -0
  120. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/env.py +0 -0
  121. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/model.py +0 -0
  122. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/provenance.py +0 -0
  123. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/schema.py +0 -0
  124. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/settings_store.py +0 -0
  125. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/toml_source.py +0 -0
  126. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/config/writer.py +0 -0
  127. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/__init__.py +0 -0
  128. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/__init__.py +0 -0
  129. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/client.py +0 -0
  130. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/discovery.py +0 -0
  131. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/document.py +0 -0
  132. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/errors.py +0 -0
  133. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/merge.py +0 -0
  134. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/reader.py +0 -0
  135. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/app/wire.py +0 -0
  136. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/base.py +0 -0
  137. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/helper_process.py +0 -0
  138. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/__init__.py +0 -0
  139. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/companion.py +0 -0
  140. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/connector.py +0 -0
  141. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/engine.py +0 -0
  142. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/frameworks.py +0 -0
  143. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/proto/__init__.py +0 -0
  144. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/proto/idb.proto +0 -0
  145. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/proto/idb_grpc.py +0 -0
  146. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/idb/proto/idb_pb2.py +0 -0
  147. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/__init__.py +0 -0
  148. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/cable.py +0 -0
  149. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/capture.py +0 -0
  150. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/connector.py +0 -0
  151. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/screen.py +0 -0
  152. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/wda.py +0 -0
  153. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/wda_client.py +0 -0
  154. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/wda_setup.py +0 -0
  155. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/iphone/wda_source.py +0 -0
  156. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/__init__.py +0 -0
  157. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/client.py +0 -0
  158. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/connector.py +0 -0
  159. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/hierarchy.py +0 -0
  160. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/merge.py +0 -0
  161. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/mcpbridge/reader.py +0 -0
  162. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/native/__init__.py +0 -0
  163. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/native/client.py +0 -0
  164. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/native/connector.py +0 -0
  165. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/native/helper.py +0 -0
  166. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/native/wire.py +0 -0
  167. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/registry.py +0 -0
  168. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/simctl/__init__.py +0 -0
  169. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/simctl/capture.py +0 -0
  170. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/connectors/simctl/connector.py +0 -0
  171. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/__init__.py +0 -0
  172. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/availability.py +0 -0
  173. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/backends.py +0 -0
  174. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/control.py +0 -0
  175. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/device_changes.py +0 -0
  176. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/device_logs.py +0 -0
  177. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/device_settings.py +0 -0
  178. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/devices.py +0 -0
  179. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/events.py +0 -0
  180. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/frames.py +0 -0
  181. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/gestures.py +0 -0
  182. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/instance.py +0 -0
  183. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/manager.py +0 -0
  184. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/reaper.py +0 -0
  185. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/recording_sources.py +0 -0
  186. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/render.py +0 -0
  187. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/runtime.py +0 -0
  188. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/screen_input.py +0 -0
  189. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/screen_relay.py +0 -0
  190. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/signing.py +0 -0
  191. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/status.py +0 -0
  192. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/text_entry.py +0 -0
  193. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/text_overlay.py +0 -0
  194. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/tickets.py +0 -0
  195. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/core/touch.py +0 -0
  196. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/__init__.py +0 -0
  197. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/app.py +0 -0
  198. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/auth.py +0 -0
  199. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/confirmations.py +0 -0
  200. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/health.py +0 -0
  201. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/lease.py +0 -0
  202. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/lifecycle.py +0 -0
  203. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/passes.py +0 -0
  204. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/policy.py +0 -0
  205. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/daemon/tokens.py +0 -0
  206. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/__init__.py +0 -0
  207. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/checks.py +0 -0
  208. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/macos.py +0 -0
  209. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/real_devices.py +0 -0
  210. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/report.py +0 -0
  211. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/doctor/tap.py +0 -0
  212. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/host_copy.py +0 -0
  213. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/hosting.py +0 -0
  214. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/mcp/__init__.py +0 -0
  215. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/mcp/relay.py +0 -0
  216. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/__init__.py +0 -0
  217. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/budget.py +0 -0
  218. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/imagehash.py +0 -0
  219. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/model.py +0 -0
  220. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/ocr.py +0 -0
  221. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/readers.py +0 -0
  222. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/settle.py +0 -0
  223. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/snapshot.py +0 -0
  224. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/vision/__init__.py +0 -0
  225. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/vision/helper.py +0 -0
  226. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/vision/probe.py +0 -0
  227. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/vision/text_recognizer.swift +0 -0
  228. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/perception/wait.py +0 -0
  229. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/__init__.py +0 -0
  230. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/developer_dir.py +0 -0
  231. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/device_data.py +0 -0
  232. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/devicectl.py +0 -0
  233. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/errors.py +0 -0
  234. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/identifiers.py +0 -0
  235. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/images.py +0 -0
  236. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/json_lines.py +0 -0
  237. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/keyboard.py +0 -0
  238. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/keychain.py +0 -0
  239. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/lockdown.py +0 -0
  240. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/process.py +0 -0
  241. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/simctl.py +0 -0
  242. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/swift.py +0 -0
  243. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/swiftpm.py +0 -0
  244. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/usbmux.py +0 -0
  245. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/xcode.py +0 -0
  246. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/platform/xcrun.py +0 -0
  247. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/protocol/__init__.py +0 -0
  248. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/protocol/_generated.py +0 -0
  249. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/protocol/messages.py +0 -0
  250. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/py.typed +0 -0
  251. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/scope.py +0 -0
  252. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/seams.py +0 -0
  253. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/__init__.py +0 -0
  254. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/agent_routes.py +0 -0
  255. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/envelope.py +0 -0
  256. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/http_routes.py +0 -0
  257. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/log_redaction.py +0 -0
  258. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/pages.py +0 -0
  259. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/security.py +0 -0
  260. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/settings_routes.py +0 -0
  261. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/settings_view.py +0 -0
  262. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/socket_routes.py +0 -0
  263. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/static/viewer/embed.html +0 -0
  264. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/static/viewer/index.html +0 -0
  265. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/server/static/viewer/start.js +0 -0
  266. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/storage/__init__.py +0 -0
  267. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/storage/app_support.py +0 -0
  268. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/storage/claims.py +0 -0
  269. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/storage/private.py +0 -0
  270. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/__init__.py +0 -0
  271. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/app_sdk.py +0 -0
  272. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/asgi.py +0 -0
  273. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/contract.py +0 -0
  274. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/daemon.py +0 -0
  275. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fakes.py +0 -0
  276. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-alert-ios27.json +0 -0
  277. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-app-sdk-alert.json +0 -0
  278. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-app-sdk-keyboard.json +0 -0
  279. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-app-sdk-swiftui.json +0 -0
  280. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-app-sdk-tagged.json +0 -0
  281. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-app-sdk-uikit.json +0 -0
  282. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-home-complete.json +0 -0
  283. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-home-ios27.json +0 -0
  284. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-keyboard-ios27.json +0 -0
  285. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-probe-ios27.json +0 -0
  286. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-safari-ios27.json +0 -0
  287. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-safari-typed.json +0 -0
  288. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-settings-interactable.json +0 -0
  289. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/ax-settings-ios27.json +0 -0
  290. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/development-certificate.pem +0 -0
  291. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-appearance.json +0 -0
  292. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-apps.json +0 -0
  293. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-devices.json +0 -0
  294. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-displays.json +0 -0
  295. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-launch-missing.json +0 -0
  296. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-launch.json +0 -0
  297. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-lock-state.json +0 -0
  298. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-processes.json +0 -0
  299. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/devicectl-screenshot.json +0 -0
  300. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-answers.json +0 -0
  301. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-alert.txt +0 -0
  302. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-home.txt +0 -0
  303. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-keyboard.txt +0 -0
  304. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-probe.txt +0 -0
  305. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-safari.txt +0 -0
  306. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/mcpbridge-hierarchy-settings.txt +0 -0
  307. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/simctl-devices.json +0 -0
  308. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/simctl-runtimes.json +0 -0
  309. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcodebuild-list-names.json +0 -0
  310. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcodebuild-list.json +0 -0
  311. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcodebuild-settings.json +0 -0
  312. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcodebuild-test-plans-none.json +0 -0
  313. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcodebuild-test-plans.json +0 -0
  314. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-build-broken.json +0 -0
  315. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-build-ok.json +0 -0
  316. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-build-results-unbuilt.json +0 -0
  317. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-summary-retries.json +0 -0
  318. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-summary-swift-testing-xcode27.json +0 -0
  319. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-summary-swift-testing.json +0 -0
  320. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-summary-unbuilt.json +0 -0
  321. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-summary.json +0 -0
  322. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-tests-retries.json +0 -0
  323. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-tests-swift-testing-xcode27.json +0 -0
  324. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-tests-swift-testing.json +0 -0
  325. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/fixtures/xcresult-test-tests.json +0 -0
  326. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/guards.py +0 -0
  327. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/native.py +0 -0
  328. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/pictures.py +0 -0
  329. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/recording.py +0 -0
  330. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/rig.py +0 -0
  331. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/usbmux.py +0 -0
  332. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/vision.py +0 -0
  333. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/testing/wda.py +0 -0
  334. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/__init__.py +0 -0
  335. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/act.py +0 -0
  336. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/app.py +0 -0
  337. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/context.py +0 -0
  338. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/device.py +0 -0
  339. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/record.py +0 -0
  340. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/registry.py +0 -0
  341. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/results.py +0 -0
  342. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/schemas.py +0 -0
  343. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/screenshot.py +0 -0
  344. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/tools/snapshot.py +0 -0
  345. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/src/sim_mirror/validation.py +0 -0
  346. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/tests/unit/protocol/test_messages.py +0 -0
  347. {sim_mirror-2.0.0 → sim_mirror-2.0.1}/viewer/README.md +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sim-mirror
3
- Version: 2.0.0
4
- Summary: Mirror and drive the iOS Simulator from Claude Code, CLI agents and the browser.
3
+ Version: 2.0.1
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 the iOS Simulator from Claude Code, CLI agents and the browser.**
42
+ **Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser.**
43
43
 
44
44
  [![CI](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml/badge.svg)](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml)
45
45
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
@@ -52,9 +52,10 @@ 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 shows exactly what your AI agent is about
56
- to tap, and token-efficient UI snapshots so agents read the screen as compact text instead of screenshots. Works with
57
- Claude Code, Codex, Cursor and any other MCP client.
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
60
  > **Status: 2.0.** The agent tools, the viewer, the protocol and the embedding API are stable, and a check holds every
60
61
  > release to [what SimMirror promises not to break](docs/stability.md). 2.0 brings real iPhones and iPads beside the
@@ -71,6 +72,9 @@ Claude Code, Codex, Cursor and any other MCP client.
71
72
  in batches with waits, and reach for a screenshot only when a question is visual.
72
73
  - **Read screens accessibility cannot.** A game or a canvas reads from its pixels, and an app under development can
73
74
  share its own views, so icon-only buttons and hand-drawn controls read by name.
75
+ - **Test on a real iPhone.** Plug it in and it sits beside the simulators: live over the cable at up to 60 fps, touched
76
+ and typed on from the browser or by an agent, and given back everything SimMirror changed on it.
77
+ - **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
78
 
75
79
  ## Install
76
80
 
@@ -84,7 +88,7 @@ Claude Code, Codex, Cursor and any other MCP client.
84
88
  **Any MCP client**, as a stdio server:
85
89
 
86
90
  ```sh
87
- claude mcp add sim-mirror -- uvx --from sim-mirror==2.0.0 sim-mirror mcp
91
+ claude mcp add sim-mirror -- uvx --from sim-mirror==2.0.1 sim-mirror mcp
88
92
  ```
89
93
 
90
94
  Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.md) and
@@ -93,7 +97,7 @@ Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.
93
97
  **The command**, for `sim-mirror open`, `doctor` and the rest:
94
98
 
95
99
  ```sh
96
- uv tool install sim-mirror==2.0.0
100
+ uv tool install sim-mirror==2.0.1
97
101
  # or
98
102
  brew install andrewkochulab/tap/sim-mirror
99
103
  ```
@@ -103,8 +107,8 @@ brew install andrewkochulab/tap/sim-mirror
103
107
  [on PyPI](https://pypi.org/project/sim-mirror/) and in a [Homebrew tap](https://github.com/AndrewKochulab/homebrew-tap).
104
108
 
105
109
  **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. Details in
107
- [Installation](docs/installation.md).
110
+ else: SimMirror drives a simulator with its own helper, which comes in the package. A real iPhone needs Developer Mode,
111
+ and an Apple development team to be touched. Details in [Installation](docs/installation.md).
108
112
 
109
113
  ## Quickstart
110
114
 
@@ -118,6 +122,12 @@ Then ask your agent:
118
122
  > Open Settings → General → About and tell me which iOS version this simulator runs. Use sim_snapshot rather than
119
123
  > screenshots.
120
124
 
125
+ With an iPhone plugged in, unlocked and in Developer Mode:
126
+
127
+ > Switch to my iPhone, build and run the app on it, walk through sign-up and record it as a GIF.
128
+
129
+ The first time, press **Set up touch** in the viewer: it builds WebDriverAgent with your project's own signing team.
130
+
121
131
  [Getting started](docs/getting-started.md) explains what happens along the way.
122
132
 
123
133
  ## Features
@@ -143,10 +153,11 @@ in a Debug build on the simulator it shares its own UIKit and SwiftUI views, so
143
153
  gesture or a hand-drawn control reads by name, and `.simMirror("Settings")` names a SwiftUI view. Optional, and nothing
144
154
  of it is in a Release build. See [The app SDK](docs/app-sdk.md).
145
155
 
146
- **Real devices.** A cabled iPhone or iPad shows up beside the simulators: its live screen over the cable, its apps,
147
- logs, look and location through Xcode's `devicectl`, and touching, typing and its element tree through a WebDriverAgent
148
- you build with your own team. What SimMirror changes on it is put back when it is let go. See
149
- [Real devices](docs/real-devices.md).
156
+ **Real devices.** A cabled iPhone or iPad shows up beside the simulators, and an agent can switch to it
157
+ (`sim_device list` and `choose`): its live screen over the cable, at about 55 frames a second; its apps, logs, look and
158
+ location through Xcode's `devicectl`; and touching, typing and its element tree through WebDriverAgent, set up once
159
+ 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
160
+ the cable, and what SimMirror changes on it is put back when it is let go. See [Real devices](docs/real-devices.md).
150
161
 
151
162
  **Recording.** The viewer's Record button, `sim_record` or `sim-mirror record` keep a device's screen as an MP4, a GIF
152
163
  or both, with each touch drawn where it landed and a 9:41 status bar. See [Recording](docs/recording.md).
@@ -206,8 +217,8 @@ an image at about 750 pixels a token).
206
217
  Rows checked on real hardware are marked verified, with the date, in [Compatibility](docs/compatibility.md).
207
218
 
208
219
  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. Connectors are the
210
- exception, and pin a minor version.
220
+ [what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
221
+ Connectors are the exception, and pin a minor version.
211
222
 
212
223
  ## Documentation
213
224
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # SimMirror
4
4
 
5
- **Mirror and drive the iOS Simulator from Claude Code, CLI agents and the browser.**
5
+ **Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser.**
6
6
 
7
7
  [![CI](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml/badge.svg)](https://github.com/AndrewKochulab/sim-mirror/actions/workflows/ci.yml)
8
8
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
@@ -15,9 +15,10 @@ 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 shows exactly what your AI agent is about
19
- to tap, and token-efficient UI snapshots so agents read the screen as compact text instead of screenshots. Works with
20
- Claude Code, Codex, Cursor and any other MCP client.
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
23
  > **Status: 2.0.** The agent tools, the viewer, the protocol and the embedding API are stable, and a check holds every
23
24
  > release to [what SimMirror promises not to break](docs/stability.md). 2.0 brings real iPhones and iPads beside the
@@ -34,6 +35,9 @@ Claude Code, Codex, Cursor and any other MCP client.
34
35
  in batches with waits, and reach for a screenshot only when a question is visual.
35
36
  - **Read screens accessibility cannot.** A game or a canvas reads from its pixels, and an app under development can
36
37
  share its own views, so icon-only buttons and hand-drawn controls read by name.
38
+ - **Test on a real iPhone.** Plug it in and it sits beside the simulators: live over the cable at up to 60 fps, touched
39
+ and typed on from the browser or by an agent, and given back everything SimMirror changed on it.
40
+ - **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
41
 
38
42
  ## Install
39
43
 
@@ -47,7 +51,7 @@ Claude Code, Codex, Cursor and any other MCP client.
47
51
  **Any MCP client**, as a stdio server:
48
52
 
49
53
  ```sh
50
- claude mcp add sim-mirror -- uvx --from sim-mirror==2.0.0 sim-mirror mcp
54
+ claude mcp add sim-mirror -- uvx --from sim-mirror==2.0.1 sim-mirror mcp
51
55
  ```
52
56
 
53
57
  Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.md) and
@@ -56,7 +60,7 @@ Configurations for [Codex](docs/clients/codex.md), [Cursor](docs/clients/cursor.
56
60
  **The command**, for `sim-mirror open`, `doctor` and the rest:
57
61
 
58
62
  ```sh
59
- uv tool install sim-mirror==2.0.0
63
+ uv tool install sim-mirror==2.0.1
60
64
  # or
61
65
  brew install andrewkochulab/tap/sim-mirror
62
66
  ```
@@ -66,8 +70,8 @@ brew install andrewkochulab/tap/sim-mirror
66
70
  [on PyPI](https://pypi.org/project/sim-mirror/) and in a [Homebrew tap](https://github.com/AndrewKochulab/homebrew-tap).
67
71
 
68
72
  **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. Details in
70
- [Installation](docs/installation.md).
73
+ else: SimMirror drives a simulator with its own helper, which comes in the package. A real iPhone needs Developer Mode,
74
+ and an Apple development team to be touched. Details in [Installation](docs/installation.md).
71
75
 
72
76
  ## Quickstart
73
77
 
@@ -81,6 +85,12 @@ Then ask your agent:
81
85
  > Open Settings → General → About and tell me which iOS version this simulator runs. Use sim_snapshot rather than
82
86
  > screenshots.
83
87
 
88
+ With an iPhone plugged in, unlocked and in Developer Mode:
89
+
90
+ > Switch to my iPhone, build and run the app on it, walk through sign-up and record it as a GIF.
91
+
92
+ The first time, press **Set up touch** in the viewer: it builds WebDriverAgent with your project's own signing team.
93
+
84
94
  [Getting started](docs/getting-started.md) explains what happens along the way.
85
95
 
86
96
  ## Features
@@ -106,10 +116,11 @@ in a Debug build on the simulator it shares its own UIKit and SwiftUI views, so
106
116
  gesture or a hand-drawn control reads by name, and `.simMirror("Settings")` names a SwiftUI view. Optional, and nothing
107
117
  of it is in a Release build. See [The app SDK](docs/app-sdk.md).
108
118
 
109
- **Real devices.** A cabled iPhone or iPad shows up beside the simulators: its live screen over the cable, its apps,
110
- logs, look and location through Xcode's `devicectl`, and touching, typing and its element tree through a WebDriverAgent
111
- you build with your own team. What SimMirror changes on it is put back when it is let go. See
112
- [Real devices](docs/real-devices.md).
119
+ **Real devices.** A cabled iPhone or iPad shows up beside the simulators, and an agent can switch to it
120
+ (`sim_device list` and `choose`): its live screen over the cable, at about 55 frames a second; its apps, logs, look and
121
+ location through Xcode's `devicectl`; and touching, typing and its element tree through WebDriverAgent, set up once
122
+ 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
123
+ the cable, and what SimMirror changes on it is put back when it is let go. See [Real devices](docs/real-devices.md).
113
124
 
114
125
  **Recording.** The viewer's Record button, `sim_record` or `sim-mirror record` keep a device's screen as an MP4, a GIF
115
126
  or both, with each touch drawn where it landed and a 9:41 status bar. See [Recording](docs/recording.md).
@@ -169,8 +180,8 @@ an image at about 750 pixels a token).
169
180
  Rows checked on real hardware are marked verified, with the date, in [Compatibility](docs/compatibility.md).
170
181
 
171
182
  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. Connectors are the
173
- exception, and pin a minor version.
183
+ [what SimMirror promises not to break](docs/stability.md): a release adds, and never breaks; 2.0 broke nothing.
184
+ Connectors are the exception, and pin a minor version.
174
185
 
175
186
  ## Documentation
176
187
 
@@ -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.0.0 sim-mirror mcp
19
+ claude mcp add sim-mirror -- uvx --from sim-mirror==2.0.1 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.0.0 sim-mirror open
33
+ uvx --from sim-mirror==2.0.1 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.0.0 sim-mirror open`
9
+ - Watch and take over in a browser: `uvx --from sim-mirror==2.0.1 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.0.0 sim-mirror open`
9
+ - Watch and take over in a browser: `uvx --from sim-mirror==2.0.1 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.0.0" \
8
+ EXAMPLE_KEY=change-me uv run --with "sim-mirror==2.0.1" \
9
9
  uvicorn --factory app:create_app --port 7484
10
10
  ```
11
11
 
@@ -3,7 +3,7 @@ import Foundation
3
3
 
4
4
  /// The helper's version, which is SimMirror's: SimMirror refuses a helper of another version.
5
5
  public enum HelperVersion {
6
- public static let current = "2.0.0"
6
+ public static let current = "2.0.1"
7
7
  }
8
8
 
9
9
  /// What a frame on the socket is.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "sim-mirror"
3
3
  dynamic = ["version"]
4
- description = "Mirror and drive the iOS Simulator from Claude Code, CLI agents and the browser."
4
+ description = "Mirror and drive iOS Simulators and real iPhones from Claude Code, CLI agents and the browser."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
7
7
  license-files = ["LICENSE", "NOTICE", "THIRD_PARTY_LICENSES.md"]
@@ -1,4 +1,4 @@
1
1
  # SPDX-License-Identifier: Apache-2.0
2
2
  """The package version, read by the build backend and by `sim-mirror version`."""
3
3
 
4
- __version__ = "2.0.0"
4
+ __version__ = "2.0.1"
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
  import asyncio
9
9
  import copy
10
10
  import os
11
+ import signal
11
12
  import sys
12
13
  import webbrowser
13
14
  from collections.abc import Awaitable, Callable, Coroutine, Mapping, Sequence
@@ -80,6 +81,8 @@ class CliContext:
80
81
  opener: relay.Opener | None = None
81
82
  open_url: Callable[[str], bool] = webbrowser.open
82
83
  spawn: Spawn = process.spawn
84
+ #: How a process is signalled: ``os.kill``, or a test's.
85
+ kill: Callable[[int, int], None] = os.kill
83
86
  serve: Serve = serve_with_uvicorn
84
87
  run: Runner = process.run
85
88
  xcrun: XcrunRunner = run_xcrun
@@ -122,6 +125,15 @@ class CliContext:
122
125
  def client(self, url: str | None = None) -> DaemonClient:
123
126
  return DaemonClient(url or self.daemon_url(), self.tokens().admin_token(), opener=self.opener)
124
127
 
128
+ def retire_daemon(self) -> bool:
129
+ """Ask the daemon this user runs to stop, as ``Ctrl-C`` would: its devices keep running. Whether one was
130
+ running to ask."""
131
+ info = read_info(self.state.run_dir())
132
+ if info is None:
133
+ return False
134
+ self.kill(info.pid, signal.SIGTERM)
135
+ return True
136
+
125
137
  def start_daemon(self, *arguments: str) -> int:
126
138
  """Start ``sim-mirror serve`` detached, its output in the log folder. Answers its pid."""
127
139
  argv = [self.python, "-m", "sim_mirror", "serve", "--foreground", *arguments]
@@ -27,6 +27,7 @@ def run(args: argparse.Namespace, ctx: CliContext) -> int:
27
27
  roots,
28
28
  client=ctx.client(),
29
29
  start_daemon=ctx.start_daemon,
30
+ retire_daemon=ctx.retire_daemon,
30
31
  env=ctx.env,
31
32
  stdin=ctx.stdin,
32
33
  stdout=ctx.stdout,
@@ -30,6 +30,10 @@ from sim_mirror.core.gestures import CHARACTER_KEYS, COMMAND_KEY, KEYS, SHIFT_KE
30
30
 
31
31
  #: How long the device's orientation is trusted before it is asked again.
32
32
  ORIENTATION_TTL_S = 2.0
33
+ #: How long the element tree is waited for. A screen WebDriverAgent cannot read quickly -- a busy web page in Safari
34
+ #: took over 30 s on an iPhone 14 Pro -- is read from its pixels instead, and a wait for it to settle is not spent
35
+ #: waiting on one request.
36
+ SOURCE_TIMEOUT_S = 5.0
33
37
  #: The most points a finger's stroke is sent with. WebDriverAgent looks each point up against the app on screen -- an
34
38
  #: accessibility round trip of about 240 ms -- so a stroke's points cost more than its length: measured on an iPhone 14
35
39
  #: Pro, a 0.4 s swipe took 7 to 11 s with 25 points, and 1.3 to 1.6 s with 2.
@@ -266,7 +270,7 @@ class WdaReader:
266
270
  self._screen = screen
267
271
 
268
272
  async def accessibility(self) -> dict[str, Any]:
269
- source = await self._client.call("GET", "/source?format=json")
273
+ source = await self._client.call("GET", "/source?format=json", timeout_s=SOURCE_TIMEOUT_S)
270
274
  turn = await self._orientation.turn()
271
275
  return {
272
276
  "backend": "wda",
@@ -125,6 +125,9 @@ class Gesture:
125
125
  @dataclass
126
126
  class _Memory:
127
127
  snapshot: Snapshot | None = None
128
+ #: Whether a step played since the snapshot was taken, so what it shows may have moved or gone -- a keyboard
129
+ #: opened over it, a screen pushed -- and a ref is read again before it is touched.
130
+ stale: bool = False
128
131
  ids: itertools.count[int] = field(default_factory=lambda: itertools.count(1))
129
132
 
130
133
 
@@ -282,6 +285,7 @@ class AgentActions:
282
285
  memory.snapshot = build(
283
286
  tree, device=instance.runtime, screen=instance.screen, max_elements=max_elements, previous=memory.snapshot
284
287
  )
288
+ memory.stale = False
285
289
  return memory.snapshot
286
290
 
287
291
  def _announce(self, instance: DeviceInstance, caller: Caller, gesture: Gesture, lead_ms: int) -> str:
@@ -454,7 +458,8 @@ class AgentActions:
454
458
  screen = instance.screen
455
459
  assert screen is not None
456
460
  if isinstance(value, str):
457
- snapshot = self._remembered(instance, caller).snapshot
461
+ memory = self._remembered(instance, caller)
462
+ snapshot = None if memory.stale else memory.snapshot
458
463
  element = snapshot.element(value) if snapshot is not None else None
459
464
  if element is None:
460
465
  snapshot = await self._read(instance, caller, max_elements)
@@ -617,6 +622,9 @@ class AgentActions:
617
622
  try:
618
623
  gesture = await self._gesture(instance, caller, kind, what, step, max_elements)
619
624
  await self._play(instance, caller, gesture, lead_ms if cursor else 0)
625
+ # A ref in a later step is where the screen now has it: a step can open a keyboard over it, or
626
+ # take it away, and a ref touched where it was would touch whatever is there now.
627
+ self._remembered(instance, caller).stale = gesture.kind != "pause"
620
628
  except ActionError as exc:
621
629
  lines.append(f"error step {number}: {exc}")
622
630
  failed = True
@@ -49,6 +49,8 @@ logger = logging.getLogger(__name__)
49
49
  Format = Literal["mp4", "gif", "both"]
50
50
  FORMATS: tuple[Format, ...] = ("mp4", "gif", "both")
51
51
  SPEEDS = ("1", "1.5", "2", "4")
52
+ #: The largest GIF GitHub shows inline in a pull request or an issue, in bytes; a larger one is only a download.
53
+ GIF_INLINE_MAX = 10_000_000
52
54
  #: A kept recording's file name: when it began, the device's name made safe for a file, and what it is.
53
55
  FILE_NAME = re.compile(r"\A[0-9]{8}-[0-9]{6}-[a-z0-9-]{1,64}\.(mp4|gif)\Z")
54
56
  _SLUG = re.compile(r"[^a-z0-9]+")
@@ -400,6 +402,7 @@ class Recordings:
400
402
  finally:
401
403
  _forget(run.source.raw)
402
404
  files: list[RecordingFile] = [_file(made) for made in rendered]
405
+ notes = [*notes, *filter(None, (_too_big(file, run.options) for file in files))]
403
406
  kept: Recording = {
404
407
  "id": run.id,
405
408
  "device": run.instance.name,
@@ -490,6 +493,19 @@ def _file(made: Rendered) -> RecordingFile:
490
493
  }
491
494
 
492
495
 
496
+ def _too_big(file: RecordingFile, options: RecordingOptions) -> str:
497
+ """What to do about a GIF too big for GitHub to show inline, where a recording is most often put -- or "" when it
498
+ is not too big."""
499
+ if file["format"] != "gif" or file["bytes"] <= GIF_INLINE_MAX:
500
+ return ""
501
+ faster = [speed for speed in SPEEDS if float(speed) > options.speed]
502
+ how = f"speed {faster[-1]}" if faster else "a shorter take"
503
+ return (
504
+ f"{file['name']} is {file['bytes'] / 1_000_000:.1f} MB, over the 10 MB GitHub shows inline: record again with "
505
+ f"{how} or a smaller recording.gif_width, or put the MP4 in a pull request instead"
506
+ )
507
+
508
+
493
509
  async def _own_status_bar(instance: DeviceInstance, control: StatusBar) -> None:
494
510
  try:
495
511
  await control.clear_status_bar(instance.udid)
@@ -3,7 +3,9 @@
3
3
 
4
4
  1. **The daemon**: asked whether it is up (``/healthz``) -- with a fresh nonce and no credential, and believed only when
5
5
  it proves it holds the admin token (`daemon.health`). When nothing answers, it is started detached and waited for,
6
- up to `HEALTH_WAIT_S`; when something else answers on its port, that is refused and sent nothing.
6
+ up to `HEALTH_WAIT_S`; when something else answers on its port, that is refused and sent nothing. A daemon of an
7
+ older SimMirror -- still running after the plugin was updated -- is stopped and this version's started in its place,
8
+ so the tools the client is offered are this version's; its viewers and other clients reconnect by themselves.
7
9
  2. **A token**: the CLI runs as the person who installed SimMirror, so it reads the admin token and mints an agent token
8
10
  for this project's scope -- naming the folders its builds and installs may reach (``--root``). The token reaches the
9
11
  relay in its environment, never argv, and is revoked when the client goes.
@@ -25,6 +27,7 @@ from pathlib import Path
25
27
  from typing import IO, Any
26
28
  from urllib.parse import quote
27
29
 
30
+ from sim_mirror._version import __version__
28
31
  from sim_mirror.daemon import health
29
32
  from sim_mirror.daemon.lease import RENEW_S
30
33
  from sim_mirror.mcp import relay
@@ -40,6 +43,23 @@ TOKEN_HEADER, SCOPE_HEADER, CLIENT_HEADER = "X-SimMirror-Token", "X-SimMirror-Sc
40
43
  OURS, OTHER, NOTHING = "ours", "other", "nothing"
41
44
 
42
45
 
46
+ def version_of(server: object) -> tuple[int, ...] | None:
47
+ """A SimMirror version from what a daemon calls itself -- ``sim-mirror/2.0.0`` -- or None."""
48
+ if not isinstance(server, str) or not server.startswith("sim-mirror/"):
49
+ return None
50
+ try:
51
+ return tuple(int(part) for part in server.removeprefix("sim-mirror/").split("+")[0].split("."))
52
+ except ValueError:
53
+ return None
54
+
55
+
56
+ def older(server: object, mine: str = __version__) -> bool:
57
+ """Whether a daemon calling itself `server` runs an older SimMirror than this one. One that says no version is
58
+ not called older: it is left as it is."""
59
+ theirs, ours = version_of(server), version_of(f"sim-mirror/{mine}")
60
+ return theirs is not None and ours is not None and theirs < ours
61
+
62
+
43
63
  class DaemonUnavailable(Exception):
44
64
  """The daemon could not be reached or started, said so a person knows what to do."""
45
65
 
@@ -70,6 +90,8 @@ class DaemonClient:
70
90
  self._token_id = token_id
71
91
  self._open = opener or relay.direct_opener()
72
92
  self._ours = False
93
+ #: What the daemon called itself when it last proved it is this user's: ``sim-mirror/<version>``.
94
+ self.server: str | None = None
73
95
 
74
96
  def _send(self, method: str, path: str, payload: object, headers: Mapping[str, str]) -> Any:
75
97
  data = None if payload is None else json.dumps(payload).encode("utf-8")
@@ -104,6 +126,9 @@ class DaemonClient:
104
126
  self._ours = health.proves(self._token, nonce, data.get("proof"))
105
127
  else:
106
128
  self._ours = health.token_proves(self._token, nonce, data.get("token_proof"))
129
+ if self._ours:
130
+ server = data.get("server")
131
+ self.server = server if isinstance(server, str) else None
107
132
  return OURS if self._ours else OTHER
108
133
 
109
134
  def healthy(self) -> bool:
@@ -174,10 +199,22 @@ def ensure_daemon(
174
199
  poll_s: float = HEALTH_POLL_S,
175
200
  clock: Callable[[], float] = time.monotonic,
176
201
  sleep: Callable[[float], None] = time.sleep,
202
+ retire: Callable[[], object] | None = None,
177
203
  ) -> None:
178
204
  """This user's daemon, running: started when nothing answers, and waited for. Something else on its port is
179
- refused rather than started over or trusted."""
205
+ refused rather than started over or trusted. With `retire`, a daemon of an older SimMirror is stopped by it and
206
+ this version's started instead; a newer one is used as it is."""
180
207
  found = client.probe()
208
+ if found == OURS and retire is not None and older(client.server):
209
+ retire()
210
+ deadline = clock() + wait_s
211
+ while (found := client.probe()) == OURS:
212
+ if clock() >= deadline:
213
+ raise DaemonUnavailable(
214
+ f"the older SimMirror daemon ({client.server}) did not stop within {wait_s:g}s; stop it, and "
215
+ "this version starts in its place"
216
+ )
217
+ sleep(poll_s)
181
218
  if found == OURS:
182
219
  return
183
220
  if found == OTHER:
@@ -223,6 +260,7 @@ def run(
223
260
  *,
224
261
  client: DaemonClient,
225
262
  start_daemon: Callable[[], object],
263
+ retire_daemon: Callable[[], object] | None = None,
226
264
  env: Mapping[str, str] | None = None,
227
265
  stdin: IO[str] | None = None,
228
266
  stdout: IO[str] | None = None,
@@ -230,7 +268,7 @@ def run(
230
268
  opener: relay.Opener | None = None,
231
269
  ) -> int:
232
270
  """Serve MCP for this scope until the client closes stdin."""
233
- ensure_daemon(client, start_daemon)
271
+ ensure_daemon(client, start_daemon, retire=retire_daemon)
234
272
  label = f"sim-mirror mcp (pid {os.getpid()})"
235
273
  token_id, token = client.mint_agent_token(scope.id, [str(root) for root in roots], label)
236
274
  stop = threading.Event()
File without changes
File without changes
File without changes
File without changes