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,515 @@
|
|
|
1
|
+
"""One attempt of one check, a verify run; Smart's approver is check_approver, never a person's approval."""
|
|
2
|
+
|
|
3
|
+
import signal
|
|
4
|
+
import subprocess
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from copy import deepcopy
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from ..verify.runner import CouldntRun, VerifyRun, resolve_mode
|
|
12
|
+
from .targets import Refused, validate_record
|
|
13
|
+
|
|
14
|
+
VIDEO_STOP_WAIT = 10.0
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ReplayPin:
|
|
18
|
+
"""One check/target's effective contract, evidence assets and approved replay revision."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, check):
|
|
21
|
+
self.declared = deepcopy(check)
|
|
22
|
+
self.effective = None
|
|
23
|
+
self.assets = None
|
|
24
|
+
self.selected = None
|
|
25
|
+
self.invalid = False
|
|
26
|
+
self.lock = threading.Lock()
|
|
27
|
+
|
|
28
|
+
def compare_declared(self, check):
|
|
29
|
+
with self.lock:
|
|
30
|
+
if check != self.declared:
|
|
31
|
+
self.invalid = True
|
|
32
|
+
|
|
33
|
+
def bind_check(self, check, live):
|
|
34
|
+
with self.lock:
|
|
35
|
+
if self.invalid:
|
|
36
|
+
return "The check changed during its repeated runs. Start a new suite."
|
|
37
|
+
if check != live:
|
|
38
|
+
return "A repeated check uses expanded project secrets. Run it without --repeat."
|
|
39
|
+
if self.effective is None:
|
|
40
|
+
self.effective = deepcopy(check)
|
|
41
|
+
elif check != self.effective:
|
|
42
|
+
return "The effective check or its fixtures changed during repeated runs. Start a new suite."
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
def bind_assets(self, proof):
|
|
46
|
+
with self.lock:
|
|
47
|
+
if self.invalid or self.effective is None:
|
|
48
|
+
return "The repeated check was not frozen before its evidence was loaded. Start a new suite."
|
|
49
|
+
if self.assets is None:
|
|
50
|
+
self.assets = deepcopy(proof)
|
|
51
|
+
elif proof != self.assets:
|
|
52
|
+
return "A fixture, condition or assertion asset changed during repeated runs. Start a new suite."
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
def bind(self, identity, env, revision):
|
|
56
|
+
with self.lock:
|
|
57
|
+
if self.invalid or self.effective is None or self.assets is None:
|
|
58
|
+
return "The repeated check was not frozen before replay admission. Start a new suite."
|
|
59
|
+
current = (deepcopy(identity), deepcopy(env), revision)
|
|
60
|
+
if self.selected is None:
|
|
61
|
+
self.selected = current
|
|
62
|
+
elif current != self.selected:
|
|
63
|
+
return "The approved replay path, app or device environment changed during repeated runs. " \
|
|
64
|
+
"Start a new suite."
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Attempts:
|
|
69
|
+
"""Runs attempts."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, *, runs_dir, keyless=False, key=None, assert_timeout=5, parallel=1, progress=None,
|
|
72
|
+
stop=None, video_dir=None, simulators=None, devices=None, run_class=VerifyRun, popen=None,
|
|
73
|
+
replay="off"):
|
|
74
|
+
self.runs_dir = Path(runs_dir)
|
|
75
|
+
self.keyless, self.key, self.assert_timeout = keyless, key, assert_timeout
|
|
76
|
+
self.parallel = max(1, int(parallel))
|
|
77
|
+
self.progress = progress or (lambda line: None)
|
|
78
|
+
self.stop = stop or threading.Event()
|
|
79
|
+
self.video_dir = Path(video_dir) if video_dir else None
|
|
80
|
+
self._simulators = simulators
|
|
81
|
+
self._devices = devices or _device_manager
|
|
82
|
+
self.run_class = run_class
|
|
83
|
+
self.replay = replay
|
|
84
|
+
self.popen = popen or subprocess.Popen
|
|
85
|
+
self.gate = None
|
|
86
|
+
self._repeat_pins = {}
|
|
87
|
+
self._reserved = {}
|
|
88
|
+
self._live = set()
|
|
89
|
+
self._lock = threading.Lock()
|
|
90
|
+
|
|
91
|
+
def bind_gate(self, gate):
|
|
92
|
+
self.gate = gate
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _repeat_key(check, record):
|
|
96
|
+
target = record.get("udid") or record.get("id") if record else (check.device, check.runtime)
|
|
97
|
+
locale = (check.conditions or {}).get("locale")
|
|
98
|
+
return check.source or id(check), locale, target
|
|
99
|
+
|
|
100
|
+
def bind_repeat(self, check, record):
|
|
101
|
+
key = self._repeat_key(check, record)
|
|
102
|
+
with self._lock:
|
|
103
|
+
pin = self._repeat_pins.get(key)
|
|
104
|
+
if pin is None:
|
|
105
|
+
self._repeat_pins[key] = ReplayPin(check)
|
|
106
|
+
else:
|
|
107
|
+
pin.compare_declared(check)
|
|
108
|
+
|
|
109
|
+
def simulators(self):
|
|
110
|
+
with self._lock:
|
|
111
|
+
if self._simulators is None:
|
|
112
|
+
self._simulators = suite_simulators(self.parallel, self.progress)
|
|
113
|
+
return self._simulators
|
|
114
|
+
|
|
115
|
+
def manager_for(self, record):
|
|
116
|
+
"""The manager a run uses: the shared simulator manager, pinned to a Mobster simulator a record names, or a
|
|
117
|
+
WdaDevice for a real device."""
|
|
118
|
+
if record is None:
|
|
119
|
+
return self.simulators()
|
|
120
|
+
validate_record(record)
|
|
121
|
+
if record.get("kind") == "android":
|
|
122
|
+
from ..android.manager import AndroidManager
|
|
123
|
+
from ..paths import dev_data_dir
|
|
124
|
+
return AndroidManager(dev_data_dir(), name=record.get("avd"))
|
|
125
|
+
if record.get("kind") == "simulator":
|
|
126
|
+
borrowed = self._reserved.get(record["udid"])
|
|
127
|
+
if borrowed is not None:
|
|
128
|
+
return borrowed
|
|
129
|
+
from ..device_targets import PinnedSimulator
|
|
130
|
+
return PinnedSimulator(self.simulators(), record["udid"])
|
|
131
|
+
return self._devices(record)
|
|
132
|
+
|
|
133
|
+
@contextmanager
|
|
134
|
+
def reserve_simulators(self, records, journal):
|
|
135
|
+
"""Hold distinct actor leases across every phase; VerifyRun receives a no-op borrowed lease."""
|
|
136
|
+
from ..device_targets import PinnedSimulator
|
|
137
|
+
from ..verify.actions import ActionError
|
|
138
|
+
from .targets import Refused
|
|
139
|
+
|
|
140
|
+
acquired = []
|
|
141
|
+
try:
|
|
142
|
+
for record in sorted(records, key=lambda item: item["udid"]):
|
|
143
|
+
if self.stop.is_set():
|
|
144
|
+
raise ActionError("stopped")
|
|
145
|
+
manager = PinnedSimulator(self.simulators(), record["udid"])
|
|
146
|
+
with journal.action("ACQUIRE"):
|
|
147
|
+
lease = manager.acquire(timeout=0, cancelled=self.stop.is_set)
|
|
148
|
+
acquired.append((record["udid"], manager, lease))
|
|
149
|
+
target = lease.target
|
|
150
|
+
expected_model = record.get("modelName") or record.get("model")
|
|
151
|
+
expected_runtime = f"iOS {record['ios']}" if record.get("ios") else None
|
|
152
|
+
if (target.udid.casefold() != record["udid"].casefold() or
|
|
153
|
+
expected_model and target.device_type != expected_model or
|
|
154
|
+
expected_runtime and target.runtime != expected_runtime):
|
|
155
|
+
raise Refused("A scenario actor's simulator changed since admission. Start a new scenario.",
|
|
156
|
+
"refused")
|
|
157
|
+
self._reserved = {udid: BorrowedSimulator(manager, lease, self.stop)
|
|
158
|
+
for udid, manager, lease in acquired}
|
|
159
|
+
yield
|
|
160
|
+
finally:
|
|
161
|
+
reserved, self._reserved = self._reserved, {}
|
|
162
|
+
for borrowed in reserved.values():
|
|
163
|
+
borrowed.active = False
|
|
164
|
+
for _, _, lease in reversed(acquired):
|
|
165
|
+
lease.release()
|
|
166
|
+
|
|
167
|
+
def __call__(self, check, record, *, label, video_name=None):
|
|
168
|
+
"""The verify result of one attempt of ``check`` (on ``record``'s device, or a simulator)."""
|
|
169
|
+
with self._lock:
|
|
170
|
+
pin = self._repeat_pins.get(self._repeat_key(check, record))
|
|
171
|
+
if pin is not None:
|
|
172
|
+
pin.compare_declared(check)
|
|
173
|
+
from ..verify.fixtures import FixtureError, for_run
|
|
174
|
+
try:
|
|
175
|
+
check = for_run(check, self.runs_dir)
|
|
176
|
+
except FixtureError as error:
|
|
177
|
+
run = self.run_class(check, mode="launch", runs_dir=self.runs_dir, manager=self.manager_for(record),
|
|
178
|
+
progress=lambda line: self.progress(f"{label}: {line}"), key=self.key,
|
|
179
|
+
assert_timeout=self.assert_timeout)
|
|
180
|
+
return run.abort("fixture", str(error))
|
|
181
|
+
manager = self.manager_for(record)
|
|
182
|
+
video = None
|
|
183
|
+
if self.video_dir is not None and video_name and (record is None or record.get("kind") in ("simulator", "usb")):
|
|
184
|
+
video = self.video_dir / f"{video_name}.mp4"
|
|
185
|
+
if record is not None and record.get("kind") == "usb":
|
|
186
|
+
from .phone_video import PhoneRecording
|
|
187
|
+
manager = PhoneRecording(manager, video, log=self.progress)
|
|
188
|
+
else:
|
|
189
|
+
manager = Recording(manager, video, popen=self.popen, log=self.progress)
|
|
190
|
+
mode = "launch" if self.keyless else resolve_mode(check, "auto")
|
|
191
|
+
extra = {"replay": self.replay} if self.replay != "off" and mode == "smart" else {}
|
|
192
|
+
if check.business is not None and check.source is not None:
|
|
193
|
+
from ..verify.business import load_business_binding
|
|
194
|
+
from ..verify.checks import CheckError, project_root
|
|
195
|
+
try:
|
|
196
|
+
extra["business_binding"] = load_business_binding(check, project_root(check.source))
|
|
197
|
+
except (CheckError, OSError):
|
|
198
|
+
# VerifyRun writes the fixture refusal with its normal run evidence.
|
|
199
|
+
pass
|
|
200
|
+
run = self.run_class(check, mode=mode, runs_dir=self.runs_dir, manager=manager,
|
|
201
|
+
progress=lambda line: self.progress(f"{label}: {line}"), key=self.key,
|
|
202
|
+
assert_timeout=self.assert_timeout, **extra)
|
|
203
|
+
run.repeat_pin = pin
|
|
204
|
+
if pin is not None and getattr(run, "replay", None) is not None:
|
|
205
|
+
run.replay.pin = pin
|
|
206
|
+
with self._lock:
|
|
207
|
+
self._live.add(run)
|
|
208
|
+
try:
|
|
209
|
+
result = self._run(run, check, mode, record, pin=pin)
|
|
210
|
+
finally:
|
|
211
|
+
with self._lock:
|
|
212
|
+
self._live.discard(run)
|
|
213
|
+
if video is not None and video.is_file() and video.stat().st_size > 0:
|
|
214
|
+
result["video"] = str(video)
|
|
215
|
+
started, t0 = getattr(manager, "started", None), getattr(run, "_t0", None)
|
|
216
|
+
if isinstance(started, (int, float)) and isinstance(t0, (int, float)):
|
|
217
|
+
# A step's at_ms counts from the run's start; the recording starts when the device is held.
|
|
218
|
+
result["video_offset_ms"] = round((started - t0) * 1000)
|
|
219
|
+
return result
|
|
220
|
+
|
|
221
|
+
def _run(self, run, check, mode, record=None, *, pin=None):
|
|
222
|
+
if self.stop.is_set():
|
|
223
|
+
return run.abort("stopped", "The suite was stopped before this ran.")
|
|
224
|
+
if check.business is not None and record is not None and record.get("kind") != "simulator":
|
|
225
|
+
return run.abort("business", "App-state fixtures run only on managed simulators.")
|
|
226
|
+
if check.steps and mode == "launch":
|
|
227
|
+
return run.abort("usage", "--keyless never calls a model, so a check with steps can't run.",
|
|
228
|
+
"Drop --keyless, or give the check no steps (launch-only).")
|
|
229
|
+
if mode == "smart":
|
|
230
|
+
from ..engines import smart_key
|
|
231
|
+
from ..verify.smart import no_smart_key
|
|
232
|
+
run.key = self.key or smart_key()
|
|
233
|
+
if not run.key and getattr(run, "replay", None) is None:
|
|
234
|
+
why, fix = no_smart_key()
|
|
235
|
+
return run.abort("usage", f"This check's steps need Smart, and {why}.", fix)
|
|
236
|
+
held = None
|
|
237
|
+
notes = []
|
|
238
|
+
try:
|
|
239
|
+
if record is not None and record.get("kind") not in ("simulator", "android"):
|
|
240
|
+
if self.gate is None:
|
|
241
|
+
return run.abort("device", "Mobster can't check this phone's app without a device gate.")
|
|
242
|
+
run._apply_fixtures()
|
|
243
|
+
run._refuse_fixtures_on_phone()
|
|
244
|
+
if (run.check.throughout or run.check.somewhere) and (pin is not None or
|
|
245
|
+
self.replay == "strict" and run.replay is not None):
|
|
246
|
+
reason = "A fixed replay cannot cover throughout or somewhere assertions on every visited screen."
|
|
247
|
+
if run.replay is not None:
|
|
248
|
+
run.replay.refuse_pin(reason)
|
|
249
|
+
raise CouldntRun("replay", reason)
|
|
250
|
+
# Ordinary phone runs expand secrets during VerifyRun.prepare, after admission. Fixed replay must
|
|
251
|
+
# reject them before acquiring a device or installing an app.
|
|
252
|
+
if pin is not None or self.replay == "strict" and run.replay is not None:
|
|
253
|
+
run._load_secrets()
|
|
254
|
+
if self.replay == "strict" and mode == "smart" and run.replay is None:
|
|
255
|
+
return run.abort("replay", "Strict replay cannot use expanded project secrets.")
|
|
256
|
+
if pin is not None and run._live != run.check:
|
|
257
|
+
return run.abort("replay", "A repeated check uses expanded project secrets.")
|
|
258
|
+
check = run.check
|
|
259
|
+
fixed = pin is not None or self.replay == "strict" and run.replay is not None
|
|
260
|
+
if fixed and check.app_path:
|
|
261
|
+
from .targets import device_build
|
|
262
|
+
if device_build(check.app_path) is not False:
|
|
263
|
+
reason = "A fixed phone replay cannot install an unverified app revision. Install the build " \
|
|
264
|
+
"first, then run the check by app bundle."
|
|
265
|
+
if run.replay is not None:
|
|
266
|
+
run.replay.refuse_pin(reason)
|
|
267
|
+
raise CouldntRun("replay", reason)
|
|
268
|
+
manager = run.manager
|
|
269
|
+
with run.actions.action("ACQUIRE"):
|
|
270
|
+
held = manager.acquire(check.device, check.runtime, cancelled=self.stop.is_set)
|
|
271
|
+
if self.stop.is_set():
|
|
272
|
+
return run.abort("stopped", "The suite was stopped before this ran.")
|
|
273
|
+
candidate = (self.gate.normalized_check(check, record)
|
|
274
|
+
if pin is not None or self.replay == "strict" and run.replay is not None else None)
|
|
275
|
+
if candidate is not None:
|
|
276
|
+
run.check = run._live = candidate
|
|
277
|
+
run.target = held.target
|
|
278
|
+
run.app = manager.installed_app(held.target, candidate.bundle_id)
|
|
279
|
+
if not getattr(run.app, "version", None):
|
|
280
|
+
reason = "A fixed phone replay needs the installed app's version before it can use a path."
|
|
281
|
+
if run.replay is not None:
|
|
282
|
+
run.replay.refuse_pin(reason)
|
|
283
|
+
raise CouldntRun("replay", reason)
|
|
284
|
+
if pin is not None:
|
|
285
|
+
from ..replay_core.heal import HealError, proof_assets
|
|
286
|
+
reason = pin.bind_check(candidate, candidate)
|
|
287
|
+
if reason:
|
|
288
|
+
if run.replay is not None:
|
|
289
|
+
run.replay.refuse_pin(reason)
|
|
290
|
+
raise CouldntRun("replay", reason)
|
|
291
|
+
try:
|
|
292
|
+
proof = proof_assets(check)
|
|
293
|
+
except HealError as error:
|
|
294
|
+
if run.replay is not None:
|
|
295
|
+
run.replay.refuse_pin(str(error))
|
|
296
|
+
raise CouldntRun("replay", str(error)) from None
|
|
297
|
+
reason = pin.bind_assets(proof)
|
|
298
|
+
if reason:
|
|
299
|
+
if run.replay is not None:
|
|
300
|
+
run.replay.refuse_pin(reason)
|
|
301
|
+
raise CouldntRun("replay", reason)
|
|
302
|
+
if run.replay is not None and candidate is not None:
|
|
303
|
+
from ..replay_core.checks import CheckReplay
|
|
304
|
+
run.replay = CheckReplay(run, self.replay)
|
|
305
|
+
run.replay.pin = pin
|
|
306
|
+
if run.replay.lookup() is None:
|
|
307
|
+
run.replay.refuse_pin(f"Fixed replay cannot run: {run.replay.reason or 'no approved path'}.")
|
|
308
|
+
elif pin is not None:
|
|
309
|
+
from ..replay_core.checks import identity
|
|
310
|
+
from ..replay_core.store import env_of
|
|
311
|
+
model = getattr(held.target, "device_type", None)
|
|
312
|
+
environment = env_of(getattr(run.app, "version", None), getattr(held.target, "runtime", None),
|
|
313
|
+
"iphone", device_type=None if model == "iPhone" else model)
|
|
314
|
+
reason = pin.bind(identity(candidate),
|
|
315
|
+
{**environment, "runtime": getattr(held.target, "runtime", None)}, "")
|
|
316
|
+
if reason:
|
|
317
|
+
raise CouldntRun("replay", reason)
|
|
318
|
+
admitted, notes, _ = self.gate.admit(check, record, cancelled=self.stop.is_set,
|
|
319
|
+
actions=run.actions)
|
|
320
|
+
if self.stop.is_set():
|
|
321
|
+
return run.abort("stopped", "The suite was stopped before this ran.")
|
|
322
|
+
if candidate is not None and admitted != candidate:
|
|
323
|
+
return run.abort("device", "The phone app changed during admission.")
|
|
324
|
+
run.check = run._live = check = admitted
|
|
325
|
+
run.manager = HeldDevice(manager, held)
|
|
326
|
+
if run.replay is not None and candidate is None:
|
|
327
|
+
from ..replay_core.checks import CheckReplay
|
|
328
|
+
run.replay = CheckReplay(run, self.replay)
|
|
329
|
+
return self._drive(run, check, mode, notes)
|
|
330
|
+
except Exception as error:
|
|
331
|
+
from ..verify.actions import ActionNotDispatched
|
|
332
|
+
if isinstance(error, ActionNotDispatched):
|
|
333
|
+
result = run.abort(error.kind, str(error), error.fix)
|
|
334
|
+
result["not_dispatched"] = True
|
|
335
|
+
return result
|
|
336
|
+
if isinstance(error, (CouldntRun, Refused)):
|
|
337
|
+
return run.abort(error.klass, error.message, error.fix)
|
|
338
|
+
klass = getattr(error, "klass", None)
|
|
339
|
+
if klass in ("journal", "unknown_outcome", "stopped"):
|
|
340
|
+
return run.abort(klass, str(error))
|
|
341
|
+
kind = getattr(error, "kind", None)
|
|
342
|
+
if isinstance(kind, str):
|
|
343
|
+
return run.abort(kind, str(error), getattr(error, "fix", "") or "")
|
|
344
|
+
raise
|
|
345
|
+
finally:
|
|
346
|
+
if held is not None:
|
|
347
|
+
held.release()
|
|
348
|
+
|
|
349
|
+
def _drive(self, run, check, mode, notes):
|
|
350
|
+
try:
|
|
351
|
+
run.prepare()
|
|
352
|
+
if mode == "smart" and getattr(run, "replay", None) is not None:
|
|
353
|
+
if not run.key and run.replay.mode != "strict" and run.replay.lookup() is None:
|
|
354
|
+
# A recorded path replays with no key; recording one needs Smart.
|
|
355
|
+
from ..verify.smart import no_smart_key
|
|
356
|
+
why, fix = no_smart_key()
|
|
357
|
+
raise CouldntRun("usage", f"This check's steps need Smart, and {why}. It has no recorded "
|
|
358
|
+
f"path to replay yet ({run.replay.reason}).", fix)
|
|
359
|
+
run.run_flow(self.stop.is_set)
|
|
360
|
+
elif mode == "smart":
|
|
361
|
+
run.run_smart(self.stop.is_set)
|
|
362
|
+
if check.surfaces: # after the flow, as runner.verify does
|
|
363
|
+
from ..verify.system_surfaces.play import drive as drive_surfaces
|
|
364
|
+
drive_surfaces(run)
|
|
365
|
+
except CouldntRun as error:
|
|
366
|
+
if run.error is None:
|
|
367
|
+
run._fail(error)
|
|
368
|
+
except Exception as error: # the run's own failure, never the suite's
|
|
369
|
+
run.fail_internal(error)
|
|
370
|
+
if self.stop.is_set() and run.result is None and run.error is None:
|
|
371
|
+
run._fail(CouldntRun("stopped", "The suite was stopped."))
|
|
372
|
+
result = run.finish()
|
|
373
|
+
if notes:
|
|
374
|
+
result["notes"] = notes
|
|
375
|
+
return result
|
|
376
|
+
|
|
377
|
+
def abort_all(self, message="The suite was stopped."):
|
|
378
|
+
"""Stop every run in flight: one still preparing stops at its next phase, a Smart run at its next step."""
|
|
379
|
+
self.stop.set()
|
|
380
|
+
with self._lock:
|
|
381
|
+
live = list(self._live)
|
|
382
|
+
for run in live:
|
|
383
|
+
threading.Thread(target=_abort, args=(run, message), daemon=True, name="mobster-test-abort").start()
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _abort(run, message):
|
|
387
|
+
try:
|
|
388
|
+
if run.state in ("new", "preparing"):
|
|
389
|
+
run.abort("stopped", message)
|
|
390
|
+
except Exception:
|
|
391
|
+
pass
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _device_manager(record):
|
|
395
|
+
from ..device_targets import WdaDevice
|
|
396
|
+
return WdaDevice(record)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class HeldDevice:
|
|
400
|
+
"""The run adopts the phone lease held through app admission."""
|
|
401
|
+
|
|
402
|
+
def __init__(self, manager, lease):
|
|
403
|
+
self.manager, self.lease = manager, lease
|
|
404
|
+
self._taken = False
|
|
405
|
+
|
|
406
|
+
def __getattr__(self, name):
|
|
407
|
+
return getattr(self.manager, name)
|
|
408
|
+
|
|
409
|
+
def acquire(self, device=None, runtime=None, *, timeout=600, cancelled=None):
|
|
410
|
+
if cancelled is not None and cancelled():
|
|
411
|
+
from ..errors import SimError
|
|
412
|
+
from ..verify.actions import ActionNotDispatched
|
|
413
|
+
raise ActionNotDispatched(SimError("stopped", "The suite was stopped before this device ran."))
|
|
414
|
+
if self._taken:
|
|
415
|
+
raise RuntimeError("The phone lease was already handed to this run")
|
|
416
|
+
self._taken = True
|
|
417
|
+
return self.lease
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class BorrowedSimulator:
|
|
421
|
+
"""A phase can release its run lease without releasing the scenario's root simulator lease."""
|
|
422
|
+
|
|
423
|
+
def __init__(self, manager, root_lease, stop):
|
|
424
|
+
self.manager, self.root_lease, self.stop = manager, root_lease, stop
|
|
425
|
+
self.active = True
|
|
426
|
+
|
|
427
|
+
def __getattr__(self, name):
|
|
428
|
+
return getattr(self.manager, name)
|
|
429
|
+
|
|
430
|
+
def acquire(self, device=None, runtime=None, *, timeout=600, cancelled=None):
|
|
431
|
+
if not self.active or self.stop.is_set() or cancelled is not None and cancelled():
|
|
432
|
+
from ..errors import SimError
|
|
433
|
+
from ..verify.actions import ActionNotDispatched
|
|
434
|
+
raise ActionNotDispatched(SimError("stopped", "The scenario was stopped before this actor ran."))
|
|
435
|
+
from ..sim import SimLease
|
|
436
|
+
return SimLease(self.root_lease.target)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def suite_simulators(parallel, progress):
|
|
440
|
+
# Each run holds its simulator's lease, so two runs never share one.
|
|
441
|
+
from ..sim import SimulatorManager
|
|
442
|
+
|
|
443
|
+
class SuiteSimulators(SimulatorManager):
|
|
444
|
+
@property
|
|
445
|
+
def max_sims(self):
|
|
446
|
+
return max(SimulatorManager.max_sims.fget(self), parallel)
|
|
447
|
+
|
|
448
|
+
return SuiteSimulators(progress=progress)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class Recording:
|
|
452
|
+
def __init__(self, manager, path, *, popen=subprocess.Popen, log=None, clock=time.monotonic):
|
|
453
|
+
self._manager, self.path, self._popen = manager, Path(path), popen
|
|
454
|
+
self._log = log or (lambda line: None)
|
|
455
|
+
self._clock = clock
|
|
456
|
+
self.started = None # time.monotonic() when the recording began
|
|
457
|
+
|
|
458
|
+
def __getattr__(self, name):
|
|
459
|
+
return getattr(self._manager, name)
|
|
460
|
+
|
|
461
|
+
def acquire(self, *args, **kwargs):
|
|
462
|
+
lease = self._manager.acquire(*args, **kwargs)
|
|
463
|
+
process = None
|
|
464
|
+
try:
|
|
465
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
466
|
+
process = self._popen(video_argv(lease.target.udid, self.path), stdin=subprocess.DEVNULL,
|
|
467
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True)
|
|
468
|
+
self.started = self._clock()
|
|
469
|
+
except Exception as error:
|
|
470
|
+
self._log(f"no screen recording ({type(error).__name__})")
|
|
471
|
+
except BaseException:
|
|
472
|
+
lease.release()
|
|
473
|
+
raise
|
|
474
|
+
return RecordedLease(lease, process)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
class RecordedLease:
|
|
478
|
+
def __init__(self, lease, process):
|
|
479
|
+
self._lease, self._process = lease, process
|
|
480
|
+
self.target = lease.target
|
|
481
|
+
|
|
482
|
+
def __getattr__(self, name):
|
|
483
|
+
return getattr(self._lease, name)
|
|
484
|
+
|
|
485
|
+
def release(self):
|
|
486
|
+
process, self._process = self._process, None
|
|
487
|
+
try:
|
|
488
|
+
if process is not None:
|
|
489
|
+
if isinstance(process, subprocess.Popen) or not callable(getattr(process, "stop", None)):
|
|
490
|
+
stop_recording(process)
|
|
491
|
+
else:
|
|
492
|
+
process.stop() # a phone recording (phone_video.Recorder) finishes its own file
|
|
493
|
+
finally:
|
|
494
|
+
self._lease.release()
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def video_argv(udid, path):
|
|
498
|
+
return ["xcrun", "simctl", "io", str(udid), "recordVideo", "--codec=h264", "--force", str(path)]
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def stop_recording(process, wait=VIDEO_STOP_WAIT):
|
|
502
|
+
"""SIGINT, which makes simctl finish the movie; a kill after ``wait`` seconds."""
|
|
503
|
+
try:
|
|
504
|
+
if process.poll() is None:
|
|
505
|
+
process.send_signal(signal.SIGINT)
|
|
506
|
+
try:
|
|
507
|
+
process.wait(timeout=wait)
|
|
508
|
+
except subprocess.TimeoutExpired:
|
|
509
|
+
process.kill()
|
|
510
|
+
process.wait(timeout=5)
|
|
511
|
+
except Exception:
|
|
512
|
+
pass
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
__all__ = ["Attempts", "Recording", "suite_simulators", "video_argv", "stop_recording"]
|