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,168 @@
1
+ """Run-owned app-state fixture in one enrolled simulator container.
2
+
3
+ Only the fixed Documents/mobster-business.json file is touched. Descriptor-
4
+ relative, no-follow access closes the gap between container validation and I/O.
5
+ """
6
+
7
+ import hashlib
8
+ import json
9
+ import os
10
+ import stat
11
+ from contextlib import contextmanager
12
+ from pathlib import Path
13
+
14
+ from ..sim.models import SimError
15
+ from .assertions import CheckError
16
+ from .business import CONTRACT, FILENAME, strict_json, valid_scalar
17
+
18
+ MAX_SNAPSHOT = 16_384
19
+
20
+
21
+ class FixtureError(RuntimeError):
22
+ pass
23
+
24
+
25
+ @contextmanager
26
+ def _documents(container):
27
+ path = Path(container)
28
+ if not path.is_absolute():
29
+ raise FixtureError("The simulator app container was not absolute")
30
+ fd = os.open("/", os.O_RDONLY | os.O_DIRECTORY)
31
+ try:
32
+ for part in (*path.parts[1:], "Documents"):
33
+ next_fd = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
34
+ os.close(fd)
35
+ fd = next_fd
36
+ yield fd
37
+ finally:
38
+ os.close(fd)
39
+
40
+
41
+ def _read(fd):
42
+ source = os.open(FILENAME, os.O_RDONLY | os.O_NONBLOCK | os.O_NOFOLLOW, dir_fd=fd)
43
+ with os.fdopen(source, "rb") as handle:
44
+ info = os.fstat(handle.fileno())
45
+ if not stat.S_ISREG(info.st_mode) or info.st_size > MAX_SNAPSHOT:
46
+ raise FixtureError("The app-state snapshot is not a bounded regular file")
47
+ raw = handle.read(MAX_SNAPSHOT + 1)
48
+ if len(raw) > MAX_SNAPSHOT:
49
+ raise FixtureError("The app-state snapshot is too large")
50
+ return raw, (info.st_dev, info.st_ino)
51
+
52
+
53
+ def _decode(raw, token, binding, *, produced):
54
+ try:
55
+ data = strict_json(raw)
56
+ except (CheckError, ValueError, UnicodeError, RecursionError):
57
+ raise FixtureError("The app-state snapshot is not valid JSON") from None
58
+ if (not isinstance(data, dict) or set(data) != {"schema", "runToken", "generation", "state"}
59
+ or data["schema"] != CONTRACT or data["runToken"] != token
60
+ or type(data["generation"]) is not int or data["generation"] < (1 if produced else 0)
61
+ or not isinstance(data["state"], dict) or set(data["state"]) != set(binding.fields)
62
+ or any(not valid_scalar(value) for value in data["state"].values())):
63
+ raise FixtureError("The app-state snapshot is stale or has the wrong schema")
64
+ return data
65
+
66
+
67
+ class AppContainerFixture:
68
+ def __init__(self, binding, manager, target, actions, run_id, token):
69
+ self.binding, self.manager, self.target, self.actions = binding, manager, target, actions
70
+ self.run_id, self.token = run_id, token
71
+ self.container = None
72
+ self.documents_identity = None
73
+ self.seeded = False
74
+ self.cleanup = "not_needed"
75
+
76
+ def _container(self):
77
+ if getattr(self.manager, "real_device", False):
78
+ raise FixtureError("App-state fixtures require a managed simulator")
79
+ try:
80
+ result = self.manager.simctl("get_app_container", self.target.udid, self.binding.bundle, "data")
81
+ if not result.ok:
82
+ raise FixtureError("The simulator app data container is unavailable")
83
+ from ..sim.apps import guard_container
84
+ return guard_container(result.out.strip(), self.target.udid)
85
+ except SimError:
86
+ raise FixtureError("The simulator app data container is not safely accessible") from None
87
+
88
+ def quiesce(self):
89
+ """A failed termination is safe only when simctl explicitly says the app is not running."""
90
+ result = self.manager.simctl("terminate", self.target.udid, self.binding.bundle, timeout=30)
91
+ if result.ok:
92
+ return
93
+ detail = " ".join((result.err or "", result.out or "")).lower()
94
+ if result.code is not None and "not running" in detail:
95
+ return
96
+ raise FixtureError("The simulator could not confirm the app was stopped")
97
+
98
+ @contextmanager
99
+ def _open_checked(self):
100
+ container = self._container()
101
+ if self.container is not None and container != self.container:
102
+ raise FixtureError("The simulator app container changed during the run")
103
+ with _documents(container) as fd:
104
+ identity = (os.fstat(fd).st_dev, os.fstat(fd).st_ino)
105
+ if self.documents_identity is not None and identity != self.documents_identity:
106
+ raise FixtureError("The app Documents folder changed during the run")
107
+ yield fd, container, identity
108
+
109
+ def setup(self):
110
+ with self._open_checked() as (fd, container, identity):
111
+ try:
112
+ _read(fd)
113
+ except FileNotFoundError:
114
+ pass
115
+ else:
116
+ raise FixtureError("The fixture file already exists; inspect and reconcile it before a new run")
117
+ payload = json.dumps({"schema": CONTRACT, "runToken": self.token, "generation": 0,
118
+ "state": self.binding.seed_state()}, sort_keys=True,
119
+ separators=(",", ":")).encode("utf-8")
120
+ temporary = f".{FILENAME}.{self.token}.tmp"
121
+ with self.actions.action("BUSINESS_SETUP", operation_id=f"{self.run_id}:business:setup"):
122
+ descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW,
123
+ 0o600, dir_fd=fd)
124
+ try:
125
+ with os.fdopen(descriptor, "wb") as destination:
126
+ destination.write(payload)
127
+ destination.flush()
128
+ os.fsync(destination.fileno())
129
+ os.link(temporary, FILENAME, src_dir_fd=fd, dst_dir_fd=fd, follow_symlinks=False)
130
+ self.container = container
131
+ self.documents_identity = identity
132
+ self.seeded = True
133
+ self.cleanup = "pending"
134
+ os.fsync(fd)
135
+ finally:
136
+ os.unlink(temporary, dir_fd=fd)
137
+
138
+ def observe(self):
139
+ if not self.seeded:
140
+ raise FixtureError("The fixture was not seeded")
141
+ with self._open_checked() as (fd, _, _):
142
+ raw, _ = _read(fd)
143
+ data = _decode(raw, self.token, self.binding, produced=True)
144
+ return data["state"], data["generation"], hashlib.sha256(raw).hexdigest()
145
+
146
+ def remove(self):
147
+ if not self.seeded:
148
+ return self.cleanup
149
+ if self.actions.blocked:
150
+ self.cleanup = "needs_reconciliation"
151
+ return self.cleanup
152
+ try:
153
+ with self.actions.action("BUSINESS_QUIESCE", operation_id=f"{self.run_id}:business:quiesce"):
154
+ self.quiesce()
155
+ with self._open_checked() as (fd, _, _):
156
+ raw, identity = _read(fd)
157
+ _decode(raw, self.token, self.binding, produced=False)
158
+ current = os.stat(FILENAME, dir_fd=fd, follow_symlinks=False)
159
+ if (current.st_dev, current.st_ino) != identity:
160
+ raise FixtureError("The app-state file changed before cleanup")
161
+ with self.actions.action("BUSINESS_CLEANUP", operation_id=f"{self.run_id}:business:cleanup"):
162
+ os.unlink(FILENAME, dir_fd=fd)
163
+ os.fsync(fd)
164
+ self.cleanup = "removed"
165
+ except BaseException:
166
+ self.cleanup = "needs_reconciliation"
167
+ raise
168
+ return self.cleanup
@@ -0,0 +1,398 @@
1
+ """The JSON Schema of a check file, built from the loader's names; never stricter than check_from_dict, the judge."""
2
+
3
+ from .. import links
4
+ from . import assertions, business, checks, conditions, dialogs, fixtures, netstubs, snapshots
5
+ from .a11y import NAME as A11Y
6
+ from .audit import AUDITS
7
+ from .device_audit import NAME as AX_DEVICE
8
+
9
+ DRAFT = "https://json-schema.org/draft/2020-12/schema"
10
+ NOT_BLANK = r"\S"
11
+ LIMITS = checks.LIMITS
12
+
13
+
14
+ def ref(name, description=None):
15
+ """A reference to ``$defs/name``, with this place's own description."""
16
+ return {"$ref": f"#/$defs/{name}", **({"description": description} if description else {})}
17
+
18
+
19
+ def nullable(schema):
20
+ """``schema`` or null: the loader reads a null value as the key left out."""
21
+ return {"anyOf": [schema, {"type": "null"}]}
22
+
23
+
24
+ def given(*keys):
25
+ """At least one of ``keys`` holds text that isn't blank: the loader's "needs bundle or path"."""
26
+ return {"anyOf": [{"required": [key], "properties": {key: {"type": "string", "pattern": NOT_BLANK}}}
27
+ for key in keys]}
28
+
29
+
30
+ def _assertion(kind):
31
+ """One kind of assertion: exactly its kind key, the bounds it takes, and an optional name."""
32
+ selector = {"$ref": "#/$defs/selector"}
33
+ shapes = {
34
+ "text": ({"text": ref("texts", "Holds when a shown element's label or value contains this text, ignoring "
35
+ "case. A list makes one assertion per string.")}, ["text"]),
36
+ "no_text": ({"no_text": ref("texts", "Holds when no shown element's label or value contains this text. A "
37
+ "list makes one assertion per string.")}, ["no_text"]),
38
+ "visible": ({"visible": {**selector, "description": "Holds when at least one shown element matches."}},
39
+ ["visible"]),
40
+ "absent": ({"absent": {**selector, "description": "Holds when no shown element matches."}}, ["absent"]),
41
+ "value": ({"value": {**selector, "description": "Holds when exactly one shown element matches and its value "
42
+ "equals equals."},
43
+ "equals": ref("expected")}, ["value", "equals"]),
44
+ "count": ({"count": {**selector, "description": "Holds when the number of shown elements that match meets "
45
+ "the bound."},
46
+ "equals": ref("whole", "Exactly this many."),
47
+ "at_least": ref("whole", "This many or more."),
48
+ "at_most": ref("whole", "This many or fewer.")}, ["count"]),
49
+ }
50
+ properties, required = shapes[kind]
51
+ out = {"type": "object", "required": required, "additionalProperties": False,
52
+ "properties": {**properties, "name": nullable(ref("assertionName"))}}
53
+ if kind == "count":
54
+ out["oneOf"] = [{"required": [bound]} for bound in assertions.BOUNDS]
55
+ return out
56
+
57
+
58
+ def _or_list(item, description):
59
+ """A matrix condition: one value, or a list that makes the check run once per combination."""
60
+ return {"anyOf": [item, {"type": "array", "minItems": 1, "items": item}],
61
+ "description": description + " A list runs the check once for each value."}
62
+
63
+
64
+ def _assertions(description, item):
65
+ return nullable({"type": "array", "maxItems": LIMITS["expect"], "items": ref(item), "description": description})
66
+
67
+
68
+ def _number(limit, description):
69
+ return nullable({"type": "number", "exclusiveMinimum": 0, "maximum": limit, "description": description})
70
+
71
+
72
+ def _defs():
73
+ audits = [*AUDITS, A11Y, AX_DEVICE]
74
+ return {
75
+ "matcher": {"type": "string", "minLength": 1, "maxLength": 2000,
76
+ "description": "Exact text, or a /regex/ (/pattern/i ignores case)."},
77
+ "texts": {"anyOf": [ref("matcher"), {"type": "array", "minItems": 1, "items": ref("matcher")}]},
78
+ "selector": {
79
+ "type": "object",
80
+ "description": "The elements to find: every field given must match.",
81
+ "minProperties": 1,
82
+ "additionalProperties": False,
83
+ "properties": {
84
+ "id": ref("matcher", "The accessibility identifier, exact or a /regex/."),
85
+ "label": ref("matcher", "The accessibility label, exact or a /regex/."),
86
+ "value": ref("matcher", "The accessibility value, exact or a /regex/. A switch's value is 1 or 0."),
87
+ "role": {"description": "A role such as button, text or field, or any XCUIElementType name.",
88
+ "anyOf": [{"enum": [*assertions.FRIENDLY_ROLES, *sorted(assertions.ELEMENT_TYPES)]},
89
+ {"type": "string", "pattern": NOT_BLANK}]},
90
+ "enabled": {"type": "boolean", "description": "Whether the element is enabled."},
91
+ "selected": {"type": "boolean", "description": "Whether the element has the Selected trait."},
92
+ },
93
+ },
94
+ "assertionName": {"type": "string", "maxLength": assertions.NAME_MAX, "pattern": NOT_BLANK,
95
+ "description": f"A label for this assertion in reports, up to {assertions.NAME_MAX} "
96
+ "characters."},
97
+ "expected": {"description": "What the value must equal: text, a number, or true or false (1, on and yes "
98
+ "are true).",
99
+ "anyOf": [{"type": "string", "maxLength": 2000}, {"type": "number"}, {"type": "boolean"}]},
100
+ "whole": {"type": "integer", "minimum": 0},
101
+ "snapshot": {
102
+ "type": "object", "required": ["snapshot"], "additionalProperties": False,
103
+ "description": "Compares the final screen to the baseline in .mobster/snapshots/, one per check.",
104
+ "properties": {
105
+ "snapshot": {"type": "string", "pattern": rf"^{snapshots.NAME.pattern}$",
106
+ "description": "The screen's name, which names the baseline file."},
107
+ "mask": {"type": "array", "maxItems": snapshots.MASK_LIMIT,
108
+ "description": "Regions the diff ignores: elements by id or label, or a rect in points.",
109
+ "items": {"type": "object", "minProperties": 1,
110
+ "properties": {"id": {"type": "string"}, "label": {"type": "string"},
111
+ "rect": {"type": "array", "minItems": 4, "maxItems": 4,
112
+ "items": {"type": "number"}}}}},
113
+ "threshold": {"type": "number", "minimum": 0, "maximum": 100,
114
+ "description": f"The percent of the screen that may change; default "
115
+ f"{snapshots.DEFAULT_THRESHOLD:g}."},
116
+ },
117
+ },
118
+ "businessScalar": {"anyOf": [{"type": "string", "maxLength": 256}, {"type": "number"},
119
+ {"type": "boolean"}, {"type": "null"}]},
120
+ "businessExpectation": {"type": "object", "required": ["field", "equals"],
121
+ "additionalProperties": False,
122
+ "properties": {"field": {"type": "string", "pattern": "^" +
123
+ business._FIELD.pattern.replace("\\Z", "$")},
124
+ "equals": ref("businessScalar")}},
125
+ "assertion": {"description": "An assertion: exactly one of text, no_text, visible, absent, value and count.",
126
+ "anyOf": [ref(f"assertion_{kind}") for kind in assertions.KINDS]},
127
+ "expectation": {"description": "An assertion, the check's one snapshot, or {sound: name}: a sound the iPhone "
128
+ "must play (experimental, MOBSTER_EXPERIMENTAL_SOUND).",
129
+ "anyOf": [ref("assertion"), ref("snapshot"),
130
+ {"type": "object", "required": ["sound"], "additionalProperties": False,
131
+ "properties": {"sound": {"type": "string",
132
+ "pattern": "^[a-z0-9][a-z0-9_-]{0,39}$"}}}]},
133
+ "everyScreenAssertion": {"description": "An assertion that can hold on every screen: no_text or absent.",
134
+ "anyOf": [ref(f"assertion_{kind}") for kind in checks.THROUGHOUT_KINDS]},
135
+ **{f"assertion_{kind}": _assertion(kind) for kind in assertions.KINDS},
136
+ "steps": nullable({
137
+ "description": f"The flow in plain English, up to {LIMITS['steps']} steps of up to {LIMITS['step']} "
138
+ "characters. None means a launch-only check.",
139
+ "anyOf": [{"type": "string", "pattern": NOT_BLANK},
140
+ {"type": "array", "maxItems": LIMITS["steps"],
141
+ "items": {"type": "string", "pattern": NOT_BLANK}}],
142
+ }),
143
+ "checkName": nullable({"type": "string", "description": f"The check's name in reports, up to "
144
+ f"{LIMITS['name']} characters."}),
145
+ "device": nullable({"type": "string", "description": "The simulator device type, such as iPhone 17 Pro, up "
146
+ "to 100 characters."}),
147
+ "runtime": nullable({"type": "string", "description": "The iOS runtime, such as iOS 26.4. Default: the newest "
148
+ "installed."}),
149
+ "reset": nullable({"enum": list(checks.RESETS),
150
+ "description": "How much to reset before the run: none, data or reinstall. Default "
151
+ "reinstall with app.path, else data."}),
152
+ "bundle": nullable({"type": "string",
153
+ "pattern": r"^\s*$|^\s*[A-Za-z0-9_][A-Za-z0-9_.-]*\.[A-Za-z0-9_.-]*[A-Za-z0-9_]\s*$",
154
+ "description": "The app's bundle ID, such as com.example.yourapp."}),
155
+ "appPath": nullable({"type": "string", "description": "The .app built for the iOS Simulator, relative to the "
156
+ "project root. Mobster installs it before the run."}),
157
+ "args": nullable({"type": "array", "maxItems": LIMITS["args"], "items": {"type": "string", "maxLength": 1000},
158
+ "description": f"Launch arguments, up to {LIMITS['args']}. Quote a value YAML would read as "
159
+ "something else, such as \"YES\"."}),
160
+ "env": nullable({"type": "object", "maxProperties": LIMITS["env"],
161
+ "propertyNames": {"pattern": rf"^{checks.ENV_KEY.pattern}$"},
162
+ "additionalProperties": {"type": "string", "maxLength": 1000},
163
+ "description": f"Launch environment variables, up to {LIMITS['env']}. Each reaches the app "
164
+ "as KEY."}),
165
+ "url": nullable({"type": "string", "pattern": rf"^\s*$|^\s*{checks.URL_SCHEME.pattern}",
166
+ "description": f"A deep link opened after launch, such as yourapp://lesson, up to "
167
+ f"{LIMITS['url']:,} characters."}),
168
+ "tag": {"type": "string", "pattern": rf"^\s*{checks.TAG.pattern}\s*$"},
169
+ "audit": {"enum": audits},
170
+ "point": {
171
+ "description": "A location as latitude and longitude, or comma-separated coordinates.",
172
+ "anyOf": [{"type": "object", "required": ["latitude", "longitude"], "additionalProperties": False,
173
+ "properties": {"latitude": {"type": "number", "minimum": -90, "maximum": 90},
174
+ "longitude": {"type": "number", "minimum": -180, "maximum": 180}}},
175
+ {"type": "string", "pattern": r"^[^,]*,[^,]*$"}],
176
+ },
177
+ "dialogs": nullable({
178
+ "type": "object",
179
+ "description": "How the run answers iOS's own dialogs, with no model. On a simulator every key has a "
180
+ "default; on a real iPhone only the keys you name apply.",
181
+ "minProperties": 1,
182
+ "additionalProperties": False,
183
+ "properties": {
184
+ "permissions": {"enum": list(dialogs.CHOICES["permissions"]),
185
+ "description": "Permission alerts (location, photos, notifications, tracking). "
186
+ "Default allow on a simulator."},
187
+ "paste": {"enum": list(dialogs.CHOICES["paste"]),
188
+ "description": "Allow Paste. Default allow on a simulator."},
189
+ "sign_in_with_apple": {"enum": list(dialogs.CHOICES["sign_in_with_apple"]),
190
+ "description": "The Sign in with Apple sheet. Default continue on a "
191
+ "simulator."},
192
+ "biometric": {"enum": list(dialogs.CHOICES["biometric"]),
193
+ "description": "A Face ID or Touch ID prompt: match or nonmatch on a simulator, or "
194
+ "cancel. Default match on a simulator."},
195
+ "purchase": {"enum": list(dialogs.CHOICES["purchase"]),
196
+ "description": "The purchase sheet. Default confirm on a simulator. Never confirmed on "
197
+ "a real iPhone."},
198
+ },
199
+ }),
200
+ "conditions": nullable({
201
+ "type": "object",
202
+ "description": "The device's state while the check runs. On a real iPhone only appearance and locale are "
203
+ "set.",
204
+ "minProperties": 1,
205
+ "additionalProperties": False,
206
+ "properties": {
207
+ "appearance": _or_list({"enum": list(conditions.APPEARANCES)}, "Light or dark mode."),
208
+ "content_size": _or_list({"enum": list(conditions.CONTENT_SIZES)}, "The Dynamic Type size."),
209
+ "locale": _or_list({"type": "string", "pattern": rf"^{conditions.LOCALE.pattern}$"},
210
+ "The app's language and region, such as de_DE or fr-CA."),
211
+ "increase_contrast": {"enum": list(conditions.CONTRASTS),
212
+ "description": "Increase Contrast on or off."},
213
+ "location": ref("point", "The simulated location."),
214
+ "route": {
215
+ "type": "object", "required": ["waypoints"], "additionalProperties": False,
216
+ "description": "Move along the waypoints at the given speed in metres per second.",
217
+ "properties": {
218
+ "waypoints": {"type": "array", "minItems": conditions.ROUTE_POINTS[0],
219
+ "maxItems": conditions.ROUTE_POINTS[1], "items": ref("point")},
220
+ "speed": {"type": "number", "exclusiveMinimum": 0, "maximum": 1000},
221
+ },
222
+ },
223
+ "permissions": {"type": "object", "minProperties": 1,
224
+ "propertyNames": {"enum": list(conditions.SERVICES)},
225
+ "additionalProperties": {"enum": list(conditions.ACTIONS)},
226
+ "description": "Privacy permissions to set before launch, such as {photos: grant}."},
227
+ "media": {"type": "array", "minItems": 1, "maxItems": conditions.MEDIA_LIMIT,
228
+ "items": {"type": "string", "pattern": NOT_BLANK},
229
+ "description": f"Photos, videos and contacts to add, up to {conditions.MEDIA_LIMIT} files "
230
+ f"relative to the project: {', '.join(conditions.MEDIA_SUFFIXES)}."},
231
+ "biometric": {"enum": list(conditions.BIOMETRICS),
232
+ "description": "Face ID enrolled or not on the simulator. Mobster puts it back when the "
233
+ "run ends."},
234
+ "push": {"type": "string", "pattern": NOT_BLANK,
235
+ "description": "A push payload file (.json or .apns, with an aps object), relative to the "
236
+ "project, sent after launch."},
237
+ "network": {"enum": list(conditions.NETWORKS),
238
+ "description": "The simulator's connection: offline, slow-3g, lte or flaky. Simulator "
239
+ "only; it reaches URLSession traffic."},
240
+ "status_bar": {
241
+ "type": "object", "minProperties": 1, "additionalProperties": False,
242
+ "description": "The status bar's overrides, for screenshots.",
243
+ "properties": {
244
+ "time": {"type": "string", "pattern": NOT_BLANK,
245
+ "description": "Such as \"9:41\", up to 40 characters."},
246
+ "operator_name": {"type": "string", "pattern": NOT_BLANK,
247
+ "description": "Up to 40 characters."},
248
+ "battery_level": {"type": "integer", "minimum": 0, "maximum": 100},
249
+ "battery_state": {"enum": list(conditions.BATTERY_STATES)},
250
+ "wifi_mode": {"enum": list(conditions.WIFI_MODES)},
251
+ "cellular_mode": {"enum": list(conditions.CELLULAR_MODES)},
252
+ "wifi_bars": {"type": "integer", "minimum": 0, "maximum": 3},
253
+ "cellular_bars": {"type": "integer", "minimum": 0, "maximum": 4},
254
+ "data_network": {"enum": list(conditions.DATA_NETWORKS)},
255
+ },
256
+ },
257
+ },
258
+ }),
259
+ }
260
+
261
+
262
+ def file_shape():
263
+ """The check file as docs/checks.mdx writes it: app, launch and budget nested."""
264
+ return {
265
+ "type": "object",
266
+ "required": ["app"],
267
+ "additionalProperties": False,
268
+ "properties": {
269
+ "version": {"const": 1, "description": "The file format's version: 1, the only one."},
270
+ "name": ref("checkName"),
271
+ "app": {"type": "object", "additionalProperties": False, **given("bundle", "path"),
272
+ "description": "The app to check: its bundle ID, its .app, or both.",
273
+ "properties": {"bundle": ref("bundle"), "path": ref("appPath")}},
274
+ "device": ref("device"),
275
+ "runtime": ref("runtime"),
276
+ "reset": ref("reset"),
277
+ "tags": nullable({"anyOf": [ref("tag"), {"type": "array", "maxItems": LIMITS["tags"],
278
+ "items": ref("tag")}],
279
+ "description": f"Up to {LIMITS['tags']} words (a-z, 0-9, - and _) that mobster test "
280
+ "--tag picks checks by."}),
281
+ "launch": nullable({"type": "object", "additionalProperties": False,
282
+ "description": "How the app launches: arguments, environment and a deep link.",
283
+ "properties": {"args": ref("args"), "env": ref("env"), "url": ref("url")}}),
284
+ "steps": ref("steps"),
285
+ "expect": _assertions("The declared expectations, including accessibility-tree assertions and at most one "
286
+ "snapshot comparison.", "expectation"),
287
+ "throughout": _assertions("Assertions that must hold on every screen the run reads: no_text and absent.",
288
+ "everyScreenAssertion"),
289
+ "somewhere": _assertions("Assertions that must hold on at least one screen the run reads.", "assertion"),
290
+ "audit": nullable({"description": "Audits by name: usage-descriptions and privacy-manifest read the built "
291
+ "app, a11y the accessibility trees the run reads, ax-device Apple's own "
292
+ "audit on a real iPhone.",
293
+ "anyOf": [ref("audit"), {"type": "array", "items": ref("audit")}]}),
294
+ "a11y": nullable({"description": "The accessibility audit: warn reports findings and passes, strict fails "
295
+ "the check. true is warn.",
296
+ "anyOf": [{"enum": ["warn", "strict"]}, {"type": "boolean"}]}),
297
+ "fixture": nullable({
298
+ "description": "A fixture name or a list of up to ten names from .mobster/fixtures.",
299
+ "anyOf": [{"type": "string", "pattern": rf"^{fixtures.NAME.pattern}$"},
300
+ {"type": "array", "minItems": 1, "maxItems": 10,
301
+ "items": {"type": "string", "pattern": rf"^{fixtures.NAME.pattern}$"}}],
302
+ }),
303
+ "business": nullable({"type": "object", "required": ["adapter", "fixture", "expect"],
304
+ "additionalProperties": False,
305
+ "description": "A locally registered simulator app-state fixture and comparisons. "
306
+ "The app must export its persisted state after launch.",
307
+ "properties": {
308
+ "adapter": {"type": "string", "pattern": "^" + business._ID.pattern.replace("\\Z", "$")},
309
+ "fixture": {"type": "string", "pattern": "^" + business._ID.pattern.replace("\\Z", "$")},
310
+ "expect": {"type": "array", "maxItems": 20,
311
+ "items": ref("businessExpectation")}}}),
312
+ "conditions": ref("conditions"),
313
+ "dialogs": ref("dialogs"),
314
+ "stubs": nullable({
315
+ "type": "array", "minItems": 1, "maxItems": netstubs.STUB_LIMIT,
316
+ "description": "Fixed answers to named requests, served by the run's proxy. Simulator only.",
317
+ "items": {
318
+ "type": "object", "required": ["match", "respond"], "additionalProperties": False,
319
+ "properties": {
320
+ "match": {"type": "object", "required": ["host"], "additionalProperties": False,
321
+ "properties": {
322
+ "host": {"type": "string", "pattern": NOT_BLANK,
323
+ "description": "A host such as api.example.com or *.example.com."},
324
+ "path_prefix": {"type": "string", "pattern": "^/",
325
+ "description": ("Requests whose path starts with this. "
326
+ "Default /.")},
327
+ "method": {"enum": list(netstubs.METHODS)}}},
328
+ "respond": {"type": "object", "additionalProperties": False,
329
+ "properties": {
330
+ "status": {"type": "integer", "minimum": 100, "maximum": 599},
331
+ "json": {"description": "The JSON body."},
332
+ "file": {"type": "string", "pattern": NOT_BLANK,
333
+ "description": "A body file, relative to the project."},
334
+ "delay_ms": {"type": "integer", "minimum": 0,
335
+ "maximum": netstubs.DELAY_LIMIT}}}}},
336
+ }),
337
+ "budget": nullable({"type": "object", "additionalProperties": False,
338
+ "description": ("Smart's limits for the flow, and the launch time, memory and hangs "
339
+ "the app must stay inside."),
340
+ "properties": {
341
+ "max_seconds": _number(LIMITS["max_seconds"], "Smart's time limit. Default "
342
+ f"{checks.DEFAULT_MAX_SECONDS}, at most "
343
+ f"{LIMITS['max_seconds']}."),
344
+ "max_usd": _number(LIMITS["max_usd"], "Smart's spend cap. Default "
345
+ f"{checks.DEFAULT_MAX_USD:.2f}, at most "
346
+ f"{LIMITS['max_usd']:.2f}."),
347
+ "launch_ms": _number(600_000, "Fail the check when the app takes longer than this "
348
+ "to reach its first stable screen after a cold launch."),
349
+ "memory_mb": _number(65_536, "Fail the check when the app's resident memory peaks "
350
+ "above this many megabytes."),
351
+ "hangs": nullable({"type": "integer", "minimum": 0, "maximum": 1000,
352
+ "description": "Fail the check when the app logs more hangs of "
353
+ "250 ms or longer than this."})}}),
354
+ "schedule": nullable({"type": "string", "description": "Run this check on an interval or five-field cron "
355
+ "while Mobster is running, up to 128 characters."}),
356
+ },
357
+ }
358
+
359
+
360
+ def tool_shape():
361
+ """The MCP tools' flat arguments (checks.TOOL_KEYS), which a check file may also hold."""
362
+ return {
363
+ "type": "object",
364
+ "additionalProperties": False,
365
+ **given("bundle_id", "app_path"),
366
+ "properties": {
367
+ "name": ref("checkName"),
368
+ "app_path": ref("appPath"),
369
+ "bundle_id": ref("bundle"),
370
+ "device": ref("device"),
371
+ "runtime": ref("runtime"),
372
+ "reset": ref("reset"),
373
+ "launch_args": ref("args"),
374
+ "launch_env": ref("env"),
375
+ "open_url": ref("url"),
376
+ "steps": ref("steps"),
377
+ "expect": _assertions("Accessibility-tree assertions checked at the end of the run.", "assertion"),
378
+ "max_usd": _number(LIMITS["max_usd"], "Smart's spend cap."),
379
+ "max_seconds": _number(LIMITS["max_seconds"], "Smart's time limit."),
380
+ **{key: {"description": "Read by the MCP tool, not part of the check."} for key in checks.TOOL_ONLY},
381
+ },
382
+ }
383
+
384
+
385
+ def schema():
386
+ """The check file's JSON Schema, as a dict."""
387
+ return {
388
+ "$schema": DRAFT,
389
+ "$id": links.CHECK_SCHEMA,
390
+ "title": "Mobster check",
391
+ "description": "A Mobster check: a flow in plain English and declared expectations, saved in .mobster/checks. "
392
+ "See https://docs.mobster.dev/checks.",
393
+ "type": "object",
394
+ "if": {"anyOf": [{"required": [key]} for key in checks.TOOL_SHAPE_KEYS]},
395
+ "then": tool_shape(),
396
+ "else": file_shape(),
397
+ "$defs": _defs(),
398
+ }