@atelierai/uco 1.0.7 → 1.0.9

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 (107) hide show
  1. package/CHANGELOG.md +29 -8
  2. package/package.json +2 -2
  3. package/vendor/plugin/com.atelierai.unity.copilot/CHANGELOG.md +4 -4
  4. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetConfig.cs +132 -132
  5. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDependencyResolver.cs +237 -237
  6. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDownloader.cs +82 -82
  7. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/MenuItems.cs +247 -247
  8. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/ToolGroups.cs +153 -153
  9. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/SystemTool/Skills.Create.SkillBody.cs +155 -155
  10. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Console.GetLogs.cs +95 -95
  11. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Docs.HttpFetcher.cs +139 -139
  12. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Instance.ListAll.cs +72 -72
  13. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/PathReadHelper.cs +106 -106
  14. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Script.Validate.cs +252 -252
  15. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests/TestResultCollector.cs +537 -537
  16. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests.cs +400 -400
  17. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/ToolGroups.SetEnabled.cs +92 -92
  18. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Branding/ProductInfo.cs +62 -62
  19. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/CopilotServerManager.cs +887 -1034
  20. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Skills/Skill_InitialSetup.cs +197 -197
  21. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.Editor.cs +201 -201
  22. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.cs +67 -67
  23. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotListWindowBase.cs +393 -393
  24. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotWindowBase.cs +113 -113
  25. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.Connection.cs +3 -3
  26. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotFeatures.cs +195 -195
  27. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotServer.cs +22 -22
  28. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CreateGUI.cs +354 -354
  29. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.cs +78 -78
  30. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowInitializer.cs +47 -47
  31. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/SerializationCheckWindow.cs +190 -190
  32. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Build.cs +91 -91
  33. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs +298 -298
  34. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Static.cs +296 -296
  35. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.cs +69 -69
  36. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PackageUtils.cs +168 -168
  37. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PrefsKeyMigration.cs +119 -119
  38. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/SafeDefaultsGuard.cs +180 -180
  39. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ScriptUtils.cs +229 -229
  40. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolGroupRegistry.cs +461 -460
  41. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolsManifestGenerator.cs +200 -200
  42. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringAdapters.cs +1467 -1467
  43. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringSafetyReloadGuard.cs +65 -65
  44. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityCopilotUpdateProjectSettings.cs +66 -66
  45. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/CopilotPromptsWindow.uss +11 -11
  46. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/CopilotResourcesWindow.uss +16 -16
  47. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/CopilotToolsWindow.uss +36 -36
  48. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/MainWindow.uss +183 -183
  49. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/common/{_mcp-list-window.uss → _copilot-list-window.uss} +18 -18
  50. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/common/_list-view.uss +77 -77
  51. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uxml/CopilotPromptsWindow.uxml +18 -18
  52. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uxml/CopilotResourcesWindow.uxml +18 -18
  53. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uxml/CopilotToolsWindow.uxml +18 -18
  54. package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uxml/MainWindow.uxml +115 -115
  55. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/ReflectorNet.dll +0 -0
  56. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.Common.dll +0 -0
  57. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.dll +0 -0
  58. package/vendor/plugin/com.atelierai.unity.copilot/README.md +88 -88
  59. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Converter/Json/Structures/EntityIdConverter.cs +120 -120
  60. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.cs +110 -110
  61. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.pre-Unity.6.5.cs +110 -110
  62. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Unity/Logs/FileLogStorage.cs +547 -547
  63. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Build.cs +243 -243
  64. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Config.cs +3 -3
  65. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.cs +295 -295
  66. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginBuilder.cs +105 -105
  67. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.Static.cs +155 -155
  68. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.cs +59 -59
  69. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Utils/EnvironmentUtils.cs +11 -11
  70. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/AtomicApiTestFixtures.cs +99 -99
  71. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/TryModifyAtTests.cs +227 -227
  72. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AuthoringSafetyPilotTests.cs +1050 -1050
  73. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetExtractorDevelopmentDependencyTests.cs +148 -148
  74. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetExtractorFlatLayoutTests.cs +166 -166
  75. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetInstallManifestTests.cs +436 -436
  76. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetLegacyMigrationTests.cs +304 -304
  77. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetPackageInstallerStaleVersionCleanupTests.cs +200 -200
  78. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Environment/EnvironmentUtilsTests.cs +2 -2
  79. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/BlacklistTypeTests.cs +416 -416
  80. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.cs +172 -172
  81. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.pre-Unity.6.5.cs +169 -169
  82. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.cs +182 -182
  83. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.pre-Unity.6.5.cs +179 -179
  84. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.cs +175 -175
  85. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.pre-Unity.6.5.cs +174 -174
  86. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.cs +171 -171
  87. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.pre-Unity.6.5.cs +170 -170
  88. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsCreateFolderTests.cs +243 -243
  89. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsMaterialTests.cs +101 -101
  90. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.cs +348 -348
  91. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.pre-Unity.6.5.cs +348 -348
  92. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsShaderTests.cs +194 -194
  93. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/GameObject/TestJsonSchema.cs +199 -199
  94. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.cs +573 -573
  95. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.pre-Unity.6.5.cs +513 -513
  96. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.cs +154 -154
  97. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.pre-Unity.6.5.cs +154 -154
  98. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotGameViewTests.cs +164 -164
  99. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotSceneViewTests.cs +155 -155
  100. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolParameterTests.cs +56 -56
  101. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.cs +651 -651
  102. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.pre-Unity.6.5.cs +651 -651
  103. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ToolBatchExecutionIntegrationTests.cs +407 -407
  104. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs → UcoPluginTests.cs} +105 -105
  105. package/vendor/plugin/com.atelierai.unity.copilot/package.json +1 -1
  106. /package/vendor/plugin/com.atelierai.unity.copilot/Editor/UI/uss/common/{_mcp-list-window.uss.meta → _copilot-list-window.uss.meta} +0 -0
  107. /package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs.meta → UcoPluginTests.cs.meta} +0 -0
