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,225 @@
1
+ """The manager VerifyRun drives for an Android check: the simulator manager's interface over adb and one emulator or
2
+ phone. One manager serves one run; its lease is the emulator manager's."""
3
+
4
+ import re
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+ from ..sim.models import AppInfo, SimError
9
+ from . import adb as adb_module
10
+ from .adb import Adb, AdbError
11
+ from .conditions import AndroidConditions
12
+ from .driver import AndroidDriver
13
+ from .emulator import EmulatorManager
14
+
15
+ # Android release -> API level, for `runtime: Android 16`.
16
+ API_LEVELS = {"12": 31, "12L": 32, "13": 33, "14": 34, "15": 35, "16": 36}
17
+ BADGING_PACKAGE = re.compile(r"package: name='([^']+)'[^\n]*?versionName='([^']*)'")
18
+ BADGING_LABEL = re.compile(r"application-label:'([^']*)'")
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class AndroidRunTarget:
23
+ """What VerifyRun reads off a leased device. ``udid`` and the two addresses exist because the iOS side's log line
24
+ and report read them; Android has no WebDriverAgent."""
25
+ serial: str
26
+ name: str
27
+ kind: str
28
+ avd: str = ""
29
+ runtime: str = ""
30
+
31
+ @property
32
+ def udid(self):
33
+ return self.serial
34
+
35
+ @property
36
+ def device_type(self):
37
+ return self.avd or self.name
38
+
39
+ wda_url = ""
40
+ mjpeg_url = ""
41
+
42
+
43
+ def _normal(text):
44
+ return re.sub(r"[^a-z0-9]", "", str(text).casefold())
45
+
46
+
47
+ def avd_for(avds, device=None, runtime=None, api_of=lambda name: None):
48
+ """The AVD a check's ``device`` and ``runtime`` name: an exact name, else the first whose name starts with the
49
+ device, then the first on that Android release. None when nothing was asked for."""
50
+ chosen = list(avds)
51
+ if device:
52
+ wanted = _normal(device)
53
+ exact = [name for name in chosen if _normal(name) == wanted]
54
+ chosen = exact or [name for name in chosen if _normal(name).startswith(wanted)]
55
+ if runtime:
56
+ level = API_LEVELS.get(re.sub(r"(?i)^android\s*", "", runtime).strip())
57
+ if level is None:
58
+ raise SimError("environment", f"“{runtime}” is not an Android release Mobster knows "
59
+ f"({', '.join('Android ' + k for k in API_LEVELS)}).",
60
+ "Name the release, such as runtime: Android 16.")
61
+ chosen = [name for name in chosen if api_of(name) == level]
62
+ if (device or runtime) and not chosen:
63
+ raise SimError("environment", f"No Android virtual device matches {device or ''} {runtime or ''}".strip() + ".",
64
+ "`mobster sim android list` shows what exists; create one in Android Studio's Device Manager.")
65
+ return chosen[0] if (device or runtime) else None
66
+
67
+
68
+ class AndroidManager:
69
+ # Fail closed: a manager is a phone's until it holds an emulator (verify.smart.is_real_device).
70
+ real_device = True
71
+ platform = "android"
72
+ system_prefixes = ("com.android.", "com.google.android.", "android")
73
+
74
+ def __init__(self, data_dir, *, name=None, emulators=None, adb_factory=None, phones=False):
75
+ self.emulators = emulators or EmulatorManager(data_dir)
76
+ self.name = name or "" # an AVD name or an adb serial, from --android
77
+ # A check runs on an emulator only: Mobster can't yet tell someone's own builds from the apps on a phone. The
78
+ # device tools pass True; their own allowance decides which phone they may drive.
79
+ self.phones = phones
80
+ self._adb = adb_factory or (lambda serial: Adb(serial, self.emulators._run, self.emulators.adb.binary))
81
+
82
+ # -- the device
83
+
84
+ def _api_of(self, avd):
85
+ ini = Path.home() / ".android" / "avd" / f"{avd}.ini"
86
+ try:
87
+ match = re.search(r"target=android-(\d+)", ini.read_text())
88
+ except OSError:
89
+ return None
90
+ return int(match.group(1)) if match else None
91
+
92
+ def acquire(self, device=None, runtime=None, *, timeout=600, cancelled=None):
93
+ # A refusal here sends nothing, so the run's journal records it as rejected (verify/actions.py). ``cancelled``
94
+ # (the runner's stop flag) is read before a device is held; an emulator boot isn't interrupted.
95
+ from ..verify.actions import ActionNotDispatched
96
+ if cancelled is not None and cancelled():
97
+ raise ActionNotDispatched(SimError("environment", "The run was stopped before an Android device was held.",
98
+ ""))
99
+ serials = {row["serial"] for row in self.emulators.devices()}
100
+ serial = self.name if self.name in serials else None
101
+ avd = None if serial else (self.name or avd_for(self.emulators.avds(), device, runtime, self._api_of))
102
+ lease = self.emulators.acquire(avd=avd, serial=serial, timeout=timeout)
103
+ target = lease.target
104
+ if target.kind != "emulator" and not self.phones:
105
+ lease.release()
106
+ raise ActionNotDispatched(SimError(
107
+ "environment", f"“{target.name}” is an Android phone, and Mobster runs checks only on Android "
108
+ "emulators for now: it can't yet tell your own builds from the other apps on a phone.",
109
+ "Run the check on an emulator, such as mobster test --android Pixel_8."))
110
+ adb = self._adb(target.serial)
111
+ try:
112
+ release = adb.prop("ro.build.version.release")
113
+ except AdbError:
114
+ release = ""
115
+ lease.target = AndroidRunTarget(target.serial, target.name, target.kind, target.avd,
116
+ f"Android {release}" if release else "")
117
+ self.real_device = target.kind != "emulator"
118
+ self.adb = adb
119
+ return lease
120
+
121
+ def build_driver(self, target, run_dir, mode):
122
+ return AndroidDriver(target.serial, self._adb(target.serial))
123
+
124
+ def front_app(self, target):
125
+ return self._adb(target.serial).front_package() or None, None
126
+
127
+ def conditions(self, spec, target, bundle, actions=None):
128
+ return AndroidConditions(spec, self._adb(target.serial), bundle=bundle, emulator=target.kind == "emulator",
129
+ actions=actions)
130
+
131
+ # -- apps
132
+
133
+ def app_info(self, app_path):
134
+ path = Path(app_path)
135
+ if path.suffix.lower() != ".apk" or not path.is_file():
136
+ raise SimError("install", f"{app_path} is not an .apk file.", "Pass the .apk of the app the check names.")
137
+ aapt = adb_module.tool_in_build_tools("aapt2", self.emulators.root)
138
+ if aapt is None:
139
+ raise SimError("install", "Mobster can't read the package name of an .apk without aapt2.",
140
+ "Install Android SDK Build-Tools from the SDK Manager, or name the app with bundle: and "
141
+ "install it yourself.")
142
+ result = self.emulators._run([aapt, "dump", "badging", str(path)], 30, None)
143
+ text = result.text
144
+ match = BADGING_PACKAGE.search(text)
145
+ if result.code != 0 or not match:
146
+ raise SimError("install", f"aapt2 could not read {path.name}.", "Check that the file is a built APK.")
147
+ label = BADGING_LABEL.search(text)
148
+ return AppInfo(bundle_id=match.group(1), name=label.group(1) if label else match.group(1),
149
+ version=match.group(2), path=str(path))
150
+
151
+ def installed_app(self, target, bundle_id):
152
+ try:
153
+ version = self._adb(target.serial).installed_version(bundle_id)
154
+ except AdbError as error:
155
+ raise SimError("install", str(error)) from None
156
+ if version is None:
157
+ raise SimError("install", f"{bundle_id} is not installed on {target.name}.",
158
+ "Pass app.path with the .apk, or install the app first.")
159
+ return AppInfo(bundle_id=bundle_id, name=bundle_id, version=version, path=None)
160
+
161
+ def install(self, target, app_path):
162
+ info = self.app_info(app_path)
163
+ try:
164
+ self._adb(target.serial).install(app_path)
165
+ except AdbError as error:
166
+ raise SimError("install", str(error)) from None
167
+ return info
168
+
169
+ def reset(self, target, bundle_id, level, app_path=None):
170
+ if self.real_device and level != "none":
171
+ raise SimError("install", f"Mobster never clears or reinstalls an app on a phone, so it didn't reset "
172
+ f"{bundle_id} ({level}).", "Use reset: none on a phone, or run the check on an emulator.")
173
+ adb = self._adb(target.serial)
174
+ try:
175
+ if level == "reinstall":
176
+ adb.uninstall(bundle_id)
177
+ adb.install(app_path)
178
+ elif level == "data":
179
+ adb.clear_data(bundle_id)
180
+ elif level != "none":
181
+ raise SimError("install", f"Mobster can't reset {bundle_id} at level {level!r} on Android.",
182
+ "Use reset: none, data or reinstall.")
183
+ except AdbError as error:
184
+ raise SimError("install", str(error)) from None
185
+
186
+ def launch(self, target, bundle_id, args=(), env=None):
187
+ if args or env:
188
+ raise SimError("launch", "Launch arguments and environment aren't supported on Android.",
189
+ "Remove launch.args and launch.env from the check.")
190
+ adb = self._adb(target.serial)
191
+ try:
192
+ adb.stop(bundle_id)
193
+ adb.launch(bundle_id)
194
+ except AdbError as error:
195
+ raise SimError("launch", str(error)) from None
196
+
197
+ def terminate(self, target, bundle_id):
198
+ try:
199
+ self._adb(target.serial).stop(bundle_id)
200
+ except AdbError as error:
201
+ raise SimError("launch", str(error)) from None
202
+
203
+ def open_url(self, target, url):
204
+ try:
205
+ self._adb(target.serial).open_url(url)
206
+ except AdbError as error:
207
+ raise SimError("launch", str(error)) from None
208
+
209
+ def screenshot(self, target, path, *, max_width=None, quality=75):
210
+ from io import BytesIO
211
+
212
+ from PIL import Image
213
+ path = Path(path)
214
+ path.parent.mkdir(parents=True, exist_ok=True)
215
+ try:
216
+ data = self._adb(target.serial).screenshot_png(30)
217
+ except AdbError as error:
218
+ raise SimError("simulator", f"The screenshot failed: {error}") from None
219
+ with Image.open(BytesIO(data)) as image:
220
+ image = image.convert("RGB")
221
+ if max_width and image.width > max_width:
222
+ image = image.resize((int(max_width), max(1, round(image.height * max_width / image.width))),
223
+ Image.LANCZOS)
224
+ image.save(path, "JPEG", quality=int(quality), optimize=True)
225
+ return path
@@ -0,0 +1,31 @@
1
+ """Android devices as the device records `list_devices` and the device picker use (devices.discover's shape)."""
2
+
3
+ from . import enabled
4
+
5
+
6
+ def records(data_dir=None):
7
+ """One record per adb device, [] when Android is off or adb fails. ``emulator`` marks a device that is not
8
+ someone's phone: it is handled like an iOS simulator everywhere a record's kind is read."""
9
+ if not enabled():
10
+ return []
11
+ from ..paths import dev_data_dir
12
+ from .emulator import EmulatorManager
13
+ try:
14
+ rows = EmulatorManager(data_dir or dev_data_dir()).devices()
15
+ except Exception:
16
+ return []
17
+ out = []
18
+ for row in rows:
19
+ state = {"device": "ready", "offline": "disconnected",
20
+ "unauthorized": "needs_setup"}.get(row["state"], "unknown")
21
+ out.append({"id": f"android:{row['serial']}", "kind": "android", "platform": "android", "name": row["name"],
22
+ "model": row["name"], "serial": row["serial"], "avd": row["avd"] or None, "state": state,
23
+ "emulator": row["kind"] == "emulator",
24
+ "reason": "unlock the phone and accept the USB debugging prompt"
25
+ if row["state"] == "unauthorized" else None})
26
+ return out
27
+
28
+
29
+ def is_phone(record):
30
+ """Someone's own device: anything that is not an iOS simulator or an Android emulator."""
31
+ return record.get("kind") != "simulator" and not record.get("emulator")
@@ -0,0 +1,96 @@
1
+ """uiautomator's hierarchy as the XML the iOS side reads, so verify, replay and the agent need no Android branch.
2
+
3
+ Roles take XCUIElementType names, resource-id becomes the identifier, text and content-desc the label, and frames are
4
+ dp (pixels over density/160), so the point-based thresholds downstream mean the same thing on both platforms.
5
+ """
6
+
7
+ import re
8
+ from xml.etree import ElementTree as ET
9
+
10
+ from ..state import _parse_wda
11
+
12
+ BOUNDS = re.compile(r"\[(-?\d+),(-?\d+)\]\[(-?\d+),(-?\d+)\]")
13
+ SYSTEM_UI = "com.android.systemui"
14
+ # Longest class suffix first wins; an unknown class is "Other".
15
+ ROLES = (
16
+ ("EditText", "TextField"), ("AutoCompleteTextView", "TextField"), ("SearchView", "SearchField"),
17
+ ("ImageButton", "Button"), ("Button", "Button"), ("Spinner", "Button"),
18
+ ("CheckBox", "Switch"), ("RadioButton", "Switch"), ("Switch", "Switch"), ("ToggleButton", "Switch"),
19
+ ("TextView", "StaticText"), ("ImageView", "Image"), ("SeekBar", "Slider"), ("ProgressBar", "ProgressIndicator"),
20
+ ("WebView", "WebView"), ("RecyclerView", "ScrollView"), ("ScrollView", "ScrollView"), ("ListView", "Table"),
21
+ ("GridView", "CollectionView"), ("TabWidget", "TabBar"), ("Toolbar", "Toolbar"),
22
+ )
23
+ TOGGLE_CLASSES = ("CheckBox", "RadioButton", "Switch", "ToggleButton")
24
+
25
+
26
+ def role_of(cls, password=False):
27
+ if password:
28
+ return "SecureTextField"
29
+ short = (cls or "").rsplit(".", 1)[-1]
30
+ for suffix, role in ROLES:
31
+ if short.endswith(suffix):
32
+ return role
33
+ return "Other"
34
+
35
+
36
+ def identifier(resource_id):
37
+ """"com.android.settings:id/search_action_bar" is "search_action_bar"."""
38
+ return (resource_id or "").rsplit("/", 1)[-1]
39
+
40
+
41
+ def front_package(root):
42
+ """The package of the app in front: the first one that is not System UI, else System UI."""
43
+ packages = [node.get("package") for node in root.iter("node") if node.get("package")]
44
+ return next((name for name in packages if name != SYSTEM_UI), packages[0] if packages else "")
45
+
46
+
47
+ def to_wda(xml, width_px, height_px, density):
48
+ """The hierarchy XML as WDA-shaped XML text. ``density`` is dpi; raises ValueError on an unusable tree."""
49
+ root = _parse_wda(xml)
50
+ if root.tag != "hierarchy":
51
+ raise ValueError("Not a uiautomator hierarchy")
52
+ scale = max(density, 1) / 160
53
+ out = ET.Element("XCUIElementTypeApplication", {
54
+ "type": "XCUIElementTypeApplication", "bundleId": front_package(root), "x": "0", "y": "0",
55
+ "width": _num(width_px / scale), "height": _num(height_px / scale)})
56
+ window = ET.SubElement(out, "XCUIElementTypeWindow", {"x": "0", "y": "0", "width": out.get("width"),
57
+ "height": out.get("height")})
58
+ for child in root.findall("node"):
59
+ _convert(child, window, scale)
60
+ return ET.tostring(out, encoding="unicode")
61
+
62
+
63
+ def _num(value):
64
+ return f"{value:.2f}".rstrip("0").rstrip(".")
65
+
66
+
67
+ def _convert(node, parent, scale):
68
+ cls = node.get("class") or ""
69
+ password = node.get("password") == "true"
70
+ role = role_of(cls, password)
71
+ match = BOUNDS.fullmatch(node.get("bounds") or "")
72
+ left, top, right, bottom = (int(n) for n in match.groups()) if match else (0, 0, 0, 0)
73
+ text, desc = node.get("text") or "", node.get("content-desc") or ""
74
+ editable = role in {"TextField", "SecureTextField", "SearchField"}
75
+ toggle = cls.rsplit(".", 1)[-1].endswith(TOGGLE_CLASSES)
76
+ attrs = {"x": _num(left / scale), "y": _num(top / scale), "width": _num(max(right - left, 0) / scale),
77
+ "height": _num(max(bottom - top, 0) / scale),
78
+ "enabled": "true" if node.get("enabled") != "false" else "false"}
79
+ attrs["label"] = desc or ("" if editable else text)
80
+ # An unlabelled icon's id ("icon") would be read as its label, so only text-bearing nodes keep a bare id.
81
+ attrs["name"] = attrs["label"] if role == "Image" and not attrs["label"] else (
82
+ identifier(node.get("resource-id")) or attrs["label"])
83
+ if editable:
84
+ attrs["value"] = "" if password else text
85
+ if node.get("hint"):
86
+ attrs["placeholderValue"] = node.get("hint")
87
+ elif toggle:
88
+ attrs["value"] = "1" if node.get("checked") == "true" else "0"
89
+ traits = ["Selected"] if node.get("selected") == "true" or (toggle and node.get("checked") == "true") else []
90
+ if traits:
91
+ attrs["traits"] = ", ".join(traits)
92
+ if node.get("clickable") != "true" and role == "Other" and not attrs["label"]:
93
+ attrs["accessible"] = "false"
94
+ element = ET.SubElement(parent, f"XCUIElementType{role}", attrs)
95
+ for child in node.findall("node"):
96
+ _convert(child, element, scale)
@@ -0,0 +1,230 @@
1
+ """The model's answer as result blocks: the Markdown tables, check lines and dated event lines in a DONE answer become
2
+ table, checklist and timeline blocks (``from_answer``), and the prompt line that asks for those shapes
3
+ (``ANSWER_SHAPES``, ``wants_shapes``). This changes with the prompt and the models' output habits; the block schema and
4
+ its one validation gate stay in ``result_blocks``.
5
+ """
6
+
7
+ import re
8
+
9
+ from .result_blocks import (
10
+ MAX_OPTIONS,
11
+ MAX_SERIES,
12
+ NUMERIC_COLUMNS,
13
+ STATUSES,
14
+ BlockError,
15
+ checklist,
16
+ status_word,
17
+ table,
18
+ timeline,
19
+ validate_block,
20
+ )
21
+
22
+ ANSWER_SHAPES = ("\n\nIn your DONE answer, start with one sentence that answers the request. When you checked or "
23
+ "tested several screens or things, then write one line per check, - [pass] what you checked: what "
24
+ "you saw (or [fail], [unsure]), and no table. Otherwise write several things that share the same "
25
+ "details as a Markdown table (a header row, then one row each). For a chart or a graph, write its "
26
+ "numbers as that table, labels or dates first: Mobster draws it. For places, give the table a Name "
27
+ "and an Address column. Write events in time order as one line each, - <time or date>: what "
28
+ "happened.")
29
+ WANTS_SHAPES = re.compile(r"\b(list|lists|table|compare|comparison|each|every|all (?:my|the|of)|check|checks|test|go "
30
+ r"through|rank|ranking|top \d+|which (?:ones|of)|summari[sz]e|breakdown|report|chart|graph|"
31
+ r"plot|visuali[sz]e|trend|map|nearby|near me|timeline|itinerary|schedule|history|vs\.?|"
32
+ r"versus)\b", re.I)
33
+ CHART_WORDS = re.compile(r"\b(chart|graph|plot|visuali[sz]e|trend|trends|over time|histogram|pie|scatter)\b", re.I)
34
+ PIE_WORDS = re.compile(r"\b(pie|donut|share|split|breakdown|proportion|percent of|makeup)\b", re.I)
35
+ LINE_WORDS = re.compile(r"\b(line|trend|trends|over time|by (?:day|week|month|year)|daily|weekly|monthly|history)\b",
36
+ re.I)
37
+ SCATTER_WORDS = re.compile(r"\b(scatter|correlat\w*|relationship between)\b", re.I)
38
+ COMPARE_WORDS = re.compile(r"\b(compare|comparison|vs\.?|versus|which (?:one |is )?(?:is )?better|pros and cons|"
39
+ r"difference between|side by side)\b", re.I)
40
+ MAP_WORDS = re.compile(r"\b(map|maps|near|nearby|near me|where|directions|around|closest|places)\b", re.I)
41
+ ADDRESS_LABELS = re.compile(r"^(address|location|where|place|street|addr\.?)$", re.I)
42
+ LAT_LABELS = re.compile(r"^(lat|latitude)$", re.I)
43
+ LON_LABELS = re.compile(r"^(lon|lng|long|longitude)$", re.I)
44
+
45
+
46
+ def wants_shapes(request):
47
+ """Whether the stable prompt asks this request's answer for tables and check lines."""
48
+ return bool(WANTS_SHAPES.search(str(request or "")))
49
+
50
+
51
+ _ROW = re.compile(r"^\s*\|(.+)\|\s*$")
52
+ _RULE = re.compile(r"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$")
53
+ _CHECK = re.compile(r"^\s*[-*]\s*(?:\[(pass|passed|ok|fail|failed|unsure|uncertain|unknown)\]|(✅|❌|⚠️?))\s*(.+?)\s*$",
54
+ re.I)
55
+ _HEADING = re.compile(r"^\s*#{1,4}\s+(.+?)\s*#*\s*$")
56
+ _NUMBER = re.compile(r"^[-+]?[$€£¥]?\s?\d[\d,]*(\.\d+)?\s?(%|[kKmM])?$")
57
+ _DATE = re.compile(r"^\d{4}-\d{2}-\d{2}([T ]\d{2}:\d{2}(:\d{2})?)?$")
58
+
59
+
60
+ def _split_row(line):
61
+ cells, current, escaped = [], "", False
62
+ for char in _ROW.match(line).group(1):
63
+ if escaped:
64
+ current += char
65
+ escaped = False
66
+ elif char == "\\":
67
+ escaped = True
68
+ elif char == "|":
69
+ cells.append(current.strip())
70
+ current = ""
71
+ else:
72
+ current += char
73
+ cells.append(current.strip())
74
+ return [re.sub(r"^\*\*(.*)\*\*$", r"\1", cell) for cell in cells]
75
+
76
+
77
+ def _column_type(values):
78
+ values = [value for value in values if value]
79
+ if not values:
80
+ return "text"
81
+ if all(_NUMBER.match(value) for value in values):
82
+ if all("%" in value for value in values):
83
+ return "percent"
84
+ if any(symbol in value for value in values for symbol in "$€£¥"):
85
+ return "money"
86
+ return "number" if not any(value[-1:] in "kKmM" for value in values) else "text"
87
+ if all(_DATE.match(value) for value in values):
88
+ return "date"
89
+ if all(value.startswith("https://") and " " not in value for value in values):
90
+ return "link"
91
+ if all(status_word(value) in STATUSES for value in values):
92
+ return "status"
93
+ return "text"
94
+
95
+
96
+ def _title_before(lines, index):
97
+ """A heading or a line ending in ":" right above ``index`` (blank lines between allowed): (title, its index)."""
98
+ at = index - 1
99
+ while at >= 0 and not lines[at].strip():
100
+ at -= 1
101
+ if at < 0:
102
+ return None, None
103
+ heading = _HEADING.match(lines[at])
104
+ text = heading.group(1) if heading else lines[at].strip()[:-1] if lines[at].strip().endswith(":") and len(
105
+ lines[at].strip()) <= 120 and not lines[at].lstrip().startswith(("-", "*", "|")) else None
106
+ return (text.strip("* ") or None, at) if text else (None, None)
107
+
108
+
109
+ _WHEN = (r"(?:\d{1,2}(?::\d{2})?\s?(?:[AaPp]\.?[Mm]\.?)|\d{1,2}:\d{2}|\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2})?|"
110
+ r"(?:Mon|Tue|Tues|Wed|Thu|Thur|Thurs|Fri|Sat|Sun)[a-z]*\.?(?:,? \d{1,2}(?::\d{2})?\s?(?:[AaPp][Mm])?)?|"
111
+ r"(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]*\.? \d{1,2}(?:, \d{4})?|"
112
+ r"(?:Day|Week) \d{1,3}|(?:19|20)\d{2}|Today|Tonight|Tomorrow|Yesterday|Morning|Noon|Afternoon|Evening)")
113
+ _EVENT = re.compile(r"^\s*[-*]\s+(?:\*\*)?(" + _WHEN + r"(?:\s?[-–]\s?" + _WHEN + r")?)"
114
+ r"(?:\*\*)?\s*(?::|—|–| - )\s*(.+?)\s*$")
115
+
116
+
117
+ def _event(line):
118
+ """(at, title, detail) of a "- 9:00 AM: Boarded the train. Seat 14" line, or None."""
119
+ match = _EVENT.match(line)
120
+ if not match:
121
+ return None
122
+ text = match.group(2).strip()
123
+ title, _, detail = text.partition(". ")
124
+ if len(title) > 120:
125
+ title, detail = text[:117].rstrip() + "…", text
126
+ return match.group(1), title.strip("* ").rstrip("."), detail.strip() or None
127
+
128
+
129
+ def _table_view(block, request):
130
+ """The view a table opens in, from what the request asked: its chart, its map or its options side by side."""
131
+ columns, rows = block["columns"], block["rows"]
132
+ words = str(request or "")
133
+ labels = [c["label"].strip() for c in columns]
134
+ numeric = [c for c in columns if c.get("type") in NUMERIC_COLUMNS]
135
+ if (any(ADDRESS_LABELS.match(l) for l in labels) or (any(LAT_LABELS.match(l) for l in labels)
136
+ and any(LON_LABELS.match(l) for l in labels))) \
137
+ and MAP_WORDS.search(words):
138
+ block["view"] = "map"
139
+ return block
140
+ if CHART_WORDS.search(words) and numeric and len(rows) >= 2:
141
+ first = next((c for c in columns if c not in numeric), None)
142
+ if SCATTER_WORDS.search(words) and len(numeric) >= 2:
143
+ chart = {"kind": "scatter", "x": numeric[0]["key"], "y": [numeric[1]["key"]]}
144
+ else:
145
+ if PIE_WORDS.search(words) and len(rows) <= 8 and all(
146
+ isinstance(r["cells"].get(numeric[0]["key"]), (int, float)) and r["cells"][numeric[0]["key"]] >= 0
147
+ for r in rows):
148
+ kind = "pie"
149
+ elif (first is not None and first.get("type") == "date") or LINE_WORDS.search(words):
150
+ kind = "area" if re.search(r"\barea\b", words, re.I) else "line"
151
+ else:
152
+ kind = "bar"
153
+ chart = {"kind": kind, "y": [c["key"] for c in numeric][:1 if kind == "pie" else MAX_SERIES]}
154
+ if first is not None:
155
+ chart["x"] = first["key"]
156
+ block["chart"], block["view"] = chart, "chart"
157
+ return block
158
+ if COMPARE_WORDS.search(words) and 2 <= len(rows) <= MAX_OPTIONS and len(columns) >= 3:
159
+ block["view"] = "compare"
160
+ return block
161
+
162
+
163
+ def from_answer(text, request=None):
164
+ if not isinstance(text, str):
165
+ return text, []
166
+ lines, blocks, drop, index = text.split("\n"), [], set(), 0
167
+ while index < len(lines):
168
+ if _ROW.match(lines[index]) and index + 1 < len(lines) and _RULE.match(lines[index + 1]):
169
+ end = index + 2
170
+ while end < len(lines) and _ROW.match(lines[end]):
171
+ end += 1
172
+ header, body = _split_row(lines[index]), [_split_row(line) for line in lines[index + 2:end]]
173
+ if body and len(header) >= 2:
174
+ width = len(header)
175
+ body = [(row + [""] * width)[:width] for row in body]
176
+ kinds = [_column_type([row[i] for row in body]) for i in range(width)]
177
+ title, at = _title_before(lines, index)
178
+ columns = [{"label": label or f"Column {i + 1}", "type": kinds[i]} for i, label in enumerate(header)]
179
+ try:
180
+ shaped = validate_block(table(columns, body, title=title, id=f"t{len(blocks) + 1}"))
181
+ except BlockError: # a table a block can't hold (17+ columns) stays in the answer as text
182
+ index = end
183
+ continue
184
+ blocks.append((index, _table_view(shaped, request) if request else shaped))
185
+ drop.update(range(index, end))
186
+ if at is not None:
187
+ drop.add(at)
188
+ index = end
189
+ continue
190
+ if _event(lines[index]):
191
+ end = index
192
+ while end < len(lines) and _event(lines[end]):
193
+ end += 1
194
+ if end - index >= 3:
195
+ items = []
196
+ for line in lines[index:end]:
197
+ at, title, detail = _event(line)
198
+ items.append({"at": at, "title": title, "detail": detail})
199
+ title, at = _title_before(lines, index)
200
+ blocks.append((index, timeline(items, title=title, id=f"e{len(blocks) + 1}")))
201
+ drop.update(range(index, end))
202
+ if at is not None:
203
+ drop.add(at)
204
+ index = end
205
+ continue
206
+ if _CHECK.match(lines[index]):
207
+ end = index
208
+ while end < len(lines) and _CHECK.match(lines[end]):
209
+ end += 1
210
+ if end - index >= 2:
211
+ items = []
212
+ for line in lines[index:end]:
213
+ match = _CHECK.match(line)
214
+ word = (match.group(1) or {"✅": "pass", "❌": "fail"}.get(match.group(2), "uncertain")).lower()
215
+ label, _, observed = match.group(3).partition(": ")
216
+ items.append({"label": label.strip("* ") or match.group(3), "status": status_word(word),
217
+ "observed": observed.strip() or None})
218
+ title, at = _title_before(lines, index)
219
+ blocks.append((index, checklist(items, title=title, id=f"c{len(blocks) + 1}")))
220
+ drop.update(range(index, end))
221
+ if at is not None:
222
+ drop.add(at)
223
+ index = end
224
+ continue
225
+ index += 1
226
+ if not blocks:
227
+ return text, []
228
+ rest = "\n".join(line for i, line in enumerate(lines) if i not in drop)
229
+ rest = re.sub(r"\n{3,}", "\n\n", rest).strip()
230
+ return rest, [block for _, block in sorted(blocks, key=lambda pair: pair[0])]