@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,298 +1,298 @@
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.Linq;
15
- using System.Text.Json;
16
- using System.Text.Json.Serialization;
17
- using com.AtelierAI.Unity.Copilot.Runtime.Utils;
18
- using Microsoft.Extensions.Logging;
19
- using UnityEngine;
20
-
21
- namespace com.AtelierAI.Unity.Copilot
22
- {
23
- public partial class UnityCopilotPluginEditor
24
- {
25
- public static string ResourcesFileName => "uco-config";
26
-
27
- /// <summary>
28
- /// Pre-rename filename (uco 0.3.0 / plugin 0.76.0 renamed
29
- /// AI-Game-Developer-Config.json to uco-config.json). When only the
30
- /// legacy file exists it is read and then migrated: saved under the
31
- /// new name and the legacy file removed.
32
- /// </summary>
33
- public static string LegacyResourcesFileName => "AI-Game-Developer-Config";
34
-
35
- /// <summary>True when only the legacy-named config exists right now.</summary>
36
- static bool LegacyConfigNeedsMigration
37
- {
38
- get
39
- {
40
- var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
41
- var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
42
- return !File.Exists(newPath) && File.Exists(legacyPath);
43
- }
44
- }
45
-
46
- /// <summary>Project-relative path of the config actually present on disk.</summary>
47
- public static string EffectiveAssetsFilePath
48
- {
49
- get
50
- {
51
- var newPath = $"UserSettings/{ResourcesFileName}.json";
52
- var legacyPath = $"UserSettings/{LegacyResourcesFileName}.json";
53
- if (File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, newPath))))
54
- return newPath;
55
- return File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, legacyPath)))
56
- ? legacyPath
57
- : newPath;
58
- }
59
- }
60
-
61
- /// <summary>
62
- /// One-shot rename migration: after a config was loaded from the
63
- /// legacy filename, persist it under the new name and delete the old
64
- /// file so the project converges on UserSettings/uco-config.json.
65
- /// </summary>
66
- static void MigrateLegacyConfigFileIfNeeded()
67
- {
68
- try
69
- {
70
- if (!LegacyConfigNeedsMigration)
71
- return;
72
- var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
73
- var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
74
- File.Copy(legacyPath, newPath, overwrite: false);
75
- File.Delete(legacyPath);
76
- _logger.LogWarning("{method}: migrated {legacy} to {new} (uco rename); the old file was removed.",
77
- nameof(MigrateLegacyConfigFileIfNeeded), LegacyResourcesFileName, ResourcesFileName);
78
- }
79
- catch (Exception e)
80
- {
81
- _logger.LogWarning(e, "{method}: legacy config migration failed; falling back to the legacy name for this session.",
82
- nameof(MigrateLegacyConfigFileIfNeeded));
83
- }
84
- }
85
-
86
- /// <summary>
87
- /// Project-relative path used by Unity's AssetDatabase API.
88
- /// Do NOT use this with System.IO File/Directory operations — use <see cref="AssetsFileAbsolutePath"/> instead.
89
- /// </summary>
90
- public static string AssetsFilePath => $"UserSettings/{ResourcesFileName}.json";
91
-
92
- /// <summary>
93
- /// Gets the Unity project root folder path (the folder that contains the Assets directory).
94
- /// Uses <see cref="Application.dataPath"/> to ensure correctness regardless of the process working directory.
95
- /// </summary>
96
- public static string ProjectRootPath => Path.GetDirectoryName(Application.dataPath)!;
97
-
98
- /// <summary>
99
- /// Absolute path to the config file for use with System.IO File/Directory operations.
100
- /// Built from <see cref="ProjectRootPath"/> and <see cref="AssetsFilePath"/> to keep paths consistent.
101
- /// </summary>
102
- public static string AssetsFileAbsolutePath => Path.GetFullPath(Path.Combine(ProjectRootPath, AssetsFilePath));
103
-
104
-
105
- /// <summary>
106
- /// Resets the config file to its default state.
107
- /// </summary>
108
- public static void ResetConfig()
109
- {
110
- Instance.unityConnectionConfig.SetDefault();
111
- var plugin = CurrentPlugin;
112
- if (plugin != null)
113
- Instance.ApplyConfigToMcpPlugin(plugin);
114
- Instance.Save(captureCurrentToolStates: false);
115
- }
116
-
117
- #if UNITY_EDITOR
118
- public static UnityEngine.TextAsset AssetFile => UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>(EffectiveAssetsFilePath);
119
- #endif
120
-
121
- UnityConnectionConfig GetOrCreateConfig() => GetOrCreateConfig(out _);
122
- UnityConnectionConfig GetOrCreateConfig(out bool wasCreated)
123
- {
124
- wasCreated = false;
125
- try
126
- {
127
- // Both Edit mode and Play mode read from the same UserSettings JSON file
128
- // Use the absolute path so File.Exists/ReadAllText resolve correctly regardless of CWD.
129
- // The effective path prefers the renamed uco-config.json and falls back to
130
- // the legacy AI-Game-Developer-Config.json until the one-shot migration runs.
131
- var effectiveAbsolute = Path.GetFullPath(Path.Combine(ProjectRootPath, EffectiveAssetsFilePath));
132
- var json = File.Exists(effectiveAbsolute) ? File.ReadAllText(effectiveAbsolute) : null;
133
- if (!string.IsNullOrWhiteSpace(json))
134
- MigrateLegacyConfigFileIfNeeded();
135
-
136
- UnityConnectionConfig? config = null;
137
- try
138
- {
139
- config = string.IsNullOrWhiteSpace(json)
140
- ? null
141
- : JsonSerializer.Deserialize<UnityConnectionConfig>(
142
- // Legacy 1.0.2-and-earlier configs may carry the removed Cloud
143
- // connection mode; JsonStringEnumConverter would throw on it.
144
- json!.Replace("\"connectionMode\": \"Cloud\"", "\"connectionMode\": \"Custom\"")
145
- .Replace("\"ConnectionMode\": \"Cloud\"", "\"ConnectionMode\": \"Custom\""),
146
- new JsonSerializerOptions
147
- {
148
- PropertyNameCaseInsensitive = true,
149
- Converters = { new JsonStringEnumConverter() }
150
- });
151
- }
152
- catch (Exception e)
153
- {
154
- _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file is corrupted at <i>{path}</i></color>",
155
- nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
156
- }
157
- // Track whether the config originated from a deserialized disk file. The
158
- // safe-defaults migration below only applies to configs that pre-existed on
159
- // disk — a freshly created config already starts with the safe defaults.
160
- var loadedFromDisk = config != null;
161
- if (config == null)
162
- {
163
- _logger.LogWarning("{method}: <color=orange><b>Creating {file}</b> file at <i>{path}</i></color>",
164
- nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
165
- config = new UnityConnectionConfig();
166
- wasCreated = true;
167
- }
168
- if (string.IsNullOrEmpty(config.LocalToken))
169
- {
170
- config.LocalToken = GenerateToken();
171
- wasCreated = true;
172
- }
173
-
174
- // ── Safe-defaults (fail-closed) one-shot migration ─────────────────────────
175
- //
176
- // Newly created configs default AllowLanBind/AllowInsecureRemoteHttp to false so
177
- // a fresh project never starts the server on 0.0.0.0 without explicit consent.
178
- // However, projects that EXISTED before this change may already have LocalHost set
179
- // to a non-loopback URL (e.g. 0.0.0.0 or a LAN IP) on purpose. Silently dropping
180
- // their server start would be a regression. Detect that case here and grant the
181
- // opt-in once, with a one-time LogWarning so the user knows it happened and can
182
- // turn it back off if they did not intend it.
183
- //
184
- // Triggers ONLY when:
185
- // • The config was actually loaded from disk (loadedFromDisk == true)
186
- // • LocalHost parses as a URL with a non-loopback host
187
- // • AllowLanBind is currently false (default)
188
- // The migration sets AllowLanBind = true and flags `wasCreated` so the Save() at
189
- // the end of construction persists the new flag immediately. AllowInsecureRemoteHttp
190
- // is NOT auto-granted — it gates the configurators (which are advisory) and only
191
- // affects emitted client configs, not server start, so a one-time warning at start
192
- // is sufficient there.
193
- if (loadedFromDisk && !config.AllowLanBind && !string.IsNullOrEmpty(config.LocalHost))
194
- {
195
- if (com.AtelierAI.Unity.Copilot.Editor.Utils.SafeDefaultsGuard.IsNonLoopbackUrl(config.LocalHost))
196
- {
197
- _logger.LogWarning(
198
- "{method}: <color=orange>Detected pre-existing LAN/all-interfaces LocalHost ({host}) without AllowLanBind. " +
199
- "Granting AllowLanBind=true once to preserve current behaviour. " +
200
- "Open the Game Developer window and turn it off if you did not intend to expose the server to the network.</color>",
201
- nameof(GetOrCreateConfig), config.LocalHost);
202
- config.AllowLanBind = true;
203
- wasCreated = true; // force Save() so the migration persists.
204
- }
205
- }
206
-
207
- return config;
208
- }
209
- catch (Exception e)
210
- {
211
- _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be loaded from <i>{path}</i></color>",
212
- nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
213
- throw;
214
- }
215
- }
216
-
217
- public void Save(bool captureCurrentToolStates = true)
218
- {
219
- #if UNITY_EDITOR
220
- Validate();
221
- try
222
- {
223
- var directory = Path.GetDirectoryName(AssetsFileAbsolutePath);
224
- if (!Directory.Exists(directory))
225
- Directory.CreateDirectory(directory!);
226
-
227
- unityConnectionConfig ??= new UnityConnectionConfig();
228
-
229
- if (captureCurrentToolStates)
230
- {
231
- var enabledToolNames = Tools?.GetAllTools()
232
- ?.Select(t => new UnityConnectionConfig.CopilotFeature(t.Name, Tools.IsToolEnabled(t.Name)))
233
- ?.ToList();
234
-
235
- var enabledPromptNames = Prompts?.GetAllPrompts()
236
- ?.Select(p => new UnityConnectionConfig.CopilotFeature(p.Name, Prompts.IsPromptEnabled(p.Name)))
237
- ?.ToList();
238
-
239
- var enabledResourceNames = Resources?.GetAllResources()
240
- ?.Select(r => new UnityConnectionConfig.CopilotFeature(r.Name, Resources.IsResourceEnabled(r.Name)))
241
- ?.ToList();
242
-
243
- unityConnectionConfig.Tools = enabledToolNames != null && enabledToolNames.Count > 0
244
- ? enabledToolNames
245
- : UnityConnectionConfig.DefaultTools;
246
-
247
- unityConnectionConfig.Prompts = enabledPromptNames != null && enabledPromptNames.Count > 0
248
- ? enabledPromptNames
249
- : UnityConnectionConfig.DefaultPrompts;
250
-
251
- unityConnectionConfig.Resources = enabledResourceNames != null && enabledResourceNames.Count > 0
252
- ? enabledResourceNames
253
- : UnityConnectionConfig.DefaultResources;
254
- }
255
-
256
- // Runtime-only overrides (env vars / CLI flags) MUST NOT be persisted to disk.
257
- // Temporarily restore the disk-baseline values for any overridden field, serialize,
258
- // then re-apply the overrides so the in-memory config keeps its runtime values.
259
- // The "in-memory keeps overrides" property is critical: callers (UI, configurators,
260
- // SignalR client) hold references to unityConnectionConfig and continue reading
261
- // from it after Save returns. Restoring overrides ensures behaviour is unchanged.
262
- var hasRuntimeOverrides = RuntimeOverrides != null && RuntimeOverrides.HasAny;
263
- if (hasRuntimeOverrides)
264
- EnvironmentUtils.ApplyBaseline(unityConnectionConfig, RuntimeOverrides!);
265
- try
266
- {
267
- var json = JsonSerializer.Serialize(unityConnectionConfig, new JsonSerializerOptions
268
- {
269
- WriteIndented = true,
270
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
271
- Converters = { new JsonStringEnumConverter() }
272
- });
273
- File.WriteAllText(AssetsFileAbsolutePath, json);
274
- }
275
- finally
276
- {
277
- if (hasRuntimeOverrides)
278
- EnvironmentUtils.ApplyOverrides(unityConnectionConfig, RuntimeOverrides!);
279
- }
280
-
281
- var assetFile = AssetFile;
282
- if (assetFile != null)
283
- UnityEditor.EditorUtility.SetDirty(assetFile);
284
- else
285
- UnityEditor.AssetDatabase.ImportAsset(AssetsFilePath);
286
- }
287
- catch (Exception e)
288
- {
289
- _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be saved at <i>{path}</i></color>",
290
- nameof(Save), ResourcesFileName, AssetsFilePath);
291
- }
292
- #else
293
- // do nothing in runtime builds
294
- return;
295
- #endif
296
- }
297
- }
298
- }
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.Linq;
15
+ using System.Text.Json;
16
+ using System.Text.Json.Serialization;
17
+ using com.AtelierAI.Unity.Copilot.Runtime.Utils;
18
+ using Microsoft.Extensions.Logging;
19
+ using UnityEngine;
20
+
21
+ namespace com.AtelierAI.Unity.Copilot
22
+ {
23
+ public partial class UnityCopilotPluginEditor
24
+ {
25
+ public static string ResourcesFileName => "uco-config";
26
+
27
+ /// <summary>
28
+ /// Pre-rename filename (uco 0.3.0 / plugin 0.76.0 renamed
29
+ /// AI-Game-Developer-Config.json to uco-config.json). When only the
30
+ /// legacy file exists it is read and then migrated: saved under the
31
+ /// new name and the legacy file removed.
32
+ /// </summary>
33
+ public static string LegacyResourcesFileName => "AI-Game-Developer-Config";
34
+
35
+ /// <summary>True when only the legacy-named config exists right now.</summary>
36
+ static bool LegacyConfigNeedsMigration
37
+ {
38
+ get
39
+ {
40
+ var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
41
+ var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
42
+ return !File.Exists(newPath) && File.Exists(legacyPath);
43
+ }
44
+ }
45
+
46
+ /// <summary>Project-relative path of the config actually present on disk.</summary>
47
+ public static string EffectiveAssetsFilePath
48
+ {
49
+ get
50
+ {
51
+ var newPath = $"UserSettings/{ResourcesFileName}.json";
52
+ var legacyPath = $"UserSettings/{LegacyResourcesFileName}.json";
53
+ if (File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, newPath))))
54
+ return newPath;
55
+ return File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, legacyPath)))
56
+ ? legacyPath
57
+ : newPath;
58
+ }
59
+ }
60
+
61
+ /// <summary>
62
+ /// One-shot rename migration: after a config was loaded from the
63
+ /// legacy filename, persist it under the new name and delete the old
64
+ /// file so the project converges on UserSettings/uco-config.json.
65
+ /// </summary>
66
+ static void MigrateLegacyConfigFileIfNeeded()
67
+ {
68
+ try
69
+ {
70
+ if (!LegacyConfigNeedsMigration)
71
+ return;
72
+ var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
73
+ var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
74
+ File.Copy(legacyPath, newPath, overwrite: false);
75
+ File.Delete(legacyPath);
76
+ _logger.LogWarning("{method}: migrated {legacy} to {new} (uco rename); the old file was removed.",
77
+ nameof(MigrateLegacyConfigFileIfNeeded), LegacyResourcesFileName, ResourcesFileName);
78
+ }
79
+ catch (Exception e)
80
+ {
81
+ _logger.LogWarning(e, "{method}: legacy config migration failed; falling back to the legacy name for this session.",
82
+ nameof(MigrateLegacyConfigFileIfNeeded));
83
+ }
84
+ }
85
+
86
+ /// <summary>
87
+ /// Project-relative path used by Unity's AssetDatabase API.
88
+ /// Do NOT use this with System.IO File/Directory operations — use <see cref="AssetsFileAbsolutePath"/> instead.
89
+ /// </summary>
90
+ public static string AssetsFilePath => $"UserSettings/{ResourcesFileName}.json";
91
+
92
+ /// <summary>
93
+ /// Gets the Unity project root folder path (the folder that contains the Assets directory).
94
+ /// Uses <see cref="Application.dataPath"/> to ensure correctness regardless of the process working directory.
95
+ /// </summary>
96
+ public static string ProjectRootPath => Path.GetDirectoryName(Application.dataPath)!;
97
+
98
+ /// <summary>
99
+ /// Absolute path to the config file for use with System.IO File/Directory operations.
100
+ /// Built from <see cref="ProjectRootPath"/> and <see cref="AssetsFilePath"/> to keep paths consistent.
101
+ /// </summary>
102
+ public static string AssetsFileAbsolutePath => Path.GetFullPath(Path.Combine(ProjectRootPath, AssetsFilePath));
103
+
104
+
105
+ /// <summary>
106
+ /// Resets the config file to its default state.
107
+ /// </summary>
108
+ public static void ResetConfig()
109
+ {
110
+ Instance.unityConnectionConfig.SetDefault();
111
+ var plugin = CurrentPlugin;
112
+ if (plugin != null)
113
+ Instance.ApplyConfigToUcoPlugin(plugin);
114
+ Instance.Save(captureCurrentToolStates: false);
115
+ }
116
+
117
+ #if UNITY_EDITOR
118
+ public static UnityEngine.TextAsset AssetFile => UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>(EffectiveAssetsFilePath);
119
+ #endif
120
+
121
+ UnityConnectionConfig GetOrCreateConfig() => GetOrCreateConfig(out _);
122
+ UnityConnectionConfig GetOrCreateConfig(out bool wasCreated)
123
+ {
124
+ wasCreated = false;
125
+ try
126
+ {
127
+ // Both Edit mode and Play mode read from the same UserSettings JSON file
128
+ // Use the absolute path so File.Exists/ReadAllText resolve correctly regardless of CWD.
129
+ // The effective path prefers the renamed uco-config.json and falls back to
130
+ // the legacy AI-Game-Developer-Config.json until the one-shot migration runs.
131
+ var effectiveAbsolute = Path.GetFullPath(Path.Combine(ProjectRootPath, EffectiveAssetsFilePath));
132
+ var json = File.Exists(effectiveAbsolute) ? File.ReadAllText(effectiveAbsolute) : null;
133
+ if (!string.IsNullOrWhiteSpace(json))
134
+ MigrateLegacyConfigFileIfNeeded();
135
+
136
+ UnityConnectionConfig? config = null;
137
+ try
138
+ {
139
+ config = string.IsNullOrWhiteSpace(json)
140
+ ? null
141
+ : JsonSerializer.Deserialize<UnityConnectionConfig>(
142
+ // Legacy 1.0.2-and-earlier configs may carry the removed Cloud
143
+ // connection mode; JsonStringEnumConverter would throw on it.
144
+ json!.Replace("\"connectionMode\": \"Cloud\"", "\"connectionMode\": \"Custom\"")
145
+ .Replace("\"ConnectionMode\": \"Cloud\"", "\"ConnectionMode\": \"Custom\""),
146
+ new JsonSerializerOptions
147
+ {
148
+ PropertyNameCaseInsensitive = true,
149
+ Converters = { new JsonStringEnumConverter() }
150
+ });
151
+ }
152
+ catch (Exception e)
153
+ {
154
+ _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file is corrupted at <i>{path}</i></color>",
155
+ nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
156
+ }
157
+ // Track whether the config originated from a deserialized disk file. The
158
+ // safe-defaults migration below only applies to configs that pre-existed on
159
+ // disk — a freshly created config already starts with the safe defaults.
160
+ var loadedFromDisk = config != null;
161
+ if (config == null)
162
+ {
163
+ _logger.LogWarning("{method}: <color=orange><b>Creating {file}</b> file at <i>{path}</i></color>",
164
+ nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
165
+ config = new UnityConnectionConfig();
166
+ wasCreated = true;
167
+ }
168
+ if (string.IsNullOrEmpty(config.LocalToken))
169
+ {
170
+ config.LocalToken = GenerateToken();
171
+ wasCreated = true;
172
+ }
173
+
174
+ // ── Safe-defaults (fail-closed) one-shot migration ─────────────────────────
175
+ //
176
+ // Newly created configs default AllowLanBind/AllowInsecureRemoteHttp to false so
177
+ // a fresh project never starts the server on 0.0.0.0 without explicit consent.
178
+ // However, projects that EXISTED before this change may already have LocalHost set
179
+ // to a non-loopback URL (e.g. 0.0.0.0 or a LAN IP) on purpose. Silently dropping
180
+ // their server start would be a regression. Detect that case here and grant the
181
+ // opt-in once, with a one-time LogWarning so the user knows it happened and can
182
+ // turn it back off if they did not intend it.
183
+ //
184
+ // Triggers ONLY when:
185
+ // • The config was actually loaded from disk (loadedFromDisk == true)
186
+ // • LocalHost parses as a URL with a non-loopback host
187
+ // • AllowLanBind is currently false (default)
188
+ // The migration sets AllowLanBind = true and flags `wasCreated` so the Save() at
189
+ // the end of construction persists the new flag immediately. AllowInsecureRemoteHttp
190
+ // is NOT auto-granted — it gates the configurators (which are advisory) and only
191
+ // affects emitted client configs, not server start, so a one-time warning at start
192
+ // is sufficient there.
193
+ if (loadedFromDisk && !config.AllowLanBind && !string.IsNullOrEmpty(config.LocalHost))
194
+ {
195
+ if (com.AtelierAI.Unity.Copilot.Editor.Utils.SafeDefaultsGuard.IsNonLoopbackUrl(config.LocalHost))
196
+ {
197
+ _logger.LogWarning(
198
+ "{method}: <color=orange>Detected pre-existing LAN/all-interfaces LocalHost ({host}) without AllowLanBind. " +
199
+ "Granting AllowLanBind=true once to preserve current behaviour. " +
200
+ "Open the Game Developer window and turn it off if you did not intend to expose the server to the network.</color>",
201
+ nameof(GetOrCreateConfig), config.LocalHost);
202
+ config.AllowLanBind = true;
203
+ wasCreated = true; // force Save() so the migration persists.
204
+ }
205
+ }
206
+
207
+ return config;
208
+ }
209
+ catch (Exception e)
210
+ {
211
+ _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be loaded from <i>{path}</i></color>",
212
+ nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
213
+ throw;
214
+ }
215
+ }
216
+
217
+ public void Save(bool captureCurrentToolStates = true)
218
+ {
219
+ #if UNITY_EDITOR
220
+ Validate();
221
+ try
222
+ {
223
+ var directory = Path.GetDirectoryName(AssetsFileAbsolutePath);
224
+ if (!Directory.Exists(directory))
225
+ Directory.CreateDirectory(directory!);
226
+
227
+ unityConnectionConfig ??= new UnityConnectionConfig();
228
+
229
+ if (captureCurrentToolStates)
230
+ {
231
+ var enabledToolNames = Tools?.GetAllTools()
232
+ ?.Select(t => new UnityConnectionConfig.CopilotFeature(t.Name, Tools.IsToolEnabled(t.Name)))
233
+ ?.ToList();
234
+
235
+ var enabledPromptNames = Prompts?.GetAllPrompts()
236
+ ?.Select(p => new UnityConnectionConfig.CopilotFeature(p.Name, Prompts.IsPromptEnabled(p.Name)))
237
+ ?.ToList();
238
+
239
+ var enabledResourceNames = Resources?.GetAllResources()
240
+ ?.Select(r => new UnityConnectionConfig.CopilotFeature(r.Name, Resources.IsResourceEnabled(r.Name)))
241
+ ?.ToList();
242
+
243
+ unityConnectionConfig.Tools = enabledToolNames != null && enabledToolNames.Count > 0
244
+ ? enabledToolNames
245
+ : UnityConnectionConfig.DefaultTools;
246
+
247
+ unityConnectionConfig.Prompts = enabledPromptNames != null && enabledPromptNames.Count > 0
248
+ ? enabledPromptNames
249
+ : UnityConnectionConfig.DefaultPrompts;
250
+
251
+ unityConnectionConfig.Resources = enabledResourceNames != null && enabledResourceNames.Count > 0
252
+ ? enabledResourceNames
253
+ : UnityConnectionConfig.DefaultResources;
254
+ }
255
+
256
+ // Runtime-only overrides (env vars / CLI flags) MUST NOT be persisted to disk.
257
+ // Temporarily restore the disk-baseline values for any overridden field, serialize,
258
+ // then re-apply the overrides so the in-memory config keeps its runtime values.
259
+ // The "in-memory keeps overrides" property is critical: callers (UI, configurators,
260
+ // SignalR client) hold references to unityConnectionConfig and continue reading
261
+ // from it after Save returns. Restoring overrides ensures behaviour is unchanged.
262
+ var hasRuntimeOverrides = RuntimeOverrides != null && RuntimeOverrides.HasAny;
263
+ if (hasRuntimeOverrides)
264
+ EnvironmentUtils.ApplyBaseline(unityConnectionConfig, RuntimeOverrides!);
265
+ try
266
+ {
267
+ var json = JsonSerializer.Serialize(unityConnectionConfig, new JsonSerializerOptions
268
+ {
269
+ WriteIndented = true,
270
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
271
+ Converters = { new JsonStringEnumConverter() }
272
+ });
273
+ File.WriteAllText(AssetsFileAbsolutePath, json);
274
+ }
275
+ finally
276
+ {
277
+ if (hasRuntimeOverrides)
278
+ EnvironmentUtils.ApplyOverrides(unityConnectionConfig, RuntimeOverrides!);
279
+ }
280
+
281
+ var assetFile = AssetFile;
282
+ if (assetFile != null)
283
+ UnityEditor.EditorUtility.SetDirty(assetFile);
284
+ else
285
+ UnityEditor.AssetDatabase.ImportAsset(AssetsFilePath);
286
+ }
287
+ catch (Exception e)
288
+ {
289
+ _logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be saved at <i>{path}</i></color>",
290
+ nameof(Save), ResourcesFileName, AssetsFilePath);
291
+ }
292
+ #else
293
+ // do nothing in runtime builds
294
+ return;
295
+ #endif
296
+ }
297
+ }
298
+ }