@@ -1,237 +1,237 @@
1
- /*
2
- +------------------------------------------------------------------+
3
- | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
- | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
- | Copyright (c) 2025 Ivan Murzak |
6
- | Licensed under the Apache License, Version 2.0. |
7
- | See the LICENSE file in the project root for more information. |
8
- +------------------------------------------------------------------+
9
- */
10
-
11
- #nullable enable
12
- using System;
13
- using System.Collections.Generic;
14
- using UnityEditor;
15
- using UnityEditor.PackageManager;
16
- using UnityEngine;
17
- using PackageManagerEvents = UnityEditor.PackageManager.Events;
18
-
19
- namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
20
- {
21
- /// <summary>
22
- /// Entry point for NuGet dependency management. Runs on every domain reload via [InitializeOnLoad]
23
- /// AND in response to UPM package upgrades via PackageManager.Events.registeredPackages.
24
- ///
25
- /// This assembly has ZERO external dependencies — it always compiles, even when the main plugin
26
- /// fails due to missing or conflicting DLLs. It downloads NuGet packages directly from nuget.org,
27
- /// extracts DLLs, skips assemblies Unity already provides, and sets the UNITY_COPILOT_READY define
28
- /// so the main plugin assemblies can compile.
29
- ///
30
- /// Why both triggers are needed:
31
- /// [InitializeOnLoad] fires after a *successful* project-wide recompile. When a user upgrades
32
- /// the Unity package and the new code references newer NuGet APIs whose DLL is still on disk
33
- /// at the old version, the post-import recompile fails, Unity blocks the domain reload, and
34
- /// the new resolver's static constructor never runs. The previous AppDomain is still alive
35
- /// though — and so are its event subscriptions. registeredPackages fires from the still-alive
36
- /// AppDomain after UPM writes new package files but before the failed recompile, giving us a
37
- /// chance to clean up stale DLLs and unblock the next compile attempt. See Unity-MCP#707.
38
- ///
39
- /// Flow on domain reload:
40
- /// 1. [InitializeOnLoad] fires
41
- /// 2. Deferred via EditorApplication.update (runs without editor focus, unlike delayCall)
42
- /// 3. NuGetPackageRestorer checks if all packages are installed
43
- /// 4. Downloads and installs any missing packages, removes stale-version siblings
44
- /// 5. Sets UNITY_COPILOT_READY scripting define
45
- /// 6. If packages were installed: triggers AssetDatabase.Refresh() → domain reload
46
- /// 7. On next reload: everything is in place, main plugin compiles
47
- ///
48
- /// Flow on UPM package change (no domain reload required to invoke):
49
- /// 1. registeredPackages fires from the previous AppDomain
50
- /// 2. NuGetPackageRestorer.Restore() runs the same install + cleanup logic
51
- /// 3. AssetDatabase.Refresh() prompts Unity to retry compilation with the corrected DLL set
52
- /// 4. On the resulting successful reload, [InitializeOnLoad] resumes the normal path
53
- /// </summary>
54
- [InitializeOnLoad]
55
- static class NuGetDependencyResolver
56
- {
57
- const string Tag = "[Unity-MCP DependencyResolver]";
58
-
59
- static NuGetDependencyResolver()
60
- {
61
- // In CI, skip runtime resolution — DLLs are committed to git
62
- // and UNITY_COPILOT_READY should already be in ProjectSettings.
63
- // Setting defines at runtime in batch mode causes "Error building Player
64
- // because scripts are compiling" when the test runner races the recompilation.
65
- if (IsCi())
66
- {
67
- RecompileGate.EnsureReadyDefine();
68
- return;
69
- }
70
-
71
- EditorApplication.update += ResolveOnce;
72
-
73
- // Subscribe to UPM package-change events so that when the user upgrades a package
74
- // whose new code references a newer NuGet DLL than the one currently on disk, we
75
- // can run the cleanup + restore from the still-alive AppDomain. The compile that
76
- // would otherwise fail (and block [InitializeOnLoad] in the next domain) gets
77
- // retried by Unity once AssetDatabase.Refresh() picks up the corrected DLL set.
78
- // -= before += guards against accidental double-subscription if a hot-reload path
79
- // ever re-runs this constructor without a full domain swap.
80
- PackageManagerEvents.registeredPackages -= OnRegisteredPackages;
81
- PackageManagerEvents.registeredPackages += OnRegisteredPackages;
82
- }
83
-
84
- /// <summary>
85
- /// Tracks whether a UPM-event-triggered restore is currently in flight, so a second
86
- /// event burst (UPM can fire multiple registeredPackages events for a single user
87
- /// action) doesn't kick off a parallel restore.
88
- /// </summary>
89
- static bool isRestoringFromPackageEvent;
90
-
91
- static void OnRegisteredPackages(PackageRegistrationEventArgs args)
92
- {
93
- if (isRestoringFromPackageEvent)
94
- return;
95
-
96
- // Cheap pre-filter: if no packages were added, removed, or upgraded, there is no
97
- // possibility of a NuGet-dep version change. Skip the heavier AllPackagesInstalled()
98
- // probe to avoid touching disk on every UPM ping.
99
- if ((args.added == null || args.added.Count == 0)
100
- && (args.removed == null || args.removed.Count == 0)
101
- && (args.changedFrom == null || args.changedFrom.Count == 0)
102
- && (args.changedTo == null || args.changedTo.Count == 0))
103
- {
104
- return;
105
- }
106
-
107
- isRestoringFromPackageEvent = true;
108
- try
109
- {
110
- Debug.Log($"{Tag} UPM package change detected — running NuGet restore.");
111
- RunRestoreAndRefresh();
112
- }
113
- catch (Exception ex)
114
- {
115
- // Same rationale as ResolveOnce: do NOT set UNITY_COPILOT_READY on a failed restore.
116
- Debug.LogError($"{Tag} Restore from UPM event failed: {ex}");
117
- }
118
- finally
119
- {
120
- isRestoringFromPackageEvent = false;
121
- }
122
- }
123
-
124
- /// <summary>
125
- /// Shared core used by both ResolveOnce (post-domain-reload) and OnRegisteredPackages
126
- /// (no-domain-reload UPM event path). AllPackagesInstalled() short-circuits the common
127
- /// no-op case without touching the network.
128
- /// </summary>
129
- static void RunRestoreAndRefresh()
130
- {
131
- if (NuGetPackageRestorer.AllPackagesInstalled())
132
- {
133
- NuGetPluginConfigurator.ConfigureAll();
134
- RecompileGate.EnsureReadyDefine();
135
- return;
136
- }
137
-
138
- Debug.Log($"{Tag} Restoring NuGet packages...");
139
- var changed = NuGetPackageRestorer.Restore();
140
-
141
- NuGetPluginConfigurator.ConfigureAll();
142
-
143
- RecompileGate.EnsureReadyDefine();
144
-
145
- if (changed)
146
- {
147
- Debug.Log($"{Tag} Packages restored. Refreshing AssetDatabase...");
148
- AssetDatabase.Refresh();
149
- }
150
- }
151
-
152
- static void ResolveOnce()
153
- {
154
- EditorApplication.update -= ResolveOnce;
155
- try
156
- {
157
- RunRestoreAndRefresh();
158
- }
159
- catch (Exception ex)
160
- {
161
- // Do NOT set UNITY_COPILOT_READY here: if restore/configuration failed, the DLL
162
- // layout is unknown/inconsistent, and letting main-plugin assemblies compile
163
- // against a partial/mismatched set (via defineConstraints) produces hard-to-
164
- // diagnose MissingMethodException / TypeLoadException at runtime. Surface the
165
- // failure loud and clear instead, so the user fixes the underlying problem
166
- // and retries (the next domain reload will run Restore again).
167
- Debug.LogError($"{Tag} Failed: {ex}");
168
- }
169
- }
170
-
171
- /// <summary>
172
- /// Checks if the current environment is a CI environment.
173
- /// Mirrors EnvironmentUtils.IsCi() but without external dependencies,
174
- /// since this assembly must compile standalone.
175
- /// Checks both command-line arguments and environment variables for
176
- /// CI, GITHUB_ACTIONS, and TF_BUILD (Azure Pipelines).
177
- /// </summary>
178
- static bool IsCi()
179
- {
180
- var args = ParseCommandLineArguments();
181
-
182
- var ci = GetArgOrEnv(args, "CI");
183
- var gha = GetArgOrEnv(args, "GITHUB_ACTIONS");
184
- var az = GetArgOrEnv(args, "TF_BUILD");
185
-
186
- return IsTrue(ci) || IsTrue(gha) || IsTrue(az);
187
-
188
- static string? GetArgOrEnv(Dictionary<string, string?> args, string key)
189
- => args.TryGetValue(key, out var v) ? v : Environment.GetEnvironmentVariable(key);
190
-
191
- static bool IsTrue(string? value)
192
- => string.Equals(value?.Trim()?.Trim('"'), "true", StringComparison.OrdinalIgnoreCase);
193
- }
194
-
195
- /// <summary>
196
- /// Parses Unity command-line arguments into a dictionary.
197
- /// Handles both "-key value" and "-key=value" forms, plus bare flags like "-batchmode".
198
- /// Keys are stored WITHOUT the leading dash.
199
- /// </summary>
200
- static Dictionary<string, string?> ParseCommandLineArguments()
201
- {
202
- var result = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
203
- var rawArgs = Environment.GetCommandLineArgs();
204
-
205
- for (var i = 0; i < rawArgs.Length; i++)
206
- {
207
- var arg = rawArgs[i];
208
- if (!arg.StartsWith("-"))
209
- continue;
210
-
211
- var key = arg.TrimStart('-');
212
-
213
- // Handle -key=value form
214
- var eqIndex = key.IndexOf('=');
215
- if (eqIndex >= 0)
216
- {
217
- result[key.Substring(0, eqIndex)] = key.Substring(eqIndex + 1);
218
- continue;
219
- }
220
-
221
- // Handle -key value form (next arg is value if it doesn't start with -)
222
- if (i + 1 < rawArgs.Length && !rawArgs[i + 1].StartsWith("-"))
223
- {
224
- result[key] = rawArgs[++i];
225
- }
226
- else
227
- {
228
- // Bare flag like -batchmode
229
- result[key] = null;
230
- }
231
- }
232
-
233
- return result;
234
- }
235
-
236
- }
237
- }
1
+ /*
2
+ +------------------------------------------------------------------+
3
+ | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
+ | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
+ | Copyright (c) 2025 Ivan Murzak |
6
+ | Licensed under the Apache License, Version 2.0. |
7
+ | See the LICENSE file in the project root for more information. |
8
+ +------------------------------------------------------------------+
9
+ */
10
+
11
+ #nullable enable
12
+ using System;
13
+ using System.Collections.Generic;
14
+ using UnityEditor;
15
+ using UnityEditor.PackageManager;
16
+ using UnityEngine;
17
+ using PackageManagerEvents = UnityEditor.PackageManager.Events;
18
+
19
+ namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
20
+ {
21
+ /// <summary>
22
+ /// Entry point for NuGet dependency management. Runs on every domain reload via [InitializeOnLoad]
23
+ /// AND in response to UPM package upgrades via PackageManager.Events.registeredPackages.
24
+ ///
25
+ /// This assembly has ZERO external dependencies — it always compiles, even when the main plugin
26
+ /// fails due to missing or conflicting DLLs. It downloads NuGet packages directly from nuget.org,
27
+ /// extracts DLLs, skips assemblies Unity already provides, and sets the UNITY_COPILOT_READY define
28
+ /// so the main plugin assemblies can compile.
29
+ ///
30
+ /// Why both triggers are needed:
31
+ /// [InitializeOnLoad] fires after a *successful* project-wide recompile. When a user upgrades
32
+ /// the Unity package and the new code references newer NuGet APIs whose DLL is still on disk
33
+ /// at the old version, the post-import recompile fails, Unity blocks the domain reload, and
34
+ /// the new resolver's static constructor never runs. The previous AppDomain is still alive
35
+ /// though — and so are its event subscriptions. registeredPackages fires from the still-alive
36
+ /// AppDomain after UPM writes new package files but before the failed recompile, giving us a
37
+ /// chance to clean up stale DLLs and unblock the next compile attempt. See Unity-Uco#707.
38
+ ///
39
+ /// Flow on domain reload:
40
+ /// 1. [InitializeOnLoad] fires
41
+ /// 2. Deferred via EditorApplication.update (runs without editor focus, unlike delayCall)
42
+ /// 3. NuGetPackageRestorer checks if all packages are installed
43
+ /// 4. Downloads and installs any missing packages, removes stale-version siblings
44
+ /// 5. Sets UNITY_COPILOT_READY scripting define
45
+ /// 6. If packages were installed: triggers AssetDatabase.Refresh() → domain reload
46
+ /// 7. On next reload: everything is in place, main plugin compiles
47
+ ///
48
+ /// Flow on UPM package change (no domain reload required to invoke):
49
+ /// 1. registeredPackages fires from the previous AppDomain
50
+ /// 2. NuGetPackageRestorer.Restore() runs the same install + cleanup logic
51
+ /// 3. AssetDatabase.Refresh() prompts Unity to retry compilation with the corrected DLL set
52
+ /// 4. On the resulting successful reload, [InitializeOnLoad] resumes the normal path
53
+ /// </summary>
54
+ [InitializeOnLoad]
55
+ static class NuGetDependencyResolver
56
+ {
57
+ const string Tag = "[Unity-Uco DependencyResolver]";
58
+
59
+ static NuGetDependencyResolver()
60
+ {
61
+ // In CI, skip runtime resolution — DLLs are committed to git
62
+ // and UNITY_COPILOT_READY should already be in ProjectSettings.
63
+ // Setting defines at runtime in batch mode causes "Error building Player
64
+ // because scripts are compiling" when the test runner races the recompilation.
65
+ if (IsCi())
66
+ {
67
+ RecompileGate.EnsureReadyDefine();
68
+ return;
69
+ }
70
+
71
+ EditorApplication.update += ResolveOnce;
72
+
73
+ // Subscribe to UPM package-change events so that when the user upgrades a package
74
+ // whose new code references a newer NuGet DLL than the one currently on disk, we
75
+ // can run the cleanup + restore from the still-alive AppDomain. The compile that
76
+ // would otherwise fail (and block [InitializeOnLoad] in the next domain) gets
77
+ // retried by Unity once AssetDatabase.Refresh() picks up the corrected DLL set.
78
+ // -= before += guards against accidental double-subscription if a hot-reload path
79
+ // ever re-runs this constructor without a full domain swap.
80
+ PackageManagerEvents.registeredPackages -= OnRegisteredPackages;
81
+ PackageManagerEvents.registeredPackages += OnRegisteredPackages;
82
+ }
83
+
84
+ /// <summary>
85
+ /// Tracks whether a UPM-event-triggered restore is currently in flight, so a second
86
+ /// event burst (UPM can fire multiple registeredPackages events for a single user
87
+ /// action) doesn't kick off a parallel restore.
88
+ /// </summary>
89
+ static bool isRestoringFromPackageEvent;
90
+
91
+ static void OnRegisteredPackages(PackageRegistrationEventArgs args)
92
+ {
93
+ if (isRestoringFromPackageEvent)
94
+ return;
95
+
96
+ // Cheap pre-filter: if no packages were added, removed, or upgraded, there is no
97
+ // possibility of a NuGet-dep version change. Skip the heavier AllPackagesInstalled()
98
+ // probe to avoid touching disk on every UPM ping.
99
+ if ((args.added == null || args.added.Count == 0)
100
+ && (args.removed == null || args.removed.Count == 0)
101
+ && (args.changedFrom == null || args.changedFrom.Count == 0)
102
+ && (args.changedTo == null || args.changedTo.Count == 0))
103
+ {
104
+ return;
105
+ }
106
+
107
+ isRestoringFromPackageEvent = true;
108
+ try
109
+ {
110
+ Debug.Log($"{Tag} UPM package change detected — running NuGet restore.");
111
+ RunRestoreAndRefresh();
112
+ }
113
+ catch (Exception ex)
114
+ {
115
+ // Same rationale as ResolveOnce: do NOT set UNITY_COPILOT_READY on a failed restore.
116
+ Debug.LogError($"{Tag} Restore from UPM event failed: {ex}");
117
+ }
118
+ finally
119
+ {
120
+ isRestoringFromPackageEvent = false;
121
+ }
122
+ }
123
+
124
+ /// <summary>
125
+ /// Shared core used by both ResolveOnce (post-domain-reload) and OnRegisteredPackages
126
+ /// (no-domain-reload UPM event path). AllPackagesInstalled() short-circuits the common
127
+ /// no-op case without touching the network.
128
+ /// </summary>
129
+ static void RunRestoreAndRefresh()
130
+ {
131
+ if (NuGetPackageRestorer.AllPackagesInstalled())
132
+ {
133
+ NuGetPluginConfigurator.ConfigureAll();
134
+ RecompileGate.EnsureReadyDefine();
135
+ return;
136
+ }
137
+
138
+ Debug.Log($"{Tag} Restoring NuGet packages...");
139
+ var changed = NuGetPackageRestorer.Restore();
140
+
141
+ NuGetPluginConfigurator.ConfigureAll();
142
+
143
+ RecompileGate.EnsureReadyDefine();
144
+
145
+ if (changed)
146
+ {
147
+ Debug.Log($"{Tag} Packages restored. Refreshing AssetDatabase...");
148
+ AssetDatabase.Refresh();
149
+ }
150
+ }
151
+
152
+ static void ResolveOnce()
153
+ {
154
+ EditorApplication.update -= ResolveOnce;
155
+ try
156
+ {
157
+ RunRestoreAndRefresh();
158
+ }
159
+ catch (Exception ex)
160
+ {
161
+ // Do NOT set UNITY_COPILOT_READY here: if restore/configuration failed, the DLL
162
+ // layout is unknown/inconsistent, and letting main-plugin assemblies compile
163
+ // against a partial/mismatched set (via defineConstraints) produces hard-to-
164
+ // diagnose MissingMethodException / TypeLoadException at runtime. Surface the
165
+ // failure loud and clear instead, so the user fixes the underlying problem
166
+ // and retries (the next domain reload will run Restore again).
167
+ Debug.LogError($"{Tag} Failed: {ex}");
168
+ }
169
+ }
170
+
171
+ /// <summary>
172
+ /// Checks if the current environment is a CI environment.
173
+ /// Mirrors EnvironmentUtils.IsCi() but without external dependencies,
174
+ /// since this assembly must compile standalone.
175
+ /// Checks both command-line arguments and environment variables for
176
+ /// CI, GITHUB_ACTIONS, and TF_BUILD (Azure Pipelines).
177
+ /// </summary>
178
+ static bool IsCi()
179
+ {
180
+ var args = ParseCommandLineArguments();
181
+
182
+ var ci = GetArgOrEnv(args, "CI");
183
+ var gha = GetArgOrEnv(args, "GITHUB_ACTIONS");
184
+ var az = GetArgOrEnv(args, "TF_BUILD");
185
+
186
+ return IsTrue(ci) || IsTrue(gha) || IsTrue(az);
187
+
188
+ static string? GetArgOrEnv(Dictionary<string, string?> args, string key)
189
+ => args.TryGetValue(key, out var v) ? v : Environment.GetEnvironmentVariable(key);
190
+
191
+ static bool IsTrue(string? value)
192
+ => string.Equals(value?.Trim()?.Trim('"'), "true", StringComparison.OrdinalIgnoreCase);
193
+ }
194
+
195
+ /// <summary>
196
+ /// Parses Unity command-line arguments into a dictionary.
197
+ /// Handles both "-key value" and "-key=value" forms, plus bare flags like "-batchmode".
198
+ /// Keys are stored WITHOUT the leading dash.
199
+ /// </summary>
200
+ static Dictionary<string, string?> ParseCommandLineArguments()
201
+ {
202
+ var result = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
203
+ var rawArgs = Environment.GetCommandLineArgs();
204
+
205
+ for (var i = 0; i < rawArgs.Length; i++)
206
+ {
207
+ var arg = rawArgs[i];
208
+ if (!arg.StartsWith("-"))
209
+ continue;
210
+
211
+ var key = arg.TrimStart('-');
212
+
213
+ // Handle -key=value form
214
+ var eqIndex = key.IndexOf('=');
215
+ if (eqIndex >= 0)
216
+ {
217
+ result[key.Substring(0, eqIndex)] = key.Substring(eqIndex + 1);
218
+ continue;
219
+ }
220
+
221
+ // Handle -key value form (next arg is value if it doesn't start with -)
222
+ if (i + 1 < rawArgs.Length && !rawArgs[i + 1].StartsWith("-"))
223
+ {
224
+ result[key] = rawArgs[++i];
225
+ }
226
+ else
227
+ {
228
+ // Bare flag like -batchmode
229
+ result[key] = null;
230
+ }
231
+ }
232
+
233
+ return result;
234
+ }
235
+
236
+ }
237
+ }
@@ -1,82 +1,82 @@
1
- /*
2
- +------------------------------------------------------------------+
3
- | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
- | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
- | Copyright (c) 2025 Ivan Murzak |
6
- | Licensed under the Apache License, Version 2.0. |
7
- | See the LICENSE file in the project root for more information. |
8
- +------------------------------------------------------------------+
9
- */
10
-
11
- #nullable enable
12
- using System;
13
- using System.IO;
14
- using System.Net.Http;
15
- using UnityEngine;
16
-
17
- namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
18
- {
19
- /// <summary>
20
- /// Downloads .nupkg files from the NuGet v3 flat container API.
21
- /// Uses the flat container URL pattern:
22
- /// https://api.nuget.org/v3-flatcontainer/{id}/{version}/{id}.{version}.nupkg
23
- /// </summary>
24
- static class NuGetDownloader
25
- {
26
- const string Tag = NuGetConfig.LogTag;
27
-
28
- static readonly HttpClient httpClient = CreateHttpClient();
29
-
30
- static HttpClient CreateHttpClient()
31
- {
32
- var handler = new HttpClientHandler
33
- {
34
- AutomaticDecompression = System.Net.DecompressionMethods.GZip
35
- | System.Net.DecompressionMethods.Deflate
36
- };
37
- var client = new HttpClient(handler);
38
- client.DefaultRequestHeaders.Add("User-Agent", "Unity-MCP-NuGetResolver");
39
- client.Timeout = TimeSpan.FromMinutes(5);
40
- return client;
41
- }
42
-
43
- /// <summary>
44
- /// Downloads a .nupkg file to the cache. Returns the cached file path.
45
- /// If the file is already cached, returns immediately without downloading.
46
- /// </summary>
47
- public static string Download(NuGetPackage package)
48
- {
49
- NuGetCache.EnsureCacheDirectory();
50
- var cachedPath = NuGetCache.GetCachedPath(package);
51
-
52
- if (File.Exists(cachedPath))
53
- return cachedPath;
54
-
55
- var url = package.DownloadUrl;
56
- Debug.Log($"{Tag} Downloading {package.Id} {package.Version} from {url}");
57
-
58
- try
59
- {
60
- using (var response = httpClient.GetAsync(url).GetAwaiter().GetResult())
61
- {
62
- response.EnsureSuccessStatusCode();
63
- using (var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
64
- using (var fileStream = File.Create(cachedPath))
65
- {
66
- stream.CopyTo(fileStream);
67
- }
68
- }
69
- Debug.Log($"{Tag} Downloaded {package.Id} {package.Version} ({new FileInfo(cachedPath).Length / 1024} KB)");
70
- }
71
- catch (Exception ex)
72
- {
73
- Debug.LogError($"{Tag} Failed to download {package}: {ex.Message}");
74
- if (File.Exists(cachedPath))
75
- File.Delete(cachedPath);
76
- throw;
77
- }
78
-
79
- return cachedPath;
80
- }
81
- }
82
- }
1
+ /*
2
+ +------------------------------------------------------------------+
3
+ | Author: Ivan Murzak (https://github.com/IvanMurzak) |
4
+ | Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) |
5
+ | Copyright (c) 2025 Ivan Murzak |
6
+ | Licensed under the Apache License, Version 2.0. |
7
+ | See the LICENSE file in the project root for more information. |
8
+ +------------------------------------------------------------------+
9
+ */
10
+
11
+ #nullable enable
12
+ using System;
13
+ using System.IO;
14
+ using System.Net.Http;
15
+ using UnityEngine;
16
+
17
+ namespace com.AtelierAI.Unity.Copilot.Editor.DependencyResolver
18
+ {
19
+ /// <summary>
20
+ /// Downloads .nupkg files from the NuGet v3 flat container API.
21
+ /// Uses the flat container URL pattern:
22
+ /// https://api.nuget.org/v3-flatcontainer/{id}/{version}/{id}.{version}.nupkg
23
+ /// </summary>
24
+ static class NuGetDownloader
25
+ {
26
+ const string Tag = NuGetConfig.LogTag;
27
+
28
+ static readonly HttpClient httpClient = CreateHttpClient();
29
+
30
+ static HttpClient CreateHttpClient()
31
+ {
32
+ var handler = new HttpClientHandler
33
+ {
34
+ AutomaticDecompression = System.Net.DecompressionMethods.GZip
35
+ | System.Net.DecompressionMethods.Deflate
36
+ };
37
+ var client = new HttpClient(handler);
38
+ client.DefaultRequestHeaders.Add("User-Agent", "Unity-Uco-NuGetResolver");
39
+ client.Timeout = TimeSpan.FromMinutes(5);
40
+ return client;
41
+ }
42
+
43
+ /// <summary>
44
+ /// Downloads a .nupkg file to the cache. Returns the cached file path.
45
+ /// If the file is already cached, returns immediately without downloading.
46
+ /// </summary>
47
+ public static string Download(NuGetPackage package)
48
+ {
49
+ NuGetCache.EnsureCacheDirectory();
50
+ var cachedPath = NuGetCache.GetCachedPath(package);
51
+
52
+ if (File.Exists(cachedPath))
53
+ return cachedPath;
54
+
55
+ var url = package.DownloadUrl;
56
+ Debug.Log($"{Tag} Downloading {package.Id} {package.Version} from {url}");
57
+
58
+ try
59
+ {
60
+ using (var response = httpClient.GetAsync(url).GetAwaiter().GetResult())
61
+ {
62
+ response.EnsureSuccessStatusCode();
63
+ using (var stream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
64
+ using (var fileStream = File.Create(cachedPath))
65
+ {
66
+ stream.CopyTo(fileStream);
67
+ }
68
+ }
69
+ Debug.Log($"{Tag} Downloaded {package.Id} {package.Version} ({new FileInfo(cachedPath).Length / 1024} KB)");
70
+ }
71
+ catch (Exception ex)
72
+ {
73
+ Debug.LogError($"{Tag} Failed to download {package}: {ex.Message}");
74
+ if (File.Exists(cachedPath))
75
+ File.Delete(cachedPath);
76
+ throw;
77
+ }
78
+
79
+ return cachedPath;
80
+ }
81
+ }
82
+ }