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/doctor.py
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
"""`mobster doctor`: check everything a live task needs, and say how to fix what is missing.
|
|
2
|
+
|
|
3
|
+
The checks change nothing on the phone: they never build, install, start or tap anything. They ask the tools, the
|
|
4
|
+
phone's lockdown service and WebDriverAgent (GET requests only) what state they are in. Keys are reported as set or not
|
|
5
|
+
set, never shown. A problem is a row of ``states.ROWS``, so it says what the Mac app and ``mobster mcp`` say, in the
|
|
6
|
+
same words; ``doctor_fix`` (``mobster doctor --fix``) acts on the row's fix.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from dataclasses import asdict, dataclass
|
|
12
|
+
|
|
13
|
+
from . import __version__, states
|
|
14
|
+
|
|
15
|
+
OK, WARN, FAIL, SKIP = "ok", "warn", "fail", "skip"
|
|
16
|
+
GLYPHS = {OK: "✓", WARN: "!", FAIL: "✗", SKIP: "–"}
|
|
17
|
+
from .links import TROUBLESHOOTING as DOCS # noqa: E402
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Check:
|
|
22
|
+
key: str
|
|
23
|
+
label: str
|
|
24
|
+
state: str
|
|
25
|
+
detail: str
|
|
26
|
+
fix: str = ""
|
|
27
|
+
# The row of states.ROWS this finding is, when it is one; its fix is what `mobster doctor --fix` runs.
|
|
28
|
+
cause: str = ""
|
|
29
|
+
|
|
30
|
+
def public(self):
|
|
31
|
+
value = asdict(self)
|
|
32
|
+
if self.cause:
|
|
33
|
+
value["action"] = states.fix_public(states.row(self.cause).fix)
|
|
34
|
+
else:
|
|
35
|
+
del value["cause"]
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def finding(key, label, level, cause, *, phone=None, extra=""):
|
|
40
|
+
"""A problem as the table says it: the row's message (then ``extra``, what only this check knows) and its next
|
|
41
|
+
step."""
|
|
42
|
+
found = states.row(cause)
|
|
43
|
+
message, step, _need = states.words(found.state, cause, phone)
|
|
44
|
+
# A fix Mobster can run, or a command to type, gets a second line saying which; the person's own taps need none.
|
|
45
|
+
named = isinstance(found.fix, states.Run) and found.fix.command in step
|
|
46
|
+
route = "" if named or isinstance(found.fix, (states.Yours, states.Wait)) \
|
|
47
|
+
else "\n" + states.how(found.fix).replace("`", "")
|
|
48
|
+
return Check(key, label, level, f"{message} {extra}".strip(), step + route, cause)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_python():
|
|
52
|
+
version = ".".join(map(str, sys.version_info[:3]))
|
|
53
|
+
if sys.version_info < (3, 12):
|
|
54
|
+
return Check("python", "Python", FAIL, version, "Mobster needs Python 3.12 or later: brew install python@3.12")
|
|
55
|
+
return Check("python", "Python", OK, version)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def check_install():
|
|
59
|
+
from .paths import source_checkout
|
|
60
|
+
kind = ("frozen app build" if getattr(sys, "frozen", False) else "source checkout" if source_checkout()
|
|
61
|
+
else "installed")
|
|
62
|
+
# One line: where Mobster keeps its data is in `mobster help environment`.
|
|
63
|
+
return Check("install", "Mobster", OK, f"{__version__}, {kind}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def check_key(env_file=None):
|
|
67
|
+
"""The key Mobster's agent runs on."""
|
|
68
|
+
from .engines import model_provider, smart_key, smart_model
|
|
69
|
+
from .login import mask, source_words
|
|
70
|
+
key = smart_key()
|
|
71
|
+
from .ai_plans import providers
|
|
72
|
+
routed = providers.get(model_provider(smart_model()))
|
|
73
|
+
if routed is not None:
|
|
74
|
+
# A plan or provider from Settings › Models or `mobster login <id>` (its key never shown, even masked).
|
|
75
|
+
if key:
|
|
76
|
+
return Check("key", "Model key", OK, f"{routed.name}: {providers.chosen_model(routed)}")
|
|
77
|
+
return finding("key", "Model key", FAIL, "no_key", extra=f"You chose {routed.name}, and its key or sign-in is "
|
|
78
|
+
"missing.")
|
|
79
|
+
if key:
|
|
80
|
+
claude = model_provider(smart_model()) == "anthropic"
|
|
81
|
+
name, variable = ("Claude", "ANTHROPIC_API_KEY") if claude else ("OpenAI", "OPENAI_API_KEY")
|
|
82
|
+
return Check("key", "Model key", OK, f"{name} ({mask(key)}), {source_words(variable)}")
|
|
83
|
+
model = smart_model()
|
|
84
|
+
if model_provider(model) == "anthropic" and os.environ.get("MOBSTER_SMART_MODEL"):
|
|
85
|
+
return finding("key", "Model key", FAIL, "no_key",
|
|
86
|
+
extra=f"MOBSTER_SMART_MODEL is {model}, and no Claude key is saved.")
|
|
87
|
+
if env_file and not os.path.exists(os.path.expanduser(str(env_file))):
|
|
88
|
+
return finding("key", "Model key", FAIL, "no_key", extra=f"The env file {_home(env_file)} doesn't exist.")
|
|
89
|
+
return finding("key", "Model key", FAIL, "no_key")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def check_xcode():
|
|
93
|
+
from .mac_tools import xcode_status
|
|
94
|
+
try:
|
|
95
|
+
status = xcode_status()
|
|
96
|
+
except Exception as error:
|
|
97
|
+
return Check("xcode", "Xcode", WARN, f"could not check ({type(error).__name__})")
|
|
98
|
+
if status["state"] == "ok":
|
|
99
|
+
return Check("xcode", "Xcode", OK, status.get("version") or "ready")
|
|
100
|
+
detail = {"missing": "not installed (the Command Line Tools alone cannot build WebDriverAgent)",
|
|
101
|
+
"license": "license not accepted",
|
|
102
|
+
"first_launch": "first-launch components missing", "no_ios": "no iOS platform installed",
|
|
103
|
+
"broken": "xcodebuild fails"}.get(status["state"], status["state"])
|
|
104
|
+
fix = " && ".join(status.get("fixes") or ()) or "Install Xcode from the App Store and open it once"
|
|
105
|
+
return Check("xcode", "Xcode", WARN, f"{detail}; needed only to build WebDriverAgent for a USB iPhone", fix)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def check_tools():
|
|
109
|
+
from .mac_tools import tool
|
|
110
|
+
names = ("idevice_id", "ideviceinfo", "iproxy", "ideviceinstaller")
|
|
111
|
+
missing = [name for name in names if not tool(name)]
|
|
112
|
+
if not missing:
|
|
113
|
+
return Check("tools", "iPhone tools", OK, "installed")
|
|
114
|
+
return Check("tools", "iPhone tools", WARN, "missing " + ", ".join(missing) + " (for an iPhone on a cable)",
|
|
115
|
+
"brew install libimobiledevice ideviceinstaller")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def check_usb(manager):
|
|
119
|
+
from .mac_tools import tool
|
|
120
|
+
if not tool("idevice_id"):
|
|
121
|
+
return Check("usb", "iPhone", SKIP, "not checked: the iPhone tools are missing")
|
|
122
|
+
try:
|
|
123
|
+
devices = manager.devices()
|
|
124
|
+
except Exception as error:
|
|
125
|
+
return Check("usb", "iPhone", WARN, f"couldn't list devices ({type(error).__name__})")
|
|
126
|
+
if not devices:
|
|
127
|
+
saved = _saved(manager)
|
|
128
|
+
return finding("usb", "iPhone", WARN, "unplugged" if saved.get("udid") else "none_chosen",
|
|
129
|
+
phone=_name(saved.get("device_name")))
|
|
130
|
+
chosen = manager.device(devices)
|
|
131
|
+
device = chosen or devices[0]
|
|
132
|
+
name = device.get("modelName") or device.get("model") or "iPhone"
|
|
133
|
+
ios = f", iOS {device['ios']}" if device.get("ios") else ""
|
|
134
|
+
more = f" (+{len(devices) - 1} more)" if len(devices) > 1 else ""
|
|
135
|
+
if not device.get("trusted"):
|
|
136
|
+
return finding("usb", "iPhone", WARN, "computer_untrusted", phone=_name(device.get("name")),
|
|
137
|
+
extra=f"It is plugged in{more}." if more else "")
|
|
138
|
+
return Check("usb", "iPhone", OK, f"{name}{ios}, trusts this Mac{more}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def check_runner(manager):
|
|
142
|
+
import time
|
|
143
|
+
|
|
144
|
+
from .signing import renew_window
|
|
145
|
+
try:
|
|
146
|
+
settings = manager.settings()
|
|
147
|
+
expires = manager.signature_expiry()
|
|
148
|
+
except Exception:
|
|
149
|
+
return Check("runner", "Mobster's helper", SKIP, "not checked")
|
|
150
|
+
if not settings.get("built_for") and not settings.get("udid"):
|
|
151
|
+
return Check("runner", "Mobster's helper", SKIP, "not installed yet",
|
|
152
|
+
"mobster setup installs it")
|
|
153
|
+
phone = _name(settings.get("device_name"))
|
|
154
|
+
if expires is not None and expires <= time.time():
|
|
155
|
+
return finding("runner", "Mobster's helper", FAIL, "signature_expired", phone=phone,
|
|
156
|
+
extra="Its 7-day signature ran out.")
|
|
157
|
+
if manager.built():
|
|
158
|
+
days = f", refreshes in {int((expires - time.time()) // 86400)} days" if expires else ""
|
|
159
|
+
if expires is not None and expires - time.time() <= renew_window():
|
|
160
|
+
return Check("runner", "Mobster's helper", WARN, f"installed{days}",
|
|
161
|
+
"Mobster for Mac refreshes it while your iPhone is plugged in and unlocked, or run: "
|
|
162
|
+
"mobster setup")
|
|
163
|
+
return Check("runner", "Mobster's helper", OK, f"installed{days}")
|
|
164
|
+
return finding("runner", "Mobster's helper", WARN, "not_built", phone=phone)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def check_wda(wda_url):
|
|
168
|
+
from .transport import HTTP
|
|
169
|
+
client = None
|
|
170
|
+
try:
|
|
171
|
+
client = HTTP(wda_url)
|
|
172
|
+
status = client.request("GET", "/status", timeout=2)
|
|
173
|
+
except Exception:
|
|
174
|
+
return Check("wda", "Phone connection", FAIL, f"nothing answers at {wda_url.replace('http://', '')}",
|
|
175
|
+
start_wda_hint())
|
|
176
|
+
finally:
|
|
177
|
+
if client is not None:
|
|
178
|
+
client.close()
|
|
179
|
+
value = status.get("value") if isinstance(status, dict) else None
|
|
180
|
+
value = value if isinstance(value, dict) else {}
|
|
181
|
+
ios = (value.get("os") or {}).get("version") if isinstance(value.get("os"), dict) else None
|
|
182
|
+
device = value.get("device") if isinstance(value.get("device"), str) else None
|
|
183
|
+
parts = [wda_url.replace("http://", "")]
|
|
184
|
+
if device:
|
|
185
|
+
parts.append(device)
|
|
186
|
+
if ios:
|
|
187
|
+
parts.append(f"iOS {ios}")
|
|
188
|
+
if value.get("ready") is not True:
|
|
189
|
+
return Check("wda", "Phone connection", WARN, ", ".join(parts) + ", not ready yet",
|
|
190
|
+
"Wait a few seconds; if it stays that way, run: mobster setup")
|
|
191
|
+
return Check("wda", "Phone connection", OK, ", ".join(parts) + ", ready")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def start_wda_hint(one_line=False):
|
|
195
|
+
"""How to get the phone to answer, one route per line (``one_line``: joined, for an error message)."""
|
|
196
|
+
routes = ("Set up your iPhone: mobster setup, or in Mobster for Mac",
|
|
197
|
+
"For a simulator: mobster sim prepare, then --device NAME")
|
|
198
|
+
return "; ".join(routes) if one_line else "\n".join(routes)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
VERIFY_HINT = ("Building an iOS app? `mobster test`, `mobster verify` and `mobster mcp` run on a simulator Mobster "
|
|
202
|
+
"manages and need none of the iPhone checks above: run `mobster sim doctor`.")
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def check_locked(wda_url, phone=None):
|
|
206
|
+
from .transport import HTTP
|
|
207
|
+
client = None
|
|
208
|
+
try:
|
|
209
|
+
client = HTTP(wda_url)
|
|
210
|
+
locked = client.request("GET", "/wda/locked", timeout=3).get("value")
|
|
211
|
+
except Exception:
|
|
212
|
+
return Check("locked", "Screen", SKIP, "could not ask whether the phone is locked")
|
|
213
|
+
finally:
|
|
214
|
+
if client is not None:
|
|
215
|
+
client.close()
|
|
216
|
+
if locked is True:
|
|
217
|
+
return finding("locked", "Screen", FAIL, "locked", phone=phone, extra="Mobster never enters a passcode.")
|
|
218
|
+
if locked is not False:
|
|
219
|
+
return Check("locked", "Screen", SKIP, "could not ask whether the phone is locked")
|
|
220
|
+
return Check("locked", "Screen", OK, "unlocked")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def check_lease(wda_url):
|
|
224
|
+
from .journal import JournalError, Lease
|
|
225
|
+
try:
|
|
226
|
+
lease = Lease.device(wda_url.rstrip("/"))
|
|
227
|
+
except JournalError:
|
|
228
|
+
return Check("lease", "In use", WARN, "another Mobster is running a task on this phone",
|
|
229
|
+
"Finish or stop the task in Mobster for Mac, `mobster serve` or the other terminal")
|
|
230
|
+
lease.close()
|
|
231
|
+
return Check("lease", "In use", OK, "no other Mobster is using it")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# The helper's log names a cause for one start; older than this, it is a different day's start.
|
|
235
|
+
LOG_FRESH_S = 600
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# The checks that say a phone's own problem: when one has, the connection that doesn't answer is its consequence.
|
|
239
|
+
PHONE_KEYS = frozenset({"usb", "runner", "wifi", "devmode"})
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def refine_wda(wda_url, manager, now=None):
|
|
243
|
+
"""The phone connection that doesn't answer, said as the row it is: the cause the helper's last start logged
|
|
244
|
+
(Developer Mode, trust, UI Automation) while that log is fresh, else a helper that isn't running. Called only when
|
|
245
|
+
the checks before it found no cause: a phone that isn't plugged in is already said, and says it once."""
|
|
246
|
+
import time
|
|
247
|
+
|
|
248
|
+
from .engine_health import PHONE_CAUSES
|
|
249
|
+
cause = "stopped"
|
|
250
|
+
try:
|
|
251
|
+
age = (time.time() if now is None else now) - (manager.logs / "wda-runner.log").stat().st_mtime
|
|
252
|
+
if age < LOG_FRESH_S:
|
|
253
|
+
manager.runner_hint()
|
|
254
|
+
if manager.runner_cause in PHONE_CAUSES:
|
|
255
|
+
cause = manager.runner_cause
|
|
256
|
+
except OSError:
|
|
257
|
+
pass
|
|
258
|
+
saved = _saved(manager)
|
|
259
|
+
return finding("wda", "Phone connection", FAIL, cause, phone=_name(saved.get("device_name")),
|
|
260
|
+
extra=f"Nothing answers at {wda_url.replace('http://', '')}.")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def check_port(manager, netstat=None, listening=None):
|
|
264
|
+
"""A program on the port Mobster's helper needs, while no helper answers there. Named, never stopped: stopping it is
|
|
265
|
+
``doctor_fix``'s ``free_port``, which asks. ``netstat`` replaces the socket list and ``listening`` the connect, in
|
|
266
|
+
tests."""
|
|
267
|
+
from .doctor_fix import holders, taken
|
|
268
|
+
ports = (manager.wda_port, manager.video_port)
|
|
269
|
+
held = taken(ports, listening)
|
|
270
|
+
if not held:
|
|
271
|
+
return Check("port", "Ports", OK, f"{manager.wda_port} and {manager.video_port} are free")
|
|
272
|
+
found = holders(held, netstat)
|
|
273
|
+
if not found:
|
|
274
|
+
return finding("port", "Ports", WARN, "port_taken",
|
|
275
|
+
extra=f"Something holds {', '.join(map(str, held))}, and Mobster couldn't read which program.")
|
|
276
|
+
who = ", ".join(f"{os.path.basename(holder.command.split()[0])} (pid {holder.pid})" for holder in found)
|
|
277
|
+
return finding("port", "Ports", WARN, "port_taken", extra=f"{who} holds {', '.join(str(h.port) for h in found)}.")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def check_network(interface=None):
|
|
281
|
+
"""Whether this Mac has a network: a default route. Reads `route`, sends nothing to anyone."""
|
|
282
|
+
from . import native_helpers
|
|
283
|
+
from .wireless import network
|
|
284
|
+
if interface is None:
|
|
285
|
+
if not native_helpers.devices_allowed():
|
|
286
|
+
return Check("network", "Network", SKIP, "not checked")
|
|
287
|
+
interface = network.primary_interface()
|
|
288
|
+
if not interface:
|
|
289
|
+
return finding("network", "Network", WARN, "network_gone")
|
|
290
|
+
return Check("network", "Network", OK, f"online ({interface})")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def check_service(read=None, urls=None):
|
|
294
|
+
"""Mobster for Mac's service, when one was started on this Mac and left its address behind: frozen (it accepted the
|
|
295
|
+
connection and never answered) or down (the address is stale: it crashed). Nothing started, nothing to say. A
|
|
296
|
+
warning, not a failure: `mobster run` and the terminal UI don't use it."""
|
|
297
|
+
from .engine_health import read_from_app
|
|
298
|
+
from .threads.client import service_urls
|
|
299
|
+
urls = service_urls() if urls is None else urls
|
|
300
|
+
if not urls:
|
|
301
|
+
return Check("service", "Mobster for Mac", SKIP, "not running")
|
|
302
|
+
engine = (read or read_from_app)(urls[0])
|
|
303
|
+
if engine and engine.get("cause") in ("no_answer", "not_running"):
|
|
304
|
+
return finding("service", "Mobster for Mac", WARN, engine["cause"])
|
|
305
|
+
return Check("service", "Mobster for Mac", OK, "answering")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _saved(manager):
|
|
309
|
+
try:
|
|
310
|
+
return manager.settings()
|
|
311
|
+
except Exception:
|
|
312
|
+
return {}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _name(value):
|
|
316
|
+
return value if isinstance(value, str) and value.strip() else None
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def check_wifi(udid, data_dir, name=None, *, peers=None, nwi=None, gateway=None, local_network=None,
|
|
320
|
+
network_result=None):
|
|
321
|
+
"""The Wi-Fi lines, for a phone with Wi-Fi turned on (`mobster wifi on`)."""
|
|
322
|
+
from .wireless import devicectl, network, store, transport_on, words
|
|
323
|
+
try:
|
|
324
|
+
setting = store.get(data_dir, udid) if udid else None
|
|
325
|
+
except Exception:
|
|
326
|
+
setting = None
|
|
327
|
+
if not setting or not setting["enabled"]:
|
|
328
|
+
return []
|
|
329
|
+
who = name or "the iPhone"
|
|
330
|
+
checks = []
|
|
331
|
+
if not transport_on():
|
|
332
|
+
checks.append(Check("wifi_gate", "Wi-Fi for iPhones", SKIP,
|
|
333
|
+
f"Wi-Fi is on for {who}, but off in this Mobster (MOBSTER_WIFI_TRANSPORT)"))
|
|
334
|
+
peer, error = None, None
|
|
335
|
+
if peers is None:
|
|
336
|
+
try:
|
|
337
|
+
peers = devicectl.list_devices()
|
|
338
|
+
except devicectl.DevicectlError as failure:
|
|
339
|
+
error = failure
|
|
340
|
+
except RuntimeError:
|
|
341
|
+
error = devicectl.DevicectlError("devices are off in this process", "off")
|
|
342
|
+
if peers is not None:
|
|
343
|
+
peer = peers.get(str(udid).upper())
|
|
344
|
+
if error is not None and error.code == "hang":
|
|
345
|
+
checks.append(Check("wifi", "Wi-Fi link", WARN, "Xcode's device service didn't answer in 8 s",
|
|
346
|
+
"Plug the iPhone in once, or restart this Mac if it keeps happening"))
|
|
347
|
+
elif error is not None:
|
|
348
|
+
checks.append(Check("wifi", "Wi-Fi link", SKIP, f"not checked: {error}"))
|
|
349
|
+
elif peer is None:
|
|
350
|
+
checks.append(Check("wifi", "Wi-Fi link", WARN, f"Xcode's device service doesn't list {who}",
|
|
351
|
+
"Plug it in once with the cable so this Mac pairs with it again"))
|
|
352
|
+
elif peer.wired:
|
|
353
|
+
checks.append(Check("wifi", "Wi-Fi link", OK, f"{who} is on the cable now; Wi-Fi takes over when it's "
|
|
354
|
+
"unplugged"))
|
|
355
|
+
elif not peer.network:
|
|
356
|
+
checks.append(finding("wifi", "Wi-Fi link", WARN, "wifi_lost", phone=name))
|
|
357
|
+
elif peer.tunnel == "up":
|
|
358
|
+
checks.append(Check("wifi", "Wi-Fi link", OK, f"{who} is reachable over the encrypted Wi-Fi link"))
|
|
359
|
+
else:
|
|
360
|
+
checks.append(Check("wifi", "Wi-Fi link", WARN, f"{who} is on the network, but the encrypted link isn't up "
|
|
361
|
+
"yet", "Unlock it; Mobster brings "
|
|
362
|
+
"the link up when it needs it"))
|
|
363
|
+
mode = peer.developer_mode if peer is not None else None
|
|
364
|
+
if mode == "enabled":
|
|
365
|
+
checks.append(Check("devmode", "Developer Mode", OK, "on"))
|
|
366
|
+
elif mode == "disabled":
|
|
367
|
+
checks.append(finding("devmode", "Developer Mode", FAIL, "developer_mode_off", phone=name))
|
|
368
|
+
elif mode:
|
|
369
|
+
checks.append(Check("devmode", "Developer Mode", FAIL, f"{mode} on {who}",
|
|
370
|
+
"On the iPhone: Settings › Privacy & Security › Developer Mode, then restart it"))
|
|
371
|
+
on_cable = peer is not None and peer.wired
|
|
372
|
+
gateway = network.default_gateway() if gateway is None else gateway
|
|
373
|
+
allowed = network.local_network_allowed(gateway) if local_network is None else local_network
|
|
374
|
+
if allowed is False:
|
|
375
|
+
checks.append(Check("localnet", "Local Network", WARN if on_cable else FAIL,
|
|
376
|
+
"macOS doesn't let this app reach your network",
|
|
377
|
+
"System Settings › Privacy & Security › Local Network: turn on Mobster (or your "
|
|
378
|
+
"terminal app)"))
|
|
379
|
+
elif allowed is True:
|
|
380
|
+
checks.append(Check("localnet", "Local Network", OK, "allowed"))
|
|
381
|
+
text = network.nwi() if nwi is None else nwi
|
|
382
|
+
vpn = network.vpn_interface(text)
|
|
383
|
+
if vpn:
|
|
384
|
+
checks.append(Check("vpn", "VPN", WARN, f"a VPN is on ({vpn})",
|
|
385
|
+
"Turn it off, or plug the iPhone in: a VPN can keep this Mac and the iPhone apart"))
|
|
386
|
+
if network_result is None:
|
|
387
|
+
try:
|
|
388
|
+
network_result = network.peer_check(gateway=gateway, allowed=allowed)
|
|
389
|
+
except Exception:
|
|
390
|
+
network_result = {"state": "unknown"}
|
|
391
|
+
state = (network_result or {}).get("state")
|
|
392
|
+
if state == "blocked":
|
|
393
|
+
problem = words.problem("network_blocked")
|
|
394
|
+
checks.append(Check("peers", "Network", WARN if on_cable else FAIL, problem["message"], problem["fix"]))
|
|
395
|
+
elif state == "ok":
|
|
396
|
+
checks.append(Check("peers", "Network", OK, "devices on it can see each other"))
|
|
397
|
+
elif state == "offline":
|
|
398
|
+
checks.append(Check("peers", "Network", WARN, "this Mac isn't on a network",
|
|
399
|
+
"Join the same Wi-Fi as the iPhone, or plug it in"))
|
|
400
|
+
elif state == "unknown":
|
|
401
|
+
checks.append(Check("peers", "Network", SKIP, "couldn't tell whether devices on it can see each other"))
|
|
402
|
+
checks.append(Check("awake", "Over Wi-Fi", SKIP, f"keep {who} unlocked and on the charger for workflows"))
|
|
403
|
+
return checks
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def run_checks(wda_url=None, data_dir=None, device=True, env_file=None, udid=None):
|
|
407
|
+
"""Every check in order. The ones that read only this Mac run beside the ones that ask the phone, so the report
|
|
408
|
+
waits for the slowest, not for their sum (the 10 s bar, S4)."""
|
|
409
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
410
|
+
|
|
411
|
+
from .cli_constants import DEFAULT_WDA_URL
|
|
412
|
+
from .device_manager import DeviceManager
|
|
413
|
+
from .devices import manager_for
|
|
414
|
+
from .paths import user_data_dir
|
|
415
|
+
wda_url = (wda_url or os.environ.get("MOBSTER_WDA_URL") or DEFAULT_WDA_URL).rstrip("/")
|
|
416
|
+
with ThreadPoolExecutor(max_workers=3, thread_name_prefix="mobster-doctor") as pool:
|
|
417
|
+
service, network = pool.submit(check_service), pool.submit(check_network)
|
|
418
|
+
xcode = pool.submit(check_xcode) if device else None
|
|
419
|
+
checks = [check_python(), check_install(), check_key(env_file), network.result()]
|
|
420
|
+
manager = name = None
|
|
421
|
+
if device:
|
|
422
|
+
manager = (manager_for(udid, data_dir) if udid else None) or DeviceManager(data_dir or user_data_dir(),
|
|
423
|
+
wda_url)
|
|
424
|
+
checks += [xcode.result(), check_tools(), check_usb(manager), check_runner(manager)]
|
|
425
|
+
saved = _saved(manager)
|
|
426
|
+
phone, name = udid or saved.get("udid"), _name(saved.get("device_name"))
|
|
427
|
+
checks += check_wifi(phone, data_dir or user_data_dir(), name)
|
|
428
|
+
wda = check_wda(wda_url)
|
|
429
|
+
phone_causes = any(c.key in PHONE_KEYS and c.cause for c in checks)
|
|
430
|
+
if wda.state == FAIL and manager is not None and not phone_causes:
|
|
431
|
+
wda = refine_wda(wda_url, manager)
|
|
432
|
+
checks.append(wda)
|
|
433
|
+
service = service.result()
|
|
434
|
+
if wda.state != FAIL:
|
|
435
|
+
checks.append(check_locked(wda_url, name))
|
|
436
|
+
elif manager is not None and not phone_causes and service.state != OK:
|
|
437
|
+
# Plugged in, built, and nothing answers: a program on the helper's port is the other thing that stops it.
|
|
438
|
+
# (A running Mobster service holds the port for its own helper.)
|
|
439
|
+
checks.append(check_port(manager))
|
|
440
|
+
checks.append(check_lease(wda_url))
|
|
441
|
+
checks.append(service)
|
|
442
|
+
return checks
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def problems(checks):
|
|
446
|
+
return [check for check in checks if check.state == FAIL]
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def fix_report(steps, paint, width=100):
|
|
450
|
+
"""What `--fix` did and could not do, one line each. The person's own taps, a command Mobster doesn't run and a
|
|
451
|
+
state that clears by itself are not here: the check that found them says them under its own line."""
|
|
452
|
+
from .style import wrap
|
|
453
|
+
glyphs = {"fixed": ("✓", "green"), "failed": ("✗", "coral"), "refused": ("–", "amber"), "declined": ("–", "amber"),
|
|
454
|
+
"needs_terminal": ("–", "amber")}
|
|
455
|
+
lines = [paint("What --fix did", "bold"), ""]
|
|
456
|
+
shown = [step for step in steps if step.outcome in glyphs]
|
|
457
|
+
for step in shown:
|
|
458
|
+
glyph, role = glyphs[step.outcome]
|
|
459
|
+
lines.append(f" {paint(glyph, role, 'bold')} " + wrap(step.detail, width, indent=4, first=0))
|
|
460
|
+
if not shown:
|
|
461
|
+
lines.append(" Nothing Mobster can do for you.")
|
|
462
|
+
return lines
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def plain_report(checks, color=False, verify_hint=None, width=100, steps=None):
|
|
466
|
+
"""The report for a terminal (or a file): one line per check, the fix under each problem, wrapped at ``width``
|
|
467
|
+
with a hanging indent under the value column. After `--fix`, ``steps`` (doctor_fix.Step) adds what it did."""
|
|
468
|
+
from .style import Palette, wrap
|
|
469
|
+
paint = Palette(color)
|
|
470
|
+
roles = {OK: "green", WARN: "amber", FAIL: "coral", SKIP: "tertiary"}
|
|
471
|
+
column = 4 + 18
|
|
472
|
+
width = max(60, min(width, 110))
|
|
473
|
+
lines = [paint("Mobster doctor", "bold"), ""]
|
|
474
|
+
for check in checks:
|
|
475
|
+
glyph = paint(GLYPHS[check.state], roles[check.state], "bold")
|
|
476
|
+
detail = wrap(check.detail, width, indent=column, first=column)[column:]
|
|
477
|
+
text = f" {glyph} {check.label:<18}{detail}"
|
|
478
|
+
lines.append(paint(text, "tertiary") if check.state == SKIP else text)
|
|
479
|
+
if check.fix and check.state in {WARN, FAIL}:
|
|
480
|
+
for route in check.fix.split("\n"):
|
|
481
|
+
lines.append(paint(wrap("→ " + route, width, indent=column + 2, first=column), "secondary"))
|
|
482
|
+
failed = problems(checks)
|
|
483
|
+
warned = [c for c in checks if c.state == WARN]
|
|
484
|
+
lines.append("")
|
|
485
|
+
help_link = "Help: " + paint(DOCS, "accent")
|
|
486
|
+
|
|
487
|
+
def ending(head, rest=""):
|
|
488
|
+
# The help link joins the line when it fits, else gets its own: no line is wider than the terminal.
|
|
489
|
+
if len(head) + len(rest) + 2 + len("Help: " + DOCS) <= width:
|
|
490
|
+
return [paint(head, "bold") + rest + " " + help_link]
|
|
491
|
+
return [paint(head, "bold") + rest, help_link]
|
|
492
|
+
if failed:
|
|
493
|
+
count = len(failed)
|
|
494
|
+
lines += ending(f"{count} thing{'s' if count != 1 else ''} to fix before your first task.")
|
|
495
|
+
elif warned:
|
|
496
|
+
lines += ending("Ready for tasks", f", with {len(warned)} thing{'s' if len(warned) != 1 else ''} to look at.")
|
|
497
|
+
else:
|
|
498
|
+
lines.append(paint("Ready for tasks.", "bold"))
|
|
499
|
+
if steps is not None:
|
|
500
|
+
lines += ["", *fix_report(steps, paint, width)]
|
|
501
|
+
if verify_hint:
|
|
502
|
+
lines += ["", wrap(verify_hint, width)]
|
|
503
|
+
return "\n".join(lines)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _home(path):
|
|
507
|
+
home = os.path.expanduser("~")
|
|
508
|
+
path = str(path)
|
|
509
|
+
return "~" + path[len(home):] if path.startswith(home) else path
|