chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,892 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Compile request data for session management.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CompileRequestData
|
||||
{
|
||||
public string requestId;
|
||||
public string json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity MCP Editor settings data.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public record McpEditorSettingsData
|
||||
{
|
||||
public int customPort = McpServerConfig.DEFAULT_PORT;
|
||||
public string projectRootPath = "";
|
||||
public string serverSessionId = "";
|
||||
public bool showDeveloperTools = false;
|
||||
public bool enableMcpLogs = false;
|
||||
public bool enableCommunicationLogs = false;
|
||||
public bool enableDevelopmentMode = false;
|
||||
public string lastUsedConfigPath = "";
|
||||
public string lastSeenSetupWizardVersion = "";
|
||||
public bool suppressSetupWizardAutoShow = false;
|
||||
|
||||
// UI State Settings
|
||||
public bool showSecuritySettings = true;
|
||||
public bool showToolSettings = true;
|
||||
|
||||
// Repository Root Toggle
|
||||
public bool addRepositoryRoot = false;
|
||||
// Default to flat installation so first-time setup does not add an extra grouping layer unless requested.
|
||||
public bool installSkillsFlat = true;
|
||||
|
||||
// Session State Settings (moved from McpSessionManager)
|
||||
// Default to true so the server starts automatically on fresh install
|
||||
public bool isServerRunning = true;
|
||||
public bool isAfterCompile = false;
|
||||
public bool isDomainReloadInProgress = false;
|
||||
public bool isReconnecting = false;
|
||||
public bool showReconnectingUI = false;
|
||||
public bool showPostCompileReconnectingUI = false;
|
||||
public int selectedEditorType = (int)McpEditorType.Cursor;
|
||||
public int connectionMode = (int)ConnectionMode.CLI;
|
||||
public float communicationLogHeight = McpUIConstants.DEFAULT_COMMUNICATION_LOG_HEIGHT;
|
||||
public string communicationLogsJson = "[]";
|
||||
public string pendingRequestsJson = "{}";
|
||||
public bool compileWindowHasData = false;
|
||||
public string[] pendingCompileRequestIds = new string[0];
|
||||
public CompileRequestData[] compileRequests = new CompileRequestData[0];
|
||||
public ConnectedLLMToolData[] connectedLLMTools = new ConnectedLLMToolData[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Management class for Unity MCP Editor settings.
|
||||
/// Saves as a JSON file in the UserSettings folder.
|
||||
/// </summary>
|
||||
public static class McpEditorSettings
|
||||
{
|
||||
private static string SettingsFilePath => Path.Combine(McpConstants.USER_SETTINGS_FOLDER, McpConstants.SETTINGS_FILE_NAME);
|
||||
|
||||
private static McpEditorSettingsData _cachedSettings;
|
||||
|
||||
internal static void InvalidateCache()
|
||||
{
|
||||
_cachedSettings = null;
|
||||
}
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void RecoverSettingsFileOnEditorLoad()
|
||||
{
|
||||
if (AssetDatabase.IsAssetImportWorkerProcess())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RecoverSettingsFileIfNeeded();
|
||||
}
|
||||
|
||||
internal static void RecoverSettingsFileIfNeeded()
|
||||
{
|
||||
if (!IsValidSettingsPath(SettingsFilePath))
|
||||
{
|
||||
throw new SecurityException($"Invalid settings file path: {SettingsFilePath}");
|
||||
}
|
||||
|
||||
AtomicFileWriter.RecoverSidecarFiles(SettingsFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the settings data.
|
||||
/// </summary>
|
||||
public static McpEditorSettingsData GetSettings()
|
||||
{
|
||||
if (_cachedSettings == null)
|
||||
{
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
return _cachedSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the settings data.
|
||||
/// </summary>
|
||||
public static void SaveSettings(McpEditorSettingsData settings)
|
||||
{
|
||||
// Security: Validate settings file path
|
||||
if (!IsValidSettingsPath(SettingsFilePath))
|
||||
{
|
||||
throw new SecurityException($"Invalid settings file path: {SettingsFilePath}");
|
||||
}
|
||||
|
||||
// Security: Ensure directory exists and create it safely
|
||||
string directory = Path.GetDirectoryName(SettingsFilePath);
|
||||
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
string json = JsonUtility.ToJson(settings, true);
|
||||
|
||||
// Security: Validate JSON content size
|
||||
if (json.Length > McpConstants.MAX_SETTINGS_SIZE_BYTES)
|
||||
{
|
||||
throw new SecurityException("Settings JSON content exceeds size limit");
|
||||
}
|
||||
|
||||
AtomicFileWriter.Write(SettingsFilePath, json);
|
||||
_cachedSettings = settings;
|
||||
|
||||
// Best-effort cleanup: even if this fails, .bak is overwritten on next save
|
||||
AtomicFileWriter.CleanupBackup(SettingsFilePath + ".bak");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a transformation to the current settings and saves once.
|
||||
/// Use when multiple fields need to be updated together to avoid redundant writes.
|
||||
/// </summary>
|
||||
public static void UpdateSettings(Func<McpEditorSettingsData, McpEditorSettingsData> transform)
|
||||
{
|
||||
Debug.Assert(transform != null, "transform must not be null");
|
||||
|
||||
McpEditorSettingsData current = GetSettings();
|
||||
McpEditorSettingsData updated = transform(current);
|
||||
SaveSettings(updated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom port number.
|
||||
/// </summary>
|
||||
public static int GetCustomPort()
|
||||
{
|
||||
return GetSettings().customPort;
|
||||
}
|
||||
|
||||
public static string GetProjectRootPath()
|
||||
{
|
||||
return GetSettings().projectRootPath ?? string.Empty;
|
||||
}
|
||||
|
||||
public static string GetServerSessionId()
|
||||
{
|
||||
return GetSettings().serverSessionId ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the custom port number.
|
||||
/// </summary>
|
||||
public static void SetCustomPort(int port)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData updatedSettings = settings with { customPort = port };
|
||||
SaveSettings(updatedSettings);
|
||||
}
|
||||
|
||||
public static void SetRunningServerSession(int port, string projectRootPath, string serverSessionId)
|
||||
{
|
||||
Debug.Assert(port > 0, "port must be positive");
|
||||
Debug.Assert(!string.IsNullOrWhiteSpace(projectRootPath), "projectRootPath must not be empty");
|
||||
Debug.Assert(!string.IsNullOrWhiteSpace(serverSessionId), "serverSessionId must not be empty");
|
||||
|
||||
string normalizedProjectRootPath = NormalizeProjectRootPath(projectRootPath);
|
||||
UpdateSettings(settings => settings with
|
||||
{
|
||||
isServerRunning = true,
|
||||
customPort = port,
|
||||
projectRootPath = normalizedProjectRootPath,
|
||||
serverSessionId = serverSessionId
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Developer Tools display setting.
|
||||
/// </summary>
|
||||
public static bool GetShowDeveloperTools()
|
||||
{
|
||||
return GetSettings().showDeveloperTools;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the Developer Tools display setting.
|
||||
/// </summary>
|
||||
public static void SetShowDeveloperTools(bool show)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData updatedSettings = settings with { showDeveloperTools = show };
|
||||
SaveSettings(updatedSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MCP log enabled flag.
|
||||
/// </summary>
|
||||
public static bool GetEnableMcpLogs()
|
||||
{
|
||||
return GetSettings().enableMcpLogs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the MCP log enabled flag.
|
||||
/// </summary>
|
||||
public static void SetEnableMcpLogs(bool enableMcpLogs)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { enableMcpLogs = enableMcpLogs };
|
||||
SaveSettings(newSettings);
|
||||
|
||||
// Synchronize McpLogger settings as well.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the communication logs enabled flag.
|
||||
/// </summary>
|
||||
public static bool GetEnableCommunicationLogs()
|
||||
{
|
||||
return GetSettings().enableCommunicationLogs;
|
||||
}
|
||||
|
||||
public static string GetLastSeenSetupWizardVersion()
|
||||
{
|
||||
return GetSettings().lastSeenSetupWizardVersion ?? string.Empty;
|
||||
}
|
||||
|
||||
public static void SetLastSeenSetupWizardVersion(string version)
|
||||
{
|
||||
string normalizedVersion = version ?? string.Empty;
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData updatedSettings = settings with { lastSeenSetupWizardVersion = normalizedVersion };
|
||||
SaveSettings(updatedSettings);
|
||||
}
|
||||
|
||||
public static bool GetSuppressSetupWizardAutoShow()
|
||||
{
|
||||
return GetSettings().suppressSetupWizardAutoShow;
|
||||
}
|
||||
|
||||
public static void SetSuppressSetupWizardAutoShow(bool suppressAutoShow)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData updatedSettings = settings with { suppressSetupWizardAutoShow = suppressAutoShow };
|
||||
SaveSettings(updatedSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the communication logs enabled flag.
|
||||
/// </summary>
|
||||
public static void SetEnableCommunicationLogs(bool enableCommunicationLogs)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { enableCommunicationLogs = enableCommunicationLogs };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the show security settings flag.
|
||||
/// </summary>
|
||||
public static bool GetShowSecuritySettings()
|
||||
{
|
||||
return GetSettings().showSecuritySettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show security settings flag.
|
||||
/// </summary>
|
||||
public static void SetShowSecuritySettings(bool showSecuritySettings)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { showSecuritySettings = showSecuritySettings };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
public static bool GetShowToolSettings()
|
||||
{
|
||||
return GetSettings().showToolSettings;
|
||||
}
|
||||
|
||||
public static void SetShowToolSettings(bool showToolSettings)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { showToolSettings = showToolSettings };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repository root usage flag.
|
||||
/// </summary>
|
||||
public static bool GetAddRepositoryRoot()
|
||||
{
|
||||
return GetSettings().addRepositoryRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the repository root usage flag.
|
||||
/// </summary>
|
||||
public static void SetAddRepositoryRoot(bool addRepositoryRoot)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { addRepositoryRoot = addRepositoryRoot };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
public static bool GetInstallSkillsFlat()
|
||||
{
|
||||
return GetSettings().installSkillsFlat;
|
||||
}
|
||||
|
||||
public static void SetInstallSkillsFlat(bool installSkillsFlat)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { installSkillsFlat = installSkillsFlat };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
// Session State Settings Methods (moved from McpSessionManager)
|
||||
|
||||
/// <summary>
|
||||
/// Gets the server running state.
|
||||
/// </summary>
|
||||
public static bool GetIsServerRunning()
|
||||
{
|
||||
return GetSettings().isServerRunning;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the server running state.
|
||||
/// </summary>
|
||||
public static void SetIsServerRunning(bool isServerRunning)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { isServerRunning = isServerRunning };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the after compile flag.
|
||||
/// </summary>
|
||||
public static bool GetIsAfterCompile()
|
||||
{
|
||||
return GetSettings().isAfterCompile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the after compile flag.
|
||||
/// </summary>
|
||||
public static void SetIsAfterCompile(bool isAfterCompile)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { isAfterCompile = isAfterCompile };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the domain reload in progress flag.
|
||||
/// </summary>
|
||||
public static bool GetIsDomainReloadInProgress()
|
||||
{
|
||||
return GetSettings().isDomainReloadInProgress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the domain reload in progress flag.
|
||||
/// </summary>
|
||||
public static void SetIsDomainReloadInProgress(bool isDomainReloadInProgress)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { isDomainReloadInProgress = isDomainReloadInProgress };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reconnecting flag.
|
||||
/// </summary>
|
||||
public static bool GetIsReconnecting()
|
||||
{
|
||||
return GetSettings().isReconnecting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the reconnecting flag.
|
||||
/// </summary>
|
||||
public static void SetIsReconnecting(bool isReconnecting)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { isReconnecting = isReconnecting };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the show reconnecting UI flag.
|
||||
/// </summary>
|
||||
public static bool GetShowReconnectingUI()
|
||||
{
|
||||
return GetSettings().showReconnectingUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show reconnecting UI flag.
|
||||
/// </summary>
|
||||
public static void SetShowReconnectingUI(bool showReconnectingUI)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { showReconnectingUI = showReconnectingUI };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the show post compile reconnecting UI flag.
|
||||
/// </summary>
|
||||
public static bool GetShowPostCompileReconnectingUI()
|
||||
{
|
||||
return GetSettings().showPostCompileReconnectingUI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the show post compile reconnecting UI flag.
|
||||
/// </summary>
|
||||
public static void SetShowPostCompileReconnectingUI(bool showPostCompileReconnectingUI)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { showPostCompileReconnectingUI = showPostCompileReconnectingUI };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected editor type.
|
||||
/// </summary>
|
||||
public static McpEditorType GetSelectedEditorType()
|
||||
{
|
||||
return (McpEditorType)GetSettings().selectedEditorType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the selected editor type.
|
||||
/// </summary>
|
||||
public static void SetSelectedEditorType(McpEditorType selectedEditorType)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { selectedEditorType = (int)selectedEditorType };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
public static ConnectionMode GetConnectionMode()
|
||||
{
|
||||
return (ConnectionMode)GetSettings().connectionMode;
|
||||
}
|
||||
|
||||
public static void SetConnectionMode(ConnectionMode mode)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { connectionMode = (int)mode };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the communication log height.
|
||||
/// </summary>
|
||||
public static float GetCommunicationLogHeight()
|
||||
{
|
||||
return GetSettings().communicationLogHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the communication log height.
|
||||
/// </summary>
|
||||
public static void SetCommunicationLogHeight(float communicationLogHeight)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { communicationLogHeight = communicationLogHeight };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the communication logs JSON.
|
||||
/// </summary>
|
||||
public static string GetCommunicationLogsJson()
|
||||
{
|
||||
return GetSettings().communicationLogsJson;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the communication logs JSON.
|
||||
/// </summary>
|
||||
public static void SetCommunicationLogsJson(string communicationLogsJson)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { communicationLogsJson = communicationLogsJson };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pending requests JSON.
|
||||
/// </summary>
|
||||
public static string GetPendingRequestsJson()
|
||||
{
|
||||
return GetSettings().pendingRequestsJson;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the pending requests JSON.
|
||||
/// </summary>
|
||||
public static void SetPendingRequestsJson(string pendingRequestsJson)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { pendingRequestsJson = pendingRequestsJson };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compile window has data flag.
|
||||
/// </summary>
|
||||
public static bool GetCompileWindowHasData()
|
||||
{
|
||||
return GetSettings().compileWindowHasData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compile window has data flag.
|
||||
/// </summary>
|
||||
public static void SetCompileWindowHasData(bool compileWindowHasData)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { compileWindowHasData = compileWindowHasData };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
// Helper methods that mirror McpSessionManager functionality
|
||||
|
||||
/// <summary>
|
||||
/// Clear server session.
|
||||
/// </summary>
|
||||
public static void ClearServerSession()
|
||||
{
|
||||
UpdateSettings(settings => settings with
|
||||
{
|
||||
isServerRunning = false,
|
||||
serverSessionId = string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear after compile flag.
|
||||
/// </summary>
|
||||
public static void ClearAfterCompileFlag()
|
||||
{
|
||||
SetIsAfterCompile(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear reconnecting flags.
|
||||
/// </summary>
|
||||
public static void ClearReconnectingFlags()
|
||||
{
|
||||
UpdateSettings(s => s with
|
||||
{
|
||||
isReconnecting = false,
|
||||
showReconnectingUI = false
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear post compile reconnecting UI.
|
||||
/// </summary>
|
||||
public static void ClearPostCompileReconnectingUI()
|
||||
{
|
||||
SetShowPostCompileReconnectingUI(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear domain reload flag.
|
||||
/// </summary>
|
||||
public static void ClearDomainReloadFlag()
|
||||
{
|
||||
SetIsDomainReloadInProgress(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear communication logs.
|
||||
/// </summary>
|
||||
public static void ClearCommunicationLogs()
|
||||
{
|
||||
UpdateSettings(s => s with
|
||||
{
|
||||
communicationLogsJson = "[]",
|
||||
pendingRequestsJson = "{}"
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear compile window data.
|
||||
/// </summary>
|
||||
public static void ClearCompileWindowData()
|
||||
{
|
||||
SetCompileWindowHasData(false);
|
||||
}
|
||||
|
||||
// Compile request management methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pending compile request IDs.
|
||||
/// </summary>
|
||||
public static string[] GetPendingCompileRequestIds()
|
||||
{
|
||||
return GetSettings().pendingCompileRequestIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the pending compile request IDs.
|
||||
/// </summary>
|
||||
public static void SetPendingCompileRequestIds(string[] pendingCompileRequestIds)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { pendingCompileRequestIds = pendingCompileRequestIds };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compile requests.
|
||||
/// </summary>
|
||||
public static CompileRequestData[] GetCompileRequests()
|
||||
{
|
||||
return GetSettings().compileRequests;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compile requests.
|
||||
/// </summary>
|
||||
public static void SetCompileRequests(CompileRequestData[] compileRequests)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { compileRequests = compileRequests };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compile request JSON by request ID.
|
||||
/// </summary>
|
||||
public static string GetCompileRequestJson(string requestId)
|
||||
{
|
||||
CompileRequestData[] requests = GetCompileRequests();
|
||||
CompileRequestData request = System.Array.Find(requests, r => r.requestId == requestId);
|
||||
return request?.json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compile request JSON for a specific request ID.
|
||||
/// </summary>
|
||||
public static void SetCompileRequestJson(string requestId, string json)
|
||||
{
|
||||
CompileRequestData[] requests = GetCompileRequests();
|
||||
CompileRequestData existingRequest = System.Array.Find(requests, r => r.requestId == requestId);
|
||||
|
||||
if (existingRequest != null)
|
||||
{
|
||||
existingRequest.json = json;
|
||||
}
|
||||
else
|
||||
{
|
||||
CompileRequestData[] newRequests = new CompileRequestData[requests.Length + 1];
|
||||
System.Array.Copy(requests, newRequests, requests.Length);
|
||||
newRequests[requests.Length] = new CompileRequestData { requestId = requestId, json = json };
|
||||
requests = newRequests;
|
||||
}
|
||||
|
||||
SetCompileRequests(requests);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all compile requests.
|
||||
/// </summary>
|
||||
public static void ClearAllCompileRequests()
|
||||
{
|
||||
SetCompileRequests(new CompileRequestData[0]);
|
||||
SetPendingCompileRequestIds(new string[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pending compile request.
|
||||
/// </summary>
|
||||
public static void AddPendingCompileRequest(string requestId)
|
||||
{
|
||||
string[] pendingIds = GetPendingCompileRequestIds();
|
||||
if (System.Array.IndexOf(pendingIds, requestId) == -1)
|
||||
{
|
||||
string[] newPendingIds = new string[pendingIds.Length + 1];
|
||||
System.Array.Copy(pendingIds, newPendingIds, pendingIds.Length);
|
||||
newPendingIds[pendingIds.Length] = requestId;
|
||||
SetPendingCompileRequestIds(newPendingIds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a pending compile request.
|
||||
/// </summary>
|
||||
public static void RemovePendingCompileRequest(string requestId)
|
||||
{
|
||||
string[] pendingIds = GetPendingCompileRequestIds();
|
||||
int index = System.Array.IndexOf(pendingIds, requestId);
|
||||
if (index != -1)
|
||||
{
|
||||
string[] newPendingIds = new string[pendingIds.Length - 1];
|
||||
System.Array.Copy(pendingIds, 0, newPendingIds, 0, index);
|
||||
System.Array.Copy(pendingIds, index + 1, newPendingIds, index, pendingIds.Length - index - 1);
|
||||
SetPendingCompileRequestIds(newPendingIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Connected LLM Tools management methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connected LLM tools.
|
||||
/// </summary>
|
||||
public static ConnectedLLMToolData[] GetConnectedLLMTools()
|
||||
{
|
||||
return GetSettings().connectedLLMTools;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the connected LLM tools.
|
||||
/// </summary>
|
||||
public static void SetConnectedLLMTools(ConnectedLLMToolData[] connectedLLMTools)
|
||||
{
|
||||
McpEditorSettingsData settings = GetSettings();
|
||||
McpEditorSettingsData newSettings = settings with { connectedLLMTools = connectedLLMTools };
|
||||
SaveSettings(newSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a connected LLM tool.
|
||||
/// </summary>
|
||||
public static void AddConnectedLLMTool(ConnectedLLMToolData toolData)
|
||||
{
|
||||
if (toolData == null || string.IsNullOrEmpty(toolData.Name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectedLLMToolData[] tools = GetConnectedLLMTools();
|
||||
|
||||
// Remove existing tool with same name if present
|
||||
ConnectedLLMToolData[] filteredTools = System.Array.FindAll(tools, t => t.Name != toolData.Name);
|
||||
|
||||
// Add new tool
|
||||
ConnectedLLMToolData[] newTools = new ConnectedLLMToolData[filteredTools.Length + 1];
|
||||
System.Array.Copy(filteredTools, newTools, filteredTools.Length);
|
||||
newTools[filteredTools.Length] = toolData;
|
||||
|
||||
SetConnectedLLMTools(newTools);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a connected LLM tool by name.
|
||||
/// </summary>
|
||||
public static void RemoveConnectedLLMTool(string toolName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(toolName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectedLLMToolData[] tools = GetConnectedLLMTools();
|
||||
ConnectedLLMToolData[] newTools = System.Array.FindAll(tools, t => t.Name != toolName);
|
||||
|
||||
if (newTools.Length != tools.Length)
|
||||
{
|
||||
SetConnectedLLMTools(newTools);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all connected LLM tools.
|
||||
/// </summary>
|
||||
public static void ClearConnectedLLMTools()
|
||||
{
|
||||
SetConnectedLLMTools(new ConnectedLLMToolData[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the settings file.
|
||||
/// </summary>
|
||||
private static void LoadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Security: Validate settings file path
|
||||
if (!IsValidSettingsPath(SettingsFilePath))
|
||||
{
|
||||
throw new SecurityException($"Invalid settings file path: {SettingsFilePath}");
|
||||
}
|
||||
|
||||
RecoverSettingsFileIfNeeded();
|
||||
|
||||
if (File.Exists(SettingsFilePath))
|
||||
{
|
||||
// Security: Check file size before reading
|
||||
FileInfo fileInfo = new FileInfo(SettingsFilePath);
|
||||
if (fileInfo.Length > McpConstants.MAX_SETTINGS_SIZE_BYTES)
|
||||
{
|
||||
throw new SecurityException("Settings file exceeds size limit");
|
||||
}
|
||||
|
||||
string json = File.ReadAllText(SettingsFilePath);
|
||||
|
||||
// Security: Validate JSON content
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
{
|
||||
throw new InvalidDataException("Settings file contains invalid JSON content");
|
||||
}
|
||||
|
||||
_cachedSettings = JsonUtility.FromJson<McpEditorSettingsData>(json);
|
||||
|
||||
// Migrate security fields before any potential SaveSettings call from this class.
|
||||
// If SaveSettings runs first, legacy security fields are stripped from JSON
|
||||
// because McpEditorSettingsData no longer defines them.
|
||||
ULoopSettings.GetSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedSettings = new McpEditorSettingsData();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Don't suppress this exception - corrupted settings should be reported
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to load MCP Editor settings from: {SettingsFilePath}. Settings file may be corrupted.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeProjectRootPath(string projectRootPath)
|
||||
{
|
||||
string fullPath = Path.GetFullPath(projectRootPath);
|
||||
return fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Security: Validate if the settings file path is safe
|
||||
/// </summary>
|
||||
private static bool IsValidSettingsPath(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Normalize the path to prevent path traversal
|
||||
string normalizedPath = Path.GetFullPath(path);
|
||||
|
||||
// Must be under UserSettings directory
|
||||
string expectedUserSettingsPath = Path.GetFullPath(McpConstants.USER_SETTINGS_FOLDER);
|
||||
|
||||
// Check if path is within the expected directory
|
||||
return normalizedPath.StartsWith(expectedUserSettingsPath, StringComparison.OrdinalIgnoreCase) &&
|
||||
normalizedPath.EndsWith(McpConstants.SETTINGS_FILE_NAME, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"{McpConstants.SECURITY_LOG_PREFIX} Error validating settings path {path}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user