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,472 @@
|
|
|
1
|
+
"""`mobster mcp install`, `mobster mcp clients` and `mobster mcp doctor`."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from . import clients as registry
|
|
9
|
+
from . import rules, skill
|
|
10
|
+
|
|
11
|
+
USAGE_EXIT, FAILED_EXIT = 2, 1
|
|
12
|
+
RESTART = {"claude-desktop", "cursor", "vscode", "windsurf", "zed", "cline", "xcode"}
|
|
13
|
+
|
|
14
|
+
INSTALL_DESCRIPTION = (
|
|
15
|
+
"Add Mobster's MCP server to your agents' configs: Claude Code, Codex, Cursor, VS Code, Claude\n"
|
|
16
|
+
"Desktop, opencode, Gemini CLI, Windsurf, Copilot CLI, Zed, Goose, Amp, Cline, oh-my-pi (omp)\n"
|
|
17
|
+
"and the agents inside Xcode. It writes the absolute path of this mobster, because apps opened from\n"
|
|
18
|
+
"the Dock don't see your shell's PATH.\n"
|
|
19
|
+
"Claude Code and Codex are set up with their own `mcp add` commands; the rest by editing the file,\n"
|
|
20
|
+
"keeping its comments and formatting, with a copy of the old file beside it (.mobster-backup).\n"
|
|
21
|
+
"Reinstalling keeps each client's server options and environment. Explicit options replace their own\n"
|
|
22
|
+
"group; --reset-server-options starts fresh. Smart's key stays in --env-file.")
|
|
23
|
+
INSTALL_EPILOG = (
|
|
24
|
+
"examples:\n"
|
|
25
|
+
" mobster mcp install --all every client found on this Mac\n"
|
|
26
|
+
" mobster mcp install claude-code cursor --with-skill\n"
|
|
27
|
+
" mobster mcp install codex --env-file ~/.config/mobster/agent.env\n"
|
|
28
|
+
" mobster mcp install --all --allow-device \"Test iPhone\"\n"
|
|
29
|
+
" mobster mcp install cursor --project . this repository's .cursor/mcp.json\n"
|
|
30
|
+
" mobster mcp install cursor --with-skill --rules . this repository's AGENTS.md and .cursor/rules\n"
|
|
31
|
+
" mobster mcp install xcode the agents inside Xcode\n"
|
|
32
|
+
" mobster mcp install claude-code codex --with-xcode-tools\n"
|
|
33
|
+
" mobster mcp install --all --remove --with-skill\n\n"
|
|
34
|
+
"clients: " + ", ".join(client.id for client in registry.CLIENTS))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def add_subcommands(parser, helpers):
|
|
38
|
+
path_type = helpers.get("path_type") or (lambda value: Path(value).expanduser())
|
|
39
|
+
subs = parser.add_subparsers(dest="mcp_command", metavar="<subcommand>", title="subcommands",
|
|
40
|
+
description="without one, `mobster mcp` serves MCP on stdio")
|
|
41
|
+
install = subs.add_parser("install", help="add Mobster's server to Claude Code, Codex, Cursor and others",
|
|
42
|
+
description=INSTALL_DESCRIPTION, epilog=INSTALL_EPILOG,
|
|
43
|
+
formatter_class=parser.formatter_class)
|
|
44
|
+
install.add_argument("clients", nargs="*", metavar="CLIENT",
|
|
45
|
+
help="the clients to set up, by name (see `mobster mcp clients`)")
|
|
46
|
+
install.add_argument("--all", action="store_true", help="every client found on this Mac")
|
|
47
|
+
install.add_argument("--project", type=path_type, metavar="DIR",
|
|
48
|
+
help="write the project's config in DIR (such as .cursor/mcp.json) instead of yours")
|
|
49
|
+
install.add_argument("--allow-device", action="append", metavar="NAME", dest="allow_devices",
|
|
50
|
+
default=argparse.SUPPRESS, help="the server drives only this real iPhone; repeatable")
|
|
51
|
+
install.add_argument("--allow-testflight", action="append", metavar="BUNDLE", dest="allow_testflight",
|
|
52
|
+
default=argparse.SUPPRESS,
|
|
53
|
+
help="run_tests with pr may drive this app's TestFlight build on an "
|
|
54
|
+
"allowed iPhone; repeatable")
|
|
55
|
+
install.add_argument("--env-file", type=path_type, metavar="PATH", default=argparse.SUPPRESS,
|
|
56
|
+
help="the server loads this file (OPENAI_API_KEY or ANTHROPIC_API_KEY for Smart); it must "
|
|
57
|
+
"be readable by you only (chmod 600)")
|
|
58
|
+
install.add_argument("--reset-server-options", action="store_true",
|
|
59
|
+
help="replace the saved server options and environment instead of keeping them")
|
|
60
|
+
install.add_argument("--with-skill", action="store_true",
|
|
61
|
+
help="also copy the mobster skill into each client's skills folder")
|
|
62
|
+
install.add_argument("--rules", type=path_type, metavar="DIR",
|
|
63
|
+
help="also write Mobster's rules for agents in DIR: a block in AGENTS.md, and "
|
|
64
|
+
".cursor/rules/mobster.mdc for Cursor")
|
|
65
|
+
install.add_argument("--with-xcode-tools", action="store_true",
|
|
66
|
+
help="also add Xcode's own tools (xcrun mcpbridge, Xcode 26.3 or later) "
|
|
67
|
+
"to Claude Code and Codex")
|
|
68
|
+
install.add_argument("--dry-run", action="store_true", help="show what would change and write nothing")
|
|
69
|
+
install.add_argument("--remove", action="store_true", help="take Mobster out of the clients' configs instead")
|
|
70
|
+
install.add_argument("--json", action="store_true", help="print the result as JSON")
|
|
71
|
+
|
|
72
|
+
listing = subs.add_parser("clients", formatter_class=parser.formatter_class,
|
|
73
|
+
help="list the agent clients found here and whether Mobster is set up",
|
|
74
|
+
description="List the agent clients Mobster can set up, whether each is installed "
|
|
75
|
+
"on this Mac, and whether its config runs Mobster's server.")
|
|
76
|
+
listing.add_argument("--project", type=path_type, metavar="DIR", help="look at the project's configs in DIR")
|
|
77
|
+
listing.add_argument("--json", action="store_true", help="print the list as JSON")
|
|
78
|
+
|
|
79
|
+
doctor = subs.add_parser("doctor", formatter_class=parser.formatter_class,
|
|
80
|
+
help="start the server as a client would, list its tools and call status",
|
|
81
|
+
description="Start Mobster's MCP server the way an app opened from the Dock would "
|
|
82
|
+
"(its absolute path, launchd's PATH, the home folder), send initialize, "
|
|
83
|
+
"list the tools and call status, with the time each step took. Then "
|
|
84
|
+
"check that every client's entry runs a mobster that exists.")
|
|
85
|
+
doctor.add_argument("--env-file", type=path_type, metavar="PATH", default=argparse.SUPPRESS,
|
|
86
|
+
help="start the server with this env file, as install would")
|
|
87
|
+
doctor.add_argument("--json", action="store_true", help="print the report as JSON")
|
|
88
|
+
return subs
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def run(args):
|
|
92
|
+
command = getattr(args, "mcp_command", None)
|
|
93
|
+
try:
|
|
94
|
+
if command == "install":
|
|
95
|
+
return install(args)
|
|
96
|
+
if command == "clients":
|
|
97
|
+
return list_clients(args)
|
|
98
|
+
if command == "doctor":
|
|
99
|
+
return doctor(args)
|
|
100
|
+
except KeyboardInterrupt:
|
|
101
|
+
return 130
|
|
102
|
+
print(f"mobster mcp: unknown subcommand {command}", file=sys.stderr)
|
|
103
|
+
return USAGE_EXIT
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# -------------------------------------------------------------------------------------------- install
|
|
107
|
+
|
|
108
|
+
def server_args(args):
|
|
109
|
+
"""The flags the clients' entries pass to `mobster mcp`."""
|
|
110
|
+
extra = []
|
|
111
|
+
env_file = getattr(args, "env_file", None)
|
|
112
|
+
if env_file:
|
|
113
|
+
extra += ["--env-file", str(Path(env_file).expanduser().absolute())]
|
|
114
|
+
for name in getattr(args, "allow_devices", None) or []:
|
|
115
|
+
extra += ["--allow-device", name]
|
|
116
|
+
for bundle in getattr(args, "allow_testflight", None) or []:
|
|
117
|
+
extra += ["--allow-testflight", bundle]
|
|
118
|
+
return extra
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def check_env_file(path):
|
|
122
|
+
"""None when ``path`` is a file only its owner can read, else the sentence that says what to fix."""
|
|
123
|
+
path = Path(path).expanduser()
|
|
124
|
+
try:
|
|
125
|
+
info = path.stat()
|
|
126
|
+
except FileNotFoundError:
|
|
127
|
+
return f"{path} doesn't exist. Create it with one line, OPENAI_API_KEY=… or ANTHROPIC_API_KEY=…, then " \
|
|
128
|
+
f"run `chmod 600 {path}`"
|
|
129
|
+
except OSError as error:
|
|
130
|
+
return f"{path} can't be read ({error.strerror})"
|
|
131
|
+
if not path.is_file():
|
|
132
|
+
return f"{path} isn't a file"
|
|
133
|
+
if info.st_mode & 0o077:
|
|
134
|
+
return f"{path} can be read by other users (mode {info.st_mode & 0o777:o}). Run `chmod 600 {path}`, " \
|
|
135
|
+
"then try again"
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def select(args, env, out):
|
|
140
|
+
"""The clients to work on, or an exit code after printing why."""
|
|
141
|
+
if args.clients and args.all:
|
|
142
|
+
out.error("name clients or use --all, not both")
|
|
143
|
+
return USAGE_EXIT
|
|
144
|
+
if args.all:
|
|
145
|
+
found = [client for client in registry.CLIENTS if client.detected(env)[0]]
|
|
146
|
+
if args.project:
|
|
147
|
+
found = [client for client in found if client.paths(env, args.project)]
|
|
148
|
+
if not found:
|
|
149
|
+
out.error("no agent clients were found on this Mac. Name one to set it up anyway: "
|
|
150
|
+
"mobster mcp install cursor")
|
|
151
|
+
return FAILED_EXIT
|
|
152
|
+
return found
|
|
153
|
+
if not args.clients:
|
|
154
|
+
found = [client.name for client in registry.CLIENTS if client.detected(env)[0]]
|
|
155
|
+
out.error("name the clients to set up, or use --all, such as `mobster mcp install claude-code cursor`."
|
|
156
|
+
+ (f" Found on this Mac: {', '.join(found)}." if found else ""))
|
|
157
|
+
return USAGE_EXIT
|
|
158
|
+
chosen, unknown = [], []
|
|
159
|
+
for name in args.clients:
|
|
160
|
+
client = registry.lookup(name)
|
|
161
|
+
if client is None:
|
|
162
|
+
hint = registry.suggestion(name)
|
|
163
|
+
unknown.append(f"{name!r}" + (f" (did you mean {hint}?)" if hint else ""))
|
|
164
|
+
elif client not in chosen:
|
|
165
|
+
chosen.append(client)
|
|
166
|
+
if unknown:
|
|
167
|
+
out.error(f"unknown client {', '.join(unknown)}. Known clients: "
|
|
168
|
+
+ ", ".join(client.id for client in registry.CLIENTS))
|
|
169
|
+
return USAGE_EXIT
|
|
170
|
+
return chosen
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def install(args, env=None, server=None, stream=None):
|
|
174
|
+
env = env or registry.Env.current()
|
|
175
|
+
out = Output(args, stream)
|
|
176
|
+
if args.remove and getattr(args, "reset_server_options", False):
|
|
177
|
+
out.error("--reset-server-options applies only when installing")
|
|
178
|
+
return USAGE_EXIT
|
|
179
|
+
if getattr(args, "env_file", None) and not args.remove:
|
|
180
|
+
problem = check_env_file(args.env_file)
|
|
181
|
+
if problem:
|
|
182
|
+
out.error(problem)
|
|
183
|
+
return USAGE_EXIT
|
|
184
|
+
for name in getattr(args, "allow_devices", None) or []:
|
|
185
|
+
if not name.strip():
|
|
186
|
+
out.error("--allow-device needs a device's id, UDID or name")
|
|
187
|
+
return USAGE_EXIT
|
|
188
|
+
for bundle in getattr(args, "allow_testflight", None) or []:
|
|
189
|
+
if not bundle.strip():
|
|
190
|
+
out.error("--allow-testflight needs your app's bundle ID")
|
|
191
|
+
return USAGE_EXIT
|
|
192
|
+
chosen = select(args, env, out)
|
|
193
|
+
if isinstance(chosen, int):
|
|
194
|
+
return chosen
|
|
195
|
+
project = Path(args.project).expanduser().absolute() if args.project else None
|
|
196
|
+
if project and not project.is_dir():
|
|
197
|
+
out.error(f"{project} isn't a folder")
|
|
198
|
+
return USAGE_EXIT
|
|
199
|
+
rules_dir = Path(args.rules).expanduser().absolute() if getattr(args, "rules", None) else None
|
|
200
|
+
if rules_dir and not rules_dir.is_dir():
|
|
201
|
+
out.error(f"{rules_dir} isn't a folder")
|
|
202
|
+
return USAGE_EXIT
|
|
203
|
+
targets = [client for client in chosen if client.id in ("claude-code", "codex")]
|
|
204
|
+
if getattr(args, "with_xcode_tools", False):
|
|
205
|
+
if project:
|
|
206
|
+
out.error("--with-xcode-tools sets up your own config, not a project's. Leave out --project")
|
|
207
|
+
return USAGE_EXIT
|
|
208
|
+
if not targets:
|
|
209
|
+
out.error("--with-xcode-tools adds Xcode's tools to Claude Code and Codex. Name one of them or use --all")
|
|
210
|
+
return USAGE_EXIT
|
|
211
|
+
server = server or registry.server_for(server_args(args), env)
|
|
212
|
+
overrides = {flag for name, flag in (("env_file", "--env-file"), ("allow_devices", "--allow-device"),
|
|
213
|
+
("allow_testflight", "--allow-testflight"))
|
|
214
|
+
if getattr(args, name, None) and (name != "env_file" or
|
|
215
|
+
not getattr(args, "env_file_from_environment", False))}
|
|
216
|
+
changes, skills, rule_rows, failed = [], [], [], False
|
|
217
|
+
for client in chosen:
|
|
218
|
+
found, why = client.detected(env)
|
|
219
|
+
if not found and not project and not args.remove:
|
|
220
|
+
changes.append(registry.Change(client.id, "skipped", message=f"{client.name} isn't installed here "
|
|
221
|
+
f"({why}). Install it first"))
|
|
222
|
+
failed = True
|
|
223
|
+
continue
|
|
224
|
+
if args.remove:
|
|
225
|
+
changes += client.remove(env, project, args.dry_run)
|
|
226
|
+
else:
|
|
227
|
+
changes += client.install(env, server, project, args.dry_run, overrides=overrides,
|
|
228
|
+
reset_options=getattr(args, "reset_server_options", False))
|
|
229
|
+
if args.with_skill:
|
|
230
|
+
done = set()
|
|
231
|
+
for client in chosen:
|
|
232
|
+
folder = client.skills_dir(env, project)
|
|
233
|
+
if folder is None:
|
|
234
|
+
skills.append({"client": client.id, "action": "skipped", "path": "",
|
|
235
|
+
"message": f"{client.name} has no skills folder"})
|
|
236
|
+
continue
|
|
237
|
+
if folder in done or not any(c.client == client.id and c.action not in ("skipped", "error")
|
|
238
|
+
for c in changes):
|
|
239
|
+
continue
|
|
240
|
+
done.add(folder)
|
|
241
|
+
try:
|
|
242
|
+
action, path, message = (skill.remove if args.remove else skill.install)(folder, args.dry_run)
|
|
243
|
+
except OSError as error:
|
|
244
|
+
action, path, message = (
|
|
245
|
+
"error", str(folder / skill.NAME), f"the skill couldn't be changed ({error.strerror})")
|
|
246
|
+
sharing = [c.name for c in chosen if c.skills_dir(env, project) == folder]
|
|
247
|
+
skills.append({"client": client.id, "clients": sharing, "action": action, "path": path,
|
|
248
|
+
"message": message})
|
|
249
|
+
if rules_dir:
|
|
250
|
+
served = {c.id: c.name for c in chosen
|
|
251
|
+
if any(ch.client == c.id and ch.action not in ("skipped", "error") for ch in changes)}
|
|
252
|
+
if served:
|
|
253
|
+
rule_rows = rules.apply(rules_dir, served, args.remove, args.dry_run)
|
|
254
|
+
if getattr(args, "with_xcode_tools", False):
|
|
255
|
+
for client in targets:
|
|
256
|
+
if any(c.client == client.id and c.action == "skipped" for c in changes):
|
|
257
|
+
continue
|
|
258
|
+
changes.append(registry.bridge_install(client, env, args.dry_run, args.remove))
|
|
259
|
+
failed = failed or any(change.action == "error" for change in changes) or \
|
|
260
|
+
any(item["action"] == "error" for item in skills + rule_rows)
|
|
261
|
+
out.install_report(server, changes, skills, args, ok=not failed, rule_rows=rule_rows)
|
|
262
|
+
return FAILED_EXIT if failed else 0
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# -------------------------------------------------------------------------------------------- clients
|
|
266
|
+
|
|
267
|
+
def list_clients(args, env=None, stream=None):
|
|
268
|
+
env = env or registry.Env.current()
|
|
269
|
+
out = Output(args, stream)
|
|
270
|
+
project = Path(args.project).expanduser().absolute() if getattr(args, "project", None) else None
|
|
271
|
+
rows = []
|
|
272
|
+
for client in registry.CLIENTS:
|
|
273
|
+
found, why = client.detected(env)
|
|
274
|
+
row = {"id": client.id, "name": client.name, "detected": found, "found": why, "docs": client.docs}
|
|
275
|
+
row.update(client.status(env, project))
|
|
276
|
+
folder = client.skills_dir(env, project)
|
|
277
|
+
row["skills_dir"] = str(folder) if folder else None
|
|
278
|
+
row["skill"] = bool(folder) and skill.ours(folder / skill.NAME)
|
|
279
|
+
rows.append(row)
|
|
280
|
+
out.clients_report(rows)
|
|
281
|
+
return 0
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# -------------------------------------------------------------------------------------------- doctor
|
|
285
|
+
|
|
286
|
+
def doctor(args, env=None, server=None, stream=None):
|
|
287
|
+
from . import doctor as probe
|
|
288
|
+
env = env or registry.Env.current()
|
|
289
|
+
out = Output(args, stream)
|
|
290
|
+
if getattr(args, "env_file", None):
|
|
291
|
+
problem = check_env_file(args.env_file)
|
|
292
|
+
if problem:
|
|
293
|
+
out.error(problem)
|
|
294
|
+
return USAGE_EXIT
|
|
295
|
+
server = server or registry.server_for(server_args(args), env)
|
|
296
|
+
report = probe.probe(server, env.environ, cwd=str(env.home))
|
|
297
|
+
problems = []
|
|
298
|
+
for client in registry.CLIENTS:
|
|
299
|
+
if not client.detected(env)[0]:
|
|
300
|
+
continue
|
|
301
|
+
state = client.status(env)
|
|
302
|
+
if state.get("error"):
|
|
303
|
+
problems.append({"client": client.id, "message": state["error"]})
|
|
304
|
+
elif state.get("configured") and not state.get("command_ok"):
|
|
305
|
+
problems.append({"client": client.id, "message":
|
|
306
|
+
f"{client.name} runs {state.get('command') or 'a command'} that doesn't exist. Run "
|
|
307
|
+
f"`mobster mcp install {client.id}`"})
|
|
308
|
+
report.setdefault("clients", []).append({"id": client.id, "name": client.name, **state})
|
|
309
|
+
report["problems"] = problems
|
|
310
|
+
out.doctor_report(report)
|
|
311
|
+
return 0 if report["ok"] and not problems else FAILED_EXIT
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# -------------------------------------------------------------------------------------------- output
|
|
315
|
+
|
|
316
|
+
class Output:
|
|
317
|
+
def __init__(self, args, stream=None):
|
|
318
|
+
self.json = bool(getattr(args, "json", False))
|
|
319
|
+
self.stream = stream or sys.stdout
|
|
320
|
+
|
|
321
|
+
def print(self, text=""):
|
|
322
|
+
print(text, file=self.stream, flush=True)
|
|
323
|
+
|
|
324
|
+
def error(self, message):
|
|
325
|
+
if self.json:
|
|
326
|
+
self.print(json.dumps({"ok": False, "error": message}))
|
|
327
|
+
else:
|
|
328
|
+
print(f"mobster mcp: {message}", file=sys.stderr, flush=True)
|
|
329
|
+
|
|
330
|
+
def install_report(self, server, changes, skills, args, *, ok, rule_rows=()):
|
|
331
|
+
if self.json:
|
|
332
|
+
self.print(json.dumps({"ok": ok, "dry_run": bool(args.dry_run), "remove": bool(args.remove),
|
|
333
|
+
"server": {"command": server.command, "args": server.args},
|
|
334
|
+
"changes": [c.json() for c in changes], "skills": skills,
|
|
335
|
+
"rules": [{k: v for k, v in row.items() if k != "diff" and (v or k != "message")}
|
|
336
|
+
for row in rule_rows]}, indent=2))
|
|
337
|
+
return
|
|
338
|
+
names = {client.id: client.name for client in registry.CLIENTS}
|
|
339
|
+
if not args.remove:
|
|
340
|
+
self.print("Mobster's MCP server: " + " ".join(_quote(part) for part in server.argv()))
|
|
341
|
+
if not getattr(args, "reset_server_options", False):
|
|
342
|
+
self.print("Existing server options and environment are kept per client.")
|
|
343
|
+
for change in changes:
|
|
344
|
+
where = registry._home(change.path) if change.path else ""
|
|
345
|
+
how = f" (with `{Path(change.command[0]).name} mcp {change.command[2]}`)" \
|
|
346
|
+
if change.method == "cli" and change.command else ""
|
|
347
|
+
line = f" {names[change.client]:<16}{change.action:<10} {where}{how}"
|
|
348
|
+
self.print(line.rstrip())
|
|
349
|
+
if change.message:
|
|
350
|
+
self.print(f" {change.message}")
|
|
351
|
+
if args.dry_run and change.method == "cli" and change.command:
|
|
352
|
+
self.print(" would run: " + " ".join(_quote(part) for part in change.command))
|
|
353
|
+
for item in skills:
|
|
354
|
+
who = _join(item.get("clients") or [names.get(item["client"], item["client"])])
|
|
355
|
+
where = registry._home(item["path"]) + " " if item["path"] else ""
|
|
356
|
+
self.print(f" {'Skill':<16}{item['action']:<10} {where}({who})")
|
|
357
|
+
if item.get("message"):
|
|
358
|
+
self.print(f" {item['message']}")
|
|
359
|
+
for row in rule_rows:
|
|
360
|
+
where = registry._home(row["path"])
|
|
361
|
+
self.print(f" {'Rules':<16}{row['action']:<10} {where} ({_join(row['clients'])})")
|
|
362
|
+
if row["message"]:
|
|
363
|
+
self.print(f" {row['message']}")
|
|
364
|
+
if args.dry_run and row["diff"]:
|
|
365
|
+
for diff_line in row["diff"].rstrip("\n").splitlines():
|
|
366
|
+
self.print(" " + diff_line)
|
|
367
|
+
if args.dry_run:
|
|
368
|
+
self.print("Nothing was written (--dry-run).")
|
|
369
|
+
return
|
|
370
|
+
touched = [c for c in changes if c.action in ("added", "updated", "removed")]
|
|
371
|
+
restart = sorted({names[c.client] for c in touched if c.client in RESTART})
|
|
372
|
+
if restart:
|
|
373
|
+
self.print(f"Restart {_join(restart)} to load the change.")
|
|
374
|
+
if touched and not args.remove:
|
|
375
|
+
self.print("New sessions of the other clients pick it up. Check it with `mobster mcp doctor`.")
|
|
376
|
+
|
|
377
|
+
def clients_report(self, rows):
|
|
378
|
+
if self.json:
|
|
379
|
+
self.print(json.dumps({"clients": rows}, indent=2))
|
|
380
|
+
return
|
|
381
|
+
import shutil
|
|
382
|
+
|
|
383
|
+
from ..style import palette
|
|
384
|
+
paint = palette(self.stream)
|
|
385
|
+
|
|
386
|
+
def state(row):
|
|
387
|
+
if row.get("error"):
|
|
388
|
+
return "error"
|
|
389
|
+
if row.get("configured"):
|
|
390
|
+
return "added" if row.get("command_ok") else "broken"
|
|
391
|
+
return "no"
|
|
392
|
+
found = sum(1 for row in rows if row["detected"])
|
|
393
|
+
added = sum(1 for row in rows if state(row) == "added")
|
|
394
|
+
# The clients on this Mac first, then the rest, each in the registry's order.
|
|
395
|
+
rows = sorted(rows, key=lambda row: not row["detected"])
|
|
396
|
+
# A column of "no" says nothing: with Mobster in no client, the summary line says it instead.
|
|
397
|
+
show_added = any(state(row) != "no" for row in rows)
|
|
398
|
+
self.print(f"{len(rows)} MCP clients, {found} on this Mac, Mobster added to {added}")
|
|
399
|
+
self.print()
|
|
400
|
+
words = {"yes": ("✓ yes", "green"), "no": ("no", "tertiary"), "added": ("✓ added", "green"),
|
|
401
|
+
"broken": ("broken", "amber"), "error": ("error", "coral")}
|
|
402
|
+
name_width = max(len("Client"), *(len(row["name"]) for row in rows)) + 3
|
|
403
|
+
columns = [("Client", name_width), ("On this Mac", 14)] + ([("Added", 10)] if show_added else [])
|
|
404
|
+
self.print(" " + "".join(paint(title, "tertiary", "underline") + " " * (width - len(title))
|
|
405
|
+
for title, width in columns) + paint("Config", "tertiary", "underline"))
|
|
406
|
+
room = max(24, shutil.get_terminal_size((100, 24)).columns - 2 - sum(width for _, width in columns))
|
|
407
|
+
for row in rows:
|
|
408
|
+
dim = not row["detected"]
|
|
409
|
+
name = paint(row["name"].ljust(name_width), "tertiary" if dim else "text")
|
|
410
|
+
mac, mac_role = words["yes" if row["detected"] else "no"]
|
|
411
|
+
line = " " + name + paint(mac, mac_role) + " " * (14 - len(mac))
|
|
412
|
+
if show_added:
|
|
413
|
+
text, role = words[state(row)]
|
|
414
|
+
line += paint(text, role) + " " * (10 - len(text))
|
|
415
|
+
self.print((line + paint(_middle(registry._home(row.get("path", "")), room), "tertiary")).rstrip())
|
|
416
|
+
self.print()
|
|
417
|
+
ready = sum(1 for row in rows if row["detected"] and state(row) == "added")
|
|
418
|
+
if found and ready < found:
|
|
419
|
+
missing = found - ready
|
|
420
|
+
self.print(f"Add Mobster to the {missing} client{'s' if missing != 1 else ''} on this Mac: "
|
|
421
|
+
+ paint("mobster mcp install --all", "accent"))
|
|
422
|
+
elif found:
|
|
423
|
+
self.print("Every client found here has Mobster. Check one: " + paint("mobster mcp doctor", "accent"))
|
|
424
|
+
else:
|
|
425
|
+
self.print("No MCP client found here. Install one, then: " + paint("mobster mcp install", "accent"))
|
|
426
|
+
if any(state(row) == "broken" for row in rows):
|
|
427
|
+
self.print(paint("broken: the entry runs a mobster that no longer exists; install again to fix it.",
|
|
428
|
+
"tertiary"))
|
|
429
|
+
|
|
430
|
+
def doctor_report(self, report):
|
|
431
|
+
if self.json:
|
|
432
|
+
self.print(json.dumps(report, indent=2))
|
|
433
|
+
return
|
|
434
|
+
self.print("Server: " + " ".join(_quote(part) for part in report["command"]))
|
|
435
|
+
self.print(f" started with launchd's PATH ({'/usr/bin:/bin:/usr/sbin:/sbin'}), as an app from the Dock "
|
|
436
|
+
"would start it")
|
|
437
|
+
for step in report["steps"]:
|
|
438
|
+
self.print(f" {step['step']:<20}{step['ms']:>6} ms")
|
|
439
|
+
if report.get("tools"):
|
|
440
|
+
self.print(f" {len(report['tools'])} tools: {', '.join(report['tools'])}")
|
|
441
|
+
if report.get("status"):
|
|
442
|
+
for line in report["status"].splitlines()[:6]:
|
|
443
|
+
self.print(f" status: {line}")
|
|
444
|
+
if report["ok"]:
|
|
445
|
+
self.print(f"OK in {report['total_ms']} ms.")
|
|
446
|
+
else:
|
|
447
|
+
self.print(f"Failed: {report.get('error')}")
|
|
448
|
+
for line in report.get("stderr") or []:
|
|
449
|
+
self.print(f" server: {line}")
|
|
450
|
+
for client in report.get("clients") or []:
|
|
451
|
+
state = "set up" if client.get("configured") else "not set up"
|
|
452
|
+
self.print(f" {client['name']:<16}{state:<11}{registry._home(client.get('path', ''))}".rstrip())
|
|
453
|
+
for problem in report.get("problems") or []:
|
|
454
|
+
self.print(f"Problem: {problem['message']}")
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def _quote(part):
|
|
458
|
+
import shlex
|
|
459
|
+
return shlex.quote(str(part))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _middle(path, width):
|
|
463
|
+
"""``path`` shortened in the middle with … when it is wider than ``width``: the file name stays whole."""
|
|
464
|
+
if len(path) <= width:
|
|
465
|
+
return path
|
|
466
|
+
keep = max(1, width - 1)
|
|
467
|
+
tail = keep * 2 // 3
|
|
468
|
+
return path[:keep - tail] + "…" + path[-tail:]
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def _join(names):
|
|
472
|
+
return names[0] if len(names) == 1 else ", ".join(names[:-1]) + " and " + names[-1]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The files a client's config lives in: read as text, shown with ~, backed up, and written in one step."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from . import textedit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _read(path):
|
|
12
|
+
"""The file's text, or None when there is no file."""
|
|
13
|
+
try:
|
|
14
|
+
return Path(path).read_text(encoding="utf-8")
|
|
15
|
+
except FileNotFoundError:
|
|
16
|
+
return None
|
|
17
|
+
except UnicodeDecodeError:
|
|
18
|
+
raise textedit.EditError("the file isn't UTF-8 text") from None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _home(path):
|
|
22
|
+
"""``path`` with the home folder written as ~."""
|
|
23
|
+
text, home = str(path), os.environ.get("HOME", "")
|
|
24
|
+
return "~" + text[len(home):] if home and text.startswith(home + "/") else text
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
BACKUP_SUFFIX = ".mobster-backup"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def backup(path):
|
|
31
|
+
"""Copy ``path`` to ``path`` + ".mobster-backup" (the copy before Mobster's latest change). No file, no copy."""
|
|
32
|
+
path = Path(os.path.realpath(path))
|
|
33
|
+
if path.is_file():
|
|
34
|
+
target = path.with_name(path.name + BACKUP_SUFFIX)
|
|
35
|
+
with tempfile.NamedTemporaryFile(dir=path.parent, prefix=f".{target.name}.", delete=False) as handle:
|
|
36
|
+
temp = Path(handle.name)
|
|
37
|
+
try:
|
|
38
|
+
shutil.copy2(path, temp)
|
|
39
|
+
os.chmod(temp, path.stat().st_mode & 0o777)
|
|
40
|
+
os.replace(temp, target)
|
|
41
|
+
finally:
|
|
42
|
+
temp.unlink(missing_ok=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def write_atomic(path, text, keep_backup=True):
|
|
46
|
+
"""Write ``text`` to ``path`` in one step: a temporary file beside it, flushed, then renamed over it."""
|
|
47
|
+
path = Path(os.path.realpath(path))
|
|
48
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
if keep_backup:
|
|
50
|
+
backup(path)
|
|
51
|
+
try:
|
|
52
|
+
mode = path.stat().st_mode & 0o777
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
mode = 0o600 if path.parent.stat().st_mode & 0o077 == 0 else 0o644
|
|
55
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=path.parent,
|
|
56
|
+
prefix=f".{path.name}.mobster-", suffix=".tmp", delete=False) as handle:
|
|
57
|
+
temp = Path(handle.name)
|
|
58
|
+
try:
|
|
59
|
+
handle.write(text)
|
|
60
|
+
handle.flush()
|
|
61
|
+
os.fsync(handle.fileno())
|
|
62
|
+
os.chmod(temp, mode)
|
|
63
|
+
os.replace(temp, path)
|
|
64
|
+
finally:
|
|
65
|
+
temp.unlink(missing_ok=True)
|