pymobiledevice3 4.14.6__py3-none-any.whl → 7.0.6__py3-none-any.whl

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 (164) hide show
  1. misc/plist_sniffer.py +15 -15
  2. misc/remotexpc_sniffer.py +29 -28
  3. misc/understanding_idevice_protocol_layers.md +15 -10
  4. pymobiledevice3/__main__.py +317 -127
  5. pymobiledevice3/_version.py +22 -4
  6. pymobiledevice3/bonjour.py +358 -113
  7. pymobiledevice3/ca.py +253 -16
  8. pymobiledevice3/cli/activation.py +31 -23
  9. pymobiledevice3/cli/afc.py +49 -40
  10. pymobiledevice3/cli/amfi.py +16 -21
  11. pymobiledevice3/cli/apps.py +87 -42
  12. pymobiledevice3/cli/backup.py +160 -90
  13. pymobiledevice3/cli/bonjour.py +44 -40
  14. pymobiledevice3/cli/cli_common.py +204 -198
  15. pymobiledevice3/cli/companion_proxy.py +14 -14
  16. pymobiledevice3/cli/crash.py +105 -56
  17. pymobiledevice3/cli/developer/__init__.py +62 -0
  18. pymobiledevice3/cli/developer/accessibility/__init__.py +65 -0
  19. pymobiledevice3/cli/developer/accessibility/settings.py +43 -0
  20. pymobiledevice3/cli/developer/arbitration.py +50 -0
  21. pymobiledevice3/cli/developer/condition.py +33 -0
  22. pymobiledevice3/cli/developer/core_device.py +294 -0
  23. pymobiledevice3/cli/developer/debugserver.py +244 -0
  24. pymobiledevice3/cli/developer/dvt/__init__.py +438 -0
  25. pymobiledevice3/cli/developer/dvt/core_profile_session.py +295 -0
  26. pymobiledevice3/cli/developer/dvt/simulate_location.py +56 -0
  27. pymobiledevice3/cli/developer/dvt/sysmon/__init__.py +69 -0
  28. pymobiledevice3/cli/developer/dvt/sysmon/process.py +188 -0
  29. pymobiledevice3/cli/developer/fetch_symbols.py +108 -0
  30. pymobiledevice3/cli/developer/simulate_location.py +51 -0
  31. pymobiledevice3/cli/diagnostics/__init__.py +75 -0
  32. pymobiledevice3/cli/diagnostics/battery.py +47 -0
  33. pymobiledevice3/cli/idam.py +42 -0
  34. pymobiledevice3/cli/lockdown.py +108 -103
  35. pymobiledevice3/cli/mounter.py +158 -99
  36. pymobiledevice3/cli/notification.py +38 -26
  37. pymobiledevice3/cli/pcap.py +45 -24
  38. pymobiledevice3/cli/power_assertion.py +18 -17
  39. pymobiledevice3/cli/processes.py +17 -23
  40. pymobiledevice3/cli/profile.py +165 -109
  41. pymobiledevice3/cli/provision.py +35 -34
  42. pymobiledevice3/cli/remote.py +217 -129
  43. pymobiledevice3/cli/restore.py +159 -143
  44. pymobiledevice3/cli/springboard.py +63 -53
  45. pymobiledevice3/cli/syslog.py +193 -86
  46. pymobiledevice3/cli/usbmux.py +73 -33
  47. pymobiledevice3/cli/version.py +5 -7
  48. pymobiledevice3/cli/webinspector.py +376 -214
  49. pymobiledevice3/common.py +3 -1
  50. pymobiledevice3/exceptions.py +182 -58
  51. pymobiledevice3/irecv.py +52 -53
  52. pymobiledevice3/irecv_devices.py +1489 -464
  53. pymobiledevice3/lockdown.py +473 -275
  54. pymobiledevice3/lockdown_service_provider.py +15 -8
  55. pymobiledevice3/osu/os_utils.py +27 -9
  56. pymobiledevice3/osu/posix_util.py +34 -15
  57. pymobiledevice3/osu/win_util.py +14 -8
  58. pymobiledevice3/pair_records.py +102 -21
  59. pymobiledevice3/remote/common.py +8 -4
  60. pymobiledevice3/remote/core_device/app_service.py +94 -67
  61. pymobiledevice3/remote/core_device/core_device_service.py +17 -14
  62. pymobiledevice3/remote/core_device/device_info.py +5 -5
  63. pymobiledevice3/remote/core_device/diagnostics_service.py +19 -4
  64. pymobiledevice3/remote/core_device/file_service.py +53 -23
  65. pymobiledevice3/remote/remote_service_discovery.py +79 -45
  66. pymobiledevice3/remote/remotexpc.py +73 -44
  67. pymobiledevice3/remote/tunnel_service.py +442 -317
  68. pymobiledevice3/remote/utils.py +14 -13
  69. pymobiledevice3/remote/xpc_message.py +145 -125
  70. pymobiledevice3/resources/dsc_uuid_map.py +19 -19
  71. pymobiledevice3/resources/firmware_notifications.py +20 -16
  72. pymobiledevice3/resources/notifications.txt +144 -0
  73. pymobiledevice3/restore/asr.py +27 -27
  74. pymobiledevice3/restore/base_restore.py +110 -21
  75. pymobiledevice3/restore/consts.py +87 -66
  76. pymobiledevice3/restore/device.py +59 -12
  77. pymobiledevice3/restore/fdr.py +46 -48
  78. pymobiledevice3/restore/ftab.py +19 -19
  79. pymobiledevice3/restore/img4.py +163 -0
  80. pymobiledevice3/restore/mbn.py +587 -0
  81. pymobiledevice3/restore/recovery.py +151 -151
  82. pymobiledevice3/restore/restore.py +562 -544
  83. pymobiledevice3/restore/restore_options.py +131 -110
  84. pymobiledevice3/restore/restored_client.py +51 -31
  85. pymobiledevice3/restore/tss.py +385 -267
  86. pymobiledevice3/service_connection.py +252 -59
  87. pymobiledevice3/services/accessibilityaudit.py +202 -120
  88. pymobiledevice3/services/afc.py +962 -365
  89. pymobiledevice3/services/amfi.py +24 -30
  90. pymobiledevice3/services/companion.py +23 -19
  91. pymobiledevice3/services/crash_reports.py +71 -47
  92. pymobiledevice3/services/debugserver_applist.py +3 -3
  93. pymobiledevice3/services/device_arbitration.py +8 -8
  94. pymobiledevice3/services/device_link.py +101 -79
  95. pymobiledevice3/services/diagnostics.py +973 -967
  96. pymobiledevice3/services/dtfetchsymbols.py +8 -8
  97. pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
  98. pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
  99. pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
  100. pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
  101. pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
  102. pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +466 -384
  103. pymobiledevice3/services/dvt/instruments/device_info.py +20 -11
  104. pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
  105. pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
  106. pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
  107. pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
  108. pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
  109. pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
  110. pymobiledevice3/services/dvt/instruments/process_control.py +35 -10
  111. pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
  112. pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
  113. pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
  114. pymobiledevice3/services/file_relay.py +10 -10
  115. pymobiledevice3/services/heartbeat.py +9 -8
  116. pymobiledevice3/services/house_arrest.py +16 -15
  117. pymobiledevice3/services/idam.py +20 -0
  118. pymobiledevice3/services/installation_proxy.py +173 -81
  119. pymobiledevice3/services/lockdown_service.py +20 -10
  120. pymobiledevice3/services/misagent.py +22 -19
  121. pymobiledevice3/services/mobile_activation.py +147 -64
  122. pymobiledevice3/services/mobile_config.py +331 -294
  123. pymobiledevice3/services/mobile_image_mounter.py +141 -113
  124. pymobiledevice3/services/mobilebackup2.py +203 -145
  125. pymobiledevice3/services/notification_proxy.py +11 -11
  126. pymobiledevice3/services/os_trace.py +134 -74
  127. pymobiledevice3/services/pcapd.py +314 -302
  128. pymobiledevice3/services/power_assertion.py +10 -9
  129. pymobiledevice3/services/preboard.py +4 -4
  130. pymobiledevice3/services/remote_fetch_symbols.py +21 -14
  131. pymobiledevice3/services/remote_server.py +176 -146
  132. pymobiledevice3/services/restore_service.py +16 -16
  133. pymobiledevice3/services/screenshot.py +15 -12
  134. pymobiledevice3/services/simulate_location.py +7 -7
  135. pymobiledevice3/services/springboard.py +15 -15
  136. pymobiledevice3/services/syslog.py +5 -5
  137. pymobiledevice3/services/web_protocol/alert.py +11 -11
  138. pymobiledevice3/services/web_protocol/automation_session.py +251 -239
  139. pymobiledevice3/services/web_protocol/cdp_screencast.py +46 -37
  140. pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
  141. pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
  142. pymobiledevice3/services/web_protocol/driver.py +114 -111
  143. pymobiledevice3/services/web_protocol/element.py +124 -111
  144. pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
  145. pymobiledevice3/services/web_protocol/selenium_api.py +49 -49
  146. pymobiledevice3/services/web_protocol/session_protocol.py +18 -12
  147. pymobiledevice3/services/web_protocol/switch_to.py +30 -27
  148. pymobiledevice3/services/webinspector.py +189 -155
  149. pymobiledevice3/tcp_forwarder.py +87 -69
  150. pymobiledevice3/tunneld/__init__.py +0 -0
  151. pymobiledevice3/tunneld/api.py +63 -0
  152. pymobiledevice3/tunneld/server.py +603 -0
  153. pymobiledevice3/usbmux.py +198 -147
  154. pymobiledevice3/utils.py +14 -11
  155. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/METADATA +55 -28
  156. pymobiledevice3-7.0.6.dist-info/RECORD +188 -0
  157. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/WHEEL +1 -1
  158. pymobiledevice3/cli/developer.py +0 -1215
  159. pymobiledevice3/cli/diagnostics.py +0 -99
  160. pymobiledevice3/tunneld.py +0 -524
  161. pymobiledevice3-4.14.6.dist-info/RECORD +0 -168
  162. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/entry_points.txt +0 -0
  163. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info/licenses}/LICENSE +0 -0
  164. {pymobiledevice3-4.14.6.dist-info → pymobiledevice3-7.0.6.dist-info}/top_level.txt +0 -0
