pymobiledevice3 6.1.6__tar.gz → 7.0.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.
Files changed (264) hide show
  1. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/python-app.yml +1 -1
  2. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/PKG-INFO +3 -2
  3. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/understanding_idevice_protocol_layers.md +10 -5
  4. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/__main__.py +136 -44
  5. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/_version.py +3 -3
  6. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/bonjour.py +19 -20
  7. pymobiledevice3-7.0.0/pymobiledevice3/cli/activation.py +46 -0
  8. pymobiledevice3-7.0.0/pymobiledevice3/cli/afc.py +64 -0
  9. pymobiledevice3-7.0.0/pymobiledevice3/cli/amfi.py +33 -0
  10. pymobiledevice3-7.0.0/pymobiledevice3/cli/apps.py +104 -0
  11. pymobiledevice3-7.0.0/pymobiledevice3/cli/backup.py +241 -0
  12. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/bonjour.py +31 -29
  13. pymobiledevice3-7.0.0/pymobiledevice3/cli/cli_common.py +313 -0
  14. pymobiledevice3-7.0.0/pymobiledevice3/cli/companion_proxy.py +22 -0
  15. pymobiledevice3-7.0.0/pymobiledevice3/cli/crash.py +131 -0
  16. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/__init__.py +62 -0
  17. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
  18. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
  19. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/arbitration.py +50 -0
  20. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/condition.py +33 -0
  21. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/core_device.py +294 -0
  22. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/debugserver.py +244 -0
  23. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/__init__.py +387 -0
  24. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
  25. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
  26. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
  27. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
  28. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
  29. pymobiledevice3-7.0.0/pymobiledevice3/cli/developer/simulate_location.py +51 -0
  30. pymobiledevice3-7.0.0/pymobiledevice3/cli/diagnostics/__init__.py +75 -0
  31. pymobiledevice3-7.0.0/pymobiledevice3/cli/diagnostics/battery.py +47 -0
  32. pymobiledevice3-7.0.0/pymobiledevice3/cli/idam.py +42 -0
  33. pymobiledevice3-7.0.0/pymobiledevice3/cli/lockdown.py +190 -0
  34. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/mounter.py +99 -57
  35. pymobiledevice3-7.0.0/pymobiledevice3/cli/notification.py +68 -0
  36. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/pcap.py +36 -20
  37. pymobiledevice3-7.0.0/pymobiledevice3/cli/power_assertion.py +26 -0
  38. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/processes.py +11 -17
  39. pymobiledevice3-7.0.0/pymobiledevice3/cli/profile.py +236 -0
  40. pymobiledevice3-7.0.0/pymobiledevice3/cli/provision.py +59 -0
  41. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/remote.py +108 -99
  42. pymobiledevice3-7.0.0/pymobiledevice3/cli/restore.py +252 -0
  43. pymobiledevice3-7.0.0/pymobiledevice3/cli/springboard.py +90 -0
  44. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/syslog.py +138 -74
  45. pymobiledevice3-7.0.0/pymobiledevice3/cli/usbmux.py +108 -0
  46. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/version.py +2 -5
  47. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/webinspector.py +149 -103
  48. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remote_service_discovery.py +11 -10
  49. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/device.py +28 -4
  50. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/service_connection.py +1 -1
  51. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobilebackup2.py +4 -1
  52. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/screenshot.py +2 -2
  53. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/automation_session.py +4 -2
  54. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_screencast.py +2 -1
  55. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/element.py +3 -3
  56. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/PKG-INFO +3 -2
  57. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/SOURCES.txt +16 -3
  58. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/requires.txt +2 -1
  59. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pyproject.toml +20 -6
  60. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pytest.ini +1 -0
  61. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/requirements.txt +2 -1
  62. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/cli/test_cli.py +4 -4
  63. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/conftest.py +3 -7
  64. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_core_profile_session.py +2 -1
  65. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_dvt_secure_socket_proxy.py +13 -14
  66. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_fetch_symbols.py +4 -5
  67. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_location.py +4 -5
  68. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/test_screenshot.py +2 -1
  69. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_accessibility.py +4 -3
  70. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_afc.py +31 -30
  71. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_apps.py +3 -2
  72. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_backup2.py +3 -3
  73. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_bonjour.py +4 -3
  74. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_crash_reports.py +9 -9
  75. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_house_arrest.py +1 -1
  76. pymobiledevice3-7.0.0/tests/services/test_list_devices.py +6 -0
  77. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_lockdown.py +3 -1
  78. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_pcapd.py +2 -1
  79. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_springboard_services_relay.py +4 -3
  80. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_start_tunnel.py +1 -1
  81. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_syslog_relay.py +2 -1
  82. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_tcp_forwarder.py +2 -2
  83. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/test_element.py +3 -2
  84. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_webinspector.py +2 -1
  85. pymobiledevice3-6.1.6/pymobiledevice3/cli/activation.py +0 -44
  86. pymobiledevice3-6.1.6/pymobiledevice3/cli/afc.py +0 -56
  87. pymobiledevice3-6.1.6/pymobiledevice3/cli/amfi.py +0 -38
  88. pymobiledevice3-6.1.6/pymobiledevice3/cli/apps.py +0 -98
  89. pymobiledevice3-6.1.6/pymobiledevice3/cli/backup.py +0 -200
  90. pymobiledevice3-6.1.6/pymobiledevice3/cli/cli_common.py +0 -366
  91. pymobiledevice3-6.1.6/pymobiledevice3/cli/companion_proxy.py +0 -22
  92. pymobiledevice3-6.1.6/pymobiledevice3/cli/completions.py +0 -50
  93. pymobiledevice3-6.1.6/pymobiledevice3/cli/crash.py +0 -88
  94. pymobiledevice3-6.1.6/pymobiledevice3/cli/developer.py +0 -1560
  95. pymobiledevice3-6.1.6/pymobiledevice3/cli/diagnostics.py +0 -110
  96. pymobiledevice3-6.1.6/pymobiledevice3/cli/idam.py +0 -46
  97. pymobiledevice3-6.1.6/pymobiledevice3/cli/lockdown.py +0 -195
  98. pymobiledevice3-6.1.6/pymobiledevice3/cli/notification.py +0 -56
  99. pymobiledevice3-6.1.6/pymobiledevice3/cli/power_assertion.py +0 -27
  100. pymobiledevice3-6.1.6/pymobiledevice3/cli/profile.py +0 -191
  101. pymobiledevice3-6.1.6/pymobiledevice3/cli/provision.py +0 -58
  102. pymobiledevice3-6.1.6/pymobiledevice3/cli/restore.py +0 -247
  103. pymobiledevice3-6.1.6/pymobiledevice3/cli/springboard.py +0 -90
  104. pymobiledevice3-6.1.6/pymobiledevice3/cli/usbmux.py +0 -69
  105. pymobiledevice3-6.1.6/tests/services/test_list_devices.py +0 -5
  106. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.gitattributes +0 -0
  107. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/FUNDING.yml +0 -0
  108. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  109. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  110. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  111. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/pull_request_template.md +0 -0
  112. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/codeql.yml +0 -0
  113. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/generate-executable.py +0 -0
  114. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/markdown-lint.yml +0 -0
  115. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.github/workflows/python-publish.yml +0 -0
  116. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.gitignore +0 -0
  117. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/.pre-commit-config.yaml +0 -0
  118. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/CODE_OF_CONDUCT.md +0 -0
  119. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/CONTRIBUTING.md +0 -0
  120. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/LICENSE +0 -0
  121. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/README.md +0 -0
  122. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/example.gif +0 -0
  123. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/markdownlint-config.json +0 -0
  124. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/DTServices-14.2.txt +0 -0
  125. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/DTServices-14.5.txt +0 -0
  126. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/RemoteXPC.md +0 -0
  127. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/plist_sniffer.py +0 -0
  128. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/pyinstaller.md +0 -0
  129. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/remotexpc_sniffer.py +0 -0
  130. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/misc/usbmux_sniff.sh +0 -0
  131. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/__init__.py +0 -0
  132. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/ca.py +0 -0
  133. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/cli/__init__.py +0 -0
  134. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/common.py +0 -0
  135. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/exceptions.py +0 -0
  136. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/irecv.py +0 -0
  137. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/irecv_devices.py +0 -0
  138. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/lockdown.py +0 -0
  139. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/lockdown_service_provider.py +0 -0
  140. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/__init__.py +0 -0
  141. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/os_utils.py +0 -0
  142. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/posix_util.py +0 -0
  143. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/osu/win_util.py +0 -0
  144. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/pair_records.py +0 -0
  145. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/__init__.py +0 -0
  146. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/common.py +0 -0
  147. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/__init__.py +0 -0
  148. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/app_service.py +0 -0
  149. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/core_device_service.py +0 -0
  150. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/device_info.py +0 -0
  151. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/diagnostics_service.py +0 -0
  152. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/core_device/file_service.py +0 -0
  153. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/module_imports.py +0 -0
  154. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remote_service.py +0 -0
  155. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/remotexpc.py +0 -0
  156. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/tunnel_service.py +0 -0
  157. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/utils.py +0 -0
  158. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/remote/xpc_message.py +0 -0
  159. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/__init__.py +0 -0
  160. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/dsc_uuid_map.json +0 -0
  161. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/dsc_uuid_map.py +0 -0
  162. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/firmware_notifications.py +0 -0
  163. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/notifications.txt +0 -0
  164. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/element_attribute.js +0 -0
  165. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/element_clear.js +0 -0
  166. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/enter_fullscreen.js +0 -0
  167. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/find_nodes.js +0 -0
  168. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/focus.js +0 -0
  169. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/get_attribute.js +0 -0
  170. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_displayed.js +0 -0
  171. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_editable.js +0 -0
  172. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/resources/webinspector/is_enabled.js +0 -0
  173. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/__init__.py +0 -0
  174. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/asr.py +0 -0
  175. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/base_restore.py +0 -0
  176. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/consts.py +0 -0
  177. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/fdr.py +0 -0
  178. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/ftab.py +0 -0
  179. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/img4.py +0 -0
  180. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/mbn.py +0 -0
  181. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/recovery.py +0 -0
  182. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restore.py +0 -0
  183. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restore_options.py +0 -0
  184. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/restored_client.py +0 -0
  185. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/restore/tss.py +0 -0
  186. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/__init__.py +0 -0
  187. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/accessibilityaudit.py +0 -0
  188. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/afc.py +0 -0
  189. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/amfi.py +0 -0
  190. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/companion.py +0 -0
  191. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/crash_reports.py +0 -0
  192. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/debugserver_applist.py +0 -0
  193. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/device_arbitration.py +0 -0
  194. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/device_link.py +0 -0
  195. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/diagnostics.py +0 -0
  196. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dtfetchsymbols.py +0 -0
  197. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/__init__.py +0 -0
  198. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +0 -0
  199. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +0 -0
  200. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/__init__.py +0 -0
  201. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +0 -0
  202. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/application_listing.py +0 -0
  203. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/condition_inducer.py +0 -0
  204. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +0 -0
  205. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/device_info.py +0 -0
  206. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/energy_monitor.py +0 -0
  207. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/graphics.py +0 -0
  208. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation.py +0 -0
  209. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/location_simulation_base.py +0 -0
  210. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/network_monitor.py +0 -0
  211. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/notifications.py +0 -0
  212. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/process_control.py +0 -0
  213. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/screenshot.py +0 -0
  214. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/instruments/sysmontap.py +0 -0
  215. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/dvt/testmanaged/xcuitest.py +0 -0
  216. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/file_relay.py +0 -0
  217. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/heartbeat.py +0 -0
  218. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/house_arrest.py +0 -0
  219. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/idam.py +0 -0
  220. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/installation_proxy.py +0 -0
  221. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/lockdown_service.py +0 -0
  222. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/misagent.py +0 -0
  223. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_activation.py +0 -0
  224. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_config.py +0 -0
  225. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/mobile_image_mounter.py +0 -0
  226. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/notification_proxy.py +0 -0
  227. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/os_trace.py +0 -0
  228. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/pcapd.py +0 -0
  229. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/power_assertion.py +0 -0
  230. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/preboard.py +0 -0
  231. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/remote_fetch_symbols.py +0 -0
  232. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/remote_server.py +0 -0
  233. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/restore_service.py +0 -0
  234. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/simulate_location.py +0 -0
  235. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/springboard.py +0 -0
  236. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/syslog.py +0 -0
  237. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/__init__.py +0 -0
  238. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/alert.py +0 -0
  239. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_server.py +0 -0
  240. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/cdp_target.py +0 -0
  241. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/driver.py +0 -0
  242. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/inspector_session.py +0 -0
  243. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/selenium_api.py +0 -0
  244. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/session_protocol.py +0 -0
  245. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/web_protocol/switch_to.py +0 -0
  246. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/services/webinspector.py +0 -0
  247. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tcp_forwarder.py +0 -0
  248. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/__init__.py +0 -0
  249. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/api.py +0 -0
  250. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/tunneld/server.py +0 -0
  251. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/usbmux.py +0 -0
  252. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3/utils.py +0 -0
  253. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/dependency_links.txt +0 -0
  254. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/entry_points.txt +0 -0
  255. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/pymobiledevice3.egg-info/top_level.txt +0 -0
  256. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/setup.cfg +0 -0
  257. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/__init__.py +0 -0
  258. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/cli/__init__.py +0 -0
  259. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/__init__.py +0 -0
  260. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/instruments/__init__.py +0 -0
  261. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/__init__.py +0 -0
  262. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/common.py +0 -0
  263. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/conftest.py +0 -0
  264. {pymobiledevice3-6.1.6 → pymobiledevice3-7.0.0}/tests/services/test_web_protocol/test_driver.py +0 -0
