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,352 @@
|
|
|
1
|
+
"""Deep links: open a screen in one call instead of tapping through menus, with a small map of links that work.
|
|
2
|
+
|
|
3
|
+
``open_link(driver, url, bundle=None)`` opens a URL the way WebDriverAgent's POST /url does (XCTest's
|
|
4
|
+
openDefaultApplicationForURL, iOS 16.4+; with ``bundleId``, that app's openURL:usingApplication:, which keeps a
|
|
5
|
+
universal link in its app instead of Safari) and answers iOS's "Open in “App”?" when it shows (system_dialogs:
|
|
6
|
+
Mobster opened the link itself). ``xcrun devicectl device process launch --payload-url`` is the other way in (iOS 17+)
|
|
7
|
+
and isn't needed while WebDriverAgent runs.
|
|
8
|
+
|
|
9
|
+
``LINKS`` is the map the agent and MCP's ``open_url`` (with no url) read: each link's app, what it opens, its URL
|
|
10
|
+
template and how safe it is. Rules:
|
|
11
|
+
- ``opens``: shows a screen (search results, a profile, a Settings pane). Nothing is sent, bought or changed.
|
|
12
|
+
- ``prefills``: opens a composer with text in it (Mail, Messages, WhatsApp). Sending is still a tap the user approves.
|
|
13
|
+
- ``acts``: does something as soon as it opens or after one system confirmation (a call, a FaceTime call, running a
|
|
14
|
+
shortcut).
|
|
15
|
+
- Whether a link asks first is ``decide``'s one rule (below): every path that opens a link uses it.
|
|
16
|
+
- Never opened: ``javascript:``, ``data:``, ``file:``, ``itms-services:`` (installs an app) and anything without a
|
|
17
|
+
scheme.
|
|
18
|
+
|
|
19
|
+
Settings links (``App-prefs:``) are undocumented: Apple's DTS calls them unsupported (developer.apple.com/forums/
|
|
20
|
+
thread/691712) and their sub-pages broke in iOS 18 (thread/759900). They are kept as ``best_effort`` and checked on the
|
|
21
|
+
phone (``verified``); when one doesn't land, the agent navigates the usual way.
|
|
22
|
+
|
|
23
|
+
Every link with ``verified`` set was opened on a real iPhone (the date, the model and the iOS version).
|
|
24
|
+
|
|
25
|
+
A web address can open an app on the wrong screen. ``APP_FORMS`` lists the ones recorded doing that and the app link
|
|
26
|
+
that opens the screen they name: ``open_link`` opens the app link instead, and ``app_for`` names the app it opens.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import re
|
|
30
|
+
import time
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from urllib.parse import quote, urlsplit
|
|
33
|
+
|
|
34
|
+
BLOCKED_SCHEMES = frozenset({"javascript", "data", "file", "itms-services", "about", "blob", "vbscript"})
|
|
35
|
+
# Links that act as they open, or after one system confirmation.
|
|
36
|
+
ACTING_SCHEMES = frozenset({"tel", "telprompt", "facetime", "facetime-audio", "facetime-group"})
|
|
37
|
+
ACTING_PATHS = (re.compile(r"^shortcuts://(?:x-callback-url/)?run-shortcut", re.I),
|
|
38
|
+
re.compile(r"^workflow://(?:x-callback-url/)?run-workflow", re.I),
|
|
39
|
+
re.compile(r"^shortcuts://(?:x-callback-url/)?import-shortcut", re.I))
|
|
40
|
+
SCHEME = re.compile(r"^([A-Za-z][A-Za-z0-9+.-]{0,40}):")
|
|
41
|
+
MAX_URL = 2000
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class Link:
|
|
46
|
+
id: str # "maps.search"
|
|
47
|
+
app: str # the app's name as people say it
|
|
48
|
+
bundle: str # the app that opens it ("" when iOS picks: the default browser)
|
|
49
|
+
does: str # what it opens, as a short phrase ("search Maps")
|
|
50
|
+
template: str # the URL with {placeholders}
|
|
51
|
+
risk: str = "opens" # opens | prefills | acts
|
|
52
|
+
best_effort: bool = False
|
|
53
|
+
verified: str = "" # "2026-10-09 iPhone 15 Pro, iOS 26.x" when opened on a real phone
|
|
54
|
+
source: str = ""
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def params(self):
|
|
58
|
+
return tuple(dict.fromkeys(re.findall(r"{(\w+)}", self.template)))
|
|
59
|
+
|
|
60
|
+
def fill(self, **values):
|
|
61
|
+
"""The URL with each placeholder filled, percent-encoded. KeyError when one is missing."""
|
|
62
|
+
missing = [name for name in self.params if not str(values.get(name) or "").strip()]
|
|
63
|
+
if missing:
|
|
64
|
+
raise KeyError(", ".join(missing))
|
|
65
|
+
return self.template.format(**{name: quote(str(values[name]).strip(), safe="@+") for name in self.params})
|
|
66
|
+
|
|
67
|
+
def public(self):
|
|
68
|
+
return {"id": self.id, "app": self.app, "bundleId": self.bundle or None, "opens": self.does,
|
|
69
|
+
"url": self.template, "risk": self.risk, "bestEffort": self.best_effort or None,
|
|
70
|
+
"verified": self.verified or None}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
APPLE_SCHEMES = "https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/"
|
|
74
|
+
INSTAGRAM_CHECKED = "2026-10-14 iPhone 15 Pro, iOS 26.0.1"
|
|
75
|
+
LINKS = (
|
|
76
|
+
# Settings (undocumented, best effort; see the module's docstring)
|
|
77
|
+
Link("settings.app", "Settings", "com.apple.Preferences", "an app's own page in Settings",
|
|
78
|
+
"App-prefs:{bundle_id}", best_effort=True, source="https://developer.apple.com/forums/thread/759900"),
|
|
79
|
+
Link("settings.root", "Settings", "com.apple.Preferences", "Settings", "App-prefs:", best_effort=True,
|
|
80
|
+
source="https://developer.apple.com/forums/thread/691712"),
|
|
81
|
+
Link("settings.wifi", "Settings", "com.apple.Preferences", "Settings › Wi-Fi", "App-prefs:WIFI",
|
|
82
|
+
best_effort=True),
|
|
83
|
+
Link("settings.bluetooth", "Settings", "com.apple.Preferences", "Settings › Bluetooth", "App-prefs:Bluetooth",
|
|
84
|
+
best_effort=True),
|
|
85
|
+
Link("settings.general", "Settings", "com.apple.Preferences", "Settings › General", "App-prefs:General",
|
|
86
|
+
best_effort=True),
|
|
87
|
+
Link("settings.notifications", "Settings", "com.apple.Preferences", "Settings › Notifications",
|
|
88
|
+
"App-prefs:NOTIFICATIONS_ID", best_effort=True),
|
|
89
|
+
Link("settings.battery", "Settings", "com.apple.Preferences", "Settings › Battery",
|
|
90
|
+
"App-prefs:BATTERY_USAGE", best_effort=True),
|
|
91
|
+
Link("settings.privacy", "Settings", "com.apple.Preferences", "Settings › Privacy & Security",
|
|
92
|
+
"App-prefs:Privacy", best_effort=True),
|
|
93
|
+
# Maps and the web
|
|
94
|
+
Link("maps.search", "Maps", "com.apple.Maps", "search Maps", "maps://?q={query}", source=APPLE_SCHEMES),
|
|
95
|
+
Link("maps.directions", "Maps", "com.apple.Maps", "directions to a place (not started)",
|
|
96
|
+
"maps://?daddr={destination}", source=APPLE_SCHEMES),
|
|
97
|
+
Link("google_maps.search", "Google Maps", "com.google.Maps", "search Google Maps", "comgooglemaps://?q={query}",
|
|
98
|
+
source="https://developers.google.com/maps/documentation/urls/ios-urlscheme"),
|
|
99
|
+
Link("web.search", "Safari", "", "a web search", "https://www.google.com/search?q={query}"),
|
|
100
|
+
# Stores and media
|
|
101
|
+
Link("app_store.search", "App Store", "com.apple.AppStore", "search the App Store",
|
|
102
|
+
"itms-apps://search.itunes.apple.com/WebObjects/MZSearch.woa/wa/search?media=software&term={query}",
|
|
103
|
+
best_effort=True),
|
|
104
|
+
Link("app_store.app", "App Store", "com.apple.AppStore", "an app's App Store page",
|
|
105
|
+
"itms-apps://apps.apple.com/app/id{app_id}"),
|
|
106
|
+
Link("youtube.search", "YouTube", "com.google.ios.youtube", "search YouTube",
|
|
107
|
+
"youtube://results?search_query={query}", best_effort=True),
|
|
108
|
+
Link("spotify.search", "Spotify", "com.spotify.client", "search Spotify", "spotify:search:{query}",
|
|
109
|
+
best_effort=True),
|
|
110
|
+
# Profiles
|
|
111
|
+
Link("instagram.profile", "Instagram", "com.burbn.instagram", "an Instagram profile",
|
|
112
|
+
"instagram://user?username={username}", verified=INSTAGRAM_CHECKED,
|
|
113
|
+
source="https://help.short.io/en/articles/9114578-how-to-create-instagram-deep-link"),
|
|
114
|
+
Link("instagram.inbox", "Instagram", "com.burbn.instagram", "the Instagram DM inbox", "instagram://direct-inbox",
|
|
115
|
+
verified=INSTAGRAM_CHECKED),
|
|
116
|
+
Link("instagram.reels", "Instagram", "com.burbn.instagram", "the Instagram Reels viewer",
|
|
117
|
+
"instagram://reels_home", verified=INSTAGRAM_CHECKED),
|
|
118
|
+
Link("x.profile", "X", "com.atebits.Tweetie2", "an X profile", "twitter://user?screen_name={username}",
|
|
119
|
+
best_effort=True),
|
|
120
|
+
Link("tiktok.profile", "TikTok", "com.zhiliaoapp.musically", "a TikTok profile",
|
|
121
|
+
"https://www.tiktok.com/@{username}", best_effort=True),
|
|
122
|
+
Link("telegram.profile", "Telegram", "ph.telegra.Telegraph", "a Telegram chat or channel",
|
|
123
|
+
"tg://resolve?domain={username}", source="https://core.telegram.org/api/links"),
|
|
124
|
+
Link("reddit.subreddit", "Reddit", "com.reddit.Reddit", "a subreddit", "https://www.reddit.com/r/{name}",
|
|
125
|
+
best_effort=True),
|
|
126
|
+
# Composers (nothing is sent until a Send the user approves)
|
|
127
|
+
Link("mail.compose", "Mail", "com.apple.mobilemail", "a new email, filled in",
|
|
128
|
+
"mailto:{to}?subject={subject}&body={body}", "prefills", source=APPLE_SCHEMES),
|
|
129
|
+
Link("messages.compose", "Messages", "com.apple.MobileSMS", "a new message, filled in", "sms:{to}&body={body}",
|
|
130
|
+
"prefills", source=APPLE_SCHEMES),
|
|
131
|
+
Link("whatsapp.compose", "WhatsApp", "net.whatsapp.WhatsApp", "a WhatsApp message to pick a chat for",
|
|
132
|
+
"whatsapp://send?text={text}", "prefills", best_effort=True),
|
|
133
|
+
# Apps' home screens
|
|
134
|
+
Link("photos.open", "Photos", "com.apple.mobileslideshow", "Photos", "photos-redirect://", best_effort=True),
|
|
135
|
+
Link("calendar.open", "Calendar", "com.apple.mobilecal", "Calendar", "calshow:", best_effort=True),
|
|
136
|
+
Link("notes.open", "Notes", "com.apple.mobilenotes", "Notes", "mobilenotes://", best_effort=True),
|
|
137
|
+
Link("shortcuts.open", "Shortcuts", "com.apple.shortcuts", "a shortcut, open to edit (not run)",
|
|
138
|
+
"shortcuts://open-shortcut?name={name}", source="https://support.apple.com/guide/shortcuts/apd624386f42/ios"),
|
|
139
|
+
# Acting links: asked first
|
|
140
|
+
Link("phone.call", "Phone", "com.apple.mobilephone", "a call (iOS confirms first)", "tel:{number}", "acts",
|
|
141
|
+
source="https://developer.apple.com/forums/thread/73406"),
|
|
142
|
+
Link("facetime.call", "FaceTime", "com.apple.facetime", "a FaceTime call", "facetime:{handle}", "acts",
|
|
143
|
+
source=APPLE_SCHEMES),
|
|
144
|
+
Link("shortcuts.run", "Shortcuts", "com.apple.shortcuts", "runs a shortcut at once",
|
|
145
|
+
"shortcuts://run-shortcut?name={name}", "acts",
|
|
146
|
+
source="https://support.apple.com/guide/shortcuts/apd624386f42/ios"),
|
|
147
|
+
)
|
|
148
|
+
BY_ID = {link.id: link for link in LINKS}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class LinkError(ValueError):
|
|
152
|
+
"""Why a URL won't be opened, in one plain sentence."""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def risk_of(url):
|
|
156
|
+
"""opens | prefills | acts for ``url``; LinkError for one Mobster never opens."""
|
|
157
|
+
text = str(url or "").strip()
|
|
158
|
+
if not text or len(text) > MAX_URL or any(ord(c) < 32 for c in text):
|
|
159
|
+
raise LinkError("That isn't a link Mobster can open.")
|
|
160
|
+
found = SCHEME.match(text)
|
|
161
|
+
if not found:
|
|
162
|
+
raise LinkError("A link needs a scheme, such as https://example.com or maps://?q=coffee.")
|
|
163
|
+
scheme = found.group(1).casefold()
|
|
164
|
+
if scheme in BLOCKED_SCHEMES:
|
|
165
|
+
raise LinkError(f"Mobster never opens {scheme}: links.")
|
|
166
|
+
if scheme in ACTING_SCHEMES or any(pattern.match(text) for pattern in ACTING_PATHS):
|
|
167
|
+
return "acts"
|
|
168
|
+
if scheme in ("mailto", "sms", "whatsapp") or text.casefold().startswith("tg://msg"):
|
|
169
|
+
return "prefills"
|
|
170
|
+
return "opens"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# Web addresses that open Instagram's home feed, not the screen they name (opened on an iPhone 15 Pro, iOS 26.0.1,
|
|
174
|
+
# 2026-10-14), and the app link that opens that screen. A post's address (instagram.com/p/<code>/) is not here: it
|
|
175
|
+
# opens the post.
|
|
176
|
+
_INSTAGRAM = r"^https?://(?:www\.)?instagram\.com"
|
|
177
|
+
APP_FORMS = (
|
|
178
|
+
(re.compile(_INSTAGRAM + r"/direct/inbox/?(?:[?#].*)?$", re.I), "instagram://direct-inbox"),
|
|
179
|
+
(re.compile(_INSTAGRAM + r"/reels/?(?:[?#].*)?$", re.I), "instagram://reels_home"),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def app_form(url):
|
|
184
|
+
text = str(url or "").strip()
|
|
185
|
+
for pattern, app_url in APP_FORMS:
|
|
186
|
+
if pattern.match(text):
|
|
187
|
+
return app_url
|
|
188
|
+
return url
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def app_for(url):
|
|
192
|
+
"""The map's bundle id for ``url`` when it matches a link's scheme and host ("" when unknown)."""
|
|
193
|
+
url = app_form(url)
|
|
194
|
+
text = str(url or "")
|
|
195
|
+
scheme = (SCHEME.match(text).group(1).casefold() if SCHEME.match(text) else "")
|
|
196
|
+
if scheme in ("http", "https"):
|
|
197
|
+
host = (urlsplit(text).hostname or "").casefold()
|
|
198
|
+
for link in LINKS:
|
|
199
|
+
if link.bundle and link.template.startswith("https://") and host and host == (
|
|
200
|
+
urlsplit(link.template.split("{")[0] + "x").hostname or "").casefold():
|
|
201
|
+
return link.bundle
|
|
202
|
+
return ""
|
|
203
|
+
for link in LINKS:
|
|
204
|
+
if link.bundle and link.template.casefold().startswith(scheme + ":"):
|
|
205
|
+
return link.bundle
|
|
206
|
+
return ""
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Whether a link may open without asking (``decide``), one rule for every path that opens one: the agent's OPEN_URL
|
|
210
|
+
# (skills/open_url.py), a coding agent's open_url (ai_plans/phone_tools.py), MCP's open_url and `mobster phone open`.
|
|
211
|
+
# - Opens: a web page (http or https with a host and no sign-in in the address), Maps, the App Store, and the apps
|
|
212
|
+
# this map knows (Settings, Photos, Instagram...).
|
|
213
|
+
# - Asks every time: a link that acts or writes to someone (tel:, FaceTime, sms:, mailto:, running a shortcut). Never
|
|
214
|
+
# remembered, whatever was remembered for its app.
|
|
215
|
+
# - Asks once per app: any other app's link (things://, venmo://). "Don't ask again for Things" covers its later links.
|
|
216
|
+
# With nobody to answer, a link that asks isn't opened.
|
|
217
|
+
ALWAYS_ASK_SCHEMES = ACTING_SCHEMES | {"sms", "mailto", "facetime-audio-prompt"}
|
|
218
|
+
OPEN_SCHEMES = frozenset({"maps", "itms-apps", "itms-appss"})
|
|
219
|
+
LINK_LABEL, LINK_OP = "Links", "OPEN_URL"
|
|
220
|
+
APP_NAMES = {"maps": "Maps", "itms-apps": "App Store", "itms-appss": "App Store"}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass(frozen=True)
|
|
224
|
+
class LinkDecision:
|
|
225
|
+
url: str
|
|
226
|
+
action: str # "open" | "ask"
|
|
227
|
+
always: bool = False # asks every time: "Don't ask again" isn't offered
|
|
228
|
+
# the app the link goes to: its bundle id, or "url:<scheme>" for an app the map doesn't know
|
|
229
|
+
app: str = ""
|
|
230
|
+
app_name: str = ""
|
|
231
|
+
title: str | None = None # the approval card's question
|
|
232
|
+
|
|
233
|
+
def remember(self):
|
|
234
|
+
"""The approval's "Don't ask again" (run.Run.request_approval's ``remember``), or None."""
|
|
235
|
+
if self.action != "ask" or self.always or not self.app:
|
|
236
|
+
return None
|
|
237
|
+
return {"app": self.app, "label": LINK_LABEL, "op": LINK_OP, "app_name": self.app_name,
|
|
238
|
+
"words": f"Don’t ask again for {self.app_name}"}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _shown(url, limit=80):
|
|
242
|
+
return url if len(url) <= limit else url[:limit - 1] + "…"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def link_title(url):
|
|
246
|
+
"""The card's question for a link that asks every time ("Call +1 555 0100?")."""
|
|
247
|
+
scheme, _, rest = url.partition(":")
|
|
248
|
+
scheme = scheme.casefold()
|
|
249
|
+
who = rest.lstrip("/").split("?", 1)[0].split("&", 1)[0][:40]
|
|
250
|
+
if scheme in ("tel", "telprompt"):
|
|
251
|
+
return f"Call {who}?" if who else "Place this call?"
|
|
252
|
+
if scheme.startswith("facetime"):
|
|
253
|
+
return f"Start a FaceTime call to {who}?" if who else "Start this FaceTime call?"
|
|
254
|
+
if scheme == "sms":
|
|
255
|
+
return f"Start a message to {who}?" if who else "Start a message?"
|
|
256
|
+
if scheme == "mailto":
|
|
257
|
+
return f"Start an email to {who}?" if who else "Start an email?"
|
|
258
|
+
if any(pattern.match(url) for pattern in ACTING_PATHS):
|
|
259
|
+
return f"Run the shortcut in {_shown(url)}?"
|
|
260
|
+
return f"Open {_shown(url)}?"
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def decide(url, remembered=None):
|
|
264
|
+
"""``LinkDecision`` for ``url``; LinkError for one Mobster never opens. ``remembered(app, label, op)``: whether the
|
|
265
|
+
person said "Don't ask again" for that app's links (permissions.exceptions.Exceptions.contains)."""
|
|
266
|
+
risk_of(url)
|
|
267
|
+
text = str(url).strip()
|
|
268
|
+
scheme = SCHEME.match(text).group(1).casefold()
|
|
269
|
+
if scheme in ("http", "https"):
|
|
270
|
+
parts = urlsplit(text)
|
|
271
|
+
if parts.hostname and "@" not in parts.netloc:
|
|
272
|
+
return LinkDecision(text, "open", app=app_for(text))
|
|
273
|
+
return LinkDecision(text, "ask", always=True, title=f"Open {_shown(text)}?")
|
|
274
|
+
if scheme in ALWAYS_ASK_SCHEMES or any(pattern.match(text) for pattern in ACTING_PATHS):
|
|
275
|
+
return LinkDecision(text, "ask", always=True, app=app_for(text), title=link_title(text))
|
|
276
|
+
if scheme in OPEN_SCHEMES:
|
|
277
|
+
return LinkDecision(text, "open", app=app_for(text), app_name=APP_NAMES[scheme])
|
|
278
|
+
known = app_for(text)
|
|
279
|
+
if known:
|
|
280
|
+
return LinkDecision(text, "open", app=known,
|
|
281
|
+
app_name=next((link.app for link in LINKS if link.bundle == known), ""))
|
|
282
|
+
app = f"url:{scheme}"
|
|
283
|
+
name = scheme.replace("-", " ").replace("_", " ").strip().title() or scheme
|
|
284
|
+
if callable(remembered):
|
|
285
|
+
try:
|
|
286
|
+
if remembered(app, LINK_LABEL, LINK_OP):
|
|
287
|
+
return LinkDecision(text, "open", app=app, app_name=name)
|
|
288
|
+
except Exception:
|
|
289
|
+
pass # an unreadable list remembers nothing: the link asks
|
|
290
|
+
return LinkDecision(text, "ask", app=app, app_name=name, title=f"Open this link in {name}?")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def describe(link_ids=None, apps=None, limit=None):
|
|
294
|
+
"""Compact lines for a prompt: "maps.search: maps://?q={query} (search Maps)"."""
|
|
295
|
+
lines = []
|
|
296
|
+
for link in LINKS:
|
|
297
|
+
if link_ids is not None and link.id not in link_ids:
|
|
298
|
+
continue
|
|
299
|
+
if apps is not None and link.bundle and link.bundle not in apps:
|
|
300
|
+
continue
|
|
301
|
+
mark = "" if link.risk == "opens" else f", {link.risk}"
|
|
302
|
+
lines.append(f"{link.template} ({link.does}{mark})")
|
|
303
|
+
if limit and len(lines) >= limit:
|
|
304
|
+
break
|
|
305
|
+
return lines
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def search(app=None, query=None):
|
|
309
|
+
"""Links for an app (its name or bundle id) and/or matching words."""
|
|
310
|
+
out = []
|
|
311
|
+
want_app = str(app or "").strip().casefold()
|
|
312
|
+
words = [w for w in re.split(r"\W+", str(query or "").casefold()) if w]
|
|
313
|
+
for link in LINKS:
|
|
314
|
+
if want_app and want_app not in (link.app.casefold(), link.bundle.casefold()):
|
|
315
|
+
continue
|
|
316
|
+
hay = f"{link.id} {link.app} {link.does}".casefold()
|
|
317
|
+
if words and not all(word in hay for word in words):
|
|
318
|
+
continue
|
|
319
|
+
out.append(link)
|
|
320
|
+
return out
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def open_link(driver, url, *, bundle=None, timeout=20, settle=0.6, clock=time.monotonic, sleep=time.sleep,
|
|
324
|
+
answer_prompt=True):
|
|
325
|
+
"""Open ``url`` on the device. Returns {"url", "bundleId", "prompt"}: ``prompt`` is the "Open in" answer
|
|
326
|
+
Mobster gave ("Open"), or None. A web address in ``APP_FORMS`` opens as its app link, and ``requested`` is the
|
|
327
|
+
address that was asked for. LinkError for a link Mobster never opens; the runner's refusal propagates."""
|
|
328
|
+
from .wda import system_dialogs
|
|
329
|
+
risk_of(url)
|
|
330
|
+
requested = url
|
|
331
|
+
url = app_form(url)
|
|
332
|
+
if url != requested:
|
|
333
|
+
bundle = app_for(url) or bundle # the app link opens in its own app, whichever app the caller named
|
|
334
|
+
body = {"url": url}
|
|
335
|
+
if bundle:
|
|
336
|
+
body["bundleId"] = bundle
|
|
337
|
+
system_dialogs.note_open(driver, url, clock=clock)
|
|
338
|
+
driver.call("POST", "/url", body, timeout=timeout)
|
|
339
|
+
pressed = None
|
|
340
|
+
if answer_prompt:
|
|
341
|
+
sleep(settle)
|
|
342
|
+
dialog = system_dialogs.read(driver, timeout=2)
|
|
343
|
+
if dialog is not None and dialog.kind == "open_in_app":
|
|
344
|
+
decision = system_dialogs.decide(dialog, system_dialogs.ASK_IMPORTANT, can_ask=False,
|
|
345
|
+
recent_=system_dialogs.recent(driver), clock=clock)
|
|
346
|
+
if decision is not None and decision.action == "press":
|
|
347
|
+
system_dialogs.press(driver, decision)
|
|
348
|
+
pressed = decision.button
|
|
349
|
+
opened = {"url": url, "bundleId": bundle or None, "prompt": pressed}
|
|
350
|
+
if url != requested:
|
|
351
|
+
opened["requested"] = requested
|
|
352
|
+
return opened
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Small, cached device identity (USB lockdown). No serial, UDID or account identifiers."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
# Marketing name mapping verified against the libimobiledevice/libirecovery
|
|
8
|
+
# device table. Unknown models retain their exact identifier, never a guess.
|
|
9
|
+
# https://github.com/libimobiledevice/libirecovery/blob/master/src/libirecovery.c
|
|
10
|
+
MODEL_NAMES = {
|
|
11
|
+
"iPhone10,1": "iPhone 8", "iPhone10,4": "iPhone 8", "iPhone10,2": "iPhone 8 Plus", "iPhone10,5": "iPhone 8 Plus",
|
|
12
|
+
"iPhone10,3": "iPhone X", "iPhone10,6": "iPhone X", "iPhone11,2": "iPhone XS", "iPhone11,4": "iPhone XS Max",
|
|
13
|
+
"iPhone11,6": "iPhone XS Max", "iPhone11,8": "iPhone XR", "iPhone12,1": "iPhone 11", "iPhone12,3": "iPhone 11 Pro",
|
|
14
|
+
"iPhone12,5": "iPhone 11 Pro Max", "iPhone12,8": "iPhone SE (2nd gen)", "iPhone13,1": "iPhone 12 mini",
|
|
15
|
+
"iPhone13,2": "iPhone 12", "iPhone13,3": "iPhone 12 Pro", "iPhone13,4": "iPhone 12 Pro Max",
|
|
16
|
+
"iPhone14,2": "iPhone 13 Pro", "iPhone14,3": "iPhone 13 Pro Max", "iPhone14,4": "iPhone 13 mini",
|
|
17
|
+
"iPhone14,5": "iPhone 13", "iPhone14,6": "iPhone SE (3rd gen)", "iPhone14,7": "iPhone 14",
|
|
18
|
+
"iPhone14,8": "iPhone 14 Plus", "iPhone15,2": "iPhone 14 Pro", "iPhone15,3": "iPhone 14 Pro Max",
|
|
19
|
+
"iPhone15,4": "iPhone 15", "iPhone15,5": "iPhone 15 Plus", "iPhone16,1": "iPhone 15 Pro",
|
|
20
|
+
"iPhone16,2": "iPhone 15 Pro Max", "iPhone17,1": "iPhone 16 Pro", "iPhone17,2": "iPhone 16 Pro Max",
|
|
21
|
+
"iPhone17,3": "iPhone 16", "iPhone17,4": "iPhone 16 Plus", "iPhone17,5": "iPhone 16e",
|
|
22
|
+
"iPhone18,1": "iPhone 17 Pro", "iPhone18,2": "iPhone 17 Pro Max", "iPhone18,3": "iPhone 17",
|
|
23
|
+
"iPhone18,4": "iPhone Air", "iPhone18,5": "iPhone 17e", "iPhone19,2": "iPhone 18 Pro",
|
|
24
|
+
# Regional variants share one marketing name.
|
|
25
|
+
"iPhone19,3": "iPhone 18 Pro Max", "iPhone19,7": "iPhone 18 Pro Max",
|
|
26
|
+
# iPads from the same table, from the iPad Pro (2nd gen, 2017) on. Wi-Fi and cellular models share one name.
|
|
27
|
+
"iPad7,1": "iPad Pro 12.9-inch (2nd gen)", "iPad7,2": "iPad Pro 12.9-inch (2nd gen)",
|
|
28
|
+
"iPad7,3": "iPad Pro 10.5-inch", "iPad7,4": "iPad Pro 10.5-inch", "iPad7,5": "iPad (6th gen)",
|
|
29
|
+
"iPad7,6": "iPad (6th gen)", "iPad7,11": "iPad (7th gen)", "iPad7,12": "iPad (7th gen)",
|
|
30
|
+
"iPad8,1": "iPad Pro 11-inch (1st gen)", "iPad8,2": "iPad Pro 11-inch (1st gen)",
|
|
31
|
+
"iPad8,3": "iPad Pro 11-inch (1st gen)", "iPad8,4": "iPad Pro 11-inch (1st gen)",
|
|
32
|
+
"iPad8,5": "iPad Pro 12.9-inch (3rd gen)", "iPad8,6": "iPad Pro 12.9-inch (3rd gen)",
|
|
33
|
+
"iPad8,7": "iPad Pro 12.9-inch (3rd gen)", "iPad8,8": "iPad Pro 12.9-inch (3rd gen)",
|
|
34
|
+
"iPad8,9": "iPad Pro 11-inch (2nd gen)", "iPad8,10": "iPad Pro 11-inch (2nd gen)",
|
|
35
|
+
"iPad8,11": "iPad Pro 12.9-inch (4th gen)", "iPad8,12": "iPad Pro 12.9-inch (4th gen)",
|
|
36
|
+
"iPad11,1": "iPad mini (5th gen)", "iPad11,2": "iPad mini (5th gen)", "iPad11,3": "iPad Air (3rd gen)",
|
|
37
|
+
"iPad11,4": "iPad Air (3rd gen)", "iPad11,6": "iPad (8th gen)", "iPad11,7": "iPad (8th gen)",
|
|
38
|
+
"iPad12,1": "iPad (9th gen)", "iPad12,2": "iPad (9th gen)", "iPad13,1": "iPad Air (4th gen)",
|
|
39
|
+
"iPad13,2": "iPad Air (4th gen)", "iPad13,4": "iPad Pro 11-inch (3rd gen)",
|
|
40
|
+
"iPad13,5": "iPad Pro 11-inch (3rd gen)", "iPad13,6": "iPad Pro 11-inch (3rd gen)",
|
|
41
|
+
"iPad13,7": "iPad Pro 11-inch (3rd gen)", "iPad13,8": "iPad Pro 12.9-inch (5th gen)",
|
|
42
|
+
"iPad13,9": "iPad Pro 12.9-inch (5th gen)", "iPad13,10": "iPad Pro 12.9-inch (5th gen)",
|
|
43
|
+
"iPad13,11": "iPad Pro 12.9-inch (5th gen)", "iPad13,16": "iPad Air (5th gen)", "iPad13,17": "iPad Air (5th gen)",
|
|
44
|
+
"iPad13,18": "iPad (10th gen)", "iPad13,19": "iPad (10th gen)", "iPad14,1": "iPad mini (6th gen)",
|
|
45
|
+
"iPad14,2": "iPad mini (6th gen)", "iPad14,3": "iPad Pro 11-inch (4th gen)",
|
|
46
|
+
"iPad14,4": "iPad Pro 11-inch (4th gen)", "iPad14,5": "iPad Pro 12.9-inch (6th gen)",
|
|
47
|
+
"iPad14,6": "iPad Pro 12.9-inch (6th gen)", "iPad14,8": "iPad Air 11-inch (M2)",
|
|
48
|
+
"iPad14,9": "iPad Air 11-inch (M2)", "iPad14,10": "iPad Air 13-inch (M2)", "iPad14,11": "iPad Air 13-inch (M2)",
|
|
49
|
+
"iPad15,3": "iPad Air 11-inch (M3)", "iPad15,4": "iPad Air 11-inch (M3)", "iPad15,5": "iPad Air 13-inch (M3)",
|
|
50
|
+
"iPad15,6": "iPad Air 13-inch (M3)", "iPad15,7": "iPad (A16)", "iPad15,8": "iPad (A16)",
|
|
51
|
+
"iPad16,1": "iPad mini (A17 Pro)", "iPad16,2": "iPad mini (A17 Pro)", "iPad16,3": "iPad Pro 11-inch (M4)",
|
|
52
|
+
"iPad16,4": "iPad Pro 11-inch (M4)", "iPad16,5": "iPad Pro 13-inch (M4)", "iPad16,6": "iPad Pro 13-inch (M4)",
|
|
53
|
+
"iPad16,8": "iPad Air 11-inch (M4)", "iPad16,9": "iPad Air 11-inch (M4)", "iPad16,10": "iPad Air 13-inch (M4)",
|
|
54
|
+
"iPad16,11": "iPad Air 13-inch (M4)", "iPad17,1": "iPad Pro 11-inch (M5)", "iPad17,2": "iPad Pro 11-inch (M5)",
|
|
55
|
+
"iPad17,3": "iPad Pro 13-inch (M5)", "iPad17,4": "iPad Pro 13-inch (M5)",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# The phone's finish as lockdown reports it (DeviceEnclosureColor): a small number on recent iPhones, a hex colour on
|
|
60
|
+
# older ones. It names Apple's own art for that finish; anything else is dropped,
|
|
61
|
+
# so the app shows the model's neutral one.
|
|
62
|
+
ENCLOSURE_PATTERN = re.compile(r"#?([0-9A-Fa-f]{1,6})")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def enclosure_code(value):
|
|
66
|
+
"""DeviceEnclosureColor as the dashboard keys it ("8", "e1ccb7"), or None when it isn't one."""
|
|
67
|
+
match = ENCLOSURE_PATTERN.fullmatch((value or "").strip())
|
|
68
|
+
return match.group(1).lower() if match else None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def usb_info(device):
|
|
72
|
+
"""Identity of a USB iPhone or iPad as its lockdown service reports it (no UDID or serial)."""
|
|
73
|
+
model = device.get("model")
|
|
74
|
+
version = device.get("ios")
|
|
75
|
+
if not isinstance(model, str) or not re.fullmatch(r"(?:iPhone|iPad)[0-9]{1,3},[0-9]{1,3}", model):
|
|
76
|
+
model = None
|
|
77
|
+
if not isinstance(version, str) or not re.fullmatch(r"[0-9]{1,2}(?:\.[0-9]{1,3}){1,2}", version):
|
|
78
|
+
version = None
|
|
79
|
+
return {"modelIdentifier": model, "modelName": MODEL_NAMES.get(model), "iosVersion": version,
|
|
80
|
+
"buildVersion": None, "virtual": False, "source": "usb",
|
|
81
|
+
"verifiedAt": time.time() * 1000, "stale": False}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class DeviceInfo:
|
|
85
|
+
"""The served phone's identity, re-read at most once a minute."""
|
|
86
|
+
virtual = False
|
|
87
|
+
|
|
88
|
+
def __init__(self, manager=None):
|
|
89
|
+
self.manager = manager
|
|
90
|
+
self.lock = threading.Lock()
|
|
91
|
+
self.checked_at = None
|
|
92
|
+
self.value = {"modelIdentifier": None, "modelName": None, "iosVersion": None,
|
|
93
|
+
"buildVersion": None, "virtual": self.virtual,
|
|
94
|
+
"source": None, "verifiedAt": None, "stale": False}
|
|
95
|
+
|
|
96
|
+
def read(self):
|
|
97
|
+
"""The identity now, or None when it cannot be read."""
|
|
98
|
+
if self.manager is None:
|
|
99
|
+
return None
|
|
100
|
+
device = self.manager.device()
|
|
101
|
+
return usb_info(device) if device is not None and device.get("trusted") else None
|
|
102
|
+
|
|
103
|
+
def snapshot(self):
|
|
104
|
+
with self.lock:
|
|
105
|
+
if self.checked_at is not None and time.monotonic() - self.checked_at < 60:
|
|
106
|
+
return dict(self.value)
|
|
107
|
+
self.checked_at = time.monotonic()
|
|
108
|
+
try:
|
|
109
|
+
value = self.read()
|
|
110
|
+
except Exception:
|
|
111
|
+
value = None
|
|
112
|
+
if value is not None:
|
|
113
|
+
self.value = value
|
|
114
|
+
elif self.manager is not None or type(self).read is not DeviceInfo.read:
|
|
115
|
+
self.value = {**self.value, "stale": self.value["verifiedAt"] is not None}
|
|
116
|
+
return dict(self.value)
|