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,382 @@
|
|
|
1
|
+
"""Without Xcode, for a paid team: read the team, show the card, and after approval write, sign, install.
|
|
2
|
+
|
|
3
|
+
``TeamSigning`` is what Setup's row and ``mobster signing`` drive. One run at a time:
|
|
4
|
+
|
|
5
|
+
1. ``card`` reads App Store Connect (the phones, the App ID, the certificates, Mobster's profiles), makes the
|
|
6
|
+
certificate signing request on this Mac if a certificate is needed, and returns the card: exactly the requests
|
|
7
|
+
Mobster would send (writes.py). It sends nothing.
|
|
8
|
+
2. ``approve`` takes the digest the person approved. It reads the team again, refuses if the card changed, then makes
|
|
9
|
+
the writes (each journaled first, none retried when the outcome is unknown), keeps the certificate beside its key,
|
|
10
|
+
signs the prebuilt runner with ``codesign`` and the new profile, mounts the developer image if the phone has none,
|
|
11
|
+
installs the signed runner, and records it for the supervisor.
|
|
12
|
+
|
|
13
|
+
Everything is off unless ``MOBSTER_EXPERIMENTAL_NO_XCODE=1`` (team_signing.enabled).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import datetime
|
|
18
|
+
import shutil
|
|
19
|
+
import tempfile
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from .. import signing
|
|
25
|
+
from ..devkit import DevError, asc
|
|
26
|
+
from ..wda import patches as wda_patches
|
|
27
|
+
from . import FLAG, bundle, enabled, prebuilt, resign, tooling, writes
|
|
28
|
+
from . import identity as identity_module
|
|
29
|
+
from . import ledger as ledger_module
|
|
30
|
+
|
|
31
|
+
STAGES = ("idle", "reading", "approval", "writing", "signing", "installing", "ready", "failed")
|
|
32
|
+
BUSY = ("reading", "writing", "signing", "installing")
|
|
33
|
+
KEY_TTL = 30.0
|
|
34
|
+
NO_KEY = (
|
|
35
|
+
"Mobster has no App Store Connect key for your team.",
|
|
36
|
+
"Make one in App Store Connect › Users and Access › Integrations with the Admin or Developer role, then "
|
|
37
|
+
"run: mobster asc key add --key-id KEY_ID --issuer-id ISSUER_ID --p8 AuthKey_KEY_ID.p8",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TeamSigning:
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
manager=None,
|
|
45
|
+
*,
|
|
46
|
+
folder=None,
|
|
47
|
+
environ=None,
|
|
48
|
+
make_client=None,
|
|
49
|
+
identity=None,
|
|
50
|
+
ledger=None,
|
|
51
|
+
sign=resign.sign,
|
|
52
|
+
run=None,
|
|
53
|
+
clock=time.time,
|
|
54
|
+
pmd3=tooling.pmd3,
|
|
55
|
+
go_ios=tooling.go_ios,
|
|
56
|
+
runner=None,
|
|
57
|
+
load_key=asc.load_key,
|
|
58
|
+
):
|
|
59
|
+
self.manager, self.environ, self.clock = manager, environ, clock
|
|
60
|
+
self.folder = Path(folder) if folder is not None else self._folder()
|
|
61
|
+
self.load_key = load_key
|
|
62
|
+
self.make_client = make_client or (lambda: asc.Client(self.load_key()))
|
|
63
|
+
self.identity = identity or identity_module.Identity(self.folder)
|
|
64
|
+
self.ledger = ledger or ledger_module.Ledger(self.folder / "ledger.jsonl")
|
|
65
|
+
self.sign, self.run, self.pmd3, self.go_ios, self.runner = sign, run, pmd3, go_ios, runner
|
|
66
|
+
self.lock = threading.Lock()
|
|
67
|
+
self.job = None
|
|
68
|
+
self.current = {"stage": "idle", "error": None, "fix": None, "card": None, "result": None}
|
|
69
|
+
self._card = None
|
|
70
|
+
self._key = (0.0, None)
|
|
71
|
+
|
|
72
|
+
def refresh(self):
|
|
73
|
+
"""Read the key again at the next status ("Check again")."""
|
|
74
|
+
self._key = (0.0, None)
|
|
75
|
+
|
|
76
|
+
def _folder(self):
|
|
77
|
+
from . import state_dir
|
|
78
|
+
|
|
79
|
+
return state_dir(getattr(self.manager, "data_dir", None))
|
|
80
|
+
|
|
81
|
+
# -- what Setup reads ------------------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
def status(self):
|
|
84
|
+
with self.lock:
|
|
85
|
+
current = dict(self.current)
|
|
86
|
+
# A finished run belongs to the phone it was for: another phone starts again from the card.
|
|
87
|
+
result = current.get("result") or {}
|
|
88
|
+
if (
|
|
89
|
+
current["stage"] == "ready"
|
|
90
|
+
and result.get("udid")
|
|
91
|
+
and self.manager is not None
|
|
92
|
+
and self.manager.settings().get("udid") not in (None, result["udid"])
|
|
93
|
+
):
|
|
94
|
+
current = {**current, "stage": "idle", "card": None, "result": None}
|
|
95
|
+
key = self._public_key()
|
|
96
|
+
driver = None
|
|
97
|
+
try:
|
|
98
|
+
driver = tooling.choose_driver(flag=enabled(self.environ), pmd3_path=self.pmd3(), go_ios_path=self.go_ios())
|
|
99
|
+
except DevError:
|
|
100
|
+
pass
|
|
101
|
+
return {
|
|
102
|
+
"enabled": enabled(self.environ),
|
|
103
|
+
"flag": FLAG,
|
|
104
|
+
"driver": driver,
|
|
105
|
+
"tools": {"pymobiledevice3": bool(self.pmd3()), "go-ios": bool(self.go_ios())},
|
|
106
|
+
"key": key,
|
|
107
|
+
"unresolved": self.ledger.unresolved(),
|
|
108
|
+
**current,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
def _public_key(self):
|
|
112
|
+
"""The key's public part, read from the Keychain at most every 30 s (Setup asks every couple of seconds)."""
|
|
113
|
+
at, key = self._key
|
|
114
|
+
if self.clock() - at < KEY_TTL and at:
|
|
115
|
+
return key
|
|
116
|
+
try:
|
|
117
|
+
key = self.load_key().public()
|
|
118
|
+
except DevError:
|
|
119
|
+
key = None
|
|
120
|
+
self._key = (self.clock(), key)
|
|
121
|
+
return key
|
|
122
|
+
|
|
123
|
+
def _set(self, **changes):
|
|
124
|
+
with self.lock:
|
|
125
|
+
self.current = {**self.current, **changes}
|
|
126
|
+
|
|
127
|
+
def _fail(self, error):
|
|
128
|
+
fix = getattr(error, "fix", "") or None
|
|
129
|
+
message = getattr(error, "message", None) or str(error) or type(error).__name__
|
|
130
|
+
self._set(stage="failed", error=message, fix=fix)
|
|
131
|
+
|
|
132
|
+
# -- the card --------------------------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
def _phones(self, phones=None):
|
|
135
|
+
if phones:
|
|
136
|
+
return [dict(phone) for phone in phones]
|
|
137
|
+
manager = self.manager
|
|
138
|
+
found = [d for d in (manager.devices() if manager else ()) if d.get("trusted")]
|
|
139
|
+
chosen = manager.device(found) if manager else None
|
|
140
|
+
ordered = ([chosen] if chosen else []) + [d for d in found if not chosen or d["udid"] != chosen["udid"]]
|
|
141
|
+
return [{"name": d["name"], "udid": d["udid"]} for d in ordered]
|
|
142
|
+
|
|
143
|
+
def _plan(self, client, phones, bundle_identifier=None, team=None):
|
|
144
|
+
phones = self._phones(phones)
|
|
145
|
+
if not phones:
|
|
146
|
+
raise DevError("Mobster has no iPhone to set up.", "Plug in an iPhone that trusts this Mac.")
|
|
147
|
+
if bundle_identifier is None:
|
|
148
|
+
team = team or (self.manager.settings().get("team") if self.manager else None) or client.team_id()
|
|
149
|
+
if not team:
|
|
150
|
+
raise DevError(
|
|
151
|
+
"Mobster couldn't tell which team the key belongs to.",
|
|
152
|
+
"Pass the team id with --team, or register an App ID on the team first.",
|
|
153
|
+
)
|
|
154
|
+
bundle_identifier = signing.runner_bundle_id(team) + ".xctrunner"
|
|
155
|
+
state = self.identity.state()
|
|
156
|
+
card = writes.plan(
|
|
157
|
+
client,
|
|
158
|
+
bundle_identifier=bundle_identifier,
|
|
159
|
+
phones=phones,
|
|
160
|
+
csr_pem=self.identity.request,
|
|
161
|
+
certificate_serial=state.get("certificate_serial"),
|
|
162
|
+
stamp=datetime.datetime.fromtimestamp(self.clock(), datetime.timezone.utc).strftime("%Y%m%d"),
|
|
163
|
+
)
|
|
164
|
+
return card, bundle_identifier
|
|
165
|
+
|
|
166
|
+
def card(self, phones=None, bundle_identifier=None, team=None):
|
|
167
|
+
"""Read the team and return the card (JSON-ready). Sends nothing to Apple but reads."""
|
|
168
|
+
with self.lock:
|
|
169
|
+
if self.current["stage"] in BUSY:
|
|
170
|
+
raise DevError("Mobster is still working on the last card.")
|
|
171
|
+
self.current = {**self.current, "stage": "reading", "error": None, "fix": None}
|
|
172
|
+
try:
|
|
173
|
+
self._require_enabled()
|
|
174
|
+
client = self.make_client()
|
|
175
|
+
card, bundle_identifier = self._plan(client, phones, bundle_identifier, team)
|
|
176
|
+
shown = card.public(key=client.key.public())
|
|
177
|
+
self._card = (card, bundle_identifier, phones, team)
|
|
178
|
+
self._set(stage="approval", card=shown)
|
|
179
|
+
return shown
|
|
180
|
+
except Exception as error: # noqa: BLE001 -- the card shows why, in words
|
|
181
|
+
self._fail(error)
|
|
182
|
+
raise
|
|
183
|
+
|
|
184
|
+
def apply(self, digest, phones=None, bundle_identifier=None, team=None, writes_only=False):
|
|
185
|
+
"""``card`` and ``approve`` in one process, for the terminal: reads the team again, refuses unless the card
|
|
186
|
+
is the one ``digest`` names, then runs it and waits."""
|
|
187
|
+
self.card(phones, bundle_identifier, team)
|
|
188
|
+
return self.approve(digest, writes_only=writes_only, background=False)
|
|
189
|
+
|
|
190
|
+
def _require_enabled(self):
|
|
191
|
+
if not enabled(self.environ):
|
|
192
|
+
raise DevError("Without Xcode is off.", f"Turn it on with {FLAG}=1.")
|
|
193
|
+
|
|
194
|
+
# -- approval and the run --------------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
def approve(self, digest, *, writes_only=False, background=True):
|
|
197
|
+
"""Run what the card shows. ``digest`` is the card's, as the person approved it."""
|
|
198
|
+
with self.lock:
|
|
199
|
+
if self.current["stage"] != "approval" or self._card is None:
|
|
200
|
+
raise DevError("There is no card waiting for approval.", "Ask for the card again.")
|
|
201
|
+
card = self._card[0]
|
|
202
|
+
if digest != card.digest:
|
|
203
|
+
raise DevError("That isn't the card on screen, so Mobster sent nothing.", "Look at it again.")
|
|
204
|
+
self.current = {**self.current, "stage": "writing", "error": None, "fix": None}
|
|
205
|
+
if background:
|
|
206
|
+
self.job = threading.Thread(
|
|
207
|
+
target=self._job, args=(digest, writes_only), name="mobster-team-signing", daemon=True
|
|
208
|
+
)
|
|
209
|
+
self.job.start()
|
|
210
|
+
else:
|
|
211
|
+
self._job(digest, writes_only)
|
|
212
|
+
return self.status()
|
|
213
|
+
|
|
214
|
+
def _job(self, digest, writes_only):
|
|
215
|
+
try:
|
|
216
|
+
client = self.make_client()
|
|
217
|
+
_, bundle_identifier, phones, team = self._card
|
|
218
|
+
self._recover(client)
|
|
219
|
+
card, _ = self._plan(client, phones, bundle_identifier, team)
|
|
220
|
+
if card.digest != digest:
|
|
221
|
+
raise DevError(
|
|
222
|
+
"Your team changed after you approved the card, so Mobster sent nothing.", "Look at the new card."
|
|
223
|
+
)
|
|
224
|
+
answers = writes.execute(client, card, digest, self.ledger, on_answer=self._keep)
|
|
225
|
+
if writes_only:
|
|
226
|
+
self._set(
|
|
227
|
+
stage="ready", result={"writes": {name: answer.get("id") for name, answer in answers.items()}}
|
|
228
|
+
)
|
|
229
|
+
return
|
|
230
|
+
self._set(stage="signing")
|
|
231
|
+
profile = self._profile(client, card, answers)
|
|
232
|
+
identity = self.identity.signing_identity()
|
|
233
|
+
if identity is None:
|
|
234
|
+
raise DevError(
|
|
235
|
+
"The signing certificate isn't in Mobster's keychain.", "Ask for the card again to make one."
|
|
236
|
+
)
|
|
237
|
+
derived = getattr(self.manager, "derived", None)
|
|
238
|
+
built = self.manager.settings().get("built_patches") if self.manager else None
|
|
239
|
+
source, where = prebuilt.locate(derived, built)
|
|
240
|
+
phone = self._phones(phones)[0]
|
|
241
|
+
signed = self.sign(
|
|
242
|
+
source,
|
|
243
|
+
profile,
|
|
244
|
+
identity["sha1"],
|
|
245
|
+
self.identity.keychain,
|
|
246
|
+
self.folder / "runners" / phone["udid"],
|
|
247
|
+
bundle_id=bundle_identifier,
|
|
248
|
+
)
|
|
249
|
+
self._set(stage="installing")
|
|
250
|
+
self._install(phone["udid"], signed)
|
|
251
|
+
self._record(phone, signed, where)
|
|
252
|
+
self._set(
|
|
253
|
+
stage="ready",
|
|
254
|
+
result={
|
|
255
|
+
"runner": signed["bundle_id"],
|
|
256
|
+
"expires": signed["expires"],
|
|
257
|
+
"phones": signed["udids"],
|
|
258
|
+
"udid": phone["udid"],
|
|
259
|
+
},
|
|
260
|
+
)
|
|
261
|
+
except Exception as error: # noqa: BLE001 -- the stage shows why; the ledger holds what was sent
|
|
262
|
+
self._fail(error)
|
|
263
|
+
|
|
264
|
+
def _keep(self, step, answer):
|
|
265
|
+
if step.produces == "certificate":
|
|
266
|
+
attributes = answer.get("attributes") or {}
|
|
267
|
+
self.identity.add_certificate(attributes.get("certificateContent") or "", attributes.get("serialNumber"))
|
|
268
|
+
|
|
269
|
+
def _recover(self, client):
|
|
270
|
+
"""A certificate Apple made whose answer Mobster didn't get to keep (it stopped between the write and the
|
|
271
|
+
keychain): fetch it by the id the ledger recorded, so the work isn't lost and the write isn't repeated."""
|
|
272
|
+
if self.identity.state().get("pending_key") != "imported":
|
|
273
|
+
return
|
|
274
|
+
writes_by_digest, made = {}, None
|
|
275
|
+
for event in self.ledger.events():
|
|
276
|
+
if event["event"] == "intent":
|
|
277
|
+
writes_by_digest[event["digest"]] = event["write"]
|
|
278
|
+
elif (
|
|
279
|
+
event["event"] in ("done", "resolved_done")
|
|
280
|
+
and writes_by_digest.get(event["digest"]) == "create_certificate"
|
|
281
|
+
):
|
|
282
|
+
made = event
|
|
283
|
+
if made is None or not (made.get("detail") or {}).get("id"):
|
|
284
|
+
return
|
|
285
|
+
content, serial = client.certificate_content(made["detail"]["id"])
|
|
286
|
+
if content:
|
|
287
|
+
self.identity.add_certificate(content, serial)
|
|
288
|
+
|
|
289
|
+
def _profile(self, client, card, answers):
|
|
290
|
+
folder = self.folder / "profiles"
|
|
291
|
+
from .. import env_file
|
|
292
|
+
|
|
293
|
+
env_file.private_dir(folder)
|
|
294
|
+
answer = answers.get("profile")
|
|
295
|
+
profile_id = (answer or card.profile or {}).get("id")
|
|
296
|
+
content = ((answer or {}).get("attributes") or {}).get("profileContent")
|
|
297
|
+
data = base64.b64decode(content) if content else client.profile_content(profile_id)
|
|
298
|
+
if not data:
|
|
299
|
+
raise DevError("Apple's profile came back empty.", "Try again.")
|
|
300
|
+
path = folder / f"{profile_id}.mobileprovision"
|
|
301
|
+
with env_file.open_private(path, "wb") as stream:
|
|
302
|
+
stream.write(data)
|
|
303
|
+
return path
|
|
304
|
+
|
|
305
|
+
def _install(self, udid, signed):
|
|
306
|
+
program = self.pmd3()
|
|
307
|
+
driver = tooling.choose_driver(flag=True, pmd3_path=program, go_ios_path=self.go_ios())
|
|
308
|
+
if driver == "pymobiledevice3":
|
|
309
|
+
tooling.ensure_mounted(program, udid, run=self.runner)
|
|
310
|
+
tooling.run_short(tooling.install_command(program, udid, signed["path"]), run=self.runner)
|
|
311
|
+
return
|
|
312
|
+
scratch = tempfile.mkdtemp(prefix="mobster-install-")
|
|
313
|
+
try:
|
|
314
|
+
ipa = tooling.ipa_of(signed["path"], scratch)
|
|
315
|
+
tooling.run_short(tooling.go_ios_install_command(self.go_ios(), udid, ipa), run=self.runner)
|
|
316
|
+
finally:
|
|
317
|
+
shutil.rmtree(scratch, ignore_errors=True)
|
|
318
|
+
|
|
319
|
+
def _record(self, phone, signed, where):
|
|
320
|
+
driver = tooling.choose_driver(flag=True, pmd3_path=self.pmd3(), go_ios_path=self.go_ios())
|
|
321
|
+
if self.manager is None:
|
|
322
|
+
return
|
|
323
|
+
self.manager.save_settings(
|
|
324
|
+
udid=phone["udid"],
|
|
325
|
+
device_name=phone["name"],
|
|
326
|
+
team=signed["team"],
|
|
327
|
+
built_for=phone["udid"],
|
|
328
|
+
built_patches=wda_patches.patch_digest(),
|
|
329
|
+
expires_at=signed["expires"],
|
|
330
|
+
autostart=True,
|
|
331
|
+
driver=driver,
|
|
332
|
+
signed_runner={
|
|
333
|
+
"path": signed["path"],
|
|
334
|
+
"bundle_id": signed["bundle_id"],
|
|
335
|
+
"profile": signed["profile"],
|
|
336
|
+
"prebuilt": where,
|
|
337
|
+
"installed_at": self.clock(),
|
|
338
|
+
},
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# -- after an unknown outcome ----------------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
def resolve(self, digest, made):
|
|
344
|
+
"""The person looked at developer.apple.com and says whether the change was made."""
|
|
345
|
+
self.ledger.resolve(digest, bool(made))
|
|
346
|
+
return self.status()
|
|
347
|
+
|
|
348
|
+
# -- Path C: a signed helper from another Mac ---------------------------------------------------------------
|
|
349
|
+
|
|
350
|
+
def export_bundle(self, destination, udid=None):
|
|
351
|
+
"""Write the helper this Mac signed for ``udid`` (default: the chosen phone) as a bundle other Macs can use."""
|
|
352
|
+
self._require_enabled()
|
|
353
|
+
udid = udid or self._phones()[0]["udid"]
|
|
354
|
+
runner = self.folder / "runners" / udid / "Runner.app"
|
|
355
|
+
if not runner.exists():
|
|
356
|
+
raise DevError("This Mac hasn't signed a helper for that iPhone.", "Set the iPhone up first.")
|
|
357
|
+
return bundle.export(runner, destination)
|
|
358
|
+
|
|
359
|
+
def import_bundle(self, path, team, phones=None):
|
|
360
|
+
"""Install a bundle's helper on the phone. ``team`` is what the person says the bundle is from."""
|
|
361
|
+
self._require_enabled()
|
|
362
|
+
phone = self._phones(phones)[0]
|
|
363
|
+
self._set(stage="installing", error=None, fix=None)
|
|
364
|
+
try:
|
|
365
|
+
imported = bundle.import_bundle(
|
|
366
|
+
path, self.folder / "runners" / phone["udid"], team=team, udid=phone["udid"]
|
|
367
|
+
)
|
|
368
|
+
self._install(phone["udid"], imported)
|
|
369
|
+
self._record(phone, imported, "an imported bundle")
|
|
370
|
+
except Exception as error: # noqa: BLE001
|
|
371
|
+
self._fail(error)
|
|
372
|
+
raise
|
|
373
|
+
self._set(
|
|
374
|
+
stage="ready",
|
|
375
|
+
result={
|
|
376
|
+
"runner": imported["bundle_id"],
|
|
377
|
+
"expires": imported["expires"],
|
|
378
|
+
"phones": imported["udids"],
|
|
379
|
+
"udid": phone["udid"],
|
|
380
|
+
},
|
|
381
|
+
)
|
|
382
|
+
return imported
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""Install and launch the signed runner without Xcode: pymobiledevice3 first, go-ios as the fallback.
|
|
2
|
+
|
|
3
|
+
Both are separate programs you install yourself: ``pipx install pymobiledevice3`` (GPL-3.0-or-later), or go-ios (MIT).
|
|
4
|
+
Mobster never imports or bundles either. It builds their command lines and runs them as it runs ``xcodebuild`` today:
|
|
5
|
+
the install is a short command, the launch is the long-running runner that Mobster's supervisor keeps alive and whose
|
|
6
|
+
readiness it checks with the same ``/status`` probe. ``xcodebuild`` and ``xcrun`` are never among them.
|
|
7
|
+
|
|
8
|
+
Unverified on a real iPhone as of 19 Oct 2026: the commands follow pymobiledevice3 11.18.0's and go-ios's own help. The
|
|
9
|
+
install-without-Xcode proof is pending a phone (team_signing/README in docs/device-setup.mdx says so).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import shutil
|
|
16
|
+
import zipfile
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from ..devkit import DevError
|
|
20
|
+
from ..phone_io import tunnel
|
|
21
|
+
|
|
22
|
+
PMD3 = "pymobiledevice3"
|
|
23
|
+
GO_IOS = "ios"
|
|
24
|
+
DRIVERS = ("xcodebuild", "pymobiledevice3", "go-ios")
|
|
25
|
+
LOOPBACK = "127.0.0.1"
|
|
26
|
+
UDID = re.compile(r"[0-9A-Fa-f-]{24,40}")
|
|
27
|
+
NEVER = frozenset({"xcodebuild", "xcrun", "xcode-select", "devicectl"})
|
|
28
|
+
GO_IOS_DIRS = (
|
|
29
|
+
"/opt/homebrew/bin",
|
|
30
|
+
"/usr/local/bin",
|
|
31
|
+
os.path.expanduser("~/go/bin"),
|
|
32
|
+
os.path.expanduser("~/.local/bin"),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def pmd3():
|
|
37
|
+
"""pymobiledevice3 on PATH or in the usual pipx and Homebrew folders; None when it isn't installed."""
|
|
38
|
+
return tunnel.tool()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def go_ios():
|
|
42
|
+
"""go-ios's ``ios`` on PATH or in the usual folders; None when it isn't installed."""
|
|
43
|
+
for candidate in (shutil.which(GO_IOS), *(os.path.join(directory, GO_IOS) for directory in GO_IOS_DIRS)):
|
|
44
|
+
if candidate and os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
|
45
|
+
return candidate
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def choose_driver(*, flag, pmd3_path=None, go_ios_path=None):
|
|
50
|
+
"""Which program installs and launches the runner. "xcodebuild" unless Without Xcode is on (__init__.FLAG); then
|
|
51
|
+
pymobiledevice3 if it is installed, else go-ios. DevError, with how to install, when neither is."""
|
|
52
|
+
if not flag:
|
|
53
|
+
return "xcodebuild"
|
|
54
|
+
if pmd3_path:
|
|
55
|
+
return "pymobiledevice3"
|
|
56
|
+
if go_ios_path:
|
|
57
|
+
return "go-ios"
|
|
58
|
+
raise DevError(
|
|
59
|
+
"Mobster needs pymobiledevice3 or go-ios to put its helper on your iPhone without Xcode, and neither "
|
|
60
|
+
"is on this Mac.",
|
|
61
|
+
"Install pymobiledevice3 with `pipx install pymobiledevice3`, then try again.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _udid(udid):
|
|
66
|
+
if not isinstance(udid, str) or not UDID.fullmatch(udid):
|
|
67
|
+
raise DevError("That isn't an iPhone's identifier.")
|
|
68
|
+
return udid
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _check(argv):
|
|
72
|
+
if Path(argv[0]).name in NEVER:
|
|
73
|
+
raise DevError(f"Mobster didn't run {Path(argv[0]).name}: this path never starts Xcode's tools.")
|
|
74
|
+
return argv
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# -- pymobiledevice3 -----------------------------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def developer_mode_command(program, udid):
|
|
81
|
+
return _check([program, "amfi", "developer-mode-status", "--native", "--udid", _udid(udid)])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def reveal_developer_mode_command(program, udid):
|
|
85
|
+
"""Makes the Developer Mode switch appear in Settings › Privacy & Security. The person still turns it on."""
|
|
86
|
+
return _check([program, "amfi", "reveal-developer-mode", "--native", "--udid", _udid(udid)])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def mounted_command(program, udid):
|
|
90
|
+
return _check([program, "mounter", "list", "--native", "--udid", _udid(udid)])
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def mount_command(program, udid):
|
|
94
|
+
"""Mounts Apple's developer disk image for the phone (it downloads the image and asks Apple's signing service to
|
|
95
|
+
personalize it, as Xcode does the first time a phone is prepared)."""
|
|
96
|
+
return _check([program, "mounter", "auto-mount", "--native", "--udid", _udid(udid)])
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def install_command(program, udid, app):
|
|
100
|
+
return _check([program, "apps", "install", str(app), "--native", "--udid", _udid(udid)])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def launch_command(program, udid, runner_bundle_id, env):
|
|
104
|
+
"""The XCTest session that is Mobster's helper. It runs until stopped. ``env`` is what the runner reads
|
|
105
|
+
(USE_IP, the capture gate)."""
|
|
106
|
+
command = [program, "developer", "dvt", "xcuitest", runner_bundle_id, "--native", "--udid", _udid(udid)]
|
|
107
|
+
for key, value in sorted(env.items()):
|
|
108
|
+
command += ["--env", f"{key}={value}"]
|
|
109
|
+
return _check(command)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def runner_env(kind="phone", environ=None):
|
|
113
|
+
"""The environment the runner starts with, as KEY=VALUE for ``--env``: USE_IP on the loopback (the USB relay reaches
|
|
114
|
+
it there) and the capture gate for an iPhone (wda_gate)."""
|
|
115
|
+
from ..wda import gate as wda_gate
|
|
116
|
+
|
|
117
|
+
gate = wda_gate.gate_for(kind, environ)
|
|
118
|
+
return {"USE_IP": LOOPBACK, wda_gate.OVERRIDE: gate}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# -- go-ios --------------------------------------------------------------------------------------------------------
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def ipa_of(app, folder):
|
|
125
|
+
"""A .ipa (Payload/<name>.app) of the signed runner, for go-ios, which installs packages and not bundles."""
|
|
126
|
+
app = Path(app)
|
|
127
|
+
target = Path(folder) / f"{app.stem}.ipa"
|
|
128
|
+
with zipfile.ZipFile(target, "w", zipfile.ZIP_DEFLATED) as archive:
|
|
129
|
+
for path in sorted(app.rglob("*")):
|
|
130
|
+
relative = f"Payload/{app.name}/{path.relative_to(app).as_posix()}"
|
|
131
|
+
info = zipfile.ZipInfo.from_file(path, relative)
|
|
132
|
+
if path.is_symlink():
|
|
133
|
+
info.external_attr = 0o120755 << 16
|
|
134
|
+
archive.writestr(info, os.readlink(path))
|
|
135
|
+
elif path.is_dir():
|
|
136
|
+
continue
|
|
137
|
+
else:
|
|
138
|
+
with open(path, "rb") as stream:
|
|
139
|
+
archive.writestr(info, stream.read(), zipfile.ZIP_DEFLATED)
|
|
140
|
+
return target
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def go_ios_install_command(program, udid, ipa):
|
|
144
|
+
return _check([program, "install", f"--path={ipa}", f"--udid={_udid(udid)}"])
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def go_ios_launch_command(program, udid, runner_bundle_id, env):
|
|
148
|
+
"""go-ios's ``runwda``: the runner's bundle id, its test bundle and the xctest configuration it runs."""
|
|
149
|
+
bundle = runner_bundle_id.removesuffix(".xctrunner")
|
|
150
|
+
command = [
|
|
151
|
+
program,
|
|
152
|
+
"runwda",
|
|
153
|
+
f"--bundleid={bundle}",
|
|
154
|
+
f"--testrunnerbundleid={runner_bundle_id}",
|
|
155
|
+
"--xctestconfig=WebDriverAgentRunner.xctest",
|
|
156
|
+
f"--udid={_udid(udid)}",
|
|
157
|
+
]
|
|
158
|
+
command += [f"--env={key}={value}" for key, value in sorted(env.items())]
|
|
159
|
+
return _check(command)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# -- one place to run the short commands ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def run_short(command, *, run=None, timeout=300):
|
|
166
|
+
"""Run an install-time command (not the launch). ``run(argv, timeout) -> (code, output)`` replaces the program in
|
|
167
|
+
tests. DevError with the program's last line when it fails."""
|
|
168
|
+
import subprocess
|
|
169
|
+
|
|
170
|
+
if run is None:
|
|
171
|
+
|
|
172
|
+
def run(argv, timeout):
|
|
173
|
+
from ..native_helpers import require_devices
|
|
174
|
+
|
|
175
|
+
require_devices()
|
|
176
|
+
env = dict(os.environ, NO_COLOR="1", TERM="dumb", COLUMNS="200")
|
|
177
|
+
try:
|
|
178
|
+
done = subprocess.run(
|
|
179
|
+
argv,
|
|
180
|
+
capture_output=True,
|
|
181
|
+
text=True,
|
|
182
|
+
errors="replace",
|
|
183
|
+
timeout=timeout,
|
|
184
|
+
stdin=subprocess.DEVNULL,
|
|
185
|
+
env=env,
|
|
186
|
+
)
|
|
187
|
+
except subprocess.TimeoutExpired:
|
|
188
|
+
return 124, f"{Path(argv[0]).name} did not finish in {timeout} seconds"
|
|
189
|
+
except OSError as error:
|
|
190
|
+
return 127, type(error).__name__
|
|
191
|
+
return done.returncode, (done.stdout or "") + (done.stderr or "")
|
|
192
|
+
|
|
193
|
+
code, output = run(command, timeout)
|
|
194
|
+
if code != 0:
|
|
195
|
+
line = next((line for line in reversed(str(output).splitlines()) if line.strip()), "")
|
|
196
|
+
raise DevError(
|
|
197
|
+
f"{Path(command[0]).name} couldn't {command[1]}. {line[:200]}".strip(),
|
|
198
|
+
"Keep the iPhone unlocked and plugged in, then try again.",
|
|
199
|
+
)
|
|
200
|
+
return output
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def developer_mode(program, udid, *, run=None):
|
|
204
|
+
"""True or False from pymobiledevice3, None when it can't say (the phone doesn't trust this Mac, or the tool is
|
|
205
|
+
missing). Reads the phone's Developer Mode state over USB; changes nothing."""
|
|
206
|
+
try:
|
|
207
|
+
output = run_short(developer_mode_command(program, udid), run=run, timeout=30)
|
|
208
|
+
except DevError:
|
|
209
|
+
return None
|
|
210
|
+
words = output.strip().lower().splitlines()
|
|
211
|
+
last = words[-1] if words else ""
|
|
212
|
+
if re.search(r"\btrue\b|\benabled\b", last):
|
|
213
|
+
return True
|
|
214
|
+
if re.search(r"\bfalse\b|\bdisabled\b", last):
|
|
215
|
+
return False
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def mounted(program, udid, *, run=None):
|
|
220
|
+
"""Whether a developer disk image is mounted on the phone: ``mounter list`` prints a JSON list of what is."""
|
|
221
|
+
try:
|
|
222
|
+
output = run_short(mounted_command(program, udid), run=run, timeout=60)
|
|
223
|
+
except DevError:
|
|
224
|
+
return False
|
|
225
|
+
try:
|
|
226
|
+
listed = json.loads(output)
|
|
227
|
+
except ValueError:
|
|
228
|
+
return False
|
|
229
|
+
return bool(listed)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def ensure_mounted(program, udid, *, run=None):
|
|
233
|
+
"""Mount the developer disk image unless one is. True when this call mounted it. A phone that Xcode (on any Mac)
|
|
234
|
+
prepared already has it, and nothing is downloaded or asked of Apple's signing service."""
|
|
235
|
+
if mounted(program, udid, run=run):
|
|
236
|
+
return False
|
|
237
|
+
try:
|
|
238
|
+
run_short(mount_command(program, udid), run=run, timeout=900)
|
|
239
|
+
except DevError as error:
|
|
240
|
+
if "already" not in error.message.lower():
|
|
241
|
+
raise
|
|
242
|
+
return False
|
|
243
|
+
return True
|