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,493 @@
|
|
|
1
|
+
"""The tools `mobster mcp` offers (SPEC §7.2 to §7.5, and devices: ship 2 SPEC §3)."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import traceback
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from urllib.parse import urlsplit
|
|
9
|
+
|
|
10
|
+
from ..android.records import is_phone
|
|
11
|
+
from ..journal import JournalError
|
|
12
|
+
from ..transport import request_once
|
|
13
|
+
from .action_tools import ActionTools
|
|
14
|
+
from .approvals import ApprovalTools
|
|
15
|
+
from .catalog import (
|
|
16
|
+
ANNOTATIONS,
|
|
17
|
+
CORE_TOOL_NAMES,
|
|
18
|
+
INSTRUCTIONS,
|
|
19
|
+
NO_SMART_KEY,
|
|
20
|
+
SIMULATOR_PROVIDERS,
|
|
21
|
+
provider_of,
|
|
22
|
+
smart_sentence,
|
|
23
|
+
titled,
|
|
24
|
+
)
|
|
25
|
+
from .codes import CodeVault
|
|
26
|
+
from .device_tools import DeviceTools
|
|
27
|
+
from .phone_tools import PhoneTools
|
|
28
|
+
|
|
29
|
+
# The names other packages import from this module, whether or not it uses them itself.
|
|
30
|
+
from .protocol import ACTS, INVALID_PARAMS, LOCAL, READ_ONLY, ProtocolError, ToolResult # noqa: F401
|
|
31
|
+
from .registry import FILE_TOOLS, providers
|
|
32
|
+
from .run_tools import RunTools
|
|
33
|
+
from .schemas import WAIT_MAX, build_tools, drop_nulls, validate
|
|
34
|
+
from .screen_tools import ScreenTools
|
|
35
|
+
from .support import ( # noqa: F401
|
|
36
|
+
INLINE_QUALITY,
|
|
37
|
+
Busy,
|
|
38
|
+
ToolError,
|
|
39
|
+
binary_stamp,
|
|
40
|
+
cap_text,
|
|
41
|
+
inline_jpeg,
|
|
42
|
+
jsonable,
|
|
43
|
+
sentence,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Every limit counts from when the request arrived (protocol.Call.started), not from when the tool began.
|
|
47
|
+
PREPARE_WAIT = 40.0
|
|
48
|
+
CALL_BUDGET = 44.0
|
|
49
|
+
# What `wait` keeps of WAIT_MAX for describing a ready run: the lock, an evidence read (4 s measured under
|
|
50
|
+
# load) and the first frame.
|
|
51
|
+
WAIT_HEADROOM = 6.0
|
|
52
|
+
# What `wait_for` keeps of CALL_BUDGET after its wait: evaluate's last read past its deadline and the outline.
|
|
53
|
+
EVALUATE_RESERVE = 8.0
|
|
54
|
+
IDLE_LIMIT = 15 * 60
|
|
55
|
+
# A device driven directly (no check) is released after 90 idle seconds: it holds the device's lease, which keeps
|
|
56
|
+
# the Mac app, `mobster run` and scheduled tasks off that phone. The next call on it opens it again.
|
|
57
|
+
DIRECT_IDLE_LIMIT = 90
|
|
58
|
+
# The simulator a session leased for itself is kept longer: the agent is likely still working in it.
|
|
59
|
+
SESSION_SIM_IDLE_LIMIT = 30 * 60
|
|
60
|
+
SESSION_LIMIT = 60 * 60
|
|
61
|
+
LOCK_WAIT = 30.0
|
|
62
|
+
DESCRIBE_LOCK_WAIT = 1.0
|
|
63
|
+
KEEP_FINISHED = 20
|
|
64
|
+
REAP_SECONDS = 15.0
|
|
65
|
+
# A track adds one sentence to the server's instructions. A longer one is left out whole, never cut mid-sentence.
|
|
66
|
+
PROVIDER_SENTENCE_MAX = 300
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class VerifyAPI:
|
|
70
|
+
def check_from_dict(self, data):
|
|
71
|
+
from ..verify.checks import check_from_dict
|
|
72
|
+
return check_from_dict(data)
|
|
73
|
+
|
|
74
|
+
def parse_assertion(self, data):
|
|
75
|
+
from ..verify.assertions import parse_assertion
|
|
76
|
+
return parse_assertion(data)
|
|
77
|
+
|
|
78
|
+
def parse_selector(self, data):
|
|
79
|
+
from ..verify.assertions import parse_selector
|
|
80
|
+
return parse_selector(data)
|
|
81
|
+
|
|
82
|
+
def evaluate_on(self, assertions, tree):
|
|
83
|
+
from ..verify.assertions import evaluate_on
|
|
84
|
+
return evaluate_on(assertions, tree)
|
|
85
|
+
|
|
86
|
+
def new_run(self, check, **options):
|
|
87
|
+
from ..verify.runner import VerifyRun
|
|
88
|
+
return VerifyRun(check, **options)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def default_manager(progress):
|
|
92
|
+
from ..sim import SimulatorManager
|
|
93
|
+
return SimulatorManager(progress=progress)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ToolSet(RunTools, DeviceTools, ScreenTools, ApprovalTools, ActionTools, PhoneTools):
|
|
97
|
+
def __init__(self, *, runs_dir, key=None, keyless=False, smart_model=None, smart_off=None, device=None,
|
|
98
|
+
runtime=None, api=None, manager=None,
|
|
99
|
+
manager_factory=None, log=None, clock=time.monotonic, imager=inline_jpeg,
|
|
100
|
+
prepare_wait=PREPARE_WAIT, call_budget=CALL_BUDGET, wait_max=WAIT_MAX, wait_headroom=WAIT_HEADROOM,
|
|
101
|
+
evaluate_reserve=EVALUATE_RESERVE, idle_limit=IDLE_LIMIT, direct_idle_limit=DIRECT_IDLE_LIMIT,
|
|
102
|
+
session_sim_idle_limit=SESSION_SIM_IDLE_LIMIT,
|
|
103
|
+
session_limit=SESSION_LIMIT,
|
|
104
|
+
lock_wait=LOCK_WAIT, describe_lock_wait=DESCRIBE_LOCK_WAIT, version=None, devices=None,
|
|
105
|
+
device_run=None, allow_devices=None, device_runs_dir=None, wda_get=None, installer=None,
|
|
106
|
+
allow_files=False, allow_testflight=None, allow_photos=False, phone_files=None, simctl=None):
|
|
107
|
+
self.runs_dir = Path(runs_dir)
|
|
108
|
+
# The agent inside Mobster.app answers only its read-only tools and stop without a plan (plan_gate).
|
|
109
|
+
from .. import plan_gate
|
|
110
|
+
self.plan_gate = plan_gate.bundled_gate()
|
|
111
|
+
self.device_runs_dir = Path(device_runs_dir) if device_runs_dir else self.runs_dir
|
|
112
|
+
self._key = key or None
|
|
113
|
+
self.keyless = bool(keyless)
|
|
114
|
+
self.smart_available = bool(self._key) and not self.keyless
|
|
115
|
+
if self.smart_available and not smart_model:
|
|
116
|
+
from ..engines import smart_model as chosen
|
|
117
|
+
smart_model = chosen()
|
|
118
|
+
self.smart_model = smart_model if self.smart_available else None
|
|
119
|
+
self.smart_off = (None if self.smart_available else "--keyless" if self.keyless
|
|
120
|
+
else smart_off or NO_SMART_KEY)
|
|
121
|
+
self.device, self.runtime = device, runtime
|
|
122
|
+
self._binary = binary_stamp()
|
|
123
|
+
# `mobster mcp --allow-device NAME`: the only real (non-simulator) devices this server drives, by id, UDID
|
|
124
|
+
# or name; None (no flag) allows every device. --device's own device is allowed too.
|
|
125
|
+
self.allow_devices = None
|
|
126
|
+
if allow_devices:
|
|
127
|
+
self.allow_devices = {str(name).strip().casefold() for name in allow_devices if str(name).strip()}
|
|
128
|
+
if device:
|
|
129
|
+
self.allow_devices.add(str(device).strip().casefold())
|
|
130
|
+
self.allow_files = bool(allow_files)
|
|
131
|
+
# `mobster mcp --allow-testflight BUNDLE`: apps whose TestFlight build run_tests with pr may drive as the user's
|
|
132
|
+
# own, on an iPhone --allow-device names. The agent can't add to it.
|
|
133
|
+
self.allow_testflight = frozenset(str(item).strip() for item in allow_testflight or () if str(item).strip())
|
|
134
|
+
# The camera roll is the user's most private folder: pull_media is listed only with --allow-photos.
|
|
135
|
+
self.allow_photos = bool(allow_photos)
|
|
136
|
+
self._phone_files, self._simctl = phone_files, simctl
|
|
137
|
+
self.api = api or VerifyAPI()
|
|
138
|
+
self._manager_instance = manager
|
|
139
|
+
self._manager_factory = manager_factory or default_manager
|
|
140
|
+
self.log = log or (lambda message: None)
|
|
141
|
+
self.clock = clock
|
|
142
|
+
self.imager = imager
|
|
143
|
+
self.prepare_wait, self.call_budget, self.wait_max = prepare_wait, call_budget, wait_max
|
|
144
|
+
self.wait_headroom, self.evaluate_reserve = wait_headroom, evaluate_reserve
|
|
145
|
+
self.idle_limit, self.session_limit, self.lock_wait = idle_limit, session_limit, lock_wait
|
|
146
|
+
self.direct_idle_limit = direct_idle_limit
|
|
147
|
+
self.session_sim_idle_limit = session_sim_idle_limit
|
|
148
|
+
self.describe_lock_wait = describe_lock_wait
|
|
149
|
+
if version is None:
|
|
150
|
+
from .. import __version__ as version
|
|
151
|
+
self.version = version
|
|
152
|
+
self.sessions = {}
|
|
153
|
+
self.device_sessions = {}
|
|
154
|
+
self._own_sim = None # the UDID of the simulator this session leased when no device was named
|
|
155
|
+
self._devices_source = devices
|
|
156
|
+
self._device_run = device_run
|
|
157
|
+
self._devices_cache = (0.0, None)
|
|
158
|
+
self._ownership_source = None
|
|
159
|
+
self._ownership_cache = (0.0, {})
|
|
160
|
+
self._engine_source = None
|
|
161
|
+
self._wda_get = wda_get or _wda_get
|
|
162
|
+
self._installer = installer
|
|
163
|
+
self._workers = []
|
|
164
|
+
self._route = None
|
|
165
|
+
self._lock = threading.RLock()
|
|
166
|
+
self.codes = CodeVault(self._lock, self._open_sessions, quality=INLINE_QUALITY)
|
|
167
|
+
self._closing = threading.Event()
|
|
168
|
+
self._reaper = None
|
|
169
|
+
self._tools = build_tools(self)
|
|
170
|
+
|
|
171
|
+
def instructions(self):
|
|
172
|
+
text = INSTRUCTIONS + (" " + smart_sentence(self.smart_model) if self.smart_available else "")
|
|
173
|
+
for provider in providers():
|
|
174
|
+
try:
|
|
175
|
+
extra = provider.instructions()
|
|
176
|
+
except Exception:
|
|
177
|
+
self.log(f"The {getattr(provider, 'name', '?')} tools' instructions failed: "
|
|
178
|
+
+ traceback.format_exc(limit=3))
|
|
179
|
+
continue
|
|
180
|
+
line = " ".join(extra.split()) if isinstance(extra, str) else ""
|
|
181
|
+
if line and not line.endswith((".", "?", "!")):
|
|
182
|
+
line += "."
|
|
183
|
+
if len(line) > PROVIDER_SENTENCE_MAX:
|
|
184
|
+
self.log(f"The {getattr(provider, 'name', '?')} tools' instructions are longer than "
|
|
185
|
+
f"{PROVIDER_SENTENCE_MAX} characters, so Mobster left them out.")
|
|
186
|
+
elif line:
|
|
187
|
+
text += " " + line
|
|
188
|
+
return text
|
|
189
|
+
|
|
190
|
+
def smart_state(self):
|
|
191
|
+
if self.smart_available:
|
|
192
|
+
return f"on ({self.smart_model}, on the user's {provider_of(self.smart_model)} key)"
|
|
193
|
+
return f"off ({self.smart_off})"
|
|
194
|
+
|
|
195
|
+
def list_tools(self):
|
|
196
|
+
return [titled({"name": name, "description": description, "inputSchema": schema},
|
|
197
|
+
dict(ANNOTATIONS.get(name, ACTS)))
|
|
198
|
+
for name, (description, schema, _handler, _budget) in self._tools.items()] + [
|
|
199
|
+
titled(definition, annotations) for _provider, definition, annotations in self._provided()]
|
|
200
|
+
|
|
201
|
+
def _provided(self):
|
|
202
|
+
out, taken = [], set(self._tools) | CORE_TOOL_NAMES
|
|
203
|
+
for provider in providers():
|
|
204
|
+
try:
|
|
205
|
+
definitions = list(provider.definitions(self) or ())
|
|
206
|
+
annotations = dict(provider.annotations() or {})
|
|
207
|
+
except Exception:
|
|
208
|
+
self.log(f"The {getattr(provider, 'name', '?')} tools could not be listed: "
|
|
209
|
+
+ traceback.format_exc(limit=3))
|
|
210
|
+
continue
|
|
211
|
+
for definition in definitions:
|
|
212
|
+
name = definition.get("name") if isinstance(definition, dict) else None
|
|
213
|
+
if not isinstance(name, str) or name in taken or not isinstance(definition.get("inputSchema"), dict):
|
|
214
|
+
continue
|
|
215
|
+
if name in FILE_TOOLS and not self.allow_files:
|
|
216
|
+
continue
|
|
217
|
+
taken.add(name)
|
|
218
|
+
kept = {"name": name, "description": str(definition.get("description") or ""),
|
|
219
|
+
"inputSchema": definition["inputSchema"]}
|
|
220
|
+
if isinstance(definition.get("title"), str) and definition["title"].strip():
|
|
221
|
+
kept["title"] = definition["title"].strip()[:80]
|
|
222
|
+
if isinstance(definition.get("outputSchema"), dict):
|
|
223
|
+
kept["outputSchema"] = definition["outputSchema"]
|
|
224
|
+
out.append((provider, kept,
|
|
225
|
+
dict(annotations.get(name, ACTS))))
|
|
226
|
+
return out
|
|
227
|
+
|
|
228
|
+
def plan_refusal(self, name, annotations, arguments=None, provider=None):
|
|
229
|
+
# A license it can't read counts as no plan; a target it can't place counts as a phone.
|
|
230
|
+
from .. import plan_gate
|
|
231
|
+
if name == "stop":
|
|
232
|
+
return None
|
|
233
|
+
stopped = plan_gate.stopped(plan_gate.TOOL) # a failed seal or a CI token that refuses: the reads too
|
|
234
|
+
if stopped is not None:
|
|
235
|
+
return ToolResult(stopped, is_error=True)
|
|
236
|
+
if self.plan_gate is None or not plan_gate.mcp_needs_plan(name, annotations):
|
|
237
|
+
return None
|
|
238
|
+
message = self.plan_gate.refusal()
|
|
239
|
+
if message is None or not self._drives_real_phone(name, arguments or {}, provider):
|
|
240
|
+
return None
|
|
241
|
+
return ToolResult(message, is_error=True)
|
|
242
|
+
|
|
243
|
+
def _drives_real_phone(self, name, arguments, provider=None):
|
|
244
|
+
if provider is not None:
|
|
245
|
+
return getattr(provider, "name", None) not in SIMULATOR_PROVIDERS
|
|
246
|
+
try:
|
|
247
|
+
return self._targets_phone(arguments)
|
|
248
|
+
except Exception:
|
|
249
|
+
return True
|
|
250
|
+
|
|
251
|
+
def _targets_phone(self, arguments):
|
|
252
|
+
run_id, device = arguments.get("run_id"), arguments.get("device")
|
|
253
|
+
if run_id:
|
|
254
|
+
session = self.sessions.get(run_id) or next(
|
|
255
|
+
(item for item in self._direct_sessions(open_only=False) if item.run_id == run_id), None)
|
|
256
|
+
if session is None:
|
|
257
|
+
return False # no such run: the tool says so without touching a device
|
|
258
|
+
record = getattr(session.run, "record", None)
|
|
259
|
+
if not isinstance(record, dict) and getattr(session.run, "device_id", None):
|
|
260
|
+
record = self._named_device(session.run.device_id)
|
|
261
|
+
if record is None:
|
|
262
|
+
return True
|
|
263
|
+
return isinstance(record, dict) and is_phone(record)
|
|
264
|
+
target = device or self.device
|
|
265
|
+
if not target:
|
|
266
|
+
return False # a check on the default simulator, or a call that names nothing to drive
|
|
267
|
+
record = self._named_device(target)
|
|
268
|
+
return record is None or is_phone(record)
|
|
269
|
+
|
|
270
|
+
def _call_provided(self, name, arguments, call):
|
|
271
|
+
provided = next(((p, d, a) for p, d, a in self._provided() if d["name"] == name), None)
|
|
272
|
+
if provided is None:
|
|
273
|
+
return None
|
|
274
|
+
provider, definition, annotations = provided
|
|
275
|
+
arguments = drop_nulls(arguments)
|
|
276
|
+
validate(definition["inputSchema"], arguments)
|
|
277
|
+
refusal = self.plan_refusal(name, annotations, arguments, provider)
|
|
278
|
+
if refusal is not None:
|
|
279
|
+
return refusal
|
|
280
|
+
try:
|
|
281
|
+
result = self._bounded(name, lambda: provider.call(name, arguments, call, self),
|
|
282
|
+
self.call_budget, run_id=lambda: arguments.get("run_id"),
|
|
283
|
+
started=self._started(call))
|
|
284
|
+
if provider.name == "testkit" and name == "compare_test_suites":
|
|
285
|
+
from ..testkit.mcp_comparison import mask_comparison
|
|
286
|
+
return mask_comparison(self.codes.mask, arguments, result)
|
|
287
|
+
if ((provider.name == "threads" and name in ("phone_task", "phone_task_status"))
|
|
288
|
+
or (provider.name == "workflows" and
|
|
289
|
+
(name in ("run_workflow", "graph_status") or name.startswith("workflow_")))):
|
|
290
|
+
from ..threads.mcp import mask_handles
|
|
291
|
+
return mask_handles(self.codes.mask, name, arguments, result)
|
|
292
|
+
return self.codes.mask(result)
|
|
293
|
+
except ToolError as error:
|
|
294
|
+
return self.codes.mask(ToolResult(cap_text(str(error)), is_error=True))
|
|
295
|
+
except ProtocolError:
|
|
296
|
+
raise
|
|
297
|
+
except Exception as error:
|
|
298
|
+
self.log(f"{name} failed: " + traceback.format_exc(limit=8))
|
|
299
|
+
return ToolResult(self._unexpected(name, error), is_error=True)
|
|
300
|
+
|
|
301
|
+
def _unexpected(self, name, error):
|
|
302
|
+
if binary_stamp() != self._binary:
|
|
303
|
+
return (f"{name} failed because Mobster was updated after this server started. Reconnect it: run /mcp "
|
|
304
|
+
"in Claude Code, or restart your agent app.")
|
|
305
|
+
return f"{name} hit an unexpected error ({type(error).__name__}). The server's log on stderr has the details."
|
|
306
|
+
|
|
307
|
+
def call(self, name, arguments, call):
|
|
308
|
+
entry = self._tools.get(name)
|
|
309
|
+
if entry is None and name not in CORE_TOOL_NAMES:
|
|
310
|
+
provided = self._call_provided(name, arguments, call)
|
|
311
|
+
if provided is not None:
|
|
312
|
+
return provided
|
|
313
|
+
if entry is None:
|
|
314
|
+
raise ProtocolError(INVALID_PARAMS, f"Mobster has no tool named {name[:80]}.")
|
|
315
|
+
_description, schema, handler, budget = entry
|
|
316
|
+
arguments = drop_nulls(arguments)
|
|
317
|
+
validate(schema, arguments)
|
|
318
|
+
refusal = self.plan_refusal(name, ANNOTATIONS.get(name, ACTS), arguments)
|
|
319
|
+
if refusal is not None:
|
|
320
|
+
return refusal
|
|
321
|
+
try:
|
|
322
|
+
self.reap()
|
|
323
|
+
except Exception:
|
|
324
|
+
self.log("Reaping before a call failed: " + traceback.format_exc(limit=5))
|
|
325
|
+
try:
|
|
326
|
+
return self.codes.mask(self._bounded(name, lambda: handler(arguments, call), budget,
|
|
327
|
+
run_id=lambda: arguments.get("run_id") or getattr(call, "run_id", None),
|
|
328
|
+
started=self._started(call)))
|
|
329
|
+
except ToolError as error:
|
|
330
|
+
return self.codes.mask(ToolResult(cap_text(str(error)), is_error=True))
|
|
331
|
+
except JournalError as error:
|
|
332
|
+
return ToolResult(sentence(error) + " Check disk space and run-folder permissions, then inspect the "
|
|
333
|
+
"screen before another action.", is_error=True)
|
|
334
|
+
except ProtocolError:
|
|
335
|
+
raise
|
|
336
|
+
except Exception as error:
|
|
337
|
+
self.log(f"{name} failed: " + traceback.format_exc(limit=8))
|
|
338
|
+
return ToolResult(self._unexpected(name, error), is_error=True)
|
|
339
|
+
|
|
340
|
+
def start(self, interval=REAP_SECONDS):
|
|
341
|
+
if self._reaper is None:
|
|
342
|
+
self._reaper = threading.Thread(target=self._reap_loop, args=(interval,), daemon=True,
|
|
343
|
+
name="mobster-mcp-reaper")
|
|
344
|
+
self._reaper.start()
|
|
345
|
+
|
|
346
|
+
def _reap_loop(self, interval):
|
|
347
|
+
while not self._closing.wait(interval):
|
|
348
|
+
try:
|
|
349
|
+
self.reap()
|
|
350
|
+
except Exception:
|
|
351
|
+
self.log("Reaper failed: " + traceback.format_exc(limit=5))
|
|
352
|
+
|
|
353
|
+
def shutdown(self, reason="the MCP client disconnected", grace=10.0):
|
|
354
|
+
self._closing.set()
|
|
355
|
+
message = f"Mobster stopped the run because {reason}."
|
|
356
|
+
end = time.monotonic() + grace
|
|
357
|
+
for session in [s for s in self._all_sessions() if s.open]:
|
|
358
|
+
session.stop_reason = session.stop_reason or message
|
|
359
|
+
session.cancelled.set()
|
|
360
|
+
if session.state in ("running", "finishing"):
|
|
361
|
+
session.wait_while(("running", "finishing"), max(0.0, end - time.monotonic()))
|
|
362
|
+
if session.state == "preparing":
|
|
363
|
+
self._abort(session, "stopped", message) # VerifyRun.abort stops a run still preparing
|
|
364
|
+
elif session.open:
|
|
365
|
+
locked = session.run.lock.acquire(timeout=max(0.1, min(5.0, end - time.monotonic())))
|
|
366
|
+
try:
|
|
367
|
+
self._abort(session, "stopped", message)
|
|
368
|
+
finally:
|
|
369
|
+
if locked:
|
|
370
|
+
session.run.lock.release()
|
|
371
|
+
for session in self._direct_sessions():
|
|
372
|
+
self._close_direct(session, message, timeout=max(0.1, min(5.0, end - time.monotonic())))
|
|
373
|
+
# A first WebDriverAgent build runs in its own process group: stop it rather than leave xcodebuild behind.
|
|
374
|
+
cancel = getattr(getattr(self._manager_instance, "wda", None), "cancel", None)
|
|
375
|
+
if callable(cancel):
|
|
376
|
+
try:
|
|
377
|
+
cancel()
|
|
378
|
+
except Exception:
|
|
379
|
+
self.log("Stopping the WebDriverAgent build failed: " + traceback.format_exc(limit=3))
|
|
380
|
+
self.log("Every run is stopped.")
|
|
381
|
+
|
|
382
|
+
def reap(self, now=None):
|
|
383
|
+
now = self.clock() if now is None else now
|
|
384
|
+
for session in self._all_sessions():
|
|
385
|
+
if not session.open or session.busy:
|
|
386
|
+
continue
|
|
387
|
+
idle = session.mode == "keyless" and now - session.last_used >= self.idle_limit
|
|
388
|
+
too_long = now - session.started >= self.session_limit
|
|
389
|
+
if not (idle or too_long):
|
|
390
|
+
continue
|
|
391
|
+
reason = ("The session was idle for 15 minutes, so Mobster stopped it." if idle
|
|
392
|
+
else "The session reached its 60-minute limit, so Mobster stopped it.")
|
|
393
|
+
session.stop_reason = session.stop_reason or reason
|
|
394
|
+
if session.mode == "smart" and session.state != "preparing":
|
|
395
|
+
session.cancelled.set()
|
|
396
|
+
elif session.state == "preparing":
|
|
397
|
+
self.log(f"Run {session.run_id}: {reason}")
|
|
398
|
+
session.cancelled.set()
|
|
399
|
+
self._abort(session, "stopped", reason)
|
|
400
|
+
elif session.state == "ready" and session.run.lock.acquire(timeout=0):
|
|
401
|
+
try:
|
|
402
|
+
self.log(f"Run {session.run_id}: {reason}")
|
|
403
|
+
self._abort(session, "stopped", reason)
|
|
404
|
+
finally:
|
|
405
|
+
session.run.lock.release()
|
|
406
|
+
for session in self._direct_sessions():
|
|
407
|
+
if self._own_sim and session.run.device_id == self._own_sim:
|
|
408
|
+
self._reap_session_simulator(session, now)
|
|
409
|
+
elif not session.busy and now - session.last_used >= self.direct_idle_limit:
|
|
410
|
+
self._close_direct(session, f"The device was idle for {self.direct_idle_limit:g} seconds, so Mobster "
|
|
411
|
+
"released it for the Mobster app and other commands. The next call on it "
|
|
412
|
+
"opens it again.", timeout=0)
|
|
413
|
+
with self._lock:
|
|
414
|
+
finished = [s for s in self.sessions.values() if not s.open]
|
|
415
|
+
for session in finished[:max(0, len(finished) - KEEP_FINISHED)]:
|
|
416
|
+
self.sessions.pop(session.run_id, None)
|
|
417
|
+
|
|
418
|
+
def _reap_session_simulator(self, session, now):
|
|
419
|
+
if session.busy:
|
|
420
|
+
return
|
|
421
|
+
from ..sim import holders
|
|
422
|
+
data_dir = getattr(self._manager_instance, "data_dir", None)
|
|
423
|
+
asked = data_dir is not None and holders.release_requested(data_dir, session.run.device_id)
|
|
424
|
+
if asked or now - session.last_used >= self.session_sim_idle_limit:
|
|
425
|
+
self._close_direct(session, "Mobster released this session's simulator "
|
|
426
|
+
+ ("because `mobster sim release` asked." if asked else
|
|
427
|
+
f"after {self.session_sim_idle_limit / 60:g} idle minutes.")
|
|
428
|
+
+ " The next call that names no device leases one again.", timeout=0)
|
|
429
|
+
|
|
430
|
+
@staticmethod
|
|
431
|
+
def _started(call):
|
|
432
|
+
started = getattr(call, "started", None)
|
|
433
|
+
return started if isinstance(started, (int, float)) and not isinstance(started, bool) else time.monotonic()
|
|
434
|
+
|
|
435
|
+
def _left(self, call, seconds):
|
|
436
|
+
return max(0.0, seconds - (time.monotonic() - self._started(call)))
|
|
437
|
+
|
|
438
|
+
def _bounded(self, name, work, seconds, run_id=None, started=None):
|
|
439
|
+
started = time.monotonic() if started is None else started
|
|
440
|
+
box, done = {}, threading.Event()
|
|
441
|
+
|
|
442
|
+
def target():
|
|
443
|
+
try:
|
|
444
|
+
box["result"] = work()
|
|
445
|
+
except BaseException as error: # handed to the caller's thread
|
|
446
|
+
box["error"] = error
|
|
447
|
+
finally:
|
|
448
|
+
done.set()
|
|
449
|
+
threading.Thread(target=target, daemon=True, name=f"mobster-mcp-{name}").start()
|
|
450
|
+
if not done.wait(max(0.0, seconds - (time.monotonic() - started))):
|
|
451
|
+
if name == "install_app":
|
|
452
|
+
raise ToolError(f"install_app is still working after {seconds:.0f} s and may finish in the background. "
|
|
453
|
+
"Its outcome is unknown. Do not retry the installation. Inspect the device and the "
|
|
454
|
+
"installation run's actions.jsonl before another installation.")
|
|
455
|
+
known = run_id() if run_id is not None else None
|
|
456
|
+
if known:
|
|
457
|
+
raise ToolError(f"{name} is still working after {seconds:.0f} s and finishes in the background. "
|
|
458
|
+
f"Call wait with run_id {known} to see where it ended.")
|
|
459
|
+
raise ToolError(f"{name} is still working after {seconds:.0f} s and finishes in the background. "
|
|
460
|
+
"Call status to see whether a run is open.")
|
|
461
|
+
if "error" in box:
|
|
462
|
+
raise box["error"]
|
|
463
|
+
return box["result"]
|
|
464
|
+
|
|
465
|
+
def _manager(self):
|
|
466
|
+
if self._manager_instance is None:
|
|
467
|
+
with self._lock:
|
|
468
|
+
if self._manager_instance is None:
|
|
469
|
+
self._manager_instance = self._manager_factory(self._manager_progress)
|
|
470
|
+
self._manager_instance.who = self._who
|
|
471
|
+
return self._manager_instance
|
|
472
|
+
|
|
473
|
+
def _who(self):
|
|
474
|
+
# the holder record `mobster sim list` shows: this server's client, and the process that holds the lease
|
|
475
|
+
from .. import ownership
|
|
476
|
+
return {"client": ownership.clean_client(self.client_name()) or "an MCP client",
|
|
477
|
+
"session": f"pid {os.getpid()}"}
|
|
478
|
+
|
|
479
|
+
def _manager_progress(self, line):
|
|
480
|
+
# A run stopped while it prepared keeps booting until its next phase: its lines must not reach the next run.
|
|
481
|
+
self.log(str(line))
|
|
482
|
+
session = self._route
|
|
483
|
+
if session is not None:
|
|
484
|
+
session.note(str(line))
|
|
485
|
+
|
|
486
|
+
def client_name(self):
|
|
487
|
+
client = getattr(self, "client", None) or {}
|
|
488
|
+
return (client.get("name") or None) if isinstance(client, dict) else None
|
|
489
|
+
|
|
490
|
+
def _wda_get(url, timeout):
|
|
491
|
+
parts = urlsplit(url)
|
|
492
|
+
path = parts.path + (f"?{parts.query}" if parts.query else "")
|
|
493
|
+
return request_once(f"{parts.scheme}://{parts.netloc}", "GET", path, timeout=timeout).get("value")
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Track `memory`: what Mobster's agent remembers for the person who uses it (SPEC §3.3).
|
|
2
|
+
|
|
3
|
+
- ``store``: facts, suggestions and settings in one SQLite file on this Mac (user_data_dir()/memory).
|
|
4
|
+
- ``extract``: suggestions read out of what the person types ("remember that…", "my X is Y", "I always…").
|
|
5
|
+
- ``retrieval`` and ``provider``: the ≤ 12 facts that go with a task, as one stable block.
|
|
6
|
+
- ``listener``: posts suggestions to the conversation and the bus; ``routes``: /api/memory; ``cli``: `mobster memory`.
|
|
7
|
+
|
|
8
|
+
Only the memory track edits this package.
|
|
9
|
+
"""
|