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,105 @@
|
|
|
1
|
+
"""The ledger of the writes Mobster makes to Apple: durable before a write is sent, and the reason a write whose outcome
|
|
2
|
+
is unknown is never sent again.
|
|
3
|
+
|
|
4
|
+
One JSON line per event in ``team-signing/ledger.jsonl`` (private, fsynced: journal.append_jsonl). ``begin`` writes
|
|
5
|
+
the intent first and refuses when the same write's last event is an intent or an unknown outcome, or when it is
|
|
6
|
+
already made. It records names, UDIDs, bundle ids and Apple's ids. It never records the API key, a token, a certificate
|
|
7
|
+
signing request, a certificate or a profile.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import fcntl
|
|
11
|
+
import json
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ..devkit import DevError
|
|
16
|
+
from ..devkit.asc import UNKNOWN_FIX, WriteUnknown
|
|
17
|
+
from ..journal import append_jsonl
|
|
18
|
+
|
|
19
|
+
OPEN = ("intent", "unknown")
|
|
20
|
+
MADE = ("done", "resolved_done")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LedgerError(DevError):
|
|
24
|
+
"""The ledger can't be read, so no write is sent."""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Ledger:
|
|
28
|
+
def __init__(self, path, clock=time.time):
|
|
29
|
+
self.path, self.clock = Path(path), clock
|
|
30
|
+
|
|
31
|
+
def events(self):
|
|
32
|
+
try:
|
|
33
|
+
lines = self.path.read_text(encoding="utf-8").splitlines()
|
|
34
|
+
except FileNotFoundError:
|
|
35
|
+
return []
|
|
36
|
+
except OSError:
|
|
37
|
+
raise LedgerError(
|
|
38
|
+
"Mobster can't read its record of changes to App Store Connect, so it sent nothing.",
|
|
39
|
+
f"Check {self.path}.",
|
|
40
|
+
) from None
|
|
41
|
+
try:
|
|
42
|
+
return [json.loads(line) for line in lines if line.strip()]
|
|
43
|
+
except ValueError:
|
|
44
|
+
raise LedgerError(
|
|
45
|
+
"Mobster's record of changes to App Store Connect is damaged, so it sent nothing.",
|
|
46
|
+
f"Move {self.path} aside to start a new one.",
|
|
47
|
+
) from None
|
|
48
|
+
|
|
49
|
+
def last(self, digest):
|
|
50
|
+
found = [event for event in self.events() if event.get("digest") == digest]
|
|
51
|
+
return found[-1] if found else None
|
|
52
|
+
|
|
53
|
+
def _append(self, event):
|
|
54
|
+
append_jsonl(self.path, {**event, "at": round(self.clock(), 3)})
|
|
55
|
+
|
|
56
|
+
def _lock(self):
|
|
57
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
handle = open(self.path.with_suffix(".lock"), "a")
|
|
59
|
+
fcntl.flock(handle, fcntl.LOCK_EX)
|
|
60
|
+
return handle
|
|
61
|
+
|
|
62
|
+
def begin(self, write, digest, summary):
|
|
63
|
+
"""Make the attempt durable. Raises (and sends nothing) when the last event for this write is an unknown
|
|
64
|
+
outcome or an attempt that never finished, or when it is already made."""
|
|
65
|
+
handle = self._lock()
|
|
66
|
+
try:
|
|
67
|
+
last = self.last(digest)
|
|
68
|
+
if last and last["event"] in OPEN:
|
|
69
|
+
raise WriteUnknown(
|
|
70
|
+
f"An earlier try of {write.replace('_', ' ')} never finished, so it may have gone through.",
|
|
71
|
+
UNKNOWN_FIX,
|
|
72
|
+
)
|
|
73
|
+
if last and last["event"] in MADE:
|
|
74
|
+
raise DevError(f"Mobster already made this change ({write.replace('_', ' ')}).")
|
|
75
|
+
self._append({"event": "intent", "write": write, "digest": digest, "summary": summary})
|
|
76
|
+
finally:
|
|
77
|
+
handle.close()
|
|
78
|
+
|
|
79
|
+
def end(self, digest, outcome, detail=None):
|
|
80
|
+
"""How a write ended: done, rejected or unknown."""
|
|
81
|
+
self._append({"event": outcome, "digest": digest, "detail": detail or {}})
|
|
82
|
+
|
|
83
|
+
def unresolved(self):
|
|
84
|
+
"""The writes whose outcome nobody knows: [{digest, write, summary}], oldest first."""
|
|
85
|
+
latest, intents = {}, {}
|
|
86
|
+
for event in self.events():
|
|
87
|
+
latest[event["digest"]] = event["event"]
|
|
88
|
+
if event["event"] == "intent":
|
|
89
|
+
intents[event["digest"]] = event
|
|
90
|
+
return [
|
|
91
|
+
{"digest": digest, "write": intents[digest]["write"], "summary": intents[digest]["summary"]}
|
|
92
|
+
for digest, state in latest.items()
|
|
93
|
+
if state in OPEN and digest in intents
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
def resolve(self, digest, made):
|
|
97
|
+
"""The person looked at Apple and says whether the change was made. Only an unresolved write can be resolved."""
|
|
98
|
+
handle = self._lock()
|
|
99
|
+
try:
|
|
100
|
+
last = self.last(digest)
|
|
101
|
+
if not last or last["event"] not in OPEN:
|
|
102
|
+
raise DevError("That change isn't waiting on you.")
|
|
103
|
+
self._append({"event": "resolved_done" if made else "resolved_not_done", "digest": digest})
|
|
104
|
+
finally:
|
|
105
|
+
handle.close()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""The unsigned helper runner Mobster signs: where it is, and whether it has the patches this Mobster has.
|
|
2
|
+
|
|
3
|
+
A Mac without Xcode can't build the runner, so it signs one built elsewhere (once per Mobster release, with the pinned
|
|
4
|
+
WebDriverAgent and ``wda_patches``). Three places, in this order: ``MOBSTER_PREBUILT_RUNNER`` (a folder holding
|
|
5
|
+
``WebDriverAgentRunner-Runner.app`` and a ``patches.txt`` with its ``wda_patches.patch_digest()``), the Mac app's own
|
|
6
|
+
copy in ``Contents/Resources/helper/``, and the runner an Xcode build on this Mac left in its derived-data folder
|
|
7
|
+
(whose digest the build recorded). A runner built with other patches is never used: it lacks the edit that keeps the
|
|
8
|
+
helper off the phone's network.
|
|
9
|
+
|
|
10
|
+
Shipping the app's copy is a release step of its own; this module only reads it.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from ..devkit import DevError
|
|
17
|
+
from ..wda import patches as wda_patches
|
|
18
|
+
|
|
19
|
+
NAME = "WebDriverAgentRunner-Runner.app"
|
|
20
|
+
ENV = "MOBSTER_PREBUILT_RUNNER"
|
|
21
|
+
APP_COPY = Path("/Applications/Mobster.app/Contents/Resources/helper")
|
|
22
|
+
DERIVED = Path("Build/Products/Debug-iphoneos") / NAME
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _digest_of(folder):
|
|
26
|
+
try:
|
|
27
|
+
return (Path(folder) / "patches.txt").read_text().strip()
|
|
28
|
+
except OSError:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def candidates(derived=None, built_patches=None, environ=None, app_copy=APP_COPY):
|
|
33
|
+
environ = os.environ if environ is None else environ
|
|
34
|
+
if environ.get(ENV):
|
|
35
|
+
folder = Path(environ[ENV]).expanduser()
|
|
36
|
+
yield folder / NAME, _digest_of(folder), "MOBSTER_PREBUILT_RUNNER"
|
|
37
|
+
yield Path(app_copy) / NAME, _digest_of(app_copy), "Mobster for Mac"
|
|
38
|
+
if derived is not None:
|
|
39
|
+
yield Path(derived) / DERIVED, built_patches, "this Mac's Xcode build"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def locate(derived=None, built_patches=None, environ=None, app_copy=APP_COPY):
|
|
43
|
+
"""(path, where) of a prebuilt runner with this Mobster's patches. DevError, with what to do, when there is none."""
|
|
44
|
+
wanted = wda_patches.patch_digest()
|
|
45
|
+
stale = None
|
|
46
|
+
for path, digest, where in candidates(derived, built_patches, environ, app_copy):
|
|
47
|
+
if not (path / "Info.plist").is_file():
|
|
48
|
+
continue
|
|
49
|
+
if digest == wanted:
|
|
50
|
+
return path, where
|
|
51
|
+
stale = where
|
|
52
|
+
if stale:
|
|
53
|
+
raise DevError(
|
|
54
|
+
f"The helper runner from {stale} was built with older patches than this Mobster has.",
|
|
55
|
+
"Update Mobster for Mac, which carries a matching helper.",
|
|
56
|
+
)
|
|
57
|
+
raise DevError(
|
|
58
|
+
"Mobster has no prebuilt helper to sign.",
|
|
59
|
+
"Update Mobster for Mac, which carries one, or point MOBSTER_PREBUILT_RUNNER at a folder with "
|
|
60
|
+
f"{NAME} and a patches.txt.",
|
|
61
|
+
)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Sign the helper's prebuilt runner on this Mac with ``codesign`` and the team's profile, and check that it verifies.
|
|
2
|
+
|
|
3
|
+
Input: the unsigned or previously signed ``WebDriverAgentRunner-Runner.app`` (the prebuilt runner, or a copy Xcode
|
|
4
|
+
built once), a development profile Apple made (``.mobileprovision``) and the identity in Mobster's signing keychain.
|
|
5
|
+
Output: a signed copy, ready for pymobiledevice3 to install. The original is never changed.
|
|
6
|
+
|
|
7
|
+
Only ``/usr/bin/codesign`` and ``/usr/bin/security`` run, with absolute paths, with ``DEVELOPER_DIR`` pointing nowhere,
|
|
8
|
+
so ``xcodebuild`` and ``xcrun`` can't start: Xcode isn't needed and isn't invoked. Nothing changes how the phone looks
|
|
9
|
+
to an app: the runner's contents are the ones it was built with.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import datetime
|
|
13
|
+
import hashlib
|
|
14
|
+
import os
|
|
15
|
+
import plistlib
|
|
16
|
+
import re
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
import tempfile
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from ..devkit import DevError
|
|
23
|
+
|
|
24
|
+
CODESIGN = "/usr/bin/codesign"
|
|
25
|
+
SECURITY = "/usr/bin/security"
|
|
26
|
+
ALLOWED = frozenset({CODESIGN, SECURITY})
|
|
27
|
+
ENV = {"PATH": "/usr/bin:/bin:/usr/sbin:/sbin", "DEVELOPER_DIR": "/var/empty", "HOME": str(Path.home())}
|
|
28
|
+
RUNNER_NAME = "WebDriverAgentRunner-Runner.app"
|
|
29
|
+
MIN_DAYS = 7 # a profile that ends sooner than this is refused: it would need replacing at once
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _run(argv, timeout=120):
|
|
33
|
+
if argv[0] not in ALLOWED:
|
|
34
|
+
raise DevError(f"Mobster didn't run {argv[0]}: signing uses codesign and security only.")
|
|
35
|
+
try:
|
|
36
|
+
done = subprocess.run(argv, capture_output=True, text=True, timeout=timeout, env=ENV, stdin=subprocess.DEVNULL)
|
|
37
|
+
except (OSError, subprocess.SubprocessError) as error:
|
|
38
|
+
return 127, "", type(error).__name__
|
|
39
|
+
return done.returncode, done.stdout, done.stderr
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def decode_profile(path, run=None):
|
|
43
|
+
"""The plist inside a ``.mobileprovision`` (a CMS envelope), as a dict. DevError when it can't be read."""
|
|
44
|
+
code, out, _ = (run or _run)([SECURITY, "cms", "-D", "-i", str(path)])
|
|
45
|
+
try:
|
|
46
|
+
profile = plistlib.loads(out.encode("utf-8")) if code == 0 and out else None
|
|
47
|
+
except Exception: # noqa: BLE001 -- unreadable is the error below
|
|
48
|
+
profile = None
|
|
49
|
+
if not isinstance(profile, dict):
|
|
50
|
+
raise DevError("Mobster couldn't read the provisioning profile.", "Make the profile again.")
|
|
51
|
+
return profile
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def profile_facts(profile):
|
|
55
|
+
"""{team, app_identifier, udids, certificates (SHA-1s), expires (epoch), uuid, name} from a decoded profile."""
|
|
56
|
+
entitlements = profile.get("Entitlements") or {}
|
|
57
|
+
teams = profile.get("TeamIdentifier") or []
|
|
58
|
+
expires = profile.get("ExpirationDate")
|
|
59
|
+
if isinstance(expires, datetime.datetime):
|
|
60
|
+
expires = (expires if expires.tzinfo else expires.replace(tzinfo=datetime.timezone.utc)).timestamp()
|
|
61
|
+
return {
|
|
62
|
+
"team": teams[0] if teams else None,
|
|
63
|
+
"app_identifier": entitlements.get("application-identifier"),
|
|
64
|
+
"udids": sorted(str(udid) for udid in profile.get("ProvisionedDevices") or ()),
|
|
65
|
+
"certificates": sorted(
|
|
66
|
+
hashlib.sha1(blob).hexdigest().upper()
|
|
67
|
+
for blob in profile.get("DeveloperCertificates") or ()
|
|
68
|
+
if isinstance(blob, bytes)
|
|
69
|
+
),
|
|
70
|
+
"expires": expires,
|
|
71
|
+
"uuid": profile.get("UUID"),
|
|
72
|
+
"name": profile.get("Name"),
|
|
73
|
+
"get_task_allow": entitlements.get("get-task-allow") is True,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def covers(app_identifier, team, bundle_id):
|
|
78
|
+
"""Whether a profile's application-identifier (``TEAM.com.example.app`` or ``TEAM.*`` or ``TEAM.com.example.*``)
|
|
79
|
+
covers ``bundle_id`` for ``team``."""
|
|
80
|
+
if not isinstance(app_identifier, str) or not app_identifier.startswith(team + "."):
|
|
81
|
+
return False
|
|
82
|
+
pattern = app_identifier[len(team) + 1 :]
|
|
83
|
+
if pattern.endswith("*"):
|
|
84
|
+
return bundle_id.startswith(pattern[:-1])
|
|
85
|
+
return pattern == bundle_id
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def runner_bundle_id(app):
|
|
89
|
+
try:
|
|
90
|
+
info = plistlib.loads((Path(app) / "Info.plist").read_bytes())
|
|
91
|
+
except (OSError, ValueError, plistlib.InvalidFileException):
|
|
92
|
+
raise DevError(f"{Path(app).name} isn't an app bundle Mobster can read.") from None
|
|
93
|
+
identifier = info.get("CFBundleIdentifier")
|
|
94
|
+
if not isinstance(identifier, str) or not identifier:
|
|
95
|
+
raise DevError(f"{Path(app).name} has no bundle identifier.")
|
|
96
|
+
return identifier
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def entitlements_for(facts, bundle_id):
|
|
100
|
+
"""What the signed runner is allowed: the four entitlements the runner has when Xcode signs it, limited to what the
|
|
101
|
+
profile grants."""
|
|
102
|
+
application = f"{facts['team']}.{bundle_id}"
|
|
103
|
+
out = {
|
|
104
|
+
"application-identifier": application,
|
|
105
|
+
"com.apple.developer.team-identifier": facts["team"],
|
|
106
|
+
"keychain-access-groups": [application],
|
|
107
|
+
}
|
|
108
|
+
if facts["get_task_allow"]:
|
|
109
|
+
out["get-task-allow"] = True
|
|
110
|
+
return out
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def code_objects(app):
|
|
114
|
+
"""Everything that carries its own signature, innermost first: the frameworks and libraries inside the test bundle,
|
|
115
|
+
the test bundle, the app's own frameworks and libraries. The app itself is signed last, by the caller."""
|
|
116
|
+
app = Path(app)
|
|
117
|
+
objects = []
|
|
118
|
+
for plugin in sorted((app / "PlugIns").glob("*.xctest")) if (app / "PlugIns").is_dir() else ():
|
|
119
|
+
frameworks = plugin / "Frameworks"
|
|
120
|
+
if frameworks.is_dir():
|
|
121
|
+
objects += sorted(p for p in frameworks.iterdir() if p.suffix in (".framework", ".dylib"))
|
|
122
|
+
objects.append(plugin)
|
|
123
|
+
frameworks = app / "Frameworks"
|
|
124
|
+
if frameworks.is_dir():
|
|
125
|
+
objects += sorted(p for p in frameworks.iterdir() if p.suffix in (".framework", ".dylib"))
|
|
126
|
+
return objects
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def sign(source, profile_path, identity, keychain, destination, *, now=None, run=None, decode=None, bundle_id=None):
|
|
130
|
+
"""Re-sign a copy of ``source`` into ``destination``/Runner.app and verify it.
|
|
131
|
+
|
|
132
|
+
``identity`` is the signing certificate's SHA-1. ``bundle_id`` is the id the signed runner carries (default: the one
|
|
133
|
+
it has). DevError, with the fix, when the profile isn't this runner's, doesn't
|
|
134
|
+
list the certificate, is about to end, or the signature doesn't verify. Returns the signed runner's facts."""
|
|
135
|
+
run = run or _run
|
|
136
|
+
source, destination = Path(source), Path(destination)
|
|
137
|
+
if not (source / "Info.plist").exists():
|
|
138
|
+
raise DevError(f"There is no runner at {source}.", "Mobster needs the prebuilt helper runner to sign.")
|
|
139
|
+
facts = profile_facts((decode or decode_profile)(profile_path))
|
|
140
|
+
original = runner_bundle_id(source)
|
|
141
|
+
bundle_id = bundle_id or original
|
|
142
|
+
now = datetime.datetime.now(datetime.timezone.utc).timestamp() if now is None else now
|
|
143
|
+
if not facts["team"] or not covers(facts["app_identifier"], facts["team"], bundle_id):
|
|
144
|
+
raise DevError(f"The profile isn't for {bundle_id}.", "Make a profile for the helper's App ID and try again.")
|
|
145
|
+
if identity.upper() not in facts["certificates"]:
|
|
146
|
+
raise DevError(
|
|
147
|
+
"The profile doesn't list the signing certificate.", "Make the profile again with the certificate."
|
|
148
|
+
)
|
|
149
|
+
if not facts["expires"] or facts["expires"] < now + MIN_DAYS * 86400:
|
|
150
|
+
raise DevError("The profile ends in under a week.", "Make a new profile.")
|
|
151
|
+
if not facts["udids"]:
|
|
152
|
+
raise DevError("The profile lists no iPhone.", "Register the iPhone and make the profile again.")
|
|
153
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
154
|
+
signed = destination / "Runner.app"
|
|
155
|
+
shutil.rmtree(signed, ignore_errors=True)
|
|
156
|
+
shutil.copytree(source, signed, symlinks=True)
|
|
157
|
+
try:
|
|
158
|
+
(signed / "embedded.mobileprovision").unlink(missing_ok=True)
|
|
159
|
+
for dsym in signed.rglob("*.dSYM"):
|
|
160
|
+
shutil.rmtree(dsym, ignore_errors=True)
|
|
161
|
+
shutil.copyfile(profile_path, signed / "embedded.mobileprovision")
|
|
162
|
+
if bundle_id != original:
|
|
163
|
+
# One prebuilt runner serves every team: an App ID is unique across Apple's teams, so each team's runner
|
|
164
|
+
# carries its own id (signing.runner_bundle_id), set here before the signature seals the bundle.
|
|
165
|
+
info = plistlib.loads((signed / "Info.plist").read_bytes())
|
|
166
|
+
info["CFBundleIdentifier"] = bundle_id
|
|
167
|
+
(signed / "Info.plist").write_bytes(plistlib.dumps(info, fmt=plistlib.FMT_XML))
|
|
168
|
+
workspace = Path(tempfile.mkdtemp(prefix="mobster-sign-"))
|
|
169
|
+
try:
|
|
170
|
+
plist = workspace / "entitlements.plist"
|
|
171
|
+
plist.write_bytes(plistlib.dumps(entitlements_for(facts, bundle_id)))
|
|
172
|
+
base = [CODESIGN, "-f", "-s", identity, "--keychain", str(keychain), "--timestamp=none"]
|
|
173
|
+
for item in code_objects(signed):
|
|
174
|
+
_checked(run, [*base, str(item)], f"sign {item.name}")
|
|
175
|
+
_checked(
|
|
176
|
+
run,
|
|
177
|
+
[*base, "--entitlements", str(plist), "--generate-entitlement-der", str(signed)],
|
|
178
|
+
f"sign {signed.name}",
|
|
179
|
+
)
|
|
180
|
+
finally:
|
|
181
|
+
shutil.rmtree(workspace, ignore_errors=True)
|
|
182
|
+
verify(signed, facts, bundle_id, run)
|
|
183
|
+
except BaseException:
|
|
184
|
+
shutil.rmtree(signed, ignore_errors=True)
|
|
185
|
+
raise
|
|
186
|
+
return {
|
|
187
|
+
"path": str(signed),
|
|
188
|
+
"team": facts["team"],
|
|
189
|
+
"bundle_id": bundle_id,
|
|
190
|
+
"udids": facts["udids"],
|
|
191
|
+
"expires": facts["expires"],
|
|
192
|
+
"profile": facts["uuid"],
|
|
193
|
+
"profile_name": facts["name"],
|
|
194
|
+
"certificate": identity.upper(),
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _checked(run, argv, what):
|
|
199
|
+
code, _, err = run(argv)
|
|
200
|
+
if code != 0:
|
|
201
|
+
line = next((line for line in reversed(err.splitlines()) if line.strip()), "")
|
|
202
|
+
raise DevError(
|
|
203
|
+
f"codesign couldn't {what}. {line[:200]}".strip(), "Unlock your Mac's login Keychain and sign again."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def verify(app, facts, bundle_id, run=None):
|
|
208
|
+
"""The signature holds all the way down, it names the team and the runner's bundle id, and the profile inside is
|
|
209
|
+
the one the signature was made with. DevError otherwise."""
|
|
210
|
+
run = run or _run
|
|
211
|
+
code, _, err = run([CODESIGN, "--verify", "--deep", "--strict", str(app)])
|
|
212
|
+
if code != 0:
|
|
213
|
+
raise DevError("The signed runner doesn't verify.", (err.strip().splitlines() or [""])[-1][:200])
|
|
214
|
+
code, _, err = run([CODESIGN, "-dvv", str(app)])
|
|
215
|
+
fields = dict(re.findall(r"^(Identifier|TeamIdentifier)=(.+)$", err, re.M))
|
|
216
|
+
if fields.get("TeamIdentifier") != facts["team"] or fields.get("Identifier") != bundle_id:
|
|
217
|
+
raise DevError("The signed runner names a different team or bundle id than its profile.")
|
|
218
|
+
code, out, _ = run([CODESIGN, "-d", "--entitlements", ":-", str(app)])
|
|
219
|
+
try:
|
|
220
|
+
granted = plistlib.loads(out.encode("utf-8"))
|
|
221
|
+
except Exception: # noqa: BLE001
|
|
222
|
+
granted = {}
|
|
223
|
+
if granted.get("application-identifier") != f"{facts['team']}.{bundle_id}":
|
|
224
|
+
raise DevError("The signed runner's entitlements don't match its profile.")
|
|
225
|
+
embedded = Path(app) / "embedded.mobileprovision"
|
|
226
|
+
if not embedded.exists():
|
|
227
|
+
raise DevError("The signed runner has no profile inside.")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def check(app, run=None, decode=None):
|
|
231
|
+
"""Read a signed runner back (Path C's import, a later install): its profile's facts after the signature verifies.
|
|
232
|
+
DevError when the signature doesn't hold or the profile is gone."""
|
|
233
|
+
app = Path(app)
|
|
234
|
+
profile = app / "embedded.mobileprovision"
|
|
235
|
+
if not profile.exists():
|
|
236
|
+
raise DevError("That runner isn't signed with a profile.")
|
|
237
|
+
facts = profile_facts(decode(profile) if decode else decode_profile(profile, run))
|
|
238
|
+
verify(app, facts, runner_bundle_id(app), run)
|
|
239
|
+
return {**facts, "bundle_id": runner_bundle_id(app)}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def tree_digest(app):
|
|
243
|
+
"""A digest of a runner bundle's files and their modes, to see that an exported bundle arrived unchanged."""
|
|
244
|
+
digest = hashlib.sha256()
|
|
245
|
+
base = Path(app)
|
|
246
|
+
for path in sorted(base.rglob("*")):
|
|
247
|
+
relative = path.relative_to(base).as_posix()
|
|
248
|
+
if path.is_symlink():
|
|
249
|
+
digest.update(f"L {relative} {os.readlink(path)}\n".encode())
|
|
250
|
+
elif path.is_file():
|
|
251
|
+
digest.update(f"F {relative} {path.stat().st_mode & 0o111 != 0} ".encode())
|
|
252
|
+
with open(path, "rb") as stream:
|
|
253
|
+
for block in iter(lambda: stream.read(1 << 20), b""):
|
|
254
|
+
digest.update(block)
|
|
255
|
+
digest.update(b"\n")
|
|
256
|
+
return digest.hexdigest()
|