@@ -22,7 +22,7 @@ jobs:
22
22
 
23
23
  strategy:
24
24
  matrix:
25
- python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13", "3.14" ]
25
+ python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13", "3.14" ]
26
26
  os: [ ubuntu-latest, macos-latest, windows-latest ]
27
27
 
28
28
  steps:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymobiledevice3
3
- Version: 6.1.6
3
+ Version: 7.0.0
4
4
  Summary: Pure python3 implementation for working with iDevices (iPhone, etc...)
5
5
  Author-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
6
6
  Maintainer-email: doronz88 <doron88@gmail.com>, matan <matan1008@gmail.com>
@@ -23,7 +23,6 @@ License-File: LICENSE
23
23
  Requires-Dist: construct>=2.9.29
24
24
  Requires-Dist: construct-typing>=0.7.0
25
25
  Requires-Dist: asn1
26
- Requires-Dist: click
27
26
  Requires-Dist: coloredlogs
28
27
  Requires-Dist: IPython
29
28
  Requires-Dist: bpylist2>=4.0.1
@@ -61,6 +60,8 @@ Requires-Dist: sslpsk-pmd3>=1.0.3; python_version < "3.13"
61
60
  Requires-Dist: python-pcapng>=2.1.1
62
61
  Requires-Dist: plumbum
