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
mobile_agent/cli_run.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""What a command's run needs besides parsing; run-time modules import it, never `__main__`,
|
|
2
|
+
which `python -m` loads twice."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from .cli_constants import DEFAULT_WDA_URL
|
|
8
|
+
|
|
9
|
+
NO_KEY = "no model key yet. Run `mobster login`, then run this again."
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def output(value):
|
|
13
|
+
import json
|
|
14
|
+
try:
|
|
15
|
+
print(json.dumps(value, allow_nan=False), flush=True)
|
|
16
|
+
except BrokenPipeError:
|
|
17
|
+
# The reader went away (`demo | head -1`): stop quietly, not with a traceback on exit.
|
|
18
|
+
os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno())
|
|
19
|
+
sys.exit(0)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class stop_at_safe_point:
|
|
23
|
+
"""While a task runs: the first ctrl+c, SIGTERM or SIGHUP sets ``stop``; a second raises KeyboardInterrupt."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, stop, on_stop=None):
|
|
26
|
+
self.stop, self.on_stop, self.caught, self.previous = stop, on_stop, [], {}
|
|
27
|
+
|
|
28
|
+
def handler(self, signum, frame):
|
|
29
|
+
self.caught.append(signum)
|
|
30
|
+
if self.stop.is_set():
|
|
31
|
+
raise KeyboardInterrupt
|
|
32
|
+
self.stop.set()
|
|
33
|
+
if self.on_stop is not None:
|
|
34
|
+
self.on_stop()
|
|
35
|
+
try:
|
|
36
|
+
print("mobster: stopping at the next safe point (ctrl+c again to force)", file=sys.stderr, flush=True)
|
|
37
|
+
except (OSError, ValueError, RuntimeError):
|
|
38
|
+
# A signal can interrupt stderr's buffered write; the diagnostic must not turn Stop into an error.
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
def __enter__(self):
|
|
42
|
+
import signal
|
|
43
|
+
import threading
|
|
44
|
+
if threading.current_thread() is threading.main_thread():
|
|
45
|
+
for name in ("SIGINT", "SIGTERM", "SIGHUP"):
|
|
46
|
+
number = getattr(signal, name, None)
|
|
47
|
+
if number is not None:
|
|
48
|
+
self.previous[number] = signal.signal(number, self.handler)
|
|
49
|
+
return self.caught
|
|
50
|
+
|
|
51
|
+
def __exit__(self, *exc):
|
|
52
|
+
import signal
|
|
53
|
+
for number, old in self.previous.items():
|
|
54
|
+
signal.signal(number, old)
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def event_printer(args, preview=False):
|
|
59
|
+
"""``output`` (JSON lines) for scripts and --json; the step renderer on a terminal."""
|
|
60
|
+
if getattr(args, "json", False) or not sys.stdout.isatty():
|
|
61
|
+
return output
|
|
62
|
+
from .console import ConsoleRenderer
|
|
63
|
+
return ConsoleRenderer(preview=preview)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def wda_url_for(args):
|
|
67
|
+
return (getattr(args, "wda_url", None) or os.environ.get("MOBSTER_WDA_URL") or DEFAULT_WDA_URL).rstrip("/")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def device_target(name):
|
|
71
|
+
"""The device `--device NAME` names (devices.discover), with a WDA address to drive."""
|
|
72
|
+
from . import devices
|
|
73
|
+
try:
|
|
74
|
+
item = devices.resolve(devices.discover(probe=False), name)
|
|
75
|
+
except LookupError as error:
|
|
76
|
+
raise NoDevice(str(error)) from None
|
|
77
|
+
if not item.get("wdaUrl"):
|
|
78
|
+
raise NoDevice(f"“{item['name']}” isn't set up yet. {item.get('reason') or ''} Set it up with "
|
|
79
|
+
"`mobster setup`, or in Mobster for Mac.".replace(" ", " "))
|
|
80
|
+
return item
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class NoDevice(ConnectionError):
|
|
84
|
+
"""Nothing answered at the WDA address (exit code 3)."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class DeviceBusy(RuntimeError):
|
|
88
|
+
"""Another Mobster process holds the phone's device lease."""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class MissingKey(ValueError):
|
|
92
|
+
"""The model key a command needs is not set (exit code 3)."""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def no_device_reason(exc):
|
|
96
|
+
"""A few words on how reaching WDA failed, from the transport's message (which never holds a body)."""
|
|
97
|
+
text = f"{type(exc).__name__} {exc}".lower()
|
|
98
|
+
for needle, words in (("refused", "connection refused"), ("reset", "connection reset"),
|
|
99
|
+
("disconnected", "connection closed"), ("gaierror", "unknown host"),
|
|
100
|
+
("timeout", "timed out"), ("deadline", "timed out"), ("http 4", "not WebDriverAgent"),
|
|
101
|
+
("json", "not WebDriverAgent")):
|
|
102
|
+
if needle in text:
|
|
103
|
+
return words
|
|
104
|
+
return "no usable answer"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def error_result(exc, exit_code):
|
|
108
|
+
"""`run`'s (and `demo`'s) JSON line for an error before or around the run."""
|
|
109
|
+
return {"event": "result", "status": "error", "ok": False, "error_type": type(exc).__name__,
|
|
110
|
+
"error": str(exc), "reason": str(exc), "exit_code": exit_code}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def error_line(exc):
|
|
114
|
+
"""A human line for a terminal: what happened, then what to do."""
|
|
115
|
+
return f"mobster: {exc}"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Compiled reading, dark behind ``MOBSTER_EXPERIMENTAL_COMPILED_READ`` (default off).
|
|
2
|
+
|
|
3
|
+
A screen is a typed interface, so reading it can be compiled the way acting is (learned/programs.py):
|
|
4
|
+
|
|
5
|
+
- ``extractors``: a screen the model read into rows once is read again by code when it has the same shape;
|
|
6
|
+
- ``delta``: a chained step sends the model the tree changes against a reference screen, not the whole tree;
|
|
7
|
+
- ``routes``: a program learned for one app version is only offered for that version.
|
|
8
|
+
|
|
9
|
+
Nothing here calls a model or imports a UI. The flag off, every hook returns what it was given.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
|
|
14
|
+
ENV_FLAG = "MOBSTER_EXPERIMENTAL_COMPILED_READ"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def enabled(env=None):
|
|
18
|
+
"""Whether compiled reading is switched on (``MOBSTER_EXPERIMENTAL_COMPILED_READ=1``)."""
|
|
19
|
+
env = os.environ if env is None else env
|
|
20
|
+
return (env.get(ENV_FLAG) or "").strip().casefold() in ("1", "true", "yes", "on")
|
|
21
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Token delta: a chained step sends the model what changed on the screen, not the whole tree.
|
|
2
|
+
|
|
3
|
+
The prompt's screen section is one line per element (``e4 Cell "General" @0,20,100,7``), its alias counting down
|
|
4
|
+
the screen. The agent's prompt keeps no earlier screen, so a diff alone would leave the model blind. The first
|
|
5
|
+
full screen of a run (and any later one a diff can't describe) becomes the *reference*: it goes once in the
|
|
6
|
+
prompt's cached part, and each next turn sends only
|
|
7
|
+
|
|
8
|
+
- the lines that changed or are new, whole, under their own alias;
|
|
9
|
+
- ``eN = reference eM`` for an element that is unchanged but moved from M to N (no arithmetic for the model);
|
|
10
|
+
- ``e1-e5 as in the reference`` for a run of unchanged elements that kept their alias.
|
|
11
|
+
|
|
12
|
+
A new app, a screen that shares under half of its lines with the reference (a scroll moves every row's frame) or a
|
|
13
|
+
diff longer than ``MAX_RATIO`` of the full screen is sent whole and becomes the new reference. Actions still
|
|
14
|
+
resolve through the current screen's aliases, so the diff changes what the model reads, never what an alias means.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import difflib
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
MAX_RATIO = .6
|
|
21
|
+
MIN_SHARED = .5
|
|
22
|
+
ALIAS = re.compile(r"^e(\d+) ")
|
|
23
|
+
HEAD = "Changes since the reference screen (turn {turn}); `eN = reference eM` is the same element, once eM:"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _split(line):
|
|
27
|
+
found = ALIAS.match(line)
|
|
28
|
+
return (int(found.group(1)), line[found.end():]) if found else (None, line)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def tokens(text):
|
|
32
|
+
"""An estimate of tokens: four characters each. The model's own tokenizer isn't called."""
|
|
33
|
+
return -(-len(text) // 4)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Delta:
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.reference, self.app, self.turn, self.shipped = None, None, 0, False
|
|
39
|
+
self.stats = {"full": 0, "delta": 0, "tokens_full": 0, "tokens_new": 0, "tokens_cached_read": 0}
|
|
40
|
+
|
|
41
|
+
def screen(self, app, lines, turn=0):
|
|
42
|
+
"""(reference lines to put in the cached part or None, the lines for this turn's screen section)."""
|
|
43
|
+
full = tokens("\n".join(lines))
|
|
44
|
+
body = self._diff(app, lines) if self.reference is not None and app == self.app else None
|
|
45
|
+
if body is None:
|
|
46
|
+
self.reference, self.app, self.turn, self.shipped = list(lines), app, turn, False
|
|
47
|
+
self.stats["full"] += 1
|
|
48
|
+
self.stats["tokens_full"] += full
|
|
49
|
+
self.stats["tokens_new"] += full
|
|
50
|
+
return None, lines
|
|
51
|
+
# The reference rides in the prompt's cached part: new input on its first turn, a cache read after.
|
|
52
|
+
held = tokens("\n".join(self.reference))
|
|
53
|
+
self.stats["delta"] += 1
|
|
54
|
+
self.stats["tokens_full"] += full
|
|
55
|
+
self.stats["tokens_new"] += tokens("\n".join(body)) + (0 if self.shipped else held)
|
|
56
|
+
self.stats["tokens_cached_read"] += held if self.shipped else 0
|
|
57
|
+
self.shipped = True
|
|
58
|
+
return list(self.reference), body
|
|
59
|
+
|
|
60
|
+
def _diff(self, app, lines):
|
|
61
|
+
old = [_split(line)[1] for line in self.reference]
|
|
62
|
+
new = [_split(line)[1] for line in lines]
|
|
63
|
+
matcher = difflib.SequenceMatcher(None, old, new, autojunk=False)
|
|
64
|
+
if matcher.ratio() < MIN_SHARED:
|
|
65
|
+
return None
|
|
66
|
+
body = []
|
|
67
|
+
for tag, a1, a2, b1, b2 in matcher.get_opcodes():
|
|
68
|
+
if tag == "equal":
|
|
69
|
+
if a1 == b1:
|
|
70
|
+
body.append(f"e{b1 + 1}-e{b2} as in the reference" if b2 - b1 > 1
|
|
71
|
+
else f"e{b1 + 1} as in the reference")
|
|
72
|
+
else:
|
|
73
|
+
body.extend(f"e{b1 + k + 1} = reference e{a1 + k + 1}" for k in range(b2 - b1))
|
|
74
|
+
else:
|
|
75
|
+
body.extend(lines[b1:b2])
|
|
76
|
+
text = [HEAD.format(turn=self.turn), *body]
|
|
77
|
+
if tokens("\n".join(text)) > MAX_RATIO * tokens("\n".join(lines)):
|
|
78
|
+
return None
|
|
79
|
+
return text
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def for_agent(agent):
|
|
83
|
+
"""The agent's Delta, made on first use; None with the flag off."""
|
|
84
|
+
from . import enabled
|
|
85
|
+
if not enabled():
|
|
86
|
+
return None
|
|
87
|
+
if getattr(agent, "_compiled_delta", None) is None:
|
|
88
|
+
agent._compiled_delta = Delta()
|
|
89
|
+
return agent._compiled_delta
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Learned extractors: a screen the model read into rows is read again by code.
|
|
2
|
+
|
|
3
|
+
After the model reads a screen (``read_screen``) every value it returned is found, word for word, on one line of the
|
|
4
|
+
screen or in one of the ", " parts iOS joins a row's texts with. The extractor keeps which line (and part) fed which
|
|
5
|
+
field of which row, and the screen's *skeleton*: each line's role, and for a line no value came from, a hash of its
|
|
6
|
+
words (digit runs folded, so a clock or a count doesn't change it). A later screen with the same skeleton is read by
|
|
7
|
+
taking those lines. Anything else is a miss and the model reads it.
|
|
8
|
+
|
|
9
|
+
The confidence check is the skeleton itself: the same number of lines, the same roles, the same hashed labels
|
|
10
|
+
where no value was, and every number field still a number. A value the model reworded (not a line's words) is not
|
|
11
|
+
learned. The store keeps no screen text: roles, hashes and line numbers only, in a replay_core path of kind
|
|
12
|
+
"extractor" (replay_core/store.py's Cache: 0600 files, written atomically).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
from ..replay_core.path import signature_hash
|
|
19
|
+
from ..replay_core.store import Cache, env_of
|
|
20
|
+
|
|
21
|
+
MAX_PER_KEY = 8
|
|
22
|
+
MAX_LINES = 400
|
|
23
|
+
NUMBER = re.compile(r"([+-]?(?:\d+\.?\d*|\.\d+))(?:\s+[^\W\d_]+)?") # "4.8", "4.8 stars"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _hash(words):
|
|
27
|
+
return hashlib.sha1(re.sub(r"\d+", "#", words).encode()).hexdigest()[:10]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _number(words):
|
|
31
|
+
found = NUMBER.fullmatch(words)
|
|
32
|
+
return float(found.group(1)) if found else None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _kind(value):
|
|
36
|
+
return "n" if isinstance(value, (int, float)) and not isinstance(value, bool) else "s"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _parts(words):
|
|
40
|
+
"""A composite label's parts: iOS joins a row's texts with ", " ("Rye loaf, $6.50, 4.8 stars")."""
|
|
41
|
+
return words.split(", ")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _matches(value, words):
|
|
45
|
+
if isinstance(value, str):
|
|
46
|
+
return words == value
|
|
47
|
+
number = _number(words)
|
|
48
|
+
return number is not None and number == value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _where(value, lines, taken, after):
|
|
52
|
+
"""(line, part) holding ``value`` exactly, the whole line (part None) before a part of it; the first after the
|
|
53
|
+
previous pick, else the first unused. None when no line has it."""
|
|
54
|
+
found = []
|
|
55
|
+
for index, (_, words) in enumerate(lines):
|
|
56
|
+
if (index, None) not in taken and _matches(value, words):
|
|
57
|
+
found.append((index, None))
|
|
58
|
+
parts = _parts(words)
|
|
59
|
+
if len(parts) > 1:
|
|
60
|
+
found.extend((index, k) for k, part in enumerate(parts)
|
|
61
|
+
if (index, k) not in taken and _matches(value, part))
|
|
62
|
+
later = [spot for spot in found if (spot[0], -1 if spot[1] is None else spot[1]) > after]
|
|
63
|
+
return (later or found or [None])[0]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def learn(lines, rows, fields):
|
|
67
|
+
"""An extractor for ``rows`` read off ``lines`` ([(role, words)]), or None when a value isn't a line's words
|
|
68
|
+
(or one of its ", " parts)."""
|
|
69
|
+
if not rows or len(lines) > MAX_LINES:
|
|
70
|
+
return None
|
|
71
|
+
taken, picks, kinds, after = set(), [], {}, (-1, -1)
|
|
72
|
+
for row in rows:
|
|
73
|
+
if not isinstance(row, dict) or set(row) != set(fields):
|
|
74
|
+
return None
|
|
75
|
+
pick = []
|
|
76
|
+
for name in fields:
|
|
77
|
+
value = row[name]
|
|
78
|
+
if value is None:
|
|
79
|
+
pick.append(None)
|
|
80
|
+
continue
|
|
81
|
+
if isinstance(value, bool) or not isinstance(value, (str, int, float)):
|
|
82
|
+
return None
|
|
83
|
+
kind = _kind(value)
|
|
84
|
+
if kinds.setdefault(name, kind) != kind or (kind == "s" and not value):
|
|
85
|
+
return None
|
|
86
|
+
spot = _where(value, lines, taken, after)
|
|
87
|
+
if spot is None:
|
|
88
|
+
return None
|
|
89
|
+
taken.add(spot)
|
|
90
|
+
after = (spot[0], -1 if spot[1] is None else spot[1])
|
|
91
|
+
pick.append(list(spot))
|
|
92
|
+
picks.append(pick)
|
|
93
|
+
whole = {name: kind == "n" and all(float(row[name]).is_integer() for row in rows if row[name] is not None)
|
|
94
|
+
for name, kind in kinds.items()}
|
|
95
|
+
used = {line for line, _ in taken}
|
|
96
|
+
skeleton = [[role, None if index in used else _hash(words)] for index, (role, words) in enumerate(lines)]
|
|
97
|
+
return {"skeleton": skeleton, "rows": picks, "kinds": [kinds.get(name, "s") for name in fields],
|
|
98
|
+
"whole": [bool(whole.get(name)) for name in fields]}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _text(lines, spot):
|
|
102
|
+
line, part = spot
|
|
103
|
+
words = lines[line][1]
|
|
104
|
+
return words if part is None else _parts(words)[part]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def extract(extractor, lines, fields):
|
|
108
|
+
"""The rows ``extractor`` reads from ``lines``, or None when the screen isn't the one it was learned on."""
|
|
109
|
+
skeleton = extractor.get("skeleton")
|
|
110
|
+
if not isinstance(skeleton, list) or len(skeleton) != len(lines):
|
|
111
|
+
return None
|
|
112
|
+
for (role, words), (want_role, want_hash) in zip(lines, skeleton):
|
|
113
|
+
if role != want_role or (want_hash is not None and _hash(words) != want_hash):
|
|
114
|
+
return None
|
|
115
|
+
kinds, whole = extractor["kinds"], extractor["whole"]
|
|
116
|
+
rows = []
|
|
117
|
+
for pick in extractor["rows"]:
|
|
118
|
+
row = {}
|
|
119
|
+
for position, name in enumerate(fields):
|
|
120
|
+
if pick[position] is None:
|
|
121
|
+
row[name] = None
|
|
122
|
+
continue
|
|
123
|
+
try:
|
|
124
|
+
text = _text(lines, pick[position])
|
|
125
|
+
except IndexError: # a composite label with fewer parts than it had
|
|
126
|
+
return None
|
|
127
|
+
if kinds[position] == "n":
|
|
128
|
+
number = _number(text)
|
|
129
|
+
if number is None:
|
|
130
|
+
return None
|
|
131
|
+
row[name] = int(number) if whole[position] else number
|
|
132
|
+
else:
|
|
133
|
+
row[name] = text
|
|
134
|
+
rows.append(row)
|
|
135
|
+
return rows
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class Extractors:
|
|
139
|
+
"""The extractors in ``directory``, one replay_core path per (app, fields); counts of hits and misses kept in it."""
|
|
140
|
+
|
|
141
|
+
def __init__(self, directory):
|
|
142
|
+
self.cache = Cache(directory)
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def identity(app, fields):
|
|
146
|
+
return {"kind": "extractor", "app": app or None, "fields": list(fields)}
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def env(version=None, runtime=None, device=None):
|
|
150
|
+
return env_of(version, runtime, device)
|
|
151
|
+
|
|
152
|
+
def _load(self, identity, env):
|
|
153
|
+
return self.cache.load(identity, env)
|
|
154
|
+
|
|
155
|
+
def read(self, app, fields, lines, env=None):
|
|
156
|
+
"""The rows read by code, or None (no extractor fits: the model reads, then ``remember``)."""
|
|
157
|
+
env = env or self.env()
|
|
158
|
+
identity = self.identity(app, fields)
|
|
159
|
+
path = self._load(identity, env)
|
|
160
|
+
if path is None:
|
|
161
|
+
return None
|
|
162
|
+
rows = None
|
|
163
|
+
for extractor in path.get("extractors") or ():
|
|
164
|
+
try:
|
|
165
|
+
rows = extract(extractor, lines, fields)
|
|
166
|
+
except (KeyError, IndexError, TypeError, ValueError):
|
|
167
|
+
rows = None
|
|
168
|
+
if rows is not None:
|
|
169
|
+
break
|
|
170
|
+
path["hits" if rows is not None else "misses"] = int(path.get("hits" if rows is not None else "misses")
|
|
171
|
+
or 0) + 1
|
|
172
|
+
self._save(path)
|
|
173
|
+
return rows
|
|
174
|
+
|
|
175
|
+
def remember(self, app, fields, lines, rows, env=None):
|
|
176
|
+
"""Keep the extractor for ``rows`` the model read off ``lines``; True when one was learned."""
|
|
177
|
+
extractor = learn(lines, rows, fields)
|
|
178
|
+
if extractor is None:
|
|
179
|
+
return False
|
|
180
|
+
env = env or self.env()
|
|
181
|
+
identity = self.identity(app, fields)
|
|
182
|
+
path = self._load(identity, env)
|
|
183
|
+
if path is None:
|
|
184
|
+
from ..replay_core.path import new_path
|
|
185
|
+
step = {"op": "READ_LIST", "app": app or None, "live": True,
|
|
186
|
+
"screen": {"hash": signature_hash(extractor["skeleton"])}}
|
|
187
|
+
path = new_path([step], kind="extractor", identity=identity, env=env)
|
|
188
|
+
path["extractors"], path["hits"], path["misses"] = [], 0, 0
|
|
189
|
+
known = [e for e in path["extractors"] if e.get("skeleton") != extractor["skeleton"]]
|
|
190
|
+
path["extractors"] = [extractor, *known][:MAX_PER_KEY]
|
|
191
|
+
self._save(path)
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
def counts(self, app, fields, env=None):
|
|
195
|
+
path = self._load(self.identity(app, fields), env or self.env())
|
|
196
|
+
return {"hits": int((path or {}).get("hits") or 0), "misses": int((path or {}).get("misses") or 0)}
|
|
197
|
+
|
|
198
|
+
def _save(self, path):
|
|
199
|
+
try:
|
|
200
|
+
self.cache.save(path)
|
|
201
|
+
except (OSError, ValueError):
|
|
202
|
+
pass
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""The local route corpus: saved programs keyed by the app version they were learned on.
|
|
2
|
+
|
|
3
|
+
A route is a saved program (learned/programs.py): a goal template and the steps that did it, as hashed screens plus
|
|
4
|
+
the controls' roles and names. The program store already finds a route first (``pre_run`` consults it before the model
|
|
5
|
+
plans); what it lacked is the version: a program learned on one build of an app replayed on the next and relied on
|
|
6
|
+
the step check to notice. With the flag on, a program is stored under the app's version (replay_core/store.py's env
|
|
7
|
+
key, so two builds keep two routes) and a later run is offered only the routes of its own version, or those learned
|
|
8
|
+
before versions were kept.
|
|
9
|
+
|
|
10
|
+
``shareable`` is the record a later opt-in sharing lane can send: the app, its version, a hash of the goal template
|
|
11
|
+
and each step's op, role and hashed label and screen. No screen text, no typed text, no template.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
|
|
16
|
+
from ..learned.programs import BLANK, folded
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def goal_fingerprint(template):
|
|
20
|
+
"""16 hex digits for a goal template: blanks, case, spacing and end punctuation don't change it."""
|
|
21
|
+
return hashlib.sha256(BLANK.sub("{}", folded(template)).encode()).hexdigest()[:16]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def version_of(runtime, bundle):
|
|
25
|
+
"""The version of ``bundle`` on the phone, or None (unknown: routes are then not told apart)."""
|
|
26
|
+
from ..packs.core import app_version
|
|
27
|
+
try:
|
|
28
|
+
return app_version(runtime, bundle)
|
|
29
|
+
except Exception:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def fits_version(path, version):
|
|
34
|
+
"""True when ``path`` may be offered on an app at ``version`` (None: any; a path without a version: always)."""
|
|
35
|
+
known = (path.get("env") or {}).get("app_version")
|
|
36
|
+
return version is None or known is None or known == version
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _digest(text):
|
|
40
|
+
return hashlib.sha1(str(text or "").encode()).hexdigest()[:10]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def shareable(path):
|
|
44
|
+
"""The hashes-only record of a route, or None when it has no version or a step it can't describe."""
|
|
45
|
+
identity, env = path.get("identity") or {}, path.get("env") or {}
|
|
46
|
+
if identity.get("kind") != "program" or not env.get("app_version") or not identity.get("app"):
|
|
47
|
+
return None
|
|
48
|
+
steps = []
|
|
49
|
+
for step in path.get("steps") or ():
|
|
50
|
+
target = step.get("target") if isinstance(step.get("target"), dict) else {}
|
|
51
|
+
screen = step.get("screen") if isinstance(step.get("screen"), dict) else {}
|
|
52
|
+
steps.append({"op": step.get("op"), "role": target.get("role") or None,
|
|
53
|
+
"label": _digest(target.get("label")) if target.get("label") else None,
|
|
54
|
+
"screen": str(screen.get("hash") or "")[:16] or None})
|
|
55
|
+
return {"app": identity["app"], "version": env["app_version"], "ios": env.get("ios"),
|
|
56
|
+
"goal": goal_fingerprint(identity.get("template") or ""), "steps": steps}
|