mobster-cli 0.4.3__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 (1009) hide show
  1. mobile_agent/__init__.py +4 -0
  2. mobile_agent/__main__.py +598 -0
  3. mobile_agent/accounts/__init__.py +1 -0
  4. mobile_agent/accounts/context.py +80 -0
  5. mobile_agent/accounts/mcp.py +55 -0
  6. mobile_agent/accounts/pacing.py +318 -0
  7. mobile_agent/accounts/register.py +78 -0
  8. mobile_agent/accounts/routes.py +212 -0
  9. mobile_agent/accounts/routing.py +69 -0
  10. mobile_agent/accounts/screen.py +273 -0
  11. mobile_agent/accounts/stats.py +54 -0
  12. mobile_agent/accounts/store.py +394 -0
  13. mobile_agent/agent_hooks.py +123 -0
  14. mobile_agent/ai_plans/__init__.py +7 -0
  15. mobile_agent/ai_plans/agents.py +96 -0
  16. mobile_agent/ai_plans/claude_code.py +208 -0
  17. mobile_agent/ai_plans/cli.py +400 -0
  18. mobile_agent/ai_plans/codex.py +258 -0
  19. mobile_agent/ai_plans/compat_chat.py +307 -0
  20. mobile_agent/ai_plans/device_login.py +164 -0
  21. mobile_agent/ai_plans/draft_client.py +68 -0
  22. mobile_agent/ai_plans/gemini.py +137 -0
  23. mobile_agent/ai_plans/instructions.py +65 -0
  24. mobile_agent/ai_plans/mcp_http.py +160 -0
  25. mobile_agent/ai_plans/phone_tools.py +892 -0
  26. mobile_agent/ai_plans/providers.py +235 -0
  27. mobile_agent/ai_plans/routes.py +160 -0
  28. mobile_agent/ai_plans/runner.py +400 -0
  29. mobile_agent/ai_plans/vault.py +114 -0
  30. mobile_agent/alerts.py +337 -0
  31. mobile_agent/android/__init__.py +18 -0
  32. mobile_agent/android/adb.py +268 -0
  33. mobile_agent/android/conditions.py +128 -0
  34. mobile_agent/android/doctor.py +54 -0
  35. mobile_agent/android/driver.py +137 -0
  36. mobile_agent/android/emulator.py +216 -0
  37. mobile_agent/android/logs.py +85 -0
  38. mobile_agent/android/manager.py +225 -0
  39. mobile_agent/android/records.py +31 -0
  40. mobile_agent/android/tree.py +96 -0
  41. mobile_agent/answer_shapes.py +230 -0
  42. mobile_agent/api_devices.py +333 -0
  43. mobile_agent/api_errors.py +17 -0
  44. mobile_agent/api_get.py +249 -0
  45. mobile_agent/api_http.py +467 -0
  46. mobile_agent/api_post.py +453 -0
  47. mobile_agent/api_routes.py +173 -0
  48. mobile_agent/app_api/__init__.py +4 -0
  49. mobile_agent/app_api/cli.py +151 -0
  50. mobile_agent/app_api/endpoints.py +193 -0
  51. mobile_agent/app_api/mcp.py +54 -0
  52. mobile_agent/app_api/openapi.py +146 -0
  53. mobile_agent/app_api/reading.py +172 -0
  54. mobile_agent/app_api/register.py +10 -0
  55. mobile_agent/app_api/screen.py +216 -0
  56. mobile_agent/app_api/service.py +382 -0
  57. mobile_agent/app_api/sessions.py +404 -0
  58. mobile_agent/app_api/tokens.py +167 -0
  59. mobile_agent/app_choice.py +131 -0
  60. mobile_agent/app_icons.py +183 -0
  61. mobile_agent/app_killer.py +69 -0
  62. mobile_agent/artifacts.py +606 -0
  63. mobile_agent/attach/__init__.py +13 -0
  64. mobile_agent/attach/cli.py +46 -0
  65. mobile_agent/attach/flutter.py +28 -0
  66. mobile_agent/attach/score.py +119 -0
  67. mobile_agent/attach/web.py +13 -0
  68. mobile_agent/attachments/__init__.py +1 -0
  69. mobile_agent/attachments/agent.py +418 -0
  70. mobile_agent/attachments/extract.py +217 -0
  71. mobile_agent/attachments/mcp.py +104 -0
  72. mobile_agent/attachments/register.py +73 -0
  73. mobile_agent/attachments/routes.py +326 -0
  74. mobile_agent/attachments/service.py +170 -0
  75. mobile_agent/attachments/sniff.py +152 -0
  76. mobile_agent/attachments/store.py +441 -0
  77. mobile_agent/audit_verify.py +436 -0
  78. mobile_agent/bench/README.md +410 -0
  79. mobile_agent/bench/__init__.py +0 -0
  80. mobile_agent/bench/__main__.py +394 -0
  81. mobile_agent/bench/ab.py +168 -0
  82. mobile_agent/bench/actuation.py +368 -0
  83. mobile_agent/bench/agents/__init__.py +9 -0
  84. mobile_agent/bench/agents/base.py +105 -0
  85. mobile_agent/bench/agents/plan_clients.py +245 -0
  86. mobile_agent/bench/agents/smart.py +533 -0
  87. mobile_agent/bench/checks.py +358 -0
  88. mobile_agent/bench/decide_gate.py +214 -0
  89. mobile_agent/bench/diagnose.py +181 -0
  90. mobile_agent/bench/evals/__init__.py +14 -0
  91. mobile_agent/bench/evals/build.py +565 -0
  92. mobile_agent/bench/evals/checkpoint.py +214 -0
  93. mobile_agent/bench/evals/irt.py +219 -0
  94. mobile_agent/bench/evals/l0.py +647 -0
  95. mobile_agent/bench/evals/l1.py +866 -0
  96. mobile_agent/bench/evals/models.py +124 -0
  97. mobile_agent/bench/evals/pairwise.py +189 -0
  98. mobile_agent/bench/evals/proxy.py +90 -0
  99. mobile_agent/bench/evals/sets.py +149 -0
  100. mobile_agent/bench/evals/traces.py +446 -0
  101. mobile_agent/bench/fixtures.py +176 -0
  102. mobile_agent/bench/guard_replay.py +129 -0
  103. mobile_agent/bench/h2-manifest.json +20 -0
  104. mobile_agent/bench/h3-manifest.json +20 -0
  105. mobile_agent/bench/heldout-manifest.json +32 -0
  106. mobile_agent/bench/heldout.py +375 -0
  107. mobile_agent/bench/iosworld.py +901 -0
  108. mobile_agent/bench/iosworld_stats.py +510 -0
  109. mobile_agent/bench/llm_units.py +116 -0
  110. mobile_agent/bench/manifest.json +93 -0
  111. mobile_agent/bench/metrics.py +66 -0
  112. mobile_agent/bench/phone.py +44 -0
  113. mobile_agent/bench/replay.py +172 -0
  114. mobile_agent/bench/report.py +207 -0
  115. mobile_agent/bench/runner.py +456 -0
  116. mobile_agent/bench/safety.py +310 -0
  117. mobile_agent/bench/stream_timing.py +213 -0
  118. mobile_agent/bench/suite.py +444 -0
  119. mobile_agent/bench/sweep.py +176 -0
  120. mobile_agent/bench/tape.py +798 -0
  121. mobile_agent/bench/tape_replay.py +88 -0
  122. mobile_agent/bench/trace_evals.py +128 -0
  123. mobile_agent/bench/trace_view.py +81 -0
  124. mobile_agent/bench/truth/iphone15pro.json +138 -0
  125. mobile_agent/bench/truth.py +340 -0
  126. mobile_agent/bench/tuning.py +543 -0
  127. mobile_agent/bench/tuning_analysis.py +656 -0
  128. mobile_agent/bench/vague-heldout-manifest.json +12 -0
  129. mobile_agent/bench/vague_rubric.py +135 -0
  130. mobile_agent/bench/vague_run.py +243 -0
  131. mobile_agent/brand.py +454 -0
  132. mobile_agent/brand_fonts.py +756 -0
  133. mobile_agent/build_identity.py +21 -0
  134. mobile_agent/cable_bt.py +623 -0
  135. mobile_agent/cable_bt_runtime.py +128 -0
  136. mobile_agent/catalog.py +69 -0
  137. mobile_agent/chart_svg.py +415 -0
  138. mobile_agent/chat_here.py +189 -0
  139. mobile_agent/cli_constants.py +5 -0
  140. mobile_agent/cli_parser.py +611 -0
  141. mobile_agent/cli_run.py +115 -0
  142. mobile_agent/compiled_read/__init__.py +21 -0
  143. mobile_agent/compiled_read/delta.py +89 -0
  144. mobile_agent/compiled_read/extractors.py +202 -0
  145. mobile_agent/compiled_read/routes.py +56 -0
  146. mobile_agent/completion.py +267 -0
  147. mobile_agent/compose.py +48 -0
  148. mobile_agent/config.py +114 -0
  149. mobile_agent/connectors/__init__.py +1 -0
  150. mobile_agent/connectors/api_session.py +76 -0
  151. mobile_agent/connectors/callback_page.py +146 -0
  152. mobile_agent/connectors/client.py +582 -0
  153. mobile_agent/connectors/github_app.py +55 -0
  154. mobile_agent/connectors/graph_api.py +396 -0
  155. mobile_agent/connectors/imports.py +170 -0
  156. mobile_agent/connectors/issue.py +66 -0
  157. mobile_agent/connectors/keystore.py +85 -0
  158. mobile_agent/connectors/marks.py +222 -0
  159. mobile_agent/connectors/oauth.py +476 -0
  160. mobile_agent/connectors/picker.py +228 -0
  161. mobile_agent/connectors/presets.py +591 -0
  162. mobile_agent/connectors/register.py +48 -0
  163. mobile_agent/connectors/routes.py +153 -0
  164. mobile_agent/connectors/service.py +966 -0
  165. mobile_agent/connectors/shared.py +136 -0
  166. mobile_agent/connectors/skill.py +446 -0
  167. mobile_agent/connectors/slack_api.py +269 -0
  168. mobile_agent/connectors/steps.py +92 -0
  169. mobile_agent/connectors/store.py +144 -0
  170. mobile_agent/connectors/vault.py +72 -0
  171. mobile_agent/console.py +178 -0
  172. mobile_agent/content_queue/__init__.py +1 -0
  173. mobile_agent/content_queue/client.py +263 -0
  174. mobile_agent/content_queue/importing.py +153 -0
  175. mobile_agent/content_queue/media_check.py +224 -0
  176. mobile_agent/content_queue/prompt.py +64 -0
  177. mobile_agent/content_queue/register.py +91 -0
  178. mobile_agent/content_queue/routes.py +370 -0
  179. mobile_agent/content_queue/runner.py +475 -0
  180. mobile_agent/content_queue/store.py +521 -0
  181. mobile_agent/contract.py +996 -0
  182. mobile_agent/contract_asks.py +248 -0
  183. mobile_agent/contract_prompt.py +45 -0
  184. mobile_agent/contract_screen.py +981 -0
  185. mobile_agent/contract_view.py +84 -0
  186. mobile_agent/corpus/__init__.py +26 -0
  187. mobile_agent/corpus/cli.py +63 -0
  188. mobile_agent/corpus/consent.py +55 -0
  189. mobile_agent/corpus/listener.py +76 -0
  190. mobile_agent/corpus/register.py +9 -0
  191. mobile_agent/corpus/skeleton.py +72 -0
  192. mobile_agent/corpus/store.py +143 -0
  193. mobile_agent/costs.py +161 -0
  194. mobile_agent/decider_scope.py +40 -0
  195. mobile_agent/decision_provider.py +189 -0
  196. mobile_agent/deeplinks.py +352 -0
  197. mobile_agent/device_info.py +116 -0
  198. mobile_agent/device_manager.py +978 -0
  199. mobile_agent/device_status.py +195 -0
  200. mobile_agent/device_targets.py +217 -0
  201. mobile_agent/devices.py +676 -0
  202. mobile_agent/devkit/__init__.py +64 -0
  203. mobile_agent/devkit/account_cli.py +90 -0
  204. mobile_agent/devkit/asc.py +562 -0
  205. mobile_agent/devkit/asc_cli.py +214 -0
  206. mobile_agent/devkit/ci.py +737 -0
  207. mobile_agent/devkit/ci_cli.py +196 -0
  208. mobile_agent/devkit/ci_hosts.py +194 -0
  209. mobile_agent/devkit/es256.py +197 -0
  210. mobile_agent/devkit/expo.py +440 -0
  211. mobile_agent/devkit/expo_cli.py +405 -0
  212. mobile_agent/devkit/expo_dev.py +203 -0
  213. mobile_agent/devkit/expo_routes.py +90 -0
  214. mobile_agent/devkit/fixtures_cli.py +133 -0
  215. mobile_agent/devkit/fixtures_routes.py +82 -0
  216. mobile_agent/devkit/links.py +375 -0
  217. mobile_agent/devkit/links_cli.py +123 -0
  218. mobile_agent/devkit/links_routes.py +83 -0
  219. mobile_agent/devkit/nightly.py +152 -0
  220. mobile_agent/devkit/shots.py +203 -0
  221. mobile_agent/devkit/testflight.py +155 -0
  222. mobile_agent/devkit/testflight_cli.py +127 -0
  223. mobile_agent/devkit/testflight_routes.py +111 -0
  224. mobile_agent/devkit/xcode.py +236 -0
  225. mobile_agent/devkit/xcode_cli.py +101 -0
  226. mobile_agent/devtools.py +419 -0
  227. mobile_agent/doctor.py +509 -0
  228. mobile_agent/doctor_fix.py +467 -0
  229. mobile_agent/downloads.py +55 -0
  230. mobile_agent/drivers.py +793 -0
  231. mobile_agent/dry_run.py +102 -0
  232. mobile_agent/effect_ledger.py +83 -0
  233. mobile_agent/engine_health.py +378 -0
  234. mobile_agent/engine_hooks.py +96 -0
  235. mobile_agent/engines.py +408 -0
  236. mobile_agent/env_file.py +63 -0
  237. mobile_agent/env_secrets.py +149 -0
  238. mobile_agent/errors.py +15 -0
  239. mobile_agent/evals/__init__.py +3 -0
  240. mobile_agent/evals/app_survey.py +149 -0
  241. mobile_agent/evals/development/__init__.py +1 -0
  242. mobile_agent/evals/development/score.py +276 -0
  243. mobile_agent/evals/latency_report.py +312 -0
  244. mobile_agent/evals/offline_report.py +374 -0
  245. mobile_agent/evals/oracles.py +298 -0
  246. mobile_agent/evals/redteam/__init__.py +15 -0
  247. mobile_agent/evals/redteam/__main__.py +72 -0
  248. mobile_agent/evals/redteam/cases.py +730 -0
  249. mobile_agent/evals/redteam/harness.py +604 -0
  250. mobile_agent/evals/redteam/live.py +139 -0
  251. mobile_agent/evals/redteam/plain.py +85 -0
  252. mobile_agent/evals/vision/__init__.py +0 -0
  253. mobile_agent/evals/vision/images/dog_001.jpg +0 -0
  254. mobile_agent/evals/vision/images/dog_002.jpg +0 -0
  255. mobile_agent/evals/vision/images/dog_003.jpg +0 -0
  256. mobile_agent/evals/vision/images/dog_004.jpg +0 -0
  257. mobile_agent/evals/vision/images/dog_005.jpg +0 -0
  258. mobile_agent/evals/vision/images/dog_006.jpg +0 -0
  259. mobile_agent/evals/vision/images/dog_007.jpg +0 -0
  260. mobile_agent/evals/vision/images/dog_010.jpg +0 -0
  261. mobile_agent/evals/vision/images/dog_011.jpg +0 -0
  262. mobile_agent/evals/vision/images/dog_013.jpg +0 -0
  263. mobile_agent/evals/vision/images/dog_014.jpg +0 -0
  264. mobile_agent/evals/vision/images/dog_015.jpg +0 -0
  265. mobile_agent/evals/vision/images/dog_016.jpg +0 -0
  266. mobile_agent/evals/vision/images/dog_017.jpg +0 -0
  267. mobile_agent/evals/vision/images/dog_019.jpg +0 -0
  268. mobile_agent/evals/vision/images/dog_020.jpg +0 -0
  269. mobile_agent/evals/vision/images/dog_021.jpg +0 -0
  270. mobile_agent/evals/vision/images/dog_022.jpg +0 -0
  271. mobile_agent/evals/vision/images/dog_023.jpg +0 -0
  272. mobile_agent/evals/vision/images/dog_024.jpg +0 -0
  273. mobile_agent/evals/vision/images/dog_026.jpg +0 -0
  274. mobile_agent/evals/vision/images/dog_027.jpg +0 -0
  275. mobile_agent/evals/vision/images/dog_034.jpg +0 -0
  276. mobile_agent/evals/vision/images/dog_035.jpg +0 -0
  277. mobile_agent/evals/vision/images/dog_038.jpg +0 -0
  278. mobile_agent/evals/vision/images/dog_042.jpg +0 -0
  279. mobile_agent/evals/vision/images/dog_047.jpg +0 -0
  280. mobile_agent/evals/vision/images/dog_052.jpg +0 -0
  281. mobile_agent/evals/vision/images/dog_058.jpg +0 -0
  282. mobile_agent/evals/vision/images/dog_062.jpg +0 -0
  283. mobile_agent/evals/vision/images/eyes_000.jpg +0 -0
  284. mobile_agent/evals/vision/images/eyes_010.jpg +0 -0
  285. mobile_agent/evals/vision/images/eyes_011.jpg +0 -0
  286. mobile_agent/evals/vision/images/eyes_015.jpg +0 -0
  287. mobile_agent/evals/vision/images/eyes_016.jpg +0 -0
  288. mobile_agent/evals/vision/images/eyes_017.jpg +0 -0
  289. mobile_agent/evals/vision/images/eyes_019.jpg +0 -0
  290. mobile_agent/evals/vision/images/eyes_021.jpg +0 -0
  291. mobile_agent/evals/vision/images/eyes_025.jpg +0 -0
  292. mobile_agent/evals/vision/images/eyes_026.jpg +0 -0
  293. mobile_agent/evals/vision/images/eyes_027.jpg +0 -0
  294. mobile_agent/evals/vision/images/eyes_030.jpg +0 -0
  295. mobile_agent/evals/vision/images/eyes_032.jpg +0 -0
  296. mobile_agent/evals/vision/images/eyes_033.jpg +0 -0
  297. mobile_agent/evals/vision/images/eyes_034.jpg +0 -0
  298. mobile_agent/evals/vision/images/eyes_035.jpg +0 -0
  299. mobile_agent/evals/vision/images/eyes_036.jpg +0 -0
  300. mobile_agent/evals/vision/images/eyes_037.jpg +0 -0
  301. mobile_agent/evals/vision/images/eyes_039.jpg +0 -0
  302. mobile_agent/evals/vision/images/eyes_040.jpg +0 -0
  303. mobile_agent/evals/vision/images/eyes_041.jpg +0 -0
  304. mobile_agent/evals/vision/images/eyes_042.jpg +0 -0
  305. mobile_agent/evals/vision/images/eyes_043.jpg +0 -0
  306. mobile_agent/evals/vision/images/eyes_044.jpg +0 -0
  307. mobile_agent/evals/vision/images/eyes_046.jpg +0 -0
  308. mobile_agent/evals/vision/images/eyes_047.jpg +0 -0
  309. mobile_agent/evals/vision/images/eyes_048.jpg +0 -0
  310. mobile_agent/evals/vision/images/eyes_049.jpg +0 -0
  311. mobile_agent/evals/vision/images/eyes_051.jpg +0 -0
  312. mobile_agent/evals/vision/images/eyes_053.jpg +0 -0
  313. mobile_agent/evals/vision/images/eyes_054.jpg +0 -0
  314. mobile_agent/evals/vision/images/eyes_055.jpg +0 -0
  315. mobile_agent/evals/vision/images/eyes_058.jpg +0 -0
  316. mobile_agent/evals/vision/images/eyes_059.jpg +0 -0
  317. mobile_agent/evals/vision/images/receipt_002.jpg +0 -0
  318. mobile_agent/evals/vision/images/receipt_003.jpg +0 -0
  319. mobile_agent/evals/vision/images/receipt_004.jpg +0 -0
  320. mobile_agent/evals/vision/images/receipt_007.jpg +0 -0
  321. mobile_agent/evals/vision/images/receipt_009.jpg +0 -0
  322. mobile_agent/evals/vision/images/receipt_010.jpg +0 -0
  323. mobile_agent/evals/vision/images/receipt_011.jpg +0 -0
  324. mobile_agent/evals/vision/images/receipt_012.jpg +0 -0
  325. mobile_agent/evals/vision/images/receipt_013.jpg +0 -0
  326. mobile_agent/evals/vision/images/receipt_014.jpg +0 -0
  327. mobile_agent/evals/vision/images/receipt_016.jpg +0 -0
  328. mobile_agent/evals/vision/images/receipt_017.jpg +0 -0
  329. mobile_agent/evals/vision/images/receipt_023.jpg +0 -0
  330. mobile_agent/evals/vision/images/receipt_027.jpg +0 -0
  331. mobile_agent/evals/vision/images/receipt_029.jpg +0 -0
  332. mobile_agent/evals/vision/images/receipt_032.jpg +0 -0
  333. mobile_agent/evals/vision/images/receipt_033.jpg +0 -0
  334. mobile_agent/evals/vision/images/receipt_036.jpg +0 -0
  335. mobile_agent/evals/vision/images/receipt_041.jpg +0 -0
  336. mobile_agent/evals/vision/images/receipt_043.jpg +0 -0
  337. mobile_agent/evals/vision/images/receipt_047.jpg +0 -0
  338. mobile_agent/evals/vision/images/receipt_049.jpg +0 -0
  339. mobile_agent/evals/vision/images/receipt_051.jpg +0 -0
  340. mobile_agent/evals/vision/images/receipt_053.jpg +0 -0
  341. mobile_agent/evals/vision/images/receipt_056.jpg +0 -0
  342. mobile_agent/evals/vision/images/receipt_057.jpg +0 -0
  343. mobile_agent/evals/vision/images/receipt_058.jpg +0 -0
  344. mobile_agent/evals/vision/images/receipt_059.jpg +0 -0
  345. mobile_agent/evals/vision/images/receipt_060.jpg +0 -0
  346. mobile_agent/evals/vision/images/receipt_061.jpg +0 -0
  347. mobile_agent/evals/vision/images/receipt_062.jpg +0 -0
  348. mobile_agent/evals/vision/images/receipt_063.jpg +0 -0
  349. mobile_agent/evals/vision/images/receipt_064.jpg +0 -0
  350. mobile_agent/evals/vision/images/receipt_065.jpg +0 -0
  351. mobile_agent/evals/vision/images/receipt_066.jpg +0 -0
  352. mobile_agent/evals/vision/images/receipt_067.jpg +0 -0
  353. mobile_agent/evals/vision/images/receipt_068.jpg +0 -0
  354. mobile_agent/evals/vision/images/receipt_069.jpg +0 -0
  355. mobile_agent/evals/vision/images/receipt_070.jpg +0 -0
  356. mobile_agent/evals/vision/images/receipt_071.jpg +0 -0
  357. mobile_agent/evals/vision/images/sunglasses_000.jpg +0 -0
  358. mobile_agent/evals/vision/images/sunglasses_001.jpg +0 -0
  359. mobile_agent/evals/vision/images/sunglasses_002.jpg +0 -0
  360. mobile_agent/evals/vision/images/sunglasses_004.jpg +0 -0
  361. mobile_agent/evals/vision/images/sunglasses_005.jpg +0 -0
  362. mobile_agent/evals/vision/images/sunglasses_008.jpg +0 -0
  363. mobile_agent/evals/vision/images/sunglasses_009.jpg +0 -0
  364. mobile_agent/evals/vision/images/sunglasses_010.jpg +0 -0
  365. mobile_agent/evals/vision/images/sunglasses_012.jpg +0 -0
  366. mobile_agent/evals/vision/images/sunglasses_013.jpg +0 -0
  367. mobile_agent/evals/vision/images/sunglasses_015.jpg +0 -0
  368. mobile_agent/evals/vision/images/sunglasses_016.jpg +0 -0
  369. mobile_agent/evals/vision/images/sunglasses_017.jpg +0 -0
  370. mobile_agent/evals/vision/images/sunglasses_018.jpg +0 -0
  371. mobile_agent/evals/vision/images/sunglasses_021.jpg +0 -0
  372. mobile_agent/evals/vision/images/sunglasses_022.jpg +0 -0
  373. mobile_agent/evals/vision/images/sunglasses_023.jpg +0 -0
  374. mobile_agent/evals/vision/images/sunglasses_024.jpg +0 -0
  375. mobile_agent/evals/vision/images/sunglasses_030.jpg +0 -0
  376. mobile_agent/evals/vision/images/sunglasses_031.jpg +0 -0
  377. mobile_agent/evals/vision/images/sunglasses_032.jpg +0 -0
  378. mobile_agent/evals/vision/images/sunglasses_033.jpg +0 -0
  379. mobile_agent/evals/vision/images/sunglasses_034.jpg +0 -0
  380. mobile_agent/evals/vision/images/sunglasses_036.jpg +0 -0
  381. mobile_agent/evals/vision/images/sunglasses_038.jpg +0 -0
  382. mobile_agent/evals/vision/images/sunglasses_039.jpg +0 -0
  383. mobile_agent/evals/vision/images/sunglasses_040.jpg +0 -0
  384. mobile_agent/evals/vision/images/sunglasses_041.jpg +0 -0
  385. mobile_agent/evals/vision/manifest.json +2355 -0
  386. mobile_agent/eventbus.py +110 -0
  387. mobile_agent/explain.py +34 -0
  388. mobile_agent/failure_line.py +197 -0
  389. mobile_agent/fanout.py +663 -0
  390. mobile_agent/feedback/__init__.py +2 -0
  391. mobile_agent/feedback/bundle.py +204 -0
  392. mobile_agent/feedback/outbox.py +196 -0
  393. mobile_agent/feedback/register.py +9 -0
  394. mobile_agent/feedback/routes.py +177 -0
  395. mobile_agent/fleet.py +841 -0
  396. mobile_agent/fleet_health.py +729 -0
  397. mobile_agent/fleet_phone.py +186 -0
  398. mobile_agent/frontier/__init__.py +79 -0
  399. mobile_agent/frontier/act.py +380 -0
  400. mobile_agent/frontier/actions.py +70 -0
  401. mobile_agent/frontier/agent.py +256 -0
  402. mobile_agent/frontier/approvals.py +366 -0
  403. mobile_agent/frontier/hooks.py +199 -0
  404. mobile_agent/frontier/interrupts.py +125 -0
  405. mobile_agent/frontier/observe.py +331 -0
  406. mobile_agent/frontier/phases.py +381 -0
  407. mobile_agent/frontier/pipeline.py +61 -0
  408. mobile_agent/frontier/prompt.py +384 -0
  409. mobile_agent/frontier/protocol.py +91 -0
  410. mobile_agent/frontier/safety.py +467 -0
  411. mobile_agent/frontier/saved.py +375 -0
  412. mobile_agent/frontier/screen.py +419 -0
  413. mobile_agent/frontier/skills.py +147 -0
  414. mobile_agent/frontier/stages/__init__.py +1 -0
  415. mobile_agent/frontier/stages/after.py +192 -0
  416. mobile_agent/frontier/stages/approve.py +138 -0
  417. mobile_agent/frontier/stages/controls.py +115 -0
  418. mobile_agent/frontier/stages/ends.py +102 -0
  419. mobile_agent/frontier/stages/gate.py +92 -0
  420. mobile_agent/frontier/stages/guards.py +132 -0
  421. mobile_agent/frontier/stages/points.py +155 -0
  422. mobile_agent/frontier/stages/send.py +177 -0
  423. mobile_agent/frontier/turn.py +151 -0
  424. mobile_agent/full_disk_access.py +83 -0
  425. mobile_agent/gemini.py +69 -0
  426. mobile_agent/genuine.py +120 -0
  427. mobile_agent/graph/__init__.py +4 -0
  428. mobile_agent/graph/catalog.py +789 -0
  429. mobile_agent/graph/chain.py +306 -0
  430. mobile_agent/graph/dispatch.py +98 -0
  431. mobile_agent/graph/draft.py +373 -0
  432. mobile_agent/graph/edit.py +76 -0
  433. mobile_agent/graph/estimate.py +189 -0
  434. mobile_agent/graph/expr.py +562 -0
  435. mobile_agent/graph/functions.py +271 -0
  436. mobile_agent/graph/infer.py +144 -0
  437. mobile_agent/graph/nodes.py +936 -0
  438. mobile_agent/graph/phone_steps.py +178 -0
  439. mobile_agent/graph/rerun.py +327 -0
  440. mobile_agent/graph/run.py +899 -0
  441. mobile_agent/graph/schema.py +842 -0
  442. mobile_agent/graph/state.py +161 -0
  443. mobile_agent/graph/values.py +198 -0
  444. mobile_agent/harness/__init__.py +2 -0
  445. mobile_agent/harness/checkpoints.py +468 -0
  446. mobile_agent/harness/control.py +101 -0
  447. mobile_agent/harness/prewarm.py +213 -0
  448. mobile_agent/harness/register.py +43 -0
  449. mobile_agent/harness/routes.py +114 -0
  450. mobile_agent/harness/settings.py +66 -0
  451. mobile_agent/harness/tools/__init__.py +67 -0
  452. mobile_agent/harness/tools/ask_user.py +102 -0
  453. mobile_agent/harness/tools/watch.py +120 -0
  454. mobile_agent/harness_api.py +815 -0
  455. mobile_agent/hedge.py +203 -0
  456. mobile_agent/help_topics.py +305 -0
  457. mobile_agent/hints.py +248 -0
  458. mobile_agent/history.py +69 -0
  459. mobile_agent/home.py +214 -0
  460. mobile_agent/http_pool.py +175 -0
  461. mobile_agent/image_policy.py +139 -0
  462. mobile_agent/inbox.py +148 -0
  463. mobile_agent/installed_apps.py +344 -0
  464. mobile_agent/integrations/__init__.py +1 -0
  465. mobile_agent/integrations/cli.py +472 -0
  466. mobile_agent/integrations/client_files.py +65 -0
  467. mobile_agent/integrations/clients.py +998 -0
  468. mobile_agent/integrations/doctor.py +185 -0
  469. mobile_agent/integrations/jsonc.py +383 -0
  470. mobile_agent/integrations/routes.py +135 -0
  471. mobile_agent/integrations/rules.py +112 -0
  472. mobile_agent/integrations/server_options.py +46 -0
  473. mobile_agent/integrations/skill.py +152 -0
  474. mobile_agent/integrations/skill_files.py +10 -0
  475. mobile_agent/integrations/textedit.py +309 -0
  476. mobile_agent/intents/__init__.py +17 -0
  477. mobile_agent/intents/atlas.py +216 -0
  478. mobile_agent/intents/callback.py +44 -0
  479. mobile_agent/intents/cli.py +55 -0
  480. mobile_agent/intents/harvest.py +91 -0
  481. mobile_agent/intents/plan.py +93 -0
  482. mobile_agent/ios/__init__.py +1 -0
  483. mobile_agent/ios/guide.py +16 -0
  484. mobile_agent/ios/kb.py +212 -0
  485. mobile_agent/ios/kb_data.py +1588 -0
  486. mobile_agent/ios/mcp.py +44 -0
  487. mobile_agent/ios/register.py +8 -0
  488. mobile_agent/ios/system.py +369 -0
  489. mobile_agent/journal.py +531 -0
  490. mobile_agent/keychain_acl.py +374 -0
  491. mobile_agent/keys.py +395 -0
  492. mobile_agent/latency_overlap.py +36 -0
  493. mobile_agent/latency_trace.py +170 -0
  494. mobile_agent/learned/__init__.py +15 -0
  495. mobile_agent/learned/app_path.py +160 -0
  496. mobile_agent/learned/code_read.py +38 -0
  497. mobile_agent/learned/parts.py +546 -0
  498. mobile_agent/learned/programs.py +691 -0
  499. mobile_agent/learned/register.py +171 -0
  500. mobile_agent/learned/routes.py +61 -0
  501. mobile_agent/learned/taught.py +329 -0
  502. mobile_agent/learned/teach.py +538 -0
  503. mobile_agent/learned/workflows.py +647 -0
  504. mobile_agent/learned/zero.py +41 -0
  505. mobile_agent/links.py +37 -0
  506. mobile_agent/listen.py +607 -0
  507. mobile_agent/lock_spike.py +278 -0
  508. mobile_agent/lockscreen.py +526 -0
  509. mobile_agent/login.py +354 -0
  510. mobile_agent/longform/__init__.py +15 -0
  511. mobile_agent/longform/ads.py +191 -0
  512. mobile_agent/longform/blocks.py +117 -0
  513. mobile_agent/longform/budget.py +265 -0
  514. mobile_agent/longform/context.py +285 -0
  515. mobile_agent/longform/costs.py +93 -0
  516. mobile_agent/longform/digest.py +145 -0
  517. mobile_agent/longform/extract.py +508 -0
  518. mobile_agent/longform/feed.py +505 -0
  519. mobile_agent/longform/followup.py +168 -0
  520. mobile_agent/longform/frames.py +82 -0
  521. mobile_agent/longform/identity.py +87 -0
  522. mobile_agent/longform/inbox.py +221 -0
  523. mobile_agent/longform/items.py +425 -0
  524. mobile_agent/longform/models.py +115 -0
  525. mobile_agent/longform/notebook.py +329 -0
  526. mobile_agent/longform/plan.py +614 -0
  527. mobile_agent/longform/recall.py +195 -0
  528. mobile_agent/longform/receipts.py +81 -0
  529. mobile_agent/longform/register.py +124 -0
  530. mobile_agent/longform/report.py +343 -0
  531. mobile_agent/longform/routes.py +102 -0
  532. mobile_agent/longform/session.py +644 -0
  533. mobile_agent/longform/store.py +146 -0
  534. mobile_agent/longform/synthesis.py +347 -0
  535. mobile_agent/mac_tools.py +223 -0
  536. mobile_agent/manual_control.py +143 -0
  537. mobile_agent/maps.py +149 -0
  538. mobile_agent/marks.py +92 -0
  539. mobile_agent/mcp_server/__init__.py +3 -0
  540. mobile_agent/mcp_server/__main__.py +29 -0
  541. mobile_agent/mcp_server/action_journal.py +24 -0
  542. mobile_agent/mcp_server/action_tools.py +638 -0
  543. mobile_agent/mcp_server/approvals.py +219 -0
  544. mobile_agent/mcp_server/catalog.py +92 -0
  545. mobile_agent/mcp_server/cli.py +194 -0
  546. mobile_agent/mcp_server/codes.py +147 -0
  547. mobile_agent/mcp_server/device_tools.py +306 -0
  548. mobile_agent/mcp_server/direct.py +272 -0
  549. mobile_agent/mcp_server/expo_tools.py +105 -0
  550. mobile_agent/mcp_server/phone_tools.py +510 -0
  551. mobile_agent/mcp_server/protocol.py +364 -0
  552. mobile_agent/mcp_server/registry.py +55 -0
  553. mobile_agent/mcp_server/run_tools.py +531 -0
  554. mobile_agent/mcp_server/schemas.py +425 -0
  555. mobile_agent/mcp_server/screen_tools.py +168 -0
  556. mobile_agent/mcp_server/session.py +469 -0
  557. mobile_agent/mcp_server/support.py +145 -0
  558. mobile_agent/mcp_server/tools.py +493 -0
  559. mobile_agent/memory/__init__.py +9 -0
  560. mobile_agent/memory/cli.py +207 -0
  561. mobile_agent/memory/extract.py +191 -0
  562. mobile_agent/memory/listener.py +100 -0
  563. mobile_agent/memory/model.py +144 -0
  564. mobile_agent/memory/project.py +96 -0
  565. mobile_agent/memory/provider.py +127 -0
  566. mobile_agent/memory/register.py +21 -0
  567. mobile_agent/memory/retrieval.py +172 -0
  568. mobile_agent/memory/routes.py +216 -0
  569. mobile_agent/memory/runs.py +124 -0
  570. mobile_agent/memory/store.py +806 -0
  571. mobile_agent/memory/writer.py +278 -0
  572. mobile_agent/mirroring.py +585 -0
  573. mobile_agent/mirroring_runtime.py +221 -0
  574. mobile_agent/model_clients.py +604 -0
  575. mobile_agent/narrate.py +813 -0
  576. mobile_agent/native_helpers.py +84 -0
  577. mobile_agent/netxray/__init__.py +1 -0
  578. mobile_agent/netxray/analyze.py +322 -0
  579. mobile_agent/netxray/attribution.py +62 -0
  580. mobile_agent/netxray/capture.py +88 -0
  581. mobile_agent/netxray/cli.py +142 -0
  582. mobile_agent/netxray/job.py +219 -0
  583. mobile_agent/netxray/mobster-pcap.c +560 -0
  584. mobile_agent/netxray/packets.py +410 -0
  585. mobile_agent/netxray/pcapng.py +223 -0
  586. mobile_agent/netxray/register.py +93 -0
  587. mobile_agent/netxray/report.py +214 -0
  588. mobile_agent/netxray/store.py +48 -0
  589. mobile_agent/netxray/summary.py +157 -0
  590. mobile_agent/netxray/target.py +67 -0
  591. mobile_agent/netxray/tool.py +66 -0
  592. mobile_agent/netxray/trackers.py +194 -0
  593. mobile_agent/notify_triggers/__init__.py +1 -0
  594. mobile_agent/notify_triggers/rules.py +39 -0
  595. mobile_agent/notify_triggers/sources.py +315 -0
  596. mobile_agent/notify_triggers/watch.py +135 -0
  597. mobile_agent/offers.py +86 -0
  598. mobile_agent/orchestrate.py +215 -0
  599. mobile_agent/outlets/__init__.py +1 -0
  600. mobile_agent/outlets/connect.py +249 -0
  601. mobile_agent/outlets/folder.py +180 -0
  602. mobile_agent/outlets/hooks.py +453 -0
  603. mobile_agent/outlets/inbound.py +472 -0
  604. mobile_agent/outlets/inbound_reply.py +57 -0
  605. mobile_agent/outlets/inbound_routes.py +81 -0
  606. mobile_agent/outlets/kinds.py +308 -0
  607. mobile_agent/outlets/mail.py +110 -0
  608. mobile_agent/outlets/net.py +86 -0
  609. mobile_agent/outlets/notion.py +244 -0
  610. mobile_agent/outlets/register.py +56 -0
  611. mobile_agent/outlets/routes.py +135 -0
  612. mobile_agent/outlets/service.py +580 -0
  613. mobile_agent/outlets/sheets.py +169 -0
  614. mobile_agent/outlets/slack.py +177 -0
  615. mobile_agent/outlets/store.py +194 -0
  616. mobile_agent/outlets/vault.py +80 -0
  617. mobile_agent/outlets/webhook.py +316 -0
  618. mobile_agent/output_contract.py +141 -0
  619. mobile_agent/owner_presence.py +211 -0
  620. mobile_agent/ownership.py +219 -0
  621. mobile_agent/packs/__init__.py +1 -0
  622. mobile_agent/packs/calendar.py +243 -0
  623. mobile_agent/packs/context.py +68 -0
  624. mobile_agent/packs/core.py +757 -0
  625. mobile_agent/packs/front_door.py +316 -0
  626. mobile_agent/packs/heal.py +214 -0
  627. mobile_agent/packs/health.py +122 -0
  628. mobile_agent/packs/insights.py +76 -0
  629. mobile_agent/packs/instagram.py +640 -0
  630. mobile_agent/packs/linkedin.py +106 -0
  631. mobile_agent/packs/maps.py +196 -0
  632. mobile_agent/packs/mcp.py +149 -0
  633. mobile_agent/packs/messages.py +270 -0
  634. mobile_agent/packs/notes.py +226 -0
  635. mobile_agent/packs/photos.py +166 -0
  636. mobile_agent/packs/pins.py +47 -0
  637. mobile_agent/packs/reddit.py +110 -0
  638. mobile_agent/packs/register.py +276 -0
  639. mobile_agent/packs/rows.py +108 -0
  640. mobile_agent/packs/safari.py +143 -0
  641. mobile_agent/packs/settings.py +253 -0
  642. mobile_agent/packs/social.py +300 -0
  643. mobile_agent/packs/threads.py +97 -0
  644. mobile_agent/packs/tiktok.py +455 -0
  645. mobile_agent/packs/tools.py +290 -0
  646. mobile_agent/packs/x.py +127 -0
  647. mobile_agent/packs/youtube.py +199 -0
  648. mobile_agent/parity.py +270 -0
  649. mobile_agent/paths.py +99 -0
  650. mobile_agent/perception.py +470 -0
  651. mobile_agent/permissions/__init__.py +32 -0
  652. mobile_agent/permissions/consequence.py +535 -0
  653. mobile_agent/permissions/exceptions.py +99 -0
  654. mobile_agent/permissions/gate.py +316 -0
  655. mobile_agent/permissions/outside.py +316 -0
  656. mobile_agent/permissions/policy.py +288 -0
  657. mobile_agent/permissions/project_policy.py +411 -0
  658. mobile_agent/permissions/register.py +148 -0
  659. mobile_agent/permissions/rules.py +291 -0
  660. mobile_agent/phone_io/__init__.py +146 -0
  661. mobile_agent/phone_io/cli.py +427 -0
  662. mobile_agent/phone_io/clipboard.py +78 -0
  663. mobile_agent/phone_io/controls.py +78 -0
  664. mobile_agent/phone_io/files.py +289 -0
  665. mobile_agent/phone_io/handoff.py +123 -0
  666. mobile_agent/phone_io/icloud.py +118 -0
  667. mobile_agent/phone_io/install.py +188 -0
  668. mobile_agent/phone_io/network_capture.py +188 -0
  669. mobile_agent/phone_io/network_cli.py +138 -0
  670. mobile_agent/phone_io/network_profiles.py +138 -0
  671. mobile_agent/phone_io/photos.py +512 -0
  672. mobile_agent/phone_io/recording.py +115 -0
  673. mobile_agent/phone_io/secrets.py +146 -0
  674. mobile_agent/phone_io/transfer.py +895 -0
  675. mobile_agent/phone_io/tunnel.py +129 -0
  676. mobile_agent/phone_ready.py +91 -0
  677. mobile_agent/pinning.py +158 -0
  678. mobile_agent/plan_gate.py +807 -0
  679. mobile_agent/plugins/__init__.py +3 -0
  680. mobile_agent/plugins/install.py +163 -0
  681. mobile_agent/plugins/manifest.py +242 -0
  682. mobile_agent/plugins/register.py +15 -0
  683. mobile_agent/plugins/routes.py +82 -0
  684. mobile_agent/plugins/service.py +322 -0
  685. mobile_agent/plugins/skills.py +76 -0
  686. mobile_agent/plugins/store.py +79 -0
  687. mobile_agent/pool.py +285 -0
  688. mobile_agent/power_watch.py +275 -0
  689. mobile_agent/premium_model.py +22 -0
  690. mobile_agent/project_env.py +291 -0
  691. mobile_agent/project_env_cli.py +74 -0
  692. mobile_agent/project_env_routes.py +55 -0
  693. mobile_agent/projects/__init__.py +27 -0
  694. mobile_agent/projects/ci.py +332 -0
  695. mobile_agent/projects/clone.py +339 -0
  696. mobile_agent/projects/drafts.py +158 -0
  697. mobile_agent/projects/folder.py +436 -0
  698. mobile_agent/projects/github.py +131 -0
  699. mobile_agent/projects/register.py +31 -0
  700. mobile_agent/projects/revisions.py +253 -0
  701. mobile_agent/projects/routes.py +234 -0
  702. mobile_agent/projects/runs.py +297 -0
  703. mobile_agent/projects/service.py +695 -0
  704. mobile_agent/projects/setup.py +84 -0
  705. mobile_agent/projects/store.py +379 -0
  706. mobile_agent/projects/sync.py +380 -0
  707. mobile_agent/projects/workflow_files.py +270 -0
  708. mobile_agent/replay_core/__init__.py +26 -0
  709. mobile_agent/replay_core/checks.py +594 -0
  710. mobile_agent/replay_core/engine.py +596 -0
  711. mobile_agent/replay_core/heal.py +364 -0
  712. mobile_agent/replay_core/path.py +240 -0
  713. mobile_agent/replay_core/recorder.py +206 -0
  714. mobile_agent/replay_core/store.py +171 -0
  715. mobile_agent/result_blocks.py +941 -0
  716. mobile_agent/run.py +642 -0
  717. mobile_agent/run_delivery.py +123 -0
  718. mobile_agent/run_executor.py +200 -0
  719. mobile_agent/run_export.py +284 -0
  720. mobile_agent/run_gif.py +891 -0
  721. mobile_agent/runner_hints.py +86 -0
  722. mobile_agent/runner_renewal.py +205 -0
  723. mobile_agent/runtime_create.py +549 -0
  724. mobile_agent/runtime_driver.py +458 -0
  725. mobile_agent/runtime_limits.py +319 -0
  726. mobile_agent/runtime_queue.py +394 -0
  727. mobile_agent/runtime_runs.py +383 -0
  728. mobile_agent/runtime_status.py +181 -0
  729. mobile_agent/runtime_teach.py +295 -0
  730. mobile_agent/runtime_work.py +599 -0
  731. mobile_agent/screen_text.py +101 -0
  732. mobile_agent/secret_filter.py +113 -0
  733. mobile_agent/serve.py +160 -0
  734. mobile_agent/server.py +363 -0
  735. mobile_agent/service_support.py +42 -0
  736. mobile_agent/settle/__init__.py +1 -0
  737. mobile_agent/settle/predict.py +102 -0
  738. mobile_agent/settle/replay.py +92 -0
  739. mobile_agent/settle/store.py +93 -0
  740. mobile_agent/settle/stream.py +43 -0
  741. mobile_agent/setup_cli.py +225 -0
  742. mobile_agent/setup_errors.py +87 -0
  743. mobile_agent/setup_service.py +527 -0
  744. mobile_agent/share.py +104 -0
  745. mobile_agent/signing.py +184 -0
  746. mobile_agent/sim/__init__.py +6 -0
  747. mobile_agent/sim/__main__.py +26 -0
  748. mobile_agent/sim/api.py +952 -0
  749. mobile_agent/sim/apps.py +172 -0
  750. mobile_agent/sim/biometrics.py +84 -0
  751. mobile_agent/sim/cli.py +271 -0
  752. mobile_agent/sim/doctor.py +293 -0
  753. mobile_agent/sim/governor.py +90 -0
  754. mobile_agent/sim/holders.py +88 -0
  755. mobile_agent/sim/models.py +62 -0
  756. mobile_agent/sim/registry.py +161 -0
  757. mobile_agent/sim/simctl.py +269 -0
  758. mobile_agent/sim/wda.py +618 -0
  759. mobile_agent/sim/xcode.py +74 -0
  760. mobile_agent/skills/__init__.py +12 -0
  761. mobile_agent/skills/codes.py +597 -0
  762. mobile_agent/skills/notifications.py +281 -0
  763. mobile_agent/skills/open_url.py +127 -0
  764. mobile_agent/smart_events.py +588 -0
  765. mobile_agent/smart_result.py +490 -0
  766. mobile_agent/smart_run.py +413 -0
  767. mobile_agent/state.py +662 -0
  768. mobile_agent/states.py +217 -0
  769. mobile_agent/steering.py +70 -0
  770. mobile_agent/style.py +178 -0
  771. mobile_agent/supervised.py +234 -0
  772. mobile_agent/swift/helper_bt_hid.swift +534 -0
  773. mobile_agent/swift/helper_doc_text.swift +184 -0
  774. mobile_agent/swift/helper_draft_reply.swift +170 -0
  775. mobile_agent/swift/helper_map.swift +110 -0
  776. mobile_agent/swift/helper_mirroring.swift +1099 -0
  777. mobile_agent/swift/helper_mjpeg_mp4.swift +103 -0
  778. mobile_agent/swift/helper_pdf.swift +100 -0
  779. mobile_agent/swift/helper_phone_audio.swift +993 -0
  780. mobile_agent/swift/helper_phone_screen.swift +416 -0
  781. mobile_agent/swift/helper_screen_text.swift +24 -0
  782. mobile_agent/swift/helper_transfer.swift +246 -0
  783. mobile_agent/swift/ocr.swift +22 -0
  784. mobile_agent/task_policy.py +337 -0
  785. mobile_agent/team_signing/__init__.py +34 -0
  786. mobile_agent/team_signing/apple_ca.py +37 -0
  787. mobile_agent/team_signing/bundle.py +136 -0
  788. mobile_agent/team_signing/cli.py +208 -0
  789. mobile_agent/team_signing/identity.py +248 -0
  790. mobile_agent/team_signing/ledger.py +105 -0
  791. mobile_agent/team_signing/prebuilt.py +61 -0
  792. mobile_agent/team_signing/resign.py +256 -0
  793. mobile_agent/team_signing/service.py +382 -0
  794. mobile_agent/team_signing/tooling.py +243 -0
  795. mobile_agent/team_signing/writes.py +268 -0
  796. mobile_agent/terminal_image.py +81 -0
  797. mobile_agent/testkit/__init__.py +1 -0
  798. mobile_agent/testkit/a11y_baseline.py +132 -0
  799. mobile_agent/testkit/api.py +403 -0
  800. mobile_agent/testkit/capsule.py +400 -0
  801. mobile_agent/testkit/capsule_cli.py +57 -0
  802. mobile_agent/testkit/cli.py +948 -0
  803. mobile_agent/testkit/cli_arguments.py +193 -0
  804. mobile_agent/testkit/comparison.py +288 -0
  805. mobile_agent/testkit/comparison_cli.py +41 -0
  806. mobile_agent/testkit/discover.py +257 -0
  807. mobile_agent/testkit/editor.py +243 -0
  808. mobile_agent/testkit/execute.py +515 -0
  809. mobile_agent/testkit/github_checks.py +248 -0
  810. mobile_agent/testkit/gitref.py +66 -0
  811. mobile_agent/testkit/heal_review.py +176 -0
  812. mobile_agent/testkit/health.py +480 -0
  813. mobile_agent/testkit/history.py +255 -0
  814. mobile_agent/testkit/hosted_results.py +321 -0
  815. mobile_agent/testkit/input_files.py +173 -0
  816. mobile_agent/testkit/jobs.py +285 -0
  817. mobile_agent/testkit/junit_verdict.py +33 -0
  818. mobile_agent/testkit/maestro.py +802 -0
  819. mobile_agent/testkit/mcp.py +562 -0
  820. mobile_agent/testkit/mcp_comparison.py +88 -0
  821. mobile_agent/testkit/minimize.py +471 -0
  822. mobile_agent/testkit/minimizer_cli.py +48 -0
  823. mobile_agent/testkit/notify.py +212 -0
  824. mobile_agent/testkit/phone_video.py +191 -0
  825. mobile_agent/testkit/pr.py +491 -0
  826. mobile_agent/testkit/pr_report.py +562 -0
  827. mobile_agent/testkit/prbuild.py +148 -0
  828. mobile_agent/testkit/proof_report.py +55 -0
  829. mobile_agent/testkit/provenance.py +119 -0
  830. mobile_agent/testkit/prrun.py +244 -0
  831. mobile_agent/testkit/recent.py +166 -0
  832. mobile_agent/testkit/recipes.py +146 -0
  833. mobile_agent/testkit/record.py +204 -0
  834. mobile_agent/testkit/register.py +288 -0
  835. mobile_agent/testkit/reports.py +998 -0
  836. mobile_agent/testkit/repro.py +432 -0
  837. mobile_agent/testkit/scenario.py +440 -0
  838. mobile_agent/testkit/scenario_cli.py +37 -0
  839. mobile_agent/testkit/snapshots_cli.py +60 -0
  840. mobile_agent/testkit/suite.py +670 -0
  841. mobile_agent/testkit/targets.py +497 -0
  842. mobile_agent/testkit/xctest.py +378 -0
  843. mobile_agent/threads/__init__.py +1 -0
  844. mobile_agent/threads/chat_once.py +264 -0
  845. mobile_agent/threads/cli.py +740 -0
  846. mobile_agent/threads/client.py +326 -0
  847. mobile_agent/threads/context.py +246 -0
  848. mobile_agent/threads/mcp.py +739 -0
  849. mobile_agent/threads/recent.py +34 -0
  850. mobile_agent/threads/register.py +54 -0
  851. mobile_agent/threads/routes.py +188 -0
  852. mobile_agent/threads/service.py +975 -0
  853. mobile_agent/threads/store.py +361 -0
  854. mobile_agent/threads/summary.py +117 -0
  855. mobile_agent/threads/term.py +101 -0
  856. mobile_agent/threads/workflow_cli.py +104 -0
  857. mobile_agent/tls.py +36 -0
  858. mobile_agent/traces.py +271 -0
  859. mobile_agent/tracks.py +235 -0
  860. mobile_agent/transport.py +271 -0
  861. mobile_agent/triggers/__init__.py +1 -0
  862. mobile_agent/triggers/actions.py +204 -0
  863. mobile_agent/triggers/model.py +146 -0
  864. mobile_agent/triggers/register.py +29 -0
  865. mobile_agent/triggers/routes.py +101 -0
  866. mobile_agent/triggers/service.py +387 -0
  867. mobile_agent/triggers/store.py +142 -0
  868. mobile_agent/tui/__init__.py +89 -0
  869. mobile_agent/tui/app.py +582 -0
  870. mobile_agent/tui/chrome.py +321 -0
  871. mobile_agent/tui/commands.py +375 -0
  872. mobile_agent/tui/demo_phone.py +369 -0
  873. mobile_agent/tui/images.py +29 -0
  874. mobile_agent/tui/keys.py +64 -0
  875. mobile_agent/tui/prompt.py +195 -0
  876. mobile_agent/tui/render.py +395 -0
  877. mobile_agent/tui/screens.py +180 -0
  878. mobile_agent/tui/session.py +308 -0
  879. mobile_agent/tui/tasks.py +287 -0
  880. mobile_agent/tui/theme.py +118 -0
  881. mobile_agent/tui/widgets.py +272 -0
  882. mobile_agent/update_check.py +213 -0
  883. mobile_agent/usage.py +184 -0
  884. mobile_agent/verify/__init__.py +0 -0
  885. mobile_agent/verify/__main__.py +41 -0
  886. mobile_agent/verify/a11y.py +189 -0
  887. mobile_agent/verify/actions.py +290 -0
  888. mobile_agent/verify/assertions.py +570 -0
  889. mobile_agent/verify/audit.py +312 -0
  890. mobile_agent/verify/business.py +221 -0
  891. mobile_agent/verify/business_run.py +131 -0
  892. mobile_agent/verify/business_state.py +168 -0
  893. mobile_agent/verify/check_schema.py +398 -0
  894. mobile_agent/verify/checks.py +614 -0
  895. mobile_agent/verify/cli.py +423 -0
  896. mobile_agent/verify/conditions.py +483 -0
  897. mobile_agent/verify/crashes.py +321 -0
  898. mobile_agent/verify/device_audit.py +246 -0
  899. mobile_agent/verify/dialogs.py +287 -0
  900. mobile_agent/verify/evidence.py +705 -0
  901. mobile_agent/verify/fixtures.py +305 -0
  902. mobile_agent/verify/flow.py +213 -0
  903. mobile_agent/verify/l10n.py +141 -0
  904. mobile_agent/verify/netcerts.py +67 -0
  905. mobile_agent/verify/netproxy.py +356 -0
  906. mobile_agent/verify/netrun.py +81 -0
  907. mobile_agent/verify/netshim.m +28 -0
  908. mobile_agent/verify/netshim.py +48 -0
  909. mobile_agent/verify/netstubs.py +132 -0
  910. mobile_agent/verify/overlay.py +174 -0
  911. mobile_agent/verify/perf.py +239 -0
  912. mobile_agent/verify/proof.py +192 -0
  913. mobile_agent/verify/replay_admission.py +48 -0
  914. mobile_agent/verify/report.py +952 -0
  915. mobile_agent/verify/run_cleanup.py +67 -0
  916. mobile_agent/verify/run_links.py +48 -0
  917. mobile_agent/verify/run_perf.py +44 -0
  918. mobile_agent/verify/run_secrets.py +39 -0
  919. mobile_agent/verify/runner.py +976 -0
  920. mobile_agent/verify/runs.py +57 -0
  921. mobile_agent/verify/screen_coverage.py +181 -0
  922. mobile_agent/verify/smart.py +106 -0
  923. mobile_agent/verify/snapshot_run.py +114 -0
  924. mobile_agent/verify/snapshots.py +306 -0
  925. mobile_agent/verify/sound.py +335 -0
  926. mobile_agent/verify/source_observation.py +130 -0
  927. mobile_agent/verify/system_surfaces/__init__.py +7 -0
  928. mobile_agent/verify/system_surfaces/play.py +106 -0
  929. mobile_agent/verify/system_surfaces/spec.py +139 -0
  930. mobile_agent/verify/system_surfaces/stage.py +138 -0
  931. mobile_agent/verify/system_surfaces/surfaces.py +121 -0
  932. mobile_agent/verify/tree.py +223 -0
  933. mobile_agent/verify/verdict.py +377 -0
  934. mobile_agent/verify/wire.py +392 -0
  935. mobile_agent/verify/wirestore.py +176 -0
  936. mobile_agent/waiting/__init__.py +1 -0
  937. mobile_agent/waiting/contacts.py +73 -0
  938. mobile_agent/waiting/drafts.py +136 -0
  939. mobile_agent/waiting/job.py +145 -0
  940. mobile_agent/waiting/messages.py +174 -0
  941. mobile_agent/waiting/rank.py +115 -0
  942. mobile_agent/waiting/register.py +26 -0
  943. mobile_agent/watch.py +502 -0
  944. mobile_agent/wda/__init__.py +2 -0
  945. mobile_agent/wda/frame_clock.py +646 -0
  946. mobile_agent/wda/frame_source.py +325 -0
  947. mobile_agent/wda/frame_store.py +115 -0
  948. mobile_agent/wda/gate.py +163 -0
  949. mobile_agent/wda/gestures.py +291 -0
  950. mobile_agent/wda/guards.py +94 -0
  951. mobile_agent/wda/identity.py +55 -0
  952. mobile_agent/wda/observe.py +884 -0
  953. mobile_agent/wda/patches.py +856 -0
  954. mobile_agent/wda/point_gestures.py +150 -0
  955. mobile_agent/wda/source.py +32 -0
  956. mobile_agent/wda/system_dialogs.py +571 -0
  957. mobile_agent/wda/text.py +694 -0
  958. mobile_agent/wda/timeouts.py +77 -0
  959. mobile_agent/wda/video.py +505 -0
  960. mobile_agent/wda/watchdog.py +243 -0
  961. mobile_agent/wireless/__init__.py +15 -0
  962. mobile_agent/wireless/api.py +354 -0
  963. mobile_agent/wireless/cli.py +287 -0
  964. mobile_agent/wireless/devicectl.py +229 -0
  965. mobile_agent/wireless/exposure.py +33 -0
  966. mobile_agent/wireless/mobster-wifi-pair.c +122 -0
  967. mobile_agent/wireless/monitor.py +332 -0
  968. mobile_agent/wireless/network.py +334 -0
  969. mobile_agent/wireless/onboarding.py +293 -0
  970. mobile_agent/wireless/pair.py +124 -0
  971. mobile_agent/wireless/probe.py +322 -0
  972. mobile_agent/wireless/register.py +8 -0
  973. mobile_agent/wireless/relay.py +179 -0
  974. mobile_agent/wireless/result.py +87 -0
  975. mobile_agent/wireless/store.py +85 -0
  976. mobile_agent/wireless/switch.py +77 -0
  977. mobile_agent/wireless/transport.py +118 -0
  978. mobile_agent/wireless/words.py +81 -0
  979. mobile_agent/wireless/xctestrun.py +83 -0
  980. mobile_agent/workflow_tools.py +59 -0
  981. mobile_agent/workflows/__init__.py +2 -0
  982. mobile_agent/workflows/brief.py +201 -0
  983. mobile_agent/workflows/cron.py +110 -0
  984. mobile_agent/workflows/health.py +257 -0
  985. mobile_agent/workflows/history.py +126 -0
  986. mobile_agent/workflows/records.py +442 -0
  987. mobile_agent/workflows/runs.py +1000 -0
  988. mobile_agent/workflows/scheduler.py +134 -0
  989. mobile_agent/workflows/service.py +973 -0
  990. mobile_agent/workflows/starters.py +285 -0
  991. mobile_agent/workflows/triggers.py +248 -0
  992. mobile_agent/xray/__init__.py +1 -0
  993. mobile_agent/xray/app_cli.py +116 -0
  994. mobile_agent/xray/cli.py +21 -0
  995. mobile_agent/xray/crawl.py +754 -0
  996. mobile_agent/xray/diff.py +171 -0
  997. mobile_agent/xray/diff_cli.py +43 -0
  998. mobile_agent/xray/job.py +184 -0
  999. mobile_agent/xray/paywall.py +188 -0
  1000. mobile_agent/xray/policy.py +305 -0
  1001. mobile_agent/xray/register.py +43 -0
  1002. mobile_agent/xray/report.py +283 -0
  1003. mobile_agent/xray/screens.py +117 -0
  1004. mobster_cli-0.4.3.dist-info/METADATA +93 -0
  1005. mobster_cli-0.4.3.dist-info/RECORD +1009 -0
  1006. mobster_cli-0.4.3.dist-info/WHEEL +5 -0
  1007. mobster_cli-0.4.3.dist-info/entry_points.txt +3 -0
  1008. mobster_cli-0.4.3.dist-info/licenses/LICENSE +21 -0
  1009. mobster_cli-0.4.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,287 @@