@@ -2,41 +2,45 @@ import logging
2
2
  import plistlib
3
3
  import tempfile
4
4
  from pathlib import Path
5
- from typing import IO, Optional
5
+ from typing import Annotated, Literal, Optional
6
6
 
7
- import click
7
+ import typer
8
+ from typer_injector import InjectingTyper
8
9
 
9
10
  from pymobiledevice3.ca import create_keybag_file
10
- from pymobiledevice3.cli.cli_common import Command, print_json
11
- from pymobiledevice3.lockdown import LockdownClient
12
- from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
11
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
13
12
  from pymobiledevice3.services.mobile_activation import MobileActivationService
14
13
  from pymobiledevice3.services.mobile_config import MobileConfigService
15
14
 
16
15
  logger = logging.getLogger(__name__)
17
16
 
18
17
 
19
- @click.group()
20
- def cli() -> None:
21
- pass
18
+ cli = InjectingTyper(
19
+ name="profile",
20
+ help="Manage installed profiles or install SSL certificates",
21
+ no_args_is_help=True,
22
+ )
22
23
 
23
24
 
24
- @cli.group('profile')
25
- def profile_group() -> None:
26
- """ Managed installed profiles or install SSL certificates """
27
- pass
28
-
29
-
30
- @profile_group.command('list', cls=Command)
31
- def profile_list(service_provider: LockdownClient):
32
- """ List installed profiles """
25
+ @cli.command("list")
26
+ def profile_list(service_provider: ServiceProviderDep) -> None:
27
+ """List installed profiles"""
33
28
  print_json(MobileConfigService(lockdown=service_provider).get_profile_list())