63
62
  Requires-Dist: pyimg4>=0.8.8
63
+ Requires-Dist: typer>=0.20.0
64
+ Requires-Dist: typer-injector>=0.2.0
64
65
  Provides-Extra: test
65
66
  Requires-Dist: pytest; extra == "test"
66
67
  Requires-Dist: pytest-asyncio; extra == "test"
@@ -287,14 +287,14 @@ pymobiledevice3 developer dvt launch com.apple.mobilesafari
287
287
 
288
288
  ## DVT
289
289
 
290
- One of the more interesting developer services, is the one exposed by `DTServiceHub`. It is using DTX protocol messages,
290
+ One of the more interesting developer services is the one exposed by `DTServiceHub`. It is using DTX protocol messages,
291
291
  but since it mainly wraps and allows access to stuff in `DVTFoundation.framework` we called it DVT in our
292
292
  implementation (probably standing for DeveloperTools).
293
293
 
294
294
  We don't delve too much into this protocol, but we'll say in general it allows us to invoke a whitelist of ObjC methods
295
295
  in different ObjC objects. The terminology used by DVT to each such ObjC object is called "channels".
296
296
 
297
- In order to access this different object use the following APIs:
297
+ To access this different object use the following APIs:
298
298
 
299
299
  ```python
300
300
  from pymobiledevice3.lockdown import create_using_usbmux
@@ -313,17 +313,22 @@ dvt_channel = Screenshot(dvt)
313
313
  open('/tmp/screen.png', 'wb').write(dvt_channel.get_screenshot())
314
314
  ```
