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,291 @@
|
|
|
1
|
+
"""WebDriverAgent touch input."""
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from ..transport import Deadline
|
|
7
|
+
from .guards import finite_positive, grounded_target
|
|
8
|
+
|
|
9
|
+
_SWIPE_X = .5
|
|
10
|
+
_SWIPE_Y = .5
|
|
11
|
+
_SWIPE_UP_Y = (.75, .25)
|
|
12
|
+
_SWIPE_DOWN_Y = (.25, .75)
|
|
13
|
+
_SWIPE_LEFT_X = (.75, .25)
|
|
14
|
+
_SWIPE_RIGHT_X = (.25, .75)
|
|
15
|
+
# LockedIn's reaction picker opened on a 1.5 s hold and not on 0.9 s (live, 25 Sep).
|
|
16
|
+
LONG_PRESS_MS = 1500
|
|
17
|
+
FLICK_MS = 120
|
|
18
|
+
# A list row's sideways swipe only reveals its actions: a slow drag over 35% of the row (from 75% to 40%), held still
|
|
19
|
+
# before it lifts so no momentum carries it on. A flick across half the row can cross an app's full-swipe line, where
|
|
20
|
+
# Notes deletes and Mail trashes with no tap (HARDENING-RESEARCH #3, 14 Oct; to be measured on the phone).
|
|
21
|
+
_REVEAL_LEFT_X = (.75, .40)
|
|
22
|
+
_REVEAL_RIGHT_X = (.25, .60)
|
|
23
|
+
REVEAL_MS = 350
|
|
24
|
+
REVEAL_HOLD_MS = 120
|
|
25
|
+
# XCTest's drag, the fallback scroll (see WDA_GLIDE_SEGMENTS for the default).
|
|
26
|
+
# A W3C drag held still before lifting flings (measured twice, 2026-09-23).
|
|
27
|
+
# Not the faster velocity drag: at 6000 px/s (2.28 s vs ~2.4 s here) its
|
|
28
|
+
# momentum carried the Settings list past General and Accessibility, so every
|
|
29
|
+
# navigation task scrolled beyond its target. A scroll must move a bounded,
|
|
30
|
+
# predictable distance; this endpoint does.
|
|
31
|
+
WDA_SWIPE_SECONDS = .1
|
|
32
|
+
WDA_TAP_HOLD_MS = 40
|
|
33
|
+
# Scrolling primitive (measured 2026-09-23, iPhone 15 Pro, iOS 26, with
|
|
34
|
+
# FrameClock odometry and AX ground truth; FrameClock validation, 23 Sep 2026).
|
|
35
|
+
# XCTest's drag (WDA_SWIPE_SECONDS above) moves exactly 0.4875 screens but
|
|
36
|
+
# starts moving only 0.65-0.75 s (Settings) / 2.3 s (Safari) after the request
|
|
37
|
+
# and returns after 1.6-1.7 s / 3.2-3.3 s. A W3C drag starts sooner, but a
|
|
38
|
+
# `pause` before lifting emits no touch samples, so the lift keeps the last
|
|
39
|
+
# move's velocity and the list flings (0.5-1.5 extra screens, ~2 s of motion),
|
|
40
|
+
# as the earlier WebKit test saw. A "glide" fixes both: a slow start (so the
|
|
41
|
+
# pan begins at a precise point: fast starts lost 0.05-0.15 screens, varying
|
|
42
|
+
# run to run), a fast middle, and a decelerating end whose last samples carry
|
|
43
|
+
# almost no velocity. Measured: 0.4742 screens in 8/8 Settings swipes, 0.481 in
|
|
44
|
+
# 4/4 Safari swipes, no motion after release, and dispatch 1.06-1.24 s
|
|
45
|
+
# (Settings) / 2.08-2.14 s (Safari). Segments: (fraction of the distance, ms).
|
|
46
|
+
WDA_GLIDE_SEGMENTS = ((.04, 80), (.92, 200), (.99, 120), (1.0, 80))
|
|
47
|
+
# Where a glide is used names no app (one general engine, 10 Oct): a tuned driver glides everywhere, a plain one
|
|
48
|
+
# wherever a healthy FrameClock watches. Motion after release (a fling or a paging snap) sends that app back to
|
|
49
|
+
# XCTest's drag for the rest of the process. MOBSTER_GLIDE=0 turns the glide off.
|
|
50
|
+
# Content that moved more than this (screen fraction) after the glide returned.
|
|
51
|
+
WDA_GLIDE_MOMENTUM = .12
|
|
52
|
+
# SWIPE_UP/DOWN as a glide: from 80 % to 12 % of the screen (up) or 20 % to 88 % (down). Starting higher lands on
|
|
53
|
+
# the navigation bar and does not scroll; Safari's bottom address bar sits at ~0.88, and a stroke that starts on it
|
|
54
|
+
# does not scroll either.
|
|
55
|
+
WDA_GLIDE_SPAN = {"SWIPE_UP": (.80, .12), "SWIPE_DOWN": (.2, .88)}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _screen_point(snapshot, x, y):
|
|
59
|
+
"""Validate screen fractions and keep rounded points inside their observation's dimensions."""
|
|
60
|
+
if any(type(v) not in (int, float) or not 0 <= v <= 1 for v in (x, y)):
|
|
61
|
+
raise ValueError("point coordinates must be finite screen fractions between 0 and 1")
|
|
62
|
+
dimensions = (getattr(snapshot, "width", None), getattr(snapshot, "height", None))
|
|
63
|
+
if any(not finite_positive(v) or v < 1 for v in dimensions):
|
|
64
|
+
raise ValueError("point actions need an observation with valid screen dimensions")
|
|
65
|
+
return tuple(min(round(value * size), math.ceil(size) - 1) for value, size in zip((x, y), dimensions))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _in_range(value, low, high):
|
|
69
|
+
return not isinstance(value, bool) and isinstance(value, (int, float)) and low <= value <= high
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# A vertical stroke that starts on one of these drags the control instead of the page:
|
|
73
|
+
# Display & Brightness's slider sits at y 0.77-0.81, under the glide's 0.80 start, and
|
|
74
|
+
# the page never scrolled (MobsterBench pass 6, state.auto_lock) -- a drag there can
|
|
75
|
+
# also move the setting itself.
|
|
76
|
+
DRAG_SENSITIVE_ROLES = frozenset({"Slider", "Switch", "Stepper", "Picker", "PickerWheel", "DatePicker",
|
|
77
|
+
"SegmentedControl"})
|
|
78
|
+
SWIPE_START_STEP = .01
|
|
79
|
+
SCROLLING_MARKERS = ("XCUIElementTypeScrollView[", "XCUIElementTypeTable[", "XCUIElementTypeCollectionView[")
|
|
80
|
+
SWIPE_START_LIMIT = .3 # the farthest a start moves (in screen heights) before giving up
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def clear_stroke_start(snapshot, operation, x, y, width, height, fixed=False):
|
|
84
|
+
"""``y`` moved along the stroke until its touch-down misses every drag-sensitive control (``fixed``: floating too).
|
|
85
|
+
SplitPay's Pay/Request button sat under the glide's start, and every glide pressed it (F2, 26 Sep)."""
|
|
86
|
+
if operation not in {"SWIPE_UP", "SWIPE_DOWN"} or snapshot is None or not width or not height:
|
|
87
|
+
return y
|
|
88
|
+
controls = [e.rect for e in getattr(snapshot, "elements", ()) if e.role in DRAG_SENSITIVE_ROLES or fixed and (
|
|
89
|
+
e.role in ("Button", "Other", "TextField", "SearchField") and e.rect[2] * e.rect[3] < .25
|
|
90
|
+
and not any(marker in e.locator for marker in SCROLLING_MARKERS))]
|
|
91
|
+
if not controls:
|
|
92
|
+
return y
|
|
93
|
+
nx, ny = x / width, y / height
|
|
94
|
+
step = -SWIPE_START_STEP if operation == "SWIPE_UP" else SWIPE_START_STEP
|
|
95
|
+
moved = 0.0
|
|
96
|
+
while any(rx <= nx <= rx + rw and ry - .01 <= ny + moved <= ry + rh + .01 for rx, ry, rw, rh in controls):
|
|
97
|
+
moved += step
|
|
98
|
+
if abs(moved) > SWIPE_START_LIMIT:
|
|
99
|
+
return y
|
|
100
|
+
return (ny + moved) * height
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# An iOS switch's knob sits at the right end of its row, about this far in from the edge.
|
|
104
|
+
SWITCH_KNOB_INSET_PT = 30
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def switch_point(target, screen_width_pt):
|
|
108
|
+
"""Where to tap ``target`` (fractions); a row-wide switch at its knob: a tap on its label doesn't toggle."""
|
|
109
|
+
x, y = getattr(target, "tap_point", target.center)
|
|
110
|
+
left, _, width, _ = target.rect
|
|
111
|
+
if target.role in ("Switch", "Toggle") and width * screen_width_pt > 3 * SWITCH_KNOB_INSET_PT:
|
|
112
|
+
x = left + width - SWITCH_KNOB_INSET_PT / screen_width_pt
|
|
113
|
+
return x, y
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _swipe_points(operation, width, height, target_rect=None, reveal=False):
|
|
117
|
+
"""The stroke's (x1, y1, x2, y2) in points; a sideways swipe crosses ``target_rect``'s row, not the one mid-screen.
|
|
118
|
+
Mid-screen, alarm-delete swiped the owner's 5:55 AM alarm open 15 times while aiming at another (B7)."""
|
|
119
|
+
if operation in {"SWIPE_UP", "SWIPE_DOWN"}:
|
|
120
|
+
start, end = _SWIPE_UP_Y if operation == "SWIPE_UP" else _SWIPE_DOWN_Y
|
|
121
|
+
x = width * _SWIPE_X
|
|
122
|
+
return x, height * start, x, height * end
|
|
123
|
+
if operation not in {"SWIPE_LEFT", "SWIPE_RIGHT"}:
|
|
124
|
+
raise ValueError(f"Unsupported swipe {operation!r}")
|
|
125
|
+
start, end = _SWIPE_LEFT_X if operation == "SWIPE_LEFT" else _SWIPE_RIGHT_X
|
|
126
|
+
if target_rect is not None and reveal:
|
|
127
|
+
start, end = _REVEAL_LEFT_X if operation == "SWIPE_LEFT" else _REVEAL_RIGHT_X
|
|
128
|
+
if target_rect is not None:
|
|
129
|
+
left, top, w, h = target_rect
|
|
130
|
+
left, right = max(0.0, left), min(1.0, left + w)
|
|
131
|
+
y = height * min(max(top + h / 2, 0.0), 1.0)
|
|
132
|
+
return (width * (left + (right - left) * start), y, width * (left + (right - left) * end), y)
|
|
133
|
+
y = height * _SWIPE_Y
|
|
134
|
+
return width * start, y, width * end, y
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class WDAGestures:
|
|
138
|
+
"""The touch part of WDA."""
|
|
139
|
+
# Apps where a glide kept moving after release, per WDA URL.
|
|
140
|
+
_momentum_apps = {}
|
|
141
|
+
|
|
142
|
+
def drag_next(self):
|
|
143
|
+
"""The next vertical swipe is XCTest's drag even where a glide would be used; False when it would
|
|
144
|
+
be a drag anyway (glides off, or only in the measured apps: WDA.tune turns them on everywhere)."""
|
|
145
|
+
if (os.environ.get("MOBSTER_GLIDE", "").strip().casefold() in {"0", "off", "false", "no"}
|
|
146
|
+
or not self.glide_everywhere):
|
|
147
|
+
return False
|
|
148
|
+
self._drag_once = True
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
def _glide_ok(self, operation, snapshot):
|
|
152
|
+
if operation not in {"SWIPE_UP", "SWIPE_DOWN"}:
|
|
153
|
+
return False
|
|
154
|
+
if self._drag_once:
|
|
155
|
+
self._drag_once = False
|
|
156
|
+
return False
|
|
157
|
+
if os.environ.get("MOBSTER_GLIDE", "").strip().casefold() in {"0", "off", "false", "no"}:
|
|
158
|
+
return False
|
|
159
|
+
bundle = snapshot.bundle_id
|
|
160
|
+
if not bundle or bundle in self._momentum_apps.get(self.url, ()):
|
|
161
|
+
return False
|
|
162
|
+
if self.glide_everywhere:
|
|
163
|
+
return True # WDA.tune: every app until the FrameClock sees one keep moving
|
|
164
|
+
# A plain driver glides only where a healthy FrameClock watches, so an app that keeps moving after a glide
|
|
165
|
+
# is demoted to XCTest's drag; without one, XCTest's drag in every app alike. This replaced a list of two
|
|
166
|
+
# apps (10 Oct) and isn't measured on the phone yet.
|
|
167
|
+
clock = getattr(self, "frame_clock", None)
|
|
168
|
+
try:
|
|
169
|
+
return clock is not None and bool(clock.healthy())
|
|
170
|
+
except Exception: # noqa: BLE001 -- a clock that can't say is not watching
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def _pointer(self, steps, deadline):
|
|
174
|
+
"""One finger doing ``steps`` (W3C pointer actions) through WDA."""
|
|
175
|
+
self.call("POST", "/actions", {"actions": [{
|
|
176
|
+
"type": "pointer", "id": "finger", "parameters": {"pointerType": "touch"}, "actions": steps}]},
|
|
177
|
+
deadline.remaining())
|
|
178
|
+
|
|
179
|
+
def _flick(self, x1, y1, x2, y2, deadline):
|
|
180
|
+
self._pointer([{"type": "pointerMove", "duration": 0, "x": round(x1), "y": round(y1)}, {"type": "pointerDown"},
|
|
181
|
+
{"type": "pointerMove", "duration": FLICK_MS, "x": round(x2), "y": round(y2)},
|
|
182
|
+
{"type": "pointerUp"}], deadline)
|
|
183
|
+
|
|
184
|
+
def _reveal(self, x1, y1, x2, y2, deadline):
|
|
185
|
+
"""A list row's swipe: a slow drag held still before it lifts, so it opens the row's actions and no more."""
|
|
186
|
+
self._pointer([{"type": "pointerMove", "duration": 0, "x": round(x1), "y": round(y1)}, {"type": "pointerDown"},
|
|
187
|
+
{"type": "pointerMove", "duration": REVEAL_MS, "x": round(x2), "y": round(y2)},
|
|
188
|
+
{"type": "pause", "duration": REVEAL_HOLD_MS}, {"type": "pointerUp"}], deadline)
|
|
189
|
+
|
|
190
|
+
def _glide(self, x1, y1, x2, y2, deadline):
|
|
191
|
+
actions = [{"type": "pointerMove", "duration": 0, "x": round(x1), "y": round(y1)}, {"type": "pointerDown"}]
|
|
192
|
+
for fraction, milliseconds in WDA_GLIDE_SEGMENTS:
|
|
193
|
+
actions.append({"type": "pointerMove", "duration": milliseconds,
|
|
194
|
+
"x": round(x1 + (x2 - x1) * fraction), "y": round(y1 + (y2 - y1) * fraction)})
|
|
195
|
+
actions.append({"type": "pointerUp"})
|
|
196
|
+
self._pointer(actions, deadline)
|
|
197
|
+
|
|
198
|
+
def long_press(self, target, snapshot, timeout=10):
|
|
199
|
+
"""Press and hold ``target`` (a reaction picker, a context menu) at its tap point."""
|
|
200
|
+
deadline = Deadline(timeout)
|
|
201
|
+
grounded_target("LONG_PRESS", target, snapshot)
|
|
202
|
+
self._check_current(snapshot, deadline)
|
|
203
|
+
self._note_tap(target.locator)
|
|
204
|
+
x, y = switch_point(target, snapshot.width)
|
|
205
|
+
self._dispatching()
|
|
206
|
+
self._frame_mark("LONG_PRESS")
|
|
207
|
+
self._pointer([{"type": "pointerMove", "duration": 0, "x": round(x * snapshot.width),
|
|
208
|
+
"y": round(y * snapshot.height)},
|
|
209
|
+
{"type": "pointerDown"}, {"type": "pause", "duration": LONG_PRESS_MS}, {"type": "pointerUp"}],
|
|
210
|
+
deadline)
|
|
211
|
+
self._frame_dispatched()
|
|
212
|
+
|
|
213
|
+
def _touch(self, start, deadline, *, hold_ms=0, end=None, move_ms=0):
|
|
214
|
+
"""One complete contact. WDA cannot interrupt it after dispatch; never split or replay it."""
|
|
215
|
+
steps = [{"type": "pointerMove", "duration": 0, "x": start[0], "y": start[1]}, {"type": "pointerDown"}]
|
|
216
|
+
if hold_ms:
|
|
217
|
+
steps.append({"type": "pause", "duration": hold_ms})
|
|
218
|
+
if end is not None:
|
|
219
|
+
if hold_ms:
|
|
220
|
+
# WDA's pause advances time without adding a native path point. Anchor the pickup
|
|
221
|
+
# location at its end, or XCTest interpolates movement across the hold as well.
|
|
222
|
+
steps.append({"type": "pointerMove", "duration": 0, "x": start[0], "y": start[1]})
|
|
223
|
+
steps.append({"type": "pointerMove", "duration": move_ms, "x": end[0], "y": end[1]})
|
|
224
|
+
steps.append({"type": "pointerUp"})
|
|
225
|
+
self._pointer(steps, deadline)
|
|
226
|
+
|
|
227
|
+
def hold_point(self, x, y, snapshot, *, duration=1.5, timeout=15):
|
|
228
|
+
"""Hold a screenshot point for 0.1 to 10 s; the caller checks permission first, as a hold can't be stopped."""
|
|
229
|
+
point = _screen_point(snapshot, x, y)
|
|
230
|
+
if not _in_range(duration, .1, 10):
|
|
231
|
+
raise ValueError("hold duration must be between 0.1 and 10 seconds")
|
|
232
|
+
deadline = Deadline(timeout)
|
|
233
|
+
self._check_current(snapshot, deadline)
|
|
234
|
+
self._pages_stale = True
|
|
235
|
+
self._dispatching()
|
|
236
|
+
self._frame_mark("HOLD_POINT")
|
|
237
|
+
self._touch(point, deadline, hold_ms=round(duration * 1000))
|
|
238
|
+
self._frame_dispatched()
|
|
239
|
+
|
|
240
|
+
def flick_point(self, start_x, start_y, end_x, end_y, snapshot, *, duration=.12, timeout=10):
|
|
241
|
+
"""A quick directional movement with immediate release; no pickup hold or end pause."""
|
|
242
|
+
start = _screen_point(snapshot, start_x, start_y)
|
|
243
|
+
end = _screen_point(snapshot, end_x, end_y)
|
|
244
|
+
if not _in_range(duration, .08, .3):
|
|
245
|
+
raise ValueError("flick duration must be between 0.08 and 0.3 seconds")
|
|
246
|
+
if start == end:
|
|
247
|
+
raise ValueError("flick endpoints must occupy different screen points")
|
|
248
|
+
deadline = Deadline(timeout)
|
|
249
|
+
self._check_current(snapshot, deadline)
|
|
250
|
+
self._pages_stale = True
|
|
251
|
+
self._dispatching()
|
|
252
|
+
self._frame_mark("FLICK_POINT")
|
|
253
|
+
self._touch(start, deadline, end=end, move_ms=round(duration * 1000))
|
|
254
|
+
self._frame_dispatched()
|
|
255
|
+
|
|
256
|
+
def drag_point(self, start_x, start_y, end_x, end_y, snapshot, *, duration=1.0, hold_seconds=.1, timeout=10):
|
|
257
|
+
"""Drag between screen fractions from one observation; duration is movement time in seconds.
|
|
258
|
+
|
|
259
|
+
hold_seconds is the initial pickup hold, independent of movement time. The caller owns screenshot
|
|
260
|
+
grounding and permission checks for both endpoints. Dispatch once: a failed response may still mean the
|
|
261
|
+
phone moved, so it must never trigger a gesture retry.
|
|
262
|
+
"""
|
|
263
|
+
start = _screen_point(snapshot, start_x, start_y)
|
|
264
|
+
end = _screen_point(snapshot, end_x, end_y)
|
|
265
|
+
if not _in_range(duration, .1, 2):
|
|
266
|
+
raise ValueError("drag duration must be between 0.1 and 2 seconds")
|
|
267
|
+
if not _in_range(hold_seconds, 0, 2):
|
|
268
|
+
raise ValueError("drag pickup hold must be between 0 and 2 seconds")
|
|
269
|
+
if start == end:
|
|
270
|
+
raise ValueError("drag endpoints must occupy different screen points")
|
|
271
|
+
deadline = Deadline(timeout)
|
|
272
|
+
self._check_current(snapshot, deadline)
|
|
273
|
+
self._pages_stale = True
|
|
274
|
+
self._dispatching()
|
|
275
|
+
self._frame_mark("DRAG_POINT")
|
|
276
|
+
self._touch(start, deadline, hold_ms=round(hold_seconds * 1000), end=end, move_ms=round(duration * 1000))
|
|
277
|
+
self._frame_dispatched()
|
|
278
|
+
|
|
279
|
+
def tap_point(self, x, y, snapshot, timeout=10, purpose=None):
|
|
280
|
+
"""A bounded tap at screen fractions (x, y), re-grounded when the observation aged."""
|
|
281
|
+
x, y = _screen_point(snapshot, x, y)
|
|
282
|
+
deadline = Deadline(timeout)
|
|
283
|
+
self._check_current(snapshot, deadline)
|
|
284
|
+
self._pages_stale = True
|
|
285
|
+
self._tap(x, y, deadline)
|
|
286
|
+
|
|
287
|
+
def _tap(self, x, y, deadline):
|
|
288
|
+
self._dispatching()
|
|
289
|
+
self._pointer([{"type": "pointerMove", "duration": 0, "x": round(x), "y": round(y)},
|
|
290
|
+
{"type": "pointerDown"}, {"type": "pause", "duration": WDA_TAP_HOLD_MS},
|
|
291
|
+
{"type": "pointerUp"}], deadline)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""What every part of the WDA driver refuses with and checks before it acts: the typed refusal a driver raises, and
|
|
2
|
+
the checks that a target belongs to the observation it was given and that a number is a usable size."""
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
import time
|
|
6
|
+
from typing import Literal, TypedDict
|
|
7
|
+
|
|
8
|
+
from ..transport import TransportError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ActivationDiagnostics(TypedDict, total=False):
|
|
12
|
+
activation_route: Literal["table_selection", "control_primary_action", "control_primary_event",
|
|
13
|
+
"control_touch_up_inside", "accessibility", "physical_tap",
|
|
14
|
+
"navigation_pop"]
|
|
15
|
+
dispatch_attempted: bool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def activation_diagnostics(payload: object) -> ActivationDiagnostics:
|
|
19
|
+
"""Bounded telemetry only. Missing diagnostics never mean no dispatch or safe retry."""
|
|
20
|
+
result: ActivationDiagnostics = {}
|
|
21
|
+
if not isinstance(payload, dict):
|
|
22
|
+
return result
|
|
23
|
+
route = payload.get("activation_route")
|
|
24
|
+
if isinstance(route, str) and route in {"table_selection", "control_primary_action", "control_primary_event",
|
|
25
|
+
"control_touch_up_inside", "accessibility", "physical_tap",
|
|
26
|
+
"navigation_pop"}:
|
|
27
|
+
result["activation_route"] = route
|
|
28
|
+
attempted = payload.get("dispatch_attempted")
|
|
29
|
+
if type(attempted) is bool:
|
|
30
|
+
result["dispatch_attempted"] = attempted
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class DriverRejection(TransportError):
|
|
35
|
+
"""A driver's typed refusal. Known codes only; never arbitrary app text or relay output."""
|
|
36
|
+
CODES = frozenset({"app_not_active", "app_preview_unavailable", "app_preview_uniform",
|
|
37
|
+
"app_preview_oversized", "incomplete_accessibility_tree", "stale_revision", "target_occluded",
|
|
38
|
+
"unsupported_or_stale_target", "selection_changed", "invalid_text", "text_target_changed",
|
|
39
|
+
"main_thread_busy", "native_exception_outcome_unknown", "main_thread_deadline_expired_outcome_unknown",
|
|
40
|
+
"request_deadline_expired", "request_cancelled", "activation_declined", "response_serialization_failed",
|
|
41
|
+
"bridge_update_required", "physical_tap_not_authorized", "physical_tap_outcome_unknown",
|
|
42
|
+
"home_outcome_unknown", "volup_outcome_unknown", "voldown_outcome_unknown",
|
|
43
|
+
"app_launch_outcome_unknown"})
|
|
44
|
+
|
|
45
|
+
# Codes the bridge returns BEFORE its documented dispatch boundary, where no
|
|
46
|
+
# app code has run. Recovering from these is not a replay: the agent observes
|
|
47
|
+
# again and decides again. Every other code -- especially the *_outcome_unknown
|
|
48
|
+
# family -- stays ambiguous and terminal, because an action that may have
|
|
49
|
+
# executed must never be repeated automatically.
|
|
50
|
+
PRE_DISPATCH = frozenset({"stale_revision", "unsupported_or_stale_target",
|
|
51
|
+
"target_occluded", "incomplete_accessibility_tree"})
|
|
52
|
+
|
|
53
|
+
def __init__(self, code, diagnostics=None):
|
|
54
|
+
self.code = code if isinstance(code, str) and code in self.CODES else "native_request_rejected"
|
|
55
|
+
self.diagnostics = activation_diagnostics(diagnostics)
|
|
56
|
+
super().__init__("Driver refused: " + self.code + "; no automatic retry")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def finite_positive(value):
|
|
60
|
+
return type(value) in (int, float) and math.isfinite(value) and value > 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def grounded_target(operation, target, snapshot):
|
|
64
|
+
"""Adapters are safety boundaries too, even when invoked without the agent loop."""
|
|
65
|
+
if target is None or not any(target == element for element in snapshot.elements):
|
|
66
|
+
raise ValueError("Action target is not part of the supplied observation")
|
|
67
|
+
if operation in {"TYPE", "TYPE_SUBMIT"} and not target.editable:
|
|
68
|
+
raise ValueError("TYPE requires an editable target")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# A read is reused for the first action when the screen stream shows not one changed byte from this
|
|
72
|
+
# long before the read began until now. A change after the read began arrives after it, however
|
|
73
|
+
# late the stream; a change before it that arrives late only makes the rule stricter.
|
|
74
|
+
STILL_MARGIN = 0.0
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def last_change(clock):
|
|
78
|
+
"""The monotonic time the stream's bytes last changed (a status bar tick included), or None when
|
|
79
|
+
``clock`` is missing, unhealthy or fails to say."""
|
|
80
|
+
still_for = getattr(clock, "still_for", None)
|
|
81
|
+
if not callable(still_for):
|
|
82
|
+
return None
|
|
83
|
+
try:
|
|
84
|
+
still = still_for()
|
|
85
|
+
except Exception:
|
|
86
|
+
return None
|
|
87
|
+
return None if still is None else time.monotonic() - still
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def unchanged_since(clock, since):
|
|
91
|
+
"""True when the stream shows not one changed byte from STILL_MARGIN before ``since`` (monotonic) until
|
|
92
|
+
now; False when unknown."""
|
|
93
|
+
changed = last_change(clock)
|
|
94
|
+
return bool(since) and changed is not None and changed <= since - STILL_MARGIN
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Which phone answers on a WebDriverAgent port: checked before any session or action (DR-68).
|
|
2
|
+
|
|
3
|
+
Every Mobster puts its first phone on 8100, so a second Mobster on this Mac (a dev window, a QA app, the installed
|
|
4
|
+
app) can find another Mobster's phone answering where it expects its own. ``verify`` compares what the WebDriverAgent
|
|
5
|
+
reports (``/wda/device/info``: the phone's name and model, and its UDID when the helper reports one) with the phone this
|
|
6
|
+
Mobster means. A mismatch, or an answer that can't be read, refuses: nothing is sent to a phone that isn't confirmed.
|
|
7
|
+
A phone whose name Mobster doesn't know yet can't be compared, and is let through.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from ..errors import MobsterError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WrongDevice(MobsterError):
|
|
14
|
+
"""The helper answering on this port is another phone's."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _same(a, b):
|
|
18
|
+
return " ".join(str(a or "").split()).casefold() == " ".join(str(b or "").split()).casefold()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def reported(url, timeout=3.0):
|
|
22
|
+
"""WebDriverAgent's own device info at ``url`` ({name, model, udid?}), or None when it can't be read."""
|
|
23
|
+
from ..transport import HTTP
|
|
24
|
+
client = None
|
|
25
|
+
try:
|
|
26
|
+
client = HTTP(url)
|
|
27
|
+
value = client.request("GET", "/wda/device/info", timeout=timeout).get("value")
|
|
28
|
+
except Exception: # noqa: BLE001 -- unreadable is a refusal in verify
|
|
29
|
+
return None
|
|
30
|
+
finally:
|
|
31
|
+
if client is not None:
|
|
32
|
+
client.close()
|
|
33
|
+
return value if isinstance(value, dict) else None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def verify(url, *, name=None, model=None, udid=None, read=None):
|
|
37
|
+
"""Raise WrongDevice unless the helper at ``url`` is the phone named by ``udid`` (when it reports one), else
|
|
38
|
+
``name`` (and ``model`` when both sides have one). Nothing known to compare: returns without reading."""
|
|
39
|
+
if not (udid or name):
|
|
40
|
+
return
|
|
41
|
+
info = (read or reported)(url)
|
|
42
|
+
if info is None:
|
|
43
|
+
raise WrongDevice(f"Mobster couldn't confirm which iPhone answers at {url}, so it did nothing. Check that "
|
|
44
|
+
"the iPhone is unlocked and its helper is running, then try again.")
|
|
45
|
+
seen_udid = info.get("udid") or info.get("UDID")
|
|
46
|
+
if udid and seen_udid:
|
|
47
|
+
ok = _same(seen_udid, udid)
|
|
48
|
+
elif name:
|
|
49
|
+
ok = _same(info.get("name"), name) and (not model or not info.get("model") or _same(info.get("model"), model))
|
|
50
|
+
else:
|
|
51
|
+
return
|
|
52
|
+
if not ok:
|
|
53
|
+
raise WrongDevice(f"The iPhone answering at {url} is “{str(info.get('name') or 'another iPhone')[:60]}”, not "
|
|
54
|
+
f"“{str(name or udid)[:60]}”. Another Mobster may be using that port. Mobster did nothing; "
|
|
55
|
+
"quit the other Mobster or restart this phone's helper, then try again.")
|