1
+ """`mobster wifi`: use an iPhone without the cable, over the encrypted Wi-Fi link."""
2
+
3
+ import json
4
+ import sys
5
+
6
+ from . import GATE, store, switch, transport_on, words
7
+
8
+ DONE, FAILED, USAGE, MISSING_EXIT = 0, 1, 2, 3
9
+
10
+
11
+ def add_arguments(parser, helpers):
12
+ subs = parser.add_subparsers(dest="wifi_command", metavar="<subcommand>")
13
+ subs.required = True
14
+
15
+ def device(sub):
16
+ sub.add_argument("--device", metavar="NAME",
17
+ help="the iPhone: an id, UDID or name from `mobster devices` (default: your iPhone)")
18
+
19
+ def as_json(sub):
20
+ sub.add_argument("--json", action="store_true", help="print one JSON object")
21
+
22
+ status = subs.add_parser("status", help="show how each iPhone is connected, and whether Wi-Fi is on for it",
23
+ description="Show each iPhone set up here: on the cable, on Wi-Fi, or not reachable, "
24
+ "and whether Wi-Fi is on for it. Reads only; changes nothing.")
25
+ as_json(status)
26
+ on = subs.add_parser("on", help="let an iPhone be used over Wi-Fi (plug it in first)",
27
+ description="Let Mobster reach an iPhone over Wi-Fi when it's unplugged, through the "
28
+ "encrypted link between this Mac and the phone. The iPhone must be plugged in "
29
+ "and trust this Mac; this is the same switch as Connect via network in Xcode.",
30
+ epilog="exit codes: 0 on, 1 it couldn't be turned on, 2 usage, 3 no such iPhone")
31
+ device(on)
32
+ as_json(on)
33
+ off = subs.add_parser("off", help="use an iPhone only with the cable again",
34
+ description="Stop using an iPhone over Wi-Fi. When Mobster turned on the iPhone's own "
35
+ "switch (Connect via network), it turns that off too, over the cable. When it "
36
+ "can't, or Xcode turned the switch on, it says what to do.",
37
+ epilog="exit codes: 0 off, 1 it failed or the iPhone's own switch is still on (plug it in "
38
+ "and run this again), 2 usage, 3 no such iPhone")
39
+ device(off)
40
+ as_json(off)
41
+ probe = subs.add_parser("probe", help="check the encrypted Wi-Fi link to an unplugged iPhone, step by step",
42
+ description="Check an unplugged iPhone over Wi-Fi: what Xcode's device service reports, "
43
+ "the encrypted tunnel, WebDriverAgent on the tunnel through this Mac's "
44
+ "relay, and that the phone's Wi-Fi address does not answer on 8100 or 9100. "
45
+ "It binds only 127.0.0.1 and changes nothing on the phone (with --start, it "
46
+ "launches Mobster's helper on it for the probe, then stops it).",
47
+ epilog="exit codes: 0 every check passed, 1 a check failed, 2 usage, 3 couldn't run")
48
+ device(probe)
49
+ probe.add_argument("--start", action="store_true",
50
+ help="launch Mobster's helper over Wi-Fi for the probe when it isn't running (quit the Mac "
51
+ "app first)")
52
+ as_json(probe)
53
+
54
+
55
+ def run(args):
56
+ as_json = bool(getattr(args, "json", False))
57
+ try:
58
+ command = args.wifi_command
59
+ if command == "status":
60
+ return _status(as_json)
61
+ if command == "probe":
62
+ return _probe(args, as_json)
63
+ return _switch(args, command == "on", as_json)
64
+ except KeyboardInterrupt:
65
+ return 130
66
+ except _Stop as stop:
67
+ return _fail(stop, as_json)
68
+
69
+
70
+ class _Stop(Exception):
71
+ def __init__(self, code, message, fix="", exit_code=FAILED):
72
+ super().__init__(message)
73
+ self.code, self.message, self.fix, self.exit_code = code, message, fix, exit_code
74
+
75
+
76
+ def _problem(code, name, exit_code=FAILED):
77
+ problem = words.problem(code, name)
78
+ return _Stop(code, problem["message"], problem["fix"], exit_code)
79
+
80
+
81
+ def _fail(stop, as_json):
82
+ if as_json:
83
+ _print({"ok": False, "error": stop.message, "code": stop.code, **({"fix": stop.fix} if stop.fix else {})})
84
+ else:
85
+ print(stop.message, file=sys.stderr, flush=True)
86
+ if stop.fix:
87
+ print(stop.fix, file=sys.stderr, flush=True)
88
+ return stop.exit_code
89
+
90
+
91
+ def _print(value):
92
+ try:
93
+ print(json.dumps(value, ensure_ascii=False), flush=True)
94
+ except BrokenPipeError:
95
+ pass
96
+
97
+
98
+ def _data_dir():
99
+ from ..paths import user_data_dir
100
+ return user_data_dir()
101
+
102
+
103
+ def _phones(data_dir):
104
+ """The USB iPhones set up here, primary first (devices.discover without probing)."""
105
+ from .. import devices
106
+ return [item for item in devices.discover(data_dir, sims=False, probe=False)
107
+ if item["kind"] == "usb" and item.get("wdaUrl")]
108
+
109
+
110
+ def _choose(data_dir, name):
111
+ from .. import devices
112
+ records = devices.discover(data_dir, sims=False, probe=False)
113
+ if name:
114
+ try:
115
+ item = devices.resolve(records, name)
116
+ except LookupError as error:
117
+ raise _Stop("no_device", str(error), "", MISSING_EXIT) from None
118
+ else:
119
+ primary = [item for item in records if item.get("primary")]
120
+ usb = [item for item in records if item["kind"] == "usb"]
121
+ item = primary[0] if len(primary) == 1 else usb[0] if len(usb) == 1 else None
122
+ if item is None:
123
+ if not usb:
124
+ raise _Stop("no_device", "Mobster sees no iPhone set up here.",
125
+ "Plug one in and set it up in the Mobster app first.", MISSING_EXIT)
126
+ raise _Stop("usage", "Several iPhones are here: " + ", ".join(f"{r['name']} ({r['id']})" for r in usb[:6])
127
+ + ".", "Name one with --device.", USAGE)
128
+ if item["kind"] != "usb":
129
+ raise _Stop("usage", f"{item['name']} isn't an iPhone on a cable. Wi-Fi is for iPhones set up with a cable.",
130
+ "", USAGE)
131
+ return item
132
+
133
+
134
+ def _listing(data_dir, udid):
135
+ """The phone's USB listing entry (trusted or not), or None when it isn't plugged in."""
136
+ from ..device_manager import DeviceManager
137
+ try:
138
+ devices = DeviceManager(data_dir).devices()
139
+ except Exception:
140
+ devices = []
141
+ return next((item for item in devices if item.get("udid", "").upper() == udid.upper()), None)
142
+
143
+
144
+ def _connection(item, enabled, peers):
145
+ """The words for how a phone is connected now, read from this Mac (the app's relay answering on its port)."""
146
+ from .. import devices
147
+ from ..mac_tools import usb_attached
148
+ if usb_attached(item["udid"]):
149
+ return "usb", words.ON_CABLE
150
+ if not enabled:
151
+ return None, words.OFF
152
+ if item.get("wdaUrl") and devices.wda_answers(item["wdaUrl"]):
153
+ return "wifi", words.ON_WIFI
154
+ peer = (peers or {}).get(item["udid"].upper())
155
+ if peer is not None and peer.network:
156
+ return None, f"{words.UNREACHABLE} yet (the encrypted link is {peer.tunnel})"
157
+ return None, words.UNREACHABLE
158
+
159
+
160
+ def _status(as_json):
161
+ from . import devicectl
162
+ data_dir = _data_dir()
163
+ phones = _phones(data_dir)
164
+ settings = {item["udid"]: store.get(data_dir, item["udid"]) for item in phones}
165
+ peers = None
166
+ if any(value["enabled"] for value in settings.values()):
167
+ try:
168
+ peers = devicectl.list_devices()
169
+ except (devicectl.DevicectlError, RuntimeError):
170
+ peers = None
171
+ rows = []
172
+ for item in phones:
173
+ value = settings[item["udid"]]
174
+ now, text = _connection(item, value["enabled"], peers)
175
+ peer = (peers or {}).get(item["udid"].upper())
176
+ rows.append({"id": item["id"], "udid": item["udid"], "name": item["name"], "enabled": value["enabled"],
177
+ "via": value["via"], "transport": now, "connection": text,
178
+ "tunnel": peer.tunnel if peer is not None and peer.network else None})
179
+ if as_json:
180
+ _print({"wifiTransport": transport_on(), "devices": rows})
181
+ return DONE
182
+ if transport_on():
183
+ print("Wi-Fi for iPhones: on")
184
+ else:
185
+ print(f"Wi-Fi for iPhones: off in this Mobster. To try it, set {GATE}=1 in your env file.")
186
+ if not rows:
187
+ print("No iPhone is set up here yet. Plug one in and set it up in the Mobster app.")
188
+ return DONE
189
+ width = max(len(row["name"] or "") for row in rows) + 2
190
+ for row in rows:
191
+ switch = "Wi-Fi on" if row["enabled"] else "Wi-Fi off"
192
+ print(f" {row['name'] or row['id']:<{width}} {row['connection']:<34} {switch}")
193
+ return DONE
194
+
195
+
196
+ def _switch(args, on, as_json):
197
+ data_dir = _data_dir()
198
+ item = _choose(data_dir, getattr(args, "device", None))
199
+ udid, name = item["udid"], item["name"]
200
+ listing = _listing(data_dir, udid)
201
+ following = None
202
+ try:
203
+ if on:
204
+ if switch.turn_on(data_dir, udid, listing) == "xcode":
205
+ following = words.problem("needs_xcode", name)
206
+ elif left_on := switch.turn_off(data_dir, udid, listing):
207
+ following = words.problem(left_on, name)
208
+ except switch.SwitchRefused as error:
209
+ raise _problem(error.code, name) from None
210
+ except RuntimeError as error:
211
+ raise _Stop("devices_off", f"{error}.", "", MISSING_EXIT) from None
212
+ if not on:
213
+ return _switched_off(item, following, as_json)
214
+ if as_json:
215
+ _print({"ok": True, "device": {"id": item["id"], "udid": udid, "name": name, "enabled": on},
216
+ "wifiTransport": transport_on(), **({"next": following} if following else {})})
217
+ return DONE
218
+ if following:
219
+ print(following["message"])
220
+ print(following["fix"])
221
+ else:
222
+ print(f"Wi-Fi is on for {name}. Unplug it when you like: Mobster reaches it through the encrypted link "
223
+ "between this Mac and the phone.")
224
+ print(f"Check it, unplugged: mobster wifi probe --device {json.dumps(name, ensure_ascii=False)}")
225
+ if not transport_on():
226
+ print(f"Wi-Fi for iPhones is off in this Mobster: to use it for tasks, set {GATE}=1 in your env file.")
227
+ return DONE
228
+
229
+
230
+ def _switched_off(item, left_on, as_json):
231
+ """Wi-Fi is off in Mobster; ``left_on`` is the problem of the phone's own switch still on, or None. A switch
232
+ Mobster turned on and couldn't turn off fails (exit 1); one Xcode turned on is the person's to turn off (exit 0,
233
+ like `on`'s Xcode step)."""
234
+ name = item["name"]
235
+ failed = left_on is not None and left_on["code"] != "xcode_on"
236
+ if as_json:
237
+ device = {"id": item["id"], "udid": item["udid"], "name": name, "enabled": False}
238
+ _print({"ok": False, "error": left_on["message"], "code": left_on["code"], "fix": left_on["fix"],
239
+ "device": device} if failed else
240
+ {"ok": True, "device": device, "wifiTransport": transport_on(),
241
+ **({"next": left_on} if left_on else {})})
242
+ return FAILED if failed else DONE
243
+ print(f"Wi-Fi is off for {name}. Mobster uses it only with the cable.")
244
+ if left_on:
245
+ stream = sys.stderr if failed else sys.stdout
246
+ print(left_on["message"], file=stream)
247
+ print(left_on["fix"], file=stream)
248
+ if failed:
249
+ print(f"Plugged in and unlocked: mobster wifi off --device {json.dumps(name, ensure_ascii=False)}",
250
+ file=sys.stderr)
251
+ return FAILED if failed else DONE
252
+
253
+
254
+ def _probe(args, as_json):
255
+ from .. import devices, native_helpers
256
+ from ..mac_tools import usb_attached
257
+ from .probe import Probe, describe
258
+ if not native_helpers.devices_allowed():
259
+ raise _Stop("devices_off", "Devices are off in this process (MOBSTER_NO_DEVICES=1).", "", MISSING_EXIT)
260
+ data_dir = _data_dir()
261
+ item = _choose(data_dir, getattr(args, "device", None))
262
+ manager = None
263
+ if item.get("primary"):
264
+ from ..device_manager import DeviceManager
265
+ manager = DeviceManager(data_dir)
266
+ else:
267
+ manager = devices.manager_for(item["udid"], data_dir)
268
+ slot = devices.port_of(item.get("wdaUrl"))
269
+ if not as_json:
270
+ print(f"Probing {item['name']} over Wi-Fi "
271
+ f"({'Wi-Fi on' if store.enabled(data_dir, item['udid']) else 'Wi-Fi off'} "
272
+ f"for it; Wi-Fi for iPhones {'on' if transport_on() else 'off'} in this Mobster)")
273
+ probe = Probe(udid=item["udid"], name=item["name"], slot_port=slot, data_dir=data_dir,
274
+ start=bool(getattr(args, "start", False)), manager=manager,
275
+ out=None if as_json else (lambda step: print(describe(step), flush=True)))
276
+ try:
277
+ probe.run(usb_attached)
278
+ finally:
279
+ probe.stop_launched()
280
+ from . import result
281
+ result.record(data_dir, item["udid"], probe.public()) # the app's Wi-Fi tile reads it (GET /api/wireless)
282
+ if as_json:
283
+ _print(probe.public())
284
+ else:
285
+ print("")
286
+ print("Every check passed." if probe.ok else "Not every check passed: see the lines above.")
287
+ return DONE if probe.ok else FAILED
@@ -0,0 +1,229 @@
1
+ """What CoreDevice sees: ``xcrun devicectl``, always with a timeout and a kill (SPEC §3.7 item 2)."""
2
+
3
+ import json
4
+ import os
5
+ import signal
6
+ import subprocess
7
+ import tempfile
8
+ import threading
9
+ import time
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Mapping, Optional
13
+
14
+ from .. import native_helpers
15
+
16
+ LIST_TIMEOUT = 8.0
17
+ BRING_UP_TIMEOUT = 20.0
18
+ APPS_TIMEOUT = 30.0
19
+ DEVELOPER_TTL = 300.0
20
+ # Apple doesn't document these field names: `mobster wifi probe` prints what the phone really reports.
21
+ TUNNEL = {"connected": "up", "connecting": "connecting", "disconnected": "down", "disconnecting": "down",
22
+ "unavailable": "down"}
23
+ ADDRESS_KEYS = ("tunnelIPAddress", "tunnelIPAddressString", "tunnelAddress")
24
+
25
+
26
+ class DevicectlError(RuntimeError):
27
+ """devicectl couldn't answer. ``code``: "no_xcode" | "hang" | "failed" | "identity"."""
28
+
29
+ def __init__(self, message, code="failed"):
30
+ super().__init__(message)
31
+ self.code = code
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class Peer:
36
+ """One phone as CoreDevice reports it."""
37
+ udid: str
38
+ name: Optional[str] = None
39
+ transport: Optional[str] = None # raw transportType: "wired", "localNetwork", ...
40
+ tunnel: str = "unknown" # "up" | "connecting" | "down" | "unknown"
41
+ tunnel_state: Optional[str] = None # raw tunnelState
42
+ address: Optional[str] = None # the tunnel address as reported (checked again before any use)
43
+ developer_mode: Optional[str] = None
44
+ os_version: Optional[str] = None # deviceProperties.osVersionNumber, e.g. "26.0"
45
+ hostnames: tuple = ()
46
+ fields: Mapping = field(default_factory=dict) # connectionProperties as read, for the probe
47
+
48
+ @property
49
+ def network(self):
50
+ return self.transport == "localNetwork"
51
+
52
+ @property
53
+ def wired(self):
54
+ return self.transport == "wired"
55
+
56
+
57
+ def _text(value):
58
+ return value if isinstance(value, str) and value else None
59
+
60
+
61
+ def parse(document):
62
+ result = document.get("result") if isinstance(document, dict) else None
63
+ if not isinstance(result, dict):
64
+ return {}
65
+ devices = result.get("devices")
66
+ if devices is None and isinstance(result.get("device"), dict):
67
+ devices = [result["device"]]
68
+ peers = {}
69
+ for device in devices if isinstance(devices, list) else ():
70
+ if not isinstance(device, dict):
71
+ continue
72
+ hardware = device.get("hardwareProperties") if isinstance(device.get("hardwareProperties"), dict) else {}
73
+ properties = device.get("deviceProperties") if isinstance(device.get("deviceProperties"), dict) else {}
74
+ connection = device.get("connectionProperties") if isinstance(device.get("connectionProperties"), dict) else {}
75
+ udid = _text(hardware.get("udid")) or _text(device.get("udid"))
76
+ if not udid:
77
+ continue
78
+ state = _text(connection.get("tunnelState"))
79
+ address = next((_text(connection.get(key)) for key in ADDRESS_KEYS if _text(connection.get(key))), None)
80
+ names = []
81
+ for key in ("localHostnames", "potentialHostnames"):
82
+ value = connection.get(key)
83
+ names += [item for item in value if isinstance(item, str) and item] if isinstance(value, list) else []
84
+ peers[udid.upper()] = Peer(
85
+ udid=udid, name=_text(properties.get("name")), transport=_text(connection.get("transportType")),
86
+ tunnel=TUNNEL.get(state, "unknown"), tunnel_state=state, address=address,
87
+ developer_mode=_text(properties.get("developerModeStatus")),
88
+ os_version=_text(properties.get("osVersionNumber")),
89
+ hostnames=tuple(dict.fromkeys(names)),
90
+ fields={key: value for key, value in connection.items()
91
+ if isinstance(value, (str, int, float, bool, list))})
92
+ return peers
93
+
94
+
95
+ _developer = {"at": None, "dir": None}
96
+ _developer_lock = threading.Lock()
97
+
98
+
99
+ def developer_dir():
100
+ """Xcode's developer folder (mac_tools.developer_dir), looked up at most every 5 minutes; None without Xcode."""
101
+ with _developer_lock:
102
+ if _developer["at"] is None or time.monotonic() - _developer["at"] > DEVELOPER_TTL:
103
+ from ..mac_tools import developer_dir as lookup
104
+ try:
105
+ _developer["dir"] = lookup()[0]
106
+ except Exception:
107
+ _developer["dir"] = None
108
+ _developer["at"] = time.monotonic()
109
+ return _developer["dir"]
110
+
111
+
112
+ def command(args, developer):
113
+ """The argv for ``xcrun devicectl <args>`` with Xcode's own xcrun."""
114
+ xcrun = Path(developer) / "usr" / "bin" / "xcrun"
115
+ return [str(xcrun) if xcrun.is_file() else "/usr/bin/xcrun", "devicectl", *args]
116
+
117
+
118
+ def run_json(args, timeout, *, developer=None, runner=None):
119
+ native_helpers.require_devices()
120
+ developer = developer or developer_dir()
121
+ if not developer:
122
+ raise DevicectlError("Xcode isn't installed or set up on this Mac", "no_xcode")
123
+ with tempfile.TemporaryDirectory(prefix="mobster-devicectl-") as folder:
124
+ output = Path(folder) / "result.json"
125
+ argv = command([*args, "--json-output", str(output)], developer)
126
+ env = {**os.environ, "DEVELOPER_DIR": developer}
127
+ code, error = (runner or _run)(argv, env, timeout)
128
+ try:
129
+ document = json.loads(output.read_text())
130
+ except (OSError, ValueError):
131
+ document = None
132
+ if document is None:
133
+ raise DevicectlError(f"devicectl gave no answer (exit {code}): {error[-200:]}".strip(), "failed")
134
+ outcome = (document.get("info") or {}).get("outcome") if isinstance(document, dict) else None
135
+ if code != 0 and outcome != "success" and not parse(document):
136
+ raise DevicectlError(f"devicectl failed (exit {code}): {error[-200:]}".strip(), "failed")
137
+ return document
138
+
139
+
140
+ # `devicectl list devices` can hang while it sets up a tunnel (Apple FB15184273).
141
+ def _run(argv, env, timeout):
142
+ """(exit code, the end of stderr). Its own process group, killed on overrun: DevicectlError "hang"."""
143
+ with tempfile.TemporaryFile() as errors:
144
+ try:
145
+ process = subprocess.Popen(argv, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=errors,
146
+ env=env, start_new_session=True)
147
+ except OSError as error:
148
+ raise DevicectlError(f"devicectl couldn't start: {error}", "no_xcode") from None
149
+ try:
150
+ code = process.wait(timeout=timeout)
151
+ except subprocess.TimeoutExpired:
152
+ kill(process)
153
+ raise DevicectlError(f"devicectl didn't answer in {timeout:g} s and was stopped", "hang") from None
154
+ errors.seek(0)
155
+ return code, errors.read()[-2000:].decode("utf-8", "replace")
156
+
157
+
158
+ def kill(process):
159
+ """Kill a hung devicectl and everything it started."""
160
+ try:
161
+ os.killpg(process.pid, signal.SIGKILL)
162
+ except (ProcessLookupError, PermissionError):
163
+ pass
164
+ try:
165
+ process.wait(timeout=2)
166
+ except subprocess.TimeoutExpired:
167
+ pass
168
+
169
+
170
+ def list_devices(timeout=LIST_TIMEOUT, **kwargs):
171
+ """{UDID: Peer} for every device CoreDevice knows (on USB, over the network, or neither now)."""
172
+ return parse(run_json(["list", "devices"], timeout, **kwargs))
173
+
174
+
175
+ def _checked(udid):
176
+ from ..devices import UDID_PATTERN
177
+ if not isinstance(udid, str) or not UDID_PATTERN.fullmatch(udid):
178
+ raise ValueError("A UDID is 24 to 40 hex digits and dashes")
179
+ return udid
180
+
181
+
182
+ def bring_up(udid, timeout=BRING_UP_TIMEOUT, **kwargs):
183
+ peers = parse(run_json(["device", "info", "details", "--device", _checked(udid)], timeout, **kwargs))
184
+ if peers and udid.upper() not in peers:
185
+ raise DevicectlError("CoreDevice answered for a different iPhone than the one asked for", "identity")
186
+ return peers
187
+
188
+
189
+ def lock_state(document):
190
+ # None is never read as unlocked
191
+ result = document.get("result") if isinstance(document, dict) else None
192
+ value = result.get("passcodeRequired") if isinstance(result, dict) else None
193
+ return value if isinstance(value, bool) else None
194
+
195
+
196
+ def read_lock_state(udid, timeout=LIST_TIMEOUT, **kwargs):
197
+ try:
198
+ return lock_state(run_json(["device", "info", "lockState", "--device", _checked(udid)], timeout, **kwargs))
199
+ except (DevicectlError, RuntimeError): # RuntimeError: MOBSTER_NO_DEVICES=1
200
+ return None
201
+
202
+
203
+ def parse_apps(document, kind="user"):
204
+ from ..installed_apps import MAX_APPS, NAME_LIMIT, _clean
205
+ from ..state import validate_bundle_id
206
+ result = document.get("result") if isinstance(document, dict) else None
207
+ items = result.get("apps") if isinstance(result, dict) else None
208
+ if not isinstance(items, list):
209
+ raise DevicectlError("devicectl printed no app list", "failed")
210
+ apps, seen = [], set()
211
+ for item in items[:MAX_APPS]:
212
+ if not isinstance(item, dict) or (item.get("defaultApp") is True) != (kind == "system"):
213
+ continue
214
+ try:
215
+ bundle = validate_bundle_id(item.get("bundleIdentifier"))
216
+ except ValueError:
217
+ continue
218
+ if bundle in seen:
219
+ continue
220
+ seen.add(bundle)
221
+ apps.append({"bundleId": bundle, "name": _clean(item.get("name")) or bundle[:NAME_LIMIT],
222
+ "version": _clean(item.get("version"))})
223
+ apps.sort(key=lambda app: (app["name"].casefold(), app["bundleId"]))
224
+ return apps
225
+
226
+
227
+ def read_apps(udid, kind="user", timeout=APPS_TIMEOUT, **kwargs):
228
+ """parse_apps for ``udid``, read through CoreDevice (USB or Wi-Fi). Raises DevicectlError."""
229
+ return parse_apps(run_json(["device", "info", "apps", "--device", _checked(udid)], timeout, **kwargs), kind)
@@ -0,0 +1,33 @@
1
+
2
+ import socket
3
+
4
+ from . import network
5
+
6
+ PORTS = (8100, 9100)
7
+ # What WebDriverAgent (and Mobster's patches to it) answer with; a router's own page says none of these.
8
+ SIGNS = (b"WDA MJPEG Server", b"sessionId", b"Local clients only", b"WebDriverAgent")
9
+
10
+
11
+ def speaks_wda(host, port, timeout=1.5, connect=None):
12
+ try:
13
+ with (connect or socket.create_connection)((host, port), timeout) as sock:
14
+ sock.settimeout(timeout)
15
+ # the local Host header: Mobster's patched helper refuses any other, and this only asks if it is there
16
+ sock.sendall(f"GET /status HTTP/1.0\r\nHost: 127.0.0.1:{port}\r\n\r\n".encode())
17
+ data = sock.recv(1024)
18
+ except OSError:
19
+ return False
20
+ return any(sign in data for sign in SIGNS)
21
+
22
+
23
+ def exposed(hostnames, *, resolve=None, gateway=None, accepts=None, speaks=None):
24
+ resolve, gateway = resolve or network.resolve, gateway or network.default_gateway
25
+ accepts, speaks = accepts or network.port_open, speaks or speaks_wda
26
+ found = resolve(list(hostnames)) if hostnames else {"ipv4": [], "ipv6": []}
27
+ addresses = found["ipv4"] + found["ipv6"]
28
+ hits = [f"{address}:{port}" for address in addresses for port in PORTS if accepts(address, port)]
29
+ # a phone sharing its hotspot is the router and has no name: there only an answer from WebDriverAgent counts
30
+ router = gateway()
31
+ if router and router not in addresses:
32
+ hits += [f"{router}:{port}" for port in PORTS if speaks(router, port)]
33
+ return addresses, hits
@@ -0,0 +1,122 @@
1
+ /*
2
+ * mobster-wifi-pair: let a trusted iPhone be reached over Wi-Fi, or stop it.
3
+ *
4
+ * It sets the lockdown value com.apple.mobile.wireless_lockdown/EnableWifiConnections, the same flag as Xcode's
5
+ * "Connect via network". Only over USB: the phone must be plugged in and trust this Mac. It never pairs a new
6
+ * Mac, never enters a passcode and changes nothing else on the phone.
7
+ *
8
+ * mobster-wifi-pair --udid UDID --enable | --disable | --status
9
+ *
10
+ * Prints one JSON line, {"enabled": true|false}, and exits:
11
+ * 0 done, 2 usage, 3 no iPhone with that UDID on USB, 4 the iPhone doesn't trust this Mac (or is locked),
12
+ * 5 the iPhone refused the change, 6 anything else.
13
+ *
14
+ * This file is MIT, like Mobster. desktop/scripts/build-iphone-tools.sh compiles it against the static
15
+ * libimobiledevice and libplist it bundles (LGPL-2.1, like the iPhone tools already in the app).
16
+ */
17
+ #include <stdio.h>
18
+ #include <string.h>
19
+
20
+ #include <libimobiledevice/libimobiledevice.h>
21
+ #include <libimobiledevice/lockdown.h>
22
+ #include <plist/plist.h>
23
+
24
+ #define DOMAIN "com.apple.mobile.wireless_lockdown"
25
+ #define KEY "EnableWifiConnections"
26
+
27
+ enum { DONE = 0, USAGE = 2, NO_DEVICE = 3, NOT_TRUSTED = 4, REFUSED = 5, FAILED = 6 };
28
+
29
+ static int usage(void)
30
+ {
31
+ fprintf(stderr, "usage: mobster-wifi-pair --udid UDID --enable | --disable | --status\n");
32
+ return USAGE;
33
+ }
34
+
35
+ static int valid_udid(const char *udid)
36
+ {
37
+ size_t length = strlen(udid);
38
+ if (length < 24 || length > 40)
39
+ return 0;
40
+ for (size_t i = 0; i < length; i++) {
41
+ char c = udid[i];
42
+ if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == '-'))
43
+ return 0;
44
+ }
45
+ return 1;
46
+ }
47
+
48
+ static int handshake_code(lockdownd_error_t error)
49
+ {
50
+ switch (error) {
51
+ case LOCKDOWN_E_PASSWORD_PROTECTED:
52
+ case LOCKDOWN_E_USER_DENIED_PAIRING:
53
+ case LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING:
54
+ case LOCKDOWN_E_INVALID_HOST_ID:
55
+ case LOCKDOWN_E_MISSING_PAIR_RECORD:
56
+ case LOCKDOWN_E_INVALID_PAIR_RECORD:
57
+ case LOCKDOWN_E_PAIRING_FAILED:
58
+ case LOCKDOWN_E_SSL_ERROR:
59
+ return NOT_TRUSTED;
60
+ default:
61
+ return FAILED;
62
+ }
63
+ }
64
+
65
+ int main(int argc, char **argv)
66
+ {
67
+ const char *udid = NULL;
68
+ int action = 0; /* 'e' enable, 'd' disable, 's' status */
69
+ for (int i = 1; i < argc; i++) {
70
+ if (!strcmp(argv[i], "--udid") && i + 1 < argc)
71
+ udid = argv[++i];
72
+ else if (!action && !strcmp(argv[i], "--enable"))
73
+ action = 'e';
74
+ else if (!action && !strcmp(argv[i], "--disable"))
75
+ action = 'd';
76
+ else if (!action && !strcmp(argv[i], "--status"))
77
+ action = 's';
78
+ else
79
+ return usage();
80
+ }
81
+ if (!udid || !action || !valid_udid(udid))
82
+ return usage();
83
+
84
+ idevice_t device = NULL;
85
+ /* USB only: the flag is turned on with the cable, never over the network. */
86
+ if (idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS || !device) {
87
+ fprintf(stderr, "no iPhone with that UDID is on USB\n");
88
+ return NO_DEVICE;
89
+ }
90
+ lockdownd_client_t client = NULL;
91
+ lockdownd_error_t error = lockdownd_client_new_with_handshake(device, &client, "mobster-wifi-pair");
92
+ if (error != LOCKDOWN_E_SUCCESS) {
93
+ fprintf(stderr, "lockdown handshake failed (%d)\n", error);
94
+ idevice_free(device);
95
+ return handshake_code(error);
96
+ }
97
+ int code = DONE;
98
+ if (action != 's') {
99
+ /* lockdownd_set_value takes the node and frees it. */
100
+ error = lockdownd_set_value(client, DOMAIN, KEY, plist_new_bool(action == 'e'));
101
+ if (error != LOCKDOWN_E_SUCCESS) {
102
+ fprintf(stderr, "the iPhone refused the change (%d)\n", error);
103
+ code = error == LOCKDOWN_E_SET_PROHIBITED || error == LOCKDOWN_E_IMMUTABLE_VALUE ? REFUSED : FAILED;
104
+ }
105
+ }
106
+ if (code == DONE) {
107
+ plist_t value = NULL;
108
+ uint8_t enabled = 0;
109
+ error = lockdownd_get_value(client, DOMAIN, KEY, &value);
110
+ if (error == LOCKDOWN_E_SUCCESS && value && plist_get_node_type(value) == PLIST_BOOLEAN)
111
+ plist_get_bool_val(value, &enabled);
112
+ else if (error != LOCKDOWN_E_SUCCESS && error != LOCKDOWN_E_MISSING_VALUE && error != LOCKDOWN_E_MISSING_KEY)
113
+ code = FAILED;
114
+ if (value)
115
+ plist_free(value);
116
+ if (code == DONE)
117
+ printf("{\"enabled\": %s}\n", enabled ? "true" : "false");
118
+ }
119
+ lockdownd_client_free(client);
120
+ idevice_free(device);
121
+ return code;
122
+ }