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,4 @@
1
+ """Mobster's agent: a frontier model drives a real iPhone or a simulator through the accessibility tree."""
2
+
3
+ # The one version source: pyproject.toml reads it, and the frozen sidecar carries it without dist-info.
4
+ __version__ = "0.4.3"
@@ -0,0 +1,598 @@
1
+ """The `mobster` command."""
2
+
3
+ import os
4
+ import sys
5
+
6
+ from . import __version__, links
7
+ from .cli_constants import DEFAULT_WDA_URL, EXIT_COULDNT, EXIT_FAILED, EXIT_OK, EXIT_USAGE
8
+ from .cli_run import (
9
+ DeviceBusy,
10
+ MissingKey,
11
+ NoDevice,
12
+ device_target,
13
+ error_line,
14
+ error_result,
15
+ no_device_reason,
16
+ output,
17
+ wda_url_for,
18
+ )
19
+ from .paths import DATA_DIR_ENV, build_dir, source_checkout, user_data_dir
20
+
21
+ DOCS = links.DOCS
22
+
23
+
24
+ # Commands that read the keys Mobster for Mac saved (config.load_env_layers). Not `mcp`: an installed MCP entry
25
+ # must never switch on paid Smart tools the user didn't give it. Not `serve`: the Mac app passes its own file.
26
+ APP_KEY_COMMANDS = {"tui", "run", "chat", "doctor", "verify", "test", "setup", "login", "logout"}
27
+
28
+
29
+ def default_state_db(args):
30
+ """Where `serve` keeps its journal when --state-db is not given: never site-packages or the signed app bundle."""
31
+ from pathlib import Path
32
+ data_dir = getattr(args, "data_dir", None) or os.environ.get(DATA_DIR_ENV)
33
+ if data_dir or not source_checkout():
34
+ return (Path(data_dir).expanduser().resolve() if data_dir else user_data_dir()) / "state" / "mobster.sqlite3"
35
+ return Path(__file__).parent / ".state" / "mobster.sqlite3"
36
+
37
+ def check_placement(parser, args, on_error=None):
38
+ """Options given before a command's name, as in `mobster --wda-url URL run …`."""
39
+ from . import devtools
40
+ from .cli_parser import wda_url_type
41
+
42
+ def error(message):
43
+ if on_error is not None:
44
+ on_error(message)
45
+ parser.error(message)
46
+ command = args.command
47
+ if command in (None, "tui"):
48
+ return
49
+ accepted = parser.commands[command]._option_string_actions
50
+ for dest, flag in (("demo", "--demo"), ("resume", "--resume"), ("app", "--app"), ("continue_", "--continue")):
51
+ if getattr(args, dest, None) and flag not in accepted:
52
+ error(f"{flag} is an option of the terminal UI (`mobster {flag}`), not of `mobster {command}`")
53
+ if getattr(args, "wda_url", None) and getattr(args, "socket", None):
54
+ error("argument --socket: not allowed with argument --wda-url")
55
+ if getattr(args, "device", None) and getattr(args, "socket", None):
56
+ error("argument --socket: not allowed with argument --device")
57
+ if command != "serve" and getattr(args, "device", None) and getattr(args, "wda_url", None) \
58
+ and command not in devtools.ORDER:
59
+ error("argument --device: not allowed with argument --wda-url")
60
+ listing = command == "devices" and getattr(args, "action", None) is None # the list reads no WDA address
61
+ if "--wda-url" in accepted and command != "serve" and not listing and not getattr(args, "wda_url", None) \
62
+ and os.environ.get("MOBSTER_WDA_URL"):
63
+ import argparse
64
+ try:
65
+ wda_url_type(os.environ["MOBSTER_WDA_URL"])
66
+ except argparse.ArgumentTypeError as problem:
67
+ error(f"$MOBSTER_WDA_URL: {problem}")
68
+
69
+
70
+ PHONE_WORDS = {"ready": "Ready", "busy": "In use", "connected": "Connected", "needs_setup": "Needs setup",
71
+ "disconnected": "Unplugged"}
72
+
73
+
74
+ def device_state_words(item):
75
+ """Mobster for Mac's phone words for a device's state (MESSAGING §11, the phone list)."""
76
+ state = item.get("state") or ""
77
+ if state == "disconnected":
78
+ return {"simulator": "Shut down", "wda": "Not answering"}.get(item.get("kind"), "Unplugged")
79
+ return PHONE_WORDS.get(state, state.replace("_", " ").capitalize())
80
+
81
+
82
+ def print_devices(as_json=False):
83
+ from . import devices
84
+ from .style import columns, palette
85
+ found = devices.discover()
86
+ if as_json:
87
+ output({"devices": found})
88
+ return EXIT_OK
89
+ paint = palette(sys.stdout)
90
+ if not found:
91
+ print("No iPhones or simulators yet. Plug in your iPhone with a cable and unlock it, then run "
92
+ + paint("mobster setup", "accent") + ".")
93
+ print("For a simulator: " + paint("mobster sim prepare", "accent") + ".")
94
+ return EXIT_OK
95
+ kinds = {"usb": "iPhone", "simulator": "Simulator", "wda": "Address"}
96
+ rows = []
97
+ for item in found:
98
+ kind = kinds.get(item["kind"], item["kind"])
99
+ if item["kind"] == "usb" and item.get("transport") == "wifi":
100
+ kind = "iPhone, Wi-Fi"
101
+ details = ", ".join(part for part in (item.get("modelName") or item.get("model"),
102
+ f"iOS {item['ios']}" if item.get("ios") else None) if part)
103
+ rows.append((item, kind, details))
104
+ name_width = min(28, max(4, *(len(item["name"]) for item, _, _ in rows))) + 2
105
+ kind_width = max(4, *(len(kind) for _, kind, _ in rows)) + 2
106
+ state_width = max(5, *(len(device_state_words(item)) for item, _, _ in rows)) + 2
107
+ print(" " + paint("Name".ljust(name_width), "bold") + paint("Kind".ljust(kind_width), "bold")
108
+ + paint("State".ljust(state_width), "bold") + paint("Details", "bold"))
109
+ roles = {"Ready": "green", "In use": "accent", "Needs setup": "amber"}
110
+ width = columns()
111
+ for item, kind, details in rows:
112
+ state = device_state_words(item)
113
+ name = item["name"] if len(item["name"]) < name_width - 1 else item["name"][:name_width - 3] + "…"
114
+ line = (" " + paint(name.ljust(name_width), "accent") + kind.ljust(kind_width)
115
+ + paint(state.ljust(state_width), roles.get(state, "secondary")) + paint(details, "tertiary"))
116
+ print(line)
117
+ if item.get("reason"):
118
+ from .style import wrap
119
+ print(paint(wrap(item["reason"], width - 2, indent=4), "tertiary"))
120
+ ready = [item for item, _, _ in rows if item.get("state") == "ready"]
121
+ print()
122
+ if ready:
123
+ print("Give one a task: " + paint(f'mobster --device "{ready[0]["name"]}"', "accent"))
124
+ elif any(item.get("state") == "needs_setup" for item, _, _ in rows):
125
+ print("Set one up: " + paint("mobster setup", "accent"))
126
+ else:
127
+ print("Check everything: " + paint("mobster doctor", "accent"))
128
+ return EXIT_OK
129
+
130
+
131
+ def print_device_status(args):
132
+ """`mobster devices status`: a device's battery and heat, and this Mac's power (device_status.py)."""
133
+ from . import device_status, devices
134
+ found = devices.discover(probe=False)
135
+ if args.device:
136
+ try:
137
+ item = devices.resolve(found, args.device)
138
+ except LookupError as error:
139
+ raise NoDevice(str(error)) from None
140
+ elif args.wda_url:
141
+ url = wda_url_for(args)
142
+ item = next((record for record in found if (record.get("wdaUrl") or "").rstrip("/") == url),
143
+ {"id": url, "kind": "wda", "name": url, "wdaUrl": url})
144
+ else:
145
+ item = (found[0] if len(found) == 1 else next((record for record in found if record.get("primary")), None))
146
+ if item is None:
147
+ raise NoDevice("No device to read. " + ("Name one with --device." if found else
148
+ "Plug in your iPhone with a cable and unlock it."))
149
+ phone = device_status.read_phone(item["kind"], udid=item.get("udid"), wda_url=item.get("wdaUrl"))
150
+ mac = device_status.read_mac()
151
+ if args.json or not sys.stdout.isatty():
152
+ output({"ok": True, "device": item["id"], "name": item.get("name"), "phone": phone, "mac": mac})
153
+ return EXIT_OK
154
+ from .style import palette
155
+ paint = palette(sys.stdout)
156
+ print(paint(item.get("name") or item["id"], "bold"))
157
+ found_rows = device_status.rows(phone, mac)
158
+ width = max(len(label) for label, _ in found_rows) + 2
159
+ for label, value in found_rows:
160
+ print(" " + paint(label.ljust(width), "tertiary") + value)
161
+ return EXIT_OK
162
+
163
+
164
+ def lock_probe(args):
165
+ """`mobster devices lock-probe`: reads the lock screen under the device lease, with no tap, key press or typing."""
166
+ from . import lockscreen
167
+ from .compose import close_all
168
+ from .drivers import build_driver, resolve_wda_session
169
+ from .journal import Lease, LeaseHeld
170
+ from .transport import TransportError
171
+ url = wda_url_for(args)
172
+ try:
173
+ lease = Lease.device(url)
174
+ except LeaseHeld:
175
+ raise DeviceBusy("Another Mobster process is using this phone. Finish or stop its task, then probe "
176
+ "again.") from None
177
+ driver = None
178
+ try:
179
+ try:
180
+ session = resolve_wda_session(url)
181
+ except (TransportError, OSError) as exc:
182
+ raise NoDevice(f"Nothing answers at {url} ({no_device_reason(exc)}).") from None
183
+ driver = build_driver("wda", url=url, session=session)
184
+ result = lockscreen.probe(driver, udid=getattr(args, "device_udid", None)
185
+ if getattr(args, "device_kind", None) == "usb" else None)
186
+ finally:
187
+ close_all(driver)
188
+ lease.close()
189
+ if args.json or not sys.stdout.isatty():
190
+ output({"ok": True, **result})
191
+ else:
192
+ print("\n".join(lockscreen.describe(result)))
193
+ return EXIT_OK
194
+
195
+
196
+ # The bare `mobster` parser's options that take a value: their value is never the command's name.
197
+ VALUE_OPTIONS = ("--wda-url", "--device", "--env-file", "--app")
198
+
199
+
200
+ def _positional(argv, names):
201
+ """The index of the first word in ``argv`` that is neither an option nor an option's value, or None."""
202
+ index = 0
203
+ while index < len(argv):
204
+ token = argv[index]
205
+ if token == "--":
206
+ return index + 1 if index + 1 < len(argv) else None
207
+ if token in VALUE_OPTIONS:
208
+ index += 2
209
+ continue
210
+ if token == "--resume":
211
+ # --resume takes an optional ID: a command's name after it is the command.
212
+ following = argv[index + 1] if index + 1 < len(argv) else None
213
+ index += 1 if following is None or following in names or following.startswith("-") else 2
214
+ continue
215
+ if token.startswith("-") and token != "-":
216
+ index += 1
217
+ continue
218
+ return index
219
+ return None
220
+
221
+
222
+ def command_word(parser, argv):
223
+ """The command ``argv`` names, read before parsing."""
224
+ names = parser.commands if hasattr(parser, "commands") else parser
225
+ index = _positional(list(argv), names)
226
+ if index is None:
227
+ return None
228
+ if index and argv[index - 1] == "--":
229
+ return None # `mobster -- verify`: after --, the word is a task, not a command
230
+ word = argv[index]
231
+ return word if word in names else None
232
+
233
+
234
+ def split_task(argv):
235
+ """`mobster "TASK"`: (task, argv without it) when the first word is a task for the terminal UI,
236
+ else (None, argv)."""
237
+ from .help_topics import COMMANDS
238
+ index = _positional(argv, COMMANDS)
239
+ if index is None or argv[index] in COMMANDS:
240
+ return None, argv
241
+ word = argv[index]
242
+ if index and argv[index - 1] == "--":
243
+ # `mobster -- calendar`: after --, a word is always the task, even one word.
244
+ return word.strip(), argv[:index - 1] + argv[index + 1:]
245
+ from .harness_api import hooks
246
+ if hooks().cli:
247
+ # A private build may add commands: its parser knows them.
248
+ from .cli_parser import build_parser
249
+ if word in build_parser(load=()).commands:
250
+ return None, argv
251
+ later = [token for token in argv[index + 1:] if not token.startswith("-")]
252
+ from .style import palette
253
+ paint = palette(sys.stderr)
254
+ if " " in word.strip() or "\n" in word.strip():
255
+ if later:
256
+ raise UsageError(paint(later[0], "amber") + " follows the task.",
257
+ "put the whole task in one pair of quotes: "
258
+ + paint(f'mobster "{word} {" ".join(later)}"', "accent"))
259
+ return word.strip(), argv[:index] + argv[index + 1:]
260
+ if later:
261
+ words = " ".join([word] + later)
262
+ raise UsageError(paint(word, "amber") + " isn't a command.",
263
+ "to run it as a task, put it in quotes: " + paint(f'mobster "{words}"', "accent"))
264
+ import difflib
265
+ close = difflib.get_close_matches(word, [name for name in COMMANDS if name != "build-ocr"], n=1, cutoff=.6)
266
+ if close:
267
+ raise UsageError(paint(word, "amber") + " isn't a command.",
268
+ "did you mean " + paint(f"mobster {close[0]}", "accent") + "?")
269
+ raise UsageError(paint(word, "amber") + " isn't a command.",
270
+ "to run it as a task: " + paint(f"mobster -- {word}", "accent"))
271
+
272
+
273
+ class UsageError(Exception):
274
+ """A usage error found before the parser runs: (message, tip). Exit code 2."""
275
+
276
+ def __init__(self, message, tip=""):
277
+ super().__init__(message)
278
+ self.message, self.tip = message, tip
279
+
280
+ def report(self, prog="mobster"):
281
+ from .cli_parser import usage_lines
282
+ from .style import palette
283
+ sys.stderr.write(usage_lines(palette(sys.stderr), self.message, self.tip, prog))
284
+ sys.stderr.flush()
285
+ return EXIT_USAGE
286
+
287
+
288
+ def route_usage_errors(parser, argv):
289
+ """A usage error the bare parser finds before `verify` exits 3,
290
+ with verify's couldnt_run JSON when output is JSON."""
291
+ from . import devtools
292
+ if command_word(parser, argv) != "verify":
293
+ return
294
+ as_json = "--json" in argv
295
+
296
+ def error(message):
297
+ parser.print_usage(sys.stderr)
298
+ print(f"{parser.prog}: error: {message}", file=sys.stderr, flush=True)
299
+ sys.exit(devtools.usage_exit("verify", message, as_json=as_json))
300
+ parser.error = error
301
+
302
+
303
+ def quick(argv):
304
+ """`--version` and `--help`, answered before the parser is built. None for anything else."""
305
+ if argv in (["-V"], ["--version"], ["version"]):
306
+ print(f"mobster {__version__}")
307
+ return EXIT_OK
308
+ if argv in (["-h"], ["--help"], ["help"]):
309
+ from . import help_topics
310
+ from .style import columns, palette
311
+ print(help_topics.top_level(palette(sys.stdout), columns()))
312
+ return EXIT_OK
313
+ if len(argv) == 2 and argv[0] == "help":
314
+ from . import help_topics
315
+ if argv[1] in help_topics.TOPICS:
316
+ from .cli_parser import print_topic
317
+ return print_topic(argv[1])
318
+ return None
319
+
320
+
321
+ # Commands that run Xcode's tools (xcodebuild, xcrun simctl or devicectl, swiftc): they use the Xcode Mobster found,
322
+ # whatever xcode-select says (mac_tools.adopt_xcode), so nobody is asked to switch it in Terminal.
323
+ XCODE_COMMANDS = frozenset({"build-ocr", "ci", "devices", "doctor", "expo", "mcp", "phone", "serve", "setup", "sim",
324
+ "test", "testflight", "verify", "wifi", "xcode", "fixtures", "links", "intents"})
325
+
326
+
327
+ def main(argv=None):
328
+ argv = sys.argv[1:] if argv is None else list(argv)
329
+ if argv[:1] == ["import"]:
330
+ argv = ["test"] + argv # `mobster import maestro .maestro` is `mobster test import maestro .maestro`
331
+ answered = quick(argv)
332
+ if answered is not None:
333
+ return answered
334
+ try:
335
+ task, argv = split_task(argv)
336
+ except UsageError as error:
337
+ return error.report()
338
+ from .cli_parser import build_parser, owner, print_help
339
+ from .help_topics import COMMANDS
340
+ index = _positional(argv, COMMANDS)
341
+ word = argv[index] if index is not None else None
342
+ wanted = {word}
343
+ if word == "help":
344
+ wanted |= set(argv[index + 1:index + 2])
345
+ parser = build_parser(load=wanted)
346
+ route_usage_errors(parser, argv)
347
+ args, extras = parser.parse_known_args(argv)
348
+ args.task = task
349
+ if getattr(args, "data_dir", None):
350
+ from .paths import use_data_dir
351
+ use_data_dir(args.data_dir)
352
+ words = [word for word in extras if not word.startswith("-")]
353
+ if extras and words == extras and args.command == "run" and getattr(args, "goal", None):
354
+ # `mobster run Turn on Dark Mode`: the task is the words, unquoted.
355
+ task = " ".join([args.goal] + words)
356
+ from .style import palette
357
+ paint = palette(sys.stderr)
358
+ return UsageError(paint("mobster run", "accent") + " needs the task in quotes.",
359
+ paint(f'mobster run "{task}"', "accent")).report("mobster run")
360
+ if extras and not getattr(args, "stub_command", False): # a track's stub takes anything (it exits 3)
361
+ # `mobster run x --bogus`: the error and usage are run's (`sim prepare`'s), not the bare `mobster`'s.
362
+ owner(parser, args).error(f"unrecognized arguments: {' '.join(extras)}")
363
+ if args.command == "help":
364
+ return print_help(parser, args.topic, everything=args.all)
365
+ messages = []
366
+ from . import devtools
367
+ try:
368
+ check_placement(parser, args, on_error=messages.append)
369
+ except SystemExit as exit_:
370
+ if exit_.code == 2 and args.command in devtools.ORDER:
371
+ # verify's usage errors exit 3 (2 means needs review), with its JSON result when output is JSON.
372
+ return devtools.usage_exit(args.command, messages[-1] if messages else None,
373
+ as_json=bool(getattr(args, "json", False)))
374
+ raise
375
+ command = args.command or "tui"
376
+ if hasattr(args, "env_file") and args.env_file is None and args.command != "serve" \
377
+ and os.environ.get("MOBSTER_ENV_FILE"):
378
+ # An explicit default for people who always use the same file; `serve` keeps its own rules.
379
+ from pathlib import Path
380
+ args.env_file = Path(os.environ["MOBSTER_ENV_FILE"]).expanduser()
381
+ args.env_file_from_environment = True
382
+ if command in APP_KEY_COMMANDS and not getattr(args, "demo", False) is True:
383
+ # The keys Mobster for Mac saved come last: the shell and an --env-file win (config.load_env_layers).
384
+ from .config import load_env_layers
385
+ for warning in load_env_layers(getattr(args, "env_file", None)):
386
+ print(f"mobster: {warning}", file=sys.stderr, flush=True)
387
+ elif getattr(args, "env_file", None) and args.command != "mcp": # mcp loads it itself, into its log
388
+ from .config import load_env_file
389
+ # A bad line is skipped, never fatal: the Mac app's agent logs it (agent.log) and still serves. A missing
390
+ # file is said too, except to `serve`, whose file the Mac app's setup creates when it saves a value.
391
+ for warning in load_env_file(args.env_file, missing_ok=args.command == "serve").warnings:
392
+ print(f"mobster: {warning}", file=sys.stderr, flush=True)
393
+ if command in XCODE_COMMANDS and sys.platform == "darwin":
394
+ from . import mac_tools
395
+ mac_tools.adopt_xcode()
396
+ # Before any model or service connection: the frozen app's Python has no usable CA file.
397
+ from . import tls
398
+ tls.ensure_ca_bundle()
399
+ from . import update_check
400
+ update_check.start(command, args)
401
+ code = dispatch(parser, args, command)
402
+ update_check.after_command(command, args)
403
+ return code
404
+
405
+
406
+ def dispatch(parser, args, command):
407
+ from . import devtools
408
+ refused = plan_refusal(args, command)
409
+ if refused is not None:
410
+ return refused
411
+ if command == "devices" and getattr(args, "action", None) is None and (
412
+ getattr(args, "device", None) or getattr(args, "wda_url", None)):
413
+ parser.commands["devices"].error("--device and --wda-url go with `mobster devices lock-probe` or `status`")
414
+ probing = command == "devices" and getattr(args, "action", None) == "lock-probe"
415
+ if (command in ("run", "screen", "doctor", "tui") or probing) and getattr(args, "device", None) \
416
+ and not getattr(args, "demo", False) is True:
417
+ # --device NAME is that device's WDA address from here on (exclusive with --wda-url).
418
+ try:
419
+ item = device_target(args.device)
420
+ except NoDevice as error:
421
+ if command == "run" and (getattr(args, "json", False) or not sys.stdout.isatty()):
422
+ output(error_result(error, EXIT_COULDNT))
423
+ else:
424
+ print(error_line(error), file=sys.stderr)
425
+ return EXIT_COULDNT
426
+ args.wda_url = item["wdaUrl"]
427
+ args.device_kind, args.device_udid = item["kind"], item.get("udid")
428
+ args.device_name = item.get("name")
429
+ if command == "chat" and not getattr(args, "unloaded_command", False):
430
+ from .chat_here import maybe_here
431
+ code = maybe_here(args)
432
+ if code is not None:
433
+ return code
434
+ if command in devtools.ORDER:
435
+ try:
436
+ return devtools.run(args)
437
+ except KeyboardInterrupt:
438
+ return 130
439
+ from .harness_api import hooks
440
+ try:
441
+ for extension in hooks().cli:
442
+ code = extension.run(args, output)
443
+ if code is not None:
444
+ return code
445
+ if command == "tui":
446
+ from .tui import main as tui_main
447
+ return tui_main(args)
448
+ if command == "version":
449
+ if args.json:
450
+ import platform
451
+
452
+ from . import tls
453
+ output({"version": __version__, "python": platform.python_version(),
454
+ "platform": platform.platform(terse=True), "tls": tls.report()})
455
+ else:
456
+ print(f"mobster {__version__}")
457
+ return EXIT_OK
458
+ if command in ("login", "logout"):
459
+ from . import login
460
+ return login.run(args)
461
+ if command == "setup":
462
+ from . import setup_cli
463
+ return setup_cli.run(args)
464
+ if command == "completion":
465
+ from . import completion
466
+ return completion.run(args)
467
+ if command == "update":
468
+ from . import update_check
469
+ return update_check.run_update(args)
470
+ if command == "devices":
471
+ if args.action == "lock-probe":
472
+ return lock_probe(args)
473
+ if args.action == "status":
474
+ return print_device_status(args)
475
+ return print_devices(as_json=args.json)
476
+ if command == "doctor":
477
+ return doctor_command(args)
478
+ if command == "history":
479
+ if getattr(args, "to_eval", None):
480
+ from .run_export import to_eval
481
+ return to_eval(args)
482
+ from .history import print_history
483
+ return print_history(limit=args.limit, as_json=args.json)
484
+ if command == "export":
485
+ from .run_export import export as export_run
486
+ return export_run(args)
487
+ if command == "screen":
488
+ from .terminal_image import show_screen
489
+ return show_screen(wda_url_for(args), out=args.out, width=args.width)
490
+ if command == "build-ocr":
491
+ import subprocess
492
+ from pathlib import Path
493
+ root = Path(__file__).parent
494
+ build = build_dir()
495
+ build.mkdir(parents=True, exist_ok=True)
496
+ subprocess.run(["xcrun", "swiftc", "-O", str(root / "swift" / "ocr.swift"),
497
+ "-o", str(build / "ocr")], check=True)
498
+ output({"ok": True, "binary": str(build / "ocr")})
499
+ elif command == "demo":
500
+ from types import SimpleNamespace
501
+
502
+ from .smart_run import run_smart
503
+ return run_smart(SimpleNamespace(goal="Search for coffee", json=args.json, execute=True, spend_cap_usd=None,
504
+ long=False, budget=None, yes=True), demo=True)
505
+ elif command == "serve":
506
+ from .serve import serve
507
+ if args.state_db is None:
508
+ args.state_db = default_state_db(args)
509
+ if getattr(args, "device", None) and not args.wda_url and not getattr(args, "manage_device", False):
510
+ args.wda_url = device_target(args.device)["wdaUrl"] # serve that device
511
+ if getattr(args, "manage_device", False) and not args.wda_url:
512
+ args.wda_url = DEFAULT_WDA_URL # The relay the device manager runs.
513
+ saved = os.environ.get("MOBSTER_ENABLE_LIVE")
514
+ if saved in {"0", "1"}:
515
+ args.enable_live = saved == "1" # The setup flow's saved choice wins.
516
+ serve(args)
517
+ elif command == "run":
518
+ return run_command(args)
519
+ return EXIT_OK
520
+ except KeyboardInterrupt:
521
+ return 130
522
+ except Exception as exc:
523
+ # Exception messages here are locally authored; transport never includes provider bodies/keys.
524
+ code = EXIT_COULDNT if isinstance(exc, (NoDevice, MissingKey)) else EXIT_FAILED
525
+ if sys.stdout.isatty() and not getattr(args, "json", False):
526
+ print(error_line(exc), file=sys.stderr)
527
+ elif command in ("run", "demo"):
528
+ output(error_result(exc, code))
529
+ else:
530
+ output({"ok": False, "error_type": type(exc).__name__, "error": str(exc)})
531
+ return code
532
+
533
+
534
+ def doctor_command(args):
535
+ """`mobster doctor`: the checks and the fix for each. With --fix, it does the fix each finding's row carries
536
+ (doctor_fix), then checks again."""
537
+ from . import doctor, doctor_fix
538
+ if getattr(args, "apps", False):
539
+ from .attach import cli as attach_cli
540
+ return attach_cli.run(args, wda_url_for(args))
541
+ usb = not args.simulator and getattr(args, "device_kind", "usb") == "usb"
542
+
543
+ def check():
544
+ return doctor.run_checks(wda_url=wda_url_for(args), device=usb, env_file=args.env_file,
545
+ udid=getattr(args, "device_udid", None))
546
+ checks = check()
547
+ steps = None
548
+ if args.fix:
549
+ terminal = sys.stdin.isatty() and not args.json
550
+ ctx = doctor_fix.Context(args, ask=doctor_fix.interactive_ask if terminal else None,
551
+ say=(lambda line: print(line, flush=True)) if terminal else None)
552
+ try:
553
+ steps = doctor_fix.run(checks, ctx, terminal=terminal)
554
+ finally:
555
+ ctx.close()
556
+ checks = check()
557
+ # Someone who never pointed Mobster at an iPhone may be here for `verify`: say where its checks are.
558
+ phone_chosen = getattr(args, "wda_url", None) or getattr(args, "device", None) \
559
+ or os.environ.get("MOBSTER_WDA_URL")
560
+ hint = doctor.VERIFY_HINT if doctor.problems(checks) and not phone_chosen else None
561
+ if args.json:
562
+ output({"ok": not doctor.problems(checks), "checks": [check.public() for check in checks],
563
+ **({"fixes": [step.public() for step in steps]} if steps is not None else {}),
564
+ **({"hint": hint} if hint else {})})
565
+ else:
566
+ from .style import color_enabled, columns
567
+ print(doctor.plain_report(checks, color=color_enabled(sys.stdout), verify_hint=hint, width=columns(),
568
+ steps=steps))
569
+ return EXIT_FAILED if doctor.problems(checks) else EXIT_OK
570
+
571
+
572
+ def plan_refusal(args, command):
573
+ """EXIT_COULDNT after saying so, when the agent inside Mobster.app has no active plan, fails its seal check or
574
+ runs under a CI token that refuses; None otherwise."""
575
+ from . import plan_gate
576
+ gate = plan_gate.bundled_gate()
577
+ if gate is not None:
578
+ plan_gate.grant_ci_token(command, args) # a CI token licenses simulator runs on a machine with no account
579
+ message = plan_gate.stopped(command, args) # a failed seal or a CI token that refuses: whatever it needs
580
+ if message is None:
581
+ message = gate.refusal() if gate is not None else None
582
+ if message is None or not plan_gate.cli_needs_plan(command, args): # the plan first: it is the cheaper read
583
+ return None
584
+ if getattr(args, "json", False):
585
+ output({"ok": False, "error_type": "plan_required", "error": message})
586
+ else:
587
+ print(f"mobster: {message}", file=sys.stderr, flush=True)
588
+ return EXIT_COULDNT
589
+
590
+
591
+ def run_command(args):
592
+ """`mobster run`: Mobster's agent through the same runtime as the terminal UI, or the scripted demo."""
593
+ from .smart_run import run_smart
594
+ return run_smart(args, demo=getattr(args, "demo", False) is True)
595
+
596
+
597
+ if __name__ == "__main__":
598
+ sys.exit(main())
@@ -0,0 +1 @@
1
+ """Profiles: which social media profile lives on which iPhone (FEATURE-BACKLOG v2 item 4, docs/profiles.mdx)."""