@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,119 +1,119 @@
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 com.AtelierAI.Unity.Copilot.Editor.Branding;
13
- using UnityEditor;
14
- using UnityEngine;
15
-
16
- namespace com.AtelierAI.Unity.Copilot.Editor.Utils
17
- {
18
- // Phase C rebrand carries forward existing user state across the
19
- // "com.ivanmurzak.unity.mcp" → "com.atelierai.unity.copilot" key namespace shift.
20
- // Runs once per project (guarded by MigrationDoneKey) on domain reload and copies
21
- // old EditorPrefs keys into the new namespace. Old keys are left in place — they're
22
- // harmless and lets users downgrade if the rebrand is reverted. A future major
23
- // version can delete this class and the old keys together.
24
- [InitializeOnLoad]
25
- internal static class PrefsKeyMigration
26
- {
27
- // Bumped when migration logic changes. Stored under the NEW prefix so the
28
- // flag survives the rename and only triggers once per (project, version).
29
- private const int MigrationVersion = 1;
30
- private static readonly string MigrationDoneKey =
31
- $"{ProductInfo.PackageId}.{Fnv1a32(Application.dataPath):X8}.PrefsKeyMigration.Version";
32
-
33
- static PrefsKeyMigration()
34
- {
35
- try
36
- {
37
- if (EditorPrefs.GetInt(MigrationDoneKey, 0) >= MigrationVersion)
38
- return;
39
-
40
- MigrateToolGroupsEnabled();
41
- MigrateUpdateCheckerKeys();
42
- MigrateServerProcessId();
43
-
44
- EditorPrefs.SetInt(MigrationDoneKey, MigrationVersion);
45
- }
46
- catch (System.Exception ex)
47
- {
48
- // Migration failure must never break Editor startup. Worst case the user
49
- // re-sees the update popup once or re-configures tool groups.
50
- Debug.LogWarning($"{ProductInfo.LogPrefix} PrefsKeyMigration skipped: {ex.Message}");
51
- }
52
- }
53
-
54
- // ToolGroupRegistry persists enabled tool-group names under a flat key — no
55
- // per-project hash because the registry is project-local already (Unity creates
56
- // a fresh EditorPrefs scope per install, and the value is just a CSV).
57
- private static void MigrateToolGroupsEnabled()
58
- {
59
- const string oldKey = "UnityMcp.ToolGroups.Enabled";
60
- string newKey = $"{ProductInfo.EditorPrefsPrefix}.ToolGroups.Enabled";
61
- if (oldKey == newKey) return;
62
- if (!EditorPrefs.HasKey(oldKey)) return;
63
- if (EditorPrefs.HasKey(newKey)) return; // user already configured under new name
64
- EditorPrefs.SetString(newKey, EditorPrefs.GetString(oldKey));
65
- }
66
-
67
- // UpdateChecker namespaces keys by (packageId, FNV-1a(Application.dataPath)) so a
68
- // user with multiple Unity projects keeps independent "Do not show again" state.
69
- // After rebrand the packageId segment changes, so for THIS project's hash we copy
70
- // each of the three sub-keys forward.
71
- private static void MigrateUpdateCheckerKeys()
72
- {
73
- string hash = $"{Fnv1a32(Application.dataPath):X8}";
74
- string oldPrefix = $"{ProductInfo.LegacyPackageId}.{hash}.UpdateChecker.";
75
- string newPrefix = $"{ProductInfo.PackageId}.{hash}.UpdateChecker.";
76
- if (oldPrefix == newPrefix) return;
77
-
78
- CopyStringIfMissing(oldPrefix + "DoNotShowAgain", newPrefix + "DoNotShowAgain");
79
- CopyStringIfMissing(oldPrefix + "NextCheckTime", newPrefix + "NextCheckTime");
80
- CopyStringIfMissing(oldPrefix + "SkippedVersion", newPrefix + "SkippedVersion");
81
- }
82
-
83
- // ServerManager pid is transient (server respawns) but copying avoids a stale
84
- // "is the server already running?" probe on the first reload after rebrand.
85
- private static void MigrateServerProcessId()
86
- {
87
- const string oldKey = "McpServerManager_ProcessId";
88
- const string newKey = "CopilotServerManager_ProcessId";
89
- if (!EditorPrefs.HasKey(oldKey)) return;
90
- if (EditorPrefs.HasKey(newKey)) return;
91
- EditorPrefs.SetInt(newKey, EditorPrefs.GetInt(oldKey));
92
- }
93
-
94
- private static void CopyStringIfMissing(string oldKey, string newKey)
95
- {
96
- if (!EditorPrefs.HasKey(oldKey)) return;
97
- if (EditorPrefs.HasKey(newKey)) return;
98
- EditorPrefs.SetString(newKey, EditorPrefs.GetString(oldKey));
99
- }
100
-
101
- // Duplicated from UpdateChecker.cs — extracting to a shared util is a larger
102
- // refactor than this phase warrants. Same FNV-1a (offsetBasis 2166136261,
103
- // prime 16777619), same per-UTF-16-codepoint two-byte loop.
104
- private static uint Fnv1a32(string input)
105
- {
106
- const uint offsetBasis = 2166136261u;
107
- const uint prime = 16777619u;
108
- var hash = offsetBasis;
109
- for (int i = 0; i < input.Length; i++)
110
- {
111
- hash ^= (byte)(input[i] & 0xFF);
112
- hash *= prime;
113
- hash ^= (byte)((input[i] >> 8) & 0xFF);
114
- hash *= prime;
115
- }
116
- return hash;
117
- }
118
- }
119
- }
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 com.AtelierAI.Unity.Copilot.Editor.Branding;
13
+ using UnityEditor;
14
+ using UnityEngine;
15
+
16
+ namespace com.AtelierAI.Unity.Copilot.Editor.Utils
17
+ {
18
+ // Phase C rebrand carries forward existing user state across the
19
+ // "com.ivanmurzak.unity.plugin" → "com.atelierai.unity.copilot" key namespace shift.
20
+ // Runs once per project (guarded by MigrationDoneKey) on domain reload and copies
21
+ // old EditorPrefs keys into the new namespace. Old keys are left in place — they're
22
+ // harmless and lets users downgrade if the rebrand is reverted. A future major
23
+ // version can delete this class and the old keys together.
24
+ [InitializeOnLoad]
25
+ internal static class PrefsKeyMigration
26
+ {
27
+ // Bumped when migration logic changes. Stored under the NEW prefix so the
28
+ // flag survives the rename and only triggers once per (project, version).
29
+ private const int MigrationVersion = 1;
30
+ private static readonly string MigrationDoneKey =
31
+ $"{ProductInfo.PackageId}.{Fnv1a32(Application.dataPath):X8}.PrefsKeyMigration.Version";
32
+
33
+ static PrefsKeyMigration()
34
+ {
35
+ try
36
+ {
37
+ if (EditorPrefs.GetInt(MigrationDoneKey, 0) >= MigrationVersion)
38
+ return;
39
+
40
+ MigrateToolGroupsEnabled();
41
+ MigrateUpdateCheckerKeys();
42
+ MigrateServerProcessId();
43
+
44
+ EditorPrefs.SetInt(MigrationDoneKey, MigrationVersion);
45
+ }
46
+ catch (System.Exception ex)
47
+ {
48
+ // Migration failure must never break Editor startup. Worst case the user
49
+ // re-sees the update popup once or re-configures tool groups.
50
+ Debug.LogWarning($"{ProductInfo.LogPrefix} PrefsKeyMigration skipped: {ex.Message}");
51
+ }
52
+ }
53
+
54
+ // ToolGroupRegistry persists enabled tool-group names under a flat key — no
55
+ // per-project hash because the registry is project-local already (Unity creates
56
+ // a fresh EditorPrefs scope per install, and the value is just a CSV).
57
+ private static void MigrateToolGroupsEnabled()
58
+ {
59
+ const string oldKey = "UnityMcp.ToolGroups.Enabled";
60
+ string newKey = $"{ProductInfo.EditorPrefsPrefix}.ToolGroups.Enabled";
61
+ if (oldKey == newKey) return;
62
+ if (!EditorPrefs.HasKey(oldKey)) return;
63
+ if (EditorPrefs.HasKey(newKey)) return; // user already configured under new name
64
+ EditorPrefs.SetString(newKey, EditorPrefs.GetString(oldKey));
65
+ }
66
+
67
+ // UpdateChecker namespaces keys by (packageId, FNV-1a(Application.dataPath)) so a
68
+ // user with multiple Unity projects keeps independent "Do not show again" state.
69
+ // After rebrand the packageId segment changes, so for THIS project's hash we copy
70
+ // each of the three sub-keys forward.
71
+ private static void MigrateUpdateCheckerKeys()
72
+ {
73
+ string hash = $"{Fnv1a32(Application.dataPath):X8}";
74
+ string oldPrefix = $"{ProductInfo.LegacyPackageId}.{hash}.UpdateChecker.";
75
+ string newPrefix = $"{ProductInfo.PackageId}.{hash}.UpdateChecker.";
76
+ if (oldPrefix == newPrefix) return;
77
+
78
+ CopyStringIfMissing(oldPrefix + "DoNotShowAgain", newPrefix + "DoNotShowAgain");
79
+ CopyStringIfMissing(oldPrefix + "NextCheckTime", newPrefix + "NextCheckTime");
80
+ CopyStringIfMissing(oldPrefix + "SkippedVersion", newPrefix + "SkippedVersion");
81
+ }
82
+
83
+ // ServerManager pid is transient (server respawns) but copying avoids a stale
84
+ // "is the server already running?" probe on the first reload after rebrand.
85
+ private static void MigrateServerProcessId()
86
+ {
87
+ const string oldKey = "McpServerManager_ProcessId";
88
+ const string newKey = "CopilotServerManager_ProcessId";
89
+ if (!EditorPrefs.HasKey(oldKey)) return;
90
+ if (EditorPrefs.HasKey(newKey)) return;
91
+ EditorPrefs.SetInt(newKey, EditorPrefs.GetInt(oldKey));
92
+ }
93
+
94
+ private static void CopyStringIfMissing(string oldKey, string newKey)
95
+ {
96
+ if (!EditorPrefs.HasKey(oldKey)) return;
97
+ if (EditorPrefs.HasKey(newKey)) return;
98
+ EditorPrefs.SetString(newKey, EditorPrefs.GetString(oldKey));
99
+ }
100
+
101
+ // Duplicated from UpdateChecker.cs — extracting to a shared util is a larger
102
+ // refactor than this phase warrants. Same FNV-1a (offsetBasis 2166136261,
103
+ // prime 16777619), same per-UTF-16-codepoint two-byte loop.
104
+ private static uint Fnv1a32(string input)
105
+ {
106
+ const uint offsetBasis = 2166136261u;
107
+ const uint prime = 16777619u;
108
+ var hash = offsetBasis;
109
+ for (int i = 0; i < input.Length; i++)
110
+ {
111
+ hash ^= (byte)(input[i] & 0xFF);
112
+ hash *= prime;
113
+ hash ^= (byte)((input[i] >> 8) & 0xFF);
114
+ hash *= prime;
115
+ }
116
+ return hash;
117
+ }
118
+ }
119
+ }
@@ -1,180 +1,180 @@
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.Net;
14
- using System.Net.Sockets;
15
-
16
- namespace com.AtelierAI.Unity.Copilot.Editor.Utils
17
- {
18
- /// <summary>
19
- /// Centralised "is the current configuration safe to start?" policy. Implements the
20
- /// fail-closed posture described on <see cref="UnityCopilotPlugin.UnityConnectionConfig.AllowLanBind"/>:
21
- /// the local MCP server only auto-starts on a non-loopback bind / over insecure remote HTTP
22
- /// when the user has explicitly opted in via the corresponding flag, AND any LAN bind
23
- /// additionally requires a token when <c>ForceTokenWhenLanBind</c> is true.
24
- ///
25
- /// The guard is purely advisory at the URL-parsing layer (no Unity API access) so it can be
26
- /// unit-tested without entering Play mode. The only Unity-side side effect lives in
27
- /// <see cref="ShouldRejectStart"/>, which reads the editor's current config.
28
- /// </summary>
29
- public static class SafeDefaultsGuard
30
- {
31
- /// <summary>
32
- /// Returns true when the given URL has a host that is NOT a loopback address.
33
- /// Loopback covers <c>localhost</c>, <c>127.0.0.0/8</c>, and <c>::1</c>.
34
- /// Wildcard binds (<c>0.0.0.0</c>, <c>::</c>, <c>*</c>, <c>+</c>) and concrete LAN IPs
35
- /// or hostnames are treated as non-loopback (and therefore "LAN bind" for the purposes
36
- /// of the guard).
37
- ///
38
- /// Defensive parsing: an unparseable / empty URL returns false so the guard never
39
- /// blocks startup on a corrupted host string — the existing host validation in the
40
- /// connection layer remains the authoritative parser.
41
- /// </summary>
42
- public static bool IsNonLoopbackUrl(string? url)
43
- {
44
- if (string.IsNullOrWhiteSpace(url))
45
- return false;
46
-
47
- if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
48
- return false;
49
-
50
- return IsNonLoopbackHost(uri.Host);
51
- }
52
-
53
- /// <summary>
54
- /// Returns true when the bare host string represents a non-loopback bind target.
55
- /// Used by <see cref="IsNonLoopbackUrl"/> and by callers that already extracted
56
- /// the host (e.g. server arg builders).
57
- /// </summary>
58
- public static bool IsNonLoopbackHost(string? host)
59
- {
60
- if (string.IsNullOrWhiteSpace(host))
61
- return false;
62
-
63
- var h = host.Trim().Trim('[', ']');
64
-
65
- // Wildcard / all-interfaces literals used by Kestrel and friends.
66
- if (h == "0.0.0.0" || h == "::" || h == "*" || h == "+")
67
- return true;
68
-
69
- // Common loopback hostname.
70
- if (h.Equals("localhost", StringComparison.OrdinalIgnoreCase))
71
- return false;
72
-
73
- if (IPAddress.TryParse(h, out var ip))
74
- {
75
- // IPAddress.Loopback covers 127.0.0.1 and ::1; IsLoopback covers the whole
76
- // 127.0.0.0/8 range and the v6 loopback. Anything else (LAN, public, link-local)
77
- // is treated as non-loopback.
78
- return !IPAddress.IsLoopback(ip);
79
- }
80
-
81
- // Non-IP hostnames (LAN names like "build-server.local", public hostnames, etc.)
82
- // are not loopback. The only special case we already handled is "localhost".
83
- return true;
84
- }
85
-
86
- /// <summary>
87
- /// True when the URL uses plain <c>http://</c> AND targets a non-loopback host.
88
- /// HTTPS endpoints, loopback HTTP, and unparseable URLs all return false — the
89
- /// guard does not block them.
90
- /// </summary>
91
- public static bool IsInsecureRemoteHttpUrl(string? url)
92
- {
93
- if (string.IsNullOrWhiteSpace(url))
94
- return false;
95
-
96
- if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
97
- return false;
98
-
99
- if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
100
- return false;
101
-
102
- return IsNonLoopbackHost(uri.Host);
103
- }
104
-
105
- /// <summary>
106
- /// Pure (no Unity API) evaluation of the fail-closed policy. Returns true when a
107
- /// caller should refuse to start the local MCP server with the supplied configuration.
108
- /// <paramref name="reason"/> contains a human-readable explanation when the result
109
- /// is <c>true</c>; otherwise it is the empty string.
110
- ///
111
- /// Policy:
112
- /// 1. LocalHost resolves to a non-loopback host AND AllowLanBind is false → reject.
113
- /// 2. LocalHost is a non-loopback bind, AllowLanBind is true, but
114
- /// ForceTokenWhenLanBind is true and either AuthOption != required or Token is
115
- /// empty → reject (LAN exposure without a token is a footgun).
116
- /// 3. Otherwise → safe.
117
- ///
118
- /// Insecure remote HTTP is NOT covered here — it is an advisory the configurators
119
- /// emit when they generate client config blocks; the server-start path does not gate
120
- /// on it because the local server is the *target* of the URL, not the *consumer*.
121
- /// </summary>
122
- public static bool ShouldRejectStart(
123
- string? localHost,
124
- bool allowLanBind,
125
- bool forceTokenWhenLanBind,
126
- global::com.AtelierAI.Uco.Framework.Common.Consts.MCP.Server.AuthOption authOption,
127
- string? token,
128
- out string reason)
129
- {
130
- reason = string.Empty;
131
-
132
- var isLan = IsNonLoopbackUrl(localHost);
133
- if (!isLan)
134
- return false;
135
-
136
- if (!allowLanBind)
137
- {
138
- reason =
139
- $"LocalHost '{localHost}' binds to a non-loopback interface but " +
140
- "'Allow LAN Bind' is disabled. Open the Game Developer window → " +
141
- "'Network Safety' section and enable 'Allow LAN Bind' to expose the " +
142
- "MCP server to the network. The safe default is loopback only.";
143
- return true;
144
- }
145
-
146
- if (forceTokenWhenLanBind)
147
- {
148
- var tokenRequired = authOption == global::com.AtelierAI.Uco.Framework.Common.Consts.MCP.Server.AuthOption.required;
149
- if (!tokenRequired || string.IsNullOrEmpty(token))
150
- {
151
- reason =
152
- $"LocalHost '{localHost}' binds to a non-loopback interface but " +
153
- "authorization is not 'required' or the token is empty. Either " +
154
- "set Authorization to 'required' and generate a token, or disable " +
155
- "'Force Token When LAN Bind' in the Game Developer window (not " +
156
- "recommended).";
157
- return true;
158
- }
159
- }
160
-
161
- return false;
162
- }
163
-
164
- /// <summary>
165
- /// Editor-side wrapper that reads the current <see cref="UnityCopilotPluginEditor"/>
166
- /// configuration and delegates to the pure overload. Kept as a thin facade so the
167
- /// pure overload is the unit-testable surface and the editor surface stays small.
168
- /// </summary>
169
- public static bool ShouldRejectStart(out string reason)
170
- {
171
- return ShouldRejectStart(
172
- localHost: UnityCopilotPluginEditor.LocalHost,
173
- allowLanBind: UnityCopilotPluginEditor.AllowLanBind,
174
- forceTokenWhenLanBind: UnityCopilotPluginEditor.ForceTokenWhenLanBind,
175
- authOption: UnityCopilotPluginEditor.AuthOption,
176
- token: UnityCopilotPluginEditor.Token,
177
- out reason);
178
- }
179
- }
180
- }
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.Net;
14
+ using System.Net.Sockets;
15
+
16
+ namespace com.AtelierAI.Unity.Copilot.Editor.Utils
17
+ {
18
+ /// <summary>
19
+ /// Centralised "is the current configuration safe to start?" policy. Implements the
20
+ /// fail-closed posture described on <see cref="UnityCopilotPlugin.UnityConnectionConfig.AllowLanBind"/>:
21
+ /// the local Uco server only auto-starts on a non-loopback bind / over insecure remote HTTP
22
+ /// when the user has explicitly opted in via the corresponding flag, AND any LAN bind
23
+ /// additionally requires a token when <c>ForceTokenWhenLanBind</c> is true.
24
+ ///
25
+ /// The guard is purely advisory at the URL-parsing layer (no Unity API access) so it can be
26
+ /// unit-tested without entering Play mode. The only Unity-side side effect lives in
27
+ /// <see cref="ShouldRejectStart"/>, which reads the editor's current config.
28
+ /// </summary>
29
+ public static class SafeDefaultsGuard
30
+ {
31
+ /// <summary>
32
+ /// Returns true when the given URL has a host that is NOT a loopback address.
33
+ /// Loopback covers <c>localhost</c>, <c>127.0.0.0/8</c>, and <c>::1</c>.
34
+ /// Wildcard binds (<c>0.0.0.0</c>, <c>::</c>, <c>*</c>, <c>+</c>) and concrete LAN IPs
35
+ /// or hostnames are treated as non-loopback (and therefore "LAN bind" for the purposes
36
+ /// of the guard).
37
+ ///
38
+ /// Defensive parsing: an unparseable / empty URL returns false so the guard never
39
+ /// blocks startup on a corrupted host string — the existing host validation in the
40
+ /// connection layer remains the authoritative parser.
41
+ /// </summary>
42
+ public static bool IsNonLoopbackUrl(string? url)
43
+ {
44
+ if (string.IsNullOrWhiteSpace(url))
45
+ return false;
46
+
47
+ if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
48
+ return false;
49
+
50
+ return IsNonLoopbackHost(uri.Host);
51
+ }
52
+
53
+ /// <summary>
54
+ /// Returns true when the bare host string represents a non-loopback bind target.
55
+ /// Used by <see cref="IsNonLoopbackUrl"/> and by callers that already extracted
56
+ /// the host (e.g. server arg builders).
57
+ /// </summary>
58
+ public static bool IsNonLoopbackHost(string? host)
59
+ {
60
+ if (string.IsNullOrWhiteSpace(host))
61
+ return false;
62
+
63
+ var h = host.Trim().Trim('[', ']');
64
+
65
+ // Wildcard / all-interfaces literals used by Kestrel and friends.
66
+ if (h == "0.0.0.0" || h == "::" || h == "*" || h == "+")
67
+ return true;
68
+
69
+ // Common loopback hostname.
70
+ if (h.Equals("localhost", StringComparison.OrdinalIgnoreCase))
71
+ return false;
72
+
73
+ if (IPAddress.TryParse(h, out var ip))
74
+ {
75
+ // IPAddress.Loopback covers 127.0.0.1 and ::1; IsLoopback covers the whole
76
+ // 127.0.0.0/8 range and the v6 loopback. Anything else (LAN, public, link-local)
77
+ // is treated as non-loopback.
78
+ return !IPAddress.IsLoopback(ip);
79
+ }
80
+
81
+ // Non-IP hostnames (LAN names like "build-server.local", public hostnames, etc.)
82
+ // are not loopback. The only special case we already handled is "localhost".
83
+ return true;
84
+ }
85
+
86
+ /// <summary>
87
+ /// True when the URL uses plain <c>http://</c> AND targets a non-loopback host.
88
+ /// HTTPS endpoints, loopback HTTP, and unparseable URLs all return false — the
89
+ /// guard does not block them.
90
+ /// </summary>
91
+ public static bool IsInsecureRemoteHttpUrl(string? url)
92
+ {
93
+ if (string.IsNullOrWhiteSpace(url))
94
+ return false;
95
+
96
+ if (!Uri.TryCreate(url, UriKind.Absolute, out var uri))
97
+ return false;
98
+
99
+ if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))
100
+ return false;
101
+
102
+ return IsNonLoopbackHost(uri.Host);
103
+ }
104
+
105
+ /// <summary>
106
+ /// Pure (no Unity API) evaluation of the fail-closed policy. Returns true when a
107
+ /// caller should refuse to start the local Uco server with the supplied configuration.
108
+ /// <paramref name="reason"/> contains a human-readable explanation when the result
109
+ /// is <c>true</c>; otherwise it is the empty string.
110
+ ///
111
+ /// Policy:
112
+ /// 1. LocalHost resolves to a non-loopback host AND AllowLanBind is false → reject.
113
+ /// 2. LocalHost is a non-loopback bind, AllowLanBind is true, but
114
+ /// ForceTokenWhenLanBind is true and either AuthOption != required or Token is
115
+ /// empty → reject (LAN exposure without a token is a footgun).
116
+ /// 3. Otherwise → safe.
117
+ ///
118
+ /// Insecure remote HTTP is NOT covered here — it is an advisory the configurators
119
+ /// emit when they generate client config blocks; the server-start path does not gate
120
+ /// on it because the local server is the *target* of the URL, not the *consumer*.
121
+ /// </summary>
122
+ public static bool ShouldRejectStart(
123
+ string? localHost,
124
+ bool allowLanBind,
125
+ bool forceTokenWhenLanBind,
126
+ global::com.AtelierAI.Uco.Framework.Common.Consts.Uco.Server.AuthOption authOption,
127
+ string? token,
128
+ out string reason)
129
+ {
130
+ reason = string.Empty;
131
+
132
+ var isLan = IsNonLoopbackUrl(localHost);
133
+ if (!isLan)
134
+ return false;
135
+
136
+ if (!allowLanBind)
137
+ {
138
+ reason =
139
+ $"LocalHost '{localHost}' binds to a non-loopback interface but " +
140
+ "'Allow LAN Bind' is disabled. Open the Game Developer window → " +
141
+ "'Network Safety' section and enable 'Allow LAN Bind' to expose the " +
142
+ "Uco server to the network. The safe default is loopback only.";
143
+ return true;
144
+ }
145
+
146
+ if (forceTokenWhenLanBind)
147
+ {
148
+ var tokenRequired = authOption == global::com.AtelierAI.Uco.Framework.Common.Consts.Uco.Server.AuthOption.required;
149
+ if (!tokenRequired || string.IsNullOrEmpty(token))
150
+ {
151
+ reason =
152
+ $"LocalHost '{localHost}' binds to a non-loopback interface but " +
153
+ "authorization is not 'required' or the token is empty. Either " +
154
+ "set Authorization to 'required' and generate a token, or disable " +
155
+ "'Force Token When LAN Bind' in the Game Developer window (not " +
156
+ "recommended).";
157
+ return true;
158
+ }
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ /// <summary>
165
+ /// Editor-side wrapper that reads the current <see cref="UnityCopilotPluginEditor"/>
166
+ /// configuration and delegates to the pure overload. Kept as a thin facade so the
167
+ /// pure overload is the unit-testable surface and the editor surface stays small.
168
+ /// </summary>
169
+ public static bool ShouldRejectStart(out string reason)
170
+ {
171
+ return ShouldRejectStart(
172
+ localHost: UnityCopilotPluginEditor.LocalHost,
173
+ allowLanBind: UnityCopilotPluginEditor.AllowLanBind,
174
+ forceTokenWhenLanBind: UnityCopilotPluginEditor.ForceTokenWhenLanBind,
175
+ authOption: UnityCopilotPluginEditor.AuthOption,
176
+ token: UnityCopilotPluginEditor.Token,
177
+ out reason);
178
+ }
179
+ }
180
+ }