mcpywrap 0.6.1__tar.gz → 0.6.2__tar.gz
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.
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/CHANGELOG.md +10 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/PKG-INFO +13 -3
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/README.md +11 -1
- mcpywrap-0.6.2/core/mcpywrap_core/_version.py +1 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/PKG-INFO +13 -3
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/SOURCES.txt +7 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/requires.txt +1 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/bridge_payload/Injector.exe +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/bridge_payload/Loader.dll +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/bridge_payload/McpyMcsAuth.dll +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/bridge_payload/manifest.json +3 -3
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/window_activation/manifest.json +1 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/window_activation/mcpy-window-activation.dll +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/window_capture/manifest.json +1 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/window_capture/mcpy-window-capture.exe +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/check_release.py +7 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/SKILL.md +3 -1
- mcpywrap-0.6.2/skills/mcpywrap/references/application-host.md +88 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/local-development.md +4 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/setup.md +5 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/scripts/bootstrap.py +29 -3
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_build_replace_retry.py +12 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_core_api.py +17 -0
- mcpywrap-0.6.2/tests/test_execution_boundaries.py +104 -0
- mcpywrap-0.6.2/tests/test_log_query.py +80 -0
- mcpywrap-0.6.2/tests/test_mobile_diagnostics.py +164 -0
- mcpywrap-0.6.2/tests/test_mobile_host.py +124 -0
- mcpywrap-0.6.2/tests/test_mobile_pairing.py +109 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_modsdk_environment.py +1 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_project_scripts.py +8 -0
- mcpywrap-0.6.2/tests/test_restricted_process_identity.py +37 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_skill_workflows.py +20 -0
- mcpywrap-0.6.1/core/mcpywrap_core/_version.py +0 -1
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/CONTRIBUTING.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/LICENSE +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/MANIFEST.in +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/RELEASING.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/THIRD_PARTY_NOTICES.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/dependency_links.txt +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/entry_points.txt +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap.egg-info/top_level.txt +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/__init__.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/bridge_payload/publisher.cer +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/code-libraries.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/deployment-recovery.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/engine-discovery.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/game-backends.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/git-dependencies.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/input-sequence.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/instance-world-settings.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/launcher-client-python.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/local-dependencies.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/macos-resource-reload.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/mcs-auth.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/project-scripts.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/qt6-development.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/qumod.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/runtime-debug.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/runtime-input.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/runtime-player.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/runtime-ui.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/shared-core.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/docs/shared-log-ui.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/examples/consumer_prepare/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/apply.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/client-background.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/game-window-background.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/libc-shim-background.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/manifest-background.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/background_window/test_window.cpp +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/mcs_auth/Bridge.cs +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/mcs_auth/Injector.cs +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/mcs_auth/Loader.c +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/mcs_auth/publisher.cer +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/mcs_auth/publisher.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/runtime_input/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/runtime_input/launcher-monotonic.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/runtime_input/manifest-monotonic.patch +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/runtime_input/steady_clock.h +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/window_activation/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/window_activation/window_activation.cpp +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/window_capture/video_capture.h +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/native/window_capture/window_capture.cpp +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/pyproject.toml +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_all_native.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_app_icon.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_distributions.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_window_activation.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/build_window_capture.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/check_mcs_auth_bridge.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/check_window_activation.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/check_window_capture.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/pypi_release.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/sign_mcs_auth_ci.ps1 +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/verify_core_release.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/verify_distributions.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/scripts/version.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/setup.cfg +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/setup.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/agents/openai.yaml +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/behavior_pack/CatalogRuntime/__init__.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/behavior_pack/manifest.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/example.toml +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/prepare.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/build_tools/schema.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/framework/pyproject.toml +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/game/.gitignore +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/game/behavior_pack/manifest.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/game/data/items.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/game/pyproject.toml +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/assets/consumer-prepare/game/resource_pack/manifest.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/capture.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/consumer-prepare-author.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/consumer-prepare.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/dual-clients.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/input-sequence.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/input-writing.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/instance-world-settings.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/macos-local.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/network-sessions.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/project-dependencies.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/project-scripts.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/project-workflow.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/remote-testing.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/runtime-debug.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/runtime-input.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/runtime-key-timeline.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/runtime-player.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/runtime-ui.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/script-example.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/references/troubleshooting.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/scripts/bootstrap.ps1 +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/scripts/game_window.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/skills/mcpywrap/scripts/smoke.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/core_fixture.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/embedded_upgrade_probe.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/consumer_prepare_acceptance.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/lifecycle_dependency_audit.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/README.md +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/client_probe.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/input_probe.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/key_timeline_probe.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/movement.json +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/run.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/server_fixture.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual/runtime_controls/support.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_background_launch.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_dependency_boundaries.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_input_window.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_integration.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_native_recording.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_remote_recording.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_runtime_input.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_runtime_input_unified.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_runtime_key_timeline.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_runtime_player.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/manual_runtime_ui.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/runtime_support.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_application_icons.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_auth_interaction.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_automation.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_builder_import_boundary.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_code_libraries.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_consumer_prepare.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_core_updates.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_dependency_boundaries.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_deployment_cli.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_deployment_encoding.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_deployment_recovery.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_distribution_layout.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_engine_backends.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_engine_compatibility.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_engine_discovery.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_engine_install.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_game_window_skill.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_git_projects.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_host_input.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_host_services.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_input_sequence.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_instance_cppconfig.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_launch_encoding.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_launcher_python.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_lifecycle_audit.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_lifecycle_races.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_local_auth_session.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_local_dependencies.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_lock_storage.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_log_stream.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_macos_network.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_manual_runtime_controls.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_mcs_auth.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_mouse.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_native_input_sequence.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_native_preferences.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_native_recording.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_network.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_network_sessions.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_pack_uuid_conflicts.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_package.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_qt_sessions.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_qumod_workflow.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_recordings.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_remote.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_remote_scripts.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_resource_reload.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_debug.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_deployment.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_groups.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_input.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_key_timeline.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_player.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_profiles.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_ui.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_runtime_ui_outline.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_screenshot.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_session_capabilities.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_shared_logs.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_unified_skill_contract.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_window_activation.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_window_focus.py +0 -0
- {mcpywrap-0.6.1 → mcpywrap-0.6.2}/tests/test_window_policy.py +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.6.2
|
|
4
|
+
|
|
5
|
+
- 日志新增字面过滤与重复消息统计,明确扫描范围;支持结构化日志的 App 宿主另提供端侧和毫秒时间范围筛选。
|
|
6
|
+
- 单会话重启明确交接旧、新 session;缓存信息显示年龄,Android 启动支持分阶段证据、后台尝试历史和独立的确认等待配置。
|
|
7
|
+
|
|
8
|
+
- 标准 CLI 与同一份 skill 支持配对 Android 本机宿主;工程解析、构建和组装由 App 内核心执行,开发端按阶段完成工具准备,连接失败不回退本地构建。
|
|
9
|
+
- 配对和准备结果支持中断恢复;最近操作保存最后观察到的状态,启动结果未知时可查询关联会话的新证据,不重放副作用。
|
|
10
|
+
- 手机日志支持分源聚合,截图可自动保存到作品 captures 目录;补全库工具在开发端执行,版本目录支持离线缓存和有界联网刷新。
|
|
11
|
+
- 两个发行包继续使用相同版本号,默认完整安装入口保持不变。
|
|
12
|
+
|
|
3
13
|
## 0.6.1
|
|
4
14
|
|
|
5
15
|
- 自动化启动默认保留后台窗口,不抢前台应用;提供 `--background` / `--foreground` 显式选择,重启和联调组保留策略。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Complete mcpy desktop tools, powered by the shared mcpywrap-core
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.9
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
License-File: THIRD_PARTY_NOTICES.md
|
|
13
|
-
Requires-Dist: mcpywrap-core==0.6.
|
|
13
|
+
Requires-Dist: mcpywrap-core==0.6.2
|
|
14
14
|
Requires-Dist: pip>=23
|
|
15
15
|
Requires-Dist: build>=1.0
|
|
16
16
|
Requires-Dist: watchdog>=2.0.0
|
|
@@ -134,9 +134,19 @@ Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与
|
|
|
134
134
|
|
|
135
135
|
外部服务器连接、可选 MCS 身份与进服判断见 [网络会话](skills/mcpywrap/references/network-sessions.md)。需要另一台 Windows 执行游戏时,再按 [远程执行端](skills/mcpywrap/references/remote-testing.md) 配置服务。远程连接不部署本地 Mod,也不提供世界实例管理。
|
|
136
136
|
|
|
137
|
+
## Android 本机宿主
|
|
138
|
+
|
|
139
|
+
仅在手机上使用外部开发工具时,按[本机 App 接入](skills/mcpywrap/references/application-host.md)完成配对。绑定工作区使用标准命令,由 App 核心执行工程操作;离线时不回退本地构建。普通 Windows/macOS 项目继续使用原本的桌面流程,无需安装手机应用或配置手机权限。
|
|
140
|
+
|
|
137
141
|
## AI Agent 使用
|
|
138
142
|
|
|
139
|
-
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。
|
|
143
|
+
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。0.6.2 起也可从已安装的发行包导出完整同版本 Skill:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
mcpy skill install --directory <Agent技能目录>/mcpywrap
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
更新已有技能目录加 `--replace`。只维护这份标准 Skill,按任务加载平台参考。
|
|
140
150
|
|
|
141
151
|
Skill 按当前任务选择参考,不要求 AI 通读产品文档。可直接描述:
|
|
142
152
|
|
|
@@ -109,9 +109,19 @@ Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与
|
|
|
109
109
|
|
|
110
110
|
外部服务器连接、可选 MCS 身份与进服判断见 [网络会话](skills/mcpywrap/references/network-sessions.md)。需要另一台 Windows 执行游戏时,再按 [远程执行端](skills/mcpywrap/references/remote-testing.md) 配置服务。远程连接不部署本地 Mod,也不提供世界实例管理。
|
|
111
111
|
|
|
112
|
+
## Android 本机宿主
|
|
113
|
+
|
|
114
|
+
仅在手机上使用外部开发工具时,按[本机 App 接入](skills/mcpywrap/references/application-host.md)完成配对。绑定工作区使用标准命令,由 App 核心执行工程操作;离线时不回退本地构建。普通 Windows/macOS 项目继续使用原本的桌面流程,无需安装手机应用或配置手机权限。
|
|
115
|
+
|
|
112
116
|
## AI Agent 使用
|
|
113
117
|
|
|
114
|
-
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。
|
|
118
|
+
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。0.6.2 起也可从已安装的发行包导出完整同版本 Skill:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
mcpy skill install --directory <Agent技能目录>/mcpywrap
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
更新已有技能目录加 `--replace`。只维护这份标准 Skill,按任务加载平台参考。
|
|
115
125
|
|
|
116
126
|
Skill 按当前任务选择参考,不要求 AI 通读产品文档。可直接描述:
|
|
117
127
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.6.2'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcpywrap
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: Complete mcpy desktop tools, powered by the shared mcpywrap-core
|
|
5
5
|
Author-email: boybook <boybook@easecation.net>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.9
|
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
License-File: THIRD_PARTY_NOTICES.md
|
|
13
|
-
Requires-Dist: mcpywrap-core==0.6.
|
|
13
|
+
Requires-Dist: mcpywrap-core==0.6.2
|
|
14
14
|
Requires-Dist: pip>=23
|
|
15
15
|
Requires-Dist: build>=1.0
|
|
16
16
|
Requires-Dist: watchdog>=2.0.0
|
|
@@ -134,9 +134,19 @@ Git 项目使用 `[[tool.mcpywrap.git_dependencies]]` 声明,支持 Addon 与
|
|
|
134
134
|
|
|
135
135
|
外部服务器连接、可选 MCS 身份与进服判断见 [网络会话](skills/mcpywrap/references/network-sessions.md)。需要另一台 Windows 执行游戏时,再按 [远程执行端](skills/mcpywrap/references/remote-testing.md) 配置服务。远程连接不部署本地 Mod,也不提供世界实例管理。
|
|
136
136
|
|
|
137
|
+
## Android 本机宿主
|
|
138
|
+
|
|
139
|
+
仅在手机上使用外部开发工具时,按[本机 App 接入](skills/mcpywrap/references/application-host.md)完成配对。绑定工作区使用标准命令,由 App 核心执行工程操作;离线时不回退本地构建。普通 Windows/macOS 项目继续使用原本的桌面流程,无需安装手机应用或配置手机权限。
|
|
140
|
+
|
|
137
141
|
## AI Agent 使用
|
|
138
142
|
|
|
139
|
-
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。
|
|
143
|
+
安装仓库内完整的 [mcpywrap Skill](skills/mcpywrap/SKILL.md)。可让 Agent 安装 `EaseCation/mcpywrap` 的 `skills/mcpywrap` 目录,或将整个文件夹复制到其技能目录。CLI 与 Skill 分别安装,安装 Python 包不会自动注册 Skill。0.6.2 起也可从已安装的发行包导出完整同版本 Skill:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
mcpy skill install --directory <Agent技能目录>/mcpywrap
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
更新已有技能目录加 `--replace`。只维护这份标准 Skill,按任务加载平台参考。
|
|
140
150
|
|
|
141
151
|
Skill 按当前任务选择参考,不要求 AI 通读产品文档。可直接描述:
|
|
142
152
|
|
|
@@ -93,6 +93,7 @@ skills/mcpywrap/assets/consumer-prepare/game/pyproject.toml
|
|
|
93
93
|
skills/mcpywrap/assets/consumer-prepare/game/behavior_pack/manifest.json
|
|
94
94
|
skills/mcpywrap/assets/consumer-prepare/game/data/items.json
|
|
95
95
|
skills/mcpywrap/assets/consumer-prepare/game/resource_pack/manifest.json
|
|
96
|
+
skills/mcpywrap/references/application-host.md
|
|
96
97
|
skills/mcpywrap/references/capture.md
|
|
97
98
|
skills/mcpywrap/references/consumer-prepare-author.md
|
|
98
99
|
skills/mcpywrap/references/consumer-prepare.md
|
|
@@ -152,6 +153,7 @@ tests/test_engine_backends.py
|
|
|
152
153
|
tests/test_engine_compatibility.py
|
|
153
154
|
tests/test_engine_discovery.py
|
|
154
155
|
tests/test_engine_install.py
|
|
156
|
+
tests/test_execution_boundaries.py
|
|
155
157
|
tests/test_game_window_skill.py
|
|
156
158
|
tests/test_git_projects.py
|
|
157
159
|
tests/test_host_input.py
|
|
@@ -165,10 +167,14 @@ tests/test_lifecycle_races.py
|
|
|
165
167
|
tests/test_local_auth_session.py
|
|
166
168
|
tests/test_local_dependencies.py
|
|
167
169
|
tests/test_lock_storage.py
|
|
170
|
+
tests/test_log_query.py
|
|
168
171
|
tests/test_log_stream.py
|
|
169
172
|
tests/test_macos_network.py
|
|
170
173
|
tests/test_manual_runtime_controls.py
|
|
171
174
|
tests/test_mcs_auth.py
|
|
175
|
+
tests/test_mobile_diagnostics.py
|
|
176
|
+
tests/test_mobile_host.py
|
|
177
|
+
tests/test_mobile_pairing.py
|
|
172
178
|
tests/test_modsdk_environment.py
|
|
173
179
|
tests/test_mouse.py
|
|
174
180
|
tests/test_native_input_sequence.py
|
|
@@ -185,6 +191,7 @@ tests/test_recordings.py
|
|
|
185
191
|
tests/test_remote.py
|
|
186
192
|
tests/test_remote_scripts.py
|
|
187
193
|
tests/test_resource_reload.py
|
|
194
|
+
tests/test_restricted_process_identity.py
|
|
188
195
|
tests/test_runtime_debug.py
|
|
189
196
|
tests/test_runtime_deployment.py
|
|
190
197
|
tests/test_runtime_groups.py
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"Loader.c": "f2f1f781ce82fc30c62a6ce22bb048d3b023f6ff500e6ec8228da28b4dbf1b4a"
|
|
12
12
|
},
|
|
13
13
|
"files": {
|
|
14
|
-
"Injector.exe": "
|
|
15
|
-
"Loader.dll": "
|
|
16
|
-
"McpyMcsAuth.dll": "
|
|
14
|
+
"Injector.exe": "05806de2bd8143d690f4136451dd495b892d47036481b5af170576edeacd1d60",
|
|
15
|
+
"Loader.dll": "7f45d503eb7f06bf535bb564cda2d3b8dc7c38118d4433f514ca57fe94040cce",
|
|
16
|
+
"McpyMcsAuth.dll": "d7bf8957a2bdcac92bbef80ff0ef5709ffdebfb9ece237a65caf8e244483acce",
|
|
17
17
|
"publisher.cer": "e5aa2ac39df40804ed8b08fe8e43c4a2b8923f0d02a360708486b2db5b6303d7"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"background_window_protocol": 1,
|
|
3
3
|
"architecture": "x64",
|
|
4
|
-
"dll_sha256": "
|
|
4
|
+
"dll_sha256": "e05ff1d30f809621dd104d87a63f26d7c725864a43c0989d9035ed2647879cb4",
|
|
5
5
|
"source_sha256": "03c1aab9e8d46c162e11f9448903c4907fbe3b87d1e77d8118b72c3e6b250ec5"
|
|
6
6
|
}
|
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"executable_sha256": "
|
|
2
|
+
"executable_sha256": "74a0ecfbcb266a0e6689ac8c981058cbd59382e9adf7a92aae832836e10e1b3b",
|
|
3
3
|
"source_sha256": "0c0b2a02bfecea68b824a486ac1d88cde6bd9fc8ee8e4c9a6939870bd8e710d1",
|
|
4
4
|
"sources": {
|
|
5
5
|
"video_capture.h": "f766d4234e41a554a8eb8ecae93fd818c2fb8877fbd1795f3915dcc8771e7909",
|
{mcpywrap-0.6.1 → mcpywrap-0.6.2}/desktop/mcpywrap_assets/window_capture/mcpy-window-capture.exe
RENAMED
|
Binary file
|
|
@@ -28,6 +28,8 @@ def check(outdir, require_native=False):
|
|
|
28
28
|
code = {p.relative_to(ROOT/'core').as_posix(): p.read_bytes()
|
|
29
29
|
for directory in ('mcpywrap_core', 'mcpywrap_host')
|
|
30
30
|
for p in (ROOT/'core'/directory).rglob('*.py') if '__pycache__' not in p.parts}
|
|
31
|
+
skill = {p.relative_to(ROOT/'skills/mcpywrap').as_posix():p.read_bytes()
|
|
32
|
+
for p in (ROOT/'skills/mcpywrap').rglob('*') if p.is_file() and '__pycache__' not in p.parts and p.suffix!='.pyc'}
|
|
31
33
|
namespaces = {}
|
|
32
34
|
for group, name in (('core', 'mcpywrap_core'), ('desktop', 'mcpywrap')):
|
|
33
35
|
prefix = name+'-'+version
|
|
@@ -45,6 +47,8 @@ def check(outdir, require_native=False):
|
|
|
45
47
|
for path, content in code.items():
|
|
46
48
|
assert archive.read(path) == content, 'Stale/missing source: '+path
|
|
47
49
|
assert not any(n.endswith(('.exe','.dll','.so','.pyd','.dylib','.pth')) for n in files)
|
|
50
|
+
bundled = {n.removeprefix('mcpywrap_core/skill/'):archive.read(n) for n in owned if n.startswith('mcpywrap_core/skill/')}
|
|
51
|
+
assert bundled == skill, 'Missing, stale or unexpected bundled skill files'
|
|
48
52
|
assert not any(n.endswith('/entry_points.txt') for n in files), 'Core must not own desktop console scripts'
|
|
49
53
|
assert all('extra ==' in r or r.split('>')[0] in ('click', 'packaging', 'tomli', 'tomli-w') for r in requirements)
|
|
50
54
|
else:
|
|
@@ -65,6 +69,9 @@ def check(outdir, require_native=False):
|
|
|
65
69
|
assert 'pyproject.toml' in paths
|
|
66
70
|
if group == 'core':
|
|
67
71
|
assert set(code) <= paths
|
|
72
|
+
for path,content in skill.items():
|
|
73
|
+
source=archive.extractfile(prefix+'/mcpywrap_core/skill/'+path)
|
|
74
|
+
assert source is not None and source.read()==content, 'Stale/missing sdist skill: '+path
|
|
68
75
|
else:
|
|
69
76
|
assert 'core/mcpywrap_core/_version.py' in paths
|
|
70
77
|
assert not any(p.startswith('desktop/mcpywrap/') for p in paths)
|
|
@@ -10,9 +10,10 @@ description: 使用 mcpywrap 管理《我的世界》中国版 Addon/地图、
|
|
|
10
10
|
## 调用约定
|
|
11
11
|
|
|
12
12
|
- 使用公开 CLI。示例中的 `mcpy` 指本机已确认的可执行命令;需要安装、升级或不确定执行位置时才读[安装与路由](references/setup.md)。
|
|
13
|
-
-
|
|
13
|
+
- 全局参数在子命令前;桌面本地示例:`mcpy --local --project <已有绝对目录> --non-interactive <命令>`。远程操作明确换成 `--remote <Windows服务HTTP地址>`,失败不回退本机;项目目录仍属于调用端。
|
|
14
14
|
- 有限命令加 `--json`;检查退出码和 `ok/error/code/hint`。`dev`、`runtime watch`、`serve` 是持续命令,不加 JSON。脚本尾参置于 `--` 之后。
|
|
15
15
|
- AI 启动本地游戏用 `run --no-gui --detach --json`,默认请求不聚焦的后台窗口,不操作 Qt/TUI;游戏仍需要图形会话和 GPU。macOS 旧包缺少后台协议时需升级运行包,不擅自改用 `--foreground`。保存返回的 session/group、项目及远程 endpoint,后续不换目标。
|
|
16
|
+
- mcpy 负责工程、会话和诊断取证,不解释 ModSDK 业务规则,也不根据日志推断模组健康;API/事件/UI 语义查对应版本的 ModSDK 文档,可使用独立文档 MCP。
|
|
16
17
|
- **业务代码禁止依赖 `mcpy.*`。** `mcpy.input/ui/player/api` 仅由 runtime install 临时注入调试会话,不是 ModSDK 或可分发依赖。宿主生成脚本用工具 Python 3,游戏代码仍受内置 Python/目标 ModSDK 约束。
|
|
17
18
|
- `running`、请求 `completed/triggered` 不等于玩法效果已验证。只结束自己创建或用户指定的会话;unknown 不盲目重发,busy 不清锁或杀无关进程。
|
|
18
19
|
|
|
@@ -31,6 +32,7 @@ description: 使用 mcpywrap 管理《我的世界》中国版 Addon/地图、
|
|
|
31
32
|
| 两名玩家、多世界、组查询/重试/停止/热更 | [双客户端联调](references/dual-clients.md) | 普通输入绑定选中的成员,再读输入页 |
|
|
32
33
|
| 连接外部服务器、可选 MCS 身份 | [网络会话](references/network-sessions.md) | 只有跨机执行才读 Windows 服务部署 |
|
|
33
34
|
| 配置/使用 Windows HTTP 远程服务 | [远程执行端](references/remote-testing.md) | 不支持远程世界实例、双客户端或资源部署 |
|
|
35
|
+
| 已配对 Android 宿主或明确准备手机开发环境 | [本机 App 接入](references/application-host.md) | 普通桌面项目无需阅读;绑定工作区按宿主能力执行 |
|
|
34
36
|
| 已有会话执行 Python、模块/资源热更、runtime watch | [运行时调试](references/runtime-debug.md) | 组操作转双客户端页;不必重读启动教程 |
|
|
35
37
|
| 移动/攻击/操作 UI、观察节点 | [统一输入](references/runtime-input.md) | 能力/参数按目标会话查询,不猜旧协议 |
|
|
36
38
|
| 截图、录制视频、下载、逐帧检查 | [画面采集](references/capture.md) | 需要操作玩家时才读输入协议 |
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 本机 App 接入
|
|
2
|
+
|
|
3
|
+
适用于 Minis、Termux 或其他能访问共享作品目录的开发工具。使用标准 `mcpy` CLI;HTTP `/v2` 是宿主协议,不需要为某个 Agent 安装专用构建器或常驻代理。
|
|
4
|
+
|
|
5
|
+
## 首次连接
|
|
6
|
+
|
|
7
|
+
1. 用户在 mcpy App「开发工具」开启开发连接。页面直接显示本机地址、配对码和有效期,可以复制配对码、标准命令,或可选的 AI 说明。普通终端无需 AI;模型账号只在使用 Agent 时需要。
|
|
8
|
+
2. 开发环境安装 Python、Git 和正式 `mcpywrap`。Minis 的 PRoot 使用 Alpine;优先使用其包管理器提供的 Python,再按标准 pip/uv 安装。解释器与虚拟环境放在该应用私有 Linux 目录,不能放在共享存储上。
|
|
9
|
+
3. 让用户将内部存储 `mcpy` 目录挂载为可读写目录。Minis 1.14 在设置 → Mount External Folders 中选择目录,并授予 Minis 自身的“所有文件访问”;仅有文件夹选择授权仍可能看不到其他 App 写入的文件。常见路径为 `/var/minis/mounts/mcpy`,以实际探针结果为准;不能把同名私有副本当作共享目录。
|
|
10
|
+
4. 完整安装技能并配对:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
mcpy skill install --directory /var/minis/skills/mcpywrap
|
|
14
|
+
mcpy host pair --url http://127.0.0.1:17653 --code <App给出的短时配对码> --workspace <实际挂载路径> --label Minis
|
|
15
|
+
mcpy --project <挂载路径>/projects/<作品> host status --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
已有技能更新使用 `skill install --replace`。配对会验证双向读写,并等待用户通过临时确认卡、通知动作或 App 页面允许连接。有悬浮权限时可直接在开发工具上方确认,无权限时使用通知;不需要常驻悬浮图标。命令将长期凭据保存到私有配置,不打印到对话;不要读取或复制该配置中的 token。
|
|
19
|
+
|
|
20
|
+
## 日常开发
|
|
21
|
+
|
|
22
|
+
工程命令绑定后继续使用 `init/mod/add/sync/build/package/run/status/logs/stop/runtime/screenshot` 标准命令,先查宿主能力;不加 `--local`。`modsdk` 补全库管理与 `skill` 安装属于开发端工具操作,不要求宿主在线,也不会作为工程降级入口。CLI 只负责输入、结果和宿主要求的工具准备,解析与组装必须在 App 的核心中执行。运行相关操作始终指定作品与会话。作品在开发环境中通过 `--project` 选择,App 的开发工具页不绑定作品。
|
|
23
|
+
|
|
24
|
+
客户端与 App 核心可能独立更新。需要执行准备阶段时,辅助代码必须与 App 的核心版本一致;按错误指引升级后重试,不修改配置绕过版本核对。Git/pip 安装、项目 hooks 和消费者准备在发起端执行,按 App 给出的阶段顺序交回结果,不能提前执行所有 hooks 或跳过 before_archive。
|
|
25
|
+
|
|
26
|
+
项目源码、依赖资源和输出都在真实共享目录内。原生工具扩展留在 PRoot 环境,不进入游戏行为包。当前宿主提供显式构建、Python 热更与重启;`dev`/`runtime watch` 未列为能力时,不创建另一套自动构建代理。修改后按标准生命周期应用源码;不要把启动成功或 running 当成模组已验证,以双端实际日志、断言和游戏截图验收。
|
|
27
|
+
|
|
28
|
+
游戏状态中的 `pip` 表示 Android 画中画(Picture-in-Picture),与 Python 的 pip 包管理器无关,也不表示脚本环境就绪。小窗验收检查 `pip=true`;游戏就绪与双端源码初始化另查世界状态、客户端/服务端探针和日志。游戏 Surface 截图用于检查完整渲染;要确认小窗位于 Agent 聊天页之上,还需检查手机实际窗口状态。
|
|
29
|
+
|
|
30
|
+
## 连接失败与恢复
|
|
31
|
+
|
|
32
|
+
- App 不可达:立即暂停自动操作,请用户手动打开 mcpy,确认开发连接已开启并暂时保持前台;也可向用户展示恢复链接 `mcpy://service/start`,不自行反复唤起。App 恢复此前已启用、尚未主动暂停的开发模式;否则显示连接页供用户开启。系统不允许唤起时请用户打开 App。仅凭连接失败不能判断进程退出、服务关闭还是端口错误;`host status` 的最后已知信息不是实时状态。不会改为本地核心执行。
|
|
33
|
+
- Git 环境:Minis 1.14 的部分 rootfs 带有无效的 `core.createObject=copy`。若 Git 原样报告 `invalid mode for object creation: copy`,在该 Linux 环境检查 `git config --show-origin --get core.createObject`,将报告位置的值修正为 Git 支持的 `rename`(系统配置可用 `git config --system core.createObject rename`);不要修改手机 App 或另一台电脑的 Git 配置。
|
|
34
|
+
- 配对恢复:新宿主声明 `pairing.resume=true`。CLI 将握手保存在私有配置目录,目录挂载修复或连接中断后重试同一条配对命令,会继续原请求;开始请求与验证回复丢失也不会重复授权。邀请有效一小时,发起请求及等待确认各有十五分钟窗口。明确过期或宿主身份变化时才重新生成配对码。
|
|
35
|
+
- 连接中断:`mcpy host status --recent --json` 离线查看本客户端最近的请求、操作 ID、最后观察到的状态及时间;`cached=true` 表示缓存。用 `mcpy host status --operation <id> --json` 查询原任务,或 `mcpy host resume --operation <id> --json` 继续等待并交回已完成的准备结果;不会新建操作。
|
|
36
|
+
- 准备阶段结果未知:CLI 在私有配置目录持久记录执行凭据。中断前已保存的结果可重传;只有“开始执行”而没有结果的阶段明确报 `preparation_unknown`,先检查原进程与输出,不重跑脚本。
|
|
37
|
+
- App 重启:未完成任务标为中断,不自动重放。结果未知时不要再次发送相同有副作用代码。
|
|
38
|
+
- 后台启动等待用户:`operation_state=waiting_user` 表示准备结果已保留。出现 `needs_user_action=true` 或 `next_action=ask_user` 时,立即停止自动重试,将 `user_message` 告诉用户并等待明确回复。不要继续 run/restart/build、新建世界、循环轮询,也不要改用其他唤起或权限绕过手段。检查 `prepared/game_started/reason/hint/actions`:没有游戏进程时不能声称游戏正在后台运行;已有进程时只需恢复画面。请用户切回 mcpy(也可打开 `mcpy://service/start`),App 自动继续同一次启动;收到用户明确确认后,再用原 `operation_id` 执行 `host resume` 查询。不重复构建、执行 hooks 或 `run --new`。未启动请求保留十五分钟,过期后使用原实例 ID 重试;已有进程不因画面恢复等待过期而退出。
|
|
39
|
+
- 撤销:`mcpy host disconnect` 撤销 App 授权及本地绑定;App 不可达时 `--offline` 只移除本地绑定,之后仍需在 App 撤销连接。
|
|
40
|
+
|
|
41
|
+
## 启动与失败诊断
|
|
42
|
+
|
|
43
|
+
运行前用 `mcpy host status --json` 或 `mcpy --project <作品目录> doctor --json` 查看 `launch_preconditions`。`ready=true` 表示没有已知阻塞项;厂商后台限制为 `unknown` 时仍须以真实启动证据为准。缺权限时按 `hint` 与系统设置 action/URI 提示用户,不用 Shizuku、辅助功能或另一个启动器绕过授权。
|
|
44
|
+
|
|
45
|
+
`status` 的 `reported_phase` 是原始上报,`state` 是结合进程与记录新鲜度的观察结果。没有进程确认时 `heartbeat=null`,部署的 `created_at` 不代表游戏存活。`unknown` 不等于系统拒绝,`unresponsive` 不等于崩溃;检查 `bootstrap`、`launch_receipt`、`process_alive`、`process_exit` 和 `engine_log_tail`。
|
|
46
|
+
|
|
47
|
+
小米系“显示悬浮窗”与“后台弹出界面”是不同权限。`vendor_background_launch_denied` 表示已查询到本应用的厂商权限被拒绝;按返回的设置入口,请用户允许“后台弹出界面”。其他系统无法查询时仍报告 `unknown`,不猜测。用户暂不授权也可以每次返回 App 继续已准备的请求;不要通过其他工具绕过授权。
|
|
48
|
+
|
|
49
|
+
`host resume --operation <id>` 不重放启动。`waiting_user` 时先提示用户操作,返回 App 后再查询原任务。若任务结果未知但同一会话后来已确认运行,可返回 `reconciled=true`;否则返回最新 `observation`。修复原因后若需重新启动,使用诊断中的原 `instance_id`,不要反复 `run --new`。
|
|
50
|
+
|
|
51
|
+
日志可用 `--source all` 聚合 game/engine;持续日志分源维护游标。截图省略 `--output` 时自动保存到作品 `captures/`,返回 `image` 路径且不覆盖已有文件;加载期截图不证明模组已就绪。实例列表使用 `run --list --json`,不要从协议操作名 `instances.list` 推断不存在的 CLI 子命令。
|
|
52
|
+
|
|
53
|
+
补全库版本目录使用 `mcpy modsdk --list --offline --json` 只读缓存,`--refresh` 显式联网刷新,整体最多等待 12 秒。ModSDK 文档 MCP 是独立工具,可按 https://github.com/MCNeteaseDevs/modsdk_mcp_server.git 的说明安装在开发环境;不加入 mcpy 或游戏包的依赖。
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## 筛选日志与会话交接
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
mcpy --project <作品目录> logs --session <sid> --filter '[组件前缀]' --summary --json
|
|
60
|
+
mcpy --project <作品目录> logs --session <sid> --source client --filter '控件路径' --tail 50 --json
|
|
61
|
+
mcpy --project <作品目录> logs --session <sid> --since-ms <Unix毫秒> --until-ms <Unix毫秒> --json
|
|
62
|
+
mcpy --project <作品目录> logs --session <sid> --follow --filter '[组件前缀]' --format jsonl
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`--filter` 是区分大小写的字面匹配,不是正则表达式。先筛选,再取 `tail`;`--summary` 统计扫描范围内完全相同的消息,客户端和服务端分开计数。统计模式不与持续跟随同用。每源每次最多扫描 512 KiB,响应提供 `available/scanned_lines/matched_lines/scan_truncated`;摘要超过 `tail` 时有 `summary_truncated`。无匹配只能说明该范围内没找到,不能证明模组正常。时间筛选不猜测缺少时间戳的消息,排除数量见 `excluded_without_timestamp`。持续读取按原始文件游标推进,未匹配记录也会推进游标。
|
|
66
|
+
|
|
67
|
+
宿主在 `log_query` 中声明筛选能力,旧 App 缺失能力时明确要求升级,不静默忽略参数。桌面本地也支持字面过滤和统计,快照扫描 512 KiB,持续跟随首次历史读取为 256 KiB;Windows 旧 HTTP 服务继续使用原接口,由客户端筛选最近 10000 行,并明确返回该扫描范围。端侧与毫秒时间筛选需要支持结构化日志的宿主,不能从桌面原始文本猜测。
|
|
68
|
+
|
|
69
|
+
`restart` 返回新 `session`、`previous_session` 和 `session_changed=true`。立即更新后续日志、截图、Python 调试的目标;旧会话仍用于历史查询。等待用户恢复的重启请求同样携带会话交接信息。
|
|
70
|
+
|
|
71
|
+
## 启动证据与等待策略
|
|
72
|
+
|
|
73
|
+
App 的 `run/restart --launch-timeout <秒>` 配置进程及画中画的确认等待,默认 45 秒,范围 5–120 秒;须由 `launch_options.ack_timeout` 声明支持。世界加载使用独立的 300 秒限制,超过确认等待不会自动重放启动、重新构建或新建世界。桌面及旧 Windows 服务未声明此参数时明确拒绝,保持原启动策略。
|
|
74
|
+
|
|
75
|
+
`status.launch_timeline` 返回已记录的部署、Activity、引擎及游戏阶段,各项有 `observed_at/age_ms/elapsed_ms/since_previous_ms`,保留有界历史并报告 `truncated`。旧会话只返回真实存在的记录,不补造阶段。`clientReady/serverReady` 描述宿主调试初始化证据,不证明任何业务系统或 UI 正常。
|
|
76
|
+
|
|
77
|
+
`launch_preconditions.checks.background_launch.last_observation` 提供最近后台尝试的结果、时间、年龄和 `same_environment`。前台恢复成功不会覆盖先前后台失败;历史结果不能代替当前权限检查,`unknown` 仍表示无法判断当前权限。App 更新或系统环境变化后的历史只作参考。离线服务信息和最近操作记录提供查询时计算的 `age_ms`,不把缓存包装成实时状态。
|
|
78
|
+
|
|
79
|
+
mcpy 提供工程管理、会话控制和原始诊断证据;ModSDK API 语义、UI 文件规则及业务修复由独立文档 MCP 和开发者负责。不要根据“无报错”“画面上有文字”推断业务代码已生效,也不要为模组引入 mcpy 专属健康上报依赖。
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
需要用户接手的结果使用统一字段:`needs_user_action=true`、`next_action=ask_user`、`retryable=false`、`retry_policy=after_user_confirmation`、`user_message` 和 `agent_instruction`。宿主不可达时也适用,但不能据此声称准备已完成、游戏已启动或权限确实被拒绝。有 `operation_id` 时附带 `resume_command`;先等用户确认,再查询原任务,不重新提交有副作用操作。普通终端和 JSON 输出都包含该指引,即使 Agent 没有加载本 skill,也应能直接看到下一步。
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
后台连接容易暂停时,请用户在 mcpy 的“开发工具”Tab 打开省电设置。小米系统选择“无限制”;该设置影响后台联网和 CPU,与“后台弹出界面”权限分开。只引导有外部工具连接需求的用户,不要求普通编辑或手动运行也授权。后台策略无法通过标准接口可靠核实时保持未知,不把打开过设置当作已经授权。Minis、Termux、编辑器与 HTTP 客户端使用相同的连接机制;AI 只是可选的操作方式。
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
宿主专用的 `--launch-timeout`、毫秒时间筛选及客户端/服务端日志来源,只在当前工作区已绑定宿主时显示在对应命令帮助中。此选择与运行 CLI 的操作系统无关:桌面也可以操作已绑定手机的工作区,手机中的普通本地工作区也不会自动变成宿主模式。`host status --binding-only --json` 只读当前工作区绑定,不访问宿主。
|
|
@@ -28,7 +28,7 @@ mcpy --local --project <项目> --non-interactive runtime capabilities --session
|
|
|
28
28
|
| 启动响应丢失 | 同一项目 status --list 找回,不立刻再次 --new |
|
|
29
29
|
| 明确删除世界 | `run --delete <前缀> --force --json`;仅用户要求删除时使用,不作为修启动的方法 |
|
|
30
30
|
|
|
31
|
-
restart 会产生新 session,世界 ID 和窗口策略保留;需要 MCS 身份的会话按提示重新 run 并提供本次身份。旧 session 可查历史,不能继续向它发送输入。实例是世界,session 是一次进程运行,group 是联调成员关系。
|
|
31
|
+
单会话 restart 返回 `previous_session` 与 `session_changed=true`,后续命令立即改用返回的新 `session`。restart 会产生新 session,世界 ID 和窗口策略保留;需要 MCS 身份的会话按提示重新 run 并提供本次身份。旧 session 可查历史,不能继续向它发送输入。实例是世界,session 是一次进程运行,group 是联调成员关系。
|
|
32
32
|
|
|
33
33
|
## 持续日志
|
|
34
34
|
|
|
@@ -81,3 +81,6 @@ python <skill>/scripts/smoke.py --command <本机mcpy命令> --project <项目>
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
smoke 会 package、创建新世界、等待就绪、各执行一次探针并清理本次创建的会话。只打包省略 --game;无业务标记/探针的结果不能当作业务验收。含预处理的项目 package 与 run 各执行自己的 hook,这是两个操作,不保证生成器只执行一次。
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
日志定位可用 `logs --session <sid> --filter '[组件前缀]' --summary --json`。过滤为区分大小写的字面匹配,先过滤后取 tail;统计只覆盖所报告的扫描范围,不解释业务错误。持续跟随可用 `--follow --filter`,不可加 `--summary`。桌面本地快照筛选最多读取最近 512 KiB(持续跟随首次历史读取为 256 KiB),`scan_truncated=true` 表示不是全量。
|
|
@@ -71,3 +71,8 @@ mcpy --local --project <项目> --non-interactive run --engine-version 3.10.0.42
|
|
|
71
71
|
| engine_version | MCPY_ENGINE_VERSION |
|
|
72
72
|
|
|
73
73
|
覆盖优先级:CLI > 环境变量 > 项目配置;项目相对路径按项目根解析,CLI/env 相对路径按调用目录解析。团队通常只提交版本,本机路径放 env。macOS 的固定运行包/引擎绑定按[平台页](macos-local.md),不能套用 Windows EXE 规则。远程这些路径由 Windows serve 配置,调用端不直接覆盖 EXE/下载目录。
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## 已绑定其他执行宿主的工作区
|
|
77
|
+
|
|
78
|
+
只有当前工作区明确绑定 Android 本机宿主时,才按[本机 App 接入](application-host.md)选择执行方式。`mcpy host status --binding-only --json` 只检查本地绑定,不访问网络;存在其他工作区的绑定不改变当前桌面项目。标准 bootstrap 只在当前工作区已绑定时访问对应宿主,宿主不可达不会回退本地。手机专用权限和恢复步骤不适用于普通 Windows/macOS 项目。
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Install/reuse mcpywrap with uv and report
|
|
1
|
+
"""Install/reuse mcpywrap with uv and report capabilities of the selected execution host."""
|
|
2
2
|
import argparse
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
@@ -24,6 +24,26 @@ def capabilities(command, remote=None, local=False):
|
|
|
24
24
|
basic = ('--project', '--non-interactive', '--json', 'status', 'logs', 'stop', 'package')
|
|
25
25
|
if help_result.returncode or any(k not in help_text for k in basic):
|
|
26
26
|
raise ValueError('现有 CLI 缺少基础能力;显式选择版本并使用 --upgrade')
|
|
27
|
+
# Probe workspace binding first. A mobile workspace must never run local doctor/build.
|
|
28
|
+
if re.search(r'^\s+host\s', help_text, re.M):
|
|
29
|
+
result = invoke([command, '--non-interactive', 'host', 'status', '--binding-only', '--json'])
|
|
30
|
+
try:
|
|
31
|
+
host = json.loads(result.stdout)
|
|
32
|
+
except ValueError:
|
|
33
|
+
raise ValueError('无法检查工作区宿主绑定;请运行 mcpy host status --json') from None
|
|
34
|
+
if result.returncode and not host.get('bound_for_project'): raise ValueError(host.get('error') or '无法检查工作区绑定,请升级 CLI 后重试')
|
|
35
|
+
if host.get('bound_for_project'):
|
|
36
|
+
if local or (remote and remote.rstrip('/') != host.get('endpoint')):
|
|
37
|
+
raise ValueError('此工作区已绑定 mcpy App,不能改用 --local 或其他远端')
|
|
38
|
+
result=invoke([command,'--non-interactive','host','status','--json'])
|
|
39
|
+
try: host=json.loads(result.stdout)
|
|
40
|
+
except ValueError: raise ValueError('已绑定宿主没有返回有效能力;不会回退本地') from None
|
|
41
|
+
if result.returncode or not host.get('ok'):
|
|
42
|
+
raise ValueError(host.get('hint') or host.get('error') or '已绑定的 App 无法连接;不会回退本地')
|
|
43
|
+
operations = host.get('operations', [])
|
|
44
|
+
return {'capabilities': operations, 'local_capabilities': [], 'local_platform': sys.platform,
|
|
45
|
+
'execution': 'host', 'application_host': host, 'remote': None,
|
|
46
|
+
'mcs_auth': {'supported': False}, 'media': None, 'local_runtime': None}
|
|
27
47
|
local_flag = ['--local'] if '--local' in help_text else []
|
|
28
48
|
run_help = invoke([command, 'run', '--help']).stdout
|
|
29
49
|
features = ['project', 'json', 'sessions', 'package']
|
|
@@ -171,7 +191,13 @@ def main(argv=None):
|
|
|
171
191
|
for required in args.require_capability:
|
|
172
192
|
game_capability = required in GAME_CAPABILITIES
|
|
173
193
|
location = '本机'
|
|
174
|
-
if
|
|
194
|
+
if report.get('application_host') is not None:
|
|
195
|
+
location = '已配对的 App'
|
|
196
|
+
available = report['application_host'].get('operations', [])
|
|
197
|
+
auth = {'supported': False}
|
|
198
|
+
required = {'py': 'runtime.py', 'reload': 'runtime.reload', 'sessions': 'status',
|
|
199
|
+
'runtime': 'runtime.capabilities', 'project': 'build'}.get(required, required)
|
|
200
|
+
elif remote is not None and game_capability:
|
|
175
201
|
location = '远端 Windows'
|
|
176
202
|
if not remote.get('ok'):
|
|
177
203
|
raise ValueError(remote.get('error') or '远端能力不可用')
|
|
@@ -181,7 +207,7 @@ def main(argv=None):
|
|
|
181
207
|
else:
|
|
182
208
|
available, auth = report['local_capabilities'], report['mcs_auth']
|
|
183
209
|
if required not in available:
|
|
184
|
-
if remote is None and game_capability and os.name != 'nt':
|
|
210
|
+
if remote is None and not report.get('application_host') and game_capability and os.name != 'nt':
|
|
185
211
|
raise ValueError('本机后端不提供此游戏能力;请检查 doctor --capabilities,或用 --remote 配置 Windows 服务地址')
|
|
186
212
|
if required == 'serve' and os.name != 'nt':
|
|
187
213
|
raise ValueError('serve 仅能在 Windows 控制台启动;macOS 请检查 remote-client 和远端能力')
|
|
@@ -50,6 +50,18 @@ class BuildRecovery(unittest.TestCase):
|
|
|
50
50
|
self.target.mkdir()
|
|
51
51
|
(self.target / 'known-good.txt').write_text('previous build', encoding='utf-8')
|
|
52
52
|
|
|
53
|
+
def test_source_change_during_assembly_does_not_replace_previous_build(self):
|
|
54
|
+
from mcpywrap_core.builders.assembly import assemble_addon
|
|
55
|
+
def change_source(packs, staged):
|
|
56
|
+
assemble_addon(packs, staged)
|
|
57
|
+
(self.source / 'behavior_pack/marker.py').write_text('CHANGED = True')
|
|
58
|
+
with patch('mcpywrap_core.builders.project_builder.assemble_addon', side_effect=change_source):
|
|
59
|
+
ok, error = AddonProjectBuilder(self.source, self.target).build()
|
|
60
|
+
self.assertFalse(ok)
|
|
61
|
+
self.assertIn('源码发生变化', error)
|
|
62
|
+
self.assertEqual((self.target / 'known-good.txt').read_text(), 'previous build')
|
|
63
|
+
self.assertFalse(list(self.source.glob('.mcpy-build-*')))
|
|
64
|
+
|
|
53
65
|
def test_install_failure_restores_old_build(self):
|
|
54
66
|
replace = os.replace
|
|
55
67
|
def fail_install(src, dst):
|
|
@@ -39,6 +39,15 @@ class CoreApiTests(unittest.TestCase):
|
|
|
39
39
|
with zipfile.ZipFile(artifact) as archive:
|
|
40
40
|
self.assertEqual(entries, {name: archive.read(name) for name in archive.namelist()})
|
|
41
41
|
|
|
42
|
+
def test_initialization_preserves_host_declared_metadata_only(self):
|
|
43
|
+
root=self.root/'host-project';root.mkdir()
|
|
44
|
+
metadata=root/'.host-project.json';metadata.write_text('{"id":"stable"}')
|
|
45
|
+
host=Core(HostServices(python_executable=None,allow_processes=False,project_metadata_files=('.host-project.json',)))
|
|
46
|
+
host.execute('init',root,name='host-project',project_type='addon')
|
|
47
|
+
self.assertEqual(metadata.read_text(),'{"id":"stable"}')
|
|
48
|
+
unrelated=self.root/'unrelated';unrelated.mkdir();(unrelated/'business.txt').write_text('keep')
|
|
49
|
+
with self.assertRaisesRegex(ValueError,'非空目录'):host.execute('init',unrelated,name='unrelated',project_type='addon')
|
|
50
|
+
|
|
42
51
|
def test_local_sync_works_without_a_python_executable(self):
|
|
43
52
|
result = self.core.execute('sync', self.project)
|
|
44
53
|
self.assertTrue(result['sources_synced'])
|
|
@@ -60,6 +69,14 @@ class CoreApiTests(unittest.TestCase):
|
|
|
60
69
|
self.assertFalse(self.core.request(request)['ok'])
|
|
61
70
|
self.assertFalse((self.project/'build').exists())
|
|
62
71
|
|
|
72
|
+
def test_project_operation_validation_keeps_the_structured_error_envelope(self):
|
|
73
|
+
for operation,options in [('mod',{}),('mod',{'name':['invalid']}),('script.run',{}),
|
|
74
|
+
('script.run',{'name':'build','args':'not-a-list'}),('script.list',{'effective':'yes'})]:
|
|
75
|
+
with self.subTest(operation=operation,options=options):
|
|
76
|
+
result=self.core.request({'core_api':1,'operation':operation,'project':str(self.project),'options':options})
|
|
77
|
+
self.assertFalse(result['ok']);self.assertIn('error',result);self.assertIn('code',result)
|
|
78
|
+
self.assertFalse((self.project/'build').exists())
|
|
79
|
+
|
|
63
80
|
def test_offline_operations_do_not_import_desktop_extras(self):
|
|
64
81
|
code = '''
|
|
65
82
|
import importlib.abc, sys, tempfile
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Mobile hints must be selected by workspace binding, never leak into desktop commands."""
|
|
2
|
+
import importlib.util
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import sys
|
|
7
|
+
import tempfile
|
|
8
|
+
import unittest
|
|
9
|
+
from unittest.mock import Mock, patch
|
|
10
|
+
from click.testing import CliRunner
|
|
11
|
+
from mcpywrap_core.cli import cli
|
|
12
|
+
from mcpywrap_core.mobile_host import profiles
|
|
13
|
+
from mcpywrap_core.mobile_host.client import Client
|
|
14
|
+
from mcpywrap_core.remote.protocol import RemoteError
|
|
15
|
+
|
|
16
|
+
ROOT=Path(__file__).resolve().parents[1]
|
|
17
|
+
PHONE_TERMS=('画中画','无限制','切回 mcpy','mcpy://service/start','needs_user_action','ask_user','Minis','PRoot')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExecutionBoundaryTests(unittest.TestCase):
|
|
21
|
+
def setUp(self):
|
|
22
|
+
temp=tempfile.TemporaryDirectory();self.addCleanup(temp.cleanup)
|
|
23
|
+
self.root=Path(temp.name);self.mobile=self.root/'mobile';self.desktop=self.root/'desktop'
|
|
24
|
+
self.mobile.mkdir();self.desktop.mkdir()
|
|
25
|
+
env=patch.dict(os.environ,MCPY_HOST_CONFIG=str(self.root/'private/hosts.json'))
|
|
26
|
+
env.start();self.addCleanup(env.stop)
|
|
27
|
+
profiles.bind({'workspace':str(self.mobile),'endpoint':'http://127.0.0.1:17653','token':'test-token',
|
|
28
|
+
'installation_id':'phone','client_id':'client'})
|
|
29
|
+
|
|
30
|
+
def invoke(self,project,*args):
|
|
31
|
+
return CliRunner().invoke(cli,['--project',str(project),*args])
|
|
32
|
+
|
|
33
|
+
def assert_desktop(self,text):
|
|
34
|
+
for term in PHONE_TERMS:self.assertNotIn(term,text)
|
|
35
|
+
|
|
36
|
+
def test_help_exposes_only_target_options_on_every_client_platform(self):
|
|
37
|
+
with patch.object(Client,'request',side_effect=AssertionError('help must not contact a host')):
|
|
38
|
+
for platform in ('win32','darwin','linux'):
|
|
39
|
+
with self.subTest(platform=platform),patch.object(sys,'platform',platform):
|
|
40
|
+
for command in ('run','restart','logs'):
|
|
41
|
+
plain=self.invoke(self.desktop,command,'--help')
|
|
42
|
+
self.assertEqual(plain.exit_code,0,plain.output);self.assert_desktop(plain.output)
|
|
43
|
+
self.assertNotIn('--launch-timeout',plain.output);self.assertNotIn('--since-ms',plain.output)
|
|
44
|
+
if command=='logs':self.assertIn('[game|engine|worker|all]',plain.output)
|
|
45
|
+
bound=self.invoke(self.mobile,command,'--help')
|
|
46
|
+
self.assertEqual(bound.exit_code,0,bound.output)
|
|
47
|
+
self.assertIn('--since-ms' if command=='logs' else '--launch-timeout',bound.output)
|
|
48
|
+
|
|
49
|
+
def test_desktop_logs_still_use_local_reader_with_an_unrelated_phone_binding(self):
|
|
50
|
+
with patch.object(Client,'request',side_effect=AssertionError('unrelated phone contacted')),patch('mcpywrap_core.mcstudio.sessions.logs',return_value='desktop output\n') as logs:
|
|
51
|
+
result=self.invoke(self.desktop,'logs','--session','a'*32,'--json')
|
|
52
|
+
self.assertEqual(result.exit_code,0,result.output)
|
|
53
|
+
self.assertEqual(json.loads(result.stdout)['text'],'desktop output\n')
|
|
54
|
+
self.assert_desktop(result.output);logs.assert_called_once()
|
|
55
|
+
|
|
56
|
+
def test_desktop_without_a_home_directory_does_not_require_phone_configuration(self):
|
|
57
|
+
with patch.dict(os.environ,{},clear=True),patch.object(Path,'home',side_effect=RuntimeError('No user home')),patch('mcpywrap_core.mcstudio.sessions.logs',return_value='desktop\n'):
|
|
58
|
+
result=self.invoke(self.desktop,'logs','--session','a'*32,'--json')
|
|
59
|
+
self.assertEqual(result.exit_code,0,result.output);self.assert_desktop(result.output)
|
|
60
|
+
|
|
61
|
+
def test_explicit_binding_location_does_not_eagerly_resolve_user_home(self):
|
|
62
|
+
with patch.object(Path,'home',side_effect=RuntimeError('No user home')):
|
|
63
|
+
self.assertEqual(profiles.read()[0]['installation_id'],'phone')
|
|
64
|
+
|
|
65
|
+
def test_desktop_failure_does_not_acquire_mobile_permission_hints(self):
|
|
66
|
+
with patch.object(Client,'request',side_effect=AssertionError('unrelated phone contacted')),patch('mcpywrap_core.mcstudio.sessions.stop',side_effect=OSError('desktop process unavailable')):
|
|
67
|
+
result=self.invoke(self.desktop,'stop','--session','a'*32,'--json')
|
|
68
|
+
self.assertNotEqual(result.exit_code,0);self.assert_desktop(result.output)
|
|
69
|
+
self.assertNotIn('needs_user_action',json.loads(result.stdout))
|
|
70
|
+
|
|
71
|
+
def test_windows_remote_failure_remains_a_windows_remote_failure(self):
|
|
72
|
+
for code in ('remote_unavailable','host_unavailable'):
|
|
73
|
+
with self.subTest(code=code),patch.object(Client,'request',side_effect=AssertionError('phone contacted')),patch('mcpywrap_core.remote.client.Client.request',side_effect=RemoteError('Windows host unavailable',code)):
|
|
74
|
+
result=self.invoke(self.desktop,'--remote','http://127.0.0.1:17651','status','--session','a'*32,'--json')
|
|
75
|
+
self.assertNotEqual(result.exit_code,0);self.assert_desktop(result.output)
|
|
76
|
+
self.assertEqual(json.loads(result.stdout)['code'],code)
|
|
77
|
+
|
|
78
|
+
def test_binding_probe_never_falls_back_to_another_workspace_or_network(self):
|
|
79
|
+
with patch.object(Client,'capabilities',side_effect=AssertionError('binding probe contacted host')):
|
|
80
|
+
unbound=json.loads(self.invoke(self.desktop,'host','status','--binding-only','--json').stdout)
|
|
81
|
+
bound=json.loads(self.invoke(self.mobile,'host','status','--binding-only','--json').stdout)
|
|
82
|
+
self.assertFalse(unbound['bound_for_project']);self.assertNotIn('endpoint',unbound)
|
|
83
|
+
self.assertTrue(bound['bound_for_project']);self.assertNotIn('token',bound)
|
|
84
|
+
|
|
85
|
+
def test_bootstrap_unbound_workspace_never_asks_phone_for_live_status(self):
|
|
86
|
+
spec=importlib.util.spec_from_file_location('bootstrap_boundary',ROOT/'skills/mcpywrap/scripts/bootstrap.py')
|
|
87
|
+
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
|
88
|
+
def invoke(args):
|
|
89
|
+
if args==['mcpy','--help']:
|
|
90
|
+
return Mock(returncode=0,stdout='--project --non-interactive --json status logs stop package\n host connection')
|
|
91
|
+
if 'host' in args:
|
|
92
|
+
self.assertIn('--binding-only',args)
|
|
93
|
+
return Mock(returncode=0,stdout=json.dumps({'ok':True,'bound_for_project':False}))
|
|
94
|
+
return Mock(returncode=0,stdout='--no-gui --detach')
|
|
95
|
+
with patch.object(module,'invoke',side_effect=invoke):value=module.capabilities('mcpy')
|
|
96
|
+
self.assertNotEqual(value.get('execution'),'host');self.assertNotIn('application_host',value)
|
|
97
|
+
|
|
98
|
+
def test_standard_skill_keeps_phone_procedures_in_opt_in_reference(self):
|
|
99
|
+
main=(ROOT/'skills/mcpywrap/SKILL.md').read_text()
|
|
100
|
+
for term in ('needs_user_action','无限制','mcpy://service/start','/var/minis'):
|
|
101
|
+
self.assertNotIn(term,main)
|
|
102
|
+
self.assertIn('application-host.md',main)
|
|
103
|
+
phone=(ROOT/'skills/mcpywrap/references/application-host.md').read_text()
|
|
104
|
+
self.assertIn('needs_user_action',phone);self.assertIn('无限制',phone)
|