mobster-cli 0.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mobile_agent/__init__.py +4 -0
- mobile_agent/__main__.py +598 -0
- mobile_agent/accounts/__init__.py +1 -0
- mobile_agent/accounts/context.py +80 -0
- mobile_agent/accounts/mcp.py +55 -0
- mobile_agent/accounts/pacing.py +318 -0
- mobile_agent/accounts/register.py +78 -0
- mobile_agent/accounts/routes.py +212 -0
- mobile_agent/accounts/routing.py +69 -0
- mobile_agent/accounts/screen.py +273 -0
- mobile_agent/accounts/stats.py +54 -0
- mobile_agent/accounts/store.py +394 -0
- mobile_agent/agent_hooks.py +123 -0
- mobile_agent/ai_plans/__init__.py +7 -0
- mobile_agent/ai_plans/agents.py +96 -0
- mobile_agent/ai_plans/claude_code.py +208 -0
- mobile_agent/ai_plans/cli.py +400 -0
- mobile_agent/ai_plans/codex.py +258 -0
- mobile_agent/ai_plans/compat_chat.py +307 -0
- mobile_agent/ai_plans/device_login.py +164 -0
- mobile_agent/ai_plans/draft_client.py +68 -0
- mobile_agent/ai_plans/gemini.py +137 -0
- mobile_agent/ai_plans/instructions.py +65 -0
- mobile_agent/ai_plans/mcp_http.py +160 -0
- mobile_agent/ai_plans/phone_tools.py +892 -0
- mobile_agent/ai_plans/providers.py +235 -0
- mobile_agent/ai_plans/routes.py +160 -0
- mobile_agent/ai_plans/runner.py +400 -0
- mobile_agent/ai_plans/vault.py +114 -0
- mobile_agent/alerts.py +337 -0
- mobile_agent/android/__init__.py +18 -0
- mobile_agent/android/adb.py +268 -0
- mobile_agent/android/conditions.py +128 -0
- mobile_agent/android/doctor.py +54 -0
- mobile_agent/android/driver.py +137 -0
- mobile_agent/android/emulator.py +216 -0
- mobile_agent/android/logs.py +85 -0
- mobile_agent/android/manager.py +225 -0
- mobile_agent/android/records.py +31 -0
- mobile_agent/android/tree.py +96 -0
- mobile_agent/answer_shapes.py +230 -0
- mobile_agent/api_devices.py +333 -0
- mobile_agent/api_errors.py +17 -0
- mobile_agent/api_get.py +249 -0
- mobile_agent/api_http.py +467 -0
- mobile_agent/api_post.py +453 -0
- mobile_agent/api_routes.py +173 -0
- mobile_agent/app_api/__init__.py +4 -0
- mobile_agent/app_api/cli.py +151 -0
- mobile_agent/app_api/endpoints.py +193 -0
- mobile_agent/app_api/mcp.py +54 -0
- mobile_agent/app_api/openapi.py +146 -0
- mobile_agent/app_api/reading.py +172 -0
- mobile_agent/app_api/register.py +10 -0
- mobile_agent/app_api/screen.py +216 -0
- mobile_agent/app_api/service.py +382 -0
- mobile_agent/app_api/sessions.py +404 -0
- mobile_agent/app_api/tokens.py +167 -0
- mobile_agent/app_choice.py +131 -0
- mobile_agent/app_icons.py +183 -0
- mobile_agent/app_killer.py +69 -0
- mobile_agent/artifacts.py +606 -0
- mobile_agent/attach/__init__.py +13 -0
- mobile_agent/attach/cli.py +46 -0
- mobile_agent/attach/flutter.py +28 -0
- mobile_agent/attach/score.py +119 -0
- mobile_agent/attach/web.py +13 -0
- mobile_agent/attachments/__init__.py +1 -0
- mobile_agent/attachments/agent.py +418 -0
- mobile_agent/attachments/extract.py +217 -0
- mobile_agent/attachments/mcp.py +104 -0
- mobile_agent/attachments/register.py +73 -0
- mobile_agent/attachments/routes.py +326 -0
- mobile_agent/attachments/service.py +170 -0
- mobile_agent/attachments/sniff.py +152 -0
- mobile_agent/attachments/store.py +441 -0
- mobile_agent/audit_verify.py +436 -0
- mobile_agent/bench/README.md +410 -0
- mobile_agent/bench/__init__.py +0 -0
- mobile_agent/bench/__main__.py +394 -0
- mobile_agent/bench/ab.py +168 -0
- mobile_agent/bench/actuation.py +368 -0
- mobile_agent/bench/agents/__init__.py +9 -0
- mobile_agent/bench/agents/base.py +105 -0
- mobile_agent/bench/agents/plan_clients.py +245 -0
- mobile_agent/bench/agents/smart.py +533 -0
- mobile_agent/bench/checks.py +358 -0
- mobile_agent/bench/decide_gate.py +214 -0
- mobile_agent/bench/diagnose.py +181 -0
- mobile_agent/bench/evals/__init__.py +14 -0
- mobile_agent/bench/evals/build.py +565 -0
- mobile_agent/bench/evals/checkpoint.py +214 -0
- mobile_agent/bench/evals/irt.py +219 -0
- mobile_agent/bench/evals/l0.py +647 -0
- mobile_agent/bench/evals/l1.py +866 -0
- mobile_agent/bench/evals/models.py +124 -0
- mobile_agent/bench/evals/pairwise.py +189 -0
- mobile_agent/bench/evals/proxy.py +90 -0
- mobile_agent/bench/evals/sets.py +149 -0
- mobile_agent/bench/evals/traces.py +446 -0
- mobile_agent/bench/fixtures.py +176 -0
- mobile_agent/bench/guard_replay.py +129 -0
- mobile_agent/bench/h2-manifest.json +20 -0
- mobile_agent/bench/h3-manifest.json +20 -0
- mobile_agent/bench/heldout-manifest.json +32 -0
- mobile_agent/bench/heldout.py +375 -0
- mobile_agent/bench/iosworld.py +901 -0
- mobile_agent/bench/iosworld_stats.py +510 -0
- mobile_agent/bench/llm_units.py +116 -0
- mobile_agent/bench/manifest.json +93 -0
- mobile_agent/bench/metrics.py +66 -0
- mobile_agent/bench/phone.py +44 -0
- mobile_agent/bench/replay.py +172 -0
- mobile_agent/bench/report.py +207 -0
- mobile_agent/bench/runner.py +456 -0
- mobile_agent/bench/safety.py +310 -0
- mobile_agent/bench/stream_timing.py +213 -0
- mobile_agent/bench/suite.py +444 -0
- mobile_agent/bench/sweep.py +176 -0
- mobile_agent/bench/tape.py +798 -0
- mobile_agent/bench/tape_replay.py +88 -0
- mobile_agent/bench/trace_evals.py +128 -0
- mobile_agent/bench/trace_view.py +81 -0
- mobile_agent/bench/truth/iphone15pro.json +138 -0
- mobile_agent/bench/truth.py +340 -0
- mobile_agent/bench/tuning.py +543 -0
- mobile_agent/bench/tuning_analysis.py +656 -0
- mobile_agent/bench/vague-heldout-manifest.json +12 -0
- mobile_agent/bench/vague_rubric.py +135 -0
- mobile_agent/bench/vague_run.py +243 -0
- mobile_agent/brand.py +454 -0
- mobile_agent/brand_fonts.py +756 -0
- mobile_agent/build_identity.py +21 -0
- mobile_agent/cable_bt.py +623 -0
- mobile_agent/cable_bt_runtime.py +128 -0
- mobile_agent/catalog.py +69 -0
- mobile_agent/chart_svg.py +415 -0
- mobile_agent/chat_here.py +189 -0
- mobile_agent/cli_constants.py +5 -0
- mobile_agent/cli_parser.py +611 -0
- mobile_agent/cli_run.py +115 -0
- mobile_agent/compiled_read/__init__.py +21 -0
- mobile_agent/compiled_read/delta.py +89 -0
- mobile_agent/compiled_read/extractors.py +202 -0
- mobile_agent/compiled_read/routes.py +56 -0
- mobile_agent/completion.py +267 -0
- mobile_agent/compose.py +48 -0
- mobile_agent/config.py +114 -0
- mobile_agent/connectors/__init__.py +1 -0
- mobile_agent/connectors/api_session.py +76 -0
- mobile_agent/connectors/callback_page.py +146 -0
- mobile_agent/connectors/client.py +582 -0
- mobile_agent/connectors/github_app.py +55 -0
- mobile_agent/connectors/graph_api.py +396 -0
- mobile_agent/connectors/imports.py +170 -0
- mobile_agent/connectors/issue.py +66 -0
- mobile_agent/connectors/keystore.py +85 -0
- mobile_agent/connectors/marks.py +222 -0
- mobile_agent/connectors/oauth.py +476 -0
- mobile_agent/connectors/picker.py +228 -0
- mobile_agent/connectors/presets.py +591 -0
- mobile_agent/connectors/register.py +48 -0
- mobile_agent/connectors/routes.py +153 -0
- mobile_agent/connectors/service.py +966 -0
- mobile_agent/connectors/shared.py +136 -0
- mobile_agent/connectors/skill.py +446 -0
- mobile_agent/connectors/slack_api.py +269 -0
- mobile_agent/connectors/steps.py +92 -0
- mobile_agent/connectors/store.py +144 -0
- mobile_agent/connectors/vault.py +72 -0
- mobile_agent/console.py +178 -0
- mobile_agent/content_queue/__init__.py +1 -0
- mobile_agent/content_queue/client.py +263 -0
- mobile_agent/content_queue/importing.py +153 -0
- mobile_agent/content_queue/media_check.py +224 -0
- mobile_agent/content_queue/prompt.py +64 -0
- mobile_agent/content_queue/register.py +91 -0
- mobile_agent/content_queue/routes.py +370 -0
- mobile_agent/content_queue/runner.py +475 -0
- mobile_agent/content_queue/store.py +521 -0
- mobile_agent/contract.py +996 -0
- mobile_agent/contract_asks.py +248 -0
- mobile_agent/contract_prompt.py +45 -0
- mobile_agent/contract_screen.py +981 -0
- mobile_agent/contract_view.py +84 -0
- mobile_agent/corpus/__init__.py +26 -0
- mobile_agent/corpus/cli.py +63 -0
- mobile_agent/corpus/consent.py +55 -0
- mobile_agent/corpus/listener.py +76 -0
- mobile_agent/corpus/register.py +9 -0
- mobile_agent/corpus/skeleton.py +72 -0
- mobile_agent/corpus/store.py +143 -0
- mobile_agent/costs.py +161 -0
- mobile_agent/decider_scope.py +40 -0
- mobile_agent/decision_provider.py +189 -0
- mobile_agent/deeplinks.py +352 -0
- mobile_agent/device_info.py +116 -0
- mobile_agent/device_manager.py +978 -0
- mobile_agent/device_status.py +195 -0
- mobile_agent/device_targets.py +217 -0
- mobile_agent/devices.py +676 -0
- mobile_agent/devkit/__init__.py +64 -0
- mobile_agent/devkit/account_cli.py +90 -0
- mobile_agent/devkit/asc.py +562 -0
- mobile_agent/devkit/asc_cli.py +214 -0
- mobile_agent/devkit/ci.py +737 -0
- mobile_agent/devkit/ci_cli.py +196 -0
- mobile_agent/devkit/ci_hosts.py +194 -0
- mobile_agent/devkit/es256.py +197 -0
- mobile_agent/devkit/expo.py +440 -0
- mobile_agent/devkit/expo_cli.py +405 -0
- mobile_agent/devkit/expo_dev.py +203 -0
- mobile_agent/devkit/expo_routes.py +90 -0
- mobile_agent/devkit/fixtures_cli.py +133 -0
- mobile_agent/devkit/fixtures_routes.py +82 -0
- mobile_agent/devkit/links.py +375 -0
- mobile_agent/devkit/links_cli.py +123 -0
- mobile_agent/devkit/links_routes.py +83 -0
- mobile_agent/devkit/nightly.py +152 -0
- mobile_agent/devkit/shots.py +203 -0
- mobile_agent/devkit/testflight.py +155 -0
- mobile_agent/devkit/testflight_cli.py +127 -0
- mobile_agent/devkit/testflight_routes.py +111 -0
- mobile_agent/devkit/xcode.py +236 -0
- mobile_agent/devkit/xcode_cli.py +101 -0
- mobile_agent/devtools.py +419 -0
- mobile_agent/doctor.py +509 -0
- mobile_agent/doctor_fix.py +467 -0
- mobile_agent/downloads.py +55 -0
- mobile_agent/drivers.py +793 -0
- mobile_agent/dry_run.py +102 -0
- mobile_agent/effect_ledger.py +83 -0
- mobile_agent/engine_health.py +378 -0
- mobile_agent/engine_hooks.py +96 -0
- mobile_agent/engines.py +408 -0
- mobile_agent/env_file.py +63 -0
- mobile_agent/env_secrets.py +149 -0
- mobile_agent/errors.py +15 -0
- mobile_agent/evals/__init__.py +3 -0
- mobile_agent/evals/app_survey.py +149 -0
- mobile_agent/evals/development/__init__.py +1 -0
- mobile_agent/evals/development/score.py +276 -0
- mobile_agent/evals/latency_report.py +312 -0
- mobile_agent/evals/offline_report.py +374 -0
- mobile_agent/evals/oracles.py +298 -0
- mobile_agent/evals/redteam/__init__.py +15 -0
- mobile_agent/evals/redteam/__main__.py +72 -0
- mobile_agent/evals/redteam/cases.py +730 -0
- mobile_agent/evals/redteam/harness.py +604 -0
- mobile_agent/evals/redteam/live.py +139 -0
- mobile_agent/evals/redteam/plain.py +85 -0
- mobile_agent/evals/vision/__init__.py +0 -0
- mobile_agent/evals/vision/images/dog_001.jpg +0 -0
- mobile_agent/evals/vision/images/dog_002.jpg +0 -0
- mobile_agent/evals/vision/images/dog_003.jpg +0 -0
- mobile_agent/evals/vision/images/dog_004.jpg +0 -0
- mobile_agent/evals/vision/images/dog_005.jpg +0 -0
- mobile_agent/evals/vision/images/dog_006.jpg +0 -0
- mobile_agent/evals/vision/images/dog_007.jpg +0 -0
- mobile_agent/evals/vision/images/dog_010.jpg +0 -0
- mobile_agent/evals/vision/images/dog_011.jpg +0 -0
- mobile_agent/evals/vision/images/dog_013.jpg +0 -0
- mobile_agent/evals/vision/images/dog_014.jpg +0 -0
- mobile_agent/evals/vision/images/dog_015.jpg +0 -0
- mobile_agent/evals/vision/images/dog_016.jpg +0 -0
- mobile_agent/evals/vision/images/dog_017.jpg +0 -0
- mobile_agent/evals/vision/images/dog_019.jpg +0 -0
- mobile_agent/evals/vision/images/dog_020.jpg +0 -0
- mobile_agent/evals/vision/images/dog_021.jpg +0 -0
- mobile_agent/evals/vision/images/dog_022.jpg +0 -0
- mobile_agent/evals/vision/images/dog_023.jpg +0 -0
- mobile_agent/evals/vision/images/dog_024.jpg +0 -0
- mobile_agent/evals/vision/images/dog_026.jpg +0 -0
- mobile_agent/evals/vision/images/dog_027.jpg +0 -0
- mobile_agent/evals/vision/images/dog_034.jpg +0 -0
- mobile_agent/evals/vision/images/dog_035.jpg +0 -0
- mobile_agent/evals/vision/images/dog_038.jpg +0 -0
- mobile_agent/evals/vision/images/dog_042.jpg +0 -0
- mobile_agent/evals/vision/images/dog_047.jpg +0 -0
- mobile_agent/evals/vision/images/dog_052.jpg +0 -0
- mobile_agent/evals/vision/images/dog_058.jpg +0 -0
- mobile_agent/evals/vision/images/dog_062.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_000.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_010.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_011.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_015.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_016.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_017.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_019.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_021.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_025.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_026.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_027.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_030.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_032.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_033.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_034.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_035.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_036.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_037.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_039.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_040.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_041.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_042.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_043.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_044.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_046.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_047.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_048.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_049.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_051.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_053.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_054.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_055.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_058.jpg +0 -0
- mobile_agent/evals/vision/images/eyes_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_002.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_003.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_004.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_007.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_009.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_010.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_011.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_012.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_013.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_014.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_016.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_017.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_023.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_027.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_029.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_032.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_033.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_036.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_041.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_043.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_047.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_049.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_051.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_053.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_056.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_057.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_058.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_059.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_060.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_061.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_062.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_063.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_064.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_065.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_066.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_067.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_068.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_069.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_070.jpg +0 -0
- mobile_agent/evals/vision/images/receipt_071.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_000.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_001.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_002.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_004.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_005.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_008.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_009.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_010.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_012.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_013.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_015.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_016.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_017.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_018.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_021.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_022.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_023.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_024.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_030.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_031.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_032.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_033.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_034.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_036.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_038.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_039.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_040.jpg +0 -0
- mobile_agent/evals/vision/images/sunglasses_041.jpg +0 -0
- mobile_agent/evals/vision/manifest.json +2355 -0
- mobile_agent/eventbus.py +110 -0
- mobile_agent/explain.py +34 -0
- mobile_agent/failure_line.py +197 -0
- mobile_agent/fanout.py +663 -0
- mobile_agent/feedback/__init__.py +2 -0
- mobile_agent/feedback/bundle.py +204 -0
- mobile_agent/feedback/outbox.py +196 -0
- mobile_agent/feedback/register.py +9 -0
- mobile_agent/feedback/routes.py +177 -0
- mobile_agent/fleet.py +841 -0
- mobile_agent/fleet_health.py +729 -0
- mobile_agent/fleet_phone.py +186 -0
- mobile_agent/frontier/__init__.py +79 -0
- mobile_agent/frontier/act.py +380 -0
- mobile_agent/frontier/actions.py +70 -0
- mobile_agent/frontier/agent.py +256 -0
- mobile_agent/frontier/approvals.py +366 -0
- mobile_agent/frontier/hooks.py +199 -0
- mobile_agent/frontier/interrupts.py +125 -0
- mobile_agent/frontier/observe.py +331 -0
- mobile_agent/frontier/phases.py +381 -0
- mobile_agent/frontier/pipeline.py +61 -0
- mobile_agent/frontier/prompt.py +384 -0
- mobile_agent/frontier/protocol.py +91 -0
- mobile_agent/frontier/safety.py +467 -0
- mobile_agent/frontier/saved.py +375 -0
- mobile_agent/frontier/screen.py +419 -0
- mobile_agent/frontier/skills.py +147 -0
- mobile_agent/frontier/stages/__init__.py +1 -0
- mobile_agent/frontier/stages/after.py +192 -0
- mobile_agent/frontier/stages/approve.py +138 -0
- mobile_agent/frontier/stages/controls.py +115 -0
- mobile_agent/frontier/stages/ends.py +102 -0
- mobile_agent/frontier/stages/gate.py +92 -0
- mobile_agent/frontier/stages/guards.py +132 -0
- mobile_agent/frontier/stages/points.py +155 -0
- mobile_agent/frontier/stages/send.py +177 -0
- mobile_agent/frontier/turn.py +151 -0
- mobile_agent/full_disk_access.py +83 -0
- mobile_agent/gemini.py +69 -0
- mobile_agent/genuine.py +120 -0
- mobile_agent/graph/__init__.py +4 -0
- mobile_agent/graph/catalog.py +789 -0
- mobile_agent/graph/chain.py +306 -0
- mobile_agent/graph/dispatch.py +98 -0
- mobile_agent/graph/draft.py +373 -0
- mobile_agent/graph/edit.py +76 -0
- mobile_agent/graph/estimate.py +189 -0
- mobile_agent/graph/expr.py +562 -0
- mobile_agent/graph/functions.py +271 -0
- mobile_agent/graph/infer.py +144 -0
- mobile_agent/graph/nodes.py +936 -0
- mobile_agent/graph/phone_steps.py +178 -0
- mobile_agent/graph/rerun.py +327 -0
- mobile_agent/graph/run.py +899 -0
- mobile_agent/graph/schema.py +842 -0
- mobile_agent/graph/state.py +161 -0
- mobile_agent/graph/values.py +198 -0
- mobile_agent/harness/__init__.py +2 -0
- mobile_agent/harness/checkpoints.py +468 -0
- mobile_agent/harness/control.py +101 -0
- mobile_agent/harness/prewarm.py +213 -0
- mobile_agent/harness/register.py +43 -0
- mobile_agent/harness/routes.py +114 -0
- mobile_agent/harness/settings.py +66 -0
- mobile_agent/harness/tools/__init__.py +67 -0
- mobile_agent/harness/tools/ask_user.py +102 -0
- mobile_agent/harness/tools/watch.py +120 -0
- mobile_agent/harness_api.py +815 -0
- mobile_agent/hedge.py +203 -0
- mobile_agent/help_topics.py +305 -0
- mobile_agent/hints.py +248 -0
- mobile_agent/history.py +69 -0
- mobile_agent/home.py +214 -0
- mobile_agent/http_pool.py +175 -0
- mobile_agent/image_policy.py +139 -0
- mobile_agent/inbox.py +148 -0
- mobile_agent/installed_apps.py +344 -0
- mobile_agent/integrations/__init__.py +1 -0
- mobile_agent/integrations/cli.py +472 -0
- mobile_agent/integrations/client_files.py +65 -0
- mobile_agent/integrations/clients.py +998 -0
- mobile_agent/integrations/doctor.py +185 -0
- mobile_agent/integrations/jsonc.py +383 -0
- mobile_agent/integrations/routes.py +135 -0
- mobile_agent/integrations/rules.py +112 -0
- mobile_agent/integrations/server_options.py +46 -0
- mobile_agent/integrations/skill.py +152 -0
- mobile_agent/integrations/skill_files.py +10 -0
- mobile_agent/integrations/textedit.py +309 -0
- mobile_agent/intents/__init__.py +17 -0
- mobile_agent/intents/atlas.py +216 -0
- mobile_agent/intents/callback.py +44 -0
- mobile_agent/intents/cli.py +55 -0
- mobile_agent/intents/harvest.py +91 -0
- mobile_agent/intents/plan.py +93 -0
- mobile_agent/ios/__init__.py +1 -0
- mobile_agent/ios/guide.py +16 -0
- mobile_agent/ios/kb.py +212 -0
- mobile_agent/ios/kb_data.py +1588 -0
- mobile_agent/ios/mcp.py +44 -0
- mobile_agent/ios/register.py +8 -0
- mobile_agent/ios/system.py +369 -0
- mobile_agent/journal.py +531 -0
- mobile_agent/keychain_acl.py +374 -0
- mobile_agent/keys.py +395 -0
- mobile_agent/latency_overlap.py +36 -0
- mobile_agent/latency_trace.py +170 -0
- mobile_agent/learned/__init__.py +15 -0
- mobile_agent/learned/app_path.py +160 -0
- mobile_agent/learned/code_read.py +38 -0
- mobile_agent/learned/parts.py +546 -0
- mobile_agent/learned/programs.py +691 -0
- mobile_agent/learned/register.py +171 -0
- mobile_agent/learned/routes.py +61 -0
- mobile_agent/learned/taught.py +329 -0
- mobile_agent/learned/teach.py +538 -0
- mobile_agent/learned/workflows.py +647 -0
- mobile_agent/learned/zero.py +41 -0
- mobile_agent/links.py +37 -0
- mobile_agent/listen.py +607 -0
- mobile_agent/lock_spike.py +278 -0
- mobile_agent/lockscreen.py +526 -0
- mobile_agent/login.py +354 -0
- mobile_agent/longform/__init__.py +15 -0
- mobile_agent/longform/ads.py +191 -0
- mobile_agent/longform/blocks.py +117 -0
- mobile_agent/longform/budget.py +265 -0
- mobile_agent/longform/context.py +285 -0
- mobile_agent/longform/costs.py +93 -0
- mobile_agent/longform/digest.py +145 -0
- mobile_agent/longform/extract.py +508 -0
- mobile_agent/longform/feed.py +505 -0
- mobile_agent/longform/followup.py +168 -0
- mobile_agent/longform/frames.py +82 -0
- mobile_agent/longform/identity.py +87 -0
- mobile_agent/longform/inbox.py +221 -0
- mobile_agent/longform/items.py +425 -0
- mobile_agent/longform/models.py +115 -0
- mobile_agent/longform/notebook.py +329 -0
- mobile_agent/longform/plan.py +614 -0
- mobile_agent/longform/recall.py +195 -0
- mobile_agent/longform/receipts.py +81 -0
- mobile_agent/longform/register.py +124 -0
- mobile_agent/longform/report.py +343 -0
- mobile_agent/longform/routes.py +102 -0
- mobile_agent/longform/session.py +644 -0
- mobile_agent/longform/store.py +146 -0
- mobile_agent/longform/synthesis.py +347 -0
- mobile_agent/mac_tools.py +223 -0
- mobile_agent/manual_control.py +143 -0
- mobile_agent/maps.py +149 -0
- mobile_agent/marks.py +92 -0
- mobile_agent/mcp_server/__init__.py +3 -0
- mobile_agent/mcp_server/__main__.py +29 -0
- mobile_agent/mcp_server/action_journal.py +24 -0
- mobile_agent/mcp_server/action_tools.py +638 -0
- mobile_agent/mcp_server/approvals.py +219 -0
- mobile_agent/mcp_server/catalog.py +92 -0
- mobile_agent/mcp_server/cli.py +194 -0
- mobile_agent/mcp_server/codes.py +147 -0
- mobile_agent/mcp_server/device_tools.py +306 -0
- mobile_agent/mcp_server/direct.py +272 -0
- mobile_agent/mcp_server/expo_tools.py +105 -0
- mobile_agent/mcp_server/phone_tools.py +510 -0
- mobile_agent/mcp_server/protocol.py +364 -0
- mobile_agent/mcp_server/registry.py +55 -0
- mobile_agent/mcp_server/run_tools.py +531 -0
- mobile_agent/mcp_server/schemas.py +425 -0
- mobile_agent/mcp_server/screen_tools.py +168 -0
- mobile_agent/mcp_server/session.py +469 -0
- mobile_agent/mcp_server/support.py +145 -0
- mobile_agent/mcp_server/tools.py +493 -0
- mobile_agent/memory/__init__.py +9 -0
- mobile_agent/memory/cli.py +207 -0
- mobile_agent/memory/extract.py +191 -0
- mobile_agent/memory/listener.py +100 -0
- mobile_agent/memory/model.py +144 -0
- mobile_agent/memory/project.py +96 -0
- mobile_agent/memory/provider.py +127 -0
- mobile_agent/memory/register.py +21 -0
- mobile_agent/memory/retrieval.py +172 -0
- mobile_agent/memory/routes.py +216 -0
- mobile_agent/memory/runs.py +124 -0
- mobile_agent/memory/store.py +806 -0
- mobile_agent/memory/writer.py +278 -0
- mobile_agent/mirroring.py +585 -0
- mobile_agent/mirroring_runtime.py +221 -0
- mobile_agent/model_clients.py +604 -0
- mobile_agent/narrate.py +813 -0
- mobile_agent/native_helpers.py +84 -0
- mobile_agent/netxray/__init__.py +1 -0
- mobile_agent/netxray/analyze.py +322 -0
- mobile_agent/netxray/attribution.py +62 -0
- mobile_agent/netxray/capture.py +88 -0
- mobile_agent/netxray/cli.py +142 -0
- mobile_agent/netxray/job.py +219 -0
- mobile_agent/netxray/mobster-pcap.c +560 -0
- mobile_agent/netxray/packets.py +410 -0
- mobile_agent/netxray/pcapng.py +223 -0
- mobile_agent/netxray/register.py +93 -0
- mobile_agent/netxray/report.py +214 -0
- mobile_agent/netxray/store.py +48 -0
- mobile_agent/netxray/summary.py +157 -0
- mobile_agent/netxray/target.py +67 -0
- mobile_agent/netxray/tool.py +66 -0
- mobile_agent/netxray/trackers.py +194 -0
- mobile_agent/notify_triggers/__init__.py +1 -0
- mobile_agent/notify_triggers/rules.py +39 -0
- mobile_agent/notify_triggers/sources.py +315 -0
- mobile_agent/notify_triggers/watch.py +135 -0
- mobile_agent/offers.py +86 -0
- mobile_agent/orchestrate.py +215 -0
- mobile_agent/outlets/__init__.py +1 -0
- mobile_agent/outlets/connect.py +249 -0
- mobile_agent/outlets/folder.py +180 -0
- mobile_agent/outlets/hooks.py +453 -0
- mobile_agent/outlets/inbound.py +472 -0
- mobile_agent/outlets/inbound_reply.py +57 -0
- mobile_agent/outlets/inbound_routes.py +81 -0
- mobile_agent/outlets/kinds.py +308 -0
- mobile_agent/outlets/mail.py +110 -0
- mobile_agent/outlets/net.py +86 -0
- mobile_agent/outlets/notion.py +244 -0
- mobile_agent/outlets/register.py +56 -0
- mobile_agent/outlets/routes.py +135 -0
- mobile_agent/outlets/service.py +580 -0
- mobile_agent/outlets/sheets.py +169 -0
- mobile_agent/outlets/slack.py +177 -0
- mobile_agent/outlets/store.py +194 -0
- mobile_agent/outlets/vault.py +80 -0
- mobile_agent/outlets/webhook.py +316 -0
- mobile_agent/output_contract.py +141 -0
- mobile_agent/owner_presence.py +211 -0
- mobile_agent/ownership.py +219 -0
- mobile_agent/packs/__init__.py +1 -0
- mobile_agent/packs/calendar.py +243 -0
- mobile_agent/packs/context.py +68 -0
- mobile_agent/packs/core.py +757 -0
- mobile_agent/packs/front_door.py +316 -0
- mobile_agent/packs/heal.py +214 -0
- mobile_agent/packs/health.py +122 -0
- mobile_agent/packs/insights.py +76 -0
- mobile_agent/packs/instagram.py +640 -0
- mobile_agent/packs/linkedin.py +106 -0
- mobile_agent/packs/maps.py +196 -0
- mobile_agent/packs/mcp.py +149 -0
- mobile_agent/packs/messages.py +270 -0
- mobile_agent/packs/notes.py +226 -0
- mobile_agent/packs/photos.py +166 -0
- mobile_agent/packs/pins.py +47 -0
- mobile_agent/packs/reddit.py +110 -0
- mobile_agent/packs/register.py +276 -0
- mobile_agent/packs/rows.py +108 -0
- mobile_agent/packs/safari.py +143 -0
- mobile_agent/packs/settings.py +253 -0
- mobile_agent/packs/social.py +300 -0
- mobile_agent/packs/threads.py +97 -0
- mobile_agent/packs/tiktok.py +455 -0
- mobile_agent/packs/tools.py +290 -0
- mobile_agent/packs/x.py +127 -0
- mobile_agent/packs/youtube.py +199 -0
- mobile_agent/parity.py +270 -0
- mobile_agent/paths.py +99 -0
- mobile_agent/perception.py +470 -0
- mobile_agent/permissions/__init__.py +32 -0
- mobile_agent/permissions/consequence.py +535 -0
- mobile_agent/permissions/exceptions.py +99 -0
- mobile_agent/permissions/gate.py +316 -0
- mobile_agent/permissions/outside.py +316 -0
- mobile_agent/permissions/policy.py +288 -0
- mobile_agent/permissions/project_policy.py +411 -0
- mobile_agent/permissions/register.py +148 -0
- mobile_agent/permissions/rules.py +291 -0
- mobile_agent/phone_io/__init__.py +146 -0
- mobile_agent/phone_io/cli.py +427 -0
- mobile_agent/phone_io/clipboard.py +78 -0
- mobile_agent/phone_io/controls.py +78 -0
- mobile_agent/phone_io/files.py +289 -0
- mobile_agent/phone_io/handoff.py +123 -0
- mobile_agent/phone_io/icloud.py +118 -0
- mobile_agent/phone_io/install.py +188 -0
- mobile_agent/phone_io/network_capture.py +188 -0
- mobile_agent/phone_io/network_cli.py +138 -0
- mobile_agent/phone_io/network_profiles.py +138 -0
- mobile_agent/phone_io/photos.py +512 -0
- mobile_agent/phone_io/recording.py +115 -0
- mobile_agent/phone_io/secrets.py +146 -0
- mobile_agent/phone_io/transfer.py +895 -0
- mobile_agent/phone_io/tunnel.py +129 -0
- mobile_agent/phone_ready.py +91 -0
- mobile_agent/pinning.py +158 -0
- mobile_agent/plan_gate.py +807 -0
- mobile_agent/plugins/__init__.py +3 -0
- mobile_agent/plugins/install.py +163 -0
- mobile_agent/plugins/manifest.py +242 -0
- mobile_agent/plugins/register.py +15 -0
- mobile_agent/plugins/routes.py +82 -0
- mobile_agent/plugins/service.py +322 -0
- mobile_agent/plugins/skills.py +76 -0
- mobile_agent/plugins/store.py +79 -0
- mobile_agent/pool.py +285 -0
- mobile_agent/power_watch.py +275 -0
- mobile_agent/premium_model.py +22 -0
- mobile_agent/project_env.py +291 -0
- mobile_agent/project_env_cli.py +74 -0
- mobile_agent/project_env_routes.py +55 -0
- mobile_agent/projects/__init__.py +27 -0
- mobile_agent/projects/ci.py +332 -0
- mobile_agent/projects/clone.py +339 -0
- mobile_agent/projects/drafts.py +158 -0
- mobile_agent/projects/folder.py +436 -0
- mobile_agent/projects/github.py +131 -0
- mobile_agent/projects/register.py +31 -0
- mobile_agent/projects/revisions.py +253 -0
- mobile_agent/projects/routes.py +234 -0
- mobile_agent/projects/runs.py +297 -0
- mobile_agent/projects/service.py +695 -0
- mobile_agent/projects/setup.py +84 -0
- mobile_agent/projects/store.py +379 -0
- mobile_agent/projects/sync.py +380 -0
- mobile_agent/projects/workflow_files.py +270 -0
- mobile_agent/replay_core/__init__.py +26 -0
- mobile_agent/replay_core/checks.py +594 -0
- mobile_agent/replay_core/engine.py +596 -0
- mobile_agent/replay_core/heal.py +364 -0
- mobile_agent/replay_core/path.py +240 -0
- mobile_agent/replay_core/recorder.py +206 -0
- mobile_agent/replay_core/store.py +171 -0
- mobile_agent/result_blocks.py +941 -0
- mobile_agent/run.py +642 -0
- mobile_agent/run_delivery.py +123 -0
- mobile_agent/run_executor.py +200 -0
- mobile_agent/run_export.py +284 -0
- mobile_agent/run_gif.py +891 -0
- mobile_agent/runner_hints.py +86 -0
- mobile_agent/runner_renewal.py +205 -0
- mobile_agent/runtime_create.py +549 -0
- mobile_agent/runtime_driver.py +458 -0
- mobile_agent/runtime_limits.py +319 -0
- mobile_agent/runtime_queue.py +394 -0
- mobile_agent/runtime_runs.py +383 -0
- mobile_agent/runtime_status.py +181 -0
- mobile_agent/runtime_teach.py +295 -0
- mobile_agent/runtime_work.py +599 -0
- mobile_agent/screen_text.py +101 -0
- mobile_agent/secret_filter.py +113 -0
- mobile_agent/serve.py +160 -0
- mobile_agent/server.py +363 -0
- mobile_agent/service_support.py +42 -0
- mobile_agent/settle/__init__.py +1 -0
- mobile_agent/settle/predict.py +102 -0
- mobile_agent/settle/replay.py +92 -0
- mobile_agent/settle/store.py +93 -0
- mobile_agent/settle/stream.py +43 -0
- mobile_agent/setup_cli.py +225 -0
- mobile_agent/setup_errors.py +87 -0
- mobile_agent/setup_service.py +527 -0
- mobile_agent/share.py +104 -0
- mobile_agent/signing.py +184 -0
- mobile_agent/sim/__init__.py +6 -0
- mobile_agent/sim/__main__.py +26 -0
- mobile_agent/sim/api.py +952 -0
- mobile_agent/sim/apps.py +172 -0
- mobile_agent/sim/biometrics.py +84 -0
- mobile_agent/sim/cli.py +271 -0
- mobile_agent/sim/doctor.py +293 -0
- mobile_agent/sim/governor.py +90 -0
- mobile_agent/sim/holders.py +88 -0
- mobile_agent/sim/models.py +62 -0
- mobile_agent/sim/registry.py +161 -0
- mobile_agent/sim/simctl.py +269 -0
- mobile_agent/sim/wda.py +618 -0
- mobile_agent/sim/xcode.py +74 -0
- mobile_agent/skills/__init__.py +12 -0
- mobile_agent/skills/codes.py +597 -0
- mobile_agent/skills/notifications.py +281 -0
- mobile_agent/skills/open_url.py +127 -0
- mobile_agent/smart_events.py +588 -0
- mobile_agent/smart_result.py +490 -0
- mobile_agent/smart_run.py +413 -0
- mobile_agent/state.py +662 -0
- mobile_agent/states.py +217 -0
- mobile_agent/steering.py +70 -0
- mobile_agent/style.py +178 -0
- mobile_agent/supervised.py +234 -0
- mobile_agent/swift/helper_bt_hid.swift +534 -0
- mobile_agent/swift/helper_doc_text.swift +184 -0
- mobile_agent/swift/helper_draft_reply.swift +170 -0
- mobile_agent/swift/helper_map.swift +110 -0
- mobile_agent/swift/helper_mirroring.swift +1099 -0
- mobile_agent/swift/helper_mjpeg_mp4.swift +103 -0
- mobile_agent/swift/helper_pdf.swift +100 -0
- mobile_agent/swift/helper_phone_audio.swift +993 -0
- mobile_agent/swift/helper_phone_screen.swift +416 -0
- mobile_agent/swift/helper_screen_text.swift +24 -0
- mobile_agent/swift/helper_transfer.swift +246 -0
- mobile_agent/swift/ocr.swift +22 -0
- mobile_agent/task_policy.py +337 -0
- mobile_agent/team_signing/__init__.py +34 -0
- mobile_agent/team_signing/apple_ca.py +37 -0
- mobile_agent/team_signing/bundle.py +136 -0
- mobile_agent/team_signing/cli.py +208 -0
- mobile_agent/team_signing/identity.py +248 -0
- mobile_agent/team_signing/ledger.py +105 -0
- mobile_agent/team_signing/prebuilt.py +61 -0
- mobile_agent/team_signing/resign.py +256 -0
- mobile_agent/team_signing/service.py +382 -0
- mobile_agent/team_signing/tooling.py +243 -0
- mobile_agent/team_signing/writes.py +268 -0
- mobile_agent/terminal_image.py +81 -0
- mobile_agent/testkit/__init__.py +1 -0
- mobile_agent/testkit/a11y_baseline.py +132 -0
- mobile_agent/testkit/api.py +403 -0
- mobile_agent/testkit/capsule.py +400 -0
- mobile_agent/testkit/capsule_cli.py +57 -0
- mobile_agent/testkit/cli.py +948 -0
- mobile_agent/testkit/cli_arguments.py +193 -0
- mobile_agent/testkit/comparison.py +288 -0
- mobile_agent/testkit/comparison_cli.py +41 -0
- mobile_agent/testkit/discover.py +257 -0
- mobile_agent/testkit/editor.py +243 -0
- mobile_agent/testkit/execute.py +515 -0
- mobile_agent/testkit/github_checks.py +248 -0
- mobile_agent/testkit/gitref.py +66 -0
- mobile_agent/testkit/heal_review.py +176 -0
- mobile_agent/testkit/health.py +480 -0
- mobile_agent/testkit/history.py +255 -0
- mobile_agent/testkit/hosted_results.py +321 -0
- mobile_agent/testkit/input_files.py +173 -0
- mobile_agent/testkit/jobs.py +285 -0
- mobile_agent/testkit/junit_verdict.py +33 -0
- mobile_agent/testkit/maestro.py +802 -0
- mobile_agent/testkit/mcp.py +562 -0
- mobile_agent/testkit/mcp_comparison.py +88 -0
- mobile_agent/testkit/minimize.py +471 -0
- mobile_agent/testkit/minimizer_cli.py +48 -0
- mobile_agent/testkit/notify.py +212 -0
- mobile_agent/testkit/phone_video.py +191 -0
- mobile_agent/testkit/pr.py +491 -0
- mobile_agent/testkit/pr_report.py +562 -0
- mobile_agent/testkit/prbuild.py +148 -0
- mobile_agent/testkit/proof_report.py +55 -0
- mobile_agent/testkit/provenance.py +119 -0
- mobile_agent/testkit/prrun.py +244 -0
- mobile_agent/testkit/recent.py +166 -0
- mobile_agent/testkit/recipes.py +146 -0
- mobile_agent/testkit/record.py +204 -0
- mobile_agent/testkit/register.py +288 -0
- mobile_agent/testkit/reports.py +998 -0
- mobile_agent/testkit/repro.py +432 -0
- mobile_agent/testkit/scenario.py +440 -0
- mobile_agent/testkit/scenario_cli.py +37 -0
- mobile_agent/testkit/snapshots_cli.py +60 -0
- mobile_agent/testkit/suite.py +670 -0
- mobile_agent/testkit/targets.py +497 -0
- mobile_agent/testkit/xctest.py +378 -0
- mobile_agent/threads/__init__.py +1 -0
- mobile_agent/threads/chat_once.py +264 -0
- mobile_agent/threads/cli.py +740 -0
- mobile_agent/threads/client.py +326 -0
- mobile_agent/threads/context.py +246 -0
- mobile_agent/threads/mcp.py +739 -0
- mobile_agent/threads/recent.py +34 -0
- mobile_agent/threads/register.py +54 -0
- mobile_agent/threads/routes.py +188 -0
- mobile_agent/threads/service.py +975 -0
- mobile_agent/threads/store.py +361 -0
- mobile_agent/threads/summary.py +117 -0
- mobile_agent/threads/term.py +101 -0
- mobile_agent/threads/workflow_cli.py +104 -0
- mobile_agent/tls.py +36 -0
- mobile_agent/traces.py +271 -0
- mobile_agent/tracks.py +235 -0
- mobile_agent/transport.py +271 -0
- mobile_agent/triggers/__init__.py +1 -0
- mobile_agent/triggers/actions.py +204 -0
- mobile_agent/triggers/model.py +146 -0
- mobile_agent/triggers/register.py +29 -0
- mobile_agent/triggers/routes.py +101 -0
- mobile_agent/triggers/service.py +387 -0
- mobile_agent/triggers/store.py +142 -0
- mobile_agent/tui/__init__.py +89 -0
- mobile_agent/tui/app.py +582 -0
- mobile_agent/tui/chrome.py +321 -0
- mobile_agent/tui/commands.py +375 -0
- mobile_agent/tui/demo_phone.py +369 -0
- mobile_agent/tui/images.py +29 -0
- mobile_agent/tui/keys.py +64 -0
- mobile_agent/tui/prompt.py +195 -0
- mobile_agent/tui/render.py +395 -0
- mobile_agent/tui/screens.py +180 -0
- mobile_agent/tui/session.py +308 -0
- mobile_agent/tui/tasks.py +287 -0
- mobile_agent/tui/theme.py +118 -0
- mobile_agent/tui/widgets.py +272 -0
- mobile_agent/update_check.py +213 -0
- mobile_agent/usage.py +184 -0
- mobile_agent/verify/__init__.py +0 -0
- mobile_agent/verify/__main__.py +41 -0
- mobile_agent/verify/a11y.py +189 -0
- mobile_agent/verify/actions.py +290 -0
- mobile_agent/verify/assertions.py +570 -0
- mobile_agent/verify/audit.py +312 -0
- mobile_agent/verify/business.py +221 -0
- mobile_agent/verify/business_run.py +131 -0
- mobile_agent/verify/business_state.py +168 -0
- mobile_agent/verify/check_schema.py +398 -0
- mobile_agent/verify/checks.py +614 -0
- mobile_agent/verify/cli.py +423 -0
- mobile_agent/verify/conditions.py +483 -0
- mobile_agent/verify/crashes.py +321 -0
- mobile_agent/verify/device_audit.py +246 -0
- mobile_agent/verify/dialogs.py +287 -0
- mobile_agent/verify/evidence.py +705 -0
- mobile_agent/verify/fixtures.py +305 -0
- mobile_agent/verify/flow.py +213 -0
- mobile_agent/verify/l10n.py +141 -0
- mobile_agent/verify/netcerts.py +67 -0
- mobile_agent/verify/netproxy.py +356 -0
- mobile_agent/verify/netrun.py +81 -0
- mobile_agent/verify/netshim.m +28 -0
- mobile_agent/verify/netshim.py +48 -0
- mobile_agent/verify/netstubs.py +132 -0
- mobile_agent/verify/overlay.py +174 -0
- mobile_agent/verify/perf.py +239 -0
- mobile_agent/verify/proof.py +192 -0
- mobile_agent/verify/replay_admission.py +48 -0
- mobile_agent/verify/report.py +952 -0
- mobile_agent/verify/run_cleanup.py +67 -0
- mobile_agent/verify/run_links.py +48 -0
- mobile_agent/verify/run_perf.py +44 -0
- mobile_agent/verify/run_secrets.py +39 -0
- mobile_agent/verify/runner.py +976 -0
- mobile_agent/verify/runs.py +57 -0
- mobile_agent/verify/screen_coverage.py +181 -0
- mobile_agent/verify/smart.py +106 -0
- mobile_agent/verify/snapshot_run.py +114 -0
- mobile_agent/verify/snapshots.py +306 -0
- mobile_agent/verify/sound.py +335 -0
- mobile_agent/verify/source_observation.py +130 -0
- mobile_agent/verify/system_surfaces/__init__.py +7 -0
- mobile_agent/verify/system_surfaces/play.py +106 -0
- mobile_agent/verify/system_surfaces/spec.py +139 -0
- mobile_agent/verify/system_surfaces/stage.py +138 -0
- mobile_agent/verify/system_surfaces/surfaces.py +121 -0
- mobile_agent/verify/tree.py +223 -0
- mobile_agent/verify/verdict.py +377 -0
- mobile_agent/verify/wire.py +392 -0
- mobile_agent/verify/wirestore.py +176 -0
- mobile_agent/waiting/__init__.py +1 -0
- mobile_agent/waiting/contacts.py +73 -0
- mobile_agent/waiting/drafts.py +136 -0
- mobile_agent/waiting/job.py +145 -0
- mobile_agent/waiting/messages.py +174 -0
- mobile_agent/waiting/rank.py +115 -0
- mobile_agent/waiting/register.py +26 -0
- mobile_agent/watch.py +502 -0
- mobile_agent/wda/__init__.py +2 -0
- mobile_agent/wda/frame_clock.py +646 -0
- mobile_agent/wda/frame_source.py +325 -0
- mobile_agent/wda/frame_store.py +115 -0
- mobile_agent/wda/gate.py +163 -0
- mobile_agent/wda/gestures.py +291 -0
- mobile_agent/wda/guards.py +94 -0
- mobile_agent/wda/identity.py +55 -0
- mobile_agent/wda/observe.py +884 -0
- mobile_agent/wda/patches.py +856 -0
- mobile_agent/wda/point_gestures.py +150 -0
- mobile_agent/wda/source.py +32 -0
- mobile_agent/wda/system_dialogs.py +571 -0
- mobile_agent/wda/text.py +694 -0
- mobile_agent/wda/timeouts.py +77 -0
- mobile_agent/wda/video.py +505 -0
- mobile_agent/wda/watchdog.py +243 -0
- mobile_agent/wireless/__init__.py +15 -0
- mobile_agent/wireless/api.py +354 -0
- mobile_agent/wireless/cli.py +287 -0
- mobile_agent/wireless/devicectl.py +229 -0
- mobile_agent/wireless/exposure.py +33 -0
- mobile_agent/wireless/mobster-wifi-pair.c +122 -0
- mobile_agent/wireless/monitor.py +332 -0
- mobile_agent/wireless/network.py +334 -0
- mobile_agent/wireless/onboarding.py +293 -0
- mobile_agent/wireless/pair.py +124 -0
- mobile_agent/wireless/probe.py +322 -0
- mobile_agent/wireless/register.py +8 -0
- mobile_agent/wireless/relay.py +179 -0
- mobile_agent/wireless/result.py +87 -0
- mobile_agent/wireless/store.py +85 -0
- mobile_agent/wireless/switch.py +77 -0
- mobile_agent/wireless/transport.py +118 -0
- mobile_agent/wireless/words.py +81 -0
- mobile_agent/wireless/xctestrun.py +83 -0
- mobile_agent/workflow_tools.py +59 -0
- mobile_agent/workflows/__init__.py +2 -0
- mobile_agent/workflows/brief.py +201 -0
- mobile_agent/workflows/cron.py +110 -0
- mobile_agent/workflows/health.py +257 -0
- mobile_agent/workflows/history.py +126 -0
- mobile_agent/workflows/records.py +442 -0
- mobile_agent/workflows/runs.py +1000 -0
- mobile_agent/workflows/scheduler.py +134 -0
- mobile_agent/workflows/service.py +973 -0
- mobile_agent/workflows/starters.py +285 -0
- mobile_agent/workflows/triggers.py +248 -0
- mobile_agent/xray/__init__.py +1 -0
- mobile_agent/xray/app_cli.py +116 -0
- mobile_agent/xray/cli.py +21 -0
- mobile_agent/xray/crawl.py +754 -0
- mobile_agent/xray/diff.py +171 -0
- mobile_agent/xray/diff_cli.py +43 -0
- mobile_agent/xray/job.py +184 -0
- mobile_agent/xray/paywall.py +188 -0
- mobile_agent/xray/policy.py +305 -0
- mobile_agent/xray/register.py +43 -0
- mobile_agent/xray/report.py +283 -0
- mobile_agent/xray/screens.py +117 -0
- mobster_cli-0.4.3.dist-info/METADATA +93 -0
- mobster_cli-0.4.3.dist-info/RECORD +1009 -0
- mobster_cli-0.4.3.dist-info/WHEEL +5 -0
- mobster_cli-0.4.3.dist-info/entry_points.txt +3 -0
- mobster_cli-0.4.3.dist-info/licenses/LICENSE +21 -0
- mobster_cli-0.4.3.dist-info/top_level.txt +1 -0
mobile_agent/__init__.py
ADDED
mobile_agent/__main__.py
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
"""The `mobster` command."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from . import __version__, links
|
|
7
|
+
from .cli_constants import DEFAULT_WDA_URL, EXIT_COULDNT, EXIT_FAILED, EXIT_OK, EXIT_USAGE
|
|
8
|
+
from .cli_run import (
|
|
9
|
+
DeviceBusy,
|
|
10
|
+
MissingKey,
|
|
11
|
+
NoDevice,
|
|
12
|
+
device_target,
|
|
13
|
+
error_line,
|
|
14
|
+
error_result,
|
|
15
|
+
no_device_reason,
|
|
16
|
+
output,
|
|
17
|
+
wda_url_for,
|
|
18
|
+
)
|
|
19
|
+
from .paths import DATA_DIR_ENV, build_dir, source_checkout, user_data_dir
|
|
20
|
+
|
|
21
|
+
DOCS = links.DOCS
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Commands that read the keys Mobster for Mac saved (config.load_env_layers). Not `mcp`: an installed MCP entry
|
|
25
|
+
# must never switch on paid Smart tools the user didn't give it. Not `serve`: the Mac app passes its own file.
|
|
26
|
+
APP_KEY_COMMANDS = {"tui", "run", "chat", "doctor", "verify", "test", "setup", "login", "logout"}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def default_state_db(args):
|
|
30
|
+
"""Where `serve` keeps its journal when --state-db is not given: never site-packages or the signed app bundle."""
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
data_dir = getattr(args, "data_dir", None) or os.environ.get(DATA_DIR_ENV)
|
|
33
|
+
if data_dir or not source_checkout():
|
|
34
|
+
return (Path(data_dir).expanduser().resolve() if data_dir else user_data_dir()) / "state" / "mobster.sqlite3"
|
|
35
|
+
return Path(__file__).parent / ".state" / "mobster.sqlite3"
|
|
36
|
+
|
|
37
|
+
def check_placement(parser, args, on_error=None):
|
|
38
|
+
"""Options given before a command's name, as in `mobster --wda-url URL run …`."""
|
|
39
|
+
from . import devtools
|
|
40
|
+
from .cli_parser import wda_url_type
|
|
41
|
+
|
|
42
|
+
def error(message):
|
|
43
|
+
if on_error is not None:
|
|
44
|
+
on_error(message)
|
|
45
|
+
parser.error(message)
|
|
46
|
+
command = args.command
|
|
47
|
+
if command in (None, "tui"):
|
|
48
|
+
return
|
|
49
|
+
accepted = parser.commands[command]._option_string_actions
|
|
50
|
+
for dest, flag in (("demo", "--demo"), ("resume", "--resume"), ("app", "--app"), ("continue_", "--continue")):
|
|
51
|
+
if getattr(args, dest, None) and flag not in accepted:
|
|
52
|
+
error(f"{flag} is an option of the terminal UI (`mobster {flag}`), not of `mobster {command}`")
|
|
53
|
+
if getattr(args, "wda_url", None) and getattr(args, "socket", None):
|
|
54
|
+
error("argument --socket: not allowed with argument --wda-url")
|
|
55
|
+
if getattr(args, "device", None) and getattr(args, "socket", None):
|
|
56
|
+
error("argument --socket: not allowed with argument --device")
|
|
57
|
+
if command != "serve" and getattr(args, "device", None) and getattr(args, "wda_url", None) \
|
|
58
|
+
and command not in devtools.ORDER:
|
|
59
|
+
error("argument --device: not allowed with argument --wda-url")
|
|
60
|
+
listing = command == "devices" and getattr(args, "action", None) is None # the list reads no WDA address
|
|
61
|
+
if "--wda-url" in accepted and command != "serve" and not listing and not getattr(args, "wda_url", None) \
|
|
62
|
+
and os.environ.get("MOBSTER_WDA_URL"):
|
|
63
|
+
import argparse
|
|
64
|
+
try:
|
|
65
|
+
wda_url_type(os.environ["MOBSTER_WDA_URL"])
|
|
66
|
+
except argparse.ArgumentTypeError as problem:
|
|
67
|
+
error(f"$MOBSTER_WDA_URL: {problem}")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
PHONE_WORDS = {"ready": "Ready", "busy": "In use", "connected": "Connected", "needs_setup": "Needs setup",
|
|
71
|
+
"disconnected": "Unplugged"}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def device_state_words(item):
|
|
75
|
+
"""Mobster for Mac's phone words for a device's state (MESSAGING §11, the phone list)."""
|
|
76
|
+
state = item.get("state") or ""
|
|
77
|
+
if state == "disconnected":
|
|
78
|
+
return {"simulator": "Shut down", "wda": "Not answering"}.get(item.get("kind"), "Unplugged")
|
|
79
|
+
return PHONE_WORDS.get(state, state.replace("_", " ").capitalize())
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def print_devices(as_json=False):
|
|
83
|
+
from . import devices
|
|
84
|
+
from .style import columns, palette
|
|
85
|
+
found = devices.discover()
|
|
86
|
+
if as_json:
|
|
87
|
+
output({"devices": found})
|
|
88
|
+
return EXIT_OK
|
|
89
|
+
paint = palette(sys.stdout)
|
|
90
|
+
if not found:
|
|
91
|
+
print("No iPhones or simulators yet. Plug in your iPhone with a cable and unlock it, then run "
|
|
92
|
+
+ paint("mobster setup", "accent") + ".")
|
|
93
|
+
print("For a simulator: " + paint("mobster sim prepare", "accent") + ".")
|
|
94
|
+
return EXIT_OK
|
|
95
|
+
kinds = {"usb": "iPhone", "simulator": "Simulator", "wda": "Address"}
|
|
96
|
+
rows = []
|
|
97
|
+
for item in found:
|
|
98
|
+
kind = kinds.get(item["kind"], item["kind"])
|
|
99
|
+
if item["kind"] == "usb" and item.get("transport") == "wifi":
|
|
100
|
+
kind = "iPhone, Wi-Fi"
|
|
101
|
+
details = ", ".join(part for part in (item.get("modelName") or item.get("model"),
|
|
102
|
+
f"iOS {item['ios']}" if item.get("ios") else None) if part)
|
|
103
|
+
rows.append((item, kind, details))
|
|
104
|
+
name_width = min(28, max(4, *(len(item["name"]) for item, _, _ in rows))) + 2
|
|
105
|
+
kind_width = max(4, *(len(kind) for _, kind, _ in rows)) + 2
|
|
106
|
+
state_width = max(5, *(len(device_state_words(item)) for item, _, _ in rows)) + 2
|
|
107
|
+
print(" " + paint("Name".ljust(name_width), "bold") + paint("Kind".ljust(kind_width), "bold")
|
|
108
|
+
+ paint("State".ljust(state_width), "bold") + paint("Details", "bold"))
|
|
109
|
+
roles = {"Ready": "green", "In use": "accent", "Needs setup": "amber"}
|
|
110
|
+
width = columns()
|
|
111
|
+
for item, kind, details in rows:
|
|
112
|
+
state = device_state_words(item)
|
|
113
|
+
name = item["name"] if len(item["name"]) < name_width - 1 else item["name"][:name_width - 3] + "…"
|
|
114
|
+
line = (" " + paint(name.ljust(name_width), "accent") + kind.ljust(kind_width)
|
|
115
|
+
+ paint(state.ljust(state_width), roles.get(state, "secondary")) + paint(details, "tertiary"))
|
|
116
|
+
print(line)
|
|
117
|
+
if item.get("reason"):
|
|
118
|
+
from .style import wrap
|
|
119
|
+
print(paint(wrap(item["reason"], width - 2, indent=4), "tertiary"))
|
|
120
|
+
ready = [item for item, _, _ in rows if item.get("state") == "ready"]
|
|
121
|
+
print()
|
|
122
|
+
if ready:
|
|
123
|
+
print("Give one a task: " + paint(f'mobster --device "{ready[0]["name"]}"', "accent"))
|
|
124
|
+
elif any(item.get("state") == "needs_setup" for item, _, _ in rows):
|
|
125
|
+
print("Set one up: " + paint("mobster setup", "accent"))
|
|
126
|
+
else:
|
|
127
|
+
print("Check everything: " + paint("mobster doctor", "accent"))
|
|
128
|
+
return EXIT_OK
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def print_device_status(args):
|
|
132
|
+
"""`mobster devices status`: a device's battery and heat, and this Mac's power (device_status.py)."""
|
|
133
|
+
from . import device_status, devices
|
|
134
|
+
found = devices.discover(probe=False)
|
|
135
|
+
if args.device:
|
|
136
|
+
try:
|
|
137
|
+
item = devices.resolve(found, args.device)
|
|
138
|
+
except LookupError as error:
|
|
139
|
+
raise NoDevice(str(error)) from None
|
|
140
|
+
elif args.wda_url:
|
|
141
|
+
url = wda_url_for(args)
|
|
142
|
+
item = next((record for record in found if (record.get("wdaUrl") or "").rstrip("/") == url),
|
|
143
|
+
{"id": url, "kind": "wda", "name": url, "wdaUrl": url})
|
|
144
|
+
else:
|
|
145
|
+
item = (found[0] if len(found) == 1 else next((record for record in found if record.get("primary")), None))
|
|
146
|
+
if item is None:
|
|
147
|
+
raise NoDevice("No device to read. " + ("Name one with --device." if found else
|
|
148
|
+
"Plug in your iPhone with a cable and unlock it."))
|
|
149
|
+
phone = device_status.read_phone(item["kind"], udid=item.get("udid"), wda_url=item.get("wdaUrl"))
|
|
150
|
+
mac = device_status.read_mac()
|
|
151
|
+
if args.json or not sys.stdout.isatty():
|
|
152
|
+
output({"ok": True, "device": item["id"], "name": item.get("name"), "phone": phone, "mac": mac})
|
|
153
|
+
return EXIT_OK
|
|
154
|
+
from .style import palette
|
|
155
|
+
paint = palette(sys.stdout)
|
|
156
|
+
print(paint(item.get("name") or item["id"], "bold"))
|
|
157
|
+
found_rows = device_status.rows(phone, mac)
|
|
158
|
+
width = max(len(label) for label, _ in found_rows) + 2
|
|
159
|
+
for label, value in found_rows:
|
|
160
|
+
print(" " + paint(label.ljust(width), "tertiary") + value)
|
|
161
|
+
return EXIT_OK
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def lock_probe(args):
|
|
165
|
+
"""`mobster devices lock-probe`: reads the lock screen under the device lease, with no tap, key press or typing."""
|
|
166
|
+
from . import lockscreen
|
|
167
|
+
from .compose import close_all
|
|
168
|
+
from .drivers import build_driver, resolve_wda_session
|
|
169
|
+
from .journal import Lease, LeaseHeld
|
|
170
|
+
from .transport import TransportError
|
|
171
|
+
url = wda_url_for(args)
|
|
172
|
+
try:
|
|
173
|
+
lease = Lease.device(url)
|
|
174
|
+
except LeaseHeld:
|
|
175
|
+
raise DeviceBusy("Another Mobster process is using this phone. Finish or stop its task, then probe "
|
|
176
|
+
"again.") from None
|
|
177
|
+
driver = None
|
|
178
|
+
try:
|
|
179
|
+
try:
|
|
180
|
+
session = resolve_wda_session(url)
|
|
181
|
+
except (TransportError, OSError) as exc:
|
|
182
|
+
raise NoDevice(f"Nothing answers at {url} ({no_device_reason(exc)}).") from None
|
|
183
|
+
driver = build_driver("wda", url=url, session=session)
|
|
184
|
+
result = lockscreen.probe(driver, udid=getattr(args, "device_udid", None)
|
|
185
|
+
if getattr(args, "device_kind", None) == "usb" else None)
|
|
186
|
+
finally:
|
|
187
|
+
close_all(driver)
|
|
188
|
+
lease.close()
|
|
189
|
+
if args.json or not sys.stdout.isatty():
|
|
190
|
+
output({"ok": True, **result})
|
|
191
|
+
else:
|
|
192
|
+
print("\n".join(lockscreen.describe(result)))
|
|
193
|
+
return EXIT_OK
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# The bare `mobster` parser's options that take a value: their value is never the command's name.
|
|
197
|
+
VALUE_OPTIONS = ("--wda-url", "--device", "--env-file", "--app")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _positional(argv, names):
|
|
201
|
+
"""The index of the first word in ``argv`` that is neither an option nor an option's value, or None."""
|
|
202
|
+
index = 0
|
|
203
|
+
while index < len(argv):
|
|
204
|
+
token = argv[index]
|
|
205
|
+
if token == "--":
|
|
206
|
+
return index + 1 if index + 1 < len(argv) else None
|
|
207
|
+
if token in VALUE_OPTIONS:
|
|
208
|
+
index += 2
|
|
209
|
+
continue
|
|
210
|
+
if token == "--resume":
|
|
211
|
+
# --resume takes an optional ID: a command's name after it is the command.
|
|
212
|
+
following = argv[index + 1] if index + 1 < len(argv) else None
|
|
213
|
+
index += 1 if following is None or following in names or following.startswith("-") else 2
|
|
214
|
+
continue
|
|
215
|
+
if token.startswith("-") and token != "-":
|
|
216
|
+
index += 1
|
|
217
|
+
continue
|
|
218
|
+
return index
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def command_word(parser, argv):
|
|
223
|
+
"""The command ``argv`` names, read before parsing."""
|
|
224
|
+
names = parser.commands if hasattr(parser, "commands") else parser
|
|
225
|
+
index = _positional(list(argv), names)
|
|
226
|
+
if index is None:
|
|
227
|
+
return None
|
|
228
|
+
if index and argv[index - 1] == "--":
|
|
229
|
+
return None # `mobster -- verify`: after --, the word is a task, not a command
|
|
230
|
+
word = argv[index]
|
|
231
|
+
return word if word in names else None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def split_task(argv):
|
|
235
|
+
"""`mobster "TASK"`: (task, argv without it) when the first word is a task for the terminal UI,
|
|
236
|
+
else (None, argv)."""
|
|
237
|
+
from .help_topics import COMMANDS
|
|
238
|
+
index = _positional(argv, COMMANDS)
|
|
239
|
+
if index is None or argv[index] in COMMANDS:
|
|
240
|
+
return None, argv
|
|
241
|
+
word = argv[index]
|
|
242
|
+
if index and argv[index - 1] == "--":
|
|
243
|
+
# `mobster -- calendar`: after --, a word is always the task, even one word.
|
|
244
|
+
return word.strip(), argv[:index - 1] + argv[index + 1:]
|
|
245
|
+
from .harness_api import hooks
|
|
246
|
+
if hooks().cli:
|
|
247
|
+
# A private build may add commands: its parser knows them.
|
|
248
|
+
from .cli_parser import build_parser
|
|
249
|
+
if word in build_parser(load=()).commands:
|
|
250
|
+
return None, argv
|
|
251
|
+
later = [token for token in argv[index + 1:] if not token.startswith("-")]
|
|
252
|
+
from .style import palette
|
|
253
|
+
paint = palette(sys.stderr)
|
|
254
|
+
if " " in word.strip() or "\n" in word.strip():
|
|
255
|
+
if later:
|
|
256
|
+
raise UsageError(paint(later[0], "amber") + " follows the task.",
|
|
257
|
+
"put the whole task in one pair of quotes: "
|
|
258
|
+
+ paint(f'mobster "{word} {" ".join(later)}"', "accent"))
|
|
259
|
+
return word.strip(), argv[:index] + argv[index + 1:]
|
|
260
|
+
if later:
|
|
261
|
+
words = " ".join([word] + later)
|
|
262
|
+
raise UsageError(paint(word, "amber") + " isn't a command.",
|
|
263
|
+
"to run it as a task, put it in quotes: " + paint(f'mobster "{words}"', "accent"))
|
|
264
|
+
import difflib
|
|
265
|
+
close = difflib.get_close_matches(word, [name for name in COMMANDS if name != "build-ocr"], n=1, cutoff=.6)
|
|
266
|
+
if close:
|
|
267
|
+
raise UsageError(paint(word, "amber") + " isn't a command.",
|
|
268
|
+
"did you mean " + paint(f"mobster {close[0]}", "accent") + "?")
|
|
269
|
+
raise UsageError(paint(word, "amber") + " isn't a command.",
|
|
270
|
+
"to run it as a task: " + paint(f"mobster -- {word}", "accent"))
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class UsageError(Exception):
|
|
274
|
+
"""A usage error found before the parser runs: (message, tip). Exit code 2."""
|
|
275
|
+
|
|
276
|
+
def __init__(self, message, tip=""):
|
|
277
|
+
super().__init__(message)
|
|
278
|
+
self.message, self.tip = message, tip
|
|
279
|
+
|
|
280
|
+
def report(self, prog="mobster"):
|
|
281
|
+
from .cli_parser import usage_lines
|
|
282
|
+
from .style import palette
|
|
283
|
+
sys.stderr.write(usage_lines(palette(sys.stderr), self.message, self.tip, prog))
|
|
284
|
+
sys.stderr.flush()
|
|
285
|
+
return EXIT_USAGE
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def route_usage_errors(parser, argv):
|
|
289
|
+
"""A usage error the bare parser finds before `verify` exits 3,
|
|
290
|
+
with verify's couldnt_run JSON when output is JSON."""
|
|
291
|
+
from . import devtools
|
|
292
|
+
if command_word(parser, argv) != "verify":
|
|
293
|
+
return
|
|
294
|
+
as_json = "--json" in argv
|
|
295
|
+
|
|
296
|
+
def error(message):
|
|
297
|
+
parser.print_usage(sys.stderr)
|
|
298
|
+
print(f"{parser.prog}: error: {message}", file=sys.stderr, flush=True)
|
|
299
|
+
sys.exit(devtools.usage_exit("verify", message, as_json=as_json))
|
|
300
|
+
parser.error = error
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def quick(argv):
|
|
304
|
+
"""`--version` and `--help`, answered before the parser is built. None for anything else."""
|
|
305
|
+
if argv in (["-V"], ["--version"], ["version"]):
|
|
306
|
+
print(f"mobster {__version__}")
|
|
307
|
+
return EXIT_OK
|
|
308
|
+
if argv in (["-h"], ["--help"], ["help"]):
|
|
309
|
+
from . import help_topics
|
|
310
|
+
from .style import columns, palette
|
|
311
|
+
print(help_topics.top_level(palette(sys.stdout), columns()))
|
|
312
|
+
return EXIT_OK
|
|
313
|
+
if len(argv) == 2 and argv[0] == "help":
|
|
314
|
+
from . import help_topics
|
|
315
|
+
if argv[1] in help_topics.TOPICS:
|
|
316
|
+
from .cli_parser import print_topic
|
|
317
|
+
return print_topic(argv[1])
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# Commands that run Xcode's tools (xcodebuild, xcrun simctl or devicectl, swiftc): they use the Xcode Mobster found,
|
|
322
|
+
# whatever xcode-select says (mac_tools.adopt_xcode), so nobody is asked to switch it in Terminal.
|
|
323
|
+
XCODE_COMMANDS = frozenset({"build-ocr", "ci", "devices", "doctor", "expo", "mcp", "phone", "serve", "setup", "sim",
|
|
324
|
+
"test", "testflight", "verify", "wifi", "xcode", "fixtures", "links", "intents"})
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def main(argv=None):
|
|
328
|
+
argv = sys.argv[1:] if argv is None else list(argv)
|
|
329
|
+
if argv[:1] == ["import"]:
|
|
330
|
+
argv = ["test"] + argv # `mobster import maestro .maestro` is `mobster test import maestro .maestro`
|
|
331
|
+
answered = quick(argv)
|
|
332
|
+
if answered is not None:
|
|
333
|
+
return answered
|
|
334
|
+
try:
|
|
335
|
+
task, argv = split_task(argv)
|
|
336
|
+
except UsageError as error:
|
|
337
|
+
return error.report()
|
|
338
|
+
from .cli_parser import build_parser, owner, print_help
|
|
339
|
+
from .help_topics import COMMANDS
|
|
340
|
+
index = _positional(argv, COMMANDS)
|
|
341
|
+
word = argv[index] if index is not None else None
|
|
342
|
+
wanted = {word}
|
|
343
|
+
if word == "help":
|
|
344
|
+
wanted |= set(argv[index + 1:index + 2])
|
|
345
|
+
parser = build_parser(load=wanted)
|
|
346
|
+
route_usage_errors(parser, argv)
|
|
347
|
+
args, extras = parser.parse_known_args(argv)
|
|
348
|
+
args.task = task
|
|
349
|
+
if getattr(args, "data_dir", None):
|
|
350
|
+
from .paths import use_data_dir
|
|
351
|
+
use_data_dir(args.data_dir)
|
|
352
|
+
words = [word for word in extras if not word.startswith("-")]
|
|
353
|
+
if extras and words == extras and args.command == "run" and getattr(args, "goal", None):
|
|
354
|
+
# `mobster run Turn on Dark Mode`: the task is the words, unquoted.
|
|
355
|
+
task = " ".join([args.goal] + words)
|
|
356
|
+
from .style import palette
|
|
357
|
+
paint = palette(sys.stderr)
|
|
358
|
+
return UsageError(paint("mobster run", "accent") + " needs the task in quotes.",
|
|
359
|
+
paint(f'mobster run "{task}"', "accent")).report("mobster run")
|
|
360
|
+
if extras and not getattr(args, "stub_command", False): # a track's stub takes anything (it exits 3)
|
|
361
|
+
# `mobster run x --bogus`: the error and usage are run's (`sim prepare`'s), not the bare `mobster`'s.
|
|
362
|
+
owner(parser, args).error(f"unrecognized arguments: {' '.join(extras)}")
|
|
363
|
+
if args.command == "help":
|
|
364
|
+
return print_help(parser, args.topic, everything=args.all)
|
|
365
|
+
messages = []
|
|
366
|
+
from . import devtools
|
|
367
|
+
try:
|
|
368
|
+
check_placement(parser, args, on_error=messages.append)
|
|
369
|
+
except SystemExit as exit_:
|
|
370
|
+
if exit_.code == 2 and args.command in devtools.ORDER:
|
|
371
|
+
# verify's usage errors exit 3 (2 means needs review), with its JSON result when output is JSON.
|
|
372
|
+
return devtools.usage_exit(args.command, messages[-1] if messages else None,
|
|
373
|
+
as_json=bool(getattr(args, "json", False)))
|
|
374
|
+
raise
|
|
375
|
+
command = args.command or "tui"
|
|
376
|
+
if hasattr(args, "env_file") and args.env_file is None and args.command != "serve" \
|
|
377
|
+
and os.environ.get("MOBSTER_ENV_FILE"):
|
|
378
|
+
# An explicit default for people who always use the same file; `serve` keeps its own rules.
|
|
379
|
+
from pathlib import Path
|
|
380
|
+
args.env_file = Path(os.environ["MOBSTER_ENV_FILE"]).expanduser()
|
|
381
|
+
args.env_file_from_environment = True
|
|
382
|
+
if command in APP_KEY_COMMANDS and not getattr(args, "demo", False) is True:
|
|
383
|
+
# The keys Mobster for Mac saved come last: the shell and an --env-file win (config.load_env_layers).
|
|
384
|
+
from .config import load_env_layers
|
|
385
|
+
for warning in load_env_layers(getattr(args, "env_file", None)):
|
|
386
|
+
print(f"mobster: {warning}", file=sys.stderr, flush=True)
|
|
387
|
+
elif getattr(args, "env_file", None) and args.command != "mcp": # mcp loads it itself, into its log
|
|
388
|
+
from .config import load_env_file
|
|
389
|
+
# A bad line is skipped, never fatal: the Mac app's agent logs it (agent.log) and still serves. A missing
|
|
390
|
+
# file is said too, except to `serve`, whose file the Mac app's setup creates when it saves a value.
|
|
391
|
+
for warning in load_env_file(args.env_file, missing_ok=args.command == "serve").warnings:
|
|
392
|
+
print(f"mobster: {warning}", file=sys.stderr, flush=True)
|
|
393
|
+
if command in XCODE_COMMANDS and sys.platform == "darwin":
|
|
394
|
+
from . import mac_tools
|
|
395
|
+
mac_tools.adopt_xcode()
|
|
396
|
+
# Before any model or service connection: the frozen app's Python has no usable CA file.
|
|
397
|
+
from . import tls
|
|
398
|
+
tls.ensure_ca_bundle()
|
|
399
|
+
from . import update_check
|
|
400
|
+
update_check.start(command, args)
|
|
401
|
+
code = dispatch(parser, args, command)
|
|
402
|
+
update_check.after_command(command, args)
|
|
403
|
+
return code
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def dispatch(parser, args, command):
|
|
407
|
+
from . import devtools
|
|
408
|
+
refused = plan_refusal(args, command)
|
|
409
|
+
if refused is not None:
|
|
410
|
+
return refused
|
|
411
|
+
if command == "devices" and getattr(args, "action", None) is None and (
|
|
412
|
+
getattr(args, "device", None) or getattr(args, "wda_url", None)):
|
|
413
|
+
parser.commands["devices"].error("--device and --wda-url go with `mobster devices lock-probe` or `status`")
|
|
414
|
+
probing = command == "devices" and getattr(args, "action", None) == "lock-probe"
|
|
415
|
+
if (command in ("run", "screen", "doctor", "tui") or probing) and getattr(args, "device", None) \
|
|
416
|
+
and not getattr(args, "demo", False) is True:
|
|
417
|
+
# --device NAME is that device's WDA address from here on (exclusive with --wda-url).
|
|
418
|
+
try:
|
|
419
|
+
item = device_target(args.device)
|
|
420
|
+
except NoDevice as error:
|
|
421
|
+
if command == "run" and (getattr(args, "json", False) or not sys.stdout.isatty()):
|
|
422
|
+
output(error_result(error, EXIT_COULDNT))
|
|
423
|
+
else:
|
|
424
|
+
print(error_line(error), file=sys.stderr)
|
|
425
|
+
return EXIT_COULDNT
|
|
426
|
+
args.wda_url = item["wdaUrl"]
|
|
427
|
+
args.device_kind, args.device_udid = item["kind"], item.get("udid")
|
|
428
|
+
args.device_name = item.get("name")
|
|
429
|
+
if command == "chat" and not getattr(args, "unloaded_command", False):
|
|
430
|
+
from .chat_here import maybe_here
|
|
431
|
+
code = maybe_here(args)
|
|
432
|
+
if code is not None:
|
|
433
|
+
return code
|
|
434
|
+
if command in devtools.ORDER:
|
|
435
|
+
try:
|
|
436
|
+
return devtools.run(args)
|
|
437
|
+
except KeyboardInterrupt:
|
|
438
|
+
return 130
|
|
439
|
+
from .harness_api import hooks
|
|
440
|
+
try:
|
|
441
|
+
for extension in hooks().cli:
|
|
442
|
+
code = extension.run(args, output)
|
|
443
|
+
if code is not None:
|
|
444
|
+
return code
|
|
445
|
+
if command == "tui":
|
|
446
|
+
from .tui import main as tui_main
|
|
447
|
+
return tui_main(args)
|
|
448
|
+
if command == "version":
|
|
449
|
+
if args.json:
|
|
450
|
+
import platform
|
|
451
|
+
|
|
452
|
+
from . import tls
|
|
453
|
+
output({"version": __version__, "python": platform.python_version(),
|
|
454
|
+
"platform": platform.platform(terse=True), "tls": tls.report()})
|
|
455
|
+
else:
|
|
456
|
+
print(f"mobster {__version__}")
|
|
457
|
+
return EXIT_OK
|
|
458
|
+
if command in ("login", "logout"):
|
|
459
|
+
from . import login
|
|
460
|
+
return login.run(args)
|
|
461
|
+
if command == "setup":
|
|
462
|
+
from . import setup_cli
|
|
463
|
+
return setup_cli.run(args)
|
|
464
|
+
if command == "completion":
|
|
465
|
+
from . import completion
|
|
466
|
+
return completion.run(args)
|
|
467
|
+
if command == "update":
|
|
468
|
+
from . import update_check
|
|
469
|
+
return update_check.run_update(args)
|
|
470
|
+
if command == "devices":
|
|
471
|
+
if args.action == "lock-probe":
|
|
472
|
+
return lock_probe(args)
|
|
473
|
+
if args.action == "status":
|
|
474
|
+
return print_device_status(args)
|
|
475
|
+
return print_devices(as_json=args.json)
|
|
476
|
+
if command == "doctor":
|
|
477
|
+
return doctor_command(args)
|
|
478
|
+
if command == "history":
|
|
479
|
+
if getattr(args, "to_eval", None):
|
|
480
|
+
from .run_export import to_eval
|
|
481
|
+
return to_eval(args)
|
|
482
|
+
from .history import print_history
|
|
483
|
+
return print_history(limit=args.limit, as_json=args.json)
|
|
484
|
+
if command == "export":
|
|
485
|
+
from .run_export import export as export_run
|
|
486
|
+
return export_run(args)
|
|
487
|
+
if command == "screen":
|
|
488
|
+
from .terminal_image import show_screen
|
|
489
|
+
return show_screen(wda_url_for(args), out=args.out, width=args.width)
|
|
490
|
+
if command == "build-ocr":
|
|
491
|
+
import subprocess
|
|
492
|
+
from pathlib import Path
|
|
493
|
+
root = Path(__file__).parent
|
|
494
|
+
build = build_dir()
|
|
495
|
+
build.mkdir(parents=True, exist_ok=True)
|
|
496
|
+
subprocess.run(["xcrun", "swiftc", "-O", str(root / "swift" / "ocr.swift"),
|
|
497
|
+
"-o", str(build / "ocr")], check=True)
|
|
498
|
+
output({"ok": True, "binary": str(build / "ocr")})
|
|
499
|
+
elif command == "demo":
|
|
500
|
+
from types import SimpleNamespace
|
|
501
|
+
|
|
502
|
+
from .smart_run import run_smart
|
|
503
|
+
return run_smart(SimpleNamespace(goal="Search for coffee", json=args.json, execute=True, spend_cap_usd=None,
|
|
504
|
+
long=False, budget=None, yes=True), demo=True)
|
|
505
|
+
elif command == "serve":
|
|
506
|
+
from .serve import serve
|
|
507
|
+
if args.state_db is None:
|
|
508
|
+
args.state_db = default_state_db(args)
|
|
509
|
+
if getattr(args, "device", None) and not args.wda_url and not getattr(args, "manage_device", False):
|
|
510
|
+
args.wda_url = device_target(args.device)["wdaUrl"] # serve that device
|
|
511
|
+
if getattr(args, "manage_device", False) and not args.wda_url:
|
|
512
|
+
args.wda_url = DEFAULT_WDA_URL # The relay the device manager runs.
|
|
513
|
+
saved = os.environ.get("MOBSTER_ENABLE_LIVE")
|
|
514
|
+
if saved in {"0", "1"}:
|
|
515
|
+
args.enable_live = saved == "1" # The setup flow's saved choice wins.
|
|
516
|
+
serve(args)
|
|
517
|
+
elif command == "run":
|
|
518
|
+
return run_command(args)
|
|
519
|
+
return EXIT_OK
|
|
520
|
+
except KeyboardInterrupt:
|
|
521
|
+
return 130
|
|
522
|
+
except Exception as exc:
|
|
523
|
+
# Exception messages here are locally authored; transport never includes provider bodies/keys.
|
|
524
|
+
code = EXIT_COULDNT if isinstance(exc, (NoDevice, MissingKey)) else EXIT_FAILED
|
|
525
|
+
if sys.stdout.isatty() and not getattr(args, "json", False):
|
|
526
|
+
print(error_line(exc), file=sys.stderr)
|
|
527
|
+
elif command in ("run", "demo"):
|
|
528
|
+
output(error_result(exc, code))
|
|
529
|
+
else:
|
|
530
|
+
output({"ok": False, "error_type": type(exc).__name__, "error": str(exc)})
|
|
531
|
+
return code
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def doctor_command(args):
|
|
535
|
+
"""`mobster doctor`: the checks and the fix for each. With --fix, it does the fix each finding's row carries
|
|
536
|
+
(doctor_fix), then checks again."""
|
|
537
|
+
from . import doctor, doctor_fix
|
|
538
|
+
if getattr(args, "apps", False):
|
|
539
|
+
from .attach import cli as attach_cli
|
|
540
|
+
return attach_cli.run(args, wda_url_for(args))
|
|
541
|
+
usb = not args.simulator and getattr(args, "device_kind", "usb") == "usb"
|
|
542
|
+
|
|
543
|
+
def check():
|
|
544
|
+
return doctor.run_checks(wda_url=wda_url_for(args), device=usb, env_file=args.env_file,
|
|
545
|
+
udid=getattr(args, "device_udid", None))
|
|
546
|
+
checks = check()
|
|
547
|
+
steps = None
|
|
548
|
+
if args.fix:
|
|
549
|
+
terminal = sys.stdin.isatty() and not args.json
|
|
550
|
+
ctx = doctor_fix.Context(args, ask=doctor_fix.interactive_ask if terminal else None,
|
|
551
|
+
say=(lambda line: print(line, flush=True)) if terminal else None)
|
|
552
|
+
try:
|
|
553
|
+
steps = doctor_fix.run(checks, ctx, terminal=terminal)
|
|
554
|
+
finally:
|
|
555
|
+
ctx.close()
|
|
556
|
+
checks = check()
|
|
557
|
+
# Someone who never pointed Mobster at an iPhone may be here for `verify`: say where its checks are.
|
|
558
|
+
phone_chosen = getattr(args, "wda_url", None) or getattr(args, "device", None) \
|
|
559
|
+
or os.environ.get("MOBSTER_WDA_URL")
|
|
560
|
+
hint = doctor.VERIFY_HINT if doctor.problems(checks) and not phone_chosen else None
|
|
561
|
+
if args.json:
|
|
562
|
+
output({"ok": not doctor.problems(checks), "checks": [check.public() for check in checks],
|
|
563
|
+
**({"fixes": [step.public() for step in steps]} if steps is not None else {}),
|
|
564
|
+
**({"hint": hint} if hint else {})})
|
|
565
|
+
else:
|
|
566
|
+
from .style import color_enabled, columns
|
|
567
|
+
print(doctor.plain_report(checks, color=color_enabled(sys.stdout), verify_hint=hint, width=columns(),
|
|
568
|
+
steps=steps))
|
|
569
|
+
return EXIT_FAILED if doctor.problems(checks) else EXIT_OK
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def plan_refusal(args, command):
|
|
573
|
+
"""EXIT_COULDNT after saying so, when the agent inside Mobster.app has no active plan, fails its seal check or
|
|
574
|
+
runs under a CI token that refuses; None otherwise."""
|
|
575
|
+
from . import plan_gate
|
|
576
|
+
gate = plan_gate.bundled_gate()
|
|
577
|
+
if gate is not None:
|
|
578
|
+
plan_gate.grant_ci_token(command, args) # a CI token licenses simulator runs on a machine with no account
|
|
579
|
+
message = plan_gate.stopped(command, args) # a failed seal or a CI token that refuses: whatever it needs
|
|
580
|
+
if message is None:
|
|
581
|
+
message = gate.refusal() if gate is not None else None
|
|
582
|
+
if message is None or not plan_gate.cli_needs_plan(command, args): # the plan first: it is the cheaper read
|
|
583
|
+
return None
|
|
584
|
+
if getattr(args, "json", False):
|
|
585
|
+
output({"ok": False, "error_type": "plan_required", "error": message})
|
|
586
|
+
else:
|
|
587
|
+
print(f"mobster: {message}", file=sys.stderr, flush=True)
|
|
588
|
+
return EXIT_COULDNT
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
def run_command(args):
|
|
592
|
+
"""`mobster run`: Mobster's agent through the same runtime as the terminal UI, or the scripted demo."""
|
|
593
|
+
from .smart_run import run_smart
|
|
594
|
+
return run_smart(args, demo=getattr(args, "demo", False) is True)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
if __name__ == "__main__":
|
|
598
|
+
sys.exit(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Profiles: which social media profile lives on which iPhone (FEATURE-BACKLOG v2 item 4, docs/profiles.mdx)."""
|