315
315
 
316
- Looking for an unimplemented feature/channel? Feel free to play with it (and submit a PR afterwards 🙏) using the
316
+ Looking for an unimplemented feature/channel? Feel free to play with it (and submit a PR afterward 🙏) using the
317
317
  following shell:
318
318
 
319
319
  ```shell
320
320
  pymobiledevice3 developer dvt shell
321
321
  ```
322
322
 
323
+ > **NOTE:** The full list of the methods that can be invoked on a DVT channel can be found by looking at all ObjC
324
+ > classes in `DVTInstrumentsFoundation.framework` implementing the `DTXAllowedRPC` protocol.
325
+ > There is an existing [Anubis rule](https://github.com/netanelc305/anubis/blob/9da337178ebd7e9f168e9df2d82b192eba4f1b30/example_rules.yaml#L14-L17)
326
+ > I use to diff new methods against the existing ones.
327
+
323
328
  ## RemoteXPC
324
329
 
325
330
  Starting at iOS 17.0, Apple made a large refactor in the manner we all interact with the developer services. There can
326
- be multiple reasons for that decision, but in general this refactor main key points are:
331
+ be multiple reasons for that decision, but in general these refactor main key points are:
327
332
 
328
333
  - Create a single standard for interacting with the new lockdown services (XPC Messages, Apple's proprietary IPC)
329
334
  - Optimize the protocol for large file transfers (such as the dyld_shared_cache)
@@ -347,7 +352,7 @@ Since all this communication is IP-based, but without any additional exported TC
347
352
  help us here. Instead, starting at iOS 16.0, when connecting an iDevice, it exports another non-standard USB-Ethernet
348
353
  adapter (with IPv6 link-local address), placing us in a subnet with the device's `remoted`.
349
354
 
350
- As we've said this communication is non-standard, and requires either:
355
+ As we've said, this communication is non-standard and requires either:
351
356
 
352
357
  - macOS Monterey or higher
353
358
  - Special driver on your linux/Windows machine
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import difflib
3
+ import importlib
3
4
  import logging
4
5
  import os
5
6
  import re
@@ -7,13 +8,18 @@ import sys
7
8
  import textwrap
8
9
  import traceback
9
10
  import warnings
10
- from typing import Union
11
+ from collections.abc import Sequence
12
+ from typing import Annotated, Optional, Union
11
13
 
12
14
  import click
13
15
  import coloredlogs
16
+ import typer
17
+ import typer.core
14
18
  from packaging.version import Version
19
+ from typer.core import TyperGroup
20
+ from typer_injector import InjectingTyper
15
21
 
16
- from pymobiledevice3.cli.cli_common import TUNNEL_ENV_VAR, isatty
22
+ from pymobiledevice3.cli.cli_common import TUNNEL_ENV_VAR, isatty, set_color_flag, set_verbosity
17
23
  from pymobiledevice3.exceptions import (
18
24
  AccessDeniedError,
19
25
  CloudConfigurationAlreadyPresentError,
@@ -112,25 +118,26 @@ CLI_GROUPS = {
112
118
  "webinspector": "webinspector",
113
119
  "idam": "idam",
114
120
  "version": "version",
115
- "install-completions": "completions",
116
121
  }
117
122
 
118
123
  # Set if used the `--reconnect` option
119
124
  RECONNECT = False
120
125
 
121
126
 
122
- class Pmd3Cli(click.Group):
123
- def list_commands(self, ctx):
124
- return CLI_GROUPS.keys()
127
+ class Pmd3TyperGroup(TyperGroup):
128
+ def list_commands(self, ctx: click.Context) -> list[str]:
129
+ # Order is preserved by dict insertion; adjust if you want alphabetical
130
+ return list(CLI_GROUPS.keys())
125
131
 
126
- def get_command(self, ctx: click.Context, name: str) -> click.Command:
127
- if name not in CLI_GROUPS:
128
- self.handle_invalid_command(ctx, name)
129
- return self.import_and_get_command(ctx, name)
132
+ def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command:
133
+ if cmd_name not in CLI_GROUPS:
134
+ self.handle_invalid_command(ctx, cmd_name)
135
+ return self.import_and_get_command(ctx, cmd_name)
130
136
 
131
- def handle_invalid_command(self, ctx: click.Context, name: str) -> None:
137
+ def handle_invalid_command(self, ctx, name: str) -> None:
132
138
  suggested_commands = self.search_commands(name)
133
139
  suggestion = self.format_suggestions(suggested_commands)
140
+ # ctx.fail raises a ClickException underneath, which Typer displays nicely
134
141
  ctx.fail(f"No such command {name!r}{suggestion}")
135
142
 
136
143
  @staticmethod
@@ -138,70 +145,109 @@ class Pmd3Cli(click.Group):
138
145
  if not suggestions:
139
146
  return ""
140
147
  cmds = textwrap.indent("\n".join(suggestions), " " * 4)
141
- return f"\nDid you mean this?\n{cmds}"
148
+ return f"\nDid you mean:\n{cmds}"
142
149
 
143
150
  @staticmethod
144
151
  def import_and_get_command(ctx: click.Context, name: str) -> click.Command:
145
152
  module_name = f"pymobiledevice3.cli.{CLI_GROUPS[name]}"
146
- mod = __import__(module_name, None, None, ["cli"])
147
- command = mod.cli.get_command(ctx, name)
148
- if not command:
149
- command_name = mod.cli.list_commands(ctx)[0]
150
- command = mod.cli.get_command(ctx, command_name)
151
- return command
153
+ mod = importlib.import_module(module_name)
154
+ # submodules expose a Typer Group named "cli"
155
+ cli: typer.Typer = mod.cli
156
+ return typer.main.get_command(cli)
152
157
 
153
158
  @staticmethod
154
159
  def highlight_keyword(text: str, keyword: str) -> str:
155
- return re.sub(f"({keyword})", click.style("\\1", bold=True), text, flags=re.IGNORECASE)
160
+ return re.sub(f"({keyword})", typer.style("\\1", bold=True), text, flags=re.IGNORECASE)
156
161
 
157
162
  @staticmethod
158
- def collect_commands(command: click.Command) -> Union[str, list[str]]:
159
- commands = []
160
- if isinstance(command, click.Group):
161
- for _k, v in command.commands.items():
162
- cmd = Pmd3Cli.collect_commands(v)
163
- if isinstance(cmd, list):
164
- commands.extend([f"{command.name} {c}" for c in cmd])
163
+ def collect_commands(command: Union[TyperGroup, click.Command]) -> Union[str, list[str]]:
164
+ if isinstance(command, TyperGroup): # group
165
+ cmds = []
166
+ for v in command.commands.values():
167
+ child = Pmd3TyperGroup.collect_commands(v)
168
+ if isinstance(child, list):
169
+ cmds.extend([f"{command.name} {c}" for c in child])
165
170
  else:
166
- commands.append(f"{command.name} {cmd}")
167
- return commands
168
- return f"{command.name}"
171
+ cmds.append(f"{command.name} {child}")
172
+ return cmds
173
+ return command.name or ""
169
174
 
170
175
  @staticmethod
171
176
  def search_commands(pattern: str) -> list[str]:
172
- all_commands = Pmd3Cli.load_all_commands()
177
+ all_commands = Pmd3TyperGroup.load_all_commands()
173
178
  matched = sorted(filter(lambda cmd: re.search(pattern, cmd), all_commands))
174
179
  if not matched:
175
180
  matched = difflib.get_close_matches(pattern, all_commands, n=20, cutoff=0.4)
176
181
  if isatty():
177
- matched = [Pmd3Cli.highlight_keyword(cmd, pattern) for cmd in matched]
182
+ matched = [Pmd3TyperGroup.highlight_keyword(cmd, pattern) for cmd in matched]
178
183
  return matched
179
184
 
180
185
  @staticmethod
181
186
  def load_all_commands() -> list[str]:
182
- all_commands = []
187
+ all_commands: list[str] = []
183
188
  for key in CLI_GROUPS:
184
189
  module_name = f"pymobiledevice3.cli.{CLI_GROUPS[key]}"
185
- mod = __import__(module_name, None, None, ["cli"])
186
- cmd = Pmd3Cli.collect_commands(mod.cli.commands[key])
190
+ mod = importlib.import_module(module_name)
191
+ if isinstance(mod.cli, typer.Typer):
192
+ cmd = Pmd3TyperGroup.collect_commands(typer.main.get_group(mod.cli))
193
+ else:
194
+ cmd = Pmd3TyperGroup.collect_commands(mod.cli.commands[key])
187
195
  if isinstance(cmd, list):
188
196
  all_commands.extend(cmd)
189
197
  else:
190
198
  all_commands.append(cmd)
191
199
  return all_commands
192
200
 
201
+ def resolve_command(
202
+ self, ctx: click.Context, args: list[str]
203
+ ) -> tuple[Optional[str], Optional[click.Command], list[str]]:
204
+ return super().resolve_command(ctx, args)
205
+
206
+
207
+ app = InjectingTyper(
208
+ cls=Pmd3TyperGroup,
209
+ context_settings=CONTEXT_SETTINGS,
210
+ no_args_is_help=True,
211
+ # add_completion=False,
212
+ rich_markup_mode="markdown",
213
+ help=(
214
+ "Swiss-army CLI for pairing, inspecting, backing up, and automating iOS devices.\n\n"
215
+ "Docs and examples: https://github.com/doronz88/pymobiledevice3"
216
+ ),
217
+ )
218
+
193
219
 
194
- @click.command(cls=Pmd3Cli, context_settings=CONTEXT_SETTINGS)
195
- @click.option("--reconnect", is_flag=True, default=False, help="Reconnect to device when disconnected.")
196
- def cli(reconnect: bool) -> None:
220
+ @app.callback()
221
+ def _root(
222
+ reconnect: Annotated[
223
+ bool,
224
+ typer.Option(
225
+ "--reconnect",
226
+ help="Automatically reconnect if the device disconnects mid-command.",
227
+ show_default=False,
228
+ ),
229
+ ] = False,
230
+ verbosity: Annotated[
231
+ int,
232
+ typer.Option(
233
+ "--verbose",
234
+ "-v",
235
+ count=True,
236
+ help="Increase logging verbosity (repeat for more detail).",
237
+ ),
238
+ ] = 0,
239
+ color: Annotated[
240
+ bool,
241
+ typer.Option(help="Colorize output; disable with --no-color for plain logs."),
242
+ ] = True,
243
+ ) -> None:
197
244
  """
198
- \b
199
- Interact with a connected iDevice (iPhone, iPad, ...)
200
- For more information please look at:
201
- https://github.com/doronz88/pymobiledevice3
245
+ Top-level options for pymobiledevice3.
202
246
  """
203
247
  global RECONNECT
204
248
  RECONNECT = reconnect
249
+ set_verbosity(verbosity)
250
+ set_color_flag(color)
205
251
 
206
252
 
207
253
  def device_might_need_tunneld(identifier: str) -> bool:
@@ -221,13 +267,53 @@ def device_might_need_tunneld(identifier: str) -> bool:
221
267
  return Version(lockdown.product_version) >= Version("17.0")
222
268
 
223
269
 
270
+ class PossiblyMisplacedOption(click.NoSuchOption):
271
+ def __init__(
272
+ self,
273
+ option_name: str,
274
+ message: Optional[str] = None,
275
+ possibilities: Optional[Sequence[str]] = None,
276
+ ctx: Optional[click.Context] = None,
277
+ suggested_ctx: Optional[click.Context] = None,
278
+ ) -> None:
279
+ super().__init__(option_name, message, possibilities, ctx)
280
+ if suggested_ctx is not None:
281
+ if ctx is not None:
282
+ self.message += f" for subcommand: {ctx.command_path}"
283
+
284
+ suggestion = f"{suggested_ctx.command_path} {option_name}"
285
+ suggestion += ctx.command_path.removeprefix(suggested_ctx.command_path) if ctx is not None else " ..."
286
+
287
+ self.message += f"\nDid you mean: {suggestion}?"
288
+
289
+ @staticmethod
290
+ def from_no_such_option(e: click.NoSuchOption) -> "PossiblyMisplacedOption":
291
+ ctx = e.ctx
292
+ while ctx:
293
+ for param in ctx.command.params:
294
+ if isinstance(param, typer.core.TyperOption) and (
295
+ e.option_name in param.opts or e.option_name in param.secondary_opts
296
+ ):
297
+ break
298
+ else:
299
+ ctx = ctx.parent
300
+ continue
301
+ break
302
+
303
+ return PossiblyMisplacedOption(e.option_name, e.message, e.possibilities, e.ctx, ctx)
304
+
305
+
224
306
  def invoke_cli_with_error_handling() -> bool:
225
307
  """
226
308
  Invoke the command line interface and return `True` if the failure reason of the command was that the device was
227
309
  disconnected.
228
310
  """
229
311
  try:
230
- cli()
312
+ # Typer apps are callable; this executes the CLI with current sys.argv
313
+ try:
314
+ app(standalone_mode=False)
315
+ except click.NoSuchOption as e:
316
+ raise PossiblyMisplacedOption.from_no_such_option(e) from e
231
317
  except NoDeviceConnectedError:
232
318
  logger.error("Device is not connected")
233
319
  return True
@@ -277,9 +363,10 @@ def invoke_cli_with_error_handling() -> bool:
277
363
  logger.warning("Got an InvalidServiceError. Trying again over tunneld since it is a developer command")
278
364
  should_retry_over_tunneld = True
279
365
  if should_retry_over_tunneld:
280
- # use a single space because click will ignore envvars of empty strings
366
+ # use a single space because Typer/Click will ignore envvars of empty strings
281
367
  os.environ[TUNNEL_ENV_VAR] = e.identifier or " "
282
- return main()
368
+ main()
369
+ return False
283
370
  logger.error(INVALID_SERVICE_MESSAGE)
284
371
  except PasswordRequiredError:
285
372
  logger.error("Device is password protected. Please unlock and retry")
@@ -315,6 +402,11 @@ def invoke_cli_with_error_handling() -> bool:
315
402
  )
316
403
  except QuicProtocolNotSupportedError:
317
404
  logger.error("Encountered a QUIC protocol error.")
405
+ except click.ClickException as e:
406
+ from typer import rich_utils
407
+
408
+ rich_utils.rich_format_error(e)
409
+ sys.exit(e.exit_code)
318
410
 
319
411
  return False
320
412
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '6.1.6'
32
- __version_tuple__ = version_tuple = (6, 1, 6)
31
+ __version__ = version = '7.0.0'
32
+ __version_tuple__ = version_tuple = (7, 0, 0)
33
33
 
34
- __commit_id__ = commit_id = 'g8900837a9'
34
+ __commit_id__ = commit_id = 'g363ad89c4'
@@ -300,10 +300,9 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
300
300
  adapters = _Adapters()
301
301
 
302
302
  ptr_targets: set[str] = set()
303
- srv_map: dict[str, dict] = {}
303
+ srv_map: dict[str, list[dict]] = defaultdict(list) # instance_name -> list of {"target", "port"}
304
304
  txt_map: dict[str, dict] = {}
305
- # host -> list[(ip, iface)]
306
- host_addrs: dict[str, list[Address]] = defaultdict(list)
305
+ host_addrs: dict[str, list[Address]] = defaultdict(list) # host -> list[(ip, iface)]
307
306
 
308
307
  def _record_addr(rr_name: str, ip_str: str, pkt_addr):
309
308
  # Determine family and possible scopeid from the packet that delivered this RR
@@ -314,7 +313,7 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
314
313
  iface = adapters.pick_iface_for_ip(ip_str, family, scopeid)
315
314
  if iface is None:
316
315
  return
317
- # avoid duplicates for the same host/ip
316
+ # Avoid duplicates for the same host/ip
318
317
  existing = host_addrs[rr_name]
319
318
  if not any(a.ip == ip_str for a in existing):
320
319
  existing.append(Address(ip=ip_str, iface=iface))
@@ -333,11 +332,10 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
333
332
  if t == QTYPE_PTR and rr.get("name") == service_type:
334
333
  ptr_targets.add(rr.get("ptrdname"))
335
334
  elif t == QTYPE_SRV:
336
- srv_map[rr["name"]] = {
337
- "target": rr.get("target"),
338
- "port": rr.get("port"),
339
- }
335
+ srv_map[rr["name"]].append({"target": rr.get("target"), "port": rr.get("port")})
340
336
  elif t == QTYPE_TXT:
337
+ # TODO: This could possibly mix the properties of multiple TXT records for the same instance.
338
+ # However, it's currently unused.
341
339
  txt_map[rr["name"]] = rr.get("txt", {})
342
340
  elif (t == QTYPE_A and rr.get("address")) or (t == QTYPE_AAAA and rr.get("address")):
343
341
  _record_addr(rr["name"], rr["address"], pkt_addr)
@@ -348,20 +346,21 @@ async def browse_service(service_type: str, timeout: float = 4.0) -> list[Servic
348
346
  # Assemble dataclasses
349
347
  results: list[ServiceInstance] = []
350
348
  for inst in sorted(ptr_targets):
351
- srv = srv_map.get(inst, {})
352
- target = srv.get("target")
353
- host = (target[:-1] if target and target.endswith(".") else target) or None
354
- addrs = host_addrs.get(target, []) if target else []
349
+ srv_entries = srv_map.get(inst, [])
355
350
  props = txt_map.get(inst, {})
356
- results.append(
357
- ServiceInstance(
358
- instance=inst,
359
- host=host,
360
- port=srv.get("port"),
361
- addresses=addrs,
362
- properties=props,
351
+ for srv in srv_entries:
352
+ target = srv.get("target")
353
+ host = (target[:-1] if target and target.endswith(".") else target) or None
354
+ addrs = host_addrs.get(target, []) if target else []
355
+ results.append(
356
+ ServiceInstance(
357
+ instance=inst,
358
+ host=host,
359
+ port=srv.get("port"),
360
+ addresses=addrs,
361
+ properties=props,
362
+ )
363
363
  )
364
- )
365
364
  return results
366
365
 
367
366
 
@@ -0,0 +1,46 @@
1
+ from typing import Annotated
2
+
3
+ import typer
4
+ from typer_injector import InjectingTyper
5
+
6
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep
7
+ from pymobiledevice3.services.mobile_activation import MobileActivationService
8
+
9
+ cli = InjectingTyper(
10
+ name="activation",
11
+ help="Perform iCloud activation/deactivation or query the current state",
12
+ no_args_is_help=True,
13
+ )
14
+
15
+
16
+ @cli.command()
17
+ def state(service_provider: ServiceProviderDep) -> None:
18
+ """Get current activation state"""
19
+ print(MobileActivationService(service_provider).state)
20
+
21
+
22
+ @cli.command()
23
+ def activate(
24
+ service_provider: ServiceProviderDep,
25
+ now: Annotated[
26
+ bool,
27
+ typer.Option(help="do not wait for next nonce cycle"),
28
+ ] = False,
29
+ ) -> None:
30
+ """Activate device"""
31
+ activation_service = MobileActivationService(service_provider)
32
+ if not now:
33
+ activation_service.wait_for_activation_session()
34
+ activation_service.activate()
35
+
36
+
37
+ @cli.command()
38
+ def deactivate(service_provider: ServiceProviderDep) -> None:
39
+ """Deactivate device"""
40
+ MobileActivationService(service_provider).deactivate()
41
+
42
+
43
+ @cli.command()
44
+ def itunes(service_provider: ServiceProviderDep) -> None:
45
+ """Tell the device that it has been connected to iTunes (useful for < iOS 4)"""
46
+ service_provider.set_value(True, key="iTunesHasConnected")
@@ -0,0 +1,64 @@
1
+ from pathlib import Path
2
+ from typing import Annotated
3
+
4
+ import typer
5
+ from typer_injector import InjectingTyper
6
+
7
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep
8
+ from pymobiledevice3.services.afc import AfcService, AfcShell
9
+
10
+ cli = InjectingTyper(
11
+ name="afc",
12
+ help="Browse, push, and pull files via the AFC service (/var/mobile/Media).",
13
+ no_args_is_help=True,
14
+ )
15
+
16
+
17
+ @cli.command("shell")
18
+ def afc_shell(service_provider: ServiceProviderDep) -> None:
19
+ """Open an interactive AFC shell rooted at /var/mobile/Media."""
20
+ AfcShell.create(service_provider)
21
+
22
+
23
+ @cli.command("pull")
24
+ def afc_pull(
25
+ service_provider: ServiceProviderDep,
26
+ remote_file: Path,
27
+ local_file: Path,
28
+ ignore_errors: Annotated[
29
+ bool,
30
+ typer.Option(
31
+ "--ignore-errors",
32
+ "-i",
33
+ help="Continue downloading even if some files error (best-effort pull).",
34
+ ),
35
+ ],
36
+ ) -> None:
37
+ """Download a remote path under /var/mobile/Media to the local filesystem."""
38
+ AfcService(lockdown=service_provider).pull(str(remote_file), str(local_file), ignore_errors=ignore_errors)
39
+
40
+
41
+ @cli.command("push")
42
+ def afc_push(service_provider: ServiceProviderDep, local_file: Path, remote_file: Path) -> None:
43
+ """Upload a local file into /var/mobile/Media."""
44
+ AfcService(lockdown=service_provider).push(str(local_file), str(remote_file))
45
+
46
+
47
+ @cli.command("ls")
48
+ def afc_ls(
49
+ service_provider: ServiceProviderDep,
50
+ remote_file: Path,
51
+ recursive: Annotated[
52
+ bool,
53
+ typer.Option("--recursive", "-r", help="Recurse into subdirectories when listing."),
54
+ ] = False,
55
+ ) -> None:
56
+ """List files under /var/mobile/Media (optionally recursively)."""
57
+ for path in AfcService(lockdown=service_provider).dirlist(str(remote_file), -1 if recursive else 1):
58
+ print(path)
59
+
60
+
61
+ @cli.command("rm")
62
+ def afc_rm(service_provider: ServiceProviderDep, remote_file: Path) -> None:
63
+ """Delete a file under /var/mobile/Media."""
64
+ AfcService(lockdown=service_provider).rm(str(remote_file))
@@ -0,0 +1,33 @@
1
+ import logging
2
+
3
+ from typer_injector import InjectingTyper
4
+
5
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
6
+ from pymobiledevice3.services.amfi import AmfiService
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ cli = InjectingTyper(
12
+ name="amfi",
13
+ help="Enable developer-mode or query its state",
14
+ no_args_is_help=True,
15
+ )
16
+
17
+
18
+ @cli.command()
19
+ def reveal_developer_mode(service_provider: ServiceProviderDep) -> None:
20
+ """reveal developer mode option in device's UI"""
21
+ AmfiService(service_provider).reveal_developer_mode_option_in_ui()
22
+
23
+
24
+ @cli.command()
25
+ def enable_developer_mode(service_provider: ServiceProviderDep) -> None:
26
+ """enable developer mode"""
27
+ AmfiService(service_provider).enable_developer_mode()
28
+
29
+
30
+ @cli.command()
31
+ def developer_mode_status(service_provider: ServiceProviderDep) -> None:
32
+ """query developer mode status"""
33
+ print_json(service_provider.developer_mode_status)