34
29
 
35
30
 
36
- @profile_group.command('install', cls=Command)
37
- @click.option('--keybag', type=click.Path(file_okay=True, dir_okay=False, exists=True))
38
- @click.argument('profiles', nargs=-1, type=click.File('rb'))
39
- def profile_install(service_provider: LockdownServiceProvider, keybag: Optional[str], profiles: list[IO]) -> None:
31
+ @cli.command("install")
32
+ def profile_install(
33
+ service_provider: ServiceProviderDep,
34
+ profiles: list[Path],
35
+ keybag: Annotated[
36
+ Optional[Path],
37
+ typer.Option(
38
+ exists=True,
39
+ file_okay=True,
40
+ dir_okay=False,
41
+ ),
42
+ ] = None,
43
+ ) -> None:
40
44
  """
41
45
  Install given profiles
42
46
 
@@ -44,101 +48,129 @@ def profile_install(service_provider: LockdownServiceProvider, keybag: Optional[
44
48
  """
45
49
  service = MobileConfigService(lockdown=service_provider)
46
50
  for profile in profiles:
47
- logger.info(f'installing {profile.name}')
51
+ logger.info(f"installing {profile}")
48
52
  if keybag is not None:
49
- service.install_profile_silent(Path(keybag), profile.read())
53
+ service.install_profile_silent(Path(keybag), profile.read_bytes())
50
54
  else:
