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,611 @@
|
|
|
1
|
+
"""The `mobster` command's argument parser and help."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from . import __version__, harness_api, help_topics, links, traces
|
|
7
|
+
from .cli_constants import DEFAULT_WDA_URL, EXIT_OK, EXIT_USAGE
|
|
8
|
+
from .style import columns, palette
|
|
9
|
+
|
|
10
|
+
# The commands in the order `mobster --help` lists them (the docs generator reads it).
|
|
11
|
+
EVERYDAY = tuple(n for _, names in help_topics.GROUPS for n in names) + help_topics.MORE
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Parser(argparse.ArgumentParser):
|
|
15
|
+
"""argparse with no abbreviated options."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, *args, allow_abbrev=False, **kwargs):
|
|
18
|
+
super().__init__(*args, allow_abbrev=allow_abbrev, **kwargs)
|
|
19
|
+
|
|
20
|
+
def error(self, message):
|
|
21
|
+
paint = palette(sys.stderr)
|
|
22
|
+
message, tip = explain(self, message, paint)
|
|
23
|
+
self.exit(EXIT_USAGE, usage_lines(paint, message, tip, self.prog, short_usage(self)))
|
|
24
|
+
|
|
25
|
+
def print_help(self, file=None):
|
|
26
|
+
file = file or sys.stdout
|
|
27
|
+
text = self.format_help()
|
|
28
|
+
paint = palette(file)
|
|
29
|
+
if paint.enabled:
|
|
30
|
+
text = colour_help(text, paint)
|
|
31
|
+
file.write(text)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def usage_lines(paint, message, tip="", prog="mobster", usage=""):
|
|
35
|
+
"""A usage error the way uv prints one: a coloured `error:`, one `tip:`, the command's usage, and `More:`."""
|
|
36
|
+
lines = [paint("error:", "coral", "bold") + " " + message]
|
|
37
|
+
if tip:
|
|
38
|
+
lines.append(" " + paint("tip:", "accent") + " " + tip)
|
|
39
|
+
if usage:
|
|
40
|
+
rest = usage.removeprefix(prog).strip()
|
|
41
|
+
lines.append(paint("Usage:", "bold") + " " + paint(prog, "accent") + (" " + paint(rest, "tertiary")
|
|
42
|
+
if rest else ""))
|
|
43
|
+
lines.append(paint("More:", "bold") + " " + paint(f"{prog} --help", "accent"))
|
|
44
|
+
return "\n".join(lines) + "\n"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def explain(parser, message, paint=None):
|
|
48
|
+
"""(message, tip) for argparse's message, painted."""
|
|
49
|
+
import difflib
|
|
50
|
+
import re
|
|
51
|
+
if paint is None:
|
|
52
|
+
from .style import Palette
|
|
53
|
+
paint = Palette(False)
|
|
54
|
+
choice = re.match(r"argument <(?:sub)?command>: invalid choice: '([^']*)'", message)
|
|
55
|
+
if choice:
|
|
56
|
+
word = choice.group(1)
|
|
57
|
+
subs = _subparsers(parser)
|
|
58
|
+
close = difflib.get_close_matches(word, list(subs.choices) if subs else [], n=1, cutoff=.6)
|
|
59
|
+
if close:
|
|
60
|
+
tip = "did you mean " + paint(f"{parser.prog} {close[0]}", "accent") + "?"
|
|
61
|
+
elif parser.prog == "mobster":
|
|
62
|
+
tip = "to run it as a task: " + paint(f"mobster -- {word}", "accent")
|
|
63
|
+
else:
|
|
64
|
+
tip = paint(f"{parser.prog} --help", "accent") + " lists them"
|
|
65
|
+
return paint(word, "amber") + " isn't a command.", tip
|
|
66
|
+
from . import devtools
|
|
67
|
+
if message.startswith("unrecognized arguments:"):
|
|
68
|
+
words = message.split(":", 1)[1].split()
|
|
69
|
+
hint = devtools.option_hint(parser, message)
|
|
70
|
+
unknown = [w for w in words if w.startswith("-")]
|
|
71
|
+
if unknown:
|
|
72
|
+
what = ("unknown option " if len(unknown) == 1 else "unknown options ") + ", ".join(
|
|
73
|
+
paint(w, "amber") for w in unknown)
|
|
74
|
+
extra = [w for w in words if not w.startswith("-")]
|
|
75
|
+
if extra:
|
|
76
|
+
what += "; and " + paint(" ".join(extra), "amber") + " is extra"
|
|
77
|
+
else:
|
|
78
|
+
what = "unexpected " + paint(" ".join(words), "amber")
|
|
79
|
+
what += "."
|
|
80
|
+
tip = ""
|
|
81
|
+
if hint:
|
|
82
|
+
options = hint.removeprefix(". Did you mean ").removesuffix("?").split(" and ")
|
|
83
|
+
tip = "did you mean " + " and ".join(paint(option, "accent") for option in options) + "?"
|
|
84
|
+
return what, tip
|
|
85
|
+
required = re.match(r"the following arguments are required: (.*)", message)
|
|
86
|
+
if required:
|
|
87
|
+
return f"{required.group(1)} is missing.", ""
|
|
88
|
+
return message, ""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def short_usage(parser):
|
|
92
|
+
"""One line: `mobster run TASK [options]`."""
|
|
93
|
+
parts = [parser.prog]
|
|
94
|
+
for action in parser._actions:
|
|
95
|
+
if action.option_strings or action.help == argparse.SUPPRESS:
|
|
96
|
+
continue
|
|
97
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
98
|
+
parts.append("<command>" if parser.prog == "mobster" else "<subcommand>")
|
|
99
|
+
continue
|
|
100
|
+
name = action.metavar or action.dest.upper()
|
|
101
|
+
if isinstance(name, tuple):
|
|
102
|
+
name = " ".join(name)
|
|
103
|
+
parts.append(f"[{name}]" if action.nargs in ("?", "*") else f"{name} ..." if action.nargs == "+" else name)
|
|
104
|
+
if any(action.option_strings and action.help != argparse.SUPPRESS and action.dest != "help"
|
|
105
|
+
for action in parser._actions):
|
|
106
|
+
parts.append("[options]")
|
|
107
|
+
return " ".join(parts)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def colour_help(text, paint):
|
|
111
|
+
"""argparse's help with bold headings and accent commands, as `mobster --help` has them."""
|
|
112
|
+
import re
|
|
113
|
+
out = []
|
|
114
|
+
section = ""
|
|
115
|
+
for line in text.split("\n"):
|
|
116
|
+
heading = re.match(r"^([a-z][a-z ()/]*):(\s*)$", line) or re.match(r"^(usage|exit codes|examples?):(.*)$", line)
|
|
117
|
+
if heading and not line.startswith(" "):
|
|
118
|
+
section = heading.group(1)
|
|
119
|
+
out.append(paint(heading.group(1)[0].upper() + heading.group(1)[1:] + ":", "bold") + heading.group(2))
|
|
120
|
+
continue
|
|
121
|
+
if section.startswith("example") and line.startswith(" mobster"):
|
|
122
|
+
command, gap, rest = re.match(r"^ (\S.*?)(\s{2,}|$)(.*)$", line).groups()
|
|
123
|
+
out.append(" " + paint("$ ", "tertiary") + paint(command, "accent") + gap + rest)
|
|
124
|
+
continue
|
|
125
|
+
option = re.match(r"^( (?:-\w, )?--?[\w-]+(?: [A-Z_<>.\[\]| ]+?)?)(\s{2,}.*|)$", line)
|
|
126
|
+
if option and section not in ("usage",):
|
|
127
|
+
out.append(paint(option.group(1), "accent") + option.group(2))
|
|
128
|
+
continue
|
|
129
|
+
out.append(line)
|
|
130
|
+
return "\n".join(out)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _subparsers(parser):
|
|
134
|
+
return next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class Formatter(argparse.RawDescriptionHelpFormatter):
|
|
138
|
+
"""The help formatter every command's parser shares (`python -m mobile_agent.sim` uses it too)."""
|
|
139
|
+
|
|
140
|
+
def __init__(self, prog):
|
|
141
|
+
super().__init__(prog, max_help_position=30, width=min(100, columns()))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
TRACE_HELP = ("\n\n--trace saves the task's trace as a zip instead: its journal (decisions, actions, cost\n"
|
|
145
|
+
"and time per model call), its outcome, and the tape that replays it offline when the\n"
|
|
146
|
+
"task ran with MOBSTER_EXPERIMENTAL_TRACES=1. Keys, codes and passwords are masked in\n"
|
|
147
|
+
"it. The task text and the text on its screens are not, so read it before you share it.\n"
|
|
148
|
+
"--screens adds the screenshots, which can't be masked.")
|
|
149
|
+
|
|
150
|
+
# A command's own options default to SUPPRESS: argparse copies a subcommand's defaults over the values
|
|
151
|
+
# parsed before it, so `mobster --wda-url URL run …` would otherwise drive the default phone.
|
|
152
|
+
COMMAND_DEFAULT = argparse.SUPPRESS
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def wda_url_type(value):
|
|
156
|
+
"""--wda-url and $MOBSTER_WDA_URL: an http(s) address without credentials, else a usage error (exit 2)."""
|
|
157
|
+
from urllib.parse import urlsplit
|
|
158
|
+
parts = urlsplit(value)
|
|
159
|
+
try:
|
|
160
|
+
parts.port
|
|
161
|
+
except ValueError:
|
|
162
|
+
parts = None
|
|
163
|
+
if (parts is None or parts.scheme not in {"http", "https"} or not parts.hostname or parts.username
|
|
164
|
+
or parts.password or parts.query or parts.fragment):
|
|
165
|
+
# The value is not echoed: it may hold a user name and password.
|
|
166
|
+
raise argparse.ArgumentTypeError(f"expected an http:// address such as {DEFAULT_WDA_URL}")
|
|
167
|
+
return value.rstrip("/")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def path_type(value):
|
|
171
|
+
"""A path from the command line, with ~ expanded: shells leave `--env-file=~/keys.env` as typed."""
|
|
172
|
+
from pathlib import Path
|
|
173
|
+
return Path(value).expanduser()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _bounded(kind, low, high=None, above=False):
|
|
177
|
+
"""An argparse type for a number from ``low`` (exclusive with ``above``) up to ``high``."""
|
|
178
|
+
import math
|
|
179
|
+
word = "a whole number" if kind is int else "a number"
|
|
180
|
+
bounds = (f"more than {low:g}" if above else f"at least {low:g}") + (
|
|
181
|
+
f" and at most {high:g}" if high is not None else "")
|
|
182
|
+
|
|
183
|
+
def parse(value):
|
|
184
|
+
try:
|
|
185
|
+
number = kind(value)
|
|
186
|
+
except ValueError:
|
|
187
|
+
raise argparse.ArgumentTypeError(f"expected {word}, got {value!r}") from None
|
|
188
|
+
if not math.isfinite(number) or (number <= low if above else number < low) or (
|
|
189
|
+
high is not None and number > high):
|
|
190
|
+
raise argparse.ArgumentTypeError(f"must be {bounds}")
|
|
191
|
+
return number
|
|
192
|
+
return parse
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _long_budget(value):
|
|
196
|
+
"""--budget: 10m, 90s, 1h, 30 or all (longform.budget.from_cli); a usage error otherwise."""
|
|
197
|
+
from .longform.budget import from_cli
|
|
198
|
+
try:
|
|
199
|
+
from_cli(value)
|
|
200
|
+
except ValueError as error:
|
|
201
|
+
raise argparse.ArgumentTypeError(str(error).replace("--budget ", "")) from None
|
|
202
|
+
return value
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _wda_option(parser, target=None, default=COMMAND_DEFAULT, help=None):
|
|
206
|
+
(target or parser).add_argument(
|
|
207
|
+
"--wda-url", metavar="URL", type=wda_url_type, default=default,
|
|
208
|
+
help=help or f"the phone's address, for a phone you set up by hand (default: {DEFAULT_WDA_URL}) "
|
|
209
|
+
"[env: MOBSTER_WDA_URL]")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _device_option(parser, target=None, default=COMMAND_DEFAULT, help=None):
|
|
213
|
+
(target or parser).add_argument(
|
|
214
|
+
"--device", metavar="NAME", default=default,
|
|
215
|
+
help=help or "the iPhone or simulator to use: an id, UDID or name from `mobster devices` (default: your "
|
|
216
|
+
"iPhone)")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _env_option(parser, default_from_environment=True, default=COMMAND_DEFAULT):
|
|
220
|
+
parser.add_argument("--env-file", type=path_type, metavar="PATH", default=default,
|
|
221
|
+
help="load KEY=VALUE lines (ANTHROPIC_API_KEY, OPENAI_API_KEY, ...); "
|
|
222
|
+
"variables already set win"
|
|
223
|
+
+ (" [env: MOBSTER_ENV_FILE]" if default_from_environment else ""))
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _tui_options(parser, top_level=False):
|
|
227
|
+
"""``top_level``: the bare `mobster` parser, which sets the real defaults; `mobster tui` suppresses its own."""
|
|
228
|
+
unset, off = (None, False) if top_level else (COMMAND_DEFAULT, COMMAND_DEFAULT)
|
|
229
|
+
_wda_option(parser, default=unset)
|
|
230
|
+
_device_option(parser, default=unset)
|
|
231
|
+
_env_option(parser, default=unset)
|
|
232
|
+
parser.add_argument("--demo", action="store_true", default=off,
|
|
233
|
+
help="use a scripted phone and policy: no iPhone, no keys, no model calls")
|
|
234
|
+
parser.add_argument("--resume", nargs="?", const="latest", metavar="ID", default=unset,
|
|
235
|
+
help="open a past task and put it back in the prompt (the latest without an ID)")
|
|
236
|
+
parser.add_argument("-c", "--continue", dest="continue_", action="store_true", default=off,
|
|
237
|
+
help="continue your last conversation")
|
|
238
|
+
parser.add_argument("--app", metavar="NAME", default=unset,
|
|
239
|
+
help="start with this app chosen (a name or bundle ID)")
|
|
240
|
+
parser.add_argument("--no-bell", action="store_true", default=off,
|
|
241
|
+
help="don't ring the terminal bell when Mobster needs you")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def build_parser(load="all"):
|
|
245
|
+
"""The whole command line."""
|
|
246
|
+
from . import devtools
|
|
247
|
+
from .help_topics import HELP
|
|
248
|
+
parser = Parser(prog="mobster", formatter_class=Formatter, usage='mobster [options] ["TASK"]\n'
|
|
249
|
+
" mobster <command> [options]", description=None, add_help=False)
|
|
250
|
+
options = parser.add_argument_group("options (the terminal UI)")
|
|
251
|
+
_tui_options(options, top_level=True)
|
|
252
|
+
options.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}",
|
|
253
|
+
help="print the version and exit")
|
|
254
|
+
options.add_argument("-h", "--help", action=TopHelp, help="show this help and exit")
|
|
255
|
+
subs = parser.add_subparsers(dest="command", metavar="<command>", help=argparse.SUPPRESS)
|
|
256
|
+
|
|
257
|
+
def command(name, help, description, epilog=None):
|
|
258
|
+
return subs.add_parser(name, prog=f"mobster {name}", help=HELP.get(name, help), description=description,
|
|
259
|
+
epilog=epilog, formatter_class=Formatter)
|
|
260
|
+
|
|
261
|
+
login = command("login", HELP["login"], (
|
|
262
|
+
"Save your Claude or OpenAI key for Mobster's agent, on this Mac. Mobster tests the key\n"
|
|
263
|
+
"with one tiny request first, then saves it in Mobster's settings file, which only you\n"
|
|
264
|
+
"can read. Mobster for Mac and every `mobster` command use it. The key is never shown:\n"
|
|
265
|
+
"only its first characters and last four.\n\n"
|
|
266
|
+
"`mobster login <plan or provider>` saves a plan's key or sign-in (Kimi Code, Grok ...)\n"
|
|
267
|
+
"or another provider's key. A ChatGPT, Claude or Google plan runs through your own\n"
|
|
268
|
+
"Codex, Claude Code or Gemini CLI, which signs in itself: `mobster login codex` says how.\n\n"
|
|
269
|
+
"`mobster login status` shows which keys, plans and providers are saved."),
|
|
270
|
+
epilog=("exit codes: 0 saved (or status shown), 1 the key or the sign-in didn't work, 2 usage,\n"
|
|
271
|
+
" 130 ctrl+c\n\n"
|
|
272
|
+
"examples:\n"
|
|
273
|
+
" mobster login\n"
|
|
274
|
+
" mobster login --provider openai\n"
|
|
275
|
+
" mobster login --provider anthropic --with-key < key.txt\n"
|
|
276
|
+
" mobster login kimi\n"
|
|
277
|
+
" mobster login status"))
|
|
278
|
+
login.add_argument("action", nargs="?", metavar="status|PLAN|PROVIDER",
|
|
279
|
+
help="status: show what is saved, masked; a plan or provider: kimi, zai, grok, openrouter, "
|
|
280
|
+
"xai, custom ...; codex, claude or gemini: how that CLI signs in")
|
|
281
|
+
login.add_argument("--provider", choices=["anthropic", "openai"], default=None,
|
|
282
|
+
help="anthropic (Claude, the default) or openai")
|
|
283
|
+
login.add_argument("--with-key", action="store_true",
|
|
284
|
+
help="read the key from standard input (a pipe or a file, never a terminal)")
|
|
285
|
+
login.add_argument("--skip-check", action="store_true", help="save it without testing it (no network)")
|
|
286
|
+
login.add_argument("--json", action="store_true", help="print the result as JSON")
|
|
287
|
+
login.add_argument("--model", metavar="ID", help="a plan or provider: the model Mobster's agent uses there")
|
|
288
|
+
login.add_argument("--base-url", metavar="URL", dest="base_url",
|
|
289
|
+
help="custom, ollama or lmstudio: the endpoint's base URL, such as http://127.0.0.1:11434/v1")
|
|
290
|
+
|
|
291
|
+
logout = command("logout", HELP["logout"], (
|
|
292
|
+
"Remove a saved key from this Mac: from Mobster's settings file, which Mobster for Mac\n"
|
|
293
|
+
"uses too. A key set in your shell or an --env-file stays where it is.\n\n"
|
|
294
|
+
"`mobster logout <plan or provider>` forgets that plan's or provider's key or sign-in."),
|
|
295
|
+
epilog="examples:\n mobster logout\n mobster logout --provider openai\n mobster logout kimi")
|
|
296
|
+
logout.add_argument("account", nargs="?", metavar="PLAN|PROVIDER",
|
|
297
|
+
help="forget a plan's or provider's key or sign-in, instead of removing a key")
|
|
298
|
+
logout.add_argument("--provider", choices=["anthropic", "openai"], default=None,
|
|
299
|
+
help="which key to remove (default: the one Mobster's agent uses)")
|
|
300
|
+
|
|
301
|
+
setup = command("setup", HELP["setup"], (
|
|
302
|
+
"Get your iPhone ready for Mobster: plug it in, trust this Mac, turn on Developer Mode\n"
|
|
303
|
+
"and put Mobster's helper on it. Each step ticks itself off as it happens. The same\n"
|
|
304
|
+
"guided setup as Mobster for Mac's."),
|
|
305
|
+
epilog=("exit codes: 0 your iPhone is ready, 1 a step failed, 3 couldn't run (no Xcode, no tools),\n"
|
|
306
|
+
" 130 ctrl+c\n\n"
|
|
307
|
+
"examples:\n mobster setup\n mobster setup --device \"Sam's iPhone\""))
|
|
308
|
+
setup.add_argument("--device", metavar="NAME", help="the iPhone to set up, when several are plugged in")
|
|
309
|
+
setup.add_argument("--team", metavar="ID", help="a paid Apple developer team ID to sign Mobster's helper with")
|
|
310
|
+
setup.add_argument("--json", action="store_true", help="print each change as a JSON line")
|
|
311
|
+
|
|
312
|
+
# verify, sim, mcp and the track commands: fixed help text, flags from each package's cli module (devtools.py).
|
|
313
|
+
devtools.add_commands(command, {"env_option": _env_option, "path_type": path_type, "bounded": _bounded},
|
|
314
|
+
load_only=None if load == "all" else set(load or ()))
|
|
315
|
+
|
|
316
|
+
run = command("run", HELP["run"], (
|
|
317
|
+
"Run one task on your iPhone and print each step. On a terminal each step prints as\n"
|
|
318
|
+
"a line; piped, or with --json, every event prints as one JSON object per line, and\n"
|
|
319
|
+
"the last is the result.\n\n"
|
|
320
|
+
"Mobster's agent runs it on your Claude or OpenAI key. By default, it asks before it\n"
|
|
321
|
+
"sends, buys, posts or deletes: on a terminal you answer y or n; with --json or a pipe\n"
|
|
322
|
+
"the answer is no, and the run ends as declined.\n\n"
|
|
323
|
+
"Long tasks (\"scroll basketball Reels for 10 minutes and tell me what you learned\",\n"
|
|
324
|
+
"\"go through my unread emails\"): Mobster prints a plan with its estimate and asks\n"
|
|
325
|
+
"Start? [Y/n] on a terminal (--yes starts at once). It covers the budget in as little\n"
|
|
326
|
+
"phone time as it can, hands the phone back, then prints the report as Markdown.\n\n"
|
|
327
|
+
"ctrl+c stops at the next safe point (twice to force)."),
|
|
328
|
+
epilog=("exit codes: 0 done, 1 the task didn't finish, 2 usage error,\n"
|
|
329
|
+
" 3 couldn't run (no phone answered, no key), 130 stopped with ctrl+c\n\n"
|
|
330
|
+
"examples:\n"
|
|
331
|
+
" mobster run \"Turn on Dark Mode\" --execute\n"
|
|
332
|
+
" mobster run \"What's my battery level?\" --execute --json\n"
|
|
333
|
+
" mobster run \"Turn on Dark Mode\" --demo a scripted phone: no iPhone, no keys\n"
|
|
334
|
+
" mobster run \"Scroll basketball Reels for 10 minutes and tell me what you learned\" --execute\n"
|
|
335
|
+
" mobster run \"Go through my unread emails\" --execute --long --budget all"))
|
|
336
|
+
run.add_argument("goal", metavar="TASK", help="what to do, in plain words")
|
|
337
|
+
target = run.add_mutually_exclusive_group()
|
|
338
|
+
_wda_option(run, target)
|
|
339
|
+
_device_option(run, target)
|
|
340
|
+
_env_option(run)
|
|
341
|
+
run.add_argument("--execute", action="store_true", help="act on the phone (a task needs it)")
|
|
342
|
+
run.add_argument("--demo", action="store_true", default=COMMAND_DEFAULT,
|
|
343
|
+
help="a scripted phone and policy: no iPhone, no keys, no model calls")
|
|
344
|
+
run.add_argument("--dry-run", action="store_true",
|
|
345
|
+
help="print the apps it would open, the acts it would ask about and the cost, and send nothing "
|
|
346
|
+
"to the phone")
|
|
347
|
+
run.add_argument("--json", action="store_true", help="print raw JSON events, even on a terminal")
|
|
348
|
+
# Checked here, as a usage error (exit 2), before anything reaches the phone.
|
|
349
|
+
run.add_argument("--spend-cap-usd", type=_bounded(float, 0, above=True), default=None, metavar="USD",
|
|
350
|
+
help="stop before further model calls once this task has cost USD")
|
|
351
|
+
run.add_argument("--state-db", type=path_type, default=None, metavar="PATH",
|
|
352
|
+
help="the private task journal this task is saved in (default: the terminal UI's, which "
|
|
353
|
+
"`mobster history` lists)")
|
|
354
|
+
# Long tasks (mobile_agent/longform).
|
|
355
|
+
run.add_argument("--long", action="store_true",
|
|
356
|
+
help="run it as a long task, with a plan first and a report at the end (most long tasks are "
|
|
357
|
+
"found on their own)")
|
|
358
|
+
run.add_argument("--budget", type=_long_budget, default=None, metavar="VALUE",
|
|
359
|
+
help="a long task's budget: a time (10m, 90s, 1h), a number of items (30) or all")
|
|
360
|
+
run.add_argument("--yes", action="store_true", help="start a long task without asking (its plan still prints)")
|
|
361
|
+
|
|
362
|
+
doctor = command("doctor", HELP["doctor"], (
|
|
363
|
+
"Check everything a task needs and print how to fix what is missing. Without --fix it\n"
|
|
364
|
+
"changes nothing. Keys are shown masked: their first characters and last four.\n\n"
|
|
365
|
+
"--fix does the fix each finding carries, then checks again and prints what it did and what\n"
|
|
366
|
+
"it could not do. It starts Mobster's helper, or refreshes one that expired, on its own, and\n"
|
|
367
|
+
"leaves it to Mobster for Mac when that is running. It names a program that holds a port, or a\n"
|
|
368
|
+
"service that doesn't answer, and asks before it stops it. It hands over to `mobster login`\n"
|
|
369
|
+
"and `mobster setup` for what needs your answers. It never enters a passcode, runs sudo or\n"
|
|
370
|
+
"downloads a runtime. Without a terminal, or with --json, it skips every question and says\n"
|
|
371
|
+
"which."),
|
|
372
|
+
epilog=("exit codes: 0 ready for tasks (warnings allowed), 1 at least one problem\n\n"
|
|
373
|
+
"examples:\n mobster doctor\n mobster doctor --fix"))
|
|
374
|
+
_wda_option(doctor)
|
|
375
|
+
_device_option(doctor)
|
|
376
|
+
_env_option(doctor)
|
|
377
|
+
doctor.add_argument("--fix", action="store_true",
|
|
378
|
+
help="do the fix each finding carries, then check again")
|
|
379
|
+
doctor.add_argument("--json", action="store_true", help="print the checks as JSON")
|
|
380
|
+
doctor.add_argument("--simulator", action="store_true",
|
|
381
|
+
help="skip the USB iPhone and Xcode checks (a simulator's phone)")
|
|
382
|
+
doctor.add_argument("--apps", action="store_true", help=argparse.SUPPRESS)
|
|
383
|
+
|
|
384
|
+
history = command("history", HELP["history"], (
|
|
385
|
+
"List the tasks you ran in this terminal, newest first. Open one with\n"
|
|
386
|
+
"`mobster --resume ID`, or save one as a GIF with `mobster export ID --gif`. The\n"
|
|
387
|
+
"history holds task text and screen text: keep it private."))
|
|
388
|
+
history.add_argument("--json", action="store_true", help="print one JSON object per task")
|
|
389
|
+
history.add_argument("-n", "--limit", type=_bounded(int, 1, 10000), default=20, metavar="N",
|
|
390
|
+
help="show N tasks (default: 20)")
|
|
391
|
+
history.add_argument("--to-eval", metavar="ID", help=(
|
|
392
|
+
"keep a finished task as a regression task in your evals, to replay offline" if traces.enabled()
|
|
393
|
+
else argparse.SUPPRESS))
|
|
394
|
+
|
|
395
|
+
traces_on = traces.enabled() # the trace options stay out of the help until MOBSTER_EXPERIMENTAL_TRACES is set
|
|
396
|
+
export = command("export", HELP["export"], (
|
|
397
|
+
"Save a finished task as a captioned GIF, one screen per step with what Mobster did,\n"
|
|
398
|
+
"ready to post: under X's 15 MB limit, on the Paper or Night ground. --mp4 saves an MP4\n"
|
|
399
|
+
"too (it needs ffmpeg). The file goes to ~/Downloads and never replaces one there.\n"
|
|
400
|
+
"It shows every screen of the task, so check it before you post it: --redact blurs\n"
|
|
401
|
+
"text fields and messages and leaves the answer out. Nothing is uploaded."
|
|
402
|
+
+ (TRACE_HELP if traces_on else "")),
|
|
403
|
+
epilog=("exit codes: 0 saved, 1 couldn't (no such task, no screens, no ffmpeg for --mp4), 2 usage\n\n"
|
|
404
|
+
"examples:\n"
|
|
405
|
+
" mobster export latest --gif\n"
|
|
406
|
+
" mobster export 3f9c2a1b7d10 --gif --mp4 --theme night\n"
|
|
407
|
+
" mobster export 3f9c --gif --redact -o ~/Desktop/clip.gif"
|
|
408
|
+
+ ("\n mobster export 3f9c --trace -o ~/Desktop/" if traces_on else "")))
|
|
409
|
+
export.add_argument("run_id", metavar="ID", help="a task's ID from `mobster history` (its first characters do), "
|
|
410
|
+
"or latest")
|
|
411
|
+
export.add_argument("--gif", action="store_true", help="save a GIF")
|
|
412
|
+
export.add_argument("--mp4", action="store_true", help="save an MP4 (needs ffmpeg)")
|
|
413
|
+
export.add_argument("--trace", action="store_true", help=(
|
|
414
|
+
"save the task's trace bundle (a zip) for a bug report or an offline replay"
|
|
415
|
+
if traces_on else argparse.SUPPRESS))
|
|
416
|
+
export.add_argument("--screens", action="store_true", help=(
|
|
417
|
+
"with --trace, add the screenshots (they can't be masked: check them first)" if traces_on
|
|
418
|
+
else argparse.SUPPRESS))
|
|
419
|
+
export.add_argument("--redact", action="store_true",
|
|
420
|
+
help="blur text fields and messages, and leave out the answer")
|
|
421
|
+
export.add_argument("--theme", choices=["paper", "night"], default="paper",
|
|
422
|
+
help="the ground: paper (light, the default) or night (dark)")
|
|
423
|
+
export.add_argument("-o", "--output", type=path_type, metavar="PATH",
|
|
424
|
+
help="a file or folder to save to (default: ~/Downloads)")
|
|
425
|
+
export.add_argument("--json", action="store_true", help="print where it saved as JSON")
|
|
426
|
+
|
|
427
|
+
screen = command("screen", HELP["screen"], (
|
|
428
|
+
"Print your iPhone's current screen. kitty, Ghostty, iTerm2 and WezTerm show the\n"
|
|
429
|
+
"real image; other terminals get a half-block drawing. Nothing is tapped."))
|
|
430
|
+
_wda_option(screen)
|
|
431
|
+
_device_option(screen)
|
|
432
|
+
screen.add_argument("--out", type=path_type, metavar="FILE", help="save the PNG to FILE instead of printing it")
|
|
433
|
+
screen.add_argument("--width", type=_bounded(int, 1), metavar="COLUMNS", help="draw it this many columns wide")
|
|
434
|
+
|
|
435
|
+
serve = command("serve", HELP["serve"], (
|
|
436
|
+
"Start the loopback HTTP API (127.0.0.1 only) that Mobster for Mac launches as its\n"
|
|
437
|
+
"sidecar. Every request needs the launch's API token. Scripting with it:\n"
|
|
438
|
+
f"{links.SCRIPTING}"))
|
|
439
|
+
serve.add_argument("--port", type=_bounded(int, 0, 65535), default=8765,
|
|
440
|
+
help="port on 127.0.0.1 (default: 8765; 0 picks a free one)")
|
|
441
|
+
# serve does not read $MOBSTER_WDA_URL: the Mac app decides which phone its sidecar drives.
|
|
442
|
+
_wda_option(serve, help=f"the WebDriverAgent to drive (default: none, or {DEFAULT_WDA_URL} with "
|
|
443
|
+
"--manage-device); a simulator's runner listens on its own port")
|
|
444
|
+
_device_option(serve, help="the device a task that names none runs on: an id, UDID or name from "
|
|
445
|
+
"`mobster devices` (default: the only device, else the one last used)")
|
|
446
|
+
serve.add_argument("--manage-device", action="store_true",
|
|
447
|
+
help="own the USB iPhone setup: build, run and supervise Mobster's helper (the Mac app does "
|
|
448
|
+
"this)")
|
|
449
|
+
serve.add_argument("--data-dir", type=path_type, metavar="PATH", help="where the managed device keeps its files")
|
|
450
|
+
serve.add_argument("--exit-with-parent", action="store_true",
|
|
451
|
+
help="shut down when the launching process exits (the Mac app's safety net)")
|
|
452
|
+
serve.add_argument("--keep-runner", action="store_true",
|
|
453
|
+
help="leave the iPhone runner running on exit, for development reloads; "
|
|
454
|
+
"the next server adopts it")
|
|
455
|
+
_env_option(serve, default_from_environment=False)
|
|
456
|
+
serve.add_argument("--session", metavar="ID", help="preferred WDA session; the one WDA is serving always wins")
|
|
457
|
+
serve.add_argument("--enable-live", action="store_true", help="allow tasks to act on the phone")
|
|
458
|
+
serve.add_argument("--spend-cap-usd", type=_bounded(float, 0, above=True), default=None, metavar="USD",
|
|
459
|
+
help="stop a run before further model calls once its observed inference spend reaches USD")
|
|
460
|
+
serve.add_argument("--state-db", type=path_type, default=None, metavar="PATH",
|
|
461
|
+
help="the private task journal (never automatically replays interrupted tasks); "
|
|
462
|
+
"default: .state/ beside the source in a checkout, else <data dir>/state/")
|
|
463
|
+
|
|
464
|
+
devices = command("devices", HELP["devices"], (
|
|
465
|
+
"List every device Mobster can drive: USB iPhones (set up or newly plugged in),\n"
|
|
466
|
+
"Mobster's simulators, and phone addresses from MOBSTER_WDA_DEVICES. Each shows\n"
|
|
467
|
+
"its state: Ready, In use, Connected, Needs setup or Unplugged. Name one with\n"
|
|
468
|
+
"--device on run, screen, doctor, serve, mcp and the terminal UI. It changes nothing.\n"
|
|
469
|
+
"\n"
|
|
470
|
+
"`mobster devices lock-probe` reads whether an iPhone is locked or showing a system\n"
|
|
471
|
+
"sheet (Face ID, a locked app, the App Store's side-button confirmation) the way a\n"
|
|
472
|
+
"task's check does, and lists what the lock screen shows. It taps and types nothing.\n"
|
|
473
|
+
"\n"
|
|
474
|
+
"`mobster devices status` reads a device's battery and thermal state, and whether\n"
|
|
475
|
+
"this Mac is on battery. A value the device doesn't report is left out.\n"
|
|
476
|
+
"Without --device it reads the only device, else the first iPhone set up."),
|
|
477
|
+
epilog=("examples:\n"
|
|
478
|
+
" mobster devices\n"
|
|
479
|
+
" mobster devices --json\n"
|
|
480
|
+
" mobster devices lock-probe --device \"Sam's iPhone\"\n"
|
|
481
|
+
" mobster devices status --device \"Sam's iPhone\"\n"
|
|
482
|
+
" mobster run \"Turn on Dark Mode\" --execute --device \"Sam's iPhone\""))
|
|
483
|
+
devices.add_argument("action", nargs="?", choices=["lock-probe", "status"], metavar="lock-probe|status",
|
|
484
|
+
help="lock-probe: the lock state and system sheets; status: battery and heat. "
|
|
485
|
+
"Neither taps anything")
|
|
486
|
+
devices.add_argument("--json", action="store_true", help="print the devices (or the probe, or the status) as JSON")
|
|
487
|
+
_wda_option(devices, help="lock-probe, status: the phone's address (default: $MOBSTER_WDA_URL, else "
|
|
488
|
+
f"{DEFAULT_WDA_URL})")
|
|
489
|
+
_device_option(devices, help="lock-probe, status: the device to read, by id, UDID or name")
|
|
490
|
+
|
|
491
|
+
demo = command("demo", HELP["demo"], (
|
|
492
|
+
"Run one task offline, \"Search for coffee\", on a scripted phone and model: the real\n"
|
|
493
|
+
"runtime and agent loop, no iPhone, no keys and no model calls. The same as `mobster\n"
|
|
494
|
+
"run \"Search for coffee\" --demo`. For the terminal UI on it, run `mobster --demo`."))
|
|
495
|
+
demo.add_argument("--json", action="store_true", help="print raw JSON events, even on a terminal")
|
|
496
|
+
|
|
497
|
+
completion = command("completion", HELP["completion"], (
|
|
498
|
+
"Print a completion script for your shell, so tab completes commands, options, your\n"
|
|
499
|
+
"devices and past task IDs. Homebrew installs the zsh and fish ones for you."),
|
|
500
|
+
epilog=("examples:\n"
|
|
501
|
+
" mobster completion zsh > \"${fpath[1]}/_mobster\"\n"
|
|
502
|
+
" mobster completion bash > ~/.local/share/bash-completion/completions/mobster\n"
|
|
503
|
+
" mobster completion fish > ~/.config/fish/completions/mobster.fish"))
|
|
504
|
+
completion.add_argument("shell", nargs="?", choices=["zsh", "bash", "fish"], help="zsh, bash or fish")
|
|
505
|
+
completion.add_argument("--values", choices=["devices", "tasks", "threads"], help=argparse.SUPPRESS)
|
|
506
|
+
|
|
507
|
+
update = command("update", HELP["update"], (
|
|
508
|
+
"Check for a newer standalone CLI on PyPI, or a frozen CLI archive on GitHub.\n"
|
|
509
|
+
"Upgrade with the installer you used: pip, uv, npm or the frozen archive. This\n"
|
|
510
|
+
"command installs nothing. A copy owned by Mobster for Mac uses the app's updater.\n"
|
|
511
|
+
"Mobster checks once a day after interactive commands; MOBSTER_NO_UPDATE_CHECK=1\n"
|
|
512
|
+
"turns off the daily check. An explicit mobster update still checks."),
|
|
513
|
+
epilog="examples:\n mobster update\n mobster update --check")
|
|
514
|
+
update.add_argument("--check", action="store_true", help="only say whether a new release is out")
|
|
515
|
+
|
|
516
|
+
version = command("version", HELP["version"], "Print the version of Mobster.")
|
|
517
|
+
version.add_argument("--json", action="store_true", help="print the version, Python and platform as JSON")
|
|
518
|
+
|
|
519
|
+
tui = command("tui", HELP["tui"], "Open the terminal UI. The same as running `mobster` with no command.")
|
|
520
|
+
_tui_options(tui)
|
|
521
|
+
|
|
522
|
+
helping = command("help", HELP["help"],
|
|
523
|
+
"Show the help for a command, or a topic: environment, exit-codes, tui. `mobster help --all`\n"
|
|
524
|
+
"lists every command.")
|
|
525
|
+
helping.add_argument("topic", nargs="*", metavar="COMMAND", help="a command (and a subcommand), or a topic")
|
|
526
|
+
helping.add_argument("--all", action="store_true", help="list every command, the hidden ones too")
|
|
527
|
+
|
|
528
|
+
command("build-ocr", HELP["build-ocr"], "Compile the standalone Apple Vision OCR helper into the build folder.")
|
|
529
|
+
|
|
530
|
+
commands = {"run": run, "serve": serve, "run_target": target, "doctor": doctor, "screen": screen}
|
|
531
|
+
for extension in harness_api.hooks().cli:
|
|
532
|
+
extension.add_arguments(subs, commands)
|
|
533
|
+
parser.commands = subs.choices # for check_placement
|
|
534
|
+
_tidy(parser)
|
|
535
|
+
return parser
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class TopHelp(argparse._HelpAction):
|
|
539
|
+
"""`mobster -h` with other options (`mobster --demo -h`): the same grouped help as `mobster --help`."""
|
|
540
|
+
|
|
541
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
542
|
+
from . import help_topics
|
|
543
|
+
print(help_topics.top_level(palette(sys.stdout), columns()))
|
|
544
|
+
parser.exit()
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def _tidy(parser):
|
|
548
|
+
"""Every parser's -h says "show this help and exit"; a SUPPRESSed subcommand stays out of its parent's list."""
|
|
549
|
+
seen = set()
|
|
550
|
+
stack = [parser]
|
|
551
|
+
while stack:
|
|
552
|
+
current = stack.pop()
|
|
553
|
+
if id(current) in seen:
|
|
554
|
+
continue
|
|
555
|
+
seen.add(id(current))
|
|
556
|
+
for action in current._actions:
|
|
557
|
+
if isinstance(action, argparse._HelpAction):
|
|
558
|
+
action.help = "show this help and exit"
|
|
559
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
560
|
+
# Python 3.12 lists a SUPPRESSed subcommand as "==SUPPRESS==".
|
|
561
|
+
action._choices_actions = [choice for choice in action._choices_actions
|
|
562
|
+
if choice.help != argparse.SUPPRESS]
|
|
563
|
+
stack.extend(action.choices.values())
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def owner(parser, args):
|
|
567
|
+
"""The innermost parser ``args`` went through: `mobster sim prepare`'s for `mobster sim prepare --dev x`."""
|
|
568
|
+
target = parser
|
|
569
|
+
while True:
|
|
570
|
+
subs = _subparsers(target)
|
|
571
|
+
chosen = getattr(args, subs.dest, None) if subs is not None else None
|
|
572
|
+
if chosen not in (subs.choices if subs is not None else ()):
|
|
573
|
+
return target
|
|
574
|
+
target = subs.choices[chosen]
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def print_help(parser, topic, everything=False):
|
|
578
|
+
"""`mobster help [COMMAND [SUBCOMMAND] | TOPIC]`: that command's --help, or a topic, on stdout, exit 0."""
|
|
579
|
+
from . import help_topics
|
|
580
|
+
if everything:
|
|
581
|
+
subs = _subparsers(parser)
|
|
582
|
+
helps = {action.dest: action.help for action in subs._choices_actions}
|
|
583
|
+
helps.update({name: help_topics.HELP.get(name, "") for name in subs.choices if name not in helps})
|
|
584
|
+
print(help_topics.all_commands(palette(sys.stdout), helps))
|
|
585
|
+
return EXIT_OK
|
|
586
|
+
if topic and topic[0] in help_topics.TOPICS:
|
|
587
|
+
return print_topic(topic[0])
|
|
588
|
+
target = parser
|
|
589
|
+
for word in topic:
|
|
590
|
+
subs = _subparsers(target)
|
|
591
|
+
if subs is None or word not in subs.choices:
|
|
592
|
+
target.error(f"argument <command>: invalid choice: {word!r}")
|
|
593
|
+
target = subs.choices[word]
|
|
594
|
+
if target is parser:
|
|
595
|
+
print(help_topics.top_level(palette(sys.stdout), columns()))
|
|
596
|
+
return EXIT_OK
|
|
597
|
+
target.print_help()
|
|
598
|
+
return EXIT_OK
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def print_topic(name):
|
|
602
|
+
from . import help_topics
|
|
603
|
+
paint, width = palette(sys.stdout), columns()
|
|
604
|
+
if name == "environment":
|
|
605
|
+
print(help_topics.environment(paint, width))
|
|
606
|
+
elif name == "exit-codes":
|
|
607
|
+
print(help_topics.exit_codes(paint, width))
|
|
608
|
+
else:
|
|
609
|
+
from .tui.keys import KEYS
|
|
610
|
+
print(help_topics.tui(paint, width, KEYS))
|
|
611
|
+
return EXIT_OK
|