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.
- mobile_agent/__init__.py +4 -0
- mobile_agent/__main__.py +598 -0
- mobile_agent/accounts/__init__.py +1 -0
- mobile_agent/accounts/context.py +80 -0
- mobile_agent/accounts/mcp.py +55 -0
- mobile_agent/accounts/pacing.py +318 -0
- mobile_agent/accounts/register.py +78 -0
- mobile_agent/accounts/routes.py +212 -0
- mobile_agent/accounts/routing.py +69 -0
- mobile_agent/accounts/screen.py +273 -0
- mobile_agent/accounts/stats.py +54 -0
- mobile_agent/accounts/store.py +394 -0
- mobile_agent/agent_hooks.py +123 -0
- mobile_agent/ai_plans/__init__.py +7 -0
- mobile_agent/ai_plans/agents.py +96 -0
- mobile_agent/ai_plans/claude_code.py +208 -0
- mobile_agent/ai_plans/cli.py +400 -0
- mobile_agent/ai_plans/codex.py +258 -0
- mobile_agent/ai_plans/compat_chat.py +307 -0
- mobile_agent/ai_plans/device_login.py +164 -0
- mobile_agent/ai_plans/draft_client.py +68 -0
- mobile_agent/ai_plans/gemini.py +137 -0
- mobile_agent/ai_plans/instructions.py +65 -0
- mobile_agent/ai_plans/mcp_http.py +160 -0
- mobile_agent/ai_plans/phone_tools.py +892 -0
- mobile_agent/ai_plans/providers.py +235 -0
- mobile_agent/ai_plans/routes.py +160 -0
- mobile_agent/ai_plans/runner.py +400 -0
- mobile_agent/ai_plans/vault.py +114 -0
- mobile_agent/alerts.py +337 -0
- mobile_agent/android/__init__.py +18 -0
- mobile_agent/android/adb.py +268 -0
- mobile_agent/android/conditions.py +128 -0
- mobile_agent/android/doctor.py +54 -0
- mobile_agent/android/driver.py +137 -0
- mobile_agent/android/emulator.py +216 -0
- mobile_agent/android/logs.py +85 -0
- mobile_agent/android/manager.py +225 -0
- mobile_agent/android/records.py +31 -0
- mobile_agent/android/tree.py +96 -0
- mobile_agent/answer_shapes.py +230 -0
- mobile_agent/api_devices.py +333 -0
- mobile_agent/api_errors.py +17 -0
- mobile_agent/api_get.py +249 -0
- mobile_agent/api_http.py +467 -0
- mobile_agent/api_post.py +453 -0
- mobile_agent/api_routes.py +173 -0
- mobile_agent/app_api/__init__.py +4 -0
- mobile_agent/app_api/cli.py +151 -0
- mobile_agent/app_api/endpoints.py +193 -0
- mobile_agent/app_api/mcp.py +54 -0
- mobile_agent/app_api/openapi.py +146 -0
- mobile_agent/app_api/reading.py +172 -0
- mobile_agent/app_api/register.py +10 -0
- mobile_agent/app_api/screen.py +216 -0
- mobile_agent/app_api/service.py +382 -0
- mobile_agent/app_api/sessions.py +404 -0
- mobile_agent/app_api/tokens.py +167 -0
- mobile_agent/app_choice.py +131 -0
- mobile_agent/app_icons.py +183 -0
- mobile_agent/app_killer.py +69 -0
- mobile_agent/artifacts.py +606 -0
- mobile_agent/attach/__init__.py +13 -0
- mobile_agent/attach/cli.py +46 -0
- mobile_agent/attach/flutter.py +28 -0
- mobile_agent/attach/score.py +119 -0
- mobile_agent/attach/web.py +13 -0
- mobile_agent/attachments/__init__.py +1 -0
- mobile_agent/attachments/agent.py +418 -0
- mobile_agent/attachments/extract.py +217 -0
- mobile_agent/attachments/mcp.py +104 -0
- mobile_agent/attachments/register.py +73 -0
- mobile_agent/attachments/routes.py +326 -0
- mobile_agent/attachments/service.py +170 -0
- mobile_agent/attachments/sniff.py +152 -0
- mobile_agent/attachments/store.py +441 -0
- mobile_agent/audit_verify.py +436 -0
- mobile_agent/bench/README.md +410 -0
- mobile_agent/bench/__init__.py +0 -0
- mobile_agent/bench/__main__.py +394 -0
- mobile_agent/bench/ab.py +168 -0
- mobile_agent/bench/actuation.py +368 -0
- mobile_agent/bench/agents/__init__.py +9 -0
- mobile_agent/bench/agents/base.py +105 -0
- mobile_agent/bench/agents/plan_clients.py +245 -0
- mobile_agent/bench/agents/smart.py +533 -0
- mobile_agent/bench/checks.py +358 -0
- mobile_agent/bench/decide_gate.py +214 -0
- mobile_agent/bench/diagnose.py +181 -0
- mobile_agent/bench/evals/__init__.py +14 -0
- mobile_agent/bench/evals/build.py +565 -0
- mobile_agent/bench/evals/checkpoint.py +214 -0
- mobile_agent/bench/evals/irt.py +219 -0
- mobile_agent/bench/evals/l0.py +647 -0
- mobile_agent/bench/evals/l1.py +866 -0
- mobile_agent/bench/evals/models.py +124 -0
- mobile_agent/bench/evals/pairwise.py +189 -0
- mobile_agent/bench/evals/proxy.py +90 -0
- mobile_agent/bench/evals/sets.py +149 -0
- mobile_agent/bench/evals/traces.py +446 -0
- mobile_agent/bench/fixtures.py +176 -0
- mobile_agent/bench/guard_replay.py +129 -0
- mobile_agent/bench/h2-manifest.json +20 -0
- mobile_agent/bench/h3-manifest.json +20 -0
- mobile_agent/bench/heldout-manifest.json +32 -0
- mobile_agent/bench/heldout.py +375 -0
- mobile_agent/bench/iosworld.py +901 -0
- mobile_agent/bench/iosworld_stats.py +510 -0
- mobile_agent/bench/llm_units.py +116 -0
- mobile_agent/bench/manifest.json +93 -0
- mobile_agent/bench/metrics.py +66 -0
- mobile_agent/bench/phone.py +44 -0
- mobile_agent/bench/replay.py +172 -0
- mobile_agent/bench/report.py +207 -0
- mobile_agent/bench/runner.py +456 -0
- mobile_agent/bench/safety.py +310 -0
- mobile_agent/bench/stream_timing.py +213 -0
- mobile_agent/bench/suite.py +444 -0
- mobile_agent/bench/sweep.py +176 -0
- mobile_agent/bench/tape.py +798 -0
- mobile_agent/bench/tape_replay.py +88 -0
- mobile_agent/bench/trace_evals.py +128 -0
- mobile_agent/bench/trace_view.py +81 -0
- mobile_agent/bench/truth/iphone15pro.json +138 -0
- mobile_agent/bench/truth.py +340 -0
- mobile_agent/bench/tuning.py +543 -0
- mobile_agent/bench/tuning_analysis.py +656 -0
- mobile_agent/bench/vague-heldout-manifest.json +12 -0
- mobile_agent/bench/vague_rubric.py +135 -0
- mobile_agent/bench/vague_run.py +243 -0
- mobile_agent/brand.py +454 -0
- mobile_agent/brand_fonts.py +756 -0
- mobile_agent/build_identity.py +21 -0
- mobile_agent/cable_bt.py +623 -0
- mobile_agent/cable_bt_runtime.py +128 -0
- mobile_agent/catalog.py +69 -0
- mobile_agent/chart_svg.py +415 -0
- mobile_agent/chat_here.py +189 -0
- mobile_agent/cli_constants.py +5 -0
- mobile_agent/cli_parser.py +611 -0
- mobile_agent/cli_run.py +115 -0
- mobile_agent/compiled_read/__init__.py +21 -0
- mobile_agent/compiled_read/delta.py +89 -0
- mobile_agent/compiled_read/extractors.py +202 -0
- mobile_agent/compiled_read/routes.py +56 -0
- mobile_agent/completion.py +267 -0
- mobile_agent/compose.py +48 -0
- mobile_agent/config.py +114 -0
- mobile_agent/connectors/__init__.py +1 -0
- mobile_agent/connectors/api_session.py +76 -0
- mobile_agent/connectors/callback_page.py +146 -0
- mobile_agent/connectors/client.py +582 -0
- mobile_agent/connectors/github_app.py +55 -0
- mobile_agent/connectors/graph_api.py +396 -0
- mobile_agent/connectors/imports.py +170 -0
- mobile_agent/connectors/issue.py +66 -0
- mobile_agent/connectors/keystore.py +85 -0
- mobile_agent/connectors/marks.py +222 -0
- mobile_agent/connectors/oauth.py +476 -0
- mobile_agent/connectors/picker.py +228 -0
- mobile_agent/connectors/presets.py +591 -0
- mobile_agent/connectors/register.py +48 -0
- mobile_agent/connectors/routes.py +153 -0
- mobile_agent/connectors/service.py +966 -0
- mobile_agent/connectors/shared.py +136 -0
- mobile_agent/connectors/skill.py +446 -0
- mobile_agent/connectors/slack_api.py +269 -0
- mobile_agent/connectors/steps.py +92 -0
- mobile_agent/connectors/store.py +144 -0
- mobile_agent/connectors/vault.py +72 -0
- mobile_agent/console.py +178 -0
- mobile_agent/content_queue/__init__.py +1 -0
- mobile_agent/content_queue/client.py +263 -0
- mobile_agent/content_queue/importing.py +153 -0
- mobile_agent/content_queue/media_check.py +224 -0
- mobile_agent/content_queue/prompt.py +64 -0
- mobile_agent/content_queue/register.py +91 -0
- mobile_agent/content_queue/routes.py +370 -0
- mobile_agent/content_queue/runner.py +475 -0
- mobile_agent/content_queue/store.py +521 -0
- mobile_agent/contract.py +996 -0
- mobile_agent/contract_asks.py +248 -0
- mobile_agent/contract_prompt.py +45 -0
- mobile_agent/contract_screen.py +981 -0
- mobile_agent/contract_view.py +84 -0
- mobile_agent/corpus/__init__.py +26 -0
- mobile_agent/corpus/cli.py +63 -0
- mobile_agent/corpus/consent.py +55 -0
- mobile_agent/corpus/listener.py +76 -0
- mobile_agent/corpus/register.py +9 -0
- mobile_agent/corpus/skeleton.py +72 -0
- mobile_agent/corpus/store.py +143 -0
- mobile_agent/costs.py +161 -0
- mobile_agent/decider_scope.py +40 -0
- mobile_agent/decision_provider.py +189 -0
- mobile_agent/deeplinks.py +352 -0
- mobile_agent/device_info.py +116 -0
- mobile_agent/device_manager.py +978 -0
- mobile_agent/device_status.py +195 -0
- mobile_agent/device_targets.py +217 -0
- mobile_agent/devices.py +676 -0
- mobile_agent/devkit/__init__.py +64 -0
- mobile_agent/devkit/account_cli.py +90 -0
- mobile_agent/devkit/asc.py +562 -0
- mobile_agent/devkit/asc_cli.py +214 -0
- mobile_agent/devkit/ci.py +737 -0
- mobile_agent/devkit/ci_cli.py +196 -0
- mobile_agent/devkit/ci_hosts.py +194 -0
- mobile_agent/devkit/es256.py +197 -0
- mobile_agent/devkit/expo.py +440 -0
- mobile_agent/devkit/expo_cli.py +405 -0
- mobile_agent/devkit/expo_dev.py +203 -0
- mobile_agent/devkit/expo_routes.py +90 -0
- mobile_agent/devkit/fixtures_cli.py +133 -0
- mobile_agent/devkit/fixtures_routes.py +82 -0
- mobile_agent/devkit/links.py +375 -0
- mobile_agent/devkit/links_cli.py +123 -0
- mobile_agent/devkit/links_routes.py +83 -0
- mobile_agent/devkit/nightly.py +152 -0
- mobile_agent/devkit/shots.py +203 -0
- mobile_agent/devkit/testflight.py +155 -0
- mobile_agent/devkit/testflight_cli.py +127 -0
- mobile_agent/devkit/testflight_routes.py +111 -0
- mobile_agent/devkit/xcode.py +236 -0
- mobile_agent/devkit/xcode_cli.py +101 -0
- mobile_agent/devtools.py +419 -0
- mobile_agent/doctor.py +509 -0
- mobile_agent/doctor_fix.py +467 -0
- mobile_agent/downloads.py +55 -0
- mobile_agent/drivers.py +793 -0
- mobile_agent/dry_run.py +102 -0
- mobile_agent/effect_ledger.py +83 -0
- mobile_agent/engine_health.py +378 -0
- mobile_agent/engine_hooks.py +96 -0
- mobile_agent/engines.py +408 -0
- mobile_agent/env_file.py +63 -0
- mobile_agent/env_secrets.py +149 -0
- mobile_agent/errors.py +15 -0
- mobile_agent/evals/__init__.py +3 -0
- mobile_agent/evals/app_survey.py +149 -0
- mobile_agent/evals/development/__init__.py +1 -0
- mobile_agent/evals/development/score.py +276 -0
- mobile_agent/evals/latency_report.py +312 -0
- mobile_agent/evals/offline_report.py +374 -0
- mobile_agent/evals/oracles.py +298 -0
- mobile_agent/evals/redteam/__init__.py +15 -0
- mobile_agent/evals/redteam/__main__.py +72 -0
- mobile_agent/evals/redteam/cases.py +730 -0
- mobile_agent/evals/redteam/harness.py +604 -0
- mobile_agent/evals/redteam/live.py +139 -0
- mobile_agent/evals/redteam/plain.py +85 -0
- mobile_agent/evals/vision/__init__.py +0 -0
- mobile_agent/evals/vision/images/dog_001.jpg +0 -0
- mobile_agent/evals/vision/images/dog_002.jpg +0 -0
- mobile_agent/evals/vision/images/dog_003.jpg +0 -0
- mobile_agent/evals/vision/images/dog_004.jpg +0 -0
- mobile_agent/evals/vision/images/dog_005.jpg +0 -0
- mobile_agent/evals/vision/images/dog_006.jpg +0 -0
- mobile_agent/evals/vision/images/dog_007.jpg +0 -0
- mobile_agent/evals/vision/images/dog_010.jpg +0 -0
- mobile_agent/evals/vision/images/dog_011.jpg +0 -0
- mobile_agent/evals/vision/images/dog_013.jpg +0 -0
- mobile_agent/evals/vision/images/dog_014.jpg +0 -0
- mobile_agent/evals/vision/images/dog_015.jpg +0 -0
- mobile_agent/evals/vision/images/dog_016.jpg +0 -0
- mobile_agent/evals/vision/images/dog_017.jpg +0 -0
- mobile_agent/evals/vision/images/dog_019.jpg +0 -0
- mobile_agent/evals/vision/images/dog_020.jpg +0 -0
- mobile_agent/evals/vision/images/dog_021.jpg +0 -0
- mobile_agent/evals/vision/images/dog_022.jpg +0 -0
- mobile_agent/evals/vision/images/dog_023.jpg +0 -0
- mobile_agent/evals/vision/images/dog_024.jpg +0 -0
- mobile_agent/evals/vision/images/dog_026.jpg +0 -0
- mobile_agent/evals/vision/images/dog_027.jpg +0 -0
- mobile_agent/evals/vision/images/dog_034.jpg +0 -0
- mobile_agent/evals/vision/images/dog_035.jpg +0 -0
- mobile_agent/evals/vision/images/dog_038.jpg +0 -0
- mobile_agent/evals/vision/images/dog_042.jpg +0 -0
- mobile_agent/evals/vision/images/dog_047.jpg +0 -0
- mobile_agent/evals/vision/images/dog_052.jpg +0 -0
- mobile_agent/evals/vision/images/dog_058.jpg +0 -0
- mobile_agent/evals/vision/images/dog_062.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_000.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_010.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_011.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_015.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_016.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_017.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_019.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_021.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_025.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_026.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_027.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_030.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_032.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_033.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_034.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_035.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_036.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_037.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_039.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_040.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_041.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_042.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_043.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_044.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_046.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_047.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_048.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_049.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_051.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_053.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_054.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_055.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_058.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_002.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_003.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_004.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_007.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_009.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_010.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_011.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_012.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_013.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_014.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_016.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_017.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_023.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_027.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_029.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_032.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_033.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_036.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_041.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_043.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_047.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_049.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_051.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_053.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_056.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_057.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_058.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_060.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_061.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_062.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_063.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_064.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_065.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_066.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_067.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_068.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_069.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_070.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_071.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_000.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_001.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_002.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_004.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_005.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_008.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_009.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_010.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_012.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_013.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_015.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_016.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_017.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_018.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_021.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_022.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_023.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_024.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_030.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_031.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_032.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_033.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_034.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_036.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_038.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_039.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_040.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_041.jpg +0 -0
- mobile_agent/evals/vision/manifest.json +2355 -0
- mobile_agent/eventbus.py +110 -0
- mobile_agent/explain.py +34 -0
- mobile_agent/failure_line.py +197 -0
- mobile_agent/fanout.py +663 -0
- mobile_agent/feedback/__init__.py +2 -0
- mobile_agent/feedback/bundle.py +204 -0
- mobile_agent/feedback/outbox.py +196 -0
- mobile_agent/feedback/register.py +9 -0
- mobile_agent/feedback/routes.py +177 -0
- mobile_agent/fleet.py +841 -0
- mobile_agent/fleet_health.py +729 -0
- mobile_agent/fleet_phone.py +186 -0
- mobile_agent/frontier/__init__.py +79 -0
- mobile_agent/frontier/act.py +380 -0
- mobile_agent/frontier/actions.py +70 -0
- mobile_agent/frontier/agent.py +256 -0
- mobile_agent/frontier/approvals.py +366 -0
- mobile_agent/frontier/hooks.py +199 -0
- mobile_agent/frontier/interrupts.py +125 -0
- mobile_agent/frontier/observe.py +331 -0
- mobile_agent/frontier/phases.py +381 -0
- mobile_agent/frontier/pipeline.py +61 -0
- mobile_agent/frontier/prompt.py +384 -0
- mobile_agent/frontier/protocol.py +91 -0
- mobile_agent/frontier/safety.py +467 -0
- mobile_agent/frontier/saved.py +375 -0
- mobile_agent/frontier/screen.py +419 -0
- mobile_agent/frontier/skills.py +147 -0
- mobile_agent/frontier/stages/__init__.py +1 -0
- mobile_agent/frontier/stages/after.py +192 -0
- mobile_agent/frontier/stages/approve.py +138 -0
- mobile_agent/frontier/stages/controls.py +115 -0
- mobile_agent/frontier/stages/ends.py +102 -0
- mobile_agent/frontier/stages/gate.py +92 -0
- mobile_agent/frontier/stages/guards.py +132 -0
- mobile_agent/frontier/stages/points.py +155 -0
- mobile_agent/frontier/stages/send.py +177 -0
- mobile_agent/frontier/turn.py +151 -0
- mobile_agent/full_disk_access.py +83 -0
- mobile_agent/gemini.py +69 -0
- mobile_agent/genuine.py +120 -0
- mobile_agent/graph/__init__.py +4 -0
- mobile_agent/graph/catalog.py +789 -0
- mobile_agent/graph/chain.py +306 -0
- mobile_agent/graph/dispatch.py +98 -0
- mobile_agent/graph/draft.py +373 -0
- mobile_agent/graph/edit.py +76 -0
- mobile_agent/graph/estimate.py +189 -0
- mobile_agent/graph/expr.py +562 -0
- mobile_agent/graph/functions.py +271 -0
- mobile_agent/graph/infer.py +144 -0
- mobile_agent/graph/nodes.py +936 -0
- mobile_agent/graph/phone_steps.py +178 -0
- mobile_agent/graph/rerun.py +327 -0
- mobile_agent/graph/run.py +899 -0
- mobile_agent/graph/schema.py +842 -0
- mobile_agent/graph/state.py +161 -0
- mobile_agent/graph/values.py +198 -0
- mobile_agent/harness/__init__.py +2 -0
- mobile_agent/harness/checkpoints.py +468 -0
- mobile_agent/harness/control.py +101 -0
- mobile_agent/harness/prewarm.py +213 -0
- mobile_agent/harness/register.py +43 -0
- mobile_agent/harness/routes.py +114 -0
- mobile_agent/harness/settings.py +66 -0
- mobile_agent/harness/tools/__init__.py +67 -0
- mobile_agent/harness/tools/ask_user.py +102 -0
- mobile_agent/harness/tools/watch.py +120 -0
- mobile_agent/harness_api.py +815 -0
- mobile_agent/hedge.py +203 -0
- mobile_agent/help_topics.py +305 -0
- mobile_agent/hints.py +248 -0
- mobile_agent/history.py +69 -0
- mobile_agent/home.py +214 -0
- mobile_agent/http_pool.py +175 -0
- mobile_agent/image_policy.py +139 -0
- mobile_agent/inbox.py +148 -0
- mobile_agent/installed_apps.py +344 -0
- mobile_agent/integrations/__init__.py +1 -0
- mobile_agent/integrations/cli.py +472 -0
- mobile_agent/integrations/client_files.py +65 -0
- mobile_agent/integrations/clients.py +998 -0
- mobile_agent/integrations/doctor.py +185 -0
- mobile_agent/integrations/jsonc.py +383 -0
- mobile_agent/integrations/routes.py +135 -0
- mobile_agent/integrations/rules.py +112 -0
- mobile_agent/integrations/server_options.py +46 -0
- mobile_agent/integrations/skill.py +152 -0
- mobile_agent/integrations/skill_files.py +10 -0
- mobile_agent/integrations/textedit.py +309 -0
- mobile_agent/intents/__init__.py +17 -0
- mobile_agent/intents/atlas.py +216 -0
- mobile_agent/intents/callback.py +44 -0
- mobile_agent/intents/cli.py +55 -0
- mobile_agent/intents/harvest.py +91 -0
- mobile_agent/intents/plan.py +93 -0
- mobile_agent/ios/__init__.py +1 -0
- mobile_agent/ios/guide.py +16 -0
- mobile_agent/ios/kb.py +212 -0
- mobile_agent/ios/kb_data.py +1588 -0
- mobile_agent/ios/mcp.py +44 -0
- mobile_agent/ios/register.py +8 -0
- mobile_agent/ios/system.py +369 -0
- mobile_agent/journal.py +531 -0
- mobile_agent/keychain_acl.py +374 -0
- mobile_agent/keys.py +395 -0
- mobile_agent/latency_overlap.py +36 -0
- mobile_agent/latency_trace.py +170 -0
- mobile_agent/learned/__init__.py +15 -0
- mobile_agent/learned/app_path.py +160 -0
- mobile_agent/learned/code_read.py +38 -0
- mobile_agent/learned/parts.py +546 -0
- mobile_agent/learned/programs.py +691 -0
- mobile_agent/learned/register.py +171 -0
- mobile_agent/learned/routes.py +61 -0
- mobile_agent/learned/taught.py +329 -0
- mobile_agent/learned/teach.py +538 -0
- mobile_agent/learned/workflows.py +647 -0
- mobile_agent/learned/zero.py +41 -0
- mobile_agent/links.py +37 -0
- mobile_agent/listen.py +607 -0
- mobile_agent/lock_spike.py +278 -0
- mobile_agent/lockscreen.py +526 -0
- mobile_agent/login.py +354 -0
- mobile_agent/longform/__init__.py +15 -0
- mobile_agent/longform/ads.py +191 -0
- mobile_agent/longform/blocks.py +117 -0
- mobile_agent/longform/budget.py +265 -0
- mobile_agent/longform/context.py +285 -0
- mobile_agent/longform/costs.py +93 -0
- mobile_agent/longform/digest.py +145 -0
- mobile_agent/longform/extract.py +508 -0
- mobile_agent/longform/feed.py +505 -0
- mobile_agent/longform/followup.py +168 -0
- mobile_agent/longform/frames.py +82 -0
- mobile_agent/longform/identity.py +87 -0
- mobile_agent/longform/inbox.py +221 -0
- mobile_agent/longform/items.py +425 -0
- mobile_agent/longform/models.py +115 -0
- mobile_agent/longform/notebook.py +329 -0
- mobile_agent/longform/plan.py +614 -0
- mobile_agent/longform/recall.py +195 -0
- mobile_agent/longform/receipts.py +81 -0
- mobile_agent/longform/register.py +124 -0
- mobile_agent/longform/report.py +343 -0
- mobile_agent/longform/routes.py +102 -0
- mobile_agent/longform/session.py +644 -0
- mobile_agent/longform/store.py +146 -0
- mobile_agent/longform/synthesis.py +347 -0
- mobile_agent/mac_tools.py +223 -0
- mobile_agent/manual_control.py +143 -0
- mobile_agent/maps.py +149 -0
- mobile_agent/marks.py +92 -0
- mobile_agent/mcp_server/__init__.py +3 -0
- mobile_agent/mcp_server/__main__.py +29 -0
- mobile_agent/mcp_server/action_journal.py +24 -0
- mobile_agent/mcp_server/action_tools.py +638 -0
- mobile_agent/mcp_server/approvals.py +219 -0
- mobile_agent/mcp_server/catalog.py +92 -0
- mobile_agent/mcp_server/cli.py +194 -0
- mobile_agent/mcp_server/codes.py +147 -0
- mobile_agent/mcp_server/device_tools.py +306 -0
- mobile_agent/mcp_server/direct.py +272 -0
- mobile_agent/mcp_server/expo_tools.py +105 -0
- mobile_agent/mcp_server/phone_tools.py +510 -0
- mobile_agent/mcp_server/protocol.py +364 -0
- mobile_agent/mcp_server/registry.py +55 -0
- mobile_agent/mcp_server/run_tools.py +531 -0
- mobile_agent/mcp_server/schemas.py +425 -0
- mobile_agent/mcp_server/screen_tools.py +168 -0
- mobile_agent/mcp_server/session.py +469 -0
- mobile_agent/mcp_server/support.py +145 -0
- mobile_agent/mcp_server/tools.py +493 -0
- mobile_agent/memory/__init__.py +9 -0
- mobile_agent/memory/cli.py +207 -0
- mobile_agent/memory/extract.py +191 -0
- mobile_agent/memory/listener.py +100 -0
- mobile_agent/memory/model.py +144 -0
- mobile_agent/memory/project.py +96 -0
- mobile_agent/memory/provider.py +127 -0
- mobile_agent/memory/register.py +21 -0
- mobile_agent/memory/retrieval.py +172 -0
- mobile_agent/memory/routes.py +216 -0
- mobile_agent/memory/runs.py +124 -0
- mobile_agent/memory/store.py +806 -0
- mobile_agent/memory/writer.py +278 -0
- mobile_agent/mirroring.py +585 -0
- mobile_agent/mirroring_runtime.py +221 -0
- mobile_agent/model_clients.py +604 -0
- mobile_agent/narrate.py +813 -0
- mobile_agent/native_helpers.py +84 -0
- mobile_agent/netxray/__init__.py +1 -0
- mobile_agent/netxray/analyze.py +322 -0
- mobile_agent/netxray/attribution.py +62 -0
- mobile_agent/netxray/capture.py +88 -0
- mobile_agent/netxray/cli.py +142 -0
- mobile_agent/netxray/job.py +219 -0
- mobile_agent/netxray/mobster-pcap.c +560 -0
- mobile_agent/netxray/packets.py +410 -0
- mobile_agent/netxray/pcapng.py +223 -0
- mobile_agent/netxray/register.py +93 -0
- mobile_agent/netxray/report.py +214 -0
- mobile_agent/netxray/store.py +48 -0
- mobile_agent/netxray/summary.py +157 -0
- mobile_agent/netxray/target.py +67 -0
- mobile_agent/netxray/tool.py +66 -0
- mobile_agent/netxray/trackers.py +194 -0
- mobile_agent/notify_triggers/__init__.py +1 -0
- mobile_agent/notify_triggers/rules.py +39 -0
- mobile_agent/notify_triggers/sources.py +315 -0
- mobile_agent/notify_triggers/watch.py +135 -0
- mobile_agent/offers.py +86 -0
- mobile_agent/orchestrate.py +215 -0
- mobile_agent/outlets/__init__.py +1 -0
- mobile_agent/outlets/connect.py +249 -0
- mobile_agent/outlets/folder.py +180 -0
- mobile_agent/outlets/hooks.py +453 -0
- mobile_agent/outlets/inbound.py +472 -0
- mobile_agent/outlets/inbound_reply.py +57 -0
- mobile_agent/outlets/inbound_routes.py +81 -0
- mobile_agent/outlets/kinds.py +308 -0
- mobile_agent/outlets/mail.py +110 -0
- mobile_agent/outlets/net.py +86 -0
- mobile_agent/outlets/notion.py +244 -0
- mobile_agent/outlets/register.py +56 -0
- mobile_agent/outlets/routes.py +135 -0
- mobile_agent/outlets/service.py +580 -0
- mobile_agent/outlets/sheets.py +169 -0
- mobile_agent/outlets/slack.py +177 -0
- mobile_agent/outlets/store.py +194 -0
- mobile_agent/outlets/vault.py +80 -0
- mobile_agent/outlets/webhook.py +316 -0
- mobile_agent/output_contract.py +141 -0
- mobile_agent/owner_presence.py +211 -0
- mobile_agent/ownership.py +219 -0
- mobile_agent/packs/__init__.py +1 -0
- mobile_agent/packs/calendar.py +243 -0
- mobile_agent/packs/context.py +68 -0
- mobile_agent/packs/core.py +757 -0
- mobile_agent/packs/front_door.py +316 -0
- mobile_agent/packs/heal.py +214 -0
- mobile_agent/packs/health.py +122 -0
- mobile_agent/packs/insights.py +76 -0
- mobile_agent/packs/instagram.py +640 -0
- mobile_agent/packs/linkedin.py +106 -0
- mobile_agent/packs/maps.py +196 -0
- mobile_agent/packs/mcp.py +149 -0
- mobile_agent/packs/messages.py +270 -0
- mobile_agent/packs/notes.py +226 -0
- mobile_agent/packs/photos.py +166 -0
- mobile_agent/packs/pins.py +47 -0
- mobile_agent/packs/reddit.py +110 -0
- mobile_agent/packs/register.py +276 -0
- mobile_agent/packs/rows.py +108 -0
- mobile_agent/packs/safari.py +143 -0
- mobile_agent/packs/settings.py +253 -0
- mobile_agent/packs/social.py +300 -0
- mobile_agent/packs/threads.py +97 -0
- mobile_agent/packs/tiktok.py +455 -0
- mobile_agent/packs/tools.py +290 -0
- mobile_agent/packs/x.py +127 -0
- mobile_agent/packs/youtube.py +199 -0
- mobile_agent/parity.py +270 -0
- mobile_agent/paths.py +99 -0
- mobile_agent/perception.py +470 -0
- mobile_agent/permissions/__init__.py +32 -0
- mobile_agent/permissions/consequence.py +535 -0
- mobile_agent/permissions/exceptions.py +99 -0
- mobile_agent/permissions/gate.py +316 -0
- mobile_agent/permissions/outside.py +316 -0
- mobile_agent/permissions/policy.py +288 -0
- mobile_agent/permissions/project_policy.py +411 -0
- mobile_agent/permissions/register.py +148 -0
- mobile_agent/permissions/rules.py +291 -0
- mobile_agent/phone_io/__init__.py +146 -0
- mobile_agent/phone_io/cli.py +427 -0
- mobile_agent/phone_io/clipboard.py +78 -0
- mobile_agent/phone_io/controls.py +78 -0
- mobile_agent/phone_io/files.py +289 -0
- mobile_agent/phone_io/handoff.py +123 -0
- mobile_agent/phone_io/icloud.py +118 -0
- mobile_agent/phone_io/install.py +188 -0
- mobile_agent/phone_io/network_capture.py +188 -0
- mobile_agent/phone_io/network_cli.py +138 -0
- mobile_agent/phone_io/network_profiles.py +138 -0
- mobile_agent/phone_io/photos.py +512 -0
- mobile_agent/phone_io/recording.py +115 -0
- mobile_agent/phone_io/secrets.py +146 -0
- mobile_agent/phone_io/transfer.py +895 -0
- mobile_agent/phone_io/tunnel.py +129 -0
- mobile_agent/phone_ready.py +91 -0
- mobile_agent/pinning.py +158 -0
- mobile_agent/plan_gate.py +807 -0
- mobile_agent/plugins/__init__.py +3 -0
- mobile_agent/plugins/install.py +163 -0
- mobile_agent/plugins/manifest.py +242 -0
- mobile_agent/plugins/register.py +15 -0
- mobile_agent/plugins/routes.py +82 -0
- mobile_agent/plugins/service.py +322 -0
- mobile_agent/plugins/skills.py +76 -0
- mobile_agent/plugins/store.py +79 -0
- mobile_agent/pool.py +285 -0
- mobile_agent/power_watch.py +275 -0
- mobile_agent/premium_model.py +22 -0
- mobile_agent/project_env.py +291 -0
- mobile_agent/project_env_cli.py +74 -0
- mobile_agent/project_env_routes.py +55 -0
- mobile_agent/projects/__init__.py +27 -0
- mobile_agent/projects/ci.py +332 -0
- mobile_agent/projects/clone.py +339 -0
- mobile_agent/projects/drafts.py +158 -0
- mobile_agent/projects/folder.py +436 -0
- mobile_agent/projects/github.py +131 -0
- mobile_agent/projects/register.py +31 -0
- mobile_agent/projects/revisions.py +253 -0
- mobile_agent/projects/routes.py +234 -0
- mobile_agent/projects/runs.py +297 -0
- mobile_agent/projects/service.py +695 -0
- mobile_agent/projects/setup.py +84 -0
- mobile_agent/projects/store.py +379 -0
- mobile_agent/projects/sync.py +380 -0
- mobile_agent/projects/workflow_files.py +270 -0
- mobile_agent/replay_core/__init__.py +26 -0
- mobile_agent/replay_core/checks.py +594 -0
- mobile_agent/replay_core/engine.py +596 -0
- mobile_agent/replay_core/heal.py +364 -0
- mobile_agent/replay_core/path.py +240 -0
- mobile_agent/replay_core/recorder.py +206 -0
- mobile_agent/replay_core/store.py +171 -0
- mobile_agent/result_blocks.py +941 -0
- mobile_agent/run.py +642 -0
- mobile_agent/run_delivery.py +123 -0
- mobile_agent/run_executor.py +200 -0
- mobile_agent/run_export.py +284 -0
- mobile_agent/run_gif.py +891 -0
- mobile_agent/runner_hints.py +86 -0
- mobile_agent/runner_renewal.py +205 -0
- mobile_agent/runtime_create.py +549 -0
- mobile_agent/runtime_driver.py +458 -0
- mobile_agent/runtime_limits.py +319 -0
- mobile_agent/runtime_queue.py +394 -0
- mobile_agent/runtime_runs.py +383 -0
- mobile_agent/runtime_status.py +181 -0
- mobile_agent/runtime_teach.py +295 -0
- mobile_agent/runtime_work.py +599 -0
- mobile_agent/screen_text.py +101 -0
- mobile_agent/secret_filter.py +113 -0
- mobile_agent/serve.py +160 -0
- mobile_agent/server.py +363 -0
- mobile_agent/service_support.py +42 -0
- mobile_agent/settle/__init__.py +1 -0
- mobile_agent/settle/predict.py +102 -0
- mobile_agent/settle/replay.py +92 -0
- mobile_agent/settle/store.py +93 -0
- mobile_agent/settle/stream.py +43 -0
- mobile_agent/setup_cli.py +225 -0
- mobile_agent/setup_errors.py +87 -0
- mobile_agent/setup_service.py +527 -0
- mobile_agent/share.py +104 -0
- mobile_agent/signing.py +184 -0
- mobile_agent/sim/__init__.py +6 -0
- mobile_agent/sim/__main__.py +26 -0
- mobile_agent/sim/api.py +952 -0
- mobile_agent/sim/apps.py +172 -0
- mobile_agent/sim/biometrics.py +84 -0
- mobile_agent/sim/cli.py +271 -0
- mobile_agent/sim/doctor.py +293 -0
- mobile_agent/sim/governor.py +90 -0
- mobile_agent/sim/holders.py +88 -0
- mobile_agent/sim/models.py +62 -0
- mobile_agent/sim/registry.py +161 -0
- mobile_agent/sim/simctl.py +269 -0
- mobile_agent/sim/wda.py +618 -0
- mobile_agent/sim/xcode.py +74 -0
- mobile_agent/skills/__init__.py +12 -0
- mobile_agent/skills/codes.py +597 -0
- mobile_agent/skills/notifications.py +281 -0
- mobile_agent/skills/open_url.py +127 -0
- mobile_agent/smart_events.py +588 -0
- mobile_agent/smart_result.py +490 -0
- mobile_agent/smart_run.py +413 -0
- mobile_agent/state.py +662 -0
- mobile_agent/states.py +217 -0
- mobile_agent/steering.py +70 -0
- mobile_agent/style.py +178 -0
- mobile_agent/supervised.py +234 -0
- mobile_agent/swift/helper_bt_hid.swift +534 -0
- mobile_agent/swift/helper_doc_text.swift +184 -0
- mobile_agent/swift/helper_draft_reply.swift +170 -0
- mobile_agent/swift/helper_map.swift +110 -0
- mobile_agent/swift/helper_mirroring.swift +1099 -0
- mobile_agent/swift/helper_mjpeg_mp4.swift +103 -0
- mobile_agent/swift/helper_pdf.swift +100 -0
- mobile_agent/swift/helper_phone_audio.swift +993 -0
- mobile_agent/swift/helper_phone_screen.swift +416 -0
- mobile_agent/swift/helper_screen_text.swift +24 -0
- mobile_agent/swift/helper_transfer.swift +246 -0
- mobile_agent/swift/ocr.swift +22 -0
- mobile_agent/task_policy.py +337 -0
- mobile_agent/team_signing/__init__.py +34 -0
- mobile_agent/team_signing/apple_ca.py +37 -0
- mobile_agent/team_signing/bundle.py +136 -0
- mobile_agent/team_signing/cli.py +208 -0
- mobile_agent/team_signing/identity.py +248 -0
- mobile_agent/team_signing/ledger.py +105 -0
- mobile_agent/team_signing/prebuilt.py +61 -0
- mobile_agent/team_signing/resign.py +256 -0
- mobile_agent/team_signing/service.py +382 -0
- mobile_agent/team_signing/tooling.py +243 -0
- mobile_agent/team_signing/writes.py +268 -0
- mobile_agent/terminal_image.py +81 -0
- mobile_agent/testkit/__init__.py +1 -0
- mobile_agent/testkit/a11y_baseline.py +132 -0
- mobile_agent/testkit/api.py +403 -0
- mobile_agent/testkit/capsule.py +400 -0
- mobile_agent/testkit/capsule_cli.py +57 -0
- mobile_agent/testkit/cli.py +948 -0
- mobile_agent/testkit/cli_arguments.py +193 -0
- mobile_agent/testkit/comparison.py +288 -0
- mobile_agent/testkit/comparison_cli.py +41 -0
- mobile_agent/testkit/discover.py +257 -0
- mobile_agent/testkit/editor.py +243 -0
- mobile_agent/testkit/execute.py +515 -0
- mobile_agent/testkit/github_checks.py +248 -0
- mobile_agent/testkit/gitref.py +66 -0
- mobile_agent/testkit/heal_review.py +176 -0
- mobile_agent/testkit/health.py +480 -0
- mobile_agent/testkit/history.py +255 -0
- mobile_agent/testkit/hosted_results.py +321 -0
- mobile_agent/testkit/input_files.py +173 -0
- mobile_agent/testkit/jobs.py +285 -0
- mobile_agent/testkit/junit_verdict.py +33 -0
- mobile_agent/testkit/maestro.py +802 -0
- mobile_agent/testkit/mcp.py +562 -0
- mobile_agent/testkit/mcp_comparison.py +88 -0
- mobile_agent/testkit/minimize.py +471 -0
- mobile_agent/testkit/minimizer_cli.py +48 -0
- mobile_agent/testkit/notify.py +212 -0
- mobile_agent/testkit/phone_video.py +191 -0
- mobile_agent/testkit/pr.py +491 -0
- mobile_agent/testkit/pr_report.py +562 -0
- mobile_agent/testkit/prbuild.py +148 -0
- mobile_agent/testkit/proof_report.py +55 -0
- mobile_agent/testkit/provenance.py +119 -0
- mobile_agent/testkit/prrun.py +244 -0
- mobile_agent/testkit/recent.py +166 -0
- mobile_agent/testkit/recipes.py +146 -0
- mobile_agent/testkit/record.py +204 -0
- mobile_agent/testkit/register.py +288 -0
- mobile_agent/testkit/reports.py +998 -0
- mobile_agent/testkit/repro.py +432 -0
- mobile_agent/testkit/scenario.py +440 -0
- mobile_agent/testkit/scenario_cli.py +37 -0
- mobile_agent/testkit/snapshots_cli.py +60 -0
- mobile_agent/testkit/suite.py +670 -0
- mobile_agent/testkit/targets.py +497 -0
- mobile_agent/testkit/xctest.py +378 -0
- mobile_agent/threads/__init__.py +1 -0
- mobile_agent/threads/chat_once.py +264 -0
- mobile_agent/threads/cli.py +740 -0
- mobile_agent/threads/client.py +326 -0
- mobile_agent/threads/context.py +246 -0
- mobile_agent/threads/mcp.py +739 -0
- mobile_agent/threads/recent.py +34 -0
- mobile_agent/threads/register.py +54 -0
- mobile_agent/threads/routes.py +188 -0
- mobile_agent/threads/service.py +975 -0
- mobile_agent/threads/store.py +361 -0
- mobile_agent/threads/summary.py +117 -0
- mobile_agent/threads/term.py +101 -0
- mobile_agent/threads/workflow_cli.py +104 -0
- mobile_agent/tls.py +36 -0
- mobile_agent/traces.py +271 -0
- mobile_agent/tracks.py +235 -0
- mobile_agent/transport.py +271 -0
- mobile_agent/triggers/__init__.py +1 -0
- mobile_agent/triggers/actions.py +204 -0
- mobile_agent/triggers/model.py +146 -0
- mobile_agent/triggers/register.py +29 -0
- mobile_agent/triggers/routes.py +101 -0
- mobile_agent/triggers/service.py +387 -0
- mobile_agent/triggers/store.py +142 -0
- mobile_agent/tui/__init__.py +89 -0
- mobile_agent/tui/app.py +582 -0
- mobile_agent/tui/chrome.py +321 -0
- mobile_agent/tui/commands.py +375 -0
- mobile_agent/tui/demo_phone.py +369 -0
- mobile_agent/tui/images.py +29 -0
- mobile_agent/tui/keys.py +64 -0
- mobile_agent/tui/prompt.py +195 -0
- mobile_agent/tui/render.py +395 -0
- mobile_agent/tui/screens.py +180 -0
- mobile_agent/tui/session.py +308 -0
- mobile_agent/tui/tasks.py +287 -0
- mobile_agent/tui/theme.py +118 -0
- mobile_agent/tui/widgets.py +272 -0
- mobile_agent/update_check.py +213 -0
- mobile_agent/usage.py +184 -0
- mobile_agent/verify/__init__.py +0 -0
- mobile_agent/verify/__main__.py +41 -0
- mobile_agent/verify/a11y.py +189 -0
- mobile_agent/verify/actions.py +290 -0
- mobile_agent/verify/assertions.py +570 -0
- mobile_agent/verify/audit.py +312 -0
- mobile_agent/verify/business.py +221 -0
- mobile_agent/verify/business_run.py +131 -0
- mobile_agent/verify/business_state.py +168 -0
- mobile_agent/verify/check_schema.py +398 -0
- mobile_agent/verify/checks.py +614 -0
- mobile_agent/verify/cli.py +423 -0
- mobile_agent/verify/conditions.py +483 -0
- mobile_agent/verify/crashes.py +321 -0
- mobile_agent/verify/device_audit.py +246 -0
- mobile_agent/verify/dialogs.py +287 -0
- mobile_agent/verify/evidence.py +705 -0
- mobile_agent/verify/fixtures.py +305 -0
- mobile_agent/verify/flow.py +213 -0
- mobile_agent/verify/l10n.py +141 -0
- mobile_agent/verify/netcerts.py +67 -0
- mobile_agent/verify/netproxy.py +356 -0
- mobile_agent/verify/netrun.py +81 -0
- mobile_agent/verify/netshim.m +28 -0
- mobile_agent/verify/netshim.py +48 -0
- mobile_agent/verify/netstubs.py +132 -0
- mobile_agent/verify/overlay.py +174 -0
- mobile_agent/verify/perf.py +239 -0
- mobile_agent/verify/proof.py +192 -0
- mobile_agent/verify/replay_admission.py +48 -0
- mobile_agent/verify/report.py +952 -0
- mobile_agent/verify/run_cleanup.py +67 -0
- mobile_agent/verify/run_links.py +48 -0
- mobile_agent/verify/run_perf.py +44 -0
- mobile_agent/verify/run_secrets.py +39 -0
- mobile_agent/verify/runner.py +976 -0
- mobile_agent/verify/runs.py +57 -0
- mobile_agent/verify/screen_coverage.py +181 -0
- mobile_agent/verify/smart.py +106 -0
- mobile_agent/verify/snapshot_run.py +114 -0
- mobile_agent/verify/snapshots.py +306 -0
- mobile_agent/verify/sound.py +335 -0
- mobile_agent/verify/source_observation.py +130 -0
- mobile_agent/verify/system_surfaces/__init__.py +7 -0
- mobile_agent/verify/system_surfaces/play.py +106 -0
- mobile_agent/verify/system_surfaces/spec.py +139 -0
- mobile_agent/verify/system_surfaces/stage.py +138 -0
- mobile_agent/verify/system_surfaces/surfaces.py +121 -0
- mobile_agent/verify/tree.py +223 -0
- mobile_agent/verify/verdict.py +377 -0
- mobile_agent/verify/wire.py +392 -0
- mobile_agent/verify/wirestore.py +176 -0
- mobile_agent/waiting/__init__.py +1 -0
- mobile_agent/waiting/contacts.py +73 -0
- mobile_agent/waiting/drafts.py +136 -0
- mobile_agent/waiting/job.py +145 -0
- mobile_agent/waiting/messages.py +174 -0
- mobile_agent/waiting/rank.py +115 -0
- mobile_agent/waiting/register.py +26 -0
- mobile_agent/watch.py +502 -0
- mobile_agent/wda/__init__.py +2 -0
- mobile_agent/wda/frame_clock.py +646 -0
- mobile_agent/wda/frame_source.py +325 -0
- mobile_agent/wda/frame_store.py +115 -0
- mobile_agent/wda/gate.py +163 -0
- mobile_agent/wda/gestures.py +291 -0
- mobile_agent/wda/guards.py +94 -0
- mobile_agent/wda/identity.py +55 -0
- mobile_agent/wda/observe.py +884 -0
- mobile_agent/wda/patches.py +856 -0
- mobile_agent/wda/point_gestures.py +150 -0
- mobile_agent/wda/source.py +32 -0
- mobile_agent/wda/system_dialogs.py +571 -0
- mobile_agent/wda/text.py +694 -0
- mobile_agent/wda/timeouts.py +77 -0
- mobile_agent/wda/video.py +505 -0
- mobile_agent/wda/watchdog.py +243 -0
- mobile_agent/wireless/__init__.py +15 -0
- mobile_agent/wireless/api.py +354 -0
- mobile_agent/wireless/cli.py +287 -0
- mobile_agent/wireless/devicectl.py +229 -0
- mobile_agent/wireless/exposure.py +33 -0
- mobile_agent/wireless/mobster-wifi-pair.c +122 -0
- mobile_agent/wireless/monitor.py +332 -0
- mobile_agent/wireless/network.py +334 -0
- mobile_agent/wireless/onboarding.py +293 -0
- mobile_agent/wireless/pair.py +124 -0
- mobile_agent/wireless/probe.py +322 -0
- mobile_agent/wireless/register.py +8 -0
- mobile_agent/wireless/relay.py +179 -0
- mobile_agent/wireless/result.py +87 -0
- mobile_agent/wireless/store.py +85 -0
- mobile_agent/wireless/switch.py +77 -0
- mobile_agent/wireless/transport.py +118 -0
- mobile_agent/wireless/words.py +81 -0
- mobile_agent/wireless/xctestrun.py +83 -0
- mobile_agent/workflow_tools.py +59 -0
- mobile_agent/workflows/__init__.py +2 -0
- mobile_agent/workflows/brief.py +201 -0
- mobile_agent/workflows/cron.py +110 -0
- mobile_agent/workflows/health.py +257 -0
- mobile_agent/workflows/history.py +126 -0
- mobile_agent/workflows/records.py +442 -0
- mobile_agent/workflows/runs.py +1000 -0
- mobile_agent/workflows/scheduler.py +134 -0
- mobile_agent/workflows/service.py +973 -0
- mobile_agent/workflows/starters.py +285 -0
- mobile_agent/workflows/triggers.py +248 -0
- mobile_agent/xray/__init__.py +1 -0
- mobile_agent/xray/app_cli.py +116 -0
- mobile_agent/xray/cli.py +21 -0
- mobile_agent/xray/crawl.py +754 -0
- mobile_agent/xray/diff.py +171 -0
- mobile_agent/xray/diff_cli.py +43 -0
- mobile_agent/xray/job.py +184 -0
- mobile_agent/xray/paywall.py +188 -0
- mobile_agent/xray/policy.py +305 -0
- mobile_agent/xray/register.py +43 -0
- mobile_agent/xray/report.py +283 -0
- mobile_agent/xray/screens.py +117 -0
- mobster_cli-0.4.3.dist-info/METADATA +93 -0
- mobster_cli-0.4.3.dist-info/RECORD +1009 -0
- mobster_cli-0.4.3.dist-info/WHEEL +5 -0
- mobster_cli-0.4.3.dist-info/entry_points.txt +3 -0
- mobster_cli-0.4.3.dist-info/licenses/LICENSE +21 -0
- mobster_cli-0.4.3.dist-info/top_level.txt +1 -0
mobile_agent/ios/mcp.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""The iOS guide over MCP (``ios_docs``): answers from the local knowledge base, reads no phone and changes nothing."""
|
|
2
|
+
|
|
3
|
+
from . import kb
|
|
4
|
+
|
|
5
|
+
TOOL = "ios_docs"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IosDocsProvider:
|
|
9
|
+
name = "ios"
|
|
10
|
+
|
|
11
|
+
def definitions(self, toolset):
|
|
12
|
+
return [{"name": TOOL, "title": "Look up how iOS works",
|
|
13
|
+
"description": ("How iOS itself works, from Mobster's iPhone guide for iOS 26 and 18: the App "
|
|
14
|
+
"Library, Spotlight, where a setting is in Settings, Control Center, the keyboard, "
|
|
15
|
+
"iOS's alerts. Local; reads no phone."),
|
|
16
|
+
"inputSchema": {"type": "object", "additionalProperties": False, "required": ["query"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"query": {"type": "string", "minLength": 2, "maxLength": 300,
|
|
19
|
+
"description": "What to look up, in plain words."},
|
|
20
|
+
"ios_version": {"type": "string", "maxLength": 20,
|
|
21
|
+
"description": "The iPhone's iOS version (\"26.0.1\", "
|
|
22
|
+
"\"18.4\"). Leave it out for the newest."},
|
|
23
|
+
"limit": {"type": "integer", "minimum": 1, "maximum": kb.MAX_K,
|
|
24
|
+
"description": "How many entries (default 4)."}}}}]
|
|
25
|
+
|
|
26
|
+
def annotations(self):
|
|
27
|
+
from ..mcp_server.tools import READ_ONLY
|
|
28
|
+
return {TOOL: dict(READ_ONLY)}
|
|
29
|
+
|
|
30
|
+
def instructions(self):
|
|
31
|
+
return "For how iOS itself works (Settings paths, the App Library, system alerts), ask ios_docs first."
|
|
32
|
+
|
|
33
|
+
def call(self, name, arguments, call, toolset):
|
|
34
|
+
from ..mcp_server.protocol import ToolResult
|
|
35
|
+
from ..mcp_server.tools import ToolError
|
|
36
|
+
if name != TOOL:
|
|
37
|
+
raise ToolError(f"Mobster has no tool named {name[:80]}.")
|
|
38
|
+
query = " ".join(str(arguments.get("query") or "").split())
|
|
39
|
+
if len(query) < 2:
|
|
40
|
+
raise ToolError("ios_docs needs a query of at least 2 characters.")
|
|
41
|
+
version = arguments.get("ios_version")
|
|
42
|
+
found = kb.query(query, version, arguments.get("limit") or 4)
|
|
43
|
+
return ToolResult(kb.answer(query, version, arguments.get("limit") or 4),
|
|
44
|
+
structured={"iosVersion": kb.known(version), "entries": [e.public() for e in found]})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Track ``ios``'s registrations (tracks.load calls ``register(api)`` once per process, staged)."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def register(api):
|
|
5
|
+
"""``api`` is mobile_agent.harness_api."""
|
|
6
|
+
from ..mcp_server import registry as mcp_registry
|
|
7
|
+
from . import mcp
|
|
8
|
+
mcp_registry.register_provider(mcp.IosDocsProvider())
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"""iOS's own moves as code, shared by every connection: the WebDriverAgent tree, iPhone Mirroring, and Cable +
|
|
2
|
+
Bluetooth.
|
|
3
|
+
|
|
4
|
+
Each primitive takes a driver with the device contract (``drivers.Driver``: ``observe``, ``execute``; optionally
|
|
5
|
+
``wait_for_change``, ``active_app``, ``system_shortcut``, ``type_focused``) and returns an
|
|
6
|
+
``Outcome``. It reads the screen after every move and stops as soon as it isn't sure where it is: it never guesses
|
|
7
|
+
and never retries a move it sent. Nothing here taps a control that commits anything, and none of them needs a model.
|
|
8
|
+
|
|
9
|
+
- ``open_app(driver, bundle, name)``: by bundle id where the connection can (WebDriverAgent's activate); otherwise
|
|
10
|
+
Spotlight, then the App Library's search, then the Home Screen's pages. Spotlight counts only when its Top Hit is the
|
|
11
|
+
app: with "Show App in Search" off for an app (Settings › Search), Spotlight lists only web and dictionary results,
|
|
12
|
+
and Return would open those (a real iPhone on iOS 26.0.1, 14 Oct 2026). The App Library lists every installed app
|
|
13
|
+
either way. A ``RouteMemory`` remembers which route worked on this phone, so the next open goes straight there.
|
|
14
|
+
- ``go_home``: the Home Screen's first page.
|
|
15
|
+
- iOS's alerts are ``system_dialogs``: it reads them from WebDriverAgent, or from the screen on a connection that sees
|
|
16
|
+
only pixels, and answers them under the user's permission mode.
|
|
17
|
+
|
|
18
|
+
Labels checked on an iOS 26.4 simulator's tree (14 Oct 2026): the Home Screen's "Page control" ("Page 1 of 3") and
|
|
19
|
+
"Search" button above the Dock; the App Library's search field (value "App Library"), its results as rows named by
|
|
20
|
+
the app; Spotlight's "SpotlightSearchField", its "Top Hit" with the app's cell, and the field's inline "Settings —
|
|
21
|
+
Open" completion.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
import time
|
|
26
|
+
import weakref
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
|
|
29
|
+
from . import kb
|
|
30
|
+
|
|
31
|
+
SPRINGBOARD = "com.apple.springboard"
|
|
32
|
+
SPOTLIGHT = "com.apple.Spotlight"
|
|
33
|
+
ROUTES = ("bundle", "spotlight", "app_library", "home_pages")
|
|
34
|
+
# The most Home Screen pages swiped past before the App Library (iOS allows 15 pages).
|
|
35
|
+
MAX_PAGES = 15
|
|
36
|
+
# The budget for a move's screen to settle and be read (a WebDriverAgent read under load takes 1-3 s).
|
|
37
|
+
SETTLE = 8.0
|
|
38
|
+
RESULTS_WAIT = 1.2
|
|
39
|
+
# Where Spotlight's Top Hit and results sit, and the App Library's results (screen fractions, top to bottom).
|
|
40
|
+
RESULTS_BAND = (.06, .62)
|
|
41
|
+
# Refusals a connection makes before it sends anything (wda.guards.DriverRejection.PRE_DISPATCH's first cause).
|
|
42
|
+
PRE_DISPATCH = frozenset({"stale_revision", "unsupported_or_stale_target"})
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Outcome:
|
|
47
|
+
ok: bool
|
|
48
|
+
route: str = ""
|
|
49
|
+
detail: str = ""
|
|
50
|
+
steps: list = field(default_factory=list)
|
|
51
|
+
snapshot: object = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Unsure(Exception):
|
|
55
|
+
"""The screen isn't what the primitive expects: it stops there."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def fold(text):
|
|
59
|
+
return " ".join(str(text or "").replace(" ", " ").replace("…", "").split()).casefold()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
MAGNIFIER = re.compile(r"^q\s+(?=\S)")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def field_name(element):
|
|
66
|
+
"""What a search field says, without the magnifier OCR reads as "Q " ("Q App Library" -> "app library")."""
|
|
67
|
+
return MAGNIFIER.sub("", fold(element.label)) if element.role == "StaticText" else fold(element.label)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def labels(snapshot):
|
|
71
|
+
return [e.label for e in getattr(snapshot, "elements", ()) or ()]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _wait(driver, snapshot, seconds=.6):
|
|
75
|
+
"""The screen after a move: the driver's own wait when it has one, else a fresh read."""
|
|
76
|
+
wait = getattr(driver, "wait_for_change", None)
|
|
77
|
+
if callable(wait) and snapshot is not None:
|
|
78
|
+
try:
|
|
79
|
+
return wait(snapshot, timeout=SETTLE, wait_seconds=seconds)
|
|
80
|
+
except TimeoutError:
|
|
81
|
+
pass # a slow read: settle by a plain read below
|
|
82
|
+
time.sleep(seconds)
|
|
83
|
+
return driver.observe()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _tap(driver, element, snapshot, steps, why):
|
|
87
|
+
"""Tap ``element``. When the connection refused before sending anything because the screen moved since it was
|
|
88
|
+
read (WebDriverAgent's stale_revision), the same control is found on a fresh read and tapped once; nothing was
|
|
89
|
+
sent, so this is no retry of an action whose outcome is unknown."""
|
|
90
|
+
try:
|
|
91
|
+
driver.execute("TAP", element, snapshot)
|
|
92
|
+
except Exception as error: # noqa: BLE001 -- only a refusal before dispatch is handled here
|
|
93
|
+
if getattr(error, "code", None) not in PRE_DISPATCH:
|
|
94
|
+
raise
|
|
95
|
+
snapshot = driver.observe()
|
|
96
|
+
element = _same(snapshot, element)
|
|
97
|
+
driver.execute("TAP", element, snapshot)
|
|
98
|
+
steps.append(f"tap {element.label[:40]!r} ({why})")
|
|
99
|
+
return _wait(driver, snapshot)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _same(snapshot, element):
|
|
103
|
+
"""``element`` on a fresher read: the same role and label, nearest to where it was; Unsure when it's gone."""
|
|
104
|
+
matches = [e for e in snapshot.elements if e.role == element.role and e.label == element.label]
|
|
105
|
+
if not matches:
|
|
106
|
+
raise Unsure(f"{element.label[:40]!r} is no longer on the screen")
|
|
107
|
+
cx, cy = element.center
|
|
108
|
+
return min(matches, key=lambda e: abs(e.center[0] - cx) + abs(e.center[1] - cy))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _type(driver, snapshot, text, steps):
|
|
112
|
+
"""Type into the focused field (Spotlight's, a search field's), on a fresh read of it."""
|
|
113
|
+
snapshot = driver.observe()
|
|
114
|
+
focused = getattr(driver, "type_focused", None)
|
|
115
|
+
field_ = next((e for e in snapshot.elements if e.editable and e.role in ("SearchField", "TextField")), None)
|
|
116
|
+
# A search field keeps its last query (Spotlight does): clear it first, or the name is typed after it.
|
|
117
|
+
clear = next((e for e in snapshot.elements if e.role == "Button" and fold(e.label) == "clear text"), None)
|
|
118
|
+
if clear is not None:
|
|
119
|
+
snapshot = _tap(driver, clear, snapshot, steps, "clear the old query")
|
|
120
|
+
field_ = next((e for e in snapshot.elements if e.editable and e.role in ("SearchField", "TextField")),
|
|
121
|
+
field_)
|
|
122
|
+
elif callable(getattr(driver, "clear_focused", None)) and getattr(snapshot, "keyboard", "") == "visible":
|
|
123
|
+
driver.clear_focused() # pixels can't tell an old query from the placeholder: empty the field either way
|
|
124
|
+
steps.append("clear the field")
|
|
125
|
+
if callable(focused) and getattr(snapshot, "keyboard", "") == "visible":
|
|
126
|
+
focused(text)
|
|
127
|
+
elif field_ is not None:
|
|
128
|
+
driver.execute("TYPE", field_, snapshot, text)
|
|
129
|
+
else:
|
|
130
|
+
raise Unsure("no field to type into")
|
|
131
|
+
steps.append(f"type {text[:40]!r}")
|
|
132
|
+
time.sleep(RESULTS_WAIT)
|
|
133
|
+
return driver.observe()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def screen(snapshot):
|
|
137
|
+
"""kb.screen_of for a snapshot."""
|
|
138
|
+
return kb.screen_of(getattr(snapshot, "bundle_id", ""), labels(snapshot), getattr(snapshot, "keyboard", ""))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _named(snapshot, name, band=None, roles=None):
|
|
142
|
+
want = fold(name)
|
|
143
|
+
out = [e for e in snapshot.elements if fold(e.label) == want and (roles is None or e.role in roles)
|
|
144
|
+
and (band is None or band[0] <= e.center[1] <= band[1])]
|
|
145
|
+
return sorted(out, key=lambda e: e.center[1])
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def go_home(driver, snapshot=None):
|
|
149
|
+
"""The Home Screen's pages: Home returns to the page an app was opened from, so it is pressed again there."""
|
|
150
|
+
snapshot = snapshot if snapshot is not None else driver.observe()
|
|
151
|
+
steps = []
|
|
152
|
+
after = snapshot
|
|
153
|
+
for _ in range(2):
|
|
154
|
+
if not _shortcut(driver, "home"):
|
|
155
|
+
driver.execute("HOME", None, after)
|
|
156
|
+
steps.append("home")
|
|
157
|
+
after = _wait(driver, after, .8)
|
|
158
|
+
if screen(after) not in ("app_library", "spotlight", "control_center"):
|
|
159
|
+
break
|
|
160
|
+
return Outcome(screen(after) in ("home", "") or after.bundle_id == SPRINGBOARD, "home", "", steps, after)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _shortcut(driver, name):
|
|
164
|
+
"""The connection's own key for ``name`` (iPhone Mirroring's cmd+1 Home, cmd+3 Spotlight), when it has one."""
|
|
165
|
+
send = getattr(driver, "system_shortcut", None)
|
|
166
|
+
if not callable(send):
|
|
167
|
+
return False
|
|
168
|
+
try:
|
|
169
|
+
return bool(send(name))
|
|
170
|
+
except ValueError:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def open_app(driver, bundle, name=None, routes=None, phone=""):
|
|
175
|
+
"""Open ``bundle`` (an app named ``name`` on its icon)."""
|
|
176
|
+
can_bundle = getattr(driver, "opens_by_bundle", True)
|
|
177
|
+
order = []
|
|
178
|
+
remembered = routes.get(phone, bundle) if routes is not None else None
|
|
179
|
+
for route in (remembered, *ROUTES):
|
|
180
|
+
if route in ROUTES and route not in order and (route != "bundle" or can_bundle):
|
|
181
|
+
order.append(route)
|
|
182
|
+
if not name and order != ["bundle"]:
|
|
183
|
+
order = [route for route in order if route == "bundle"]
|
|
184
|
+
tried = []
|
|
185
|
+
for route in order:
|
|
186
|
+
try:
|
|
187
|
+
outcome = ROUTE_FUNCS[route](driver, bundle, name)
|
|
188
|
+
except Unsure as reason:
|
|
189
|
+
outcome = Outcome(False, route, str(reason))
|
|
190
|
+
tried.append(f"{route}: {'ok' if outcome.ok else outcome.detail or 'no'}")
|
|
191
|
+
if outcome.ok:
|
|
192
|
+
if routes is not None:
|
|
193
|
+
routes.put(phone, bundle, route)
|
|
194
|
+
outcome.steps = [*tried[:-1], *outcome.steps]
|
|
195
|
+
return outcome
|
|
196
|
+
if routes is not None and route == remembered:
|
|
197
|
+
routes.miss(phone, bundle, route)
|
|
198
|
+
if route == "bundle":
|
|
199
|
+
break # an app iOS can't activate by id isn't on the phone: no route opens it
|
|
200
|
+
if tried and order != ["bundle"]:
|
|
201
|
+
try:
|
|
202
|
+
go_home(driver) # leave the phone on the Home Screen, not in a half-typed search
|
|
203
|
+
except Exception: # noqa: BLE001 -- the failure below is what the caller needs to hear
|
|
204
|
+
pass
|
|
205
|
+
return Outcome(False, "", "couldn't open " + (name or bundle) + " (" + "; ".join(tried) + ")", tried)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _opened(driver, bundle, before, after):
|
|
209
|
+
"""Whether the app is in front now."""
|
|
210
|
+
reader = getattr(driver, "active_app", None)
|
|
211
|
+
if callable(reader) and getattr(driver, "reads_front_app", True):
|
|
212
|
+
try:
|
|
213
|
+
return reader() == bundle
|
|
214
|
+
except Exception: # noqa: BLE001 -- can't tell: not proven
|
|
215
|
+
return False
|
|
216
|
+
return screen(after) not in ("spotlight", "app_library", "home") \
|
|
217
|
+
and getattr(after, "image_hash", "x") != getattr(before, "image_hash", "y")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _by_bundle(driver, bundle, name):
|
|
221
|
+
snapshot = driver.observe()
|
|
222
|
+
driver.execute("LAUNCH_APP", bundle, snapshot)
|
|
223
|
+
after = _wait(driver, snapshot, .8)
|
|
224
|
+
return Outcome(_opened(driver, bundle, snapshot, after), "bundle", "", [f"launch {bundle}"], after)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _spotlight(driver, bundle, name):
|
|
228
|
+
steps = []
|
|
229
|
+
home = go_home(driver)
|
|
230
|
+
snapshot = home.snapshot
|
|
231
|
+
if not _shortcut(driver, "spotlight"):
|
|
232
|
+
search = _named(snapshot, "Search", band=(.7, .9))
|
|
233
|
+
if search:
|
|
234
|
+
snapshot = _tap(driver, search[0], snapshot, steps, "Spotlight")
|
|
235
|
+
else:
|
|
236
|
+
driver.execute("SWIPE_DOWN", None, snapshot)
|
|
237
|
+
steps.append("pull down for Spotlight")
|
|
238
|
+
snapshot = _wait(driver, snapshot)
|
|
239
|
+
else:
|
|
240
|
+
steps.append("Spotlight key")
|
|
241
|
+
time.sleep(.6)
|
|
242
|
+
snapshot = driver.observe()
|
|
243
|
+
results = _type(driver, snapshot, name, steps)
|
|
244
|
+
hit = top_hit(results, name)
|
|
245
|
+
if hit is None:
|
|
246
|
+
_leave(driver, results, steps)
|
|
247
|
+
return Outcome(False, "spotlight", f"Spotlight's top hit isn't {name}", steps)
|
|
248
|
+
after = _tap(driver, hit, results, steps, "Spotlight's top hit")
|
|
249
|
+
ok = _opened(driver, bundle, results, after)
|
|
250
|
+
return Outcome(ok, "spotlight", "" if ok else f"{name} didn't open from Spotlight", steps, after)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def top_hit(snapshot, name):
|
|
254
|
+
"""The element to tap for app ``name`` in Spotlight's results: its cell or icon under "Top Hit" (or under
|
|
255
|
+
"Apps"), never a web, dictionary or Siri Knowledge result. None when the app isn't a hit."""
|
|
256
|
+
rows = sorted(snapshot.elements, key=lambda e: e.center[1])
|
|
257
|
+
# Section headings ("Settings" is one too, over settings results: never the app's own name).
|
|
258
|
+
heads = [e for e in rows if e.role != "Cell" and fold(e.label) != fold(name) and fold(e.label) in (
|
|
259
|
+
"top hit", "apps", "suggestions", "siri suggestions", "siri knowledge", "search in apps", "websites",
|
|
260
|
+
"search the web", "related searches", "dictionary", "maps", "contacts", "messages", "settings", "music",
|
|
261
|
+
"photos", "files", "mail", "notes", "shortcuts") and e.role == "StaticText"]
|
|
262
|
+
for head in heads:
|
|
263
|
+
if fold(head.label) not in ("top hit", "apps"):
|
|
264
|
+
continue
|
|
265
|
+
below = [h.center[1] for h in heads if h.center[1] > head.center[1] + .01]
|
|
266
|
+
end = min(below) if below else RESULTS_BAND[1]
|
|
267
|
+
for element in rows:
|
|
268
|
+
if head.center[1] < element.center[1] < end and fold(element.label) == fold(name) \
|
|
269
|
+
and element.role != "TextField" and not element.editable:
|
|
270
|
+
return element
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _leave(driver, snapshot, steps):
|
|
275
|
+
"""Back out of Spotlight or a search to the Home Screen, sending nothing."""
|
|
276
|
+
go_home(driver, snapshot)
|
|
277
|
+
steps.append("leave search")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _app_library(driver, bundle, name):
|
|
281
|
+
steps = []
|
|
282
|
+
snapshot = _to_app_library(driver, steps)
|
|
283
|
+
field_ = next((e for e in snapshot.elements if e.role == "SearchField" or field_name(e) == "app library"), None)
|
|
284
|
+
if field_ is None:
|
|
285
|
+
raise Unsure("no App Library search field")
|
|
286
|
+
if getattr(snapshot, "keyboard", "") != "visible":
|
|
287
|
+
snapshot = _tap(driver, field_, snapshot, steps, "App Library search")
|
|
288
|
+
results = _type(driver, snapshot, name, steps)
|
|
289
|
+
found = _named(results, name, band=(.1, .7), roles=("Cell", "StaticText", "Icon", "Button"))
|
|
290
|
+
cells = [e for e in found if e.role == "Cell"] or found
|
|
291
|
+
if not cells:
|
|
292
|
+
_leave(driver, results, steps)
|
|
293
|
+
return Outcome(False, "app_library", f"the App Library has no {name}", steps)
|
|
294
|
+
after = _tap(driver, cells[0], results, steps, "App Library result")
|
|
295
|
+
ok = _opened(driver, bundle, results, after)
|
|
296
|
+
return Outcome(ok, "app_library", "" if ok else f"{name} didn't open from the App Library", steps, after)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _to_app_library(driver, steps):
|
|
300
|
+
"""Home, then swipes left past the last page until the App Library shows."""
|
|
301
|
+
snapshot = go_home(driver).snapshot
|
|
302
|
+
steps.append("home")
|
|
303
|
+
for _ in range(MAX_PAGES + 1):
|
|
304
|
+
if screen(snapshot) == "app_library":
|
|
305
|
+
return snapshot
|
|
306
|
+
driver.execute("SWIPE_LEFT", None, snapshot)
|
|
307
|
+
steps.append("next page")
|
|
308
|
+
after = _wait(driver, snapshot)
|
|
309
|
+
if fold(" ".join(labels(after))) == fold(" ".join(labels(snapshot))) and screen(after) != "app_library":
|
|
310
|
+
raise Unsure("the Home Screen stopped turning before the App Library")
|
|
311
|
+
snapshot = after
|
|
312
|
+
raise Unsure("no App Library after the last Home Screen page")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _home_pages(driver, bundle, name):
|
|
316
|
+
steps = []
|
|
317
|
+
snapshot = go_home(driver).snapshot
|
|
318
|
+
if not _first_page(snapshot):
|
|
319
|
+
snapshot = go_home(driver, snapshot).snapshot # Home on a Home Screen page shows the first page
|
|
320
|
+
for _ in range(MAX_PAGES):
|
|
321
|
+
icons = _named(snapshot, name, band=(.05, .97), roles=("Icon", "StaticText", "Button"))
|
|
322
|
+
if icons:
|
|
323
|
+
after = _tap(driver, icons[0], snapshot, steps, "Home Screen icon")
|
|
324
|
+
ok = _opened(driver, bundle, snapshot, after)
|
|
325
|
+
return Outcome(ok, "home_pages", "" if ok else f"{name} didn't open from its icon", steps, after)
|
|
326
|
+
if screen(snapshot) == "app_library":
|
|
327
|
+
break
|
|
328
|
+
driver.execute("SWIPE_LEFT", None, snapshot)
|
|
329
|
+
steps.append("next page")
|
|
330
|
+
after = _wait(driver, snapshot)
|
|
331
|
+
if fold(" ".join(labels(after))) == fold(" ".join(labels(snapshot))):
|
|
332
|
+
break
|
|
333
|
+
snapshot = after
|
|
334
|
+
return Outcome(False, "home_pages", f"no {name} icon on the Home Screen", steps)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _first_page(snapshot):
|
|
338
|
+
"""True when the Home Screen's page dots say page 1 (a tree reads them; on pixels it is unknown: False)."""
|
|
339
|
+
dots = next((e for e in snapshot.elements if e.role == "PageIndicator"), None)
|
|
340
|
+
return dots is not None and bool(re.match(r"page 1 of", fold(dots.value)))
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
ROUTE_FUNCS = {"bundle": _by_bundle, "spotlight": _spotlight, "app_library": _app_library,
|
|
344
|
+
"home_pages": _home_pages}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
_versions = weakref.WeakKeyDictionary()
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def ios_version(driver):
|
|
351
|
+
try:
|
|
352
|
+
if driver in _versions:
|
|
353
|
+
return _versions[driver]
|
|
354
|
+
except TypeError:
|
|
355
|
+
pass
|
|
356
|
+
version = getattr(driver, "ios_version", None)
|
|
357
|
+
http = getattr(driver, "http", None)
|
|
358
|
+
if not isinstance(version, str) and http is not None:
|
|
359
|
+
try:
|
|
360
|
+
status = http.request("GET", "/status", None, 3)
|
|
361
|
+
version = ((status or {}).get("value") or {}).get("os", {}).get("version")
|
|
362
|
+
except Exception: # noqa: BLE001 -- unknown is a fine answer
|
|
363
|
+
version = None
|
|
364
|
+
version = version if isinstance(version, str) else None
|
|
365
|
+
try:
|
|
366
|
+
_versions[driver] = version
|
|
367
|
+
except TypeError:
|
|
368
|
+
pass
|
|
369
|
+
return version
|