51
- service.install_profile(profile.read())
55
+ service.install_profile(profile.read_bytes())
52
56
 
53
57
 
54
- @profile_group.command('cloud-configuration', cls=Command)
55
- @click.argument('config', type=click.File('rb'), required=False)
56
- def profile_cloud_configuration(service_provider: LockdownServiceProvider, config: Optional[IO]) -> None:
57
- """ Get/Set cloud configuration """
58
+ @cli.command("cloud-configuration")
59
+ def profile_cloud_configuration(service_provider: ServiceProviderDep, config: Optional[Path] = None) -> None:
60
+ """Get/Set cloud configuration"""
58
61
  if not config:
59
62
  print_json(MobileConfigService(lockdown=service_provider).get_cloud_configuration())
60
63
  else:
61
- config_json = plistlib.load(config)
62
- logger.info(f'applying cloud configuration {config_json}')
64
+ with config.open("rb") as config_file:
65
+ config_json = plistlib.load(config_file)
66
+ logger.info(f"applying cloud configuration {config_json}")
63
67
  MobileConfigService(lockdown=service_provider).set_cloud_configuration(config_json)
64
- logger.info('applied cloud configuration')
68
+ logger.info("applied cloud configuration")
65
69
 
66
70
 
67
- @profile_group.command('store', cls=Command)
68
- @click.argument('profiles', nargs=-1, type=click.File('rb'))
69
- def profile_store(service_provider: LockdownServiceProvider, profiles: list[IO]) -> None:
70
- """ Store a profile """
71
+ @cli.command("store")
72
+ def profile_store(service_provider: ServiceProviderDep, profiles: list[Path]) -> None:
73
+ """Store a profile"""
71
74
  service = MobileConfigService(lockdown=service_provider)
72
75
  for profile in profiles:
73
- logger.info(f'storing {profile.name}')
74
- service.store_profile(profile.read())
76
+ logger.info(f"storing {profile.name}")
77
+ service.store_profile(profile.read_bytes())
75
78
 
76
79
 
77
- @profile_group.command('remove', cls=Command)
78
- @click.argument('name')
79
- def profile_remove(service_provider: LockdownServiceProvider, name: str) -> None:
80
- """ Remove a profile by its name """
80
+ @cli.command("remove")
81
+ def profile_remove(service_provider: ServiceProviderDep, name: str) -> None:
82
+ """Remove a profile by its name"""
81
83
  MobileConfigService(lockdown=service_provider).remove_profile(name)
82
84
 
83
85
 
84
- @profile_group.command('set-wifi-power', cls=Command)
85
- @click.argument('state', type=click.Choice(['on', 'off']), required=False)
86
- def profile_set_wifi_power(service_provider: LockdownServiceProvider, state: str) -> None:
87
- """ change Wi-Fi power state """
88
- MobileConfigService(lockdown=service_provider).set_wifi_power_state(state == 'on')
89
-
90
-
91
- @profile_group.command('erase-device', cls=Command)
92
- @click.option('--preserve-data-plan/--no-preserve-data-plan', default=True,
93
- help='Preserves eSIM / data plan after erase')
94
- @click.option('--disallow-proximity-setup/--no-disallow-proximity-setup', default=False,
95
- help='Disallows to setup the erased device from nearby devices')
96
- def profile_erase_device(service_provider: LockdownServiceProvider, preserve_data_plan: bool,
97
- disallow_proximity_setup: bool) -> None:
98
- """ Erase device """
99
- logger.info(f'Erasing device with preserve_data_plan: {preserve_data_plan}, '
100
- f'disallow_proximity_setup: {disallow_proximity_setup}')
86
+ @cli.command("set-wifi-power")
87
+ def profile_set_wifi_power(service_provider: ServiceProviderDep, state: Literal["on", "off"] = "off") -> None:
88
+ """change Wi-Fi power state"""
89
+ MobileConfigService(lockdown=service_provider).set_wifi_power_state(state == "on")
90
+
91
+
92
+ @cli.command("erase-device")
93
+ def profile_erase_device(
94
+ service_provider: ServiceProviderDep,
95
+ preserve_data_plan: Annotated[
96
+ bool,
97
+ typer.Option(help="Preserves eSIM / data plan after erase"),
98
+ ] = True,
99
+ disallow_proximity_setup: Annotated[
100
+ bool,
101
+ typer.Option(help="Disallows setup of the erased device from nearby devices"),
102
+ ] = False,
103
+ ) -> None:
104
+ """Erase device"""
105
+ logger.info(
106
+ f"Erasing device with preserve_data_plan: {preserve_data_plan}, "
107
+ f"disallow_proximity_setup: {disallow_proximity_setup}"
108
+ )
101
109
  MobileConfigService(lockdown=service_provider).erase_device(preserve_data_plan, disallow_proximity_setup)
