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,267 @@
1
+ """`mobster completion zsh|bash|fish`: tab completion, generated from the command line's own parser.
2
+
3
+ The script is written from ``build_parser()`` when you ask for it, so it always matches this build's commands and
4
+ options, and nothing here costs another command any start-up time. Device names, past task IDs and conversation
5
+ IDs complete from `mobster completion --values devices|tasks|threads`, which reads the same lists `mobster devices`
6
+ and `mobster history` do.
7
+ """
8
+
9
+ import argparse
10
+ import re
11
+ import sys
12
+
13
+ VALUE_SOURCES = {"device": "devices", "resume": "tasks", "thread": "threads"}
14
+ PATH_DESTS = {"env_file", "out", "output", "junit", "html", "app", "check", "state_db", "data_dir"}
15
+
16
+
17
+ def run(args):
18
+ if getattr(args, "values", None):
19
+ return print_values(args.values)
20
+ if not args.shell:
21
+ print("mobster completion: name a shell: zsh, bash or fish. Example: mobster completion zsh",
22
+ file=sys.stderr)
23
+ return 2
24
+ from .cli_parser import build_parser
25
+ tree = describe(build_parser())
26
+ text = {"zsh": zsh, "bash": bash, "fish": fish}[args.shell](tree)
27
+ try:
28
+ sys.stdout.write(text)
29
+ sys.stdout.flush()
30
+ except BrokenPipeError:
31
+ pass
32
+ return 0
33
+
34
+
35
+ def print_values(kind):
36
+ """One value per line, for the scripts' dynamic completion. Never fails loudly: a shell is waiting."""
37
+ try:
38
+ if kind == "devices":
39
+ from . import devices
40
+ for item in devices.discover(probe=False):
41
+ print(item["name"])
42
+ elif kind == "tasks":
43
+ from .history import read_history
44
+ from .paths import default_history_db
45
+ for record in read_history(default_history_db(), 30):
46
+ if record.get("id"):
47
+ print(record["id"])
48
+ elif kind == "threads":
49
+ import json
50
+
51
+ from .threads.recent import path
52
+ try:
53
+ for value in json.loads(path().read_text()).values():
54
+ print(value)
55
+ except (OSError, ValueError):
56
+ pass
57
+ except Exception: # noqa: BLE001
58
+ pass
59
+ return 0
60
+
61
+
62
+ def _visible(action):
63
+ return action.help != argparse.SUPPRESS
64
+
65
+
66
+ def describe(parser):
67
+ """{"options": [...], "commands": {name: {"help", "options", "commands", "positionals"}}} from a parser."""
68
+ from .help_topics import visible
69
+
70
+ def options(target):
71
+ out = []
72
+ for action in target._actions:
73
+ if not action.option_strings or not _visible(action):
74
+ continue
75
+ takes = action.nargs != 0 and not isinstance(action, (argparse._StoreTrueAction,
76
+ argparse._StoreFalseAction,
77
+ argparse._HelpAction, argparse._VersionAction,
78
+ argparse._CountAction))
79
+ source = VALUE_SOURCES.get(action.dest)
80
+ if takes and action.choices:
81
+ source = "choices:" + " ".join(str(c) for c in action.choices)
82
+ elif takes and source is None and (action.dest in PATH_DESTS or action.type is not None and
83
+ getattr(action.type, "__name__", "") == "path_type"):
84
+ source = "files"
85
+ out.append({"flags": list(action.option_strings), "help": action.help or "", "takes": takes,
86
+ "metavar": (action.metavar if isinstance(action.metavar, str) else None)
87
+ or action.dest.upper(), "source": source})
88
+ return out
89
+
90
+ def node(target, top=False):
91
+ subs = next((a for a in target._actions if isinstance(a, argparse._SubParsersAction)), None)
92
+ commands = {}
93
+ if subs is not None:
94
+ helps = {a.dest: a.help for a in subs._choices_actions}
95
+ for name, child in subs.choices.items():
96
+ if top and not visible(name):
97
+ continue
98
+ if name not in helps and not top:
99
+ continue
100
+ entry = node(child)
101
+ entry["help"] = helps.get(name) or ""
102
+ commands[name] = entry
103
+ positionals = [a for a in target._actions if not a.option_strings and _visible(a)
104
+ and not isinstance(a, argparse._SubParsersAction)]
105
+ choices = []
106
+ for action in positionals:
107
+ if action.choices:
108
+ choices += [str(c) for c in action.choices]
109
+ return {"options": options(target), "commands": commands, "choices": choices}
110
+ tree = node(parser, top=True)
111
+ # The commands in the order `mobster --help` lists them, then any a private build adds.
112
+ from .help_topics import GROUPS, MORE
113
+ order = [name for _, names in GROUPS for name in names] + list(MORE)
114
+ tree["commands"] = dict(sorted(tree["commands"].items(),
115
+ key=lambda item: order.index(item[0]) if item[0] in order else len(order)))
116
+ return tree
117
+
118
+
119
+ def _zsh_text(text):
120
+ # Inside zsh's single quotes nothing escapes a quote, so an apostrophe is written as ’.
121
+ return re.sub(r"([\[\]:\\])", r"\\\1", text.replace("`", "").replace("'", "’")).replace("\n", " ")[:120]
122
+
123
+
124
+ def _zsh_option(option):
125
+ flags = option["flags"]
126
+ help_text = _zsh_text(option["help"])
127
+ action = ""
128
+ if option["takes"]:
129
+ source = option["source"]
130
+ if source == "files":
131
+ completer = "_files"
132
+ elif source and source.startswith("choices:"):
133
+ completer = "(" + source.split(":", 1)[1] + ")"
134
+ elif source in ("devices", "tasks", "threads"):
135
+ completer = f"_mobster_values {source}"
136
+ else:
137
+ completer = " "
138
+ action = f":{_zsh_text(option['metavar'].lower())}:{completer}"
139
+ if len(flags) > 1:
140
+ exclusive = " ".join(flags)
141
+ return f"'({exclusive})'{{{','.join(flags)}}}'[{help_text}]{action}'"
142
+ return f"'{flags[0]}[{help_text}]{action}'"
143
+
144
+
145
+ def zsh(tree):
146
+ lines = ["#compdef mobster", "# mobster completion zsh: generated from this build's parser.", "",
147
+ "_mobster_values() {", " local -a values",
148
+ ' values=("${(@f)$(mobster completion --values $1 2>/dev/null)}")',
149
+ " compadd -a values", "}", ""]
150
+
151
+ def function(name, entry):
152
+ commands = entry["commands"]
153
+ body = [f"{name}() {{", " local curcontext=\"$curcontext\" state line", " typeset -A opt_args"]
154
+ specs = [_zsh_option(option) for option in entry["options"]]
155
+ if commands:
156
+ body.append(" local -a commands")
157
+ body.append(" commands=(")
158
+ for sub, child in commands.items():
159
+ body.append(f" '{sub}:{_zsh_text(child.get('help') or '')}'")
160
+ body.append(" )")
161
+ specs += ["'1: :->command'", "'*:: :->args'"]
162
+ elif entry["choices"]:
163
+ specs.append(f"'1: :({' '.join(entry['choices'])})'")
164
+ specs.append("'*:argument:_default'")
165
+ else:
166
+ specs.append("'*:argument:_default'")
167
+ body.append(" _arguments -C -s \\")
168
+ body += [f" {spec} \\" for spec in specs[:-1]] + [f" {specs[-1]}"]
169
+ if commands:
170
+ body.append(" case $state in")
171
+ body.append(" command) _describe -t commands 'mobster command' commands ;;")
172
+ body.append(" args)")
173
+ body.append(" case $line[1] in")
174
+ for sub in commands:
175
+ body.append(f" {sub}) {name}_{sub.replace('-', '_')} ;;")
176
+ body.append(" esac ;;")
177
+ body.append(" esac")
178
+ body.append("}")
179
+ body.append("")
180
+ out = body
181
+ for sub, child in commands.items():
182
+ out += function(f"{name}_{sub.replace('-', '_')}", child)
183
+ return out
184
+ lines += function("_mobster", tree)
185
+ lines += ['if [ "$funcstack[1]" = "_mobster" ]; then', ' _mobster "$@"', "else",
186
+ " compdef _mobster mobster", "fi", ""]
187
+ return "\n".join(lines)
188
+
189
+
190
+ def bash(tree):
191
+ lines = ["# mobster completion bash: generated from this build's parser.", "_mobster() {",
192
+ " local cur prev words cword", " cur=\"${COMP_WORDS[COMP_CWORD]}\"",
193
+ " prev=\"${COMP_WORDS[COMP_CWORD-1]}\"",
194
+ " local command=\"\" sub=\"\"", " local i",
195
+ " for ((i = 1; i < COMP_CWORD; i++)); do",
196
+ " case \"${COMP_WORDS[i]}\" in -*) ;; *) if [ -z \"$command\" ]; then command=\"${COMP_WORDS[i]}\"; "
197
+ "elif [ -z \"$sub\" ]; then sub=\"${COMP_WORDS[i]}\"; fi ;; esac", " done",
198
+ " case \"$prev\" in",
199
+ " --device) COMPREPLY=($(compgen -W \"$(mobster completion --values devices 2>/dev/null)\" -- "
200
+ "\"$cur\")); return ;;",
201
+ " --resume) COMPREPLY=($(compgen -W \"$(mobster completion --values tasks 2>/dev/null)\" -- \"$cur\"));"
202
+ " return ;;",
203
+ " --thread) COMPREPLY=($(compgen -W \"$(mobster completion --values threads 2>/dev/null)\" -- "
204
+ "\"$cur\")); return ;;",
205
+ " --env-file|--out|--output|--junit|--html) COMPREPLY=($(compgen -f -- \"$cur\")); return ;;",
206
+ " esac", " local words_for=\"\""]
207
+ top = " ".join(list(tree["commands"]) + [f for o in tree["options"] for f in o["flags"]])
208
+ lines.append(" if [ -z \"$command\" ]; then words_for=\"" + top + "\"")
209
+ lines.append(" else")
210
+ lines.append(" case \"$command\" in")
211
+ for name, entry in tree["commands"].items():
212
+ flags = " ".join(f for o in entry["options"] for f in o["flags"])
213
+ subs = " ".join(list(entry["commands"]) + entry["choices"])
214
+ if entry["commands"]:
215
+ lines.append(f" {name}) if [ -z \"$sub\" ]; then words_for=\"{subs} {flags}\"; else case \"$sub\" in")
216
+ for sub, child in entry["commands"].items():
217
+ child_flags = " ".join(f for o in child["options"] for f in o["flags"])
218
+ lines.append(f" {sub}) words_for=\"{' '.join(child['choices'])} {child_flags}\" ;;")
219
+ lines.append(" esac; fi ;;")
220
+ else:
221
+ lines.append(f" {name}) words_for=\"{subs} {flags}\" ;;")
222
+ lines += [" esac", " fi", " COMPREPLY=($(compgen -W \"$words_for\" -- \"$cur\"))", "}",
223
+ "complete -o default -F _mobster mobster", ""]
224
+ return "\n".join(lines)
225
+
226
+
227
+ def _fish_text(text):
228
+ return text.replace("\\", "\\\\").replace("'", "\\'").replace("\n", " ")[:120]
229
+
230
+
231
+ def fish(tree):
232
+ lines = ["# mobster completion fish: generated from this build's parser.", "complete -c mobster -f"]
233
+ names = " ".join(tree["commands"])
234
+ for name, entry in tree["commands"].items():
235
+ lines.append(f"complete -c mobster -n 'not __fish_seen_subcommand_from {names}' -a {name} "
236
+ f"-d '{_fish_text(entry.get('help') or '')}'")
237
+
238
+ def option_lines(condition, options):
239
+ out = []
240
+ for option in options:
241
+ parts = []
242
+ for flag in option["flags"]:
243
+ parts.append(f"-l {flag[2:]}" if flag.startswith("--") else f"-s {flag[1:]}")
244
+ extra = ""
245
+ if option["takes"]:
246
+ source = option["source"]
247
+ if source == "files":
248
+ extra = " -r -F"
249
+ elif source and source.startswith("choices:"):
250
+ extra = f" -x -a '{source.split(':', 1)[1]}'"
251
+ elif source in ("devices", "tasks", "threads"):
252
+ extra = f" -x -a '(mobster completion --values {source} 2>/dev/null)'"
253
+ else:
254
+ extra = " -x"
255
+ out.append(f"complete -c mobster -n '{condition}' {' '.join(parts)}{extra} "
256
+ f"-d '{_fish_text(option['help'])}'")
257
+ return out
258
+ lines += option_lines(f"not __fish_seen_subcommand_from {names}", tree["options"])
259
+ for name, entry in tree["commands"].items():
260
+ condition = f"__fish_seen_subcommand_from {name}"
261
+ lines += option_lines(condition, entry["options"])
262
+ for sub, child in entry["commands"].items():
263
+ lines.append(f"complete -c mobster -n '{condition}' -a {sub} -d '{_fish_text(child.get('help') or '')}'")
264
+ lines += option_lines(f"{condition}; and __fish_seen_subcommand_from {sub}", child["options"])
265
+ if entry["choices"]:
266
+ lines.append(f"complete -c mobster -n '{condition}' -a '{' '.join(entry['choices'])}'")
267
+ return "\n".join(lines) + "\n"
@@ -0,0 +1,48 @@
1
+ """Composition root: the driver for a run target, and best-effort shutdown."""
2
+
3
+ from . import harness_api
4
+ from .drivers import build_driver
5
+
6
+
7
+ def build_target_driver(*, wda_url=None, session=None, expected_bundle=None, connection=None, app_names=None,
8
+ labeller=None, icon_memory=None, **options):
9
+ if connection == "mirroring":
10
+ from . import mirroring
11
+ if not mirroring.enabled():
12
+ raise ValueError(f"The iPhone Mirroring connection is experimental: set {mirroring.ENV_FLAG}=1 to use it")
13
+ return build_driver("mirroring", app_names=app_names, labeller=labeller, icon_memory=icon_memory)
14
+ if connection == "cable_bt":
15
+ from . import cable_bt
16
+ if not cable_bt.enabled():
17
+ raise ValueError(f"The Cable + Bluetooth connection is experimental: set {cable_bt.ENV_FLAG}=1 to use it")
18
+ return build_driver("cable_bt", app_names=app_names, labeller=labeller, icon_memory=icon_memory, **options)
19
+ if connection not in (None, "wda"):
20
+ raise ValueError(f"Unknown connection {connection!r}")
21
+ if wda_url:
22
+ driver = build_driver("wda", url=wda_url, session=session)
23
+ driver.configure()
24
+ return driver
25
+ options = {key: value for key, value in options.items() if value is not None}
26
+ target_driver = harness_api.hooks().target_driver
27
+ if options and target_driver is not None:
28
+ return target_driver(expected_bundle=expected_bundle, **options)
29
+ raise ValueError("A run needs --wda-url (and optionally --session)")
30
+
31
+
32
+ def _closer(client, name):
33
+ try:
34
+ return getattr(client, name, None)
35
+ except Exception:
36
+ return None
37
+
38
+
39
+ def close_all(*clients):
40
+ for client in clients:
41
+ if client is None:
42
+ continue
43
+ for closer in (_closer(client, "close"), _closer(_closer(client, "http"), "close")):
44
+ if callable(closer):
45
+ try:
46
+ closer()
47
+ except Exception:
48
+ pass
mobile_agent/config.py ADDED
@@ -0,0 +1,114 @@
1
+ """Typed run budgets."""
2
+
3
+ from pathlib import Path
4
+ from typing import NamedTuple
5
+
6
+
7
+ class EnvFile(NamedTuple):
8
+ """What ``load_env_file`` did."""
9
+
10
+ loaded: list
11
+ warnings: list
12
+
13
+
14
+ def missing_env_file(path):
15
+ """The warning for an env file that doesn't exist: one sentence naming it (~ for the home folder)."""
16
+ shown = str(path)
17
+ home = str(Path.home())
18
+ if shown == home or shown.startswith(home + "/"):
19
+ shown = "~" + shown[len(home):]
20
+ return f"{shown} does not exist; no keys or settings were loaded from it"
21
+
22
+
23
+ def load_env_file(path, missing_ok=False):
24
+ """Load KEY=VALUE lines into the environment. Variables already set win; a bad line is a warning, never a stop."""
25
+ import os
26
+
27
+ from . import env_secrets
28
+ values, warnings = _parse_env_file(path, missing_ok)
29
+ loaded = []
30
+ # Settings can choose the secret store (MOBSTER_VAULT=file). App secrets come only from its locked read,
31
+ # never this plaintext snapshot, which may precede a key replacement or logout.
32
+ for key, value in values.items():
33
+ if key not in env_secrets.SECRET_NAMES and key not in os.environ:
34
+ os.environ[key] = value
35
+ loaded.append(key)
36
+ secrets, problems = env_secrets.secrets_for(path, values)
37
+ warnings += problems
38
+ if not env_secrets.applies(path):
39
+ secrets = {key: value for key, value in values.items() if key in env_secrets.SECRET_NAMES}
40
+ for key, value in secrets.items():
41
+ if key not in os.environ:
42
+ os.environ[key] = value
43
+ loaded.append(key)
44
+ return EnvFile(loaded, warnings)
45
+
46
+
47
+ def read_env_values(path):
48
+ """{NAME: value} for the file's valid lines, without touching the environment ({} when it can't be read)."""
49
+ return _parse_env_file(path, missing_ok=True)[0]
50
+
51
+
52
+ def _parse_env_file(path, missing_ok):
53
+ import re
54
+ values, warnings = {}, []
55
+ name = Path(path).name
56
+ try:
57
+ data = Path(path).read_bytes()
58
+ except FileNotFoundError:
59
+ if not missing_ok:
60
+ warnings.append(missing_env_file(path))
61
+ return values, warnings
62
+ except OSError as error:
63
+ warnings.append(f"{name} could not be read ({type(error).__name__}); no settings were loaded from it")
64
+ return values, warnings
65
+ for number, raw in enumerate(data.removeprefix(b"\xef\xbb\xbf").splitlines(), 1):
66
+ try:
67
+ line = raw.decode("utf-8").strip()
68
+ except UnicodeDecodeError:
69
+ warnings.append(f"{name} line {number} is not UTF-8 text; skipped it")
70
+ continue
71
+ if not line or line.startswith("#"):
72
+ continue
73
+ if "=" not in line:
74
+ warnings.append(f"{name} line {number} is not NAME=value; skipped it")
75
+ continue
76
+ key, value = (part.strip() for part in line.split("=", 1))
77
+ key = key.removeprefix("export ").strip()
78
+ if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", key):
79
+ warnings.append(f"{name} line {number} has a name with characters other than letters, digits "
80
+ "and _; skipped it")
81
+ continue
82
+ if len(value) >= 2 and value[0] == value[-1] and value[0] in "'\"":
83
+ value = value[1:-1]
84
+ if "\x00" in value:
85
+ warnings.append(f"{name} line {number} contains a null character; skipped it")
86
+ continue
87
+ values.setdefault(key, value) # the first line for a name wins, as it did when each line set the environment
88
+ return values, warnings
89
+
90
+
91
+ # Where each key and setting this process loaded came from: {name: "env-file" | "mac-app"}. A name set in the shell
92
+ # is not here. doctor and `mobster login status` read it to say "from Mobster for Mac".
93
+ SOURCES = {}
94
+
95
+
96
+ def app_env_file():
97
+ """The file Mobster for Mac (and `mobster login`) keep keys and settings in: agent.env in the data folder, 0600."""
98
+ from .paths import user_data_dir
99
+ return user_data_dir() / "agent.env"
100
+
101
+
102
+ def load_env_layers(env_file=None):
103
+ """Load an explicit --env-file, then the keys Mobster for Mac saved, never over a variable already set."""
104
+ warnings = []
105
+ if env_file:
106
+ loaded = load_env_file(env_file)
107
+ SOURCES.update({name: "env-file" for name in loaded.loaded})
108
+ warnings += loaded.warnings
109
+ path = app_env_file()
110
+ if env_file is None or Path(env_file).expanduser().resolve() != path.resolve():
111
+ loaded = load_env_file(path, missing_ok=True)
112
+ SOURCES.update({name: "mac-app" for name in loaded.loaded})
113
+ # The app's own file is never the user's to fix line by line: its bad lines are skipped quietly.
114
+ return warnings
@@ -0,0 +1 @@
1
+ """Connectors: the agent uses tools of other services (MCP servers) during a task."""
@@ -0,0 +1,76 @@
1
+ """What the connectors that answer MCP calls with a service's own API share (graph_api.py, slack_api.py): the tool
2
+ descriptor, the argument checks, ``Refusal``, and the session surface the connector service drives (client.Session's:
3
+ initialize, ping, list_tools, call_tool, close)."""
4
+
5
+ import json
6
+ import time
7
+
8
+ from ..outlets import net
9
+ from . import client as mcp
10
+
11
+
12
+ class Refusal:
13
+ """The service (or the arguments) said no before anything happened: the tool's error result."""
14
+
15
+ def __init__(self, message):
16
+ self.message = message
17
+
18
+
19
+ def tool_spec(name, title, description, properties, required=(), read_only=True):
20
+ return {"name": name, "title": title, "description": description,
21
+ "inputSchema": {"type": "object", "properties": properties, "required": list(required),
22
+ "additionalProperties": False},
23
+ "annotations": {"title": title, "readOnlyHint": read_only, "destructiveHint": False,
24
+ "openWorldHint": not read_only}}
25
+
26
+
27
+ def count_schema(top, default):
28
+ return {"type": "integer", "minimum": 1, "maximum": top, "description": f"At most {top}; {default} if left out."}
29
+
30
+
31
+ def short(value, limit):
32
+ return str(value)[:limit] if value is not None else None
33
+
34
+
35
+ def required_text(value, field, limit):
36
+ if not isinstance(value, str) or not value.strip():
37
+ raise ValueError(f"{field} is required")
38
+ if len(value) > limit:
39
+ raise ValueError(f"{field} is longer than {limit} characters")
40
+ return value.strip()
41
+
42
+
43
+ class ApiSession:
44
+ """A session over a service's API."""
45
+
46
+ NAME = TOOLS = None
47
+
48
+ def __init__(self, token, name=None, call=None):
49
+ self.token, self.name = token, name or self.NAME
50
+ self.call = call or net.call
51
+ self.ready, self.tools_changed = False, False
52
+ self.server_info, self.instructions = {}, ""
53
+
54
+ def list_tools(self, timeout=mcp.LIST_TIMEOUT):
55
+ return [json.loads(json.dumps(spec)) for spec in self.TOOLS]
56
+
57
+ def close(self):
58
+ self.ready = False
59
+
60
+ def call_tool(self, name, arguments, timeout=mcp.CALL_TIMEOUT):
61
+ """The tool's result ({"content", "structuredContent"?, "isError"?}), as client.Session.call_tool."""
62
+ if not self.ready:
63
+ raise mcp.NotSent(f"{self.name} isn't connected.")
64
+ method = getattr(self, f"_{name}", None) if name in {t["name"] for t in self.TOOLS} else None
65
+ if method is None:
66
+ raise mcp.ServerError(f"{self.name} has no tool called {name}.")
67
+ arguments = arguments if isinstance(arguments, dict) else {}
68
+ deadline = time.monotonic() + timeout
69
+ try:
70
+ found = method(arguments, deadline)
71
+ except ValueError as error:
72
+ found = Refusal(str(error))
73
+ if isinstance(found, Refusal):
74
+ return {"content": [{"type": "text", "text": found.message}], "isError": True}
75
+ return {"content": [{"type": "text", "text": json.dumps(found, ensure_ascii=False)}],
76
+ "structuredContent": found}
@@ -0,0 +1,146 @@
1
+ """The page your browser shows when a sign-in comes back to this Mac (oauth.SignIn's listener): Mobster's app icon and
2
+ the service's own mark, joined by a dotted line, then one line that says what happened."""
3
+
4
+ import base64
5
+ import hashlib
6
+ import html
7
+ import re
8
+
9
+ from .. import brand
10
+ from ..brand_fonts import FONT_FACES
11
+ from .marks import DARK, LIGHT, NAMES
12
+
13
+ APP_LINK = "mobster://connectors"
14
+ BACK = "Mobster is back in front. You can close this tab."
15
+ # The success page waits for its line to finish drawing (about 600 ms) before Mobster comes forward.
16
+ HAND_BACK = (f'setTimeout(function () {{ location.href = "{APP_LINK}" }}, '
17
+ f'matchMedia("(prefers-reduced-motion: reduce)").matches ? 0 : 900)')
18
+
19
+ # Mobster's app icon, drawn from the mark (brand.MARK): the Night tile, the felt slabs and the violet row between them.
20
+ _SLABS = re.findall(r"M[^M]*", re.search(r' d="([^"]+)"', brand.MARK["light"]).group(1))
21
+ _TILE = ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><rect width="512" height="512" fill="{night}"/>'
22
+ '<g transform="translate(256 256) scale(.7) translate(-256 -256)">{paths}</g></svg>').format(
23
+ night=brand.DARK["paper"],
24
+ paths="".join(f'<path d="{d}" fill="{brand.LIGHT["violet-solid"] if index == 1 else brand.LIGHT["sunken"]}"/>'
25
+ for index, d in enumerate(_SLABS)))
26
+ _SANS = "".join(face for face in re.findall(r"@font-face \{.*?\}", FONT_FACES) if '"Mobster Sans"' in face)
27
+
28
+
29
+ def _data(svg):
30
+ return "data:image/svg+xml;base64," + base64.b64encode(svg.encode()).decode()
31
+
32
+
33
+ def mark_of(name):
34
+ """The service's mark, by any name it goes by ("Google Drive", "monday.com"), as the tile's picture: the mark on the
35
+ tile's square (docs/brand/marks, scripts/gen_agent_marks.py), with its version for a dark page where it has one."""
36
+ key = NAMES.get(re.sub(r"[^a-z0-9]+", "-", (name or "").lower()).strip("-"))
37
+ if key is None:
38
+ return None
39
+ img = f'<img src="{_data(LIGHT[key])}" alt="">'
40
+ if key not in DARK:
41
+ return img
42
+ return f'<picture><source srcset="{_data(DARK[key])}" media="(prefers-color-scheme: dark)">{img}</picture>'
43
+
44
+
45
+ STYLE = """
46
+ /* A desktop page at desktop widths, as Remote's: Mobster at the gutter (32, 16 on a phone), then one centred 640 px
47
+ column with a 32 px title; a phone gets the same column at its own width. */
48
+ body { margin: 0; min-height: 100vh; display: grid; grid-template-rows: auto 1fr; background: var(--paper);
49
+ color: var(--ink); font: 400 16px/24px var(--font-sans); -webkit-font-smoothing: antialiased; }
50
+ .top { display: flex; align-items: center; min-height: 72px; padding: 16px 32px; }
51
+ .brand { display: inline-flex; align-items: center; gap: 8px; font-size: 17px; font-weight: 500; letter-spacing: -.01em; }
52
+ .brand img { display: block; width: 28px; height: 28px; border-radius: 23%; }
53
+ main { place-self: center; display: grid; justify-items: center; width: min(640px, calc(100vw - 32px)); padding: 48px 0;
54
+ text-align: center; }
55
+ .link { display: flex; align-items: center; gap: 12px; margin-bottom: 32px; }
56
+ .tile { position: relative; flex: none; display: grid; place-items: center; width: 80px; height: 80px;
57
+ border-radius: 23%; overflow: hidden; background: var(--surface); color: var(--ink-2); font-size: 32px; font-weight: 500; }
58
+ .tile img, .tile picture { display: block; width: 100%; height: 100%; }
59
+ .tile::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px rgb(24 24 24 / .08); }
60
+ .run { display: flex; gap: 8px; }
61
+ .run i { width: 4px; height: 4px; border-radius: 50%; background: var(--ink-4); }
62
+ .knot { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; }
63
+ .knot svg { display: block; }
64
+ [data-state="ok"] .run i { background: var(--green); }
65
+ [data-state="ok"] .knot { background: var(--green); color: var(--paper); }
66
+ [data-state="error"] .knot { background: var(--coral); color: var(--paper); }
67
+ /* Not allowed: the line breaks where the check would be, and its last dots fade into the gap. */
68
+ [data-state="denied"] .knot, [data-state="pick"] .knot { visibility: hidden; }
69
+ [data-state="denied"] .run:first-of-type i:last-child, [data-state="denied"] .run:last-of-type i:first-child { opacity: .35; }
70
+ h1 { margin: 0; font-size: 32px; line-height: 40px; font-weight: 500; letter-spacing: -.025em; text-wrap: balance; }
71
+ p { margin: 8px auto 0; max-width: 60ch; color: var(--ink-2); text-wrap: pretty; }
72
+ /* The title and the line take the column's width, centred inside it, so a short one doesn't shrink the page. */
73
+ main > h1, main > p { justify-self: stretch; }
74
+ a { display: inline-flex; align-items: center; height: 40px; margin-top: 32px; padding: 0 16px; border-radius: 999px;
75
+ background: var(--surface); box-shadow: var(--shadow-button-secondary); color: var(--ink); font-weight: 500;
76
+ letter-spacing: -.011em; text-decoration: none; transition: background-color 150ms var(--ease-standard); }
77
+ a:hover { background: var(--card-hover); }
78
+ a:focus-visible { outline: 2px solid var(--violet-solid); outline-offset: 2px; }
79
+ @media (prefers-color-scheme: dark) { .tile::after { box-shadow: inset 0 0 0 1px rgb(255 255 255 / .1); } }
80
+ @media (max-width: 639px) { .top { padding: 16px; } h1 { font-size: 24px; line-height: 32px; letter-spacing: -.022em; }
81
+ .tile { width: 64px; height: 64px; font-size: 28px; } .link { margin-bottom: 24px; } }
82
+ /* Connected: the dots arrive from each icon toward the middle, 50 ms apart, then the check grows in and draws. */
83
+ @media (prefers-reduced-motion: no-preference) {
84
+ [data-state="ok"] .run i { animation: dot 160ms var(--ease-out) both; }
85
+ [data-state="ok"] .run:first-of-type i:nth-child(2), [data-state="ok"] .run:last-of-type i:nth-child(3) { animation-delay: 50ms; }
86
+ [data-state="ok"] .run:first-of-type i:nth-child(3), [data-state="ok"] .run:last-of-type i:nth-child(2) { animation-delay: 100ms; }
87
+ [data-state="ok"] .run:first-of-type i:nth-child(4), [data-state="ok"] .run:last-of-type i:nth-child(1) { animation-delay: 150ms; }
88
+ [data-state="ok"] .knot { animation: knot 240ms var(--ease-out) 260ms both; }
89
+ [data-state="ok"] .knot path { stroke-dasharray: 16; animation: tick 200ms var(--ease-out) 400ms both; }
90
+ }
91
+ @keyframes dot { from { opacity: 0; transform: scale(.4); background: var(--ink-4); } }
92
+ @keyframes knot { from { opacity: 0; transform: scale(.6); } }
93
+ @keyframes tick { from { stroke-dashoffset: 16; } }
94
+ """ # noqa: E501
95
+
96
+ GLYPHS = {
97
+ "ok": '<path d="m7.5 12.5 3 3 6-6.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" '
98
+ 'stroke-linejoin="round"/>',
99
+ "error": '<path d="M12 7.5v5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
100
+ '<circle cx="12" cy="16.25" r="1.25" fill="currentColor"/>',
101
+ "denied": "",
102
+ "pick": "",
103
+ }
104
+
105
+
106
+ def render(state, name, message):
107
+ """The page for ``state`` ("ok", "denied" or "error") about the connector called ``name``, with its mark when the
108
+ name is a service's (a preset keeps its service's name). ``name`` and ``message`` are never markup."""
109
+ if state == "ok":
110
+ message = BACK
111
+ title = {"ok": f"Connected to {name}", "denied": f"{name} isn’t connected"}.get(state,
112
+ f"Mobster couldn’t connect {name}")
113
+ return document(state, name, title, message, script=HAND_BACK if state == "ok" else None)
114
+
115
+
116
+ def document(state, name, title, message, *, script=None, nonce=None, actions="", style="", referrer="no-referrer"):
117
+ """The page: Mobster's icon and ``name``'s mark joined in ``state``, then ``title`` and ``message`` (text, never
118
+ markup), ``actions`` (markup) beside Open Mobster, and ``script`` inline, with ``nonce`` when the page's CSP names
119
+ one."""
120
+ # The app's typography: a curly apostrophe, also in a message the service or the listener wrote.
121
+ title, message = (text.replace("'", "’") for text in (title, message))
122
+ theirs = mark_of(name) or html.escape((name or "?").strip()[:1].upper())
123
+ dots = '<span class="run">' + "<i></i>" * 4 + "</span>"
124
+ link = (f'<div class="link" aria-hidden="true"><span class="tile"><img src="{_data(_TILE)}" alt=""></span>{dots}'
125
+ '<span class="knot"><svg width="24" height="24" viewBox="0 0 24 '
126
+ f'24">{GLYPHS.get(state, GLYPHS["error"])}</svg></span>{dots}<span class="tile">{theirs}</span></div>')
127
+ attribute = f' nonce="{nonce}"' if nonce else ""
128
+ script = f"<script{attribute}>{script}</script>" if script else ""
129
+ back = f'<a href="{APP_LINK}">Open Mobster</a>'
130
+ return ('<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,'
131
+ 'initial-scale=1"><meta name="color-scheme" content="light dark"><meta '
132
+ f'name="referrer" content="{referrer}">'
133
+ f"<title>{html.escape(title)}</title><style>{_SANS}{brand.STYLESHEET}{STYLE}{style}</style>"
134
+ f'<header class="top"><span class="brand"><img src="{_data(_TILE)}" alt="">Mobster</span></header>'
135
+ f'<main data-state="{state if state in GLYPHS else "error"}" '
136
+ f'aria-live="polite">{link}<h1>{html.escape(title)}</h1><p>{html.escape(message)}</p>'
137
+ f'{f"<div class=actions>{actions}{back}</div>" if actions else back}</main>'
138
+ f"{script}</html>")
139
+
140
+
141
+ def csp():
142
+ """The page's Content-Security-Policy: nothing from outside, inline styles and data: pictures and fonts, and the
143
+ hand-back script by its hash."""
144
+ digest = base64.b64encode(hashlib.sha256(HAND_BACK.encode()).digest()).decode()
145
+ return ("default-src 'none'; style-src 'unsafe-inline'; img-src data:; font-src data:; script-src "
146
+ f"'sha256-{digest}'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'")