mcpywrap-core 0.6.0__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.
Files changed (195) hide show
  1. mcpywrap_core-0.6.0/LICENSE +21 -0
  2. mcpywrap_core-0.6.0/MANIFEST.in +6 -0
  3. mcpywrap_core-0.6.0/PKG-INFO +33 -0
  4. mcpywrap_core-0.6.0/README.md +11 -0
  5. mcpywrap_core-0.6.0/THIRD_PARTY_NOTICES.md +35 -0
  6. mcpywrap_core-0.6.0/mcpywrap_core/__init__.py +2 -0
  7. mcpywrap_core-0.6.0/mcpywrap_core/__main__.py +18 -0
  8. mcpywrap_core-0.6.0/mcpywrap_core/_script_worker.py +278 -0
  9. mcpywrap_core-0.6.0/mcpywrap_core/_version.py +1 -0
  10. mcpywrap_core-0.6.0/mcpywrap_core/api.py +69 -0
  11. mcpywrap_core-0.6.0/mcpywrap_core/builders/AddonsPack.py +310 -0
  12. mcpywrap_core-0.6.0/mcpywrap_core/builders/MapPack.py +171 -0
  13. mcpywrap_core-0.6.0/mcpywrap_core/builders/__init__.py +20 -0
  14. mcpywrap_core-0.6.0/mcpywrap_core/builders/assembly.py +100 -0
  15. mcpywrap_core-0.6.0/mcpywrap_core/builders/dependency_manager.py +239 -0
  16. mcpywrap_core-0.6.0/mcpywrap_core/builders/file_merge.py +260 -0
  17. mcpywrap_core-0.6.0/mcpywrap_core/builders/project_builder.py +182 -0
  18. mcpywrap_core-0.6.0/mcpywrap_core/builders/watcher.py +162 -0
  19. mcpywrap_core-0.6.0/mcpywrap_core/cli.py +75 -0
  20. mcpywrap_core-0.6.0/mcpywrap_core/code_libraries.py +201 -0
  21. mcpywrap_core-0.6.0/mcpywrap_core/command_context.py +242 -0
  22. mcpywrap_core-0.6.0/mcpywrap_core/commands/__init__.py +1 -0
  23. mcpywrap_core-0.6.0/mcpywrap_core/commands/add_cmd.py +76 -0
  24. mcpywrap_core-0.6.0/mcpywrap_core/commands/build_cmd.py +15 -0
  25. mcpywrap_core-0.6.0/mcpywrap_core/commands/connect_cmd.py +46 -0
  26. mcpywrap_core-0.6.0/mcpywrap_core/commands/default_cmd.py +14 -0
  27. mcpywrap_core-0.6.0/mcpywrap_core/commands/dependency_prompt.py +45 -0
  28. mcpywrap_core-0.6.0/mcpywrap_core/commands/deployment_cmd.py +115 -0
  29. mcpywrap_core-0.6.0/mcpywrap_core/commands/deployment_output.py +289 -0
  30. mcpywrap_core-0.6.0/mcpywrap_core/commands/desktop_cmd.py +104 -0
  31. mcpywrap_core-0.6.0/mcpywrap_core/commands/dev_cmd.py +108 -0
  32. mcpywrap_core-0.6.0/mcpywrap_core/commands/doctor_cmd.py +32 -0
  33. mcpywrap_core-0.6.0/mcpywrap_core/commands/edit_cmd.py +109 -0
  34. mcpywrap_core-0.6.0/mcpywrap_core/commands/engine_cmd.py +90 -0
  35. mcpywrap_core-0.6.0/mcpywrap_core/commands/init_cmd.py +48 -0
  36. mcpywrap_core-0.6.0/mcpywrap_core/commands/mod_cmd.py +53 -0
  37. mcpywrap_core-0.6.0/mcpywrap_core/commands/modsdk_cmd.py +41 -0
  38. mcpywrap_core-0.6.0/mcpywrap_core/commands/package_cmd.py +16 -0
  39. mcpywrap_core-0.6.0/mcpywrap_core/commands/publish_cmd.py +51 -0
  40. mcpywrap_core-0.6.0/mcpywrap_core/commands/record_cmd.py +85 -0
  41. mcpywrap_core-0.6.0/mcpywrap_core/commands/remove_cmd.py +62 -0
  42. mcpywrap_core-0.6.0/mcpywrap_core/commands/run_cmd.py +169 -0
  43. mcpywrap_core-0.6.0/mcpywrap_core/commands/runtime_cmd.py +150 -0
  44. mcpywrap_core-0.6.0/mcpywrap_core/commands/runtime_input_cmd.py +86 -0
  45. mcpywrap_core-0.6.0/mcpywrap_core/commands/runtime_player_cmd.py +192 -0
  46. mcpywrap_core-0.6.0/mcpywrap_core/commands/runtime_ui_cmd.py +121 -0
  47. mcpywrap_core-0.6.0/mcpywrap_core/commands/script_cmd.py +54 -0
  48. mcpywrap_core-0.6.0/mcpywrap_core/commands/serve_cmd.py +39 -0
  49. mcpywrap_core-0.6.0/mcpywrap_core/commands/session_cmd.py +95 -0
  50. mcpywrap_core-0.6.0/mcpywrap_core/commands/sync_cmd.py +29 -0
  51. mcpywrap_core-0.6.0/mcpywrap_core/commands/ui_cmd.py +36 -0
  52. mcpywrap_core-0.6.0/mcpywrap_core/config.py +254 -0
  53. mcpywrap_core-0.6.0/mcpywrap_core/consumer_prepare.py +471 -0
  54. mcpywrap_core-0.6.0/mcpywrap_core/core-contract.json +6 -0
  55. mcpywrap_core-0.6.0/mcpywrap_core/custom_packaging.py +41 -0
  56. mcpywrap_core-0.6.0/mcpywrap_core/dependencies.py +326 -0
  57. mcpywrap_core-0.6.0/mcpywrap_core/engines/__init__.py +1 -0
  58. mcpywrap_core-0.6.0/mcpywrap_core/engines/android_icon.py +186 -0
  59. mcpywrap_core-0.6.0/mcpywrap_core/engines/apk_profile.py +90 -0
  60. mcpywrap_core-0.6.0/mcpywrap_core/engines/backend.py +134 -0
  61. mcpywrap_core-0.6.0/mcpywrap_core/engines/host.py +32 -0
  62. mcpywrap_core-0.6.0/mcpywrap_core/engines/install.py +665 -0
  63. mcpywrap_core-0.6.0/mcpywrap_core/engines/macos.py +483 -0
  64. mcpywrap_core-0.6.0/mcpywrap_core/engines/macos_icons.py +159 -0
  65. mcpywrap_core-0.6.0/mcpywrap_core/engines/macos_network.py +58 -0
  66. mcpywrap_core-0.6.0/mcpywrap_core/engines/official.py +129 -0
  67. mcpywrap_core-0.6.0/mcpywrap_core/engines/preferences.py +146 -0
  68. mcpywrap_core-0.6.0/mcpywrap_core/engines/tui.py +110 -0
  69. mcpywrap_core-0.6.0/mcpywrap_core/engines/windows.py +144 -0
  70. mcpywrap_core-0.6.0/mcpywrap_core/framework_presets.py +22 -0
  71. mcpywrap_core-0.6.0/mcpywrap_core/frameworks.py +77 -0
  72. mcpywrap_core-0.6.0/mcpywrap_core/git_cache.py +154 -0
  73. mcpywrap_core-0.6.0/mcpywrap_core/git_projects.py +266 -0
  74. mcpywrap_core-0.6.0/mcpywrap_core/host.py +93 -0
  75. mcpywrap_core-0.6.0/mcpywrap_core/host_assets.py +14 -0
  76. mcpywrap_core-0.6.0/mcpywrap_core/input_executor.py +246 -0
  77. mcpywrap_core-0.6.0/mcpywrap_core/input_plan.py +257 -0
  78. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/SimpleMonitor.py +51 -0
  79. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/__init__.py +1 -0
  80. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/auth_interaction.py +88 -0
  81. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/bridge_assets.py +85 -0
  82. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/deployment_links.py +670 -0
  83. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/diagnostics.py +21 -0
  84. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/discovery.py +337 -0
  85. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/editor.py +81 -0
  86. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/file_logs.py +66 -0
  87. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/game.py +256 -0
  88. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/host_input.py +252 -0
  89. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/hot_reload.py +218 -0
  90. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/image.py +20 -0
  91. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/input_sequence.py +204 -0
  92. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/launcher_control.py +56 -0
  93. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/launcher_python.py +134 -0
  94. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/log_colorizer.py +252 -0
  95. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/log_protocol.py +74 -0
  96. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/log_stream.py +136 -0
  97. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/mcs.py +149 -0
  98. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/mcs_auth.py +196 -0
  99. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/network.py +187 -0
  100. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/private_logs.py +70 -0
  101. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/processes.py +41 -0
  102. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/recording_worker.py +154 -0
  103. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/recordings.py +351 -0
  104. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_cppconfig.py +249 -0
  105. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_debug.py +437 -0
  106. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_input_payload.py +277 -0
  107. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_key_timeline_payload.py +229 -0
  108. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_player_payload.py +713 -0
  109. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_ui.py +212 -0
  110. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_ui_outline.py +424 -0
  111. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/runtime_ui_payload.py +560 -0
  112. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/screenshot.py +94 -0
  113. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/session_capabilities.py +54 -0
  114. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/session_worker.py +177 -0
  115. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/sessions.py +285 -0
  116. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/studio_server.py +363 -0
  117. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/studio_server_ui.py +126 -0
  118. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/symlink_helper_global.py +75 -0
  119. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/symlink_helper_map.py +114 -0
  120. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/symlinks.py +534 -0
  121. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/unified_input.py +39 -0
  122. mcpywrap_core-0.6.0/mcpywrap_core/mcstudio/window.py +755 -0
  123. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/__init__.py +1 -0
  124. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/addons.py +148 -0
  125. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/level_dat.py +958 -0
  126. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/map.py +85 -0
  127. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/modsdk_environment.py +197 -0
  128. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/netease_modsdk.py +269 -0
  129. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/template/generate_mod_files.py +116 -0
  130. mcpywrap_core-0.6.0/mcpywrap_core/minecraft/template/mod_template.py +494 -0
  131. mcpywrap_core-0.6.0/mcpywrap_core/operations/__init__.py +1 -0
  132. mcpywrap_core-0.6.0/mcpywrap_core/operations/build.py +66 -0
  133. mcpywrap_core-0.6.0/mcpywrap_core/operations/package.py +97 -0
  134. mcpywrap_core-0.6.0/mcpywrap_core/operations/sync.py +82 -0
  135. mcpywrap_core-0.6.0/mcpywrap_core/prepare_environment.py +149 -0
  136. mcpywrap_core-0.6.0/mcpywrap_core/project_dependencies.py +164 -0
  137. mcpywrap_core-0.6.0/mcpywrap_core/project_init.py +73 -0
  138. mcpywrap_core-0.6.0/mcpywrap_core/project_scripts.py +369 -0
  139. mcpywrap_core-0.6.0/mcpywrap_core/recording_io.py +122 -0
  140. mcpywrap_core-0.6.0/mcpywrap_core/remote/__init__.py +1 -0
  141. mcpywrap_core-0.6.0/mcpywrap_core/remote/client.py +271 -0
  142. mcpywrap_core-0.6.0/mcpywrap_core/remote/http_server.py +205 -0
  143. mcpywrap_core-0.6.0/mcpywrap_core/remote/protocol.py +22 -0
  144. mcpywrap_core-0.6.0/mcpywrap_core/remote/service.py +317 -0
  145. mcpywrap_core-0.6.0/mcpywrap_core/runtime/__init__.py +1 -0
  146. mcpywrap_core-0.6.0/mcpywrap_core/runtime/deployment.py +501 -0
  147. mcpywrap_core-0.6.0/mcpywrap_core/runtime/deployment_inspection.py +645 -0
  148. mcpywrap_core-0.6.0/mcpywrap_core/runtime/deployment_service.py +688 -0
  149. mcpywrap_core-0.6.0/mcpywrap_core/runtime/diagnostics.py +74 -0
  150. mcpywrap_core-0.6.0/mcpywrap_core/runtime/groups.py +666 -0
  151. mcpywrap_core-0.6.0/mcpywrap_core/runtime/instances.py +147 -0
  152. mcpywrap_core-0.6.0/mcpywrap_core/runtime/native.py +64 -0
  153. mcpywrap_core-0.6.0/mcpywrap_core/runtime/occupancy.py +78 -0
  154. mcpywrap_core-0.6.0/mcpywrap_core/runtime/preparation.py +97 -0
  155. mcpywrap_core-0.6.0/mcpywrap_core/runtime/profiles.py +183 -0
  156. mcpywrap_core-0.6.0/mcpywrap_core/runtime/storage.py +42 -0
  157. mcpywrap_core-0.6.0/mcpywrap_core/runtime/targets.py +24 -0
  158. mcpywrap_core-0.6.0/mcpywrap_core/runtime/watch.py +158 -0
  159. mcpywrap_core-0.6.0/mcpywrap_core/runtime/windows.py +184 -0
  160. mcpywrap_core-0.6.0/mcpywrap_core/source_files.py +82 -0
  161. mcpywrap_core-0.6.0/mcpywrap_core/timeline.py +26 -0
  162. mcpywrap_core-0.6.0/mcpywrap_core/ui/__init__.py +5 -0
  163. mcpywrap_core-0.6.0/mcpywrap_core/ui/application.py +58 -0
  164. mcpywrap_core-0.6.0/mcpywrap_core/ui/assets/mcpy.icns +0 -0
  165. mcpywrap_core-0.6.0/mcpywrap_core/ui/assets/mcpy.icon/Assets/01-cube.svg +5 -0
  166. mcpywrap_core-0.6.0/mcpywrap_core/ui/assets/mcpy.icon/Assets/02-prompt.svg +4 -0
  167. mcpywrap_core-0.6.0/mcpywrap_core/ui/assets/mcpy.icon/icon.json +56 -0
  168. mcpywrap_core-0.6.0/mcpywrap_core/ui/assets/mcpy.png +0 -0
  169. mcpywrap_core-0.6.0/mcpywrap_core/ui/engine_setup.py +119 -0
  170. mcpywrap_core-0.6.0/mcpywrap_core/ui/log_view.py +201 -0
  171. mcpywrap_core-0.6.0/mcpywrap_core/ui/log_window.py +147 -0
  172. mcpywrap_core-0.6.0/mcpywrap_core/ui/project_ui.py +963 -0
  173. mcpywrap_core-0.6.0/mcpywrap_core/ui/run_session.py +89 -0
  174. mcpywrap_core-0.6.0/mcpywrap_core/ui/session_controller.py +416 -0
  175. mcpywrap_core-0.6.0/mcpywrap_core/ui/session_tools.py +76 -0
  176. mcpywrap_core-0.6.0/mcpywrap_core/ui/session_views.py +236 -0
  177. mcpywrap_core-0.6.0/mcpywrap_core/ui/world_config.py +112 -0
  178. mcpywrap_core-0.6.0/mcpywrap_core/utils/__init__.py +6 -0
  179. mcpywrap_core-0.6.0/mcpywrap_core/utils/console.py +13 -0
  180. mcpywrap_core-0.6.0/mcpywrap_core/utils/link_snapshot.py +131 -0
  181. mcpywrap_core-0.6.0/mcpywrap_core/utils/locks.py +85 -0
  182. mcpywrap_core-0.6.0/mcpywrap_core/utils/paths.py +18 -0
  183. mcpywrap_core-0.6.0/mcpywrap_core/utils/pip_error_parser.py +184 -0
  184. mcpywrap_core-0.6.0/mcpywrap_core/utils/print_guide.py +30 -0
  185. mcpywrap_core-0.6.0/mcpywrap_core/utils/project_setup.py +148 -0
  186. mcpywrap_core-0.6.0/mcpywrap_core/utils/utils.py +38 -0
  187. mcpywrap_core-0.6.0/mcpywrap_core.egg-info/PKG-INFO +33 -0
  188. mcpywrap_core-0.6.0/mcpywrap_core.egg-info/SOURCES.txt +193 -0
  189. mcpywrap_core-0.6.0/mcpywrap_core.egg-info/dependency_links.txt +1 -0
  190. mcpywrap_core-0.6.0/mcpywrap_core.egg-info/requires.txt +11 -0
  191. mcpywrap_core-0.6.0/mcpywrap_core.egg-info/top_level.txt +2 -0
  192. mcpywrap_core-0.6.0/mcpywrap_host/__init__.py +9 -0
  193. mcpywrap_core-0.6.0/mcpywrap_host/store.py +276 -0
  194. mcpywrap_core-0.6.0/pyproject.toml +35 -0
  195. mcpywrap_core-0.6.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 EaseCation®️
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include README.md LICENSE THIRD_PARTY_NOTICES.md
2
+ include mcpywrap_core/core-contract.json
3
+ recursive-include mcpywrap_core/ui/assets *.svg *.png *.icns *.json
4
+ prune mcpywrap_core/mcstudio/bridge_payload
5
+ prune mcpywrap_core/mcstudio/window_capture
6
+ global-exclude __pycache__ *.py[cod] *.exe *.dll *.so *.dylib *.pyd
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcpywrap-core
3
+ Version: 0.6.0
4
+ Summary: Shared mcpy project dependency, build and runtime logic
5
+ Author-email: boybook <boybook@easecation.net>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/EaseCation/mcpywrap
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ License-File: THIRD_PARTY_NOTICES.md
12
+ Requires-Dist: click>=8
13
+ Requires-Dist: packaging>=21
14
+ Requires-Dist: tomli>=2
15
+ Requires-Dist: tomli-w>=1
16
+ Provides-Extra: watch
17
+ Requires-Dist: watchdog>=2; extra == "watch"
18
+ Provides-Extra: prepare
19
+ Requires-Dist: jsonschema<5,>=4.18; extra == "prepare"
20
+ Requires-Dist: psutil>=5.8; extra == "prepare"
21
+ Dynamic: license-file
22
+
23
+ # mcpywrap-core
24
+
25
+ Shared engineering and runtime implementation for mcpywrap. Install `mcpywrap`
26
+ for the complete desktop tool and the `mcpy` command. Install `mcpywrap-core`
27
+ for the minimal Python library used by embedded hosts.
28
+
29
+ Both distributions share a repository, release version and implementation.
30
+ The public library entry point is `mcpywrap_core.api.Core`. Optional groups are
31
+ `watch` and `prepare`; platform-specific capabilities are supplied by the host.
32
+
33
+ Documentation: https://github.com/EaseCation/mcpywrap/blob/main/docs/shared-core.md
@@ -0,0 +1,11 @@
1
+ # mcpywrap-core
2
+
3
+ Shared engineering and runtime implementation for mcpywrap. Install `mcpywrap`
4
+ for the complete desktop tool and the `mcpy` command. Install `mcpywrap-core`
5
+ for the minimal Python library used by embedded hosts.
6
+
7
+ Both distributions share a repository, release version and implementation.
8
+ The public library entry point is `mcpywrap_core.api.Core`. Optional groups are
9
+ `watch` and `prepare`; platform-specific capabilities are supplied by the host.
10
+
11
+ Documentation: https://github.com/EaseCation/mcpywrap/blob/main/docs/shared-core.md
@@ -0,0 +1,35 @@
1
+ # GUI dependency notices
2
+
3
+ mcpywrap's own source code is distributed under the MIT License in `LICENSE`.
4
+ This notice covers the Qt GUI dependencies, not the complete dependency tree.
5
+ Other installed packages retain their own copyright and license notices.
6
+
7
+ ## PySide6, Shiboken6 and Qt
8
+
9
+ The Windows and macOS GUI uses the official Qt for Python bindings, installed
10
+ as `PySide6-Essentials` and its `shiboken6` dependency. The application uses
11
+ QtCore, QtGui and QtWidgets; it does not require PyQt or PySide6-Addons.
12
+
13
+ We select the LGPLv3 licensing option for these dependencies. The inspected
14
+ 6.11.2 distributions declare `LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only`.
15
+ The Qt libraries used by the application are available under LGPLv3.
16
+ Copyright and additional third-party notices remain with the upstream packages.
17
+
18
+ The mcpywrap wheel does not bundle Qt, PySide6 or Shiboken6 binaries. Package
19
+ managers install them separately in the user's Python environment. Users may
20
+ replace compatible versions; mcpywrap does not prevent modification or debugging
21
+ of these libraries. Retain upstream notices and provide the applicable
22
+ corresponding-source access when redistributing these dependencies. A future
23
+ frozen application or a change in Qt modules requires a fresh distribution audit.
24
+
25
+ Upstream licensing and source:
26
+
27
+ - https://doc.qt.io/qtforpython-6/licenses.html
28
+ - https://code.qt.io/cgit/pyside/pyside-setup.git/
29
+ - https://code.qt.io/cgit/qt/qtbase.git/
30
+
31
+ ## Native game launcher
32
+
33
+ The separately downloaded native launcher has its own GPL licensing and
34
+ corresponding-source distribution. Changing the Python GUI binding does not
35
+ change that launcher's license or permit redistribution of Minecraft binaries.
@@ -0,0 +1,2 @@
1
+ """Shared mcpy engineering implementation."""
2
+ from ._version import __version__
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ mcpywrap 命令行工具的入口点
4
+ """
5
+
6
+ from .cli import cli
7
+
8
+ def main():
9
+ """主函数,作为 CLI 入口点"""
10
+ import sys
11
+ # Keep the caller's text encoding; unsupported symbols must not abort an operation.
12
+ for stream in (sys.stdout, sys.stderr):
13
+ if hasattr(stream, 'reconfigure'):
14
+ stream.reconfigure(errors='backslashreplace')
15
+ cli()
16
+
17
+ if __name__ == "__main__":
18
+ main()
@@ -0,0 +1,278 @@
1
+ """Private finite-script supervisor. Invoked by absolute path; never a public CLI."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import signal
6
+ import subprocess
7
+ import sys
8
+ import time
9
+
10
+ import psutil
11
+
12
+
13
+ def identity(pid):
14
+ p = psutil.Process(pid)
15
+ return {'pid': pid, 'created_at': p.create_time(), 'executable': p.exe()}
16
+
17
+
18
+ def alive(record):
19
+ try:
20
+ p = psutil.Process(record['pid'])
21
+ return abs(p.create_time() - record['created_at']) < .01 and p.status() != psutil.STATUS_ZOMBIE
22
+ except psutil.NoSuchProcess:
23
+ return False
24
+
25
+
26
+ def save(path, data):
27
+ temporary = path.with_suffix('.writing')
28
+ temporary.write_text(json.dumps(data, ensure_ascii=True), encoding='utf-8')
29
+ for attempt in range(10):
30
+ try:
31
+ temporary.replace(path)
32
+ return
33
+ except PermissionError:
34
+ if os.name != 'nt' or attempt == 9: raise
35
+ time.sleep(.02)
36
+
37
+
38
+ class WindowsJob:
39
+ """Closing the supervisor's handle terminates its assigned command tree."""
40
+ def __init__(self):
41
+ import ctypes as c
42
+ from ctypes import wintypes as w
43
+ class Basic(c.Structure):
44
+ _fields_ = [('PerProcessUserTimeLimit', c.c_longlong), ('PerJobUserTimeLimit', c.c_longlong),
45
+ ('LimitFlags', w.DWORD), ('MinimumWorkingSetSize', c.c_size_t),
46
+ ('MaximumWorkingSetSize', c.c_size_t), ('ActiveProcessLimit', w.DWORD),
47
+ ('Affinity', c.c_size_t), ('PriorityClass', w.DWORD), ('SchedulingClass', w.DWORD)]
48
+ class IO(c.Structure):
49
+ _fields_ = [(name, c.c_ulonglong) for name in ('ReadOperationCount', 'WriteOperationCount',
50
+ 'OtherOperationCount', 'ReadTransferCount', 'WriteTransferCount', 'OtherTransferCount')]
51
+ class Extended(c.Structure):
52
+ _fields_ = [('BasicLimitInformation', Basic), ('IoInfo', IO),
53
+ ('ProcessMemoryLimit', c.c_size_t), ('JobMemoryLimit', c.c_size_t),
54
+ ('PeakProcessMemoryUsed', c.c_size_t), ('PeakJobMemoryUsed', c.c_size_t)]
55
+ self.kernel = c.WinDLL('kernel32', use_last_error=True)
56
+ self.kernel.CreateJobObjectW.argtypes = [c.c_void_p, w.LPCWSTR]
57
+ self.kernel.CreateJobObjectW.restype = w.HANDLE
58
+ self.kernel.SetInformationJobObject.argtypes = [w.HANDLE, c.c_int, c.c_void_p, w.DWORD]
59
+ self.kernel.AssignProcessToJobObject.argtypes = [w.HANDLE, w.HANDLE]
60
+ self.kernel.QueryInformationJobObject.argtypes = [w.HANDLE, c.c_int, c.c_void_p, w.DWORD, c.c_void_p]
61
+ self.kernel.TerminateJobObject.argtypes = [w.HANDLE, w.UINT]
62
+ self.kernel.OpenProcess.argtypes = [w.DWORD, w.BOOL, w.DWORD]
63
+ self.kernel.OpenProcess.restype = w.HANDLE
64
+ self.kernel.IsProcessInJob.argtypes = [w.HANDLE, w.HANDLE, c.POINTER(w.BOOL)]
65
+ self.kernel.WaitForSingleObject.argtypes = [w.HANDLE, w.DWORD]
66
+ self.kernel.WaitForSingleObject.restype = w.DWORD
67
+ self.kernel.CloseHandle.argtypes = [w.HANDLE]
68
+ self.kernel.OpenThread.argtypes = [w.DWORD, w.BOOL, w.DWORD]
69
+ self.kernel.OpenThread.restype = w.HANDLE
70
+ self.kernel.ResumeThread.argtypes = [w.HANDLE]
71
+ self.kernel.ResumeThread.restype = w.DWORD
72
+ self.handle = self.kernel.CreateJobObjectW(None, None)
73
+ if not self.handle: raise c.WinError(c.get_last_error())
74
+ info = Extended(); info.BasicLimitInformation.LimitFlags = 0x2000 # KILL_ON_JOB_CLOSE
75
+ if not self.kernel.SetInformationJobObject(self.handle, 9, c.byref(info), c.sizeof(info)):
76
+ error = c.WinError(c.get_last_error()); self.close(); raise error
77
+
78
+ def assign(self, process):
79
+ import ctypes as c
80
+ if not self.kernel.AssignProcessToJobObject(self.handle, int(process._handle)):
81
+ raise c.WinError(c.get_last_error())
82
+ # The command was created suspended: no child can escape before job assignment.
83
+ threads = psutil.Process(process.pid).threads()
84
+ if not threads:
85
+ raise RuntimeError('脚本主线程不存在')
86
+ thread = self.kernel.OpenThread(0x0002, False, threads[0].id)
87
+ if not thread:
88
+ raise c.WinError(c.get_last_error())
89
+ try:
90
+ if self.kernel.ResumeThread(thread) == 0xffffffff:
91
+ raise c.WinError(c.get_last_error())
92
+ finally:
93
+ self.kernel.CloseHandle(thread)
94
+
95
+ def close(self):
96
+ if self.handle:
97
+ self.kernel.CloseHandle(self.handle)
98
+ self.handle = None
99
+
100
+ def process_ids(self):
101
+ import ctypes as c
102
+ from ctypes import wintypes as w
103
+ capacity = 16
104
+ for _ in range(8):
105
+ class ProcessIds(c.Structure):
106
+ _fields_ = [('assigned', w.DWORD), ('listed', w.DWORD),
107
+ ('ids', c.c_size_t * capacity)]
108
+ info = ProcessIds()
109
+ # JobObjectBasicProcessIdList retains descendants after their
110
+ # parent exits. A concurrent spawn may require a larger buffer.
111
+ if self.kernel.QueryInformationJobObject(self.handle, 3, c.byref(info), c.sizeof(info), None):
112
+ return list(info.ids[:info.listed])
113
+ error = c.get_last_error()
114
+ if error != 234: # ERROR_MORE_DATA
115
+ raise c.WinError(error)
116
+ capacity = max(capacity * 2, info.assigned)
117
+ raise RuntimeError('脚本进程树持续增长,无法确认成员')
118
+
119
+ def active_count(self):
120
+ import ctypes as c
121
+ from ctypes import wintypes as w
122
+ count = 0
123
+ for pid in self.process_ids():
124
+ handle = self.kernel.OpenProcess(0x00100000 | 0x1000, False, pid) # SYNCHRONIZE | QUERY_LIMITED_INFORMATION
125
+ if not handle:
126
+ error = c.get_last_error()
127
+ if error == 87: # ERROR_INVALID_PARAMETER: process already gone
128
+ continue
129
+ raise c.WinError(error)
130
+ try:
131
+ belongs = w.BOOL()
132
+ if not self.kernel.IsProcessInJob(handle, self.handle, c.byref(belongs)):
133
+ raise c.WinError(c.get_last_error())
134
+ if not belongs.value:
135
+ continue # The PID may have been reused outside this job.
136
+ state = self.kernel.WaitForSingleObject(handle, 0)
137
+ if state == 258: # WAIT_TIMEOUT: process is still running
138
+ count += 1
139
+ elif state != 0: # WAIT_OBJECT_0: process has exited
140
+ raise c.WinError(c.get_last_error())
141
+ finally:
142
+ self.kernel.CloseHandle(handle)
143
+ return count
144
+
145
+ def terminate(self):
146
+ import ctypes as c
147
+ if not self.kernel.TerminateJobObject(self.handle, 1):
148
+ raise c.WinError(c.get_last_error())
149
+
150
+
151
+ def members(process, known):
152
+ try:
153
+ for p in psutil.Process(process.pid).children(recursive=True):
154
+ known[p.pid] = identity(p.pid)
155
+ except psutil.NoSuchProcess:
156
+ pass
157
+ if os.name != 'nt':
158
+ for p in psutil.process_iter(['pid', 'status']):
159
+ try:
160
+ if p.info['status'] != psutil.STATUS_ZOMBIE and os.getpgid(p.pid) == process.pid:
161
+ known[p.pid] = identity(p.pid)
162
+ except (ProcessLookupError, PermissionError, psutil.NoSuchProcess):
163
+ pass
164
+ return [r for r in known.values() if alive(r)]
165
+
166
+
167
+ def finish_tree(process, known, job):
168
+ if job:
169
+ # Keep the job handle until exit is verified, including descendants
170
+ # never observed by psutil. Closing it alone only requests termination.
171
+ job.terminate()
172
+ if process.poll() is None:
173
+ process.kill() # Also handles failure to assign a suspended child.
174
+ process.wait(timeout=3)
175
+ deadline = time.monotonic() + 2
176
+ while job.active_count():
177
+ if time.monotonic() >= deadline:
178
+ raise RuntimeError('脚本后代进程尚未确认退出')
179
+ time.sleep(.05)
180
+ return
181
+ if os.name != 'nt':
182
+ try: os.killpg(process.pid, signal.SIGTERM)
183
+ except ProcessLookupError: pass
184
+ pending = members(process, known)
185
+ for record in pending:
186
+ try: psutil.Process(record['pid']).terminate()
187
+ except psutil.NoSuchProcess: pass
188
+ deadline = time.monotonic() + 2
189
+ while time.monotonic() < deadline and any(alive(r) for r in pending):
190
+ process.poll()
191
+ time.sleep(.05)
192
+ remaining = members(process, known)
193
+ if not remaining:
194
+ process.wait(timeout=3)
195
+ return
196
+ if os.name != 'nt':
197
+ try: os.killpg(process.pid, signal.SIGKILL)
198
+ except ProcessLookupError: pass
199
+ for record in remaining:
200
+ if alive(record):
201
+ try: psutil.Process(record['pid']).kill()
202
+ except psutil.NoSuchProcess: pass
203
+ process.wait(timeout=3)
204
+ deadline = time.monotonic() + 2
205
+ while time.monotonic() < deadline:
206
+ if not any(alive(r) for r in known.values()): return
207
+ time.sleep(.05)
208
+ raise RuntimeError('脚本后代进程尚未确认退出')
209
+
210
+
211
+ def main():
212
+ spec = json.loads(sys.stdin.buffer.read())
213
+ path = Path(spec.pop('result'))
214
+ command, timeout = spec.pop('command'), spec.pop('timeout')
215
+ cancel, parent = Path(spec.pop('cancel')), spec.pop('parent')
216
+ state = dict(spec, supervisor=identity(os.getpid()), state='starting')
217
+ process, job, known = None, None, {}
218
+ started = time.monotonic()
219
+ try:
220
+ save(path, state)
221
+ if not alive(parent) or cancel.exists():
222
+ state.update(state='cancelled', code='script_cancelled')
223
+ return
224
+ if os.name == 'nt': job = WindowsJob()
225
+ state['state'] = 'spawning'; save(path, state)
226
+ process = subprocess.Popen(command, stdin=subprocess.DEVNULL,
227
+ **({'creationflags': subprocess.CREATE_NO_WINDOW | subprocess.CREATE_NEW_PROCESS_GROUP | 0x00000004}
228
+ if os.name == 'nt' else {'start_new_session': True}))
229
+ try:
230
+ child = identity(process.pid)
231
+ except psutil.ZombieProcess:
232
+ child = {'pid': process.pid, 'created_at': psutil.Process(process.pid).create_time(), 'executable': command[0]}
233
+ state.update(child=child, group=process.pid if os.name != 'nt' else None, state='running')
234
+ known[process.pid] = child
235
+ save(path, state)
236
+ if job: job.assign(process)
237
+ while process.poll() is None:
238
+ if not job:
239
+ members(process, known)
240
+ if cancel.exists() or not alive(parent):
241
+ state.update(state='cancelled', code='script_cancelled', error='操作已取消或调用进程已退出')
242
+ break
243
+ if time.monotonic() - started >= timeout:
244
+ state.update(state='timed_out', code='script_timeout', error='执行超时')
245
+ break
246
+ time.sleep(.05)
247
+ else:
248
+ # Windows 的 Job 是所有权依据。父进程退出后再按 PID 查询树,
249
+ # 可能观察到复用该 PID 的无关进程;同时给短暂的退出收尾留出时间。
250
+ descendants = [] if job else [r for r in members(process, known) if r['pid'] != process.pid]
251
+ remaining = job.active_count() if job else 0
252
+ settle_deadline = time.monotonic() + .2
253
+ while remaining and time.monotonic() < settle_deadline:
254
+ time.sleep(.01)
255
+ remaining = job.active_count()
256
+ state.update(state='completed' if process.returncode == 0 else 'failed',
257
+ code=None if process.returncode == 0 else 'script_failed', exit_code=process.returncode)
258
+ if descendants or remaining:
259
+ state.update(state='failed', code='script_background_process', error='有限任务不能遗留后台进程')
260
+ except BaseException as exc:
261
+ state.update(state='failed', code='script_start_failed', error=str(exc))
262
+ finally:
263
+ if process:
264
+ try:
265
+ finish_tree(process, known, job)
266
+ except BaseException as exc:
267
+ import traceback
268
+ traceback.print_exc()
269
+ state.update(state='recovery_required', code='script_recovery_required', error=str(exc))
270
+ state.setdefault('exit_code', process.returncode)
271
+ if job: job.close()
272
+ state['duration'] = time.monotonic() - started
273
+ state['finished_at'] = time.time()
274
+ save(path, state)
275
+
276
+
277
+ if __name__ == '__main__':
278
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "0.6.0"
@@ -0,0 +1,69 @@
1
+ """Versioned engineering API. All frontends call the same operation functions."""
2
+ from pathlib import Path
3
+
4
+ from . import __version__
5
+ from .host import HOST_API, HostServices, host_scope, current_host
6
+
7
+ CORE_API = 1
8
+ OPERATIONS = ('resolve', 'build', 'package', 'sync')
9
+
10
+
11
+ class Core:
12
+ def __init__(self, host=None):
13
+ self.host = host or current_host()
14
+
15
+ def describe(self):
16
+ return {'core_api': CORE_API, 'host_api': HOST_API, 'version': __version__,
17
+ 'operations': list(OPERATIONS), 'capabilities': self.host.capabilities()}
18
+
19
+ def execute(self, operation, project, **options):
20
+ """Execute or raise the original structured error; never invoke another core."""
21
+ if operation not in OPERATIONS:
22
+ raise ValueError('未知工程操作:' + str(operation))
23
+ allowed = {'resolve': set(), 'build': {'merge'}, 'package': {'merge'},
24
+ 'sync': {'install', 'migrate_windows_lock'}}[operation]
25
+ if set(options) - allowed or any(type(value) is not bool for value in options.values()):
26
+ raise ValueError('无效的工程操作参数')
27
+ project = Path(project).expanduser().resolve()
28
+ if not project.is_dir() or not (project/'pyproject.toml').is_file():
29
+ raise ValueError('未找到项目 pyproject.toml:' + str(project))
30
+ from .command_context import project_scope
31
+ with host_scope(self.host), project_scope(project) as context:
32
+ if operation == 'resolve':
33
+ from .dependencies import DependencyService
34
+ manager = DependencyService(project).resolve()
35
+ result = {'project': str(project), 'dependencies': [
36
+ {'name': pack.pkg_name, 'path': str(pack.path)}
37
+ for pack in manager.get_all_dependencies().values()], 'warnings': manager.warnings}
38
+ elif operation == 'build':
39
+ from .operations.build import build_project
40
+ result = build_project(project, **options)
41
+ elif operation == 'package':
42
+ from .operations.package import package_project
43
+ result = package_project(project, **options)
44
+ else:
45
+ from .operations.sync import sync_project
46
+ result = sync_project(project, **options)
47
+ if context.warnings:
48
+ result['warnings'] = list(dict.fromkeys([*result.get('warnings', []), *context.warnings]))
49
+ return result
50
+
51
+ def request(self, request):
52
+ """Transport-neutral envelope. Authentication/path grants belong to the host.
53
+
54
+ The host resolves an authorized project ID to a path before calling this
55
+ method. Do not expose raw caller-supplied paths on an unauthenticated port.
56
+ """
57
+ try:
58
+ if not isinstance(request, dict) or request.get('core_api') != CORE_API:
59
+ raise ValueError('不兼容的核心协议')
60
+ if request.get('operation') == 'describe':
61
+ return {'ok': True, 'result': self.describe()}
62
+ options = request.get('options', {})
63
+ if not isinstance(options, dict) or not isinstance(request.get('project'), str):
64
+ raise ValueError('无效的工程请求')
65
+ result = self.execute(request.get('operation'), request['project'], **options)
66
+ return {'ok': True, 'result': result, 'core': self.describe()}
67
+ except (ValueError, OSError) as error:
68
+ return {'ok': False, 'error': str(error), 'code': getattr(error, 'code', 'operation_failed'),
69
+ 'hint': getattr(error, 'hint', None), 'details': getattr(error, 'details', {})}