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,526 @@
1
+ """The phone guard: whether an iPhone can be driven now, decided by code (never the model).
2
+
3
+ This build only *detects*. It never types a passcode, never taps the lock screen and never answers a
4
+ system sheet: a locked iPhone, a locked app or the App Store's side-button confirmation
5
+ stops the run at once with one plain sentence, before a model call is spent on a screen the agent cannot
6
+ use. One exception waits instead of stopping ("Look at your iPhone", WOW §4.1): a task a person just started
7
+ in the Mac app, the terminal UI or ``mobster chat`` on a locked iPhone waits up to UNLOCK_WAIT_S for that
8
+ person to unlock it, reading only ``GET /wda/locked``, and starts the moment it reads unlocked. An attended run
9
+ also waits for a Face ID sheet to clear, then reads the app again; only the owner authenticates. Unlocking with a saved
10
+ passcode (PLAN §3) waits for the owner's sign-off; ``CAN_UNLOCK`` says so to the other parts (MCP's ``unlock_status``,
11
+ the settings), and ``finish`` has nothing to relock.
12
+
13
+ What it reads, all through WebDriverAgent and all read-only:
14
+
15
+ - ``GET /wda/locked`` (sessionless, answers in milliseconds even when the UI is wedged), at every check;
16
+ - ``GET /wda/activeAppInfo`` (sessionless), to name the app a sheet is over;
17
+ - SpringBoard's own accessibility tree, only when the frontier suspects a system sheet: the session's
18
+ ``defaultActiveApplication`` is pointed at SpringBoard for one ``/source`` read and always restored.
19
+
20
+ Sheet labels are matched as whole labels, never substrings, so a notification banner that happens to say
21
+ "locked" is not a locked-app sheet. They come from iOS 18-26's wording and are provisional until the
22
+ owner's read-only ``mobster devices lock-probe`` confirms them on his phone (PLAN §6.1); a sheet the
23
+ classifier does not know is ``unknown`` and the run goes on as before (the guard never taps anything,
24
+ so a miss costs no more than today).
25
+ """
26
+
27
+ import os
28
+ import re
29
+ import time
30
+
31
+ from . import harness_api
32
+ from .agent_hooks import READY, GuardVerdict
33
+
34
+ # This build cannot unlock an iPhone (no passcode is ever stored, read or typed).
35
+ CAN_UNLOCK = False
36
+ SPRINGBOARD = "com.apple.springboard"
37
+ # Each read the guard makes; a locked phone answers /wda/locked in well under a second.
38
+ READ_TIMEOUT = 3.0
39
+ # How long a USB listing is trusted (``attached``): a pulled cable is known within this many seconds.
40
+ ATTACHED_TTL = 2.0
41
+ # Look at your iPhone (WOW §4.1, v0): an interactive task that starts on a locked iPhone waits this many seconds for
42
+ # the person to unlock it, reading /wda/locked every UNLOCK_POLL_S. Reads only: no button, no tap, no key, ever.
43
+ UNLOCK_WAIT_S = 120
44
+ UNLOCK_POLL_S = 1.0
45
+ # The states a start waits through: the person clears them by unlocking. Never a sheet or an unreadable phone.
46
+ WAIT_STATES = ("lock_screen", "passcode_keypad")
47
+
48
+ STATES = ("unlocked", "lock_screen", "passcode_keypad", "face_id_sheet", "app_locked_sheet", "apple_confirmation",
49
+ "unknown")
50
+
51
+ # The user-facing sentences (PLAN §3.9-§3.10). None names a passcode, a code or a digit count. The §3.9
52
+ # sentence's "or let Mobster unlock it in Settings › iPhones" clause is left out: that setting needs
53
+ # passcode unlock, which this build does not have.
54
+ PHONE_LOCKED = "Your iPhone is locked. Unlock it and try again. No action was taken."
55
+ LOCKED_DURING = "Your iPhone locked during the task, so Mobster stopped. Unlock it and try again."
56
+ NOT_RESPONDING = ("Your iPhone isn't responding. Unlock it, close Control Center or Siri if they're open, then "
57
+ "try again. No action was taken.")
58
+ UNPLUGGED = "Your iPhone was unplugged. Plug it back in and try again."
59
+ # A task over Wi-Fi whose encrypted link went down.
60
+ WIFI_LOST = ("Your iPhone went out of reach over Wi-Fi, so Mobster stopped. Keep it unlocked and on the same "
61
+ "Wi-Fi as this Mac, or plug it in, then try again.")
62
+ # A task a person started waits this long for its phone to come back (DetectGuard.await_return), then stops with these.
63
+ RETURN_WAIT_TIMED_OUT = {
64
+ "unplugged": "Your iPhone stayed unplugged, so Mobster stopped. "
65
+ "Plug it back in and pick the task up where it left off.",
66
+ "wifi_lost": ("Your iPhone stayed out of reach over Wi-Fi, so Mobster stopped. Plug it in or bring it back near "
67
+ "this Mac, then pick the task up where it left off."),
68
+ }
69
+ FACE_ID = "{app} is asking for Face ID. Mobster can't use Face ID: open {app} on your iPhone once, then try again."
70
+ APP_LOCKED = "{app} is locked on your iPhone. Mobster won't open locked apps; open it yourself or remove the lock."
71
+ APPLE_CONFIRMATION = "The App Store wants you to confirm on your iPhone. Mobster never does that for you."
72
+ # The stop when a start waited on a locked iPhone (the Mac app's card, `mobster chat`) and nobody unlocked it.
73
+ LOCK_WAIT_TIMED_OUT = "Still locked, so the task didn't start. Unlock your iPhone and try again."
74
+ # A lock in the middle of a run a person started (DESIGN §12.1) waits the same way, and stops if nobody unlocks.
75
+ LOCK_WAIT_MIDRUN_TIMED_OUT = ("Your iPhone stayed locked, so Mobster stopped. Unlock it and pick the task up where it "
76
+ "left off.")
77
+ MIDRUN_CAUSES = ("resume", "lock_suspected")
78
+ # The owner is on the phone (owner_presence.py): a lock is theirs to deal with, never a prompt from Mobster. No
79
+ # sentence here names a passcode, and no event asks for an unlock.
80
+ OWNER_HAS_PHONE = ("You're using your iPhone, so Mobster stayed off it and the task didn't start. "
81
+ "Start it again when you put the phone down.")
82
+
83
+ # Whole-label markers (case-insensitive). The keypad needs all ten digit keys besides a title.
84
+ _KEYPAD_TITLE = re.compile(r"^(enter( your)? (iphone )?passcode|passcode)$", re.I)
85
+ _LOCK_EXTRAS = re.compile(r"^(emergency|cancel|delete)$", re.I)
86
+ _FACE_ID = re.compile(r"^(face id|try face id again|use face id( to (unlock|continue))?)$", re.I)
87
+ # Only a quoted app name: "Your account is locked" in a banner is not a locked app.
88
+ _APP_LOCKED = re.compile(r"^(?:“(?P<a>[^”]{1,60})”|\"(?P<b>[^\"]{1,60})\") is locked\.?$", re.I)
89
+ _APP_LOCKED_HINT = re.compile(r"^(?:require face id|face id is required to open .{1,60})$", re.I)
90
+ _CONFIRM = re.compile(r"^(double[- ]click to (install|pay|get|confirm|subscribe)|confirm with side button)\.?$", re.I)
91
+
92
+
93
+ def tree_labels(tree, limit=400):
94
+ """The labels in a WDA ``/source?format=json`` tree, in reading order: (type, text) pairs."""
95
+ found, stack = [], [tree] if isinstance(tree, dict) else []
96
+ while stack and len(found) < limit:
97
+ node = stack.pop()
98
+ if not isinstance(node, dict):
99
+ continue
100
+ text = next((node[key] for key in ("label", "name", "value")
101
+ if isinstance(node.get(key), str) and node[key].strip()), "")
102
+ if text:
103
+ found.append((str(node.get("type") or ""), text.strip()[:200]))
104
+ children = node.get("children")
105
+ if isinstance(children, list):
106
+ stack.extend(reversed(children))
107
+ return found
108
+
109
+
110
+ def _keypad(texts):
111
+ digits = {t for t in texts if len(t) == 1 and t.isdigit()}
112
+ return len(digits) == 10 and any(_KEYPAD_TITLE.match(t) for t in texts)
113
+
114
+
115
+ def classify(locked, front=None, labels=None):
116
+ """(state, app or None) from what the guard read; a keypad needs SpringBoard, its title and all ten digits."""
117
+ texts = [text for _, text in (labels or ())]
118
+ if locked is True:
119
+ return ("passcode_keypad" if front in (None, SPRINGBOARD) and _keypad(texts) else "lock_screen"), None
120
+ if locked is None:
121
+ return "unknown", None
122
+ for text in texts:
123
+ if _CONFIRM.match(text):
124
+ return "apple_confirmation", None
125
+ for text in texts:
126
+ match = _APP_LOCKED.match(text)
127
+ if match:
128
+ return "app_locked_sheet", next(name for name in match.groups() if name)
129
+ if any(_APP_LOCKED_HINT.match(text) for text in texts):
130
+ return "app_locked_sheet", None
131
+ if any(_FACE_ID.match(text) for text in texts):
132
+ return "face_id_sheet", None
133
+ if labels is not None and front == SPRINGBOARD and _keypad(texts) and any(_LOCK_EXTRAS.match(t) for t in texts):
134
+ # A passcode keypad over an unlocked phone: iOS asking for the passcode (a locked app whose Face ID
135
+ # failed, a Settings change). Never ours to answer.
136
+ return "app_locked_sheet", None
137
+ return "unlocked", None
138
+
139
+
140
+ def app_name(bundle, named=None):
141
+ """A display name for a sentence: the sheet's own, else the catalog's, else "This app"."""
142
+ if named:
143
+ return named
144
+ if bundle and bundle != SPRINGBOARD:
145
+ from .catalog import app_label
146
+ label = app_label(bundle)
147
+ if label != bundle:
148
+ return label.rsplit(" (", 1)[0]
149
+ return "This app"
150
+
151
+
152
+ def verdict_for(state, *, cause, app=None):
153
+ """The GuardVerdict a state means for a check made for ``cause``."""
154
+ if state == "unlocked":
155
+ return READY
156
+ if state in ("lock_screen", "passcode_keypad"):
157
+ return GuardVerdict("stop", "phone_locked", PHONE_LOCKED if cause == "preflight" else LOCKED_DURING)
158
+ if state == "face_id_sheet":
159
+ return GuardVerdict("stop", "face_id", FACE_ID.format(app=app or "This app"))
160
+ if state == "app_locked_sheet":
161
+ return GuardVerdict("stop", "app_locked", APP_LOCKED.format(app=app or "This app"))
162
+ if state == "apple_confirmation":
163
+ return GuardVerdict("stop", "apple_confirmation", APPLE_CONFIRMATION)
164
+ # unknown: the phone could not be read. Fail closed: never act on a phone the guard could not read.
165
+ return GuardVerdict("stop", "phone_locked", NOT_RESPONDING if cause == "preflight" else LOCKED_DURING)
166
+
167
+
168
+ def read_locked(driver, timeout=READ_TIMEOUT):
169
+ """/wda/locked: True, False, or None when WDA could not say."""
170
+ try:
171
+ value = driver.http.request("GET", "/wda/locked", None, timeout).get("value")
172
+ except Exception:
173
+ return None
174
+ return value if isinstance(value, bool) else None
175
+
176
+
177
+ def read_front(driver, timeout=READ_TIMEOUT):
178
+ """The foreground bundle, or None; /wda/activeAppInfo is never waited on past READ_TIMEOUT."""
179
+ slow = getattr(driver, "slow_front", None)
180
+ if callable(slow):
181
+ try:
182
+ bundle = slow()
183
+ except Exception:
184
+ bundle = None
185
+ if isinstance(bundle, str) and bundle:
186
+ return bundle
187
+ try:
188
+ value = driver.http.request("GET", "/wda/activeAppInfo", None, min(timeout, READ_TIMEOUT)).get("value")
189
+ except Exception:
190
+ return None
191
+ bundle = value.get("bundleId") if isinstance(value, dict) else None
192
+ return bundle if isinstance(bundle, str) and bundle else None
193
+
194
+
195
+ def read_springboard_tree(driver, timeout=READ_TIMEOUT):
196
+ """SpringBoard's accessibility tree, or None; the session's active application is always put back afterwards."""
197
+ previous = getattr(driver, "_hinted", None) or "auto"
198
+ try:
199
+ driver.call("POST", "/appium/settings", {"settings": {"defaultActiveApplication": SPRINGBOARD}}, timeout)
200
+ except Exception:
201
+ return None
202
+ try:
203
+ return driver.call("GET", "/source?format=json", None, timeout)
204
+ except Exception:
205
+ return None
206
+ finally:
207
+ try:
208
+ driver.call("POST", "/appium/settings", {"settings": {"defaultActiveApplication": previous}}, timeout)
209
+ except Exception:
210
+ pass
211
+
212
+
213
+ def read_springboard(driver, timeout=READ_TIMEOUT):
214
+ """SpringBoard's (type, text) labels, or None."""
215
+ tree = read_springboard_tree(driver, timeout)
216
+ return None if tree is None else tree_labels(tree)
217
+
218
+
219
+ def read_coredevice_locked(udid):
220
+ """The phone's lock as CoreDevice reports it (True, False or None): the second reader for a silent /wda/locked."""
221
+ if not udid:
222
+ return None
223
+ reader = harness_api.hooks().coredevice_locked
224
+ try:
225
+ value = reader(udid)
226
+ except Exception:
227
+ return None
228
+ return value if isinstance(value, bool) else None
229
+
230
+
231
+ def probe(driver, *, springboard=True, timeout=READ_TIMEOUT, udid=None):
232
+ """Everything the guard can read, for ``mobster devices lock-probe``: no tap, no key, no typing."""
233
+ wda = read_locked(driver, timeout)
234
+ core = read_coredevice_locked(udid) if wda is None else None
235
+ locked = True if core is True else wda
236
+ front = read_front(driver, timeout) if wda is not None else None
237
+ labels = read_springboard(driver, timeout) if springboard and wda is not None else None
238
+ state, named = classify(locked, front, labels)
239
+ return {"locked": locked, "front": front, "state": state, "app": named,
240
+ **({"coreDevice": core} if wda is None and udid else {}),
241
+ "labels": [{"type": kind, "label": text} for kind, text in (labels or ())]}
242
+
243
+
244
+ # Causes that read SpringBoard's tree for a sheet. Preflight does not: it presses Home next, and a locked
245
+ # phone must stop in well under 2 s.
246
+ SHEET_CAUSES = ("launch_failed", "lock_suspected", "sheet_suspected", "resume")
247
+
248
+
249
+ def blocked_apps(env=None):
250
+ """MOBSTER_BLOCKED_APPS: bundle ids Mobster never opens (comma-separated)."""
251
+ raw = (os.environ if env is None else env).get("MOBSTER_BLOCKED_APPS") or ""
252
+ return frozenset(item.strip() for item in raw.split(",") if item.strip())
253
+
254
+
255
+ class DetectGuard:
256
+ """agent_hooks.PhoneGuard that only detects; with ``wait_s`` > 0 a lock waits for the person instead of stopping."""
257
+
258
+ def __init__(self, *, device_id=None, usb=False, emit=None, blocked=None, attached_reader=None, wait_s=0.0,
259
+ cancelled=None, sleep=time.sleep, clock=time.monotonic, owner_active=None):
260
+ self.device_id = device_id
261
+ self.owner_active = owner_active
262
+ self.usb = bool(usb and device_id)
263
+ self.emit = emit
264
+ self.blocked = blocked_apps() if blocked is None else frozenset(blocked)
265
+ self._attached_reader = attached_reader
266
+ self.wait_s = max(0.0, float(wait_s or 0))
267
+ self.cancelled = cancelled
268
+ self.sleep, self.clock = sleep, clock
269
+
270
+ def check(self, driver, *, cause):
271
+ locked = read_locked(driver)
272
+ if locked is None:
273
+ attached = self.attached()
274
+ if attached is False:
275
+ return self.await_return(driver, "unplugged", midrun=cause != "preflight") or GuardVerdict(
276
+ "stop", "unplugged", UNPLUGGED)
277
+ if attached is None and self.wifi_lost():
278
+ return self.await_return(driver, "wifi_lost", midrun=cause != "preflight") or GuardVerdict(
279
+ "stop", "unplugged", WIFI_LOST)
280
+ if locked is not False or cause not in SHEET_CAUSES:
281
+ state = classify(locked)[0]
282
+ if state in WAIT_STATES and not self._person_waits(cause) and self._owner_has_phone():
283
+ # An unattended check (a schedule, MCP, a script) on a phone its owner is using: "you have it",
284
+ # never "locked, unlock it". A task a person started waits as before: they asked.
285
+ return GuardVerdict("stop", "owner_active", OWNER_HAS_PHONE)
286
+ if cause == "preflight" and self.wait_s > 0 and state in WAIT_STATES:
287
+ return self._await_unlock(driver, state)
288
+ if cause in MIDRUN_CAUSES and self.wait_s > 0 and state in WAIT_STATES:
289
+ return self._await_unlock(driver, state, midrun=True)
290
+ return verdict_for(state, cause=cause)
291
+ front = read_front(driver)
292
+ labels = read_springboard(driver)
293
+ if labels is None:
294
+ return READY # unlocked, and no sheet could be read: nothing to stop for
295
+ state, named = classify(False, front, labels)
296
+ if state == "face_id_sheet" and self.wait_s > 0:
297
+ return self._await_face_id(driver, app_name(front, named))
298
+ return verdict_for(state, cause=cause, app=app_name(front, named))
299
+
300
+ def _await_face_id(self, driver, app):
301
+ """Wait for the owner to clear Face ID. Never treat an unreadable sheet as authentication."""
302
+ started = self.clock()
303
+ verdict = verdict_for("face_id_sheet", cause="resume", app=app)
304
+ self._emit({"event": "handoff_waiting", "kind": "face_id", "seconds": int(self.wait_s)})
305
+ while not self._stopped():
306
+ left = started + self.wait_s - self.clock()
307
+ if left <= 0:
308
+ break
309
+ self.sleep(min(UNLOCK_POLL_S, left))
310
+ if self._stopped():
311
+ break
312
+ locked = read_locked(driver)
313
+ if locked is not False:
314
+ attached = self.attached()
315
+ if attached is False or attached is None and self.wifi_lost():
316
+ verdict = GuardVerdict("stop", "unplugged", UNPLUGGED if attached is False else WIFI_LOST)
317
+ else:
318
+ verdict = verdict_for(classify(locked)[0], cause="resume")
319
+ break
320
+ front, labels = read_front(driver), read_springboard(driver)
321
+ state, named = classify(False, front, labels) if front and labels is not None else ("unknown", None)
322
+ if self._stopped():
323
+ break
324
+ verdict = verdict_for(state, cause="resume", app=app_name(front, named))
325
+ if state != "face_id_sheet":
326
+ break
327
+ self._emit({"event": "handoff_finished", "kind": "face_id", "ok": verdict.state == "ready",
328
+ "waited_s": round(max(0.0, self.clock() - started), 1)})
329
+ return verdict
330
+
331
+ def _await_unlock(self, driver, kind, midrun=False):
332
+ """Look at your iPhone: wait up to ``wait_s`` for the person to unlock the phone, then READY, or a stop.
333
+
334
+ The only requests are ``GET /wda/locked`` once a second and, when a read fails, the USB listing. Nothing is
335
+ pressed, tapped or typed while the phone is locked: the person unlocks it, never Mobster. A read that fails
336
+ while the cable is known to be in keeps waiting (over Wi-Fi a locked phone may drop its link for a moment)."""
337
+ started = self.clock()
338
+ deadline = started + self.wait_s
339
+ where = {"midrun": True} if midrun else {}
340
+ self._emit({"event": "handoff_waiting", "kind": kind, "seconds": int(self.wait_s), **where})
341
+ while True:
342
+ if self._stopped():
343
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
344
+ # Stop: the run ends as stopped (run.stop is set)
345
+ return verdict_for(kind, cause="resume" if midrun else "preflight")
346
+ left = deadline - self.clock()
347
+ if left <= 0:
348
+ break
349
+ self.sleep(min(UNLOCK_POLL_S, left))
350
+ if self._stopped():
351
+ continue
352
+ locked = read_locked(driver)
353
+ if locked is False:
354
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": True,
355
+ "waited_s": round(max(0.0, self.clock() - started), 1), **where})
356
+ return READY
357
+ if locked is None:
358
+ attached = self.attached()
359
+ if attached is False or attached is None and self.wifi_lost():
360
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
361
+ return GuardVerdict("stop", "unplugged", UNPLUGGED if attached is False else WIFI_LOST)
362
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
363
+ return GuardVerdict("stop", "phone_locked", LOCK_WAIT_MIDRUN_TIMED_OUT if midrun else LOCK_WAIT_TIMED_OUT)
364
+
365
+ def await_return(self, driver, kind="unplugged", midrun=True):
366
+ """Wait up to ``wait_s`` for an unplugged or Wi-Fi-lost phone to answer; None when the run doesn't wait."""
367
+ if self.wait_s <= 0:
368
+ return None
369
+ started = self.clock()
370
+ deadline = started + self.wait_s
371
+ where = {"midrun": True} if midrun else {}
372
+ self._emit({"event": "handoff_waiting", "kind": kind, "seconds": int(self.wait_s), **where})
373
+ while not self._stopped():
374
+ left = deadline - self.clock()
375
+ if left <= 0:
376
+ break
377
+ self.sleep(min(UNLOCK_POLL_S, left))
378
+ if self._stopped() or self.attached() is False:
379
+ continue
380
+ locked = read_locked(driver)
381
+ if locked is None:
382
+ continue # plugged in or on the link again, its runner not answering yet
383
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": True,
384
+ "waited_s": round(max(0.0, self.clock() - started), 1), **where})
385
+ return self._await_unlock(driver, "lock_screen", midrun=True) if locked else READY
386
+ self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
387
+ if self._stopped():
388
+ return GuardVerdict("stop", "unplugged", UNPLUGGED if kind == "unplugged" else WIFI_LOST)
389
+ return GuardVerdict("stop", "unplugged", RETURN_WAIT_TIMED_OUT[kind])
390
+
391
+ def _person_waits(self, cause):
392
+ """Whether a check for ``cause`` waits for a person to unlock instead of stopping (a task they started)."""
393
+ if self.wait_s <= 0:
394
+ return False
395
+ return cause in ("preflight", *MIDRUN_CAUSES)
396
+
397
+ def _owner_has_phone(self):
398
+ """Whether the phone's owner is using it now. A reader that fails says no: the lock rules stand."""
399
+ try:
400
+ return bool(self.owner_active()) if callable(self.owner_active) else False
401
+ except Exception:
402
+ return False
403
+
404
+ def _stopped(self):
405
+ try:
406
+ return bool(self.cancelled()) if callable(self.cancelled) else False
407
+ except Exception:
408
+ return True # can't tell whether Stop was pressed: stop, never wait on
409
+
410
+ def _emit(self, event):
411
+ if callable(self.emit):
412
+ self.emit(event)
413
+
414
+ def attached(self):
415
+ """Whether the phone is connected; None, not False, when it may be on Wi-Fi, so it isn't stopped unplugged."""
416
+ if not self.usb:
417
+ return None
418
+ wifi = _wifi_attached(self.device_id)
419
+ if wifi is not _NOT_WIFI:
420
+ return wifi
421
+ reader = self._attached_reader or harness_api.hooks().usb_attached
422
+ try:
423
+ usb = reader(self.device_id)
424
+ except Exception:
425
+ return None
426
+ if usb is False and _wifi_may_reach(self.device_id):
427
+ return None
428
+ return usb
429
+
430
+ def wifi_lost(self):
431
+ """Whether the phone was running over Wi-Fi and its link went down (not a switch Mobster is making)."""
432
+ if not self.usb:
433
+ return False
434
+ lost = harness_api.hooks().wifi_lost
435
+ try:
436
+ return bool(lost(self.device_id))
437
+ except Exception:
438
+ return False
439
+
440
+ def allows_app(self, bundle_id):
441
+ return bundle_id not in self.blocked
442
+
443
+ def finish(self, driver):
444
+ """Nothing to relock: this guard never unlocks."""
445
+
446
+
447
+ _NOT_WIFI = object()
448
+
449
+
450
+ def _wifi_attached(udid):
451
+ attached = harness_api.hooks().wifi_attached
452
+ try:
453
+ wifi, value = attached(udid)
454
+ except Exception:
455
+ return _NOT_WIFI
456
+ return value if wifi else _NOT_WIFI
457
+
458
+
459
+ def _wifi_may_reach(udid):
460
+ """Whether an unplugged phone may run over the Mac app's Wi-Fi relay, in a process that doesn't run its link."""
461
+ may_reach = harness_api.hooks().wifi_may_reach
462
+ try:
463
+ return bool(may_reach(udid))
464
+ except Exception:
465
+ return False
466
+
467
+
468
+ def guard_for(*, device_id, wda_url, mode, approve=None, emit=None, video=None, usb=True, wait_s=0.0, cancelled=None):
469
+ """The phone guard for one run (agent_hooks.PhoneGuard). ``mode``: "interactive" | "schedule" | "scripts".
470
+ ``usb``: whether ``device_id`` is a USB iPhone's UDID (a simulator or a bare WDA address is never
471
+ "unplugged"). ``approve`` and ``video`` are for unlocking, which this build does not do. ``wait_s``: how long a
472
+ start on a locked phone, or a run the phone locks under, waits for the person to unlock it (0, the default, stops
473
+ at once: ``mobster run``, schedules, MCP); ``cancelled``: whether Stop was pressed."""
474
+ if mode not in ("interactive", "schedule", "scripts"):
475
+ raise ValueError(f"unknown guard mode {mode!r}")
476
+ from . import owner_presence
477
+ return DetectGuard(device_id=device_id, usb=usb, emit=emit, wait_s=wait_s if mode == "interactive" else 0.0,
478
+ cancelled=cancelled,
479
+ owner_active=lambda: owner_presence.is_owner_active(wda_url))
480
+
481
+
482
+ def after_error(guard, driver):
483
+ """Why a task's step failed when the phone itself says so (cable, Wi-Fi, lock), else None; it only reads."""
484
+ if guard is None:
485
+ return None
486
+ from .agent_hooks import safe_attached
487
+ attached = safe_attached(guard)
488
+ if attached is False:
489
+ return GuardVerdict("stop", "unplugged", UNPLUGGED)
490
+ if attached is None:
491
+ try:
492
+ lost = bool(guard.wifi_lost())
493
+ except Exception:
494
+ lost = False
495
+ if lost:
496
+ return GuardVerdict("stop", "unplugged", WIFI_LOST)
497
+ if driver is not None and read_locked(driver) is True:
498
+ return GuardVerdict("stop", "phone_locked", LOCKED_DURING)
499
+ return None
500
+
501
+
502
+ def unlock_settings(record=None):
503
+ """The phone's unlock settings, as booleans: off, since this build cannot unlock."""
504
+ return {"enabled": False, "askBeforeUnlocking": True, "onSchedule": "never", "scripts": False,
505
+ "relockAfter": True, "needsCheck": False, "supported": CAN_UNLOCK}
506
+
507
+
508
+ def describe(probe_result):
509
+ """Plain lines for ``mobster devices lock-probe``."""
510
+ words = {"unlocked": "unlocked, no system sheet", "lock_screen": "locked (lock screen)",
511
+ "passcode_keypad": "locked (passcode keypad showing)", "face_id_sheet": "asking for Face ID",
512
+ "app_locked_sheet": "a locked app's sheet", "apple_confirmation": "asking to confirm with the side button",
513
+ "unknown": "not readable (WebDriverAgent didn't answer)"}
514
+ lines = [f"State: {words.get(probe_result['state'], probe_result['state'])}"]
515
+ if "coreDevice" in probe_result:
516
+ lines.append("CoreDevice lock state: " + {True: "locked", False: "unlocked", None: "not readable"}[
517
+ probe_result["coreDevice"]])
518
+ if probe_result.get("front"):
519
+ lines.append(f"Front app: {probe_result['front']}")
520
+ labels = probe_result.get("labels") or []
521
+ if labels:
522
+ lines.append(f"SpringBoard shows {len(labels)} labels:")
523
+ lines.extend(f" {item['type'] or '?'}: {item['label']}" for item in labels)
524
+ lines.append("Nothing was tapped or typed.")
525
+ return lines
526
+