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
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
"""iPhone Mirroring, an experimental connection (MOBSTER_EXPERIMENTAL_MIRRORING=1); a refused gesture isn't retried."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import queue
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from . import perception
|
|
14
|
+
from .drivers import Driver, grounded_target, register_driver
|
|
15
|
+
from .state import validate_bundle_id, validate_input_text
|
|
16
|
+
from .transport import TransportError
|
|
17
|
+
from .wda.gestures import _swipe_points
|
|
18
|
+
from .wda.text import NoKeyboardFocus
|
|
19
|
+
|
|
20
|
+
SOURCE = "mirroring"
|
|
21
|
+
ENV_FLAG = "MOBSTER_EXPERIMENTAL_MIRRORING"
|
|
22
|
+
SPRINGBOARD = "com.apple.springboard"
|
|
23
|
+
|
|
24
|
+
# A screen read older than this is checked against a fresh capture before a tap lands on it.
|
|
25
|
+
FRESH_SECONDS = 1.0
|
|
26
|
+
# The model's picture reuses the last read's frame when it is this recent (it goes with that read); WATCH samples
|
|
27
|
+
# faster than FRESH_SECONDS, so anything older is captured again.
|
|
28
|
+
PREVIEW_REUSE_SECONDS = .3
|
|
29
|
+
# Settling: frames this far apart, until two agree, capped.
|
|
30
|
+
SETTLE_POLL_SECONDS = .12
|
|
31
|
+
SETTLE_CAP_SECONDS = 2.0
|
|
32
|
+
# A scroll moves about this much of the window's height (SWIPE_UP reveals what is below).
|
|
33
|
+
SCROLL_FRACTION = .45
|
|
34
|
+
# iPhone Mirroring's own keys for iOS's moves (ios.system asks for them by name).
|
|
35
|
+
SHORTCUTS = {"home": "cmd+1", "app_switcher": "cmd+2", "spotlight": "cmd+3"}
|
|
36
|
+
# The phone's key in the route memory: a Mac mirrors one iPhone.
|
|
37
|
+
PHONE_KEY = "mirroring"
|
|
38
|
+
# Text longer than this, or with characters that have no US key, goes in through the Mac's pasteboard.
|
|
39
|
+
KEYSTROKE_MAX_CHARS = 80
|
|
40
|
+
KEYBOARD_WAIT_SECONDS = 2.0
|
|
41
|
+
HELPER_TIMEOUT = 15
|
|
42
|
+
INPUT_ENV = "MOBSTER_MIRRORING_INPUT"
|
|
43
|
+
GESTURES = ("tap", "press", "drag", "scroll", "key", "type")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def enabled():
|
|
47
|
+
"""Whether the experimental Mirroring connection is switched on (``MOBSTER_EXPERIMENTAL_MIRRORING=1``)."""
|
|
48
|
+
return os.environ.get(ENV_FLAG, "").strip().casefold() in ("1", "true", "yes", "on")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MirroringError(TransportError):
|
|
52
|
+
"""The helper refused or failed."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, message, code="capture", said=()):
|
|
55
|
+
super().__init__(message)
|
|
56
|
+
self.code = code
|
|
57
|
+
self.said = tuple(said)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# What to do about each refusal, said after what happened.
|
|
61
|
+
FIXES = {
|
|
62
|
+
"not_running": "Open iPhone Mirroring on this Mac and connect your iPhone, then try again.",
|
|
63
|
+
"no_window": "Open iPhone Mirroring's window and connect your iPhone, then try again.",
|
|
64
|
+
"blocked": "iPhone Mirroring isn't showing your iPhone. Lock your iPhone, keep it nearby, and click Connect or "
|
|
65
|
+
"Try Again in iPhone Mirroring, then try again.",
|
|
66
|
+
"login": "iPhone Mirroring is asking for this Mac's password. Enter it yourself, or set iPhone Mirroring to "
|
|
67
|
+
"authenticate automatically in its settings (cmd+,). Mobster never types passwords.",
|
|
68
|
+
"covered": "Another window covers iPhone Mirroring, and input is set to foreground. Bring it to the front, or "
|
|
69
|
+
"remove MOBSTER_MIRRORING_INPUT, then try again.",
|
|
70
|
+
"background": "iPhone Mirroring can't take input in the background right now. Wait for it to answer, or set "
|
|
71
|
+
"MOBSTER_MIRRORING_INPUT=foreground to let Mobster bring the window forward for each gesture, "
|
|
72
|
+
"then try again.",
|
|
73
|
+
"permission": "Give the app you run Mobster from Screen Recording and Accessibility in System Settings > "
|
|
74
|
+
"Privacy & Security, then try again.",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def input_mode():
|
|
79
|
+
"""``background`` (default) or ``foreground``, from MOBSTER_MIRRORING_INPUT; a typo is an error, never a mode."""
|
|
80
|
+
value = os.environ.get(INPUT_ENV, "").strip().casefold()
|
|
81
|
+
if value in ("", "background"):
|
|
82
|
+
return "background"
|
|
83
|
+
if value == "foreground":
|
|
84
|
+
return "foreground"
|
|
85
|
+
raise MirroringError(f"{INPUT_ENV} is \"{value}\", so nothing was sent. Set it to background or foreground, or "
|
|
86
|
+
"remove it, then try again.", "usage")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def fix_for(error):
|
|
90
|
+
"""The sentence that says what to do about ``error`` (a MirroringError), or ""."""
|
|
91
|
+
return FIXES.get(getattr(error, "code", ""), "")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def helper_path(name="mirroring"):
|
|
95
|
+
"""A helper's executable: the Mac app's own, else built from source (needs Xcode's command line tools)."""
|
|
96
|
+
from . import native_helpers
|
|
97
|
+
native_helpers.require_devices()
|
|
98
|
+
return native_helpers.find(name) or native_helpers.build(name)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Helper:
|
|
102
|
+
"""One helper process, spoken to in JSON lines; a request waits for its own answer, at most ``timeout``."""
|
|
103
|
+
|
|
104
|
+
def __init__(self, path=None, spawn=subprocess.Popen):
|
|
105
|
+
self._process = spawn([str(path or helper_path())], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
106
|
+
stderr=subprocess.DEVNULL, text=True, bufsize=1)
|
|
107
|
+
self._lines = queue.Queue()
|
|
108
|
+
self._lock = threading.Lock()
|
|
109
|
+
self._next = 0
|
|
110
|
+
self._reader = threading.Thread(target=self._read, name="mirroring-helper", daemon=True)
|
|
111
|
+
self._reader.start()
|
|
112
|
+
|
|
113
|
+
def _read(self):
|
|
114
|
+
for line in self._process.stdout:
|
|
115
|
+
self._lines.put(line)
|
|
116
|
+
self._lines.put(None)
|
|
117
|
+
|
|
118
|
+
def request(self, op, timeout=HELPER_TIMEOUT, **fields):
|
|
119
|
+
"""The helper's answer to ``op``; MirroringError when it refuses, fails, or doesn't answer in ``timeout``."""
|
|
120
|
+
if op in GESTURES:
|
|
121
|
+
fields = {**fields, "input": input_mode()}
|
|
122
|
+
with self._lock:
|
|
123
|
+
self._next += 1
|
|
124
|
+
ident = self._next
|
|
125
|
+
try:
|
|
126
|
+
self._process.stdin.write(json.dumps({"op": op, "id": ident, **fields}) + "\n")
|
|
127
|
+
self._process.stdin.flush()
|
|
128
|
+
except (OSError, ValueError):
|
|
129
|
+
raise MirroringError("Mobster's Mirroring helper stopped", "capture") from None
|
|
130
|
+
deadline = time.monotonic() + timeout
|
|
131
|
+
while True:
|
|
132
|
+
left = deadline - time.monotonic()
|
|
133
|
+
if left <= 0:
|
|
134
|
+
raise MirroringError(f"Mobster's Mirroring helper didn't answer {op} in {timeout:g} s", "capture")
|
|
135
|
+
try:
|
|
136
|
+
line = self._lines.get(timeout=left)
|
|
137
|
+
except queue.Empty:
|
|
138
|
+
continue
|
|
139
|
+
if line is None:
|
|
140
|
+
raise MirroringError("Mobster's Mirroring helper stopped", "capture")
|
|
141
|
+
try:
|
|
142
|
+
answer = json.loads(line)
|
|
143
|
+
except ValueError:
|
|
144
|
+
continue
|
|
145
|
+
if answer.get("id") not in (ident, None):
|
|
146
|
+
continue # a late answer to a request that timed out
|
|
147
|
+
if answer.get("ok") is not True:
|
|
148
|
+
raise MirroringError(str(answer.get("error") or "iPhone Mirroring refused"),
|
|
149
|
+
str(answer.get("code") or "capture"), answer.get("said") or ())
|
|
150
|
+
return answer
|
|
151
|
+
|
|
152
|
+
def close(self):
|
|
153
|
+
process, self._process = self._process, None
|
|
154
|
+
if process is None:
|
|
155
|
+
return
|
|
156
|
+
try:
|
|
157
|
+
process.stdin.close()
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
try:
|
|
161
|
+
process.wait(timeout=2)
|
|
162
|
+
except Exception:
|
|
163
|
+
process.kill()
|
|
164
|
+
try:
|
|
165
|
+
process.stdout.close()
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def screen_from(capture, *, bundle="", read_started=0.0, icons=()):
|
|
171
|
+
"""A ``Snapshot`` of one helper capture (perception.snapshot_from, with this driver's source)."""
|
|
172
|
+
return perception.snapshot_from(capture, source=SOURCE, bundle=bundle, read_started=read_started, icons=icons)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def typable(text):
|
|
176
|
+
"""Whether ``text`` can go in as US key codes (letters, digits, ASCII punctuation, spaces, line breaks)."""
|
|
177
|
+
return len(text) <= KEYSTROKE_MAX_CHARS and all(c == "\n" or " " <= c <= "~" for c in text)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class MirroringDriver(Driver):
|
|
181
|
+
"""The device contract over iPhone Mirroring."""
|
|
182
|
+
|
|
183
|
+
capabilities = frozenset({
|
|
184
|
+
"observe_ready", "wait_for_change", "capture_preview", "active_app", "source_reads", "source_seconds",
|
|
185
|
+
"tap_point", "dismiss_at", "long_press", "helper", "opens_by_bundle", "reads_front_app", "system_shortcut",
|
|
186
|
+
"type_focused", "clear_focused"})
|
|
187
|
+
can_type = True
|
|
188
|
+
reports_settling = False
|
|
189
|
+
# ios.system: apps open by name (no bundle id), and the front app can't be read (``current_app`` is a guess).
|
|
190
|
+
opens_by_bundle = False
|
|
191
|
+
reads_front_app = False
|
|
192
|
+
# ios.system's route memory (learned.routes.RouteMemory), set by the runtime; None remembers nothing.
|
|
193
|
+
routes = None
|
|
194
|
+
|
|
195
|
+
def __init__(self, helper=None, *, app_names=None, frames_dir=None, sleep=time.sleep, clock=time.monotonic,
|
|
196
|
+
labeller=None, icon_memory=None):
|
|
197
|
+
self.helper = helper if helper is not None else Helper()
|
|
198
|
+
# Icons: a chat client that names the glyphs Vision can't read (perception.label_icons), and what it learned.
|
|
199
|
+
# Without one, icons stay unlisted and a tap on one is a point tap, which asks.
|
|
200
|
+
self.labeller = labeller
|
|
201
|
+
self.icon_memory = icon_memory if icon_memory is not None else perception.IconMemory()
|
|
202
|
+
self.app_names = dict(app_names or {})
|
|
203
|
+
self._sleep, self._clock = sleep, clock
|
|
204
|
+
self._own_dir = not frames_dir
|
|
205
|
+
self._dir = Path(frames_dir) if frames_dir else Path(tempfile.mkdtemp(prefix="mobster-mirroring-"))
|
|
206
|
+
self._dir.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
207
|
+
self._frame = self._dir / "frame.jpg"
|
|
208
|
+
self._frame_at = None
|
|
209
|
+
self._window = None
|
|
210
|
+
self.current_app = ""
|
|
211
|
+
# Read counts and time, as WDA's driver keeps them (the run's timing: source_reads, source_seconds).
|
|
212
|
+
self.source_reads = 0
|
|
213
|
+
self.source_seconds = 0.0
|
|
214
|
+
|
|
215
|
+
def _capture(self, ocr, timeout):
|
|
216
|
+
started = self._clock()
|
|
217
|
+
result = self.helper.request("capture", timeout=min(timeout, HELPER_TIMEOUT), path=str(self._frame), ocr=ocr)
|
|
218
|
+
if result.get("blocked"):
|
|
219
|
+
code = "login" if result.get("login") else "blocked"
|
|
220
|
+
said = result.get("said") or ()
|
|
221
|
+
raise MirroringError(f"iPhone Mirroring isn't showing your iPhone ({(said or ['blocked'])[0]})", code,
|
|
222
|
+
said)
|
|
223
|
+
self._window = result.get("window") or self._window
|
|
224
|
+
self._frame_at = self._clock()
|
|
225
|
+
if ocr:
|
|
226
|
+
self.source_reads += 1
|
|
227
|
+
self.source_seconds += self._clock() - started
|
|
228
|
+
return result, started
|
|
229
|
+
|
|
230
|
+
def observe(self, timeout=10):
|
|
231
|
+
result, started = self._capture(True, timeout)
|
|
232
|
+
return screen_from(result, bundle=self.current_app, read_started=started, icons=self._icons(result))
|
|
233
|
+
|
|
234
|
+
def _icons(self, result):
|
|
235
|
+
"""The named icons on the frame just captured: [(rect, label, kind)] (perception.label_icons)."""
|
|
236
|
+
if self.labeller is None and not self.icon_memory.data:
|
|
237
|
+
return []
|
|
238
|
+
try:
|
|
239
|
+
from PIL import Image
|
|
240
|
+
with Image.open(self._frame) as image:
|
|
241
|
+
image.load()
|
|
242
|
+
rows = [r for r in result.get("rows") or ()
|
|
243
|
+
if float(r.get("confidence") or 0) >= perception.MIN_CONFIDENCE]
|
|
244
|
+
boxes = perception.icon_candidates(image, rows)
|
|
245
|
+
named = perception.label_icons(self.labeller, image, boxes, bundle=self.current_app,
|
|
246
|
+
memory=self.icon_memory)
|
|
247
|
+
named = perception.with_selection(image, named, boxes)
|
|
248
|
+
except (OSError, ValueError):
|
|
249
|
+
return []
|
|
250
|
+
return named
|
|
251
|
+
|
|
252
|
+
def _settled_hash(self, timeout, unlike=None, wait_seconds=None):
|
|
253
|
+
cap = self._clock() + min(SETTLE_CAP_SECONDS, timeout)
|
|
254
|
+
give_up = self._clock() + wait_seconds if wait_seconds is not None else None
|
|
255
|
+
previous = None
|
|
256
|
+
while self._clock() < cap:
|
|
257
|
+
result, _ = self._capture(False, max(cap - self._clock(), .5))
|
|
258
|
+
digest = result.get("hash")
|
|
259
|
+
changed = unlike is None or digest != unlike or (give_up is not None and self._clock() >= give_up)
|
|
260
|
+
if previous is not None and digest == previous and changed:
|
|
261
|
+
return True
|
|
262
|
+
previous = digest
|
|
263
|
+
self._sleep(SETTLE_POLL_SECONDS)
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
def observe_ready(self, timeout=10):
|
|
267
|
+
"""A read of a screen at rest: frames until two agree (capped for video), then one read with text."""
|
|
268
|
+
self._settled_hash(timeout)
|
|
269
|
+
return self.observe(timeout=timeout)
|
|
270
|
+
|
|
271
|
+
def wait_for_change(self, snapshot, timeout=2, wait_seconds=.6, on_settling=None, single_read=False,
|
|
272
|
+
early=False):
|
|
273
|
+
"""The screen after an action."""
|
|
274
|
+
self._settled_hash(timeout, unlike=getattr(snapshot, "image_hash", None), wait_seconds=wait_seconds)
|
|
275
|
+
return self.observe(timeout=max(timeout, 5))
|
|
276
|
+
|
|
277
|
+
def capture_preview(self, timeout=3):
|
|
278
|
+
"""The model's picture: the frame the last read captured when it is fresh, else a new capture."""
|
|
279
|
+
if (self._frame_at is None or self._clock() - self._frame_at > PREVIEW_REUSE_SECONDS
|
|
280
|
+
or not self._frame.is_file()):
|
|
281
|
+
self._capture(False, timeout)
|
|
282
|
+
data = self._frame.read_bytes()
|
|
283
|
+
return "data:image/jpeg;base64," + base64.b64encode(data).decode()
|
|
284
|
+
|
|
285
|
+
def active_app(self, timeout=5):
|
|
286
|
+
if not self.current_app:
|
|
287
|
+
raise TransportError("iPhone Mirroring can't tell which app is in front")
|
|
288
|
+
return self.current_app
|
|
289
|
+
|
|
290
|
+
def _check_current(self, snapshot, target=None):
|
|
291
|
+
"""Before a tap on an older read, the screen must still show it; unsure means no tap."""
|
|
292
|
+
if self._clock() - getattr(snapshot, "captured_at", 0) <= FRESH_SECONDS:
|
|
293
|
+
return
|
|
294
|
+
result, _ = self._capture(target is not None, 5)
|
|
295
|
+
if result.get("hash") == snapshot.image_hash:
|
|
296
|
+
return
|
|
297
|
+
if target is not None:
|
|
298
|
+
if perception.still_there(target, screen_from(result)):
|
|
299
|
+
return
|
|
300
|
+
raise ValueError("The screen changed since it was read; nothing was tapped")
|
|
301
|
+
|
|
302
|
+
def _tap(self, x, y, hold_ms=None):
|
|
303
|
+
fields = {"x": float(x), "y": float(y)}
|
|
304
|
+
if hold_ms is None:
|
|
305
|
+
self.helper.request("tap", **fields)
|
|
306
|
+
else:
|
|
307
|
+
self.helper.request("press", ms=hold_ms, **fields)
|
|
308
|
+
|
|
309
|
+
def _opened_from_home(self, target):
|
|
310
|
+
"""A tap on an app's name on the Home Screen opens that app: the driver's idea of the front app follows."""
|
|
311
|
+
if self.current_app != SPRINGBOARD:
|
|
312
|
+
return
|
|
313
|
+
wanted = " ".join(target.label.split()).casefold()
|
|
314
|
+
for bundle, name in self._names().items():
|
|
315
|
+
if name.casefold() == wanted:
|
|
316
|
+
self.current_app = bundle
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
def _names(self):
|
|
320
|
+
from .catalog import _EXTRA_NAMES, APPS
|
|
321
|
+
names = {app["bundleId"]: app["name"] for app in APPS}
|
|
322
|
+
names.update(_EXTRA_NAMES)
|
|
323
|
+
names.update(self.app_names)
|
|
324
|
+
return names
|
|
325
|
+
|
|
326
|
+
def _type(self, text):
|
|
327
|
+
if typable(text):
|
|
328
|
+
self.helper.request("type", text=text, mode="keys", timeout=HELPER_TIMEOUT + len(text) * .1)
|
|
329
|
+
else:
|
|
330
|
+
self.helper.request("type", text=text, mode="paste")
|
|
331
|
+
|
|
332
|
+
def _await_keyboard(self):
|
|
333
|
+
deadline = self._clock() + KEYBOARD_WAIT_SECONDS
|
|
334
|
+
while self._clock() < deadline:
|
|
335
|
+
self._sleep(.25)
|
|
336
|
+
if self.observe(timeout=5).keyboard == "visible":
|
|
337
|
+
return True
|
|
338
|
+
return False
|
|
339
|
+
|
|
340
|
+
def execute(self, operation, target, snapshot, text=None, timeout=10):
|
|
341
|
+
if operation == "HOME":
|
|
342
|
+
self.helper.request("key", combo="cmd+1")
|
|
343
|
+
self.current_app = SPRINGBOARD
|
|
344
|
+
return {"dispatch_attempted": True}
|
|
345
|
+
if operation in ("VOLUME_UP", "VOLUME_DOWN"):
|
|
346
|
+
raise ValueError("iPhone Mirroring has no volume keys")
|
|
347
|
+
if operation == "LAUNCH_APP":
|
|
348
|
+
return self._launch(target)
|
|
349
|
+
if operation == "TAP":
|
|
350
|
+
grounded_target(operation, target, snapshot)
|
|
351
|
+
self._check_current(snapshot, target=target)
|
|
352
|
+
x, y = target.tap_point
|
|
353
|
+
self._tap(x, y)
|
|
354
|
+
self._opened_from_home(target)
|
|
355
|
+
return None
|
|
356
|
+
if operation in ("TYPE", "TYPE_SUBMIT"):
|
|
357
|
+
grounded_target(operation, target, snapshot)
|
|
358
|
+
validate_input_text(text, multiline=False)
|
|
359
|
+
if getattr(snapshot, "keyboard", "") != "visible":
|
|
360
|
+
self._check_current(snapshot, target=target)
|
|
361
|
+
x, y = target.tap_point
|
|
362
|
+
self._tap(x, y)
|
|
363
|
+
if not self._await_keyboard():
|
|
364
|
+
raise NoKeyboardFocus("The field did not take keyboard focus; nothing was typed")
|
|
365
|
+
self._type(text)
|
|
366
|
+
if operation == "TYPE_SUBMIT":
|
|
367
|
+
self.helper.request("key", combo="return")
|
|
368
|
+
return None
|
|
369
|
+
if operation == "SUBMIT":
|
|
370
|
+
grounded_target(operation, target, snapshot)
|
|
371
|
+
self.helper.request("key", combo="return")
|
|
372
|
+
return None
|
|
373
|
+
if operation in ("SWIPE_UP", "SWIPE_DOWN"):
|
|
374
|
+
height = float((self._window or {}).get("h") or 700)
|
|
375
|
+
dy = height * SCROLL_FRACTION * (1 if operation == "SWIPE_UP" else -1)
|
|
376
|
+
self.helper.request("scroll", x=.5, y=.5, dy=dy, dx=0)
|
|
377
|
+
return None
|
|
378
|
+
if operation in ("SWIPE_LEFT", "SWIPE_RIGHT"):
|
|
379
|
+
aimed = None
|
|
380
|
+
if target is not None:
|
|
381
|
+
grounded_target(operation, target, snapshot)
|
|
382
|
+
aimed = target.rect
|
|
383
|
+
x1, y1, x2, y2 = _swipe_points(operation, 1.0, 1.0, aimed)
|
|
384
|
+
self.helper.request("drag", x1=x1, y1=y1, x2=x2, y2=y2, ms=250)
|
|
385
|
+
return None
|
|
386
|
+
raise ValueError(f"iPhone Mirroring can't do {operation}")
|
|
387
|
+
|
|
388
|
+
def _launch(self, bundle):
|
|
389
|
+
"""Open ``bundle`` by its name, the way a person would (ios.system.open_app)."""
|
|
390
|
+
from .ios import system
|
|
391
|
+
bundle = validate_bundle_id(bundle)
|
|
392
|
+
name = self._names().get(bundle)
|
|
393
|
+
if not name:
|
|
394
|
+
raise ValueError(f"iPhone Mirroring opens apps by name, and {bundle} has none here")
|
|
395
|
+
outcome = system.open_app(self, bundle, name, routes=self.routes, phone=PHONE_KEY)
|
|
396
|
+
if not outcome.ok:
|
|
397
|
+
raise TransportError(outcome.detail or f"Mobster couldn't open {name}")
|
|
398
|
+
self.current_app = bundle
|
|
399
|
+
return {"route": outcome.route}
|
|
400
|
+
|
|
401
|
+
# ios.system calls these by name.
|
|
402
|
+
|
|
403
|
+
def system_shortcut(self, name):
|
|
404
|
+
"""Send iPhone Mirroring's key for an iOS move ("home", "spotlight", "app_switcher"); False when it has none."""
|
|
405
|
+
combo = SHORTCUTS.get(name)
|
|
406
|
+
if combo is None:
|
|
407
|
+
return False
|
|
408
|
+
self.helper.request("key", combo=combo)
|
|
409
|
+
self.current_app = SPRINGBOARD
|
|
410
|
+
return True
|
|
411
|
+
|
|
412
|
+
def type_focused(self, text):
|
|
413
|
+
"""Type into the field that has the keyboard."""
|
|
414
|
+
validate_input_text(text, multiline=False)
|
|
415
|
+
self._type(text)
|
|
416
|
+
|
|
417
|
+
def clear_focused(self):
|
|
418
|
+
"""Empty the field that has the keyboard: select all, then delete."""
|
|
419
|
+
self.helper.request("key", combo="cmd+a")
|
|
420
|
+
self.helper.request("key", combo="delete")
|
|
421
|
+
|
|
422
|
+
def tap_point(self, x, y, snapshot, timeout=10, purpose=None):
|
|
423
|
+
if not (0 <= x <= 1 and 0 <= y <= 1):
|
|
424
|
+
raise ValueError("The point is outside the screen")
|
|
425
|
+
self._check_current(snapshot)
|
|
426
|
+
self._tap(x, y)
|
|
427
|
+
|
|
428
|
+
dismiss_at = tap_point
|
|
429
|
+
|
|
430
|
+
def transfer_to_phone(self, paths, at=None):
|
|
431
|
+
"""Files from the Mac onto the phone by drag and drop (module function transfer_to_phone)."""
|
|
432
|
+
return transfer_to_phone(paths, at=at)
|
|
433
|
+
|
|
434
|
+
def transfer_from_phone(self, at, dest_dir):
|
|
435
|
+
"""An item off the phone into ``dest_dir`` by drag and drop (module function transfer_from_phone)."""
|
|
436
|
+
return transfer_from_phone(at, dest_dir)
|
|
437
|
+
|
|
438
|
+
def long_press(self, target, snapshot, timeout=10):
|
|
439
|
+
grounded_target("TAP", target, snapshot)
|
|
440
|
+
self._check_current(snapshot, target=target)
|
|
441
|
+
x, y = target.tap_point
|
|
442
|
+
self._tap(x, y, hold_ms=1500)
|
|
443
|
+
|
|
444
|
+
def close(self):
|
|
445
|
+
helper, self.helper = self.helper, None
|
|
446
|
+
if helper is None:
|
|
447
|
+
return
|
|
448
|
+
try:
|
|
449
|
+
helper.request("restore", timeout=3)
|
|
450
|
+
except Exception:
|
|
451
|
+
pass
|
|
452
|
+
helper.close()
|
|
453
|
+
try:
|
|
454
|
+
self._frame.unlink(missing_ok=True)
|
|
455
|
+
if self._own_dir:
|
|
456
|
+
self._dir.rmdir()
|
|
457
|
+
except OSError:
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
register_driver(SOURCE, MirroringDriver)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def stream_command(fps, detail):
|
|
465
|
+
"""The helper's live-stream argv (``mirroring stream FPS MAX_WIDTH QUALITY``) for a live-view quality."""
|
|
466
|
+
from .wda.frame_source import DETAIL_QUALITY, DETAIL_WIDTH
|
|
467
|
+
return [str(helper_path()), "stream", str(int(fps)), str(DETAIL_WIDTH[detail]), str(DETAIL_QUALITY[detail])]
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def live_video(**kwargs):
|
|
471
|
+
"""The live view's frame source for iPhone Mirroring."""
|
|
472
|
+
from .wda.frame_source import ProcessFrameSource
|
|
473
|
+
return ProcessFrameSource(stream_command, source="mirroring_sck", **kwargs)
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
class MirroringControl:
|
|
477
|
+
"""The person's gestures from the live view, through the helper: nothing is sent while the phone is covered."""
|
|
478
|
+
def __init__(self, driver_factory=None):
|
|
479
|
+
self.driver_factory = driver_factory or MirroringDriver
|
|
480
|
+
self.driver = None
|
|
481
|
+
self.lock = threading.Lock()
|
|
482
|
+
|
|
483
|
+
def _driver(self):
|
|
484
|
+
if self.driver is None:
|
|
485
|
+
self.driver = self.driver_factory()
|
|
486
|
+
return self.driver
|
|
487
|
+
|
|
488
|
+
def perform(self, body, teach=None):
|
|
489
|
+
from .manual_control import parse_command
|
|
490
|
+
command = parse_command(body)
|
|
491
|
+
with self.lock:
|
|
492
|
+
driver = self._driver()
|
|
493
|
+
before = teach.read(driver) if teach is not None else None
|
|
494
|
+
try:
|
|
495
|
+
self._dispatch(driver, command)
|
|
496
|
+
except Exception:
|
|
497
|
+
self.close()
|
|
498
|
+
raise
|
|
499
|
+
if teach is not None:
|
|
500
|
+
teach.record(command, before)
|
|
501
|
+
return command["action"]
|
|
502
|
+
|
|
503
|
+
def observe(self, timeout=8):
|
|
504
|
+
with self.lock:
|
|
505
|
+
return self._driver().observe(timeout=timeout)
|
|
506
|
+
|
|
507
|
+
@staticmethod
|
|
508
|
+
def _dispatch(driver, command):
|
|
509
|
+
helper = driver.helper
|
|
510
|
+
kind = command["action"]
|
|
511
|
+
if kind == "button":
|
|
512
|
+
if command["name"] != "home":
|
|
513
|
+
raise ValueError("iPhone Mirroring has no volume keys")
|
|
514
|
+
helper.request("key", combo="cmd+1")
|
|
515
|
+
driver.current_app = SPRINGBOARD
|
|
516
|
+
elif kind == "type":
|
|
517
|
+
driver._type(command["text"])
|
|
518
|
+
if command["submit"]:
|
|
519
|
+
helper.request("key", combo="return")
|
|
520
|
+
elif kind == "tap":
|
|
521
|
+
point = {"x": command["x"], "y": command["y"]}
|
|
522
|
+
if command["holdMs"] >= 400:
|
|
523
|
+
helper.request("press", ms=command["holdMs"], **point)
|
|
524
|
+
else:
|
|
525
|
+
helper.request("tap", **point)
|
|
526
|
+
else:
|
|
527
|
+
helper.request("drag", x1=command["x1"], y1=command["y1"], x2=command["x2"], y2=command["y2"],
|
|
528
|
+
ms=command["durationMs"])
|
|
529
|
+
|
|
530
|
+
def close(self):
|
|
531
|
+
driver, self.driver = self.driver, None
|
|
532
|
+
if driver is not None:
|
|
533
|
+
driver.close()
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
TRANSFER_SECONDS = 75
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def _point(at):
|
|
540
|
+
"""Screen fractions for ``at``: an element (its tap point), (x, y), or None for the middle of the screen."""
|
|
541
|
+
if at is None:
|
|
542
|
+
return .5, .5
|
|
543
|
+
if hasattr(at, "tap_point"):
|
|
544
|
+
return at.tap_point
|
|
545
|
+
x, y = at
|
|
546
|
+
if not (0 <= x <= 1 and 0 <= y <= 1):
|
|
547
|
+
raise ValueError("The point is outside the screen")
|
|
548
|
+
return float(x), float(y)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _transfer(args, run=subprocess.run):
|
|
552
|
+
try:
|
|
553
|
+
done = run([str(helper_path())] + args, capture_output=True, text=True, timeout=TRANSFER_SECONDS)
|
|
554
|
+
except subprocess.TimeoutExpired:
|
|
555
|
+
raise MirroringError("The transfer didn't finish in time; check the phone for what arrived",
|
|
556
|
+
"refused") from None
|
|
557
|
+
lines = [line for line in (done.stdout or "").splitlines() if line.strip()]
|
|
558
|
+
try:
|
|
559
|
+
answer = json.loads(lines[-1]) if lines else {}
|
|
560
|
+
except ValueError:
|
|
561
|
+
answer = {}
|
|
562
|
+
if answer.get("ok") is not True:
|
|
563
|
+
raise MirroringError(str(answer.get("error") or "The transfer failed"), str(answer.get("code") or "refused"))
|
|
564
|
+
return answer
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def transfer_to_phone(paths, *, at=None, run=subprocess.run):
|
|
568
|
+
"""Drop Mac files on the phone at ``at``; blocked or covered sends nothing; the drop isn't read back."""
|
|
569
|
+
files = [Path(p).expanduser() for p in paths]
|
|
570
|
+
if not files:
|
|
571
|
+
raise ValueError("No files to send")
|
|
572
|
+
for file in files:
|
|
573
|
+
if not file.is_file():
|
|
574
|
+
raise ValueError(f"No file at {file.name}")
|
|
575
|
+
x, y = _point(at)
|
|
576
|
+
return _transfer(["drop", f"{x:.4f}", f"{y:.4f}"] + [str(f.resolve()) for f in files], run=run)
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def transfer_from_phone(at, dest_dir, *, run=subprocess.run):
|
|
580
|
+
dest = Path(dest_dir).expanduser()
|
|
581
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
582
|
+
x, y = _point(at)
|
|
583
|
+
answer = _transfer(["drag-out", f"{x:.4f}", f"{y:.4f}", str(dest.resolve())], run=run)
|
|
584
|
+
return [str(p) for p in answer.get("received") or ()]
|
|
585
|
+
|