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,47 @@
|
|
|
1
|
+
"""Pack pins: a team can pin each app pack to the app version it was checked on (``Verified.version``, the version
|
|
2
|
+
``GET /api/packs`` shows), or turn one off. A Mac with a pin uses a pack only when this build's pack matches it; any
|
|
3
|
+
other pack steps aside, and the general agent does the step, as it does for a pack that is stale. A pin never makes a
|
|
4
|
+
pack do more: packs only move around and read, so a pin is about every Mac in a team doing the same thing, not about
|
|
5
|
+
safety. The team track (teams/control_plane.py) sets the pins it read; with none set, every pack works as shipped.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
OFF = "off"
|
|
9
|
+
_pins = {} # pack key -> OFF, or the app version the team pinned the pack to
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def set_pins(pins):
|
|
13
|
+
"""Replace the pins with ``pins`` ({pack key: OFF or a version}); {} clears them."""
|
|
14
|
+
_pins.clear()
|
|
15
|
+
_pins.update(
|
|
16
|
+
{key: value for key, value in dict(pins or {}).items() if isinstance(key, str) and isinstance(value, str)}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def current():
|
|
21
|
+
return dict(_pins)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def aside(pack):
|
|
25
|
+
"""Why this Mac's ``pack`` steps aside under the team's pin, in a sentence, or None when it may be used."""
|
|
26
|
+
pin = _pins.get(pack.key)
|
|
27
|
+
if pin is None:
|
|
28
|
+
return None
|
|
29
|
+
if pin == OFF:
|
|
30
|
+
return f"Your team turned off the {pack.name} pack."
|
|
31
|
+
built = pack.verified.version if pack.verified else None
|
|
32
|
+
if built == pin:
|
|
33
|
+
return None
|
|
34
|
+
on = f"the pack checked on {pack.name} {built}" if built else "a pack with no checked version"
|
|
35
|
+
return (
|
|
36
|
+
f"Your team pinned the {pack.name} pack to {pack.name} {pin}, and this Mobster has {on}. Update Mobster, "
|
|
37
|
+
"or ask an Admin to change the pin."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def state(pack):
|
|
42
|
+
"""What this Mac reports about ``pack`` to its team: the version it has and whether it is stepping aside."""
|
|
43
|
+
return {
|
|
44
|
+
"key": pack.key,
|
|
45
|
+
"version": pack.verified.version if pack.verified else None,
|
|
46
|
+
"aside": aside(pack) is not None,
|
|
47
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""The Reddit pack: your home feed, a post and its comments, communities, chat, your inbox and search."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from . import core, rows, social
|
|
6
|
+
from .core import PickRow, Reader, Route, ScreenSpec, Tap, TypeIn, Verified, sig
|
|
7
|
+
|
|
8
|
+
BUNDLE = "com.reddit.Reddit"
|
|
9
|
+
TAB = (.86, 1.0)
|
|
10
|
+
TOP = (0.0, .2)
|
|
11
|
+
TAB_LABEL = r"^(Home|Communities|Answers|Create|Chat|Inbox)\b"
|
|
12
|
+
COLUMNS = (("place", "Community", "text"), ("handle", "Author", "text"), ("text", "Post", "text"),
|
|
13
|
+
("when", "When", "text"), ("score", "Score", "number"), ("comments", "Comments", "number"),
|
|
14
|
+
("ad", "Ad", "text"))
|
|
15
|
+
COMMENT_COLUMNS = (("handle", "Author", "text"), ("text", "Comment", "text"), ("when", "When", "text"),
|
|
16
|
+
("score", "Score", "number"))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
tab = rows.tab_tap(TAB)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Reddit(core.Pack):
|
|
23
|
+
name, key, prefix, bundle = "Reddit", "reddit", "RD", BUNDLE
|
|
24
|
+
verified = Verified("2025.44.0", "2026-10-14", "inferred from Reddit's usual labels; no recorded tree yet")
|
|
25
|
+
tabs = sig(TAB_LABEL, "Button", region=TAB)
|
|
26
|
+
never = re.compile(r"^(upvote|downvote|join|leave|award|give award|share|crosspost|save|hide|report|block|"
|
|
27
|
+
r"reply|post|send|follow)\b", re.I)
|
|
28
|
+
mentions = re.compile(r"\b(reddit|subreddits?|r/[A-Za-z0-9_]+|upvotes?)\b", re.I)
|
|
29
|
+
sites = ("www.reddit.com", "reddit.com", "old.reddit.com", "redd.it")
|
|
30
|
+
|
|
31
|
+
screens = (
|
|
32
|
+
ScreenSpec("home", "your Reddit home feed", (
|
|
33
|
+
sig(r"^Home$", "Button", region=TAB, selected=True, must=True, weight=2),
|
|
34
|
+
sig(r"\br/[A-Za-z0-9_]{2,}", "Cell", weight=2), sig(r"\b\d[\d,.]*[KM]? (upvotes?|comments?|votes?)\b")),
|
|
35
|
+
ocr=(sig(r"\br/[A-Za-z0-9_]{2,}", weight=2),
|
|
36
|
+
sig(r"\b\d[\d,.]*[KM]?\s*(upvotes?|comments?|votes?)\b|^\d[\d.,]*[KM]?$"),
|
|
37
|
+
sig(r"^(Home|Popular|Latest)$", region=TOP), sig(r"^(Join|Joined)$", never=True))),
|
|
38
|
+
ScreenSpec("community", "a community", (
|
|
39
|
+
sig(r"^r/[A-Za-z0-9_]{2,}$", region=TOP, must=True, weight=2), sig(r"^(Join|Joined)$", "Button"),
|
|
40
|
+
sig(r"\b(members|online)\b")),
|
|
41
|
+
ocr=(sig(r"^r/[A-Za-z0-9_]{2,}$", region=TOP, must=True, weight=2), sig(r"^(Join|Joined)$"),
|
|
42
|
+
sig(r"\b(members|online)\b"))),
|
|
43
|
+
ScreenSpec("post", "a post and its comments", (
|
|
44
|
+
sig(r"^(Add a comment|Join the conversation)", must=True, weight=2), sig(r"^Back$", "Button", region=TOP),
|
|
45
|
+
sig(r"^Sort by|^(Best|Top|New) comments")),
|
|
46
|
+
ocr=(sig(r"^(Add a comment|Join the conversation)", must=True, weight=2),
|
|
47
|
+
sig(r"^Back$", "Button", region=TOP), sig(r"^Sort by|^(Best|Top|New) comments"))),
|
|
48
|
+
ScreenSpec("chat", "your chats", (
|
|
49
|
+
sig(r"^Chat$", "Button", region=TAB, selected=True, must=True, weight=2),
|
|
50
|
+
sig(r"^(Threads|Requests|Start chat)\b")),
|
|
51
|
+
ocr=(sig(r"^Chat$", region=TOP, must=True, weight=2), sig(r"^(Threads|Requests|Start chat)\b"))),
|
|
52
|
+
ScreenSpec("inbox", "your inbox", (
|
|
53
|
+
sig(r"^Inbox$", "Button", region=TAB, selected=True, must=True, weight=2),
|
|
54
|
+
sig(r"^(Notifications|Messages)$", region=TOP)),
|
|
55
|
+
ocr=(sig(r"^Inbox$", region=TOP, must=True, weight=2),
|
|
56
|
+
sig(r"^(Notifications|Messages)$", region=(0, .25)))),
|
|
57
|
+
ScreenSpec("search", "search results", (
|
|
58
|
+
sig(r"^Search", "SearchField", must=True),
|
|
59
|
+
sig(r"^(Posts|Communities|Comments|Media|People)$", region=(0, .3), weight=2)),
|
|
60
|
+
ocr=(sig(r"^Search", "SearchField", must=True),
|
|
61
|
+
sig(r"^(Posts|Communities|Comments|Media|People)$", region=(0, .3), weight=2))),
|
|
62
|
+
)
|
|
63
|
+
routes = {
|
|
64
|
+
"home": Route("home", "your Reddit home feed", ("home",), (tab(r"^Home$", "the Home tab"),)),
|
|
65
|
+
"chat": Route("chat", "your chats", ("chat",), (tab(r"^Chat$", "the Chat tab"),)),
|
|
66
|
+
"inbox": Route("inbox", "your inbox", ("inbox",), (tab(r"^Inbox$", "the Inbox tab"),)),
|
|
67
|
+
"thread": Route("thread", "a chat", ("chat",), (tab(r"^Chat$", "the Chat tab"),
|
|
68
|
+
PickRow("chat", roles=("Cell", "Button"))), arg="name"),
|
|
69
|
+
"community": Route("community", "a community", ("community",), (),
|
|
70
|
+
link="https://www.reddit.com/r/{arg}", arg="name", from_tabs=False),
|
|
71
|
+
"user": Route("user", "a profile", ("community", "home"), (), link="https://www.reddit.com/user/{arg}",
|
|
72
|
+
arg="user", from_tabs=False),
|
|
73
|
+
"search": Route("search", "search results", ("search",),
|
|
74
|
+
(tab(r"^Home$", "the Home tab"), Tap(r"^Search", "Search", region=TOP),
|
|
75
|
+
TypeIn(r"^Search", "the search field", submit=True)), arg="query"),
|
|
76
|
+
"post": Route("post", "the post", ("post",), (), link="{arg}", arg="url", from_tabs=False),
|
|
77
|
+
"comments": Route("comments", "the comments", ("post",), (), from_tabs=False),
|
|
78
|
+
}
|
|
79
|
+
hidden_routes = ("user", "comments")
|
|
80
|
+
open_aliases = {"feed": "home", "subreddit": "community", "r": "community", "dms": "chat", "messages": "inbox",
|
|
81
|
+
"link": "post", "chats": "chat"}
|
|
82
|
+
read_aliases = {"feed": "home", "posts": "home", "subreddit": "community", "comment": "comments",
|
|
83
|
+
"dms": "chat", "chats": "chat"}
|
|
84
|
+
read_default = {"home": 10, "community": 10, "comments": 20, "search": 10, "chat": 15}
|
|
85
|
+
read_titles = {"home": "posts", "community": "posts", "comments": "comments", "search": "results",
|
|
86
|
+
"chat": "chats"}
|
|
87
|
+
nouns = {"home": "post", "community": "post", "comments": "comment", "search": "result", "chat": "chat"}
|
|
88
|
+
prompts = {
|
|
89
|
+
"OPEN": ("- RD_OPEN where: go to a Reddit screen by code, no taps from you: home | community <name> | chat "
|
|
90
|
+
"| inbox | thread <name> | search <words> | post <link>. If it can't, go on the usual way. It never "
|
|
91
|
+
"votes, joins, comments or posts: those are your taps, and the person approves them."),
|
|
92
|
+
"READ": ("- RD_READ what [n]: read Reddit by code from the screen's tree: home n | community n (the one "
|
|
93
|
+
"open) | comments n (on the post open) | search n | chat n. Gives community, author, title, time, "
|
|
94
|
+
"score and comments. Opens the screen it needs."),
|
|
95
|
+
}
|
|
96
|
+
mcp_open_hint = "your home feed, a community, chats, your inbox, search, a post's link"
|
|
97
|
+
mcp_read_hint = "your home feed, a community, a post's comments, search results or your chats"
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def readers(self):
|
|
101
|
+
not_a_chat = re.compile(r"^(threads|requests|start chat|new chat|search)\b", re.I)
|
|
102
|
+
return {"home": Reader(social.feed_reader("home", "posts", COLUMNS), ("home",), "home"),
|
|
103
|
+
"community": Reader(social.feed_reader("community", "posts", COLUMNS), ("community",), None),
|
|
104
|
+
"comments": Reader(social.feed_reader("comments", "comments", COMMENT_COLUMNS, roles=("Cell", "Other")),
|
|
105
|
+
("post",), None),
|
|
106
|
+
"search": Reader(social.feed_reader("search", "results", COLUMNS), ("search",), None),
|
|
107
|
+
"chat": Reader(social.dm_reader(not_a_chat), ("chat",), "chat")}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
PACK = Reddit()
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""Track ``packs``'s registrations (tracks.load calls ``register(api)`` once per process, staged)."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import time as _time
|
|
5
|
+
|
|
6
|
+
from . import pins
|
|
7
|
+
|
|
8
|
+
TOOL_ORDER = 4
|
|
9
|
+
CONTEXT_ORDER = 40
|
|
10
|
+
SLEEP = _time.sleep # the health run's pause after a tap (tests set it to nothing)
|
|
11
|
+
# The packs Mobster ships, by module: each module has PACK, an instance of core.Pack.
|
|
12
|
+
MODULES = ("instagram", "tiktok", "messages", "settings", "calendar", "notes", "maps", "photos", "safari",
|
|
13
|
+
"x", "youtube", "threads", "linkedin", "reddit")
|
|
14
|
+
_extra = []
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def all_packs():
|
|
18
|
+
"""Every pack this build has, in MODULES order, then any a test added (``add_for_tests``)."""
|
|
19
|
+
import importlib
|
|
20
|
+
out = []
|
|
21
|
+
for name in MODULES:
|
|
22
|
+
try:
|
|
23
|
+
module = importlib.import_module(f"{__package__}.{name}")
|
|
24
|
+
except ModuleNotFoundError as error:
|
|
25
|
+
if error.name != f"{__package__}.{name}":
|
|
26
|
+
raise
|
|
27
|
+
continue
|
|
28
|
+
out.append(module.PACK)
|
|
29
|
+
return out + list(_extra)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def add_for_tests(pack):
|
|
33
|
+
_extra.append(pack)
|
|
34
|
+
return lambda: _extra.remove(pack)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def status_route(request):
|
|
38
|
+
"""GET /api/packs: {packs: [health.status ...]}."""
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
|
|
41
|
+
from ..api_routes import Response
|
|
42
|
+
from . import core, health
|
|
43
|
+
runtime = request.runtime
|
|
44
|
+
state_db = getattr(getattr(runtime, "config", None), "state_db", None)
|
|
45
|
+
entries = health.recent(Path(state_db).parent if state_db else None)
|
|
46
|
+
return Response.json({"packs": [health.status(pack, core.app_version(runtime, pack.bundle), entries)
|
|
47
|
+
for pack in all_packs()]})
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Confirmer:
|
|
51
|
+
def on_finished(self, run, summary, private):
|
|
52
|
+
if (summary or {}).get("status") == "completed":
|
|
53
|
+
from . import heal
|
|
54
|
+
heal.confirm_run(getattr(run, "id", None))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def register(api):
|
|
58
|
+
"""``api`` is mobile_agent.harness_api."""
|
|
59
|
+
from .. import api_routes
|
|
60
|
+
from ..mcp_server import registry as mcp_registry
|
|
61
|
+
from . import context, mcp, tools
|
|
62
|
+
packs = all_packs()
|
|
63
|
+
if packs:
|
|
64
|
+
# What a pack read stays in front of the model on later turns (context.py).
|
|
65
|
+
api.register_context_provider(context.factory(packs), order=CONTEXT_ORDER)
|
|
66
|
+
for pack in packs:
|
|
67
|
+
api.register_tool(tools.factory(pack, "OPEN"), order=TOOL_ORDER)
|
|
68
|
+
api.register_tool(tools.factory(pack, "READ"), order=TOOL_ORDER)
|
|
69
|
+
if packs:
|
|
70
|
+
mcp_registry.register_provider(mcp.PacksProvider(packs))
|
|
71
|
+
# A run that finished completed confirms the variants proposed from screens a pack missed in it (heal.py).
|
|
72
|
+
api.register_run_listener(Confirmer())
|
|
73
|
+
api_routes.register(api_routes.Route("packs", "GET", "/api/packs", status_route, body="none"))
|
|
74
|
+
if packs:
|
|
75
|
+
# The watch: a read-only health run per app, as a task with packCheck (a schedule can run it).
|
|
76
|
+
api.register_run_field("packCheck", functools.partial(validate_pack_key, "packCheck"))
|
|
77
|
+
api.register_pre_run(pack_check, order=3)
|
|
78
|
+
# The numbers: a read-only run per app that stores today's profile and Insights numbers (accounts/stats.py).
|
|
79
|
+
api.register_run_field("packStats", functools.partial(validate_pack_key, "packStats"))
|
|
80
|
+
api.register_pre_run(pack_stats, order=3)
|
|
81
|
+
# A workflow's "Read an app" step (graph app.read): one reader's rows, by code, no model.
|
|
82
|
+
api.register_run_field("packRead", validate_pack_read)
|
|
83
|
+
api.register_pre_run(pack_read, order=3)
|
|
84
|
+
# The front door: a request that is exactly a pack reader's question or a Settings page is answered by code,
|
|
85
|
+
# before any model call (front_door.py).
|
|
86
|
+
from . import front_door
|
|
87
|
+
api.register_pre_run(front_door.hook, order=front_door.ORDER)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def validate_pack_key(field, value, runtime):
|
|
91
|
+
"""A pack run field (``packCheck``, ``packStats``): a pack's key; the run is that pack's read-only run."""
|
|
92
|
+
if not isinstance(value, str) or value not in {pack.key for pack in all_packs()}:
|
|
93
|
+
raise ValueError(f"{field} is the key of an app pack (GET /api/packs)")
|
|
94
|
+
return value
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def validate_pack_read(value, runtime):
|
|
98
|
+
"""``packRead``: {"pack", "reader", "n", "arg"}, one of a pack's readers; the run is that read, with no model."""
|
|
99
|
+
if not isinstance(value, dict) or set(value) - {"pack", "reader", "n", "arg"}:
|
|
100
|
+
raise ValueError("packRead is {pack, reader, n, arg}")
|
|
101
|
+
pack = next((p for p in all_packs() if p.key == value.get("pack")), None)
|
|
102
|
+
if pack is None or value.get("reader") not in pack.readers:
|
|
103
|
+
raise ValueError("packRead names an app pack and one of its readers (GET /api/packs)")
|
|
104
|
+
n = value.get("n", 10)
|
|
105
|
+
if type(n) is not int or not 1 <= n <= 50:
|
|
106
|
+
raise ValueError("packRead reads 1 to 50 rows")
|
|
107
|
+
arg = value.get("arg")
|
|
108
|
+
if arg is not None and (not isinstance(arg, str) or not 1 <= len(arg.strip()) <= 200):
|
|
109
|
+
raise ValueError("packRead's arg is 1 to 200 characters")
|
|
110
|
+
return {"pack": pack.key, "reader": value["reader"], "n": n, "arg": arg.strip() if arg else None}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def stepping_aside(hook):
|
|
114
|
+
"""A pack pre-run hook for a run that names its pack (packRead, packCheck, packStats): when the team's pin has the
|
|
115
|
+
pack step aside, the run ends saying so, and never goes on to a model that nobody asked for."""
|
|
116
|
+
@functools.wraps(hook)
|
|
117
|
+
def wrapped(ctx, driver, approve):
|
|
118
|
+
from .. import harness_api
|
|
119
|
+
from . import core
|
|
120
|
+
try:
|
|
121
|
+
return hook(ctx, driver, approve)
|
|
122
|
+
except core.PinnedAside as aside:
|
|
123
|
+
return harness_api.PreRun(result={
|
|
124
|
+
"event": "result", "status": "completion_not_confirmed", "engine": "smart", "outcome": "check",
|
|
125
|
+
"answer": None, "reason": str(aside), "actions": 0, "decisions": 0, "costUsd": 0.0, "model": None,
|
|
126
|
+
"elapsed_ms": 0, "independently_verified": False, "proof": []})
|
|
127
|
+
return wrapped
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@stepping_aside
|
|
131
|
+
def pack_read(ctx, driver, approve):
|
|
132
|
+
"""Pre-run hook: a run with ``packRead`` reads that reader's rows on the phone by code and ends with them as its
|
|
133
|
+
data and a table."""
|
|
134
|
+
spec = (ctx.extras or {}).get("packRead")
|
|
135
|
+
if not spec or driver is None:
|
|
136
|
+
return None
|
|
137
|
+
import time
|
|
138
|
+
|
|
139
|
+
from .. import harness_api
|
|
140
|
+
from .. import result_blocks as rb
|
|
141
|
+
from . import core
|
|
142
|
+
pack = next(p for p in all_packs() if p.key == spec["pack"])
|
|
143
|
+
io = _io(ctx, driver, pack) # a pack the team's pin has step aside ends the run here, before anything acts
|
|
144
|
+
# An acting event, journaled before the first tap: a crash mid-run is never read as a run that didn't act.
|
|
145
|
+
ctx.emit({"event": "skill_started", "op": f"{pack.prefix}_READ"})
|
|
146
|
+
started = time.monotonic()
|
|
147
|
+
|
|
148
|
+
def ended(status, outcome, answer, reason=None, data=None, blocks=()):
|
|
149
|
+
return harness_api.PreRun(result={
|
|
150
|
+
"event": "result", "status": status, "engine": "smart", "outcome": outcome, "answer": answer,
|
|
151
|
+
"reason": reason, "data": data, "actions": 1, "decisions": 0, "costUsd": 0.0, "model": None,
|
|
152
|
+
"elapsed_ms": round((time.monotonic() - started) * 1000), "blocks": rb.envelope(list(blocks)),
|
|
153
|
+
"independently_verified": outcome == "done", "proof": []})
|
|
154
|
+
try:
|
|
155
|
+
result = pack.read(io, spec["reader"], spec["n"], spec.get("arg"))
|
|
156
|
+
except core.Lost as lost:
|
|
157
|
+
said = str(lost).rstrip(".")
|
|
158
|
+
return ended("completion_not_confirmed", "check", None,
|
|
159
|
+
f"Mobster couldn't read {pack.name}: {said}. Check the phone shows {pack.name}, "
|
|
160
|
+
"then run it again.")
|
|
161
|
+
scalars = (str, int, float, bool, type(None))
|
|
162
|
+
rows = [{k: v for k, v in row.items() if isinstance(v, scalars)} for row in result.rows]
|
|
163
|
+
columns = [{"key": k, "label": label, "type": kind} for k, label, kind in result.columns]
|
|
164
|
+
data = {"rows": rows, "columns": columns, "title": result.title, "stopped": result.stopped or None}
|
|
165
|
+
table = rb.table(columns, rows, title=result.title, id=f"{pack.key}-{spec['reader']}") if rows else None
|
|
166
|
+
return ended("completed", "done", f"{result.title}: {len(rows)} {'row' if len(rows) == 1 else 'rows'}.",
|
|
167
|
+
data=data, blocks=[table] if table else ())
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _io(ctx, driver, pack):
|
|
171
|
+
from . import core
|
|
172
|
+
pinned = pins.aside(pack)
|
|
173
|
+
if pinned:
|
|
174
|
+
raise core.PinnedAside(pinned)
|
|
175
|
+
return core.PackIO(driver, pack, cancelled=ctx.cancelled, sleep=SLEEP,
|
|
176
|
+
allows_app=lambda bundle: ctx.runtime is None or getattr(ctx.runtime, "allows_app",
|
|
177
|
+
lambda *a: True)(ctx.run_id, bundle))
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def pack_run(ctx, driver, field, op):
|
|
181
|
+
"""What both pack pre-run hooks start from: ``(pack, io, started)`` for the run's ``field``, or None when the run
|
|
182
|
+
doesn't carry it or has no phone."""
|
|
183
|
+
key = (ctx.extras or {}).get(field)
|
|
184
|
+
if not key or driver is None:
|
|
185
|
+
return None
|
|
186
|
+
import time
|
|
187
|
+
pack = next(p for p in all_packs() if p.key == key)
|
|
188
|
+
io = _io(ctx, driver, pack) # a pack the team's pin has step aside ends the run here, before anything acts
|
|
189
|
+
# An acting event, journaled before the first tap: a crash mid-run is never read as a run that didn't act.
|
|
190
|
+
ctx.emit({"event": "skill_started", "op": f"{pack.prefix}_{op}"})
|
|
191
|
+
return pack, io, time.monotonic()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@stepping_aside
|
|
195
|
+
def pack_check(ctx, driver, approve):
|
|
196
|
+
"""Pre-run hook: a run with ``packCheck`` visits the pack's tab screens on the phone, records each in the health
|
|
197
|
+
log,
|
|
198
|
+
and ends with a checklist."""
|
|
199
|
+
run = pack_run(ctx, driver, "packCheck", "CHECK")
|
|
200
|
+
if run is None:
|
|
201
|
+
return None
|
|
202
|
+
import time
|
|
203
|
+
|
|
204
|
+
from . import core, health
|
|
205
|
+
pack, io, started = run
|
|
206
|
+
rows = health.check(pack, io)
|
|
207
|
+
version = core.app_version(ctx.runtime, pack.bundle)
|
|
208
|
+
for row in rows:
|
|
209
|
+
health.record(ctx.data_dir, {"pack": pack.key, "op": "CHECK", "step": row["route"], "ok": row["landed"],
|
|
210
|
+
"screen": row["screen"], "confidence": row["confidence"], "version": version,
|
|
211
|
+
**({"unsure_screen": pack.routes[row["route"]].lands[0]}
|
|
212
|
+
if not row["landed"] and not row["screen"] else {})})
|
|
213
|
+
ctx.emit({"event": "app_pack_check", "pack": pack.key, "routes": len(rows),
|
|
214
|
+
"landed": sum(1 for row in rows if row["landed"]), "version": version})
|
|
215
|
+
from .. import harness_api
|
|
216
|
+
return harness_api.PreRun(result=health.check_result(pack, rows, version, time.monotonic() - started))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@stepping_aside
|
|
220
|
+
def pack_stats(ctx, driver, approve):
|
|
221
|
+
"""Pre-run hook: a run with ``packStats`` reads the signed-in profile's numbers and its Insights overview and stores
|
|
222
|
+
them as today's row (accounts/stats.py), no model."""
|
|
223
|
+
run = pack_run(ctx, driver, "packStats", "STATS")
|
|
224
|
+
if run is None:
|
|
225
|
+
return None
|
|
226
|
+
import time
|
|
227
|
+
|
|
228
|
+
from .. import harness_api
|
|
229
|
+
from .. import result_blocks as rb
|
|
230
|
+
from ..accounts import stats
|
|
231
|
+
from . import core
|
|
232
|
+
pack, io, started = run
|
|
233
|
+
account = (ctx.extras or {}).get("account")
|
|
234
|
+
wanted = account.get("handle") if isinstance(account, dict) else None
|
|
235
|
+
|
|
236
|
+
def done(answer, rows=(), ok=True):
|
|
237
|
+
blocks = [rb.table([{"label": label, "key": k, "type": kind} for k, label, kind in pack_columns],
|
|
238
|
+
list(rows), title=f"{pack.name} numbers", id=f"{pack.key}-stats")] if rows else []
|
|
239
|
+
return harness_api.PreRun(result={
|
|
240
|
+
"event": "result", "status": "completed", "engine": "smart", "outcome": "done" if ok else "check",
|
|
241
|
+
"answer": answer, "actions": 1, "decisions": 0, "elapsed_ms": round((time.monotonic() - started) * 1000),
|
|
242
|
+
"costUsd": 0.0, "model": None, "blocks": rb.envelope(blocks)})
|
|
243
|
+
|
|
244
|
+
pack_columns = (("handle", "Profile", "text"), ("followers", "Followers", "number"),
|
|
245
|
+
("following", "Following", "number"), ("posts", "Posts", "number"), ("likes", "Likes", "number"),
|
|
246
|
+
("period", "Period", "text"), ("views", "Views", "number"), ("reach", "Reached", "number"),
|
|
247
|
+
("profile_visits", "Profile visits", "number"), ("follower_change", "Follower change", "number"))
|
|
248
|
+
try:
|
|
249
|
+
profile = pack.read(io, "profile", 1, None)
|
|
250
|
+
mine = profile.rows[0] if profile.rows else {}
|
|
251
|
+
handle = mine.get("handle")
|
|
252
|
+
if mine.get("state") != "yours" or not handle:
|
|
253
|
+
return done(f"Mobster didn't store {pack.name} numbers: the Profile tab doesn't show your own profile. "
|
|
254
|
+
"Open your profile in the app, then run this again.", ok=False)
|
|
255
|
+
if wanted and wanted.lstrip("@").casefold() != handle.lstrip("@").casefold():
|
|
256
|
+
return done(f"Mobster didn't store {pack.name} numbers: the app shows @{handle}, "
|
|
257
|
+
f"not @{wanted.lstrip('@')}. "
|
|
258
|
+
"Switch to that account, then run this again.", ok=False)
|
|
259
|
+
values = {k: mine.get(k) for k in ("followers", "following", "posts", "likes")}
|
|
260
|
+
try:
|
|
261
|
+
overview = pack.read(io, "insights", 1, None).rows[0]
|
|
262
|
+
values.update({k: overview.get(k) for k in ("period", "views", "reach", "profile_visits",
|
|
263
|
+
"follower_change")})
|
|
264
|
+
note = ""
|
|
265
|
+
except core.Lost as lost:
|
|
266
|
+
note = f" Insights weren't read: {str(lost).rstrip('.')}."
|
|
267
|
+
folder = ctx.data_dir
|
|
268
|
+
if ctx.runtime is not None:
|
|
269
|
+
from ..accounts.store import store_for
|
|
270
|
+
folder = store_for(ctx.runtime).data_dir
|
|
271
|
+
stats.record(folder, pack.key, handle, values)
|
|
272
|
+
except core.Lost as lost:
|
|
273
|
+
return done(f"Mobster couldn't read {pack.name}: {str(lost).rstrip('.')}. Check the phone shows "
|
|
274
|
+
f"{pack.name}, then run this again.", ok=False)
|
|
275
|
+
ctx.emit({"event": "app_pack_stats", "pack": pack.key})
|
|
276
|
+
return done(f"Stored @{handle}'s {pack.name} numbers for today.{note}", [{"handle": handle, **values}])
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Rows read from list screens, the same way on an accessibility tree and on a picture (image-only connections)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from . import core
|
|
6
|
+
|
|
7
|
+
# A time or a date as list rows show them: "9:55 PM", "Yesterday", "Monday", "10/1/26", "Oct 3", "2h", "3 days ago".
|
|
8
|
+
WHEN = re.compile(r"^(\d{1,2}:\d{2}\s?([AaPp]\.?[Mm]\.?)?|yesterday|today|now|just now|"
|
|
9
|
+
r"(mon|tues|wednes|thurs|fri|satur|sun)day|\d{1,2}/\d{1,2}(/\d{2,4})?|"
|
|
10
|
+
r"(jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\.? \d{1,2}(, \d{4})?|"
|
|
11
|
+
r"\d{1,2} (jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*|"
|
|
12
|
+
r"\d+\s?(s|m|min|h|hr|d|w|mo|y)|\d+ (seconds?|minutes?|hours?|days?|weeks?|months?|years?) ago)$",
|
|
13
|
+
re.I)
|
|
14
|
+
FLAGS = ("unread", "pinned", "muted", "time sensitive", "draft", "starred", "important", "verified", "new")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def labelled(snapshot, roles=("Cell",), region=(.1, .92), minimum=2):
|
|
18
|
+
"""The elements in ``region`` with ``roles`` and a label of at least ``minimum`` characters, each label once
|
|
19
|
+
(largest
|
|
20
|
+
element first when two share a label at the same place)."""
|
|
21
|
+
out, seen = [], set()
|
|
22
|
+
for element in sorted(core.find_all(snapshot, r".", roles=roles, region=region),
|
|
23
|
+
key=lambda e: (round(core.centre_y(e), 2), -e.rect[2] * e.rect[3])):
|
|
24
|
+
label = core.plain(element.label)
|
|
25
|
+
key = (label, round(core.centre_y(element), 2))
|
|
26
|
+
if len(label) < minimum or key in seen:
|
|
27
|
+
continue
|
|
28
|
+
seen.add(key)
|
|
29
|
+
out.append(element)
|
|
30
|
+
return out
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def parts(label):
|
|
34
|
+
"""(parts, flags): a row label's comma parts with iOS's flags ("Unread", "Pinned"...) taken out, lowercased."""
|
|
35
|
+
tokens = [t.strip() for t in core.plain(label).split(",")]
|
|
36
|
+
flags = {t.casefold() for t in tokens if t.casefold() in FLAGS}
|
|
37
|
+
return [t for t in tokens if t and t.casefold() not in FLAGS], flags
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def lines(snapshot, region=(0.0, 1.0)):
|
|
41
|
+
"""A picture's text lines in ``region``, top to bottom then left to right."""
|
|
42
|
+
return sorted((e for e in getattr(snapshot, "elements", ()) or ()
|
|
43
|
+
if e.label and region[0] <= core.centre_y(e) <= region[1]),
|
|
44
|
+
key=lambda e: (round(core.centre_y(e), 3), e.rect[0]))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def anchored(snapshot, anchor=WHEN, region=(.1, .92), right=.55, same_line=.012):
|
|
48
|
+
"""On a picture: rows found by ``anchor`` (a regex on a whole line) at the right of their first line."""
|
|
49
|
+
texts = lines(snapshot, region)
|
|
50
|
+
marks = [e for e in texts if e.rect[0] >= right and anchor.match(core.plain(e.label))]
|
|
51
|
+
rows = []
|
|
52
|
+
for index, mark in enumerate(marks):
|
|
53
|
+
top = core.centre_y(mark)
|
|
54
|
+
bottom = core.centre_y(marks[index + 1]) - same_line if index + 1 < len(marks) else region[1]
|
|
55
|
+
head = [e for e in texts if e is not mark and abs(core.centre_y(e) - top) <= same_line
|
|
56
|
+
and e.rect[0] < mark.rect[0]]
|
|
57
|
+
body = [e for e in texts if e is not mark and e not in head and top + same_line < core.centre_y(e) < bottom]
|
|
58
|
+
rows.append({"anchor": core.plain(mark.label), "head": [core.plain(e.label) for e in head],
|
|
59
|
+
"body": [core.plain(e.label) for e in body], "top": round(top, 3)})
|
|
60
|
+
return rows
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def pairs(snapshot, region=(.1, .95), roles=("Cell", "Button", "StaticText", "Other")):
|
|
64
|
+
"""{label: value} of a settings-style list: "iOS Version, 26.0.1" on a tree; on a picture, a line on the left and a
|
|
65
|
+
line on the right at the same height."""
|
|
66
|
+
out = {}
|
|
67
|
+
if core.image_only(snapshot):
|
|
68
|
+
texts = lines(snapshot, region)
|
|
69
|
+
for left in (e for e in texts if e.rect[0] < .45):
|
|
70
|
+
right = [e for e in texts if e.rect[0] >= .45 and abs(core.centre_y(e) - core.centre_y(left)) < .012]
|
|
71
|
+
value = re.sub(r"\s*[>›]$", "", core.plain(right[-1].label)) if right else ""
|
|
72
|
+
if value and not re.fullmatch(r"[>›<‹]", value):
|
|
73
|
+
out.setdefault(re.sub(r"\s*[>›]$", "", core.plain(left.label)), value)
|
|
74
|
+
return out
|
|
75
|
+
for element in labelled(snapshot, roles, region):
|
|
76
|
+
label, value = core.plain(element.label), core.plain(element.value)
|
|
77
|
+
if value and value not in ("0", "1") and label:
|
|
78
|
+
out.setdefault(label, value)
|
|
79
|
+
elif ", " in label:
|
|
80
|
+
key, _, rest = label.partition(", ")
|
|
81
|
+
if key and rest:
|
|
82
|
+
out.setdefault(key, re.sub(r"(\d), (\d{3})\b", r"\1,\2", rest))
|
|
83
|
+
return out
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def tabbar(names, region=(.86, 1.0)):
|
|
87
|
+
"""A tab bar's signal: any of ``names`` (whole labels) at the bottom, a button on a tree, a line on a picture."""
|
|
88
|
+
return core.sig(rf"^({'|'.join(names)})$", region=region)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def top_left_back(snapshot, top=(.04, .14), never=core.NEVER):
|
|
92
|
+
"""A navigation bar's Back button: a button at the top left whose label names the screen before ("Settings",
|
|
93
|
+
"General", "Search", "Oct 15"), never one whose words name an act."""
|
|
94
|
+
if core.image_only(snapshot):
|
|
95
|
+
return None
|
|
96
|
+
for element in sorted(core.find_all(snapshot, r"\S", role="Button", region=top), key=lambda e: e.rect[0]):
|
|
97
|
+
if element.rect[0] < .2 and element.rect[2] < .4 and not never.search(element.label or "") \
|
|
98
|
+
and not core.ACT_LABEL.match(core.plain(element.label)):
|
|
99
|
+
return element
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def back_unless_act(pack, snapshot):
|
|
104
|
+
return top_left_back(snapshot, never=pack.never)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def tab_tap(region):
|
|
108
|
+
return lambda label, what: core.Tap(label, what, role="Button", region=region)
|