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/login.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""`mobster login`, `mobster login status` and `mobster logout`: your model key, saved for Mobster on this Mac."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
PROVIDERS = {
|
|
8
|
+
"anthropic": {"name": "Claude", "company": "Anthropic", "variable": "ANTHROPIC_API_KEY", "prefix": "sk-ant-",
|
|
9
|
+
"console": "the Claude Console", "url": "https://platform.claude.com/settings/keys"},
|
|
10
|
+
"openai": {"name": "OpenAI", "company": "OpenAI", "variable": "OPENAI_API_KEY", "prefix": "sk-",
|
|
11
|
+
"console": "the OpenAI Platform", "url": "https://platform.openai.com/api-keys"},
|
|
12
|
+
}
|
|
13
|
+
ORDER = ("anthropic", "openai")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def mask(key):
|
|
17
|
+
"""sk-ant-…a1F2: the key's well-known prefix and its last four characters, never more."""
|
|
18
|
+
key = (key or "").strip()
|
|
19
|
+
if len(key) < 12:
|
|
20
|
+
return "…" + key[-2:] if key else ""
|
|
21
|
+
for prefix in ("sk-ant-", "sk-proj-", "sk-svcacct-", "sk-"):
|
|
22
|
+
if key.startswith(prefix):
|
|
23
|
+
return f"{prefix}…{key[-4:]}"
|
|
24
|
+
return f"…{key[-4:]}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def source_words(variable):
|
|
28
|
+
"""Where a saved key came from, in words: Mobster for Mac's settings, an env file, or your shell."""
|
|
29
|
+
from .config import SOURCES
|
|
30
|
+
source = SOURCES.get(variable)
|
|
31
|
+
if source == "mac-app":
|
|
32
|
+
return "from Mobster for Mac"
|
|
33
|
+
if source == "env-file":
|
|
34
|
+
return "from your env file"
|
|
35
|
+
return "from your shell"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# The CLI agents sign in with their own CLI: Mobster only runs them (ai_plans/agents.py).
|
|
39
|
+
CLI_SIGN_IN = {"codex": ("Codex", "codex login"),
|
|
40
|
+
"claude_code": ("Claude Code", "claude, then /login"), "claude": ("Claude Code", "claude, then /login"),
|
|
41
|
+
"gemini": ("Gemini CLI", "gemini, then choose Sign in with Google")}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run(args):
|
|
45
|
+
action = getattr(args, "action", None)
|
|
46
|
+
if args.command == "logout":
|
|
47
|
+
account = getattr(args, "account", None)
|
|
48
|
+
if account:
|
|
49
|
+
return logout_provider(account)
|
|
50
|
+
return logout(args)
|
|
51
|
+
if action == "status":
|
|
52
|
+
return status(args)
|
|
53
|
+
if action in CLI_SIGN_IN:
|
|
54
|
+
name, command = CLI_SIGN_IN[action]
|
|
55
|
+
print(f"{name} signs in itself: in Terminal, run {command}. Mobster runs your {name} from then on; it never "
|
|
56
|
+
"sees your sign-in.")
|
|
57
|
+
return 0
|
|
58
|
+
if action:
|
|
59
|
+
return login_provider(args, action)
|
|
60
|
+
return login(args)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def login_provider(args, provider_id):
|
|
64
|
+
"""`mobster login <plan or provider>`."""
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
from .ai_plans import device_login, providers, routes
|
|
68
|
+
paint = _paint()
|
|
69
|
+
provider = providers.get(provider_id)
|
|
70
|
+
if provider is None:
|
|
71
|
+
known = ", ".join(sorted(["codex", "claude", "gemini", *providers.ALL]))
|
|
72
|
+
print(f"mobster login: no plan or provider called {provider_id}. Try one of: {known}", file=sys.stderr)
|
|
73
|
+
return 2
|
|
74
|
+
body = {}
|
|
75
|
+
if getattr(args, "model", None):
|
|
76
|
+
body["model"] = args.model
|
|
77
|
+
if getattr(args, "base_url", None):
|
|
78
|
+
body["baseUrl"] = args.base_url
|
|
79
|
+
try:
|
|
80
|
+
if provider.mechanism == "device":
|
|
81
|
+
attempt = device_login.start(provider.id)
|
|
82
|
+
print(paint(f"Sign in to {provider.name}", "bold") + ": open " + paint(attempt["url"], "accent")
|
|
83
|
+
+ " and enter " + paint(attempt["user_code"], "bold") + ".")
|
|
84
|
+
print(" " + paint("Waiting for you to finish. ctrl+c cancels.", "tertiary"), flush=True)
|
|
85
|
+
device_login.wait(provider.id, attempt)
|
|
86
|
+
if body:
|
|
87
|
+
routes.handle(_keys(), provider.id, "key", body)
|
|
88
|
+
else:
|
|
89
|
+
if provider.mechanism in ("key", "custom"):
|
|
90
|
+
if getattr(args, "with_key", False):
|
|
91
|
+
key = sys.stdin.read().strip()
|
|
92
|
+
else:
|
|
93
|
+
import getpass
|
|
94
|
+
where = f" (from {provider.console})" if provider.console else ""
|
|
95
|
+
key = getpass.getpass(f"Paste your {provider.name} key{where}: ").strip()
|
|
96
|
+
body["key"] = key
|
|
97
|
+
routes.handle(_keys(), provider.id, "key", body)
|
|
98
|
+
routes.handle(_keys(), provider.id, "use", {})
|
|
99
|
+
except KeyboardInterrupt:
|
|
100
|
+
print()
|
|
101
|
+
print(paint("Cancelled. Nothing was saved.", "tertiary"))
|
|
102
|
+
return 130
|
|
103
|
+
except (routes.RouteError, device_login.DeviceError) as error:
|
|
104
|
+
print(paint("✗ ", "coral", "bold") + str(error))
|
|
105
|
+
return 1
|
|
106
|
+
model = providers.chosen_model(provider)
|
|
107
|
+
vision = "" if provider.vision(model) else " It can't see images, so it reads the screen as text."
|
|
108
|
+
bill = f"on {provider.plan_label}" if provider.group == "plan" else f"on your {provider.name} key"
|
|
109
|
+
print(paint("✓ ", "green", "bold") + f"Mobster's agent runs on {model}, {bill}.{vision}")
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def logout_provider(provider_id):
|
|
114
|
+
import sys
|
|
115
|
+
|
|
116
|
+
from .ai_plans import providers, routes
|
|
117
|
+
if providers.get(provider_id) is None:
|
|
118
|
+
print(f"mobster logout: no plan or provider called {provider_id}.", file=sys.stderr)
|
|
119
|
+
return 2
|
|
120
|
+
routes.handle(_keys(), provider_id, "forget", {})
|
|
121
|
+
print(f"Mobster forgot {providers.get(provider_id).name} on this Mac.")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _paint(stream=None):
|
|
126
|
+
from .style import palette
|
|
127
|
+
return palette(stream or sys.stdout)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _keys():
|
|
131
|
+
from .config import app_env_file
|
|
132
|
+
from .keys import Keys
|
|
133
|
+
return Keys(app_env_file())
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _saved():
|
|
137
|
+
"""{variable: value} in the Mac app's settings file (not the shell's)."""
|
|
138
|
+
from .config import app_env_file
|
|
139
|
+
path = app_env_file()
|
|
140
|
+
values = {}
|
|
141
|
+
wanted = {p["variable"] for p in PROVIDERS.values()} | {"MOBSTER_SMART_PROVIDER"}
|
|
142
|
+
try:
|
|
143
|
+
for line in path.read_text(encoding="utf-8").splitlines():
|
|
144
|
+
name, sep, value = line.partition("=")
|
|
145
|
+
if sep and name.strip() in wanted:
|
|
146
|
+
values[name.strip()] = value.strip().strip("'\"")
|
|
147
|
+
except (OSError, UnicodeDecodeError):
|
|
148
|
+
pass
|
|
149
|
+
# The keys themselves are in the Keychain once the Mac app or this command saved them there (env_secrets.py).
|
|
150
|
+
from . import env_secrets
|
|
151
|
+
values.update({name: value for name, value in env_secrets.load(path).items() if name in wanted})
|
|
152
|
+
return values
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def status(args):
|
|
156
|
+
"""Masked rows: which keys Mobster can use, and which one its agent uses."""
|
|
157
|
+
from .engines import model_provider, smart_model
|
|
158
|
+
paint = _paint()
|
|
159
|
+
rows = []
|
|
160
|
+
for provider in ORDER:
|
|
161
|
+
info = PROVIDERS[provider]
|
|
162
|
+
key = os.environ.get(info["variable"], "").strip()
|
|
163
|
+
rows.append({"provider": provider, "name": info["name"], "set": bool(key), "key": mask(key) if key else None,
|
|
164
|
+
"source": source_words(info["variable"]) if key else None})
|
|
165
|
+
from .ai_plans import providers
|
|
166
|
+
ready = [row for row in providers.public() if row["configured"]]
|
|
167
|
+
smart = model_provider(smart_model()) if any(r["set"] for r in rows) or ready else None
|
|
168
|
+
if getattr(args, "json", False):
|
|
169
|
+
print(json.dumps({"keys": rows, "agent": smart, "model": smart_model() if smart else None,
|
|
170
|
+
"providers": [{key: row[key] for key in ("id", "name", "model", "vision")}
|
|
171
|
+
for row in ready]}))
|
|
172
|
+
return 0
|
|
173
|
+
print(paint("Keys", "bold"))
|
|
174
|
+
for row in rows:
|
|
175
|
+
name = paint(row["name"].ljust(8), "accent")
|
|
176
|
+
if row["set"]:
|
|
177
|
+
note = " Mobster's agent uses it" if row["provider"] == smart else ""
|
|
178
|
+
print(f" {paint('✓', 'green')} {name}{row['key'].ljust(16)}{paint(row['source'], 'tertiary')}"
|
|
179
|
+
+ paint(note, "secondary"))
|
|
180
|
+
else:
|
|
181
|
+
print(f" {paint('–', 'tertiary')} {name}{paint('not set', 'tertiary')}")
|
|
182
|
+
if ready:
|
|
183
|
+
print(paint("Plans and providers", "bold"))
|
|
184
|
+
for row in ready:
|
|
185
|
+
uses = paint(" Mobster's agent uses it", "secondary") if smart == row["id"] else ""
|
|
186
|
+
vision = "" if row["vision"] else paint(" reads the screen as text", "tertiary")
|
|
187
|
+
print(f" {paint('✓', 'green')} {row['name']} {row['model']}{vision}{uses}")
|
|
188
|
+
if not any(row["set"] for row in rows) and not ready:
|
|
189
|
+
print("\nNo key yet. Run " + paint("mobster login", "accent") + ", or pick Codex or Claude Code in the Mac "
|
|
190
|
+
"app's composer to run on your plan.")
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _choose(paint):
|
|
195
|
+
"""Ask which provider, Claude first. Returns anthropic or openai."""
|
|
196
|
+
print(paint("Which AI do you use?", "bold"))
|
|
197
|
+
print(" " + paint("1", "accent") + " Claude, by Anthropic " + paint("recommended", "green"))
|
|
198
|
+
print(" " + paint("2", "accent") + " OpenAI")
|
|
199
|
+
while True:
|
|
200
|
+
try:
|
|
201
|
+
answer = input("Choose 1 or 2 [1]: ").strip()
|
|
202
|
+
except EOFError:
|
|
203
|
+
return None
|
|
204
|
+
if answer in ("", "1", "claude", "anthropic"):
|
|
205
|
+
return "anthropic"
|
|
206
|
+
if answer in ("2", "openai"):
|
|
207
|
+
return "openai"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def save_key(provider, key, *, check=True):
|
|
211
|
+
"""Check a pasted key and save it, as `mobster login` and the terminal UI both do."""
|
|
212
|
+
from .env_secrets import REFUSED, KeychainError
|
|
213
|
+
from .keys import KEY_PATTERN
|
|
214
|
+
info = PROVIDERS[provider]
|
|
215
|
+
if not key or not KEY_PATTERN.fullmatch(key):
|
|
216
|
+
return {"ok": False, "problem": "pattern", "message":
|
|
217
|
+
f"That doesn't look like {'an' if provider == 'openai' else 'a'} {info['name']} key. "
|
|
218
|
+
"Copy it again, without spaces."}
|
|
219
|
+
variable = info["variable"]
|
|
220
|
+
saved = {name: os.environ.get(name) for name in (variable, "MOBSTER_SMART_PROVIDER")}
|
|
221
|
+
os.environ[variable], os.environ["MOBSTER_SMART_PROVIDER"] = key, provider
|
|
222
|
+
|
|
223
|
+
def fail(result):
|
|
224
|
+
for name, value in saved.items():
|
|
225
|
+
if value is None:
|
|
226
|
+
os.environ.pop(name, None)
|
|
227
|
+
else:
|
|
228
|
+
os.environ[name] = value
|
|
229
|
+
return {"ok": False, **result}
|
|
230
|
+
|
|
231
|
+
keys = _keys()
|
|
232
|
+
if check:
|
|
233
|
+
try:
|
|
234
|
+
result = keys.test(provider)
|
|
235
|
+
except Exception as error: # noqa: BLE001 -- the test's own words, or a plain one
|
|
236
|
+
result = {"ok": False, "message": f"Mobster couldn't check the key ({type(error).__name__})."}
|
|
237
|
+
if not result.get("ok"):
|
|
238
|
+
return fail({"problem": result.get("problem"), "link": result.get("link"),
|
|
239
|
+
"message": (result.get("message") or "The key didn't work.") + " Nothing was saved."})
|
|
240
|
+
try:
|
|
241
|
+
keys.update({provider: {"key": key}, "smartProvider": provider})
|
|
242
|
+
except KeychainError:
|
|
243
|
+
return fail({"problem": "keychain", "message": REFUSED})
|
|
244
|
+
from .config import SOURCES
|
|
245
|
+
SOURCES[variable] = "mac-app"
|
|
246
|
+
return {"ok": True, "problem": None, "message": f"{info['name']} is connected."}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def login(args):
|
|
250
|
+
paint = _paint()
|
|
251
|
+
provider = args.provider
|
|
252
|
+
if args.with_key:
|
|
253
|
+
if sys.stdin.isatty():
|
|
254
|
+
print("mobster login: --with-key reads the key from a pipe or a file, not the keyboard: "
|
|
255
|
+
"mobster login --with-key < key.txt", file=sys.stderr)
|
|
256
|
+
return 2
|
|
257
|
+
key = sys.stdin.read().strip()
|
|
258
|
+
provider = provider or _guess(key) or "anthropic"
|
|
259
|
+
else:
|
|
260
|
+
if not sys.stdin.isatty():
|
|
261
|
+
print("mobster login: this asks for your key, so it needs a terminal. In a script: "
|
|
262
|
+
"mobster login --provider anthropic --with-key < key.txt", file=sys.stderr)
|
|
263
|
+
return 2
|
|
264
|
+
print("Mobster's agent runs on your own Claude or OpenAI key.")
|
|
265
|
+
print("You pay them for what it uses, and the key stays on this Mac.")
|
|
266
|
+
print()
|
|
267
|
+
if provider is None:
|
|
268
|
+
try:
|
|
269
|
+
provider = _choose(paint)
|
|
270
|
+
except KeyboardInterrupt:
|
|
271
|
+
print()
|
|
272
|
+
print(paint("Nothing was saved.", "tertiary"))
|
|
273
|
+
return 130
|
|
274
|
+
if provider is None:
|
|
275
|
+
return 2
|
|
276
|
+
print()
|
|
277
|
+
info = PROVIDERS[provider]
|
|
278
|
+
print(paint(f"Get a key from {info['console']}: ", "tertiary") + paint(info["url"], "accent"))
|
|
279
|
+
import getpass
|
|
280
|
+
try:
|
|
281
|
+
key = getpass.getpass(f"Paste your {info['name']} key" + (f" (it starts with {info['prefix']}; "
|
|
282
|
+
if info["prefix"] else " (") + "it isn't shown): ").strip()
|
|
283
|
+
except (EOFError, KeyboardInterrupt):
|
|
284
|
+
print()
|
|
285
|
+
print(paint("Nothing was saved.", "tertiary"))
|
|
286
|
+
return 130
|
|
287
|
+
info = PROVIDERS[provider]
|
|
288
|
+
if not args.skip_check and not args.json:
|
|
289
|
+
print(paint("Checking your key…", "tertiary"), flush=True)
|
|
290
|
+
result = save_key(provider, key, check=not args.skip_check)
|
|
291
|
+
if result["problem"] == "pattern":
|
|
292
|
+
print(f"mobster login: {result['message']}", file=sys.stderr)
|
|
293
|
+
return 2
|
|
294
|
+
if not result["ok"]:
|
|
295
|
+
if args.json:
|
|
296
|
+
print(json.dumps({"ok": False, "provider": provider, "message": result["message"],
|
|
297
|
+
"problem": result["problem"]}))
|
|
298
|
+
else:
|
|
299
|
+
print(paint("✗ ", "coral", "bold") + result["message"])
|
|
300
|
+
link = result.get("link")
|
|
301
|
+
if isinstance(link, dict) and link.get("url"):
|
|
302
|
+
print(" " + paint(link["url"], "accent"))
|
|
303
|
+
return 1
|
|
304
|
+
if args.json:
|
|
305
|
+
print(json.dumps({"ok": True, "provider": provider, "key": mask(key), "checked": not args.skip_check}))
|
|
306
|
+
return 0
|
|
307
|
+
from .engines import smart_model
|
|
308
|
+
print(paint("✓ ", "green", "bold") + paint(f"{info['name']} is connected", "bold") + f", key {mask(key)}, "
|
|
309
|
+
f"{smart_model()}")
|
|
310
|
+
print(" " + paint("Saved on this Mac in Mobster's settings file (only you can read it). Mobster for Mac and "
|
|
311
|
+
"every `mobster` command use it.", "tertiary"))
|
|
312
|
+
print(" Next: " + paint("mobster doctor", "accent") + " checks your iPhone, or " + paint("mobster", "accent")
|
|
313
|
+
+ " gives it a task.")
|
|
314
|
+
return 0
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _guess(key):
|
|
318
|
+
if key.startswith("sk-ant-"):
|
|
319
|
+
return "anthropic"
|
|
320
|
+
if key.startswith("sk-"):
|
|
321
|
+
return "openai"
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def logout(args):
|
|
326
|
+
"""Remove one saved key from the settings file Mobster for Mac and `mobster login` share."""
|
|
327
|
+
from .engines import model_provider, smart_model
|
|
328
|
+
paint = _paint()
|
|
329
|
+
saved = _saved()
|
|
330
|
+
provider = args.provider
|
|
331
|
+
if provider is None:
|
|
332
|
+
known = [name for name in ORDER if PROVIDERS[name]["variable"] in saved]
|
|
333
|
+
chosen = model_provider(smart_model())
|
|
334
|
+
# A plan or another provider can be selected for Smart: it names no key here, so only a sole key is removed.
|
|
335
|
+
provider = chosen if chosen in known else known[0] if len(known) == 1 else None
|
|
336
|
+
if provider is None and known:
|
|
337
|
+
print("mobster logout: more than one key is saved. Choose one with --provider "
|
|
338
|
+
+ ", ".join(known) + ".", file=sys.stderr)
|
|
339
|
+
return 2
|
|
340
|
+
if provider is None:
|
|
341
|
+
print("No key is saved for Mobster on this Mac.")
|
|
342
|
+
return 0
|
|
343
|
+
if PROVIDERS[provider]["variable"] not in saved:
|
|
344
|
+
print(f"No {PROVIDERS[provider]['name']} key is saved for Mobster on this Mac.")
|
|
345
|
+
if os.environ.get(PROVIDERS[provider]["variable"]):
|
|
346
|
+
print(" " + paint(f"One is set in your shell or an env file ({PROVIDERS[provider]['variable']}); "
|
|
347
|
+
"remove it there.", "tertiary"))
|
|
348
|
+
return 0
|
|
349
|
+
changes = {provider: None}
|
|
350
|
+
if saved.get("MOBSTER_SMART_PROVIDER") == provider:
|
|
351
|
+
changes["smartProvider"] = None
|
|
352
|
+
_keys().update(changes)
|
|
353
|
+
print(paint("✓ ", "green", "bold") + f"Removed the {PROVIDERS[provider]['name']} key from this Mac.")
|
|
354
|
+
return 0
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Long-form tasks (track ``longform``, DESIGN.md)."""
|
|
2
|
+
|
|
3
|
+
TRACK = "longform"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def enabled():
|
|
7
|
+
"""Whether long tasks run: the track is loaded and on (its run field is registered), and ``MOBSTER_LONGFORM``
|
|
8
|
+
isn't "off" (the switch the benchmark's "before" runs can use; every task then runs as it did before long
|
|
9
|
+
tasks)."""
|
|
10
|
+
import os
|
|
11
|
+
if os.environ.get("MOBSTER_LONGFORM", "").strip().casefold() in ("off", "0", "false", "no"):
|
|
12
|
+
return False
|
|
13
|
+
from .. import harness_api
|
|
14
|
+
return not harness_api.disabled(TRACK) and TRACK in harness_api.run_fields()
|
|
15
|
+
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Reading one feed item (Reels, TikTok, Shorts): who made it, what it says, whether it is an ad (DESIGN.md § 7.3)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
AD_LABEL = re.compile(r"^\s*(sponsored|ad|promoted|advertisement)\s*$", re.I)
|
|
7
|
+
PAID = re.compile(r"\bpaid partnership\b", re.I)
|
|
8
|
+
CTA = re.compile(r"^\s*(shop now|learn more|install now|install|sign up|book now|order now|get offer|download|"
|
|
9
|
+
r"visit (site|advertiser|website)|apply now|watch more|get quote|contact us|"
|
|
10
|
+
r"subscribe now)\s*[›>]?\s*$",
|
|
11
|
+
re.I)
|
|
12
|
+
HANDLE = re.compile(r"^@?([A-Za-z0-9._]{2,30})$")
|
|
13
|
+
# Signs of a full-screen video that name no app (10 Oct, one general engine): a screen-covering element whose label or
|
|
14
|
+
# identifier says video ("sundial-viewer-video-cell": Instagram's viewer opened from search, which its item label
|
|
15
|
+
# missed), and a small "Post by name" control naming the creator.
|
|
16
|
+
VIDEO_SURFACE = re.compile(r"(?:^|[\s_-])video(?:[\s_-]|$)", re.I)
|
|
17
|
+
BY_HANDLE = re.compile(r"^(?:reel|video|post|short|tiktok) by @?([\w.]{2,30})$", re.I)
|
|
18
|
+
# More bylines no app names (real iPhone, 13 Oct: a collab Reel showed no "@handle", only these): a creator's
|
|
19
|
+
# picture ("42ledger. Profile picture") and a collab line ("42ledger and fahaadk_"); the first creator is the item's.
|
|
20
|
+
PICTURE_OF = re.compile(r"^@?([\w.]{2,30}?)\.?,? profile picture$", re.I)
|
|
21
|
+
COLLAB = re.compile(r"^@?([\w.]{2,30}) and @?([\w.]{2,30})$", re.I)
|
|
22
|
+
# A playing video's progress bar ("Video progress: 11 percent"): the screen is a video item, whatever its labels.
|
|
23
|
+
PROGRESS = re.compile(r"^video progress\b", re.I)
|
|
24
|
+
# "Followed by simon": who of the person's follows follows the creator; that person is not the creator.
|
|
25
|
+
FOLLOWED_BY = re.compile(r"^followed by @?([\w.]{2,30})\b", re.I)
|
|
26
|
+
# An accessibility identifier read as a label ("simon, clips_viewer_clips_tab"): a control's name, never a caption.
|
|
27
|
+
IDENTIFIER = re.compile(r"\b[a-z]+(?:_[a-z]+){2,}\b|^[a-z0-9]+(?:-[a-z0-9]+){2,}$")
|
|
28
|
+
# The control that expands a collapsed caption: never the caption itself.
|
|
29
|
+
MORE_CAPTION = re.compile(r"^more,? (button,? )?tap to see the (whole|full) caption$", re.I)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class Hints:
|
|
34
|
+
item: re.Pattern # the full-screen element's label ("Reel by @hoopslab")
|
|
35
|
+
item_area: float # or any element covering this share of the screen
|
|
36
|
+
creator: re.Pattern # a label that names the creator; group 1 is the handle
|
|
37
|
+
audio: re.Pattern # the audio line
|
|
38
|
+
chrome: re.Pattern # controls and labels that are never the caption
|
|
39
|
+
min_caption: int = 8
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_CHROME = (r"^(like|liked|unlike|comment|comments|share|send|save|saved|save post|repost|remix|more|more options|"
|
|
43
|
+
r"more actions|"
|
|
44
|
+
r"options|follow|"
|
|
45
|
+
r"following|back|close|reels|for you|following|friends|home|search|explore|inbox|profile|shop|create|"
|
|
46
|
+
r"camera|mute|unmute|sound|pause|play|subscribe|subscribed|dislike|shorts|\d[\d.,]*[kmb]?|"
|
|
47
|
+
r"original audio|see translation|view insights|boost reel|"
|
|
48
|
+
r"\d[\d.,]*[kmb]? (likes?|comments?|shares?|views?|plays?|reposts?|saves?))$")
|
|
49
|
+
|
|
50
|
+
# One reader for every app (one general engine, 10 Oct: the per-app hints for Instagram, TikTok and YouTube are gone).
|
|
51
|
+
# What a feed item shows is the same everywhere: a full-screen element named "Reel/Video/Post/Short/TikTok by
|
|
52
|
+
# @name" or a video surface, a creator handle, a caption, an audio or sound line, and the controls around it.
|
|
53
|
+
GENERIC = Hints(item=re.compile(r"^(reel|video|short|post|tiktok) by @?[\w.]+", re.I), item_area=.8,
|
|
54
|
+
creator=re.compile(r"^(?:reel by |video by |post by )?@([\w.-]{2,30})$", re.I),
|
|
55
|
+
audio=re.compile(r"(♫|♪|original (audio|sound)|\baudio\b|\bsound\b)", re.I),
|
|
56
|
+
chrome=re.compile(_CHROME, re.I))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class ItemRead:
|
|
61
|
+
"""One screen of a feed, read: None where the tree doesn't say."""
|
|
62
|
+
full_screen: bool = False
|
|
63
|
+
creator: str | None = None
|
|
64
|
+
caption: str | None = None
|
|
65
|
+
audio: str | None = None
|
|
66
|
+
ad: str | None = None # why it is an ad, or None
|
|
67
|
+
sponsored: bool = False # a paid partnership: kept, flagged
|
|
68
|
+
overlay: str | None = None # an Alert or Sheet over the feed
|
|
69
|
+
item_label: bool = False # the full-screen element is labelled as an item ("Reel by @hoopslab"), or a
|
|
70
|
+
# video's progress bar shows
|
|
71
|
+
lines: list = field(default_factory=list) # every distinct text, top to bottom (for the extractor)
|
|
72
|
+
|
|
73
|
+
def signals(self):
|
|
74
|
+
"""How many of a feed item's signs this screen shows: a creator, a caption or audio line, an item label."""
|
|
75
|
+
return int(bool(self.creator)) + int(bool(self.caption or self.audio)) + int(self.item_label)
|
|
76
|
+
|
|
77
|
+
def ax_text(self):
|
|
78
|
+
parts = []
|
|
79
|
+
if self.creator:
|
|
80
|
+
parts.append(f"Creator: @{self.creator}")
|
|
81
|
+
if self.caption:
|
|
82
|
+
parts.append(f"Caption: {self.caption}")
|
|
83
|
+
if self.audio:
|
|
84
|
+
parts.append(f"Audio: {self.audio}")
|
|
85
|
+
if self.sponsored:
|
|
86
|
+
parts.append("Labelled: Paid partnership")
|
|
87
|
+
rest = [line for line in self.lines if line not in (self.caption, self.audio)
|
|
88
|
+
and not (self.creator and line.lstrip("@") == self.creator)]
|
|
89
|
+
if rest:
|
|
90
|
+
parts.append("Other text: " + " | ".join(rest[:12]))
|
|
91
|
+
return "\n".join(parts)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _within(rect, outer):
|
|
95
|
+
x, y, w, h = rect
|
|
96
|
+
ox, oy, ow, oh = outer
|
|
97
|
+
return ox - .01 <= x + w / 2 <= ox + ow + .01 and oy - .01 <= y + h / 2 <= oy + oh + .01
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _text(element):
|
|
101
|
+
label = " ".join((getattr(element, "label", "") or "").split())
|
|
102
|
+
value = " ".join((getattr(element, "value", "") or "").split())
|
|
103
|
+
return label if not value or value == label else f"{label} {value}".strip()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def read(snapshot, hints):
|
|
107
|
+
"""``snapshot`` (one screen of the feed) as an ItemRead, by ``hints``."""
|
|
108
|
+
out = ItemRead()
|
|
109
|
+
bylines, pictures, followed = [], [], set()
|
|
110
|
+
surface = None
|
|
111
|
+
elements = sorted(getattr(snapshot, "elements", ()) or (), key=lambda e: (e.rect[1], e.rect[0]))
|
|
112
|
+
captions = []
|
|
113
|
+
for element in elements:
|
|
114
|
+
role = (element.role or "").removeprefix("XCUIElementType")
|
|
115
|
+
text = _text(element)
|
|
116
|
+
if role in ("Alert", "Sheet"):
|
|
117
|
+
out.overlay = out.overlay or (text or role)
|
|
118
|
+
continue
|
|
119
|
+
x, y, w, h = element.rect
|
|
120
|
+
# A full-screen item is one the tree names as an item, or a video. An app's root "Other" (or a big cell or
|
|
121
|
+
# picture) covers the screen in almost every app: a home feed, DMs, Stories and card stacks aren't feeds.
|
|
122
|
+
if w * h >= hints.item_area:
|
|
123
|
+
if hints.item.match(text) or role == "Video" or VIDEO_SURFACE.search(text):
|
|
124
|
+
out.full_screen = True
|
|
125
|
+
# Named as an item ("Reel by @name"), or as a video surface: a screen-covering video is an item's sign.
|
|
126
|
+
out.item_label = out.item_label or bool(hints.item.match(text)) or role == "Video" \
|
|
127
|
+
or bool(VIDEO_SURFACE.search(text))
|
|
128
|
+
surface = surface or element.rect
|
|
129
|
+
match = re.search(r"@([\w.-]{2,30})", text)
|
|
130
|
+
if match and not out.creator:
|
|
131
|
+
out.creator = match.group(1)
|
|
132
|
+
continue # a container as big as the screen: its label is never the item's caption
|
|
133
|
+
if PROGRESS.match(text):
|
|
134
|
+
out.item_label = True # a playing video's own progress bar
|
|
135
|
+
continue
|
|
136
|
+
if MORE_CAPTION.match(text) or IDENTIFIER.search(text):
|
|
137
|
+
continue
|
|
138
|
+
social = FOLLOWED_BY.match(text)
|
|
139
|
+
if social:
|
|
140
|
+
followed.add(social.group(1).casefold())
|
|
141
|
+
continue
|
|
142
|
+
picture = PICTURE_OF.match(text)
|
|
143
|
+
if picture and role in ("Button", "Image", "Link", "Other"):
|
|
144
|
+
pictures.append(picture.group(1))
|
|
145
|
+
continue
|
|
146
|
+
collab = COLLAB.match(text)
|
|
147
|
+
if collab and not out.creator and role in ("Button", "StaticText", "Link", "Other") and len(text) <= 64:
|
|
148
|
+
out.creator = collab.group(1)
|
|
149
|
+
continue
|
|
150
|
+
if not text or hints.item.match(text): # a thumbnail of another item (a grid), not this item's text
|
|
151
|
+
if text and BY_HANDLE.match(text):
|
|
152
|
+
bylines.append(BY_HANDLE.match(text).group(1))
|
|
153
|
+
continue
|
|
154
|
+
if AD_LABEL.match(text):
|
|
155
|
+
out.ad = out.ad or text.strip()
|
|
156
|
+
continue
|
|
157
|
+
if PAID.search(text):
|
|
158
|
+
out.sponsored = True
|
|
159
|
+
continue
|
|
160
|
+
if CTA.match(text) and role in ("Button", "Link", "Other", "StaticText"):
|
|
161
|
+
out.ad = out.ad or text.strip()
|
|
162
|
+
continue
|
|
163
|
+
creator = hints.creator.match(text)
|
|
164
|
+
if creator and role in ("Button", "StaticText", "Link") and not out.creator:
|
|
165
|
+
out.creator = creator.group(1)
|
|
166
|
+
continue
|
|
167
|
+
if hints.audio.search(text) and not out.audio and len(text) <= 120:
|
|
168
|
+
out.audio = text
|
|
169
|
+
continue
|
|
170
|
+
if hints.chrome.match(text):
|
|
171
|
+
continue
|
|
172
|
+
if text not in out.lines:
|
|
173
|
+
out.lines.append(text[:300])
|
|
174
|
+
if role in ("StaticText", "TextView", "Other", "Button") and len(text) >= hints.min_caption:
|
|
175
|
+
captions.append((text, element.rect))
|
|
176
|
+
if surface is not None:
|
|
177
|
+
# The item's own text sits on it; a tab bar or a toolbar below a feed that leaves room for one is not a caption.
|
|
178
|
+
captions = [(text, rect) for text, rect in captions if _within(rect, surface)]
|
|
179
|
+
if captions:
|
|
180
|
+
out.caption = max((text for text, _ in captions), key=len)[:300]
|
|
181
|
+
if not out.creator and len(set(bylines)) == 1:
|
|
182
|
+
out.creator = bylines[0] # one "Post by name" on the screen is its item's creator (a grid has many)
|
|
183
|
+
pictures = [name for name in pictures if name.casefold() not in followed]
|
|
184
|
+
if not out.creator and out.full_screen and 1 <= len(set(pictures)) <= 2:
|
|
185
|
+
out.creator = pictures[-1] # the creator's picture beside the caption (a collab shows two; the lower is first)
|
|
186
|
+
return out
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def ad_reason(item_read):
|
|
190
|
+
"""Why this item is an ad ("Sponsored", "Shop now"), or None. A paid partnership is not an ad."""
|
|
191
|
+
return item_read.ad
|