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,526 @@
|
|
|
1
|
+
"""The phone guard: whether an iPhone can be driven now, decided by code (never the model).
|
|
2
|
+
|
|
3
|
+
This build only *detects*. It never types a passcode, never taps the lock screen and never answers a
|
|
4
|
+
system sheet: a locked iPhone, a locked app or the App Store's side-button confirmation
|
|
5
|
+
stops the run at once with one plain sentence, before a model call is spent on a screen the agent cannot
|
|
6
|
+
use. One exception waits instead of stopping ("Look at your iPhone", WOW §4.1): a task a person just started
|
|
7
|
+
in the Mac app, the terminal UI or ``mobster chat`` on a locked iPhone waits up to UNLOCK_WAIT_S for that
|
|
8
|
+
person to unlock it, reading only ``GET /wda/locked``, and starts the moment it reads unlocked. An attended run
|
|
9
|
+
also waits for a Face ID sheet to clear, then reads the app again; only the owner authenticates. Unlocking with a saved
|
|
10
|
+
passcode (PLAN §3) waits for the owner's sign-off; ``CAN_UNLOCK`` says so to the other parts (MCP's ``unlock_status``,
|
|
11
|
+
the settings), and ``finish`` has nothing to relock.
|
|
12
|
+
|
|
13
|
+
What it reads, all through WebDriverAgent and all read-only:
|
|
14
|
+
|
|
15
|
+
- ``GET /wda/locked`` (sessionless, answers in milliseconds even when the UI is wedged), at every check;
|
|
16
|
+
- ``GET /wda/activeAppInfo`` (sessionless), to name the app a sheet is over;
|
|
17
|
+
- SpringBoard's own accessibility tree, only when the frontier suspects a system sheet: the session's
|
|
18
|
+
``defaultActiveApplication`` is pointed at SpringBoard for one ``/source`` read and always restored.
|
|
19
|
+
|
|
20
|
+
Sheet labels are matched as whole labels, never substrings, so a notification banner that happens to say
|
|
21
|
+
"locked" is not a locked-app sheet. They come from iOS 18-26's wording and are provisional until the
|
|
22
|
+
owner's read-only ``mobster devices lock-probe`` confirms them on his phone (PLAN §6.1); a sheet the
|
|
23
|
+
classifier does not know is ``unknown`` and the run goes on as before (the guard never taps anything,
|
|
24
|
+
so a miss costs no more than today).
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import re
|
|
29
|
+
import time
|
|
30
|
+
|
|
31
|
+
from . import harness_api
|
|
32
|
+
from .agent_hooks import READY, GuardVerdict
|
|
33
|
+
|
|
34
|
+
# This build cannot unlock an iPhone (no passcode is ever stored, read or typed).
|
|
35
|
+
CAN_UNLOCK = False
|
|
36
|
+
SPRINGBOARD = "com.apple.springboard"
|
|
37
|
+
# Each read the guard makes; a locked phone answers /wda/locked in well under a second.
|
|
38
|
+
READ_TIMEOUT = 3.0
|
|
39
|
+
# How long a USB listing is trusted (``attached``): a pulled cable is known within this many seconds.
|
|
40
|
+
ATTACHED_TTL = 2.0
|
|
41
|
+
# Look at your iPhone (WOW §4.1, v0): an interactive task that starts on a locked iPhone waits this many seconds for
|
|
42
|
+
# the person to unlock it, reading /wda/locked every UNLOCK_POLL_S. Reads only: no button, no tap, no key, ever.
|
|
43
|
+
UNLOCK_WAIT_S = 120
|
|
44
|
+
UNLOCK_POLL_S = 1.0
|
|
45
|
+
# The states a start waits through: the person clears them by unlocking. Never a sheet or an unreadable phone.
|
|
46
|
+
WAIT_STATES = ("lock_screen", "passcode_keypad")
|
|
47
|
+
|
|
48
|
+
STATES = ("unlocked", "lock_screen", "passcode_keypad", "face_id_sheet", "app_locked_sheet", "apple_confirmation",
|
|
49
|
+
"unknown")
|
|
50
|
+
|
|
51
|
+
# The user-facing sentences (PLAN §3.9-§3.10). None names a passcode, a code or a digit count. The §3.9
|
|
52
|
+
# sentence's "or let Mobster unlock it in Settings › iPhones" clause is left out: that setting needs
|
|
53
|
+
# passcode unlock, which this build does not have.
|
|
54
|
+
PHONE_LOCKED = "Your iPhone is locked. Unlock it and try again. No action was taken."
|
|
55
|
+
LOCKED_DURING = "Your iPhone locked during the task, so Mobster stopped. Unlock it and try again."
|
|
56
|
+
NOT_RESPONDING = ("Your iPhone isn't responding. Unlock it, close Control Center or Siri if they're open, then "
|
|
57
|
+
"try again. No action was taken.")
|
|
58
|
+
UNPLUGGED = "Your iPhone was unplugged. Plug it back in and try again."
|
|
59
|
+
# A task over Wi-Fi whose encrypted link went down.
|
|
60
|
+
WIFI_LOST = ("Your iPhone went out of reach over Wi-Fi, so Mobster stopped. Keep it unlocked and on the same "
|
|
61
|
+
"Wi-Fi as this Mac, or plug it in, then try again.")
|
|
62
|
+
# A task a person started waits this long for its phone to come back (DetectGuard.await_return), then stops with these.
|
|
63
|
+
RETURN_WAIT_TIMED_OUT = {
|
|
64
|
+
"unplugged": "Your iPhone stayed unplugged, so Mobster stopped. "
|
|
65
|
+
"Plug it back in and pick the task up where it left off.",
|
|
66
|
+
"wifi_lost": ("Your iPhone stayed out of reach over Wi-Fi, so Mobster stopped. Plug it in or bring it back near "
|
|
67
|
+
"this Mac, then pick the task up where it left off."),
|
|
68
|
+
}
|
|
69
|
+
FACE_ID = "{app} is asking for Face ID. Mobster can't use Face ID: open {app} on your iPhone once, then try again."
|
|
70
|
+
APP_LOCKED = "{app} is locked on your iPhone. Mobster won't open locked apps; open it yourself or remove the lock."
|
|
71
|
+
APPLE_CONFIRMATION = "The App Store wants you to confirm on your iPhone. Mobster never does that for you."
|
|
72
|
+
# The stop when a start waited on a locked iPhone (the Mac app's card, `mobster chat`) and nobody unlocked it.
|
|
73
|
+
LOCK_WAIT_TIMED_OUT = "Still locked, so the task didn't start. Unlock your iPhone and try again."
|
|
74
|
+
# A lock in the middle of a run a person started (DESIGN §12.1) waits the same way, and stops if nobody unlocks.
|
|
75
|
+
LOCK_WAIT_MIDRUN_TIMED_OUT = ("Your iPhone stayed locked, so Mobster stopped. Unlock it and pick the task up where it "
|
|
76
|
+
"left off.")
|
|
77
|
+
MIDRUN_CAUSES = ("resume", "lock_suspected")
|
|
78
|
+
# The owner is on the phone (owner_presence.py): a lock is theirs to deal with, never a prompt from Mobster. No
|
|
79
|
+
# sentence here names a passcode, and no event asks for an unlock.
|
|
80
|
+
OWNER_HAS_PHONE = ("You're using your iPhone, so Mobster stayed off it and the task didn't start. "
|
|
81
|
+
"Start it again when you put the phone down.")
|
|
82
|
+
|
|
83
|
+
# Whole-label markers (case-insensitive). The keypad needs all ten digit keys besides a title.
|
|
84
|
+
_KEYPAD_TITLE = re.compile(r"^(enter( your)? (iphone )?passcode|passcode)$", re.I)
|
|
85
|
+
_LOCK_EXTRAS = re.compile(r"^(emergency|cancel|delete)$", re.I)
|
|
86
|
+
_FACE_ID = re.compile(r"^(face id|try face id again|use face id( to (unlock|continue))?)$", re.I)
|
|
87
|
+
# Only a quoted app name: "Your account is locked" in a banner is not a locked app.
|
|
88
|
+
_APP_LOCKED = re.compile(r"^(?:“(?P<a>[^”]{1,60})”|\"(?P<b>[^\"]{1,60})\") is locked\.?$", re.I)
|
|
89
|
+
_APP_LOCKED_HINT = re.compile(r"^(?:require face id|face id is required to open .{1,60})$", re.I)
|
|
90
|
+
_CONFIRM = re.compile(r"^(double[- ]click to (install|pay|get|confirm|subscribe)|confirm with side button)\.?$", re.I)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def tree_labels(tree, limit=400):
|
|
94
|
+
"""The labels in a WDA ``/source?format=json`` tree, in reading order: (type, text) pairs."""
|
|
95
|
+
found, stack = [], [tree] if isinstance(tree, dict) else []
|
|
96
|
+
while stack and len(found) < limit:
|
|
97
|
+
node = stack.pop()
|
|
98
|
+
if not isinstance(node, dict):
|
|
99
|
+
continue
|
|
100
|
+
text = next((node[key] for key in ("label", "name", "value")
|
|
101
|
+
if isinstance(node.get(key), str) and node[key].strip()), "")
|
|
102
|
+
if text:
|
|
103
|
+
found.append((str(node.get("type") or ""), text.strip()[:200]))
|
|
104
|
+
children = node.get("children")
|
|
105
|
+
if isinstance(children, list):
|
|
106
|
+
stack.extend(reversed(children))
|
|
107
|
+
return found
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _keypad(texts):
|
|
111
|
+
digits = {t for t in texts if len(t) == 1 and t.isdigit()}
|
|
112
|
+
return len(digits) == 10 and any(_KEYPAD_TITLE.match(t) for t in texts)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def classify(locked, front=None, labels=None):
|
|
116
|
+
"""(state, app or None) from what the guard read; a keypad needs SpringBoard, its title and all ten digits."""
|
|
117
|
+
texts = [text for _, text in (labels or ())]
|
|
118
|
+
if locked is True:
|
|
119
|
+
return ("passcode_keypad" if front in (None, SPRINGBOARD) and _keypad(texts) else "lock_screen"), None
|
|
120
|
+
if locked is None:
|
|
121
|
+
return "unknown", None
|
|
122
|
+
for text in texts:
|
|
123
|
+
if _CONFIRM.match(text):
|
|
124
|
+
return "apple_confirmation", None
|
|
125
|
+
for text in texts:
|
|
126
|
+
match = _APP_LOCKED.match(text)
|
|
127
|
+
if match:
|
|
128
|
+
return "app_locked_sheet", next(name for name in match.groups() if name)
|
|
129
|
+
if any(_APP_LOCKED_HINT.match(text) for text in texts):
|
|
130
|
+
return "app_locked_sheet", None
|
|
131
|
+
if any(_FACE_ID.match(text) for text in texts):
|
|
132
|
+
return "face_id_sheet", None
|
|
133
|
+
if labels is not None and front == SPRINGBOARD and _keypad(texts) and any(_LOCK_EXTRAS.match(t) for t in texts):
|
|
134
|
+
# A passcode keypad over an unlocked phone: iOS asking for the passcode (a locked app whose Face ID
|
|
135
|
+
# failed, a Settings change). Never ours to answer.
|
|
136
|
+
return "app_locked_sheet", None
|
|
137
|
+
return "unlocked", None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def app_name(bundle, named=None):
|
|
141
|
+
"""A display name for a sentence: the sheet's own, else the catalog's, else "This app"."""
|
|
142
|
+
if named:
|
|
143
|
+
return named
|
|
144
|
+
if bundle and bundle != SPRINGBOARD:
|
|
145
|
+
from .catalog import app_label
|
|
146
|
+
label = app_label(bundle)
|
|
147
|
+
if label != bundle:
|
|
148
|
+
return label.rsplit(" (", 1)[0]
|
|
149
|
+
return "This app"
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def verdict_for(state, *, cause, app=None):
|
|
153
|
+
"""The GuardVerdict a state means for a check made for ``cause``."""
|
|
154
|
+
if state == "unlocked":
|
|
155
|
+
return READY
|
|
156
|
+
if state in ("lock_screen", "passcode_keypad"):
|
|
157
|
+
return GuardVerdict("stop", "phone_locked", PHONE_LOCKED if cause == "preflight" else LOCKED_DURING)
|
|
158
|
+
if state == "face_id_sheet":
|
|
159
|
+
return GuardVerdict("stop", "face_id", FACE_ID.format(app=app or "This app"))
|
|
160
|
+
if state == "app_locked_sheet":
|
|
161
|
+
return GuardVerdict("stop", "app_locked", APP_LOCKED.format(app=app or "This app"))
|
|
162
|
+
if state == "apple_confirmation":
|
|
163
|
+
return GuardVerdict("stop", "apple_confirmation", APPLE_CONFIRMATION)
|
|
164
|
+
# unknown: the phone could not be read. Fail closed: never act on a phone the guard could not read.
|
|
165
|
+
return GuardVerdict("stop", "phone_locked", NOT_RESPONDING if cause == "preflight" else LOCKED_DURING)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def read_locked(driver, timeout=READ_TIMEOUT):
|
|
169
|
+
"""/wda/locked: True, False, or None when WDA could not say."""
|
|
170
|
+
try:
|
|
171
|
+
value = driver.http.request("GET", "/wda/locked", None, timeout).get("value")
|
|
172
|
+
except Exception:
|
|
173
|
+
return None
|
|
174
|
+
return value if isinstance(value, bool) else None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def read_front(driver, timeout=READ_TIMEOUT):
|
|
178
|
+
"""The foreground bundle, or None; /wda/activeAppInfo is never waited on past READ_TIMEOUT."""
|
|
179
|
+
slow = getattr(driver, "slow_front", None)
|
|
180
|
+
if callable(slow):
|
|
181
|
+
try:
|
|
182
|
+
bundle = slow()
|
|
183
|
+
except Exception:
|
|
184
|
+
bundle = None
|
|
185
|
+
if isinstance(bundle, str) and bundle:
|
|
186
|
+
return bundle
|
|
187
|
+
try:
|
|
188
|
+
value = driver.http.request("GET", "/wda/activeAppInfo", None, min(timeout, READ_TIMEOUT)).get("value")
|
|
189
|
+
except Exception:
|
|
190
|
+
return None
|
|
191
|
+
bundle = value.get("bundleId") if isinstance(value, dict) else None
|
|
192
|
+
return bundle if isinstance(bundle, str) and bundle else None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def read_springboard_tree(driver, timeout=READ_TIMEOUT):
|
|
196
|
+
"""SpringBoard's accessibility tree, or None; the session's active application is always put back afterwards."""
|
|
197
|
+
previous = getattr(driver, "_hinted", None) or "auto"
|
|
198
|
+
try:
|
|
199
|
+
driver.call("POST", "/appium/settings", {"settings": {"defaultActiveApplication": SPRINGBOARD}}, timeout)
|
|
200
|
+
except Exception:
|
|
201
|
+
return None
|
|
202
|
+
try:
|
|
203
|
+
return driver.call("GET", "/source?format=json", None, timeout)
|
|
204
|
+
except Exception:
|
|
205
|
+
return None
|
|
206
|
+
finally:
|
|
207
|
+
try:
|
|
208
|
+
driver.call("POST", "/appium/settings", {"settings": {"defaultActiveApplication": previous}}, timeout)
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def read_springboard(driver, timeout=READ_TIMEOUT):
|
|
214
|
+
"""SpringBoard's (type, text) labels, or None."""
|
|
215
|
+
tree = read_springboard_tree(driver, timeout)
|
|
216
|
+
return None if tree is None else tree_labels(tree)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def read_coredevice_locked(udid):
|
|
220
|
+
"""The phone's lock as CoreDevice reports it (True, False or None): the second reader for a silent /wda/locked."""
|
|
221
|
+
if not udid:
|
|
222
|
+
return None
|
|
223
|
+
reader = harness_api.hooks().coredevice_locked
|
|
224
|
+
try:
|
|
225
|
+
value = reader(udid)
|
|
226
|
+
except Exception:
|
|
227
|
+
return None
|
|
228
|
+
return value if isinstance(value, bool) else None
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def probe(driver, *, springboard=True, timeout=READ_TIMEOUT, udid=None):
|
|
232
|
+
"""Everything the guard can read, for ``mobster devices lock-probe``: no tap, no key, no typing."""
|
|
233
|
+
wda = read_locked(driver, timeout)
|
|
234
|
+
core = read_coredevice_locked(udid) if wda is None else None
|
|
235
|
+
locked = True if core is True else wda
|
|
236
|
+
front = read_front(driver, timeout) if wda is not None else None
|
|
237
|
+
labels = read_springboard(driver, timeout) if springboard and wda is not None else None
|
|
238
|
+
state, named = classify(locked, front, labels)
|
|
239
|
+
return {"locked": locked, "front": front, "state": state, "app": named,
|
|
240
|
+
**({"coreDevice": core} if wda is None and udid else {}),
|
|
241
|
+
"labels": [{"type": kind, "label": text} for kind, text in (labels or ())]}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Causes that read SpringBoard's tree for a sheet. Preflight does not: it presses Home next, and a locked
|
|
245
|
+
# phone must stop in well under 2 s.
|
|
246
|
+
SHEET_CAUSES = ("launch_failed", "lock_suspected", "sheet_suspected", "resume")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def blocked_apps(env=None):
|
|
250
|
+
"""MOBSTER_BLOCKED_APPS: bundle ids Mobster never opens (comma-separated)."""
|
|
251
|
+
raw = (os.environ if env is None else env).get("MOBSTER_BLOCKED_APPS") or ""
|
|
252
|
+
return frozenset(item.strip() for item in raw.split(",") if item.strip())
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class DetectGuard:
|
|
256
|
+
"""agent_hooks.PhoneGuard that only detects; with ``wait_s`` > 0 a lock waits for the person instead of stopping."""
|
|
257
|
+
|
|
258
|
+
def __init__(self, *, device_id=None, usb=False, emit=None, blocked=None, attached_reader=None, wait_s=0.0,
|
|
259
|
+
cancelled=None, sleep=time.sleep, clock=time.monotonic, owner_active=None):
|
|
260
|
+
self.device_id = device_id
|
|
261
|
+
self.owner_active = owner_active
|
|
262
|
+
self.usb = bool(usb and device_id)
|
|
263
|
+
self.emit = emit
|
|
264
|
+
self.blocked = blocked_apps() if blocked is None else frozenset(blocked)
|
|
265
|
+
self._attached_reader = attached_reader
|
|
266
|
+
self.wait_s = max(0.0, float(wait_s or 0))
|
|
267
|
+
self.cancelled = cancelled
|
|
268
|
+
self.sleep, self.clock = sleep, clock
|
|
269
|
+
|
|
270
|
+
def check(self, driver, *, cause):
|
|
271
|
+
locked = read_locked(driver)
|
|
272
|
+
if locked is None:
|
|
273
|
+
attached = self.attached()
|
|
274
|
+
if attached is False:
|
|
275
|
+
return self.await_return(driver, "unplugged", midrun=cause != "preflight") or GuardVerdict(
|
|
276
|
+
"stop", "unplugged", UNPLUGGED)
|
|
277
|
+
if attached is None and self.wifi_lost():
|
|
278
|
+
return self.await_return(driver, "wifi_lost", midrun=cause != "preflight") or GuardVerdict(
|
|
279
|
+
"stop", "unplugged", WIFI_LOST)
|
|
280
|
+
if locked is not False or cause not in SHEET_CAUSES:
|
|
281
|
+
state = classify(locked)[0]
|
|
282
|
+
if state in WAIT_STATES and not self._person_waits(cause) and self._owner_has_phone():
|
|
283
|
+
# An unattended check (a schedule, MCP, a script) on a phone its owner is using: "you have it",
|
|
284
|
+
# never "locked, unlock it". A task a person started waits as before: they asked.
|
|
285
|
+
return GuardVerdict("stop", "owner_active", OWNER_HAS_PHONE)
|
|
286
|
+
if cause == "preflight" and self.wait_s > 0 and state in WAIT_STATES:
|
|
287
|
+
return self._await_unlock(driver, state)
|
|
288
|
+
if cause in MIDRUN_CAUSES and self.wait_s > 0 and state in WAIT_STATES:
|
|
289
|
+
return self._await_unlock(driver, state, midrun=True)
|
|
290
|
+
return verdict_for(state, cause=cause)
|
|
291
|
+
front = read_front(driver)
|
|
292
|
+
labels = read_springboard(driver)
|
|
293
|
+
if labels is None:
|
|
294
|
+
return READY # unlocked, and no sheet could be read: nothing to stop for
|
|
295
|
+
state, named = classify(False, front, labels)
|
|
296
|
+
if state == "face_id_sheet" and self.wait_s > 0:
|
|
297
|
+
return self._await_face_id(driver, app_name(front, named))
|
|
298
|
+
return verdict_for(state, cause=cause, app=app_name(front, named))
|
|
299
|
+
|
|
300
|
+
def _await_face_id(self, driver, app):
|
|
301
|
+
"""Wait for the owner to clear Face ID. Never treat an unreadable sheet as authentication."""
|
|
302
|
+
started = self.clock()
|
|
303
|
+
verdict = verdict_for("face_id_sheet", cause="resume", app=app)
|
|
304
|
+
self._emit({"event": "handoff_waiting", "kind": "face_id", "seconds": int(self.wait_s)})
|
|
305
|
+
while not self._stopped():
|
|
306
|
+
left = started + self.wait_s - self.clock()
|
|
307
|
+
if left <= 0:
|
|
308
|
+
break
|
|
309
|
+
self.sleep(min(UNLOCK_POLL_S, left))
|
|
310
|
+
if self._stopped():
|
|
311
|
+
break
|
|
312
|
+
locked = read_locked(driver)
|
|
313
|
+
if locked is not False:
|
|
314
|
+
attached = self.attached()
|
|
315
|
+
if attached is False or attached is None and self.wifi_lost():
|
|
316
|
+
verdict = GuardVerdict("stop", "unplugged", UNPLUGGED if attached is False else WIFI_LOST)
|
|
317
|
+
else:
|
|
318
|
+
verdict = verdict_for(classify(locked)[0], cause="resume")
|
|
319
|
+
break
|
|
320
|
+
front, labels = read_front(driver), read_springboard(driver)
|
|
321
|
+
state, named = classify(False, front, labels) if front and labels is not None else ("unknown", None)
|
|
322
|
+
if self._stopped():
|
|
323
|
+
break
|
|
324
|
+
verdict = verdict_for(state, cause="resume", app=app_name(front, named))
|
|
325
|
+
if state != "face_id_sheet":
|
|
326
|
+
break
|
|
327
|
+
self._emit({"event": "handoff_finished", "kind": "face_id", "ok": verdict.state == "ready",
|
|
328
|
+
"waited_s": round(max(0.0, self.clock() - started), 1)})
|
|
329
|
+
return verdict
|
|
330
|
+
|
|
331
|
+
def _await_unlock(self, driver, kind, midrun=False):
|
|
332
|
+
"""Look at your iPhone: wait up to ``wait_s`` for the person to unlock the phone, then READY, or a stop.
|
|
333
|
+
|
|
334
|
+
The only requests are ``GET /wda/locked`` once a second and, when a read fails, the USB listing. Nothing is
|
|
335
|
+
pressed, tapped or typed while the phone is locked: the person unlocks it, never Mobster. A read that fails
|
|
336
|
+
while the cable is known to be in keeps waiting (over Wi-Fi a locked phone may drop its link for a moment)."""
|
|
337
|
+
started = self.clock()
|
|
338
|
+
deadline = started + self.wait_s
|
|
339
|
+
where = {"midrun": True} if midrun else {}
|
|
340
|
+
self._emit({"event": "handoff_waiting", "kind": kind, "seconds": int(self.wait_s), **where})
|
|
341
|
+
while True:
|
|
342
|
+
if self._stopped():
|
|
343
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
|
|
344
|
+
# Stop: the run ends as stopped (run.stop is set)
|
|
345
|
+
return verdict_for(kind, cause="resume" if midrun else "preflight")
|
|
346
|
+
left = deadline - self.clock()
|
|
347
|
+
if left <= 0:
|
|
348
|
+
break
|
|
349
|
+
self.sleep(min(UNLOCK_POLL_S, left))
|
|
350
|
+
if self._stopped():
|
|
351
|
+
continue
|
|
352
|
+
locked = read_locked(driver)
|
|
353
|
+
if locked is False:
|
|
354
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": True,
|
|
355
|
+
"waited_s": round(max(0.0, self.clock() - started), 1), **where})
|
|
356
|
+
return READY
|
|
357
|
+
if locked is None:
|
|
358
|
+
attached = self.attached()
|
|
359
|
+
if attached is False or attached is None and self.wifi_lost():
|
|
360
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
|
|
361
|
+
return GuardVerdict("stop", "unplugged", UNPLUGGED if attached is False else WIFI_LOST)
|
|
362
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
|
|
363
|
+
return GuardVerdict("stop", "phone_locked", LOCK_WAIT_MIDRUN_TIMED_OUT if midrun else LOCK_WAIT_TIMED_OUT)
|
|
364
|
+
|
|
365
|
+
def await_return(self, driver, kind="unplugged", midrun=True):
|
|
366
|
+
"""Wait up to ``wait_s`` for an unplugged or Wi-Fi-lost phone to answer; None when the run doesn't wait."""
|
|
367
|
+
if self.wait_s <= 0:
|
|
368
|
+
return None
|
|
369
|
+
started = self.clock()
|
|
370
|
+
deadline = started + self.wait_s
|
|
371
|
+
where = {"midrun": True} if midrun else {}
|
|
372
|
+
self._emit({"event": "handoff_waiting", "kind": kind, "seconds": int(self.wait_s), **where})
|
|
373
|
+
while not self._stopped():
|
|
374
|
+
left = deadline - self.clock()
|
|
375
|
+
if left <= 0:
|
|
376
|
+
break
|
|
377
|
+
self.sleep(min(UNLOCK_POLL_S, left))
|
|
378
|
+
if self._stopped() or self.attached() is False:
|
|
379
|
+
continue
|
|
380
|
+
locked = read_locked(driver)
|
|
381
|
+
if locked is None:
|
|
382
|
+
continue # plugged in or on the link again, its runner not answering yet
|
|
383
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": True,
|
|
384
|
+
"waited_s": round(max(0.0, self.clock() - started), 1), **where})
|
|
385
|
+
return self._await_unlock(driver, "lock_screen", midrun=True) if locked else READY
|
|
386
|
+
self._emit({"event": "handoff_finished", "kind": kind, "ok": False, **where})
|
|
387
|
+
if self._stopped():
|
|
388
|
+
return GuardVerdict("stop", "unplugged", UNPLUGGED if kind == "unplugged" else WIFI_LOST)
|
|
389
|
+
return GuardVerdict("stop", "unplugged", RETURN_WAIT_TIMED_OUT[kind])
|
|
390
|
+
|
|
391
|
+
def _person_waits(self, cause):
|
|
392
|
+
"""Whether a check for ``cause`` waits for a person to unlock instead of stopping (a task they started)."""
|
|
393
|
+
if self.wait_s <= 0:
|
|
394
|
+
return False
|
|
395
|
+
return cause in ("preflight", *MIDRUN_CAUSES)
|
|
396
|
+
|
|
397
|
+
def _owner_has_phone(self):
|
|
398
|
+
"""Whether the phone's owner is using it now. A reader that fails says no: the lock rules stand."""
|
|
399
|
+
try:
|
|
400
|
+
return bool(self.owner_active()) if callable(self.owner_active) else False
|
|
401
|
+
except Exception:
|
|
402
|
+
return False
|
|
403
|
+
|
|
404
|
+
def _stopped(self):
|
|
405
|
+
try:
|
|
406
|
+
return bool(self.cancelled()) if callable(self.cancelled) else False
|
|
407
|
+
except Exception:
|
|
408
|
+
return True # can't tell whether Stop was pressed: stop, never wait on
|
|
409
|
+
|
|
410
|
+
def _emit(self, event):
|
|
411
|
+
if callable(self.emit):
|
|
412
|
+
self.emit(event)
|
|
413
|
+
|
|
414
|
+
def attached(self):
|
|
415
|
+
"""Whether the phone is connected; None, not False, when it may be on Wi-Fi, so it isn't stopped unplugged."""
|
|
416
|
+
if not self.usb:
|
|
417
|
+
return None
|
|
418
|
+
wifi = _wifi_attached(self.device_id)
|
|
419
|
+
if wifi is not _NOT_WIFI:
|
|
420
|
+
return wifi
|
|
421
|
+
reader = self._attached_reader or harness_api.hooks().usb_attached
|
|
422
|
+
try:
|
|
423
|
+
usb = reader(self.device_id)
|
|
424
|
+
except Exception:
|
|
425
|
+
return None
|
|
426
|
+
if usb is False and _wifi_may_reach(self.device_id):
|
|
427
|
+
return None
|
|
428
|
+
return usb
|
|
429
|
+
|
|
430
|
+
def wifi_lost(self):
|
|
431
|
+
"""Whether the phone was running over Wi-Fi and its link went down (not a switch Mobster is making)."""
|
|
432
|
+
if not self.usb:
|
|
433
|
+
return False
|
|
434
|
+
lost = harness_api.hooks().wifi_lost
|
|
435
|
+
try:
|
|
436
|
+
return bool(lost(self.device_id))
|
|
437
|
+
except Exception:
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
def allows_app(self, bundle_id):
|
|
441
|
+
return bundle_id not in self.blocked
|
|
442
|
+
|
|
443
|
+
def finish(self, driver):
|
|
444
|
+
"""Nothing to relock: this guard never unlocks."""
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
_NOT_WIFI = object()
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _wifi_attached(udid):
|
|
451
|
+
attached = harness_api.hooks().wifi_attached
|
|
452
|
+
try:
|
|
453
|
+
wifi, value = attached(udid)
|
|
454
|
+
except Exception:
|
|
455
|
+
return _NOT_WIFI
|
|
456
|
+
return value if wifi else _NOT_WIFI
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _wifi_may_reach(udid):
|
|
460
|
+
"""Whether an unplugged phone may run over the Mac app's Wi-Fi relay, in a process that doesn't run its link."""
|
|
461
|
+
may_reach = harness_api.hooks().wifi_may_reach
|
|
462
|
+
try:
|
|
463
|
+
return bool(may_reach(udid))
|
|
464
|
+
except Exception:
|
|
465
|
+
return False
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def guard_for(*, device_id, wda_url, mode, approve=None, emit=None, video=None, usb=True, wait_s=0.0, cancelled=None):
|
|
469
|
+
"""The phone guard for one run (agent_hooks.PhoneGuard). ``mode``: "interactive" | "schedule" | "scripts".
|
|
470
|
+
``usb``: whether ``device_id`` is a USB iPhone's UDID (a simulator or a bare WDA address is never
|
|
471
|
+
"unplugged"). ``approve`` and ``video`` are for unlocking, which this build does not do. ``wait_s``: how long a
|
|
472
|
+
start on a locked phone, or a run the phone locks under, waits for the person to unlock it (0, the default, stops
|
|
473
|
+
at once: ``mobster run``, schedules, MCP); ``cancelled``: whether Stop was pressed."""
|
|
474
|
+
if mode not in ("interactive", "schedule", "scripts"):
|
|
475
|
+
raise ValueError(f"unknown guard mode {mode!r}")
|
|
476
|
+
from . import owner_presence
|
|
477
|
+
return DetectGuard(device_id=device_id, usb=usb, emit=emit, wait_s=wait_s if mode == "interactive" else 0.0,
|
|
478
|
+
cancelled=cancelled,
|
|
479
|
+
owner_active=lambda: owner_presence.is_owner_active(wda_url))
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def after_error(guard, driver):
|
|
483
|
+
"""Why a task's step failed when the phone itself says so (cable, Wi-Fi, lock), else None; it only reads."""
|
|
484
|
+
if guard is None:
|
|
485
|
+
return None
|
|
486
|
+
from .agent_hooks import safe_attached
|
|
487
|
+
attached = safe_attached(guard)
|
|
488
|
+
if attached is False:
|
|
489
|
+
return GuardVerdict("stop", "unplugged", UNPLUGGED)
|
|
490
|
+
if attached is None:
|
|
491
|
+
try:
|
|
492
|
+
lost = bool(guard.wifi_lost())
|
|
493
|
+
except Exception:
|
|
494
|
+
lost = False
|
|
495
|
+
if lost:
|
|
496
|
+
return GuardVerdict("stop", "unplugged", WIFI_LOST)
|
|
497
|
+
if driver is not None and read_locked(driver) is True:
|
|
498
|
+
return GuardVerdict("stop", "phone_locked", LOCKED_DURING)
|
|
499
|
+
return None
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def unlock_settings(record=None):
|
|
503
|
+
"""The phone's unlock settings, as booleans: off, since this build cannot unlock."""
|
|
504
|
+
return {"enabled": False, "askBeforeUnlocking": True, "onSchedule": "never", "scripts": False,
|
|
505
|
+
"relockAfter": True, "needsCheck": False, "supported": CAN_UNLOCK}
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def describe(probe_result):
|
|
509
|
+
"""Plain lines for ``mobster devices lock-probe``."""
|
|
510
|
+
words = {"unlocked": "unlocked, no system sheet", "lock_screen": "locked (lock screen)",
|
|
511
|
+
"passcode_keypad": "locked (passcode keypad showing)", "face_id_sheet": "asking for Face ID",
|
|
512
|
+
"app_locked_sheet": "a locked app's sheet", "apple_confirmation": "asking to confirm with the side button",
|
|
513
|
+
"unknown": "not readable (WebDriverAgent didn't answer)"}
|
|
514
|
+
lines = [f"State: {words.get(probe_result['state'], probe_result['state'])}"]
|
|
515
|
+
if "coreDevice" in probe_result:
|
|
516
|
+
lines.append("CoreDevice lock state: " + {True: "locked", False: "unlocked", None: "not readable"}[
|
|
517
|
+
probe_result["coreDevice"]])
|
|
518
|
+
if probe_result.get("front"):
|
|
519
|
+
lines.append(f"Front app: {probe_result['front']}")
|
|
520
|
+
labels = probe_result.get("labels") or []
|
|
521
|
+
if labels:
|
|
522
|
+
lines.append(f"SpringBoard shows {len(labels)} labels:")
|
|
523
|
+
lines.extend(f" {item['type'] or '?'}: {item['label']}" for item in labels)
|
|
524
|
+
lines.append("Nothing was tapped or typed.")
|
|
525
|
+
return lines
|
|
526
|
+
|