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,287 @@
|
|
|
1
|
+
"""`mobster wifi`: use an iPhone without the cable, over the encrypted Wi-Fi link."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from . import GATE, store, switch, transport_on, words
|
|
7
|
+
|
|
8
|
+
DONE, FAILED, USAGE, MISSING_EXIT = 0, 1, 2, 3
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def add_arguments(parser, helpers):
|
|
12
|
+
subs = parser.add_subparsers(dest="wifi_command", metavar="<subcommand>")
|
|
13
|
+
subs.required = True
|
|
14
|
+
|
|
15
|
+
def device(sub):
|
|
16
|
+
sub.add_argument("--device", metavar="NAME",
|
|
17
|
+
help="the iPhone: an id, UDID or name from `mobster devices` (default: your iPhone)")
|
|
18
|
+
|
|
19
|
+
def as_json(sub):
|
|
20
|
+
sub.add_argument("--json", action="store_true", help="print one JSON object")
|
|
21
|
+
|
|
22
|
+
status = subs.add_parser("status", help="show how each iPhone is connected, and whether Wi-Fi is on for it",
|
|
23
|
+
description="Show each iPhone set up here: on the cable, on Wi-Fi, or not reachable, "
|
|
24
|
+
"and whether Wi-Fi is on for it. Reads only; changes nothing.")
|
|
25
|
+
as_json(status)
|
|
26
|
+
on = subs.add_parser("on", help="let an iPhone be used over Wi-Fi (plug it in first)",
|
|
27
|
+
description="Let Mobster reach an iPhone over Wi-Fi when it's unplugged, through the "
|
|
28
|
+
"encrypted link between this Mac and the phone. The iPhone must be plugged in "
|
|
29
|
+
"and trust this Mac; this is the same switch as Connect via network in Xcode.",
|
|
30
|
+
epilog="exit codes: 0 on, 1 it couldn't be turned on, 2 usage, 3 no such iPhone")
|
|
31
|
+
device(on)
|
|
32
|
+
as_json(on)
|
|
33
|
+
off = subs.add_parser("off", help="use an iPhone only with the cable again",
|
|
34
|
+
description="Stop using an iPhone over Wi-Fi. When Mobster turned on the iPhone's own "
|
|
35
|
+
"switch (Connect via network), it turns that off too, over the cable. When it "
|
|
36
|
+
"can't, or Xcode turned the switch on, it says what to do.",
|
|
37
|
+
epilog="exit codes: 0 off, 1 it failed or the iPhone's own switch is still on (plug it in "
|
|
38
|
+
"and run this again), 2 usage, 3 no such iPhone")
|
|
39
|
+
device(off)
|
|
40
|
+
as_json(off)
|
|
41
|
+
probe = subs.add_parser("probe", help="check the encrypted Wi-Fi link to an unplugged iPhone, step by step",
|
|
42
|
+
description="Check an unplugged iPhone over Wi-Fi: what Xcode's device service reports, "
|
|
43
|
+
"the encrypted tunnel, WebDriverAgent on the tunnel through this Mac's "
|
|
44
|
+
"relay, and that the phone's Wi-Fi address does not answer on 8100 or 9100. "
|
|
45
|
+
"It binds only 127.0.0.1 and changes nothing on the phone (with --start, it "
|
|
46
|
+
"launches Mobster's helper on it for the probe, then stops it).",
|
|
47
|
+
epilog="exit codes: 0 every check passed, 1 a check failed, 2 usage, 3 couldn't run")
|
|
48
|
+
device(probe)
|
|
49
|
+
probe.add_argument("--start", action="store_true",
|
|
50
|
+
help="launch Mobster's helper over Wi-Fi for the probe when it isn't running (quit the Mac "
|
|
51
|
+
"app first)")
|
|
52
|
+
as_json(probe)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run(args):
|
|
56
|
+
as_json = bool(getattr(args, "json", False))
|
|
57
|
+
try:
|
|
58
|
+
command = args.wifi_command
|
|
59
|
+
if command == "status":
|
|
60
|
+
return _status(as_json)
|
|
61
|
+
if command == "probe":
|
|
62
|
+
return _probe(args, as_json)
|
|
63
|
+
return _switch(args, command == "on", as_json)
|
|
64
|
+
except KeyboardInterrupt:
|
|
65
|
+
return 130
|
|
66
|
+
except _Stop as stop:
|
|
67
|
+
return _fail(stop, as_json)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _Stop(Exception):
|
|
71
|
+
def __init__(self, code, message, fix="", exit_code=FAILED):
|
|
72
|
+
super().__init__(message)
|
|
73
|
+
self.code, self.message, self.fix, self.exit_code = code, message, fix, exit_code
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _problem(code, name, exit_code=FAILED):
|
|
77
|
+
problem = words.problem(code, name)
|
|
78
|
+
return _Stop(code, problem["message"], problem["fix"], exit_code)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _fail(stop, as_json):
|
|
82
|
+
if as_json:
|
|
83
|
+
_print({"ok": False, "error": stop.message, "code": stop.code, **({"fix": stop.fix} if stop.fix else {})})
|
|
84
|
+
else:
|
|
85
|
+
print(stop.message, file=sys.stderr, flush=True)
|
|
86
|
+
if stop.fix:
|
|
87
|
+
print(stop.fix, file=sys.stderr, flush=True)
|
|
88
|
+
return stop.exit_code
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _print(value):
|
|
92
|
+
try:
|
|
93
|
+
print(json.dumps(value, ensure_ascii=False), flush=True)
|
|
94
|
+
except BrokenPipeError:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _data_dir():
|
|
99
|
+
from ..paths import user_data_dir
|
|
100
|
+
return user_data_dir()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _phones(data_dir):
|
|
104
|
+
"""The USB iPhones set up here, primary first (devices.discover without probing)."""
|
|
105
|
+
from .. import devices
|
|
106
|
+
return [item for item in devices.discover(data_dir, sims=False, probe=False)
|
|
107
|
+
if item["kind"] == "usb" and item.get("wdaUrl")]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _choose(data_dir, name):
|
|
111
|
+
from .. import devices
|
|
112
|
+
records = devices.discover(data_dir, sims=False, probe=False)
|
|
113
|
+
if name:
|
|
114
|
+
try:
|
|
115
|
+
item = devices.resolve(records, name)
|
|
116
|
+
except LookupError as error:
|
|
117
|
+
raise _Stop("no_device", str(error), "", MISSING_EXIT) from None
|
|
118
|
+
else:
|
|
119
|
+
primary = [item for item in records if item.get("primary")]
|
|
120
|
+
usb = [item for item in records if item["kind"] == "usb"]
|
|
121
|
+
item = primary[0] if len(primary) == 1 else usb[0] if len(usb) == 1 else None
|
|
122
|
+
if item is None:
|
|
123
|
+
if not usb:
|
|
124
|
+
raise _Stop("no_device", "Mobster sees no iPhone set up here.",
|
|
125
|
+
"Plug one in and set it up in the Mobster app first.", MISSING_EXIT)
|
|
126
|
+
raise _Stop("usage", "Several iPhones are here: " + ", ".join(f"{r['name']} ({r['id']})" for r in usb[:6])
|
|
127
|
+
+ ".", "Name one with --device.", USAGE)
|
|
128
|
+
if item["kind"] != "usb":
|
|
129
|
+
raise _Stop("usage", f"{item['name']} isn't an iPhone on a cable. Wi-Fi is for iPhones set up with a cable.",
|
|
130
|
+
"", USAGE)
|
|
131
|
+
return item
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _listing(data_dir, udid):
|
|
135
|
+
"""The phone's USB listing entry (trusted or not), or None when it isn't plugged in."""
|
|
136
|
+
from ..device_manager import DeviceManager
|
|
137
|
+
try:
|
|
138
|
+
devices = DeviceManager(data_dir).devices()
|
|
139
|
+
except Exception:
|
|
140
|
+
devices = []
|
|
141
|
+
return next((item for item in devices if item.get("udid", "").upper() == udid.upper()), None)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _connection(item, enabled, peers):
|
|
145
|
+
"""The words for how a phone is connected now, read from this Mac (the app's relay answering on its port)."""
|
|
146
|
+
from .. import devices
|
|
147
|
+
from ..mac_tools import usb_attached
|
|
148
|
+
if usb_attached(item["udid"]):
|
|
149
|
+
return "usb", words.ON_CABLE
|
|
150
|
+
if not enabled:
|
|
151
|
+
return None, words.OFF
|
|
152
|
+
if item.get("wdaUrl") and devices.wda_answers(item["wdaUrl"]):
|
|
153
|
+
return "wifi", words.ON_WIFI
|
|
154
|
+
peer = (peers or {}).get(item["udid"].upper())
|
|
155
|
+
if peer is not None and peer.network:
|
|
156
|
+
return None, f"{words.UNREACHABLE} yet (the encrypted link is {peer.tunnel})"
|
|
157
|
+
return None, words.UNREACHABLE
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _status(as_json):
|
|
161
|
+
from . import devicectl
|
|
162
|
+
data_dir = _data_dir()
|
|
163
|
+
phones = _phones(data_dir)
|
|
164
|
+
settings = {item["udid"]: store.get(data_dir, item["udid"]) for item in phones}
|
|
165
|
+
peers = None
|
|
166
|
+
if any(value["enabled"] for value in settings.values()):
|
|
167
|
+
try:
|
|
168
|
+
peers = devicectl.list_devices()
|
|
169
|
+
except (devicectl.DevicectlError, RuntimeError):
|
|
170
|
+
peers = None
|
|
171
|
+
rows = []
|
|
172
|
+
for item in phones:
|
|
173
|
+
value = settings[item["udid"]]
|
|
174
|
+
now, text = _connection(item, value["enabled"], peers)
|
|
175
|
+
peer = (peers or {}).get(item["udid"].upper())
|
|
176
|
+
rows.append({"id": item["id"], "udid": item["udid"], "name": item["name"], "enabled": value["enabled"],
|
|
177
|
+
"via": value["via"], "transport": now, "connection": text,
|
|
178
|
+
"tunnel": peer.tunnel if peer is not None and peer.network else None})
|
|
179
|
+
if as_json:
|
|
180
|
+
_print({"wifiTransport": transport_on(), "devices": rows})
|
|
181
|
+
return DONE
|
|
182
|
+
if transport_on():
|
|
183
|
+
print("Wi-Fi for iPhones: on")
|
|
184
|
+
else:
|
|
185
|
+
print(f"Wi-Fi for iPhones: off in this Mobster. To try it, set {GATE}=1 in your env file.")
|
|
186
|
+
if not rows:
|
|
187
|
+
print("No iPhone is set up here yet. Plug one in and set it up in the Mobster app.")
|
|
188
|
+
return DONE
|
|
189
|
+
width = max(len(row["name"] or "") for row in rows) + 2
|
|
190
|
+
for row in rows:
|
|
191
|
+
switch = "Wi-Fi on" if row["enabled"] else "Wi-Fi off"
|
|
192
|
+
print(f" {row['name'] or row['id']:<{width}} {row['connection']:<34} {switch}")
|
|
193
|
+
return DONE
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _switch(args, on, as_json):
|
|
197
|
+
data_dir = _data_dir()
|
|
198
|
+
item = _choose(data_dir, getattr(args, "device", None))
|
|
199
|
+
udid, name = item["udid"], item["name"]
|
|
200
|
+
listing = _listing(data_dir, udid)
|
|
201
|
+
following = None
|
|
202
|
+
try:
|
|
203
|
+
if on:
|
|
204
|
+
if switch.turn_on(data_dir, udid, listing) == "xcode":
|
|
205
|
+
following = words.problem("needs_xcode", name)
|
|
206
|
+
elif left_on := switch.turn_off(data_dir, udid, listing):
|
|
207
|
+
following = words.problem(left_on, name)
|
|
208
|
+
except switch.SwitchRefused as error:
|
|
209
|
+
raise _problem(error.code, name) from None
|
|
210
|
+
except RuntimeError as error:
|
|
211
|
+
raise _Stop("devices_off", f"{error}.", "", MISSING_EXIT) from None
|
|
212
|
+
if not on:
|
|
213
|
+
return _switched_off(item, following, as_json)
|
|
214
|
+
if as_json:
|
|
215
|
+
_print({"ok": True, "device": {"id": item["id"], "udid": udid, "name": name, "enabled": on},
|
|
216
|
+
"wifiTransport": transport_on(), **({"next": following} if following else {})})
|
|
217
|
+
return DONE
|
|
218
|
+
if following:
|
|
219
|
+
print(following["message"])
|
|
220
|
+
print(following["fix"])
|
|
221
|
+
else:
|
|
222
|
+
print(f"Wi-Fi is on for {name}. Unplug it when you like: Mobster reaches it through the encrypted link "
|
|
223
|
+
"between this Mac and the phone.")
|
|
224
|
+
print(f"Check it, unplugged: mobster wifi probe --device {json.dumps(name, ensure_ascii=False)}")
|
|
225
|
+
if not transport_on():
|
|
226
|
+
print(f"Wi-Fi for iPhones is off in this Mobster: to use it for tasks, set {GATE}=1 in your env file.")
|
|
227
|
+
return DONE
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _switched_off(item, left_on, as_json):
|
|
231
|
+
"""Wi-Fi is off in Mobster; ``left_on`` is the problem of the phone's own switch still on, or None. A switch
|
|
232
|
+
Mobster turned on and couldn't turn off fails (exit 1); one Xcode turned on is the person's to turn off (exit 0,
|
|
233
|
+
like `on`'s Xcode step)."""
|
|
234
|
+
name = item["name"]
|
|
235
|
+
failed = left_on is not None and left_on["code"] != "xcode_on"
|
|
236
|
+
if as_json:
|
|
237
|
+
device = {"id": item["id"], "udid": item["udid"], "name": name, "enabled": False}
|
|
238
|
+
_print({"ok": False, "error": left_on["message"], "code": left_on["code"], "fix": left_on["fix"],
|
|
239
|
+
"device": device} if failed else
|
|
240
|
+
{"ok": True, "device": device, "wifiTransport": transport_on(),
|
|
241
|
+
**({"next": left_on} if left_on else {})})
|
|
242
|
+
return FAILED if failed else DONE
|
|
243
|
+
print(f"Wi-Fi is off for {name}. Mobster uses it only with the cable.")
|
|
244
|
+
if left_on:
|
|
245
|
+
stream = sys.stderr if failed else sys.stdout
|
|
246
|
+
print(left_on["message"], file=stream)
|
|
247
|
+
print(left_on["fix"], file=stream)
|
|
248
|
+
if failed:
|
|
249
|
+
print(f"Plugged in and unlocked: mobster wifi off --device {json.dumps(name, ensure_ascii=False)}",
|
|
250
|
+
file=sys.stderr)
|
|
251
|
+
return FAILED if failed else DONE
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _probe(args, as_json):
|
|
255
|
+
from .. import devices, native_helpers
|
|
256
|
+
from ..mac_tools import usb_attached
|
|
257
|
+
from .probe import Probe, describe
|
|
258
|
+
if not native_helpers.devices_allowed():
|
|
259
|
+
raise _Stop("devices_off", "Devices are off in this process (MOBSTER_NO_DEVICES=1).", "", MISSING_EXIT)
|
|
260
|
+
data_dir = _data_dir()
|
|
261
|
+
item = _choose(data_dir, getattr(args, "device", None))
|
|
262
|
+
manager = None
|
|
263
|
+
if item.get("primary"):
|
|
264
|
+
from ..device_manager import DeviceManager
|
|
265
|
+
manager = DeviceManager(data_dir)
|
|
266
|
+
else:
|
|
267
|
+
manager = devices.manager_for(item["udid"], data_dir)
|
|
268
|
+
slot = devices.port_of(item.get("wdaUrl"))
|
|
269
|
+
if not as_json:
|
|
270
|
+
print(f"Probing {item['name']} over Wi-Fi "
|
|
271
|
+
f"({'Wi-Fi on' if store.enabled(data_dir, item['udid']) else 'Wi-Fi off'} "
|
|
272
|
+
f"for it; Wi-Fi for iPhones {'on' if transport_on() else 'off'} in this Mobster)")
|
|
273
|
+
probe = Probe(udid=item["udid"], name=item["name"], slot_port=slot, data_dir=data_dir,
|
|
274
|
+
start=bool(getattr(args, "start", False)), manager=manager,
|
|
275
|
+
out=None if as_json else (lambda step: print(describe(step), flush=True)))
|
|
276
|
+
try:
|
|
277
|
+
probe.run(usb_attached)
|
|
278
|
+
finally:
|
|
279
|
+
probe.stop_launched()
|
|
280
|
+
from . import result
|
|
281
|
+
result.record(data_dir, item["udid"], probe.public()) # the app's Wi-Fi tile reads it (GET /api/wireless)
|
|
282
|
+
if as_json:
|
|
283
|
+
_print(probe.public())
|
|
284
|
+
else:
|
|
285
|
+
print("")
|
|
286
|
+
print("Every check passed." if probe.ok else "Not every check passed: see the lines above.")
|
|
287
|
+
return DONE if probe.ok else FAILED
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""What CoreDevice sees: ``xcrun devicectl``, always with a timeout and a kill (SPEC §3.7 item 2)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import signal
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
import threading
|
|
9
|
+
import time
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Mapping, Optional
|
|
13
|
+
|
|
14
|
+
from .. import native_helpers
|
|
15
|
+
|
|
16
|
+
LIST_TIMEOUT = 8.0
|
|
17
|
+
BRING_UP_TIMEOUT = 20.0
|
|
18
|
+
APPS_TIMEOUT = 30.0
|
|
19
|
+
DEVELOPER_TTL = 300.0
|
|
20
|
+
# Apple doesn't document these field names: `mobster wifi probe` prints what the phone really reports.
|
|
21
|
+
TUNNEL = {"connected": "up", "connecting": "connecting", "disconnected": "down", "disconnecting": "down",
|
|
22
|
+
"unavailable": "down"}
|
|
23
|
+
ADDRESS_KEYS = ("tunnelIPAddress", "tunnelIPAddressString", "tunnelAddress")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DevicectlError(RuntimeError):
|
|
27
|
+
"""devicectl couldn't answer. ``code``: "no_xcode" | "hang" | "failed" | "identity"."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, message, code="failed"):
|
|
30
|
+
super().__init__(message)
|
|
31
|
+
self.code = code
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class Peer:
|
|
36
|
+
"""One phone as CoreDevice reports it."""
|
|
37
|
+
udid: str
|
|
38
|
+
name: Optional[str] = None
|
|
39
|
+
transport: Optional[str] = None # raw transportType: "wired", "localNetwork", ...
|
|
40
|
+
tunnel: str = "unknown" # "up" | "connecting" | "down" | "unknown"
|
|
41
|
+
tunnel_state: Optional[str] = None # raw tunnelState
|
|
42
|
+
address: Optional[str] = None # the tunnel address as reported (checked again before any use)
|
|
43
|
+
developer_mode: Optional[str] = None
|
|
44
|
+
os_version: Optional[str] = None # deviceProperties.osVersionNumber, e.g. "26.0"
|
|
45
|
+
hostnames: tuple = ()
|
|
46
|
+
fields: Mapping = field(default_factory=dict) # connectionProperties as read, for the probe
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def network(self):
|
|
50
|
+
return self.transport == "localNetwork"
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def wired(self):
|
|
54
|
+
return self.transport == "wired"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _text(value):
|
|
58
|
+
return value if isinstance(value, str) and value else None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parse(document):
|
|
62
|
+
result = document.get("result") if isinstance(document, dict) else None
|
|
63
|
+
if not isinstance(result, dict):
|
|
64
|
+
return {}
|
|
65
|
+
devices = result.get("devices")
|
|
66
|
+
if devices is None and isinstance(result.get("device"), dict):
|
|
67
|
+
devices = [result["device"]]
|
|
68
|
+
peers = {}
|
|
69
|
+
for device in devices if isinstance(devices, list) else ():
|
|
70
|
+
if not isinstance(device, dict):
|
|
71
|
+
continue
|
|
72
|
+
hardware = device.get("hardwareProperties") if isinstance(device.get("hardwareProperties"), dict) else {}
|
|
73
|
+
properties = device.get("deviceProperties") if isinstance(device.get("deviceProperties"), dict) else {}
|
|
74
|
+
connection = device.get("connectionProperties") if isinstance(device.get("connectionProperties"), dict) else {}
|
|
75
|
+
udid = _text(hardware.get("udid")) or _text(device.get("udid"))
|
|
76
|
+
if not udid:
|
|
77
|
+
continue
|
|
78
|
+
state = _text(connection.get("tunnelState"))
|
|
79
|
+
address = next((_text(connection.get(key)) for key in ADDRESS_KEYS if _text(connection.get(key))), None)
|
|
80
|
+
names = []
|
|
81
|
+
for key in ("localHostnames", "potentialHostnames"):
|
|
82
|
+
value = connection.get(key)
|
|
83
|
+
names += [item for item in value if isinstance(item, str) and item] if isinstance(value, list) else []
|
|
84
|
+
peers[udid.upper()] = Peer(
|
|
85
|
+
udid=udid, name=_text(properties.get("name")), transport=_text(connection.get("transportType")),
|
|
86
|
+
tunnel=TUNNEL.get(state, "unknown"), tunnel_state=state, address=address,
|
|
87
|
+
developer_mode=_text(properties.get("developerModeStatus")),
|
|
88
|
+
os_version=_text(properties.get("osVersionNumber")),
|
|
89
|
+
hostnames=tuple(dict.fromkeys(names)),
|
|
90
|
+
fields={key: value for key, value in connection.items()
|
|
91
|
+
if isinstance(value, (str, int, float, bool, list))})
|
|
92
|
+
return peers
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
_developer = {"at": None, "dir": None}
|
|
96
|
+
_developer_lock = threading.Lock()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def developer_dir():
|
|
100
|
+
"""Xcode's developer folder (mac_tools.developer_dir), looked up at most every 5 minutes; None without Xcode."""
|
|
101
|
+
with _developer_lock:
|
|
102
|
+
if _developer["at"] is None or time.monotonic() - _developer["at"] > DEVELOPER_TTL:
|
|
103
|
+
from ..mac_tools import developer_dir as lookup
|
|
104
|
+
try:
|
|
105
|
+
_developer["dir"] = lookup()[0]
|
|
106
|
+
except Exception:
|
|
107
|
+
_developer["dir"] = None
|
|
108
|
+
_developer["at"] = time.monotonic()
|
|
109
|
+
return _developer["dir"]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def command(args, developer):
|
|
113
|
+
"""The argv for ``xcrun devicectl <args>`` with Xcode's own xcrun."""
|
|
114
|
+
xcrun = Path(developer) / "usr" / "bin" / "xcrun"
|
|
115
|
+
return [str(xcrun) if xcrun.is_file() else "/usr/bin/xcrun", "devicectl", *args]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def run_json(args, timeout, *, developer=None, runner=None):
|
|
119
|
+
native_helpers.require_devices()
|
|
120
|
+
developer = developer or developer_dir()
|
|
121
|
+
if not developer:
|
|
122
|
+
raise DevicectlError("Xcode isn't installed or set up on this Mac", "no_xcode")
|
|
123
|
+
with tempfile.TemporaryDirectory(prefix="mobster-devicectl-") as folder:
|
|
124
|
+
output = Path(folder) / "result.json"
|
|
125
|
+
argv = command([*args, "--json-output", str(output)], developer)
|
|
126
|
+
env = {**os.environ, "DEVELOPER_DIR": developer}
|
|
127
|
+
code, error = (runner or _run)(argv, env, timeout)
|
|
128
|
+
try:
|
|
129
|
+
document = json.loads(output.read_text())
|
|
130
|
+
except (OSError, ValueError):
|
|
131
|
+
document = None
|
|
132
|
+
if document is None:
|
|
133
|
+
raise DevicectlError(f"devicectl gave no answer (exit {code}): {error[-200:]}".strip(), "failed")
|
|
134
|
+
outcome = (document.get("info") or {}).get("outcome") if isinstance(document, dict) else None
|
|
135
|
+
if code != 0 and outcome != "success" and not parse(document):
|
|
136
|
+
raise DevicectlError(f"devicectl failed (exit {code}): {error[-200:]}".strip(), "failed")
|
|
137
|
+
return document
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# `devicectl list devices` can hang while it sets up a tunnel (Apple FB15184273).
|
|
141
|
+
def _run(argv, env, timeout):
|
|
142
|
+
"""(exit code, the end of stderr). Its own process group, killed on overrun: DevicectlError "hang"."""
|
|
143
|
+
with tempfile.TemporaryFile() as errors:
|
|
144
|
+
try:
|
|
145
|
+
process = subprocess.Popen(argv, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=errors,
|
|
146
|
+
env=env, start_new_session=True)
|
|
147
|
+
except OSError as error:
|
|
148
|
+
raise DevicectlError(f"devicectl couldn't start: {error}", "no_xcode") from None
|
|
149
|
+
try:
|
|
150
|
+
code = process.wait(timeout=timeout)
|
|
151
|
+
except subprocess.TimeoutExpired:
|
|
152
|
+
kill(process)
|
|
153
|
+
raise DevicectlError(f"devicectl didn't answer in {timeout:g} s and was stopped", "hang") from None
|
|
154
|
+
errors.seek(0)
|
|
155
|
+
return code, errors.read()[-2000:].decode("utf-8", "replace")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def kill(process):
|
|
159
|
+
"""Kill a hung devicectl and everything it started."""
|
|
160
|
+
try:
|
|
161
|
+
os.killpg(process.pid, signal.SIGKILL)
|
|
162
|
+
except (ProcessLookupError, PermissionError):
|
|
163
|
+
pass
|
|
164
|
+
try:
|
|
165
|
+
process.wait(timeout=2)
|
|
166
|
+
except subprocess.TimeoutExpired:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def list_devices(timeout=LIST_TIMEOUT, **kwargs):
|
|
171
|
+
"""{UDID: Peer} for every device CoreDevice knows (on USB, over the network, or neither now)."""
|
|
172
|
+
return parse(run_json(["list", "devices"], timeout, **kwargs))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _checked(udid):
|
|
176
|
+
from ..devices import UDID_PATTERN
|
|
177
|
+
if not isinstance(udid, str) or not UDID_PATTERN.fullmatch(udid):
|
|
178
|
+
raise ValueError("A UDID is 24 to 40 hex digits and dashes")
|
|
179
|
+
return udid
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def bring_up(udid, timeout=BRING_UP_TIMEOUT, **kwargs):
|
|
183
|
+
peers = parse(run_json(["device", "info", "details", "--device", _checked(udid)], timeout, **kwargs))
|
|
184
|
+
if peers and udid.upper() not in peers:
|
|
185
|
+
raise DevicectlError("CoreDevice answered for a different iPhone than the one asked for", "identity")
|
|
186
|
+
return peers
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def lock_state(document):
|
|
190
|
+
# None is never read as unlocked
|
|
191
|
+
result = document.get("result") if isinstance(document, dict) else None
|
|
192
|
+
value = result.get("passcodeRequired") if isinstance(result, dict) else None
|
|
193
|
+
return value if isinstance(value, bool) else None
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_lock_state(udid, timeout=LIST_TIMEOUT, **kwargs):
|
|
197
|
+
try:
|
|
198
|
+
return lock_state(run_json(["device", "info", "lockState", "--device", _checked(udid)], timeout, **kwargs))
|
|
199
|
+
except (DevicectlError, RuntimeError): # RuntimeError: MOBSTER_NO_DEVICES=1
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def parse_apps(document, kind="user"):
|
|
204
|
+
from ..installed_apps import MAX_APPS, NAME_LIMIT, _clean
|
|
205
|
+
from ..state import validate_bundle_id
|
|
206
|
+
result = document.get("result") if isinstance(document, dict) else None
|
|
207
|
+
items = result.get("apps") if isinstance(result, dict) else None
|
|
208
|
+
if not isinstance(items, list):
|
|
209
|
+
raise DevicectlError("devicectl printed no app list", "failed")
|
|
210
|
+
apps, seen = [], set()
|
|
211
|
+
for item in items[:MAX_APPS]:
|
|
212
|
+
if not isinstance(item, dict) or (item.get("defaultApp") is True) != (kind == "system"):
|
|
213
|
+
continue
|
|
214
|
+
try:
|
|
215
|
+
bundle = validate_bundle_id(item.get("bundleIdentifier"))
|
|
216
|
+
except ValueError:
|
|
217
|
+
continue
|
|
218
|
+
if bundle in seen:
|
|
219
|
+
continue
|
|
220
|
+
seen.add(bundle)
|
|
221
|
+
apps.append({"bundleId": bundle, "name": _clean(item.get("name")) or bundle[:NAME_LIMIT],
|
|
222
|
+
"version": _clean(item.get("version"))})
|
|
223
|
+
apps.sort(key=lambda app: (app["name"].casefold(), app["bundleId"]))
|
|
224
|
+
return apps
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def read_apps(udid, kind="user", timeout=APPS_TIMEOUT, **kwargs):
|
|
228
|
+
"""parse_apps for ``udid``, read through CoreDevice (USB or Wi-Fi). Raises DevicectlError."""
|
|
229
|
+
return parse_apps(run_json(["device", "info", "apps", "--device", _checked(udid)], timeout, **kwargs), kind)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import socket
|
|
3
|
+
|
|
4
|
+
from . import network
|
|
5
|
+
|
|
6
|
+
PORTS = (8100, 9100)
|
|
7
|
+
# What WebDriverAgent (and Mobster's patches to it) answer with; a router's own page says none of these.
|
|
8
|
+
SIGNS = (b"WDA MJPEG Server", b"sessionId", b"Local clients only", b"WebDriverAgent")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def speaks_wda(host, port, timeout=1.5, connect=None):
|
|
12
|
+
try:
|
|
13
|
+
with (connect or socket.create_connection)((host, port), timeout) as sock:
|
|
14
|
+
sock.settimeout(timeout)
|
|
15
|
+
# the local Host header: Mobster's patched helper refuses any other, and this only asks if it is there
|
|
16
|
+
sock.sendall(f"GET /status HTTP/1.0\r\nHost: 127.0.0.1:{port}\r\n\r\n".encode())
|
|
17
|
+
data = sock.recv(1024)
|
|
18
|
+
except OSError:
|
|
19
|
+
return False
|
|
20
|
+
return any(sign in data for sign in SIGNS)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def exposed(hostnames, *, resolve=None, gateway=None, accepts=None, speaks=None):
|
|
24
|
+
resolve, gateway = resolve or network.resolve, gateway or network.default_gateway
|
|
25
|
+
accepts, speaks = accepts or network.port_open, speaks or speaks_wda
|
|
26
|
+
found = resolve(list(hostnames)) if hostnames else {"ipv4": [], "ipv6": []}
|
|
27
|
+
addresses = found["ipv4"] + found["ipv6"]
|
|
28
|
+
hits = [f"{address}:{port}" for address in addresses for port in PORTS if accepts(address, port)]
|
|
29
|
+
# a phone sharing its hotspot is the router and has no name: there only an answer from WebDriverAgent counts
|
|
30
|
+
router = gateway()
|
|
31
|
+
if router and router not in addresses:
|
|
32
|
+
hits += [f"{router}:{port}" for port in PORTS if speaks(router, port)]
|
|
33
|
+
return addresses, hits
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* mobster-wifi-pair: let a trusted iPhone be reached over Wi-Fi, or stop it.
|
|
3
|
+
*
|
|
4
|
+
* It sets the lockdown value com.apple.mobile.wireless_lockdown/EnableWifiConnections, the same flag as Xcode's
|
|
5
|
+
* "Connect via network". Only over USB: the phone must be plugged in and trust this Mac. It never pairs a new
|
|
6
|
+
* Mac, never enters a passcode and changes nothing else on the phone.
|
|
7
|
+
*
|
|
8
|
+
* mobster-wifi-pair --udid UDID --enable | --disable | --status
|
|
9
|
+
*
|
|
10
|
+
* Prints one JSON line, {"enabled": true|false}, and exits:
|
|
11
|
+
* 0 done, 2 usage, 3 no iPhone with that UDID on USB, 4 the iPhone doesn't trust this Mac (or is locked),
|
|
12
|
+
* 5 the iPhone refused the change, 6 anything else.
|
|
13
|
+
*
|
|
14
|
+
* This file is MIT, like Mobster. desktop/scripts/build-iphone-tools.sh compiles it against the static
|
|
15
|
+
* libimobiledevice and libplist it bundles (LGPL-2.1, like the iPhone tools already in the app).
|
|
16
|
+
*/
|
|
17
|
+
#include <stdio.h>
|
|
18
|
+
#include <string.h>
|
|
19
|
+
|
|
20
|
+
#include <libimobiledevice/libimobiledevice.h>
|
|
21
|
+
#include <libimobiledevice/lockdown.h>
|
|
22
|
+
#include <plist/plist.h>
|
|
23
|
+
|
|
24
|
+
#define DOMAIN "com.apple.mobile.wireless_lockdown"
|
|
25
|
+
#define KEY "EnableWifiConnections"
|
|
26
|
+
|
|
27
|
+
enum { DONE = 0, USAGE = 2, NO_DEVICE = 3, NOT_TRUSTED = 4, REFUSED = 5, FAILED = 6 };
|
|
28
|
+
|
|
29
|
+
static int usage(void)
|
|
30
|
+
{
|
|
31
|
+
fprintf(stderr, "usage: mobster-wifi-pair --udid UDID --enable | --disable | --status\n");
|
|
32
|
+
return USAGE;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static int valid_udid(const char *udid)
|
|
36
|
+
{
|
|
37
|
+
size_t length = strlen(udid);
|
|
38
|
+
if (length < 24 || length > 40)
|
|
39
|
+
return 0;
|
|
40
|
+
for (size_t i = 0; i < length; i++) {
|
|
41
|
+
char c = udid[i];
|
|
42
|
+
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == '-'))
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static int handshake_code(lockdownd_error_t error)
|
|
49
|
+
{
|
|
50
|
+
switch (error) {
|
|
51
|
+
case LOCKDOWN_E_PASSWORD_PROTECTED:
|
|
52
|
+
case LOCKDOWN_E_USER_DENIED_PAIRING:
|
|
53
|
+
case LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING:
|
|
54
|
+
case LOCKDOWN_E_INVALID_HOST_ID:
|
|
55
|
+
case LOCKDOWN_E_MISSING_PAIR_RECORD:
|
|
56
|
+
case LOCKDOWN_E_INVALID_PAIR_RECORD:
|
|
57
|
+
case LOCKDOWN_E_PAIRING_FAILED:
|
|
58
|
+
case LOCKDOWN_E_SSL_ERROR:
|
|
59
|
+
return NOT_TRUSTED;
|
|
60
|
+
default:
|
|
61
|
+
return FAILED;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
int main(int argc, char **argv)
|
|
66
|
+
{
|
|
67
|
+
const char *udid = NULL;
|
|
68
|
+
int action = 0; /* 'e' enable, 'd' disable, 's' status */
|
|
69
|
+
for (int i = 1; i < argc; i++) {
|
|
70
|
+
if (!strcmp(argv[i], "--udid") && i + 1 < argc)
|
|
71
|
+
udid = argv[++i];
|
|
72
|
+
else if (!action && !strcmp(argv[i], "--enable"))
|
|
73
|
+
action = 'e';
|
|
74
|
+
else if (!action && !strcmp(argv[i], "--disable"))
|
|
75
|
+
action = 'd';
|
|
76
|
+
else if (!action && !strcmp(argv[i], "--status"))
|
|
77
|
+
action = 's';
|
|
78
|
+
else
|
|
79
|
+
return usage();
|
|
80
|
+
}
|
|
81
|
+
if (!udid || !action || !valid_udid(udid))
|
|
82
|
+
return usage();
|
|
83
|
+
|
|
84
|
+
idevice_t device = NULL;
|
|
85
|
+
/* USB only: the flag is turned on with the cable, never over the network. */
|
|
86
|
+
if (idevice_new_with_options(&device, udid, IDEVICE_LOOKUP_USBMUX) != IDEVICE_E_SUCCESS || !device) {
|
|
87
|
+
fprintf(stderr, "no iPhone with that UDID is on USB\n");
|
|
88
|
+
return NO_DEVICE;
|
|
89
|
+
}
|
|
90
|
+
lockdownd_client_t client = NULL;
|
|
91
|
+
lockdownd_error_t error = lockdownd_client_new_with_handshake(device, &client, "mobster-wifi-pair");
|
|
92
|
+
if (error != LOCKDOWN_E_SUCCESS) {
|
|
93
|
+
fprintf(stderr, "lockdown handshake failed (%d)\n", error);
|
|
94
|
+
idevice_free(device);
|
|
95
|
+
return handshake_code(error);
|
|
96
|
+
}
|
|
97
|
+
int code = DONE;
|
|
98
|
+
if (action != 's') {
|
|
99
|
+
/* lockdownd_set_value takes the node and frees it. */
|
|
100
|
+
error = lockdownd_set_value(client, DOMAIN, KEY, plist_new_bool(action == 'e'));
|
|
101
|
+
if (error != LOCKDOWN_E_SUCCESS) {
|
|
102
|
+
fprintf(stderr, "the iPhone refused the change (%d)\n", error);
|
|
103
|
+
code = error == LOCKDOWN_E_SET_PROHIBITED || error == LOCKDOWN_E_IMMUTABLE_VALUE ? REFUSED : FAILED;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (code == DONE) {
|
|
107
|
+
plist_t value = NULL;
|
|
108
|
+
uint8_t enabled = 0;
|
|
109
|
+
error = lockdownd_get_value(client, DOMAIN, KEY, &value);
|
|
110
|
+
if (error == LOCKDOWN_E_SUCCESS && value && plist_get_node_type(value) == PLIST_BOOLEAN)
|
|
111
|
+
plist_get_bool_val(value, &enabled);
|
|
112
|
+
else if (error != LOCKDOWN_E_SUCCESS && error != LOCKDOWN_E_MISSING_VALUE && error != LOCKDOWN_E_MISSING_KEY)
|
|
113
|
+
code = FAILED;
|
|
114
|
+
if (value)
|
|
115
|
+
plist_free(value);
|
|
116
|
+
if (code == DONE)
|
|
117
|
+
printf("{\"enabled\": %s}\n", enabled ? "true" : "false");
|
|
118
|
+
}
|
|
119
|
+
lockdownd_client_free(client);
|
|
120
|
+
idevice_free(device);
|
|
121
|
+
return code;
|
|
122
|
+
}
|