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,104 @@
|
|
|
1
|
+
"""`mobster workflow show NAME`: print a saved workflow's graph from the running Mobster (client.py)."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from ..graph import chain as graph_chain
|
|
7
|
+
from ..graph.catalog import TYPES
|
|
8
|
+
from .client import Client, NoService, ServiceError
|
|
9
|
+
|
|
10
|
+
WORDS = ("text", "question", "instruction", "message") # the field that says what a step does, in the person's words
|
|
11
|
+
WORDS_SHOWN = 70
|
|
12
|
+
NOT_RUNNING = "Mobster isn't running here. Open Mobster or run `mobster serve`, then try again."
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def add_arguments(parser, helpers):
|
|
16
|
+
subs = parser.add_subparsers(dest="workflow_command", metavar="<subcommand>", required=True)
|
|
17
|
+
show = subs.add_parser("show", help="print a workflow's steps in the order they run",
|
|
18
|
+
formatter_class=parser.formatter_class,
|
|
19
|
+
description="Print a saved workflow's steps in the order they run: each one's id, type, "
|
|
20
|
+
"title and the steps it waits for, with a loop's steps under it. It shows "
|
|
21
|
+
"the published version unless you add --draft, and changes nothing.",
|
|
22
|
+
epilog='examples:\n mobster workflow show "Answer DMs"\n'
|
|
23
|
+
" mobster workflow show 3f9c2a1b7d0e --draft --json")
|
|
24
|
+
show.add_argument("name", metavar="NAME", help="the workflow's name or id")
|
|
25
|
+
show.add_argument("--draft", action="store_true", help="the draft you're editing, not the published version")
|
|
26
|
+
show.add_argument("--json", action="store_true", help="print the steps as JSON")
|
|
27
|
+
show.add_argument("--url", metavar="URL", help="Mobster's address (default: $MOBSTER_URL, else the running Mac "
|
|
28
|
+
"app's, else http://127.0.0.1:8765)")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def steps(graph):
|
|
32
|
+
"""[{id, type, title, after: [{node, port}], parent, depth, words}] in the order they run (graph/chain.ordered)."""
|
|
33
|
+
nodes = {node["id"]: node for node in graph.get("nodes") or ()}
|
|
34
|
+
into = {}
|
|
35
|
+
for edge in graph.get("edges") or ():
|
|
36
|
+
into.setdefault(edge["target"], []).append({"node": edge["source"], "port": edge.get("sourcePort") or "out"})
|
|
37
|
+
|
|
38
|
+
def depth(node):
|
|
39
|
+
level, parent = 0, node.get("parent")
|
|
40
|
+
while parent in nodes and level < 10:
|
|
41
|
+
level, parent = level + 1, nodes[parent].get("parent")
|
|
42
|
+
return level
|
|
43
|
+
out = []
|
|
44
|
+
for node in graph_chain.ordered(graph):
|
|
45
|
+
kind, config = node["type"], node.get("config") or {}
|
|
46
|
+
words = next((config[field] for field in WORDS if isinstance(config.get(field), str)), None)
|
|
47
|
+
out.append({"id": node["id"], "type": kind, "title": node.get("title")
|
|
48
|
+
or getattr(TYPES.get(kind), "label", kind),
|
|
49
|
+
"after": into.get(node["id"], []), "parent": node.get("parent"), "depth": depth(node),
|
|
50
|
+
"words": " ".join(words.split()) if words else None})
|
|
51
|
+
return out
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def lines(workflow, revision, shown):
|
|
55
|
+
"""The text `show` prints: a heading, then one line per step."""
|
|
56
|
+
which = f"published revision {revision}" if revision else "the draft"
|
|
57
|
+
head = f"“{workflow.get('name')}” ({workflow.get('id')}): {which}, {len(shown)} steps"
|
|
58
|
+
width = max((len(step["id"]) + 2 * step["depth"] for step in shown), default=0)
|
|
59
|
+
out = [head]
|
|
60
|
+
for step in shown:
|
|
61
|
+
waits = ", ".join(a["node"] + (f" ({a['port']})" if a["port"] != "out" else "") for a in step["after"])
|
|
62
|
+
line = f" {' ' * step['depth']}{step['id']:<{width - 2 * step['depth']}} {step['title']} ({step['type']})"
|
|
63
|
+
if waits:
|
|
64
|
+
line += f", after {waits}"
|
|
65
|
+
if step["words"]:
|
|
66
|
+
words = step["words"]
|
|
67
|
+
line += f": “{words[:WORDS_SHOWN]}{'…' if len(words) > WORDS_SHOWN else ''}”"
|
|
68
|
+
out.append(line)
|
|
69
|
+
return out
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def run(args):
|
|
73
|
+
if args.workflow_command != "show":
|
|
74
|
+
return 2
|
|
75
|
+
client = Client(getattr(args, "url", None))
|
|
76
|
+
try:
|
|
77
|
+
try:
|
|
78
|
+
workflow = client.find_workflow(args.name)
|
|
79
|
+
except LookupError as error:
|
|
80
|
+
return _fail(str(error), 1)
|
|
81
|
+
revision = None if args.draft else (workflow.get("published") or {}).get("revision")
|
|
82
|
+
graph = workflow.get("graph") or {}
|
|
83
|
+
if revision:
|
|
84
|
+
graph = (client.call("GET", f"/api/workflows/{workflow['id']}/versions/{revision}").get("workflow")
|
|
85
|
+
or {}).get("graph") or {}
|
|
86
|
+
except NoService:
|
|
87
|
+
return _fail(NOT_RUNNING, 3)
|
|
88
|
+
except ServiceError as error:
|
|
89
|
+
return _fail(str(error), 1)
|
|
90
|
+
shown = steps(graph)
|
|
91
|
+
try:
|
|
92
|
+
if args.json:
|
|
93
|
+
print(json.dumps({"workflowId": workflow.get("id"), "name": workflow.get("name"), "revision": revision,
|
|
94
|
+
"draft": revision is None, "steps": shown}, ensure_ascii=False), flush=True)
|
|
95
|
+
else:
|
|
96
|
+
print("\n".join(lines(workflow, revision, shown)), flush=True)
|
|
97
|
+
except BrokenPipeError:
|
|
98
|
+
pass
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _fail(message, code):
|
|
103
|
+
print(f"mobster workflow: {message}", file=sys.stderr, flush=True)
|
|
104
|
+
return code
|
mobile_agent/tls.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Where HTTPS certificates are verified from: macOS's bundle for the frozen app or a Python missing its own."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import ssl
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
SYSTEM_BUNDLE = "/etc/ssl/cert.pem"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _own_paths_exist():
|
|
11
|
+
paths = ssl.get_default_verify_paths()
|
|
12
|
+
if paths.cafile and os.path.isfile(paths.cafile):
|
|
13
|
+
return True
|
|
14
|
+
return bool(paths.capath and os.path.isdir(paths.capath) and os.listdir(paths.capath))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ensure_ca_bundle(environ=None, frozen=None, bundle=SYSTEM_BUNDLE):
|
|
18
|
+
"""Point OpenSSL at macOS's bundle when this Python's own can't be trusted to exist."""
|
|
19
|
+
environ = os.environ if environ is None else environ
|
|
20
|
+
if environ.get("SSL_CERT_FILE") or environ.get("SSL_CERT_DIR"):
|
|
21
|
+
return None
|
|
22
|
+
frozen = getattr(sys, "frozen", False) if frozen is None else frozen
|
|
23
|
+
if not frozen and _own_paths_exist():
|
|
24
|
+
return None
|
|
25
|
+
if not os.path.isfile(bundle):
|
|
26
|
+
return None
|
|
27
|
+
environ["SSL_CERT_FILE"] = bundle
|
|
28
|
+
return bundle
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def report():
|
|
32
|
+
"""For `mobster version --json`: the CA file TLS verifies against and how many CAs load from it."""
|
|
33
|
+
context = ssl.create_default_context()
|
|
34
|
+
return {"caFile": ssl.get_default_verify_paths().cafile,
|
|
35
|
+
"caCerts": context.cert_store_stats().get("x509_ca", 0)}
|
|
36
|
+
|
mobile_agent/traces.py
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Traces: a run's tape while it runs, and its bundle after.
|
|
2
|
+
|
|
3
|
+
``MOBSTER_EXPERIMENTAL_TRACES=1`` turns traces on. A run in the app or the terminal then records a tape
|
|
4
|
+
(bench/tape.py): the request, every screen the agent read as data, every model reply and every action, in a private
|
|
5
|
+
file beside the run's frames. The secret filter masks each entry as it is written. Without durable history there is
|
|
6
|
+
nowhere to keep it, so nothing is recorded.
|
|
7
|
+
|
|
8
|
+
``mobster export ID --trace`` packs one finished run into a bundle (a zip):
|
|
9
|
+
|
|
10
|
+
manifest.json the run's facts: task, app, status, outcome, cost, model, counts, and what the bundle holds
|
|
11
|
+
events.jsonl the run's journal, one event a line: decisions, actions, refusals, cost and latency per call
|
|
12
|
+
tape.jsonl.gz the tape, when the run recorded one: replay it with bench/tape_replay.py
|
|
13
|
+
screens/*.jpg the run's screenshots, only with --screens
|
|
14
|
+
|
|
15
|
+
Every string in the bundle goes through ``secret_filter.redact``; the pictures cannot, so they stay out unless asked
|
|
16
|
+
for. A bundle holds task text and screen text. Nothing here uploads it.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import re
|
|
22
|
+
import shutil
|
|
23
|
+
import tempfile
|
|
24
|
+
import time
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
from . import __version__, failure_line, secret_filter
|
|
28
|
+
|
|
29
|
+
ENV_FLAG = "MOBSTER_EXPERIMENTAL_TRACES"
|
|
30
|
+
FORMAT = "mobster-trace/1"
|
|
31
|
+
TAPE = "tape.jsonl.gz" # bench/tape.py's NAME, gzipped when the run ends
|
|
32
|
+
BLOB = 4096 # a longer string with no space in it is a picture or a dump: never kept
|
|
33
|
+
MAX_DEPTH = 64
|
|
34
|
+
MAX_SCREENS_BYTES = 40_000_000
|
|
35
|
+
# What the bundle keeps of a run's summary: how it ended and what it cost, never its answer data or result cards.
|
|
36
|
+
SUMMARY_KEYS = ("status", "outcome", "reason", "answer", "code", "stop_code", "actions", "decisions", "elapsed_ms",
|
|
37
|
+
"costUsd", "model", "engine", "timing", "deviceKind", "failure")
|
|
38
|
+
RUN_KEYS = ("id", "goal", "appName", "mode", "engine", "status", "createdAt", "finishedAt", "costUsd", "origin")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def enabled(env=None):
|
|
42
|
+
return (os.environ if env is None else env).get(ENV_FLAG, "").strip().casefold() in ("1", "true", "yes", "on")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# -- masking --------------------------------------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
def mask(value, depth=0):
|
|
48
|
+
"""``value`` (JSON-shaped) with the secret filter applied to every string and every key. Unlike
|
|
49
|
+
``secret_filter.redact_deep``, nothing passes unfiltered: a long unbroken string is dropped, and so is
|
|
50
|
+
everything nested deeper than MAX_DEPTH."""
|
|
51
|
+
if isinstance(value, str):
|
|
52
|
+
if len(value) > BLOB and " " not in value[:BLOB]:
|
|
53
|
+
return f"[{len(value)} characters left out]"
|
|
54
|
+
return secret_filter.redact(value)
|
|
55
|
+
if isinstance(value, dict):
|
|
56
|
+
return {secret_filter.redact(str(key)): mask(item, depth + 1) for key, item in value.items()} \
|
|
57
|
+
if depth < MAX_DEPTH else {}
|
|
58
|
+
if isinstance(value, (list, tuple)):
|
|
59
|
+
return [mask(item, depth + 1) for item in value] if depth < MAX_DEPTH else []
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# -- recording ------------------------------------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
class Recording:
|
|
66
|
+
"""One run's tape. ``attach`` is what ``engines.build_frontier`` calls with the pair the agent is built on: it
|
|
67
|
+
returns the same pair recording to a tape in the run's folder when traces are on and the run has a place to keep
|
|
68
|
+
it, and the pair unchanged otherwise. ``finish`` ends the tape with the run's outcome, once."""
|
|
69
|
+
|
|
70
|
+
def __init__(self, frames, run_id, request):
|
|
71
|
+
self.frames, self.run_id, self.request, self.tape = frames, run_id, request, None
|
|
72
|
+
|
|
73
|
+
def attach(self, driver, client, *, apps, frontier, hooks=()):
|
|
74
|
+
folder = self.frames.folder(self.run_id) if self.frames is not None and enabled() else None
|
|
75
|
+
if folder is None:
|
|
76
|
+
return driver, client
|
|
77
|
+
from .bench import tape as tape_module
|
|
78
|
+
try:
|
|
79
|
+
folder.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
80
|
+
os.chmod(folder.parent, 0o700)
|
|
81
|
+
folder.mkdir(exist_ok=True, mode=0o700)
|
|
82
|
+
except OSError:
|
|
83
|
+
return driver, client
|
|
84
|
+
tape = tape_module.Tape(folder / tape_module.NAME, scrub=mask)
|
|
85
|
+
tape.header(request=self.request, apps=apps, model=getattr(client, "model", None), frontier=frontier,
|
|
86
|
+
driver=driver, hooks=hooks)
|
|
87
|
+
self.tape = tape
|
|
88
|
+
return tape_module.TapeDriver(driver, tape), tape_module.TapeClient(client, tape)
|
|
89
|
+
|
|
90
|
+
def finish(self, outcome=None):
|
|
91
|
+
"""End the tape; a run that ended without an outcome (an exception) ends it as an error. A tape that stopped
|
|
92
|
+
early (past its size, a write that failed) can't replay anything: it is removed."""
|
|
93
|
+
tape, self.tape = self.tape, None
|
|
94
|
+
if tape is None:
|
|
95
|
+
return
|
|
96
|
+
tape.end(outcome or {"status": "error"})
|
|
97
|
+
if tape.broken:
|
|
98
|
+
tape.path.unlink(missing_ok=True)
|
|
99
|
+
Path(str(tape.path) + ".gz").unlink(missing_ok=True)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# -- the bundle -----------------------------------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
def _frame_ids(value, found):
|
|
105
|
+
if isinstance(value, dict):
|
|
106
|
+
for key, item in value.items():
|
|
107
|
+
if key == "frameId" and isinstance(item, str):
|
|
108
|
+
found.setdefault(item)
|
|
109
|
+
else:
|
|
110
|
+
_frame_ids(item, found)
|
|
111
|
+
elif isinstance(value, list):
|
|
112
|
+
for item in value:
|
|
113
|
+
_frame_ids(item, found)
|
|
114
|
+
return found
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _facts(summary):
|
|
118
|
+
return {k: v for k, v in summary.items() if k in SUMMARY_KEYS}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _lean(event):
|
|
122
|
+
"""A run's last two events carry its whole summary (answer data, result cards): the bundle keeps the facts."""
|
|
123
|
+
if event.get("event") == "run_finished" and isinstance(event.get("summary"), dict):
|
|
124
|
+
return {**event, "summary": _facts(event["summary"])}
|
|
125
|
+
if event.get("event") == "result":
|
|
126
|
+
return {k: v for k, v in event.items() if k in SUMMARY_KEYS or k in ("event", "seq", "timestamp")}
|
|
127
|
+
return event
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def masked_tape(path):
|
|
131
|
+
"""The tape at ``path`` with every entry masked again, as gzip bytes, and its entry count; None when it can't
|
|
132
|
+
be read whole (a bundle never carries a tape it could not mask)."""
|
|
133
|
+
import gzip # export only: a run's start never loads gzip or zipfile (scripts/perf-budgets.json)
|
|
134
|
+
try:
|
|
135
|
+
text = gzip.decompress(Path(path).read_bytes()).decode("utf-8")
|
|
136
|
+
entries = [mask(json.loads(line)) for line in text.splitlines() if line.strip()]
|
|
137
|
+
except (OSError, ValueError, EOFError):
|
|
138
|
+
return None
|
|
139
|
+
body = "".join(json.dumps(entry, ensure_ascii=False, separators=(",", ":")) + "\n" for entry in entries)
|
|
140
|
+
return gzip.compress(body.encode("utf-8"), 6, mtime=0), len(entries)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _jsonl(rows):
|
|
144
|
+
return "".join(json.dumps(row, ensure_ascii=False, separators=(",", ":")) + "\n" for row in rows).encode("utf-8")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def bundle(record_, folder, *, read_frame=None):
|
|
148
|
+
"""(zip bytes, manifest) for a finished run. ``record_``: the run's journal row with its ``events``; ``folder``:
|
|
149
|
+
where the run's tape is (its frames folder), or None; ``read_frame(frame_id)``: bytes or None, given only when
|
|
150
|
+
the screenshots are wanted."""
|
|
151
|
+
import io
|
|
152
|
+
import zipfile
|
|
153
|
+
events = [mask(_lean(event)) for event in record_.get("events") or () if isinstance(event, dict)]
|
|
154
|
+
summary = record_.get("summary") if isinstance(record_.get("summary"), dict) else {}
|
|
155
|
+
facts = {k: record_.get(k) for k in RUN_KEYS}
|
|
156
|
+
facts["app"] = (record_.get("app") or {}).get("bundleId") if isinstance(record_.get("app"), dict) else None
|
|
157
|
+
facts["summary"] = _facts(summary)
|
|
158
|
+
members = {"events.jsonl": _jsonl(events)}
|
|
159
|
+
tape_file = Path(folder) / TAPE if folder is not None else None
|
|
160
|
+
packed = masked_tape(tape_file) if tape_file is not None and tape_file.is_file() else None
|
|
161
|
+
if packed is not None:
|
|
162
|
+
members[TAPE], entries = packed
|
|
163
|
+
tape_note = {"included": True, "entries": entries}
|
|
164
|
+
elif tape_file is not None and tape_file.exists():
|
|
165
|
+
tape_note = {"included": False, "why": "the tape could not be read whole, so it was left out"}
|
|
166
|
+
else:
|
|
167
|
+
tape_note = {"included": False, "why": f"this run recorded no tape (set {ENV_FLAG}=1 before it runs)"}
|
|
168
|
+
screens = {"included": False}
|
|
169
|
+
if read_frame is not None:
|
|
170
|
+
taken, size = {}, 0
|
|
171
|
+
for frame_id in _frame_ids(record_.get("events") or (), {}):
|
|
172
|
+
data = read_frame(frame_id)
|
|
173
|
+
if data and size + len(data) <= MAX_SCREENS_BYTES:
|
|
174
|
+
taken[f"screens/{frame_id}.jpg"] = bytes(data)
|
|
175
|
+
size += len(data)
|
|
176
|
+
members.update(taken)
|
|
177
|
+
screens = {"included": True, "count": len(taken)}
|
|
178
|
+
manifest = {"format": FORMAT, "run": mask(facts), "events": len(events), "tape": tape_note, "screens": screens,
|
|
179
|
+
"masked": "secret_filter"}
|
|
180
|
+
members = {"manifest.json": json.dumps(manifest, ensure_ascii=False, indent=1).encode("utf-8") + b"\n", **members}
|
|
181
|
+
out = io.BytesIO()
|
|
182
|
+
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as archive:
|
|
183
|
+
for name, data in members.items():
|
|
184
|
+
info = zipfile.ZipInfo(name) # the 1980 date: nothing of this Mac's clock rides along
|
|
185
|
+
info.compress_type, info.external_attr = zipfile.ZIP_DEFLATED, 0o600 << 16
|
|
186
|
+
archive.writestr(info, data)
|
|
187
|
+
return out.getvalue(), manifest
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def save(data, folder, stem):
|
|
191
|
+
"""Write ``data`` as ``folder``/``stem``.zip, private, and never over a file that is there ("stem (1).zip"...).
|
|
192
|
+
The saved path; OSError when the folder can't be written."""
|
|
193
|
+
folder = Path(folder)
|
|
194
|
+
folder.mkdir(parents=True, exist_ok=True)
|
|
195
|
+
for index in range(1000):
|
|
196
|
+
target = folder / (f"{stem}.zip" if index == 0 else f"{stem} ({index}).zip")
|
|
197
|
+
try:
|
|
198
|
+
descriptor = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600)
|
|
199
|
+
except FileExistsError:
|
|
200
|
+
continue
|
|
201
|
+
with os.fdopen(descriptor, "wb") as stream:
|
|
202
|
+
stream.write(data)
|
|
203
|
+
return target
|
|
204
|
+
raise OSError("Too many files with that name")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# -- the local eval set ---------------------------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
class EvalError(Exception):
|
|
210
|
+
"""A run that can't become an eval. ``code``: running, no_tape or unreadable; the message says what happened and
|
|
211
|
+
what to do."""
|
|
212
|
+
|
|
213
|
+
def __init__(self, code, message):
|
|
214
|
+
super().__init__(message)
|
|
215
|
+
self.code = code
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _slug(text):
|
|
219
|
+
return re.sub(r"-+", "-", re.sub(r"[^a-z0-9]+", "-", str(text).lower())).strip("-")[:40].strip("-")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def add_to_evals(record_, folder, evals_dir):
|
|
223
|
+
"""Keep a finished run as a regression task in the eval set at ``evals_dir`` (bench/trace_evals.py says what a
|
|
224
|
+
task is). ``record_``: the run's row with its ``events``; ``folder``: where its tape is. A run that failed expects
|
|
225
|
+
a replay that changes; one that didn't, a replay that holds. Adding the same run again changes nothing.
|
|
226
|
+
Returns {"id", "path", "added", "expected", "recorded"}; EvalError when the run is still going or has no tape."""
|
|
227
|
+
from .bench import trace_evals
|
|
228
|
+
if record_.get("status") in ("queued", "running") or record_.get("finishedAt") is None:
|
|
229
|
+
raise EvalError("running", "This task is still running. Add it to your evals once it ends.")
|
|
230
|
+
tape_file = Path(folder) / TAPE if folder is not None else None
|
|
231
|
+
if tape_file is None or not tape_file.is_file():
|
|
232
|
+
raise EvalError("no_tape", f"This task recorded no tape, so it can't be replayed. Set {ENV_FLAG}=1 and run "
|
|
233
|
+
"it again.")
|
|
234
|
+
packed = masked_tape(tape_file)
|
|
235
|
+
if packed is None:
|
|
236
|
+
raise EvalError("unreadable", "This task's tape could not be read whole. Run it again to record a new one.")
|
|
237
|
+
events = [event for event in record_.get("events") or () if isinstance(event, dict)]
|
|
238
|
+
summary = record_.get("summary") if isinstance(record_.get("summary"), dict) else {}
|
|
239
|
+
failure = summary.get("failure") or failure_line.failure_line(summary, events)
|
|
240
|
+
recorded = mask({"status": record_.get("status"), "outcome": summary.get("outcome"),
|
|
241
|
+
"steps": summary.get("decisions"), "actions": summary.get("actions"),
|
|
242
|
+
"costUsd": summary.get("costUsd"), "failure": failure})
|
|
243
|
+
expected = {"status": "completed" if failure else record_.get("status"),
|
|
244
|
+
"replay": "changes" if failure else "holds"}
|
|
245
|
+
goal = mask(record_.get("goal") or "")
|
|
246
|
+
task_id = f"{_slug(goal) or 'task'}-{record_['id']}"
|
|
247
|
+
task = {"format": trace_evals.FORMAT, "id": task_id, "added": time.strftime("%Y-%m-%d"), "goal": goal,
|
|
248
|
+
"app": (record_.get("app") or {}).get("bundleId") if isinstance(record_.get("app"), dict) else None,
|
|
249
|
+
"source": {"run": record_["id"], "version": __version__}, "recorded": recorded, "expected": expected}
|
|
250
|
+
target = Path(evals_dir) / task_id
|
|
251
|
+
result = {"id": task_id, "path": str(target), "expected": expected, "recorded": recorded}
|
|
252
|
+
if target.exists():
|
|
253
|
+
return {**result, "added": False}
|
|
254
|
+
Path(evals_dir).mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
255
|
+
os.chmod(evals_dir, 0o700)
|
|
256
|
+
building = Path(tempfile.mkdtemp(prefix=f".{task_id}.", dir=evals_dir)) # a task appears whole or not at all
|
|
257
|
+
try:
|
|
258
|
+
for name, data in ((trace_evals.TASK, (json.dumps(task, ensure_ascii=False, indent=1) + "\n").encode()),
|
|
259
|
+
(trace_evals.TAPE, packed[0])):
|
|
260
|
+
descriptor = os.open(building / name, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|
261
|
+
with os.fdopen(descriptor, "wb") as stream:
|
|
262
|
+
stream.write(data)
|
|
263
|
+
try:
|
|
264
|
+
os.rename(building, target)
|
|
265
|
+
except OSError:
|
|
266
|
+
if not target.exists():
|
|
267
|
+
raise
|
|
268
|
+
return {**result, "added": False} # the same run, added at the same moment
|
|
269
|
+
finally:
|
|
270
|
+
shutil.rmtree(building, ignore_errors=True)
|
|
271
|
+
return {**result, "added": True}
|
mobile_agent/tracks.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Static registration of the SOTA tracks (seam S1). Static imports so PyInstaller follows them.
|
|
2
|
+
|
|
3
|
+
``load()`` imports each track's ``register`` module and calls ``register(api)`` (api = harness_api) inside
|
|
4
|
+
``_staged()``: every seam registry (harness_api's, api_routes, journal.SCHEMAS, mcp_server.registry) is
|
|
5
|
+
snapshotted first and restored if register raises, so a track that raises leaves nothing registered. It is
|
|
6
|
+
recorded in STATUS and skipped: Mobster still starts. ``load`` does no I/O beyond imports.
|
|
7
|
+
|
|
8
|
+
Callers: server.Runtime.__init__ (first line), mcp_server.cli at startup, and each new CLI command.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import threading
|
|
12
|
+
from contextlib import contextmanager
|
|
13
|
+
|
|
14
|
+
from . import harness_api as api
|
|
15
|
+
|
|
16
|
+
TRACKS = ("harness", "threads", "memory", "attachments", "wireless", "testkit", "remote", "accounts", "learned",
|
|
17
|
+
"longform", "outlets", "connectors", "plugins", "permissions", "packs", "app_api", "waiting", "netxray",
|
|
18
|
+
"content_queue", "feedback", "ios", "teams", "xray", "triggers", "projects", "corpus")
|
|
19
|
+
STATUS: dict = {}
|
|
20
|
+
_loaded = False
|
|
21
|
+
_lock = threading.Lock()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _importers():
|
|
25
|
+
"""(track, importer) pairs. Each importer is a plain ``from .x import register`` (PyInstaller follows it)."""
|
|
26
|
+
def harness():
|
|
27
|
+
from .harness import register
|
|
28
|
+
return register
|
|
29
|
+
|
|
30
|
+
def threads():
|
|
31
|
+
from .threads import register
|
|
32
|
+
return register
|
|
33
|
+
|
|
34
|
+
def memory():
|
|
35
|
+
from .memory import register
|
|
36
|
+
return register
|
|
37
|
+
|
|
38
|
+
def attachments():
|
|
39
|
+
from .attachments import register
|
|
40
|
+
return register
|
|
41
|
+
|
|
42
|
+
def wireless():
|
|
43
|
+
from .wireless import register
|
|
44
|
+
return register
|
|
45
|
+
|
|
46
|
+
def testkit():
|
|
47
|
+
from .testkit import register
|
|
48
|
+
return register
|
|
49
|
+
|
|
50
|
+
def remote():
|
|
51
|
+
from .remote import register
|
|
52
|
+
return register
|
|
53
|
+
|
|
54
|
+
def accounts():
|
|
55
|
+
from .accounts import register
|
|
56
|
+
return register
|
|
57
|
+
|
|
58
|
+
def learned():
|
|
59
|
+
from .learned import register
|
|
60
|
+
return register
|
|
61
|
+
|
|
62
|
+
def longform():
|
|
63
|
+
from .longform import register
|
|
64
|
+
return register
|
|
65
|
+
|
|
66
|
+
def outlets():
|
|
67
|
+
from .outlets import register
|
|
68
|
+
return register
|
|
69
|
+
|
|
70
|
+
def connectors():
|
|
71
|
+
from .connectors import register
|
|
72
|
+
return register
|
|
73
|
+
|
|
74
|
+
def plugins():
|
|
75
|
+
from .plugins import register
|
|
76
|
+
return register
|
|
77
|
+
|
|
78
|
+
def permissions():
|
|
79
|
+
from .permissions import register
|
|
80
|
+
return register
|
|
81
|
+
|
|
82
|
+
def packs():
|
|
83
|
+
from .packs import register
|
|
84
|
+
return register
|
|
85
|
+
|
|
86
|
+
def app_api():
|
|
87
|
+
from .app_api import register
|
|
88
|
+
return register
|
|
89
|
+
|
|
90
|
+
def waiting():
|
|
91
|
+
from .waiting import register
|
|
92
|
+
return register
|
|
93
|
+
|
|
94
|
+
def netxray():
|
|
95
|
+
from .netxray import register
|
|
96
|
+
return register
|
|
97
|
+
|
|
98
|
+
def content_queue():
|
|
99
|
+
from .content_queue import register
|
|
100
|
+
return register
|
|
101
|
+
|
|
102
|
+
def feedback():
|
|
103
|
+
from .feedback import register
|
|
104
|
+
return register
|
|
105
|
+
|
|
106
|
+
def ios():
|
|
107
|
+
from .ios import register
|
|
108
|
+
return register
|
|
109
|
+
|
|
110
|
+
def triggers():
|
|
111
|
+
from .triggers import register
|
|
112
|
+
return register
|
|
113
|
+
|
|
114
|
+
def teams():
|
|
115
|
+
from .teams import register
|
|
116
|
+
return register
|
|
117
|
+
|
|
118
|
+
def xray():
|
|
119
|
+
from .xray import register
|
|
120
|
+
return register
|
|
121
|
+
|
|
122
|
+
def projects():
|
|
123
|
+
from .projects import register
|
|
124
|
+
return register
|
|
125
|
+
|
|
126
|
+
def corpus():
|
|
127
|
+
from .corpus import register
|
|
128
|
+
return register
|
|
129
|
+
|
|
130
|
+
return (("harness", harness), ("threads", threads), ("memory", memory), ("attachments", attachments),
|
|
131
|
+
("wireless", wireless), ("testkit", testkit), ("remote", remote), ("accounts", accounts),
|
|
132
|
+
("learned", learned), ("longform", longform), ("outlets", outlets),
|
|
133
|
+
("connectors", connectors), ("plugins", plugins),
|
|
134
|
+
("permissions", permissions), ("packs", packs), ("app_api", app_api),
|
|
135
|
+
("waiting", waiting), ("netxray", netxray),
|
|
136
|
+
("content_queue", content_queue), ("feedback", feedback), ("ios", ios), ("teams", teams), ("xray", xray),
|
|
137
|
+
("triggers", triggers), ("projects", projects), ("corpus", corpus))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _registries():
|
|
141
|
+
"""(snapshot, restore) pairs of every seam registry a track's register() may touch."""
|
|
142
|
+
from . import api_routes, journal
|
|
143
|
+
from .mcp_server import registry as mcp_registry
|
|
144
|
+
pairs = [(api._snapshot, api._restore)]
|
|
145
|
+
pairs.append((lambda: list(api_routes._routes), lambda saved: _replace(api_routes._routes, saved)))
|
|
146
|
+
pairs.append((lambda: dict(journal.SCHEMAS), lambda saved: _replace(journal.SCHEMAS, saved)))
|
|
147
|
+
pairs.append((lambda: dict(journal.SCHEMA_OWNERS), lambda saved: _replace(journal.SCHEMA_OWNERS, saved)))
|
|
148
|
+
pairs.append((lambda: list(mcp_registry._providers), lambda saved: _replace(mcp_registry._providers, saved)))
|
|
149
|
+
return pairs
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _replace(target, saved):
|
|
153
|
+
target.clear()
|
|
154
|
+
if isinstance(target, dict):
|
|
155
|
+
target.update(saved)
|
|
156
|
+
else:
|
|
157
|
+
target.extend(saved)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@contextmanager
|
|
161
|
+
def _staged(track):
|
|
162
|
+
"""Registrations inside belong to ``track``; if the body raises, every seam registry goes back to how it was."""
|
|
163
|
+
pairs = _registries()
|
|
164
|
+
saved = [(snapshot(), restore) for snapshot, restore in pairs]
|
|
165
|
+
try:
|
|
166
|
+
with api.owned_by(track):
|
|
167
|
+
yield
|
|
168
|
+
except BaseException:
|
|
169
|
+
for value, restore in saved:
|
|
170
|
+
restore(value)
|
|
171
|
+
raise
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def register_one(track, register):
|
|
175
|
+
"""Run one track's ``register(api)`` staged; record STATUS. Returns True when it registered."""
|
|
176
|
+
try:
|
|
177
|
+
with _staged(track):
|
|
178
|
+
register(api)
|
|
179
|
+
except Exception as error: # noqa: BLE001 -- a track never stops Mobster from starting
|
|
180
|
+
STATUS[track] = f"error: {type(error).__name__}"
|
|
181
|
+
return False
|
|
182
|
+
STATUS[track] = "ok"
|
|
183
|
+
return True
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def load() -> None:
|
|
187
|
+
"""Idempotent: register every track once per process."""
|
|
188
|
+
global _loaded
|
|
189
|
+
with _lock:
|
|
190
|
+
if _loaded:
|
|
191
|
+
return
|
|
192
|
+
_loaded = True
|
|
193
|
+
for track, importer in _importers():
|
|
194
|
+
try:
|
|
195
|
+
module = importer()
|
|
196
|
+
except ModuleNotFoundError as error:
|
|
197
|
+
# A track left out of this build (Remote isn't in the open-source tree) is absent, not broken.
|
|
198
|
+
own = error.name == f"{__package__}.{track}"
|
|
199
|
+
STATUS[track] = "absent" if own else f"error: {type(error).__name__}"
|
|
200
|
+
continue
|
|
201
|
+
except Exception as error: # noqa: BLE001
|
|
202
|
+
STATUS[track] = f"error: {type(error).__name__}"
|
|
203
|
+
continue
|
|
204
|
+
register_one(track, module.register)
|
|
205
|
+
try: # GET /api/access/full-disk belongs to no track, so it never answers 503 (full_disk_access.py)
|
|
206
|
+
from .full_disk_access import register_route
|
|
207
|
+
register_route()
|
|
208
|
+
except Exception: # noqa: BLE001 -- the route is an extra; Mobster still starts
|
|
209
|
+
pass
|
|
210
|
+
try: # /api/project/env belongs to no track either
|
|
211
|
+
from .project_env_routes import register_routes
|
|
212
|
+
register_routes()
|
|
213
|
+
except Exception: # noqa: BLE001
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def disable(track, reason) -> None:
|
|
218
|
+
"""Turn ``track`` off (journal.register_schema: "error: newer data" or "error: migration")."""
|
|
219
|
+
STATUS[track] = reason
|
|
220
|
+
api.disable(track)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def reset_for_tests() -> None:
|
|
224
|
+
"""Forget every registration and allow ``load`` again."""
|
|
225
|
+
global _loaded
|
|
226
|
+
from . import api_routes, journal
|
|
227
|
+
from .mcp_server import registry as mcp_registry
|
|
228
|
+
with _lock:
|
|
229
|
+
_loaded = False
|
|
230
|
+
STATUS.clear()
|
|
231
|
+
api.reset_for_tests()
|
|
232
|
+
api_routes._routes.clear()
|
|
233
|
+
journal.SCHEMAS.clear()
|
|
234
|
+
journal.SCHEMA_OWNERS.clear()
|
|
235
|
+
mcp_registry._providers.clear()
|