@atelierai/uco 1.0.7 → 1.0.8

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 (87) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +1 -1
  3. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetConfig.cs +132 -132
  4. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDependencyResolver.cs +237 -237
  5. package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDownloader.cs +82 -82
  6. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/MenuItems.cs +247 -247
  7. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/ToolGroups.cs +153 -153
  8. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/SystemTool/Skills.Create.SkillBody.cs +155 -155
  9. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Console.GetLogs.cs +95 -95
  10. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Docs.HttpFetcher.cs +139 -139
  11. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Instance.ListAll.cs +72 -72
  12. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/PathReadHelper.cs +106 -106
  13. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Script.Validate.cs +252 -252
  14. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests/TestResultCollector.cs +537 -537
  15. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests.cs +400 -400
  16. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Branding/ProductInfo.cs +62 -62
  17. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/CopilotServerManager.cs +1034 -1034
  18. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Skills/Skill_InitialSetup.cs +197 -197
  19. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.Editor.cs +201 -201
  20. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.cs +67 -67
  21. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotListWindowBase.cs +393 -393
  22. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotWindowBase.cs +113 -113
  23. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.Connection.cs +3 -3
  24. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotFeatures.cs +195 -195
  25. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotServer.cs +20 -20
  26. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CreateGUI.cs +354 -354
  27. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.cs +78 -78
  28. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowInitializer.cs +47 -47
  29. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/SerializationCheckWindow.cs +190 -190
  30. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Build.cs +91 -91
  31. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs +298 -298
  32. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Static.cs +296 -296
  33. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.cs +69 -69
  34. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PackageUtils.cs +168 -168
  35. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PrefsKeyMigration.cs +119 -119
  36. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/SafeDefaultsGuard.cs +180 -180
  37. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ScriptUtils.cs +229 -229
  38. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolsManifestGenerator.cs +200 -200
  39. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringAdapters.cs +1467 -1467
  40. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringSafetyReloadGuard.cs +65 -65
  41. package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityCopilotUpdateProjectSettings.cs +66 -66
  42. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/ReflectorNet.dll +0 -0
  43. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.Common.dll +0 -0
  44. package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.dll +0 -0
  45. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Converter/Json/Structures/EntityIdConverter.cs +120 -120
  46. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.cs +110 -110
  47. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.pre-Unity.6.5.cs +110 -110
  48. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Unity/Logs/FileLogStorage.cs +547 -547
  49. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Build.cs +243 -243
  50. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Config.cs +3 -3
  51. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.cs +295 -295
  52. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginBuilder.cs +105 -105
  53. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.Static.cs +155 -155
  54. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.cs +59 -59
  55. package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Utils/EnvironmentUtils.cs +11 -11
  56. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/AtomicApiTestFixtures.cs +99 -99
  57. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/TryModifyAtTests.cs +227 -227
  58. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AuthoringSafetyPilotTests.cs +1050 -1050
  59. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetLegacyMigrationTests.cs +304 -304
  60. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Environment/EnvironmentUtilsTests.cs +1 -1
  61. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/BlacklistTypeTests.cs +416 -416
  62. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.cs +172 -172
  63. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.pre-Unity.6.5.cs +169 -169
  64. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.cs +182 -182
  65. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.pre-Unity.6.5.cs +179 -179
  66. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.cs +175 -175
  67. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.pre-Unity.6.5.cs +174 -174
  68. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.cs +171 -171
  69. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.pre-Unity.6.5.cs +170 -170
  70. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsCreateFolderTests.cs +243 -243
  71. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsMaterialTests.cs +101 -101
  72. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.cs +348 -348
  73. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.pre-Unity.6.5.cs +348 -348
  74. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsShaderTests.cs +194 -194
  75. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.cs +573 -573
  76. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.pre-Unity.6.5.cs +513 -513
  77. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.cs +154 -154
  78. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.pre-Unity.6.5.cs +154 -154
  79. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotGameViewTests.cs +164 -164
  80. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotSceneViewTests.cs +155 -155
  81. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolParameterTests.cs +56 -56
  82. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.cs +651 -651
  83. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.pre-Unity.6.5.cs +651 -651
  84. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ToolBatchExecutionIntegrationTests.cs +407 -407
  85. package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs → UcoPluginTests.cs} +105 -105
  86. package/vendor/plugin/com.atelierai.unity.copilot/package.json +1 -1
  87. /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
+ }