102
- logger.info('Erased device')
103
-
104
-
105
- @profile_group.command('create-keybag')
106
- @click.argument('keybag', type=click.Path(file_okay=True, dir_okay=False, exists=False))
107
- @click.argument('organization')
108
- def profile_create_keybag(keybag: str, organization: str) -> None:
109
- """ Create keybag storing certificate and private key """
110
- create_keybag_file(Path(keybag), organization)
111
-
112
-
113
- @profile_group.command('supervise', cls=Command)
114
- @click.argument('organization')
115
- @click.option('--keybag', type=click.Path(file_okay=True, dir_okay=False, exists=True))
116
- def profile_supervise(service_provider: LockdownServiceProvider, organization: str, keybag: Optional[str]) -> None:
117
- """ Supervise device """
118
- if MobileActivationService(service_provider).state == 'Unactivated':
119
- logger.info('Activating device')
110
+ logger.info("Erased device")
111
+
112
+
113
+ @cli.command("create-keybag")
114
+ def profile_create_keybag(
115
+ keybag: Annotated[
116
+ Path,
117
+ typer.Argument(
118
+ exists=False,
119
+ file_okay=True,
120
+ dir_okay=False,
121
+ ),
122
+ ],
123
+ organization: str,
124
+ ) -> None:
125
+ """Create keybag storing certificate and private key"""
126
+ create_keybag_file(keybag, organization)
127
+
128
+
129
+ @cli.command("supervise")
130
+ def profile_supervise(
131
+ service_provider: ServiceProviderDep,
132
+ organization: str,
133
+ keybag: Annotated[
134
+ Optional[Path],
135
+ typer.Option(
136
+ exists=True,
137
+ file_okay=True,
138
+ dir_okay=False,
139
+ ),
140
+ ] = None,
141
+ ) -> None:
142
+ """Supervise device"""
143
+ if MobileActivationService(service_provider).state == "Unactivated":
144
+ logger.info("Activating device")
120
145
  MobileActivationService(service_provider).activate()
121
- logger.info('Device has been successfully activated')
122
- logger.info('Supervising device')
146
+ logger.info("Device has been successfully activated")
147
+ logger.info("Supervising device")
123
148
  if keybag is None:
124
149
  with tempfile.TemporaryDirectory() as temp_dir:
125
- keybag = Path(temp_dir) / 'keybag'
150
+ keybag = Path(temp_dir) / "keybag"
126
151
  create_keybag_file(keybag, organization)
127
152
  MobileConfigService(lockdown=service_provider).supervise(organization, keybag)
128
153
  else:
129
154
  MobileConfigService(lockdown=service_provider).supervise(organization, Path(keybag))
130
155
 
131
- logger.info('Device has been successfully supervised')
132
-
133
-
134
- @profile_group.command('install-wifi-profile', cls=Command)
135
- @click.argument('encryption_type')
136
- @click.argument('ssid')
137
- @click.argument('password')
138
- @click.option('--keybag', type=click.Path(file_okay=True, dir_okay=False, exists=True))
139
- def profile_install_wifi_profile(service_provider: LockdownServiceProvider, encryption_type: str, ssid: str,
140
- password: str,
141
- keybag: Optional[str]) -> None:
156
+ logger.info("Device has been successfully supervised")
157
+
158
+
159
+ @cli.command("install-wifi-profile")
160
+ def profile_install_wifi_profile(
161
+ service_provider: ServiceProviderDep,
162
+ encryption_type: str,
163
+ ssid: str,
164
+ password: str,
165
+ keybag: Annotated[
166
+ Optional[Path],
167
+ typer.Option(
168
+ exists=True,
169
+ file_okay=True,
170
+ dir_okay=False,
171
+ ),
172
+ ] = None,
173
+ ) -> None:
142
174
  """
143
175
  Install Wi-Fi profile
144
176
 
@@ -147,34 +179,58 @@ def profile_install_wifi_profile(service_provider: LockdownServiceProvider, encr
147
179
  if keybag is not None:
148
180
  keybag = Path(keybag)
149
181
  MobileConfigService(lockdown=service_provider).install_wifi_profile(
150
- encryption_type=encryption_type, ssid=ssid, password=password, keybag_file=keybag)
151
-
152
-
153
- @profile_group.command('install-http-proxy', cls=Command)
154
- @click.argument('server')
155
- @click.argument('port', type=click.IntRange(1, 65535))
156
- @click.option('--keybag', type=click.Path(file_okay=True, dir_okay=False, exists=True))
157
- def profile_install_http_proxy(service_provider: LockdownServiceProvider, server: str, port: int,
158
- keybag: Optional[str]) -> None:
159
- """ Install HTTP Proxy profile """
182
+ encryption_type=encryption_type, ssid=ssid, password=password, keybag_file=keybag
183
+ )
184
+
185
+
186
+ @cli.command("install-http-proxy")
187
+ def profile_install_http_proxy(
188
+ service_provider: ServiceProviderDep,
189
+ server: str,
190
+ port: Annotated[
191
+ int,
192
+ typer.Argument(min=1, max=0xFFFF),
193
+ ],
194
+ keybag: Annotated[
195
+ Optional[Path],
196
+ typer.Option(
197
+ exists=True,
198
+ file_okay=True,
199
+ dir_okay=False,
200
+ ),
201
+ ] = None,
202
+ ) -> None:
203
+ """Install HTTP Proxy profile"""
160
204
  if keybag is not None:
161
205
  keybag = Path(keybag)
162
206
  MobileConfigService(lockdown=service_provider).install_http_proxy(server, port, keybag_file=keybag)
163
207
 
164
208
 
165
- @profile_group.command('remove-http-proxy', cls=Command)
166
- def profile_remove_http_proxy(service_provider: LockdownServiceProvider) -> None:
167
- """ Remove HTTP Proxy profile that was previously installed using pymobiledevice3 """
209
+ @cli.command("remove-http-proxy")
210
+ def profile_remove_http_proxy(service_provider: ServiceProviderDep) -> None:
211
+ """Remove HTTP Proxy profile that was previously installed using pymobiledevice3"""
168
212
  MobileConfigService(lockdown=service_provider).remove_http_proxy()
169
213
 
170
214
 
171
- @profile_group.command('install-restrictions-profile', cls=Command)
172
- @click.option('--keybag', type=click.Path(file_okay=True, dir_okay=False, exists=True))
173
- @click.option('--enforced-software-update-delay', type=click.IntRange(0, 90), default=0)
215
+ @cli.command("install-restrictions-profile")
174
216
  def profile_install_restrictions_profile(
175
- service_provider: LockdownServiceProvider, keybag: Optional[str], enforced_software_update_delay: int) -> None:
176
- """ Install restrictions profile (can be used for delayed OTA) """
217
+ service_provider: ServiceProviderDep,
218
+ keybag: Annotated[
219
+ Optional[Path],
220
+ typer.Option(
221
+ exists=True,
222
+ file_okay=True,
223
+ dir_okay=False,
224
+ ),
225
+ ] = None,
226
+ enforced_software_update_delay: Annotated[
227
+ int,
228
+ typer.Option(min=0, max=90),
229
+ ] = 0,
230
+ ) -> None:
231
+ """Install restrictions profile (can be used for delayed OTA)"""
177
232
  if keybag is not None:
178
233
  keybag = Path(keybag)
179
234
  MobileConfigService(lockdown=service_provider).install_restrictions_profile(
180
- enforced_software_update_delay=enforced_software_update_delay, keybag_file=keybag)
235
+ enforced_software_update_delay=enforced_software_update_delay, keybag_file=keybag
236
+ )
@@ -1,58 +1,59 @@
1
1
  import logging
2
2
  from pathlib import Path
3
+ from typing import Annotated
3
4
 
4
- import click
5
+ import typer
6
+ from typer_injector import InjectingTyper
5
7
 
6
- from pymobiledevice3.cli.cli_common import Command, print_json
7
- from pymobiledevice3.lockdown import LockdownClient
8
+ from pymobiledevice3.cli.cli_common import ServiceProviderDep, print_json
8
9
  from pymobiledevice3.services.misagent import MisagentService
9
10
 
10
11
  logger = logging.getLogger(__name__)
11
12
 
12
13
 
13
- @click.group()
14
- def cli() -> None:
15
- pass
14
+ cli = InjectingTyper(
15
+ name="provision",
16
+ help="Manage installed provision profiles",
17
+ no_args_is_help=True,
18
+ )
16
19
 
17
20
 
18
- @cli.group()
19
- def provision() -> None:
20
- """ Manage installed provision profiles """
21
- pass
21
+ @cli.command("install")
22
+ def provision_install(service_provider: ServiceProviderDep, profile: Path) -> None:
23
+ """install a provision profile (.mobileprovision file)"""
24
+ with profile.open("rb") as profile_file:
25
+ MisagentService(lockdown=service_provider).install(profile_file)
22
26
 
23
27
 
24
- @provision.command('install', cls=Command)
25
- @click.argument('profile', type=click.File('rb'))
26
- def provision_install(service_provider: LockdownClient, profile):
27
- """ install a provision profile (.mobileprovision file) """
28
- MisagentService(lockdown=service_provider).install(profile)
29
-
30
-
31
- @provision.command('remove', cls=Command)
32
- @click.argument('profile_id')
33
- def provision_remove(service_provider: LockdownClient, profile_id):
34
- """ remove a provision profile """
28
+ @cli.command("remove")
29
+ def provision_remove(service_provider: ServiceProviderDep, profile_id: str) -> None:
30
+ """remove a provision profile"""
35
31
  MisagentService(lockdown=service_provider).remove(profile_id)
36
32
 
37
33
 
38
- @provision.command('clear', cls=Command)
39
- def provision_clear(service_provider: LockdownClient):
40
- """ remove all provision profiles """
34
+ @cli.command("clear")
35
+ def provision_clear(service_provider: ServiceProviderDep) -> None:
36
+ """remove all provision profiles"""
41
37
  for profile in MisagentService(lockdown=service_provider).copy_all():
42
- MisagentService(lockdown=service_provider).remove(profile.plist['UUID'])
38
+ MisagentService(lockdown=service_provider).remove(profile.plist["UUID"])
43
39
 
44
40
 
45
- @provision.command('list', cls=Command)
46
- def provision_list(service_provider: LockdownClient):
47
- """ list installed provision profiles """
41
+ @cli.command("list")
42
+ def provision_list(service_provider: ServiceProviderDep) -> None:
43
+ """list installed provision profiles"""
48
44
  print_json([p.plist for p in MisagentService(lockdown=service_provider).copy_all()])
49
45
 
50
46
 
51
- @provision.command('dump', cls=Command)
52
- @click.argument('out', type=click.Path(file_okay=False, dir_okay=True, exists=True))
53
- def provision_dump(service_provider: LockdownClient, out):
54
- """ dump installed provision profiles to specified location """
47
+ @cli.command("dump")
48
+ def provision_dump(
49
+ service_provider: ServiceProviderDep,
50
+ out: Annotated[
51
+ Path,
52
+ typer.Argument(file_okay=False, dir_okay=True, exists=True),
53
+ ],
54
+ ) -> None:
55
+ """dump installed provision profiles to specified location"""
55
56
  for profile in MisagentService(lockdown=service_provider).copy_all():
56
- filename = f'{profile.plist["UUID"]}.mobileprovision'
57
- logger.info(f'downloading {filename}')
57
+ filename = f"{profile.plist['UUID']}.mobileprovision"
58
+ logger.info(f"downloading {filename}")
58
59
  (Path(out) / filename).write_bytes(profile.buf)