chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57ec66af50b5e4a23b7938cc070d1b90
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,277 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the caching of compilation results.
|
||||
/// Avoids recompiling the same code by hashing requests with SHA256.
|
||||
/// </summary>
|
||||
public class CompilationCacheManager
|
||||
{
|
||||
private const int MaxCacheEntries = 32;
|
||||
private readonly Dictionary<string, CachedCompilationResult> _compilationCache = new();
|
||||
private readonly Queue<string> _cacheOrder = new();
|
||||
|
||||
public CompilationResult CheckCache(CompilationRequest request)
|
||||
{
|
||||
string cacheKey = GenerateCacheKey(request);
|
||||
if (_compilationCache.TryGetValue(cacheKey, out CachedCompilationResult cachedResult))
|
||||
{
|
||||
return CloneCompilationResult(cachedResult);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CacheResultIfSuccessful(CompilationResult result, CompilationRequest request)
|
||||
{
|
||||
if (result.Success && result.CompiledAssembly != null)
|
||||
{
|
||||
string cacheKey = GenerateCacheKey(request);
|
||||
CachedCompilationResult cachedResult = CreateCachedCompilationResult(result);
|
||||
if (_compilationCache.ContainsKey(cacheKey))
|
||||
{
|
||||
_compilationCache[cacheKey] = cachedResult;
|
||||
return;
|
||||
}
|
||||
|
||||
// Dynamic assemblies cannot be unloaded from the default AppDomain,
|
||||
// so the cache keeps only a small hot set instead of retaining every snippet forever.
|
||||
if (_compilationCache.Count >= MaxCacheEntries)
|
||||
{
|
||||
string oldestKey = _cacheOrder.Dequeue();
|
||||
_compilationCache.Remove(oldestKey);
|
||||
}
|
||||
|
||||
_compilationCache[cacheKey] = cachedResult;
|
||||
_cacheOrder.Enqueue(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
_compilationCache.Clear();
|
||||
_cacheOrder.Clear();
|
||||
}
|
||||
|
||||
public string GenerateCacheKey(CompilationRequest request)
|
||||
{
|
||||
StringBuilder keyBuilder = new StringBuilder();
|
||||
keyBuilder.Append(request.Code);
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(request.ClassName ?? "");
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(request.Namespace ?? "");
|
||||
|
||||
if (request.AdditionalReferences != null && request.AdditionalReferences.Any())
|
||||
{
|
||||
keyBuilder.Append("|");
|
||||
keyBuilder.Append(string.Join(",", request.AdditionalReferences.OrderBy(r => r)));
|
||||
}
|
||||
|
||||
using (SHA256 sha256 = SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(keyBuilder.ToString()));
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private static CachedCompilationResult CreateCachedCompilationResult(CompilationResult result)
|
||||
{
|
||||
return new CachedCompilationResult(
|
||||
result.CompiledAssembly,
|
||||
CloneCompilationErrors(result.Errors),
|
||||
CloneStrings(result.Warnings),
|
||||
result.UpdatedCode,
|
||||
result.HasSecurityViolations,
|
||||
CloneSecurityViolations(result.SecurityViolations),
|
||||
result.FailureReason,
|
||||
CloneAmbiguousTypeCandidates(result.AmbiguousTypeCandidates),
|
||||
CloneStrings(result.AutoInjectedNamespaces),
|
||||
CloneStrings(result.Timings),
|
||||
CloneStrings(result.AdvisoryLogs),
|
||||
result.CompilationBackendKind);
|
||||
}
|
||||
|
||||
private static CompilationResult CloneCompilationResult(CachedCompilationResult cachedResult)
|
||||
{
|
||||
return new CompilationResult
|
||||
{
|
||||
Success = true,
|
||||
CompiledAssembly = cachedResult.CompiledAssembly,
|
||||
Errors = CloneCompilationErrors(cachedResult.Errors),
|
||||
Warnings = CloneStrings(cachedResult.Warnings),
|
||||
UpdatedCode = cachedResult.UpdatedCode,
|
||||
HasSecurityViolations = cachedResult.HasSecurityViolations,
|
||||
SecurityViolations = CloneSecurityViolations(cachedResult.SecurityViolations),
|
||||
FailureReason = cachedResult.FailureReason,
|
||||
AmbiguousTypeCandidates = CloneAmbiguousTypeCandidates(cachedResult.AmbiguousTypeCandidates),
|
||||
AutoInjectedNamespaces = CloneStrings(cachedResult.AutoInjectedNamespaces),
|
||||
Timings = BuildCachedCompilationTimings(cachedResult.CompilationBackendKind),
|
||||
AdvisoryLogs = CloneStrings(cachedResult.AdvisoryLogs),
|
||||
CompilationBackendKind = cachedResult.CompilationBackendKind
|
||||
};
|
||||
}
|
||||
|
||||
private static List<string> BuildCachedCompilationTimings(
|
||||
DynamicCompilationBackendKind compilationBackendKind)
|
||||
{
|
||||
List<string> timings = new List<string>
|
||||
{
|
||||
"[Perf] ReferenceResolution: 0.0ms",
|
||||
"[Perf] Build: 0.0ms",
|
||||
"[Perf] AssemblyLoad: 0.0ms"
|
||||
};
|
||||
|
||||
switch (compilationBackendKind)
|
||||
{
|
||||
case DynamicCompilationBackendKind.SharedRoslynWorker:
|
||||
timings.Add("[Perf] Backend: SharedRoslynWorker");
|
||||
break;
|
||||
case DynamicCompilationBackendKind.OneShotRoslyn:
|
||||
timings.Add("[Perf] Backend: OneShotRoslyn");
|
||||
break;
|
||||
case DynamicCompilationBackendKind.AssemblyBuilderFallback:
|
||||
timings.Add("[Perf] Backend: AssemblyBuilderFallback");
|
||||
break;
|
||||
}
|
||||
|
||||
timings.Add("[Perf] CacheHit: true");
|
||||
return timings;
|
||||
}
|
||||
|
||||
private static List<CompilationError> CloneCompilationErrors(List<CompilationError> errors)
|
||||
{
|
||||
List<CompilationError> clonedErrors = new List<CompilationError>();
|
||||
if (errors == null)
|
||||
{
|
||||
return clonedErrors;
|
||||
}
|
||||
|
||||
foreach (CompilationError error in errors)
|
||||
{
|
||||
clonedErrors.Add(new CompilationError
|
||||
{
|
||||
Message = error.Message,
|
||||
Line = error.Line,
|
||||
Column = error.Column,
|
||||
ErrorCode = error.ErrorCode
|
||||
});
|
||||
}
|
||||
|
||||
return clonedErrors;
|
||||
}
|
||||
|
||||
private static List<SecurityViolation> CloneSecurityViolations(List<SecurityViolation> securityViolations)
|
||||
{
|
||||
List<SecurityViolation> clonedViolations = new List<SecurityViolation>();
|
||||
if (securityViolations == null)
|
||||
{
|
||||
return clonedViolations;
|
||||
}
|
||||
|
||||
foreach (SecurityViolation violation in securityViolations)
|
||||
{
|
||||
clonedViolations.Add(new SecurityViolation
|
||||
{
|
||||
Type = violation.Type,
|
||||
Description = violation.Description,
|
||||
LineNumber = violation.LineNumber,
|
||||
CodeSnippet = violation.CodeSnippet,
|
||||
Message = violation.Message,
|
||||
ApiName = violation.ApiName,
|
||||
Location = violation.Location
|
||||
});
|
||||
}
|
||||
|
||||
return clonedViolations;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> CloneAmbiguousTypeCandidates(
|
||||
Dictionary<string, List<string>> ambiguousTypeCandidates)
|
||||
{
|
||||
Dictionary<string, List<string>> clonedCandidates = new Dictionary<string, List<string>>();
|
||||
if (ambiguousTypeCandidates == null)
|
||||
{
|
||||
return clonedCandidates;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, List<string>> entry in ambiguousTypeCandidates)
|
||||
{
|
||||
clonedCandidates[entry.Key] = CloneStrings(entry.Value);
|
||||
}
|
||||
|
||||
return clonedCandidates;
|
||||
}
|
||||
|
||||
private static List<string> CloneStrings(List<string> values)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
return new List<string>(values);
|
||||
}
|
||||
|
||||
private sealed class CachedCompilationResult
|
||||
{
|
||||
public Assembly CompiledAssembly { get; }
|
||||
|
||||
public List<CompilationError> Errors { get; }
|
||||
|
||||
public List<string> Warnings { get; }
|
||||
|
||||
public string UpdatedCode { get; }
|
||||
|
||||
public bool HasSecurityViolations { get; }
|
||||
|
||||
public List<SecurityViolation> SecurityViolations { get; }
|
||||
|
||||
public CompilationFailureReason FailureReason { get; }
|
||||
|
||||
public Dictionary<string, List<string>> AmbiguousTypeCandidates { get; }
|
||||
|
||||
public List<string> AutoInjectedNamespaces { get; }
|
||||
|
||||
public List<string> Timings { get; }
|
||||
|
||||
public List<string> AdvisoryLogs { get; }
|
||||
|
||||
public DynamicCompilationBackendKind CompilationBackendKind { get; }
|
||||
|
||||
public CachedCompilationResult(
|
||||
Assembly compiledAssembly,
|
||||
List<CompilationError> errors,
|
||||
List<string> warnings,
|
||||
string updatedCode,
|
||||
bool hasSecurityViolations,
|
||||
List<SecurityViolation> securityViolations,
|
||||
CompilationFailureReason failureReason,
|
||||
Dictionary<string, List<string>> ambiguousTypeCandidates,
|
||||
List<string> autoInjectedNamespaces,
|
||||
List<string> timings,
|
||||
List<string> advisoryLogs,
|
||||
DynamicCompilationBackendKind compilationBackendKind)
|
||||
{
|
||||
CompiledAssembly = compiledAssembly;
|
||||
Errors = errors;
|
||||
Warnings = warnings;
|
||||
UpdatedCode = updatedCode;
|
||||
HasSecurityViolations = hasSecurityViolations;
|
||||
SecurityViolations = securityViolations;
|
||||
FailureReason = failureReason;
|
||||
AmbiguousTypeCandidates = ambiguousTypeCandidates;
|
||||
AutoInjectedNamespaces = autoInjectedNamespaces;
|
||||
Timings = timings;
|
||||
AdvisoryLogs = advisoryLogs;
|
||||
CompilationBackendKind = compilationBackendKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8f1ec6f980b54adda3f6317615191b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class DomainReloadStateRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IDomainReloadStateProvider _provider;
|
||||
|
||||
public static void RegisterProvider(IDomainReloadStateProvider provider)
|
||||
{
|
||||
Debug.Assert(provider != null, "provider must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDomainReloadInProgress()
|
||||
{
|
||||
IDomainReloadStateProvider provider;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
provider = _provider;
|
||||
}
|
||||
|
||||
if (provider == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return provider.IsDomainReloadInProgress();
|
||||
}
|
||||
|
||||
public static IDomainReloadStateProvider SwapProviderForTests(IDomainReloadStateProvider provider)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IDomainReloadStateProvider previousProvider = _provider;
|
||||
_provider = provider;
|
||||
return previousProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16f03c71027d5433cb920e1830289b86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class DynamicCompilationServiceRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IDynamicCompilationServiceFactory _factory;
|
||||
|
||||
public static bool HasRegisteredFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return _factory != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterFactory(IDynamicCompilationServiceFactory factory)
|
||||
{
|
||||
Debug.Assert(factory != null, "factory must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryCreate(DynamicCodeSecurityLevel securityLevel, out IDynamicCompilationService service)
|
||||
{
|
||||
IDynamicCompilationServiceFactory factory;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
factory = _factory;
|
||||
}
|
||||
|
||||
if (factory == null)
|
||||
{
|
||||
service = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
service = factory.Create(securityLevel);
|
||||
return service != null;
|
||||
}
|
||||
|
||||
public static IDynamicCompilationServiceFactory SwapFactoryForTests(IDynamicCompilationServiceFactory factory)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IDynamicCompilationServiceFactory previousFactory = _factory;
|
||||
_factory = factory;
|
||||
return previousFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed1454bd9d3934dea8704fd28061c595
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class PreloadAssemblySecurityValidatorRegistry
|
||||
{
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static IPreloadAssemblySecurityValidator _validator;
|
||||
|
||||
public static void RegisterValidator(IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
Debug.Assert(validator != null, "validator must not be null");
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
_validator = validator;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetValidator(out IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
validator = _validator;
|
||||
}
|
||||
|
||||
return validator != null;
|
||||
}
|
||||
|
||||
public static IPreloadAssemblySecurityValidator SwapValidatorForTests(IPreloadAssemblySecurityValidator validator)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
IPreloadAssemblySecurityValidator previousValidator = _validator;
|
||||
_validator = validator;
|
||||
return previousValidator;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb0c09b05bdd64ef3b534c2a17507638
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 263a267a9e45a4c288198414a6f98e7d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,212 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Central constants repository for Unity MCP system.
|
||||
///
|
||||
/// Design document reference: Packages/src/Editor/ARCHITECTURE.md
|
||||
///
|
||||
/// Related classes:
|
||||
/// - McpConfigService: Uses these constants for configuration management
|
||||
/// - McpServerConfigFactory: Uses port and environment variable constants
|
||||
/// - McpEditorWindow: Uses SessionState keys for UI state persistence
|
||||
/// - McpSessionManager: Uses SessionState keys for connection state management
|
||||
/// - EditorConfigProvider: Provides client names via GetClientNameForEditor method
|
||||
/// </summary>
|
||||
public static class McpConstants
|
||||
{
|
||||
private static UnityEditor.PackageManager.PackageInfo _cachedPackageInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PackageInfo for uLoopMCP package.
|
||||
/// Results are cached for performance.
|
||||
/// </summary>
|
||||
/// <exception cref="System.InvalidOperationException">Thrown when package info cannot be resolved.</exception>
|
||||
public static UnityEditor.PackageManager.PackageInfo PackageInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cachedPackageInfo == null)
|
||||
{
|
||||
_cachedPackageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssembly(
|
||||
typeof(McpConstants).Assembly);
|
||||
|
||||
if (_cachedPackageInfo == null)
|
||||
{
|
||||
throw new System.InvalidOperationException(
|
||||
"Failed to resolve PackageInfo for uLoopMCP. " +
|
||||
"Ensure the package is properly installed via Package Manager.");
|
||||
}
|
||||
}
|
||||
return _cachedPackageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package name (e.g., "io.github.hatayama.uloopmcp").
|
||||
/// </summary>
|
||||
public static string PackageName => PackageInfo.name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unity asset path for the package (e.g., "Packages/io.github.hatayama.uloopmcp").
|
||||
/// Use this for AssetDatabase.LoadAssetAtPath().
|
||||
/// </summary>
|
||||
public static string PackageAssetPath => PackageInfo.assetPath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the resolved file system path for the package.
|
||||
/// Use this for file system operations.
|
||||
/// </summary>
|
||||
public static string PackageResolvedPath => PackageInfo.resolvedPath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package name pattern for directory searching (e.g., "io.github.hatayama.uloopmcp@*").
|
||||
/// Use this for Directory.GetDirectories() in PackageCache.
|
||||
/// </summary>
|
||||
public static string PackageNamePattern => $"{PackageName}@*";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the C# namespace for uLoopMCP (e.g., "io.github.hatayama.uLoopMCP").
|
||||
/// </summary>
|
||||
public static string PackageNamespace => typeof(McpConstants).Namespace;
|
||||
|
||||
public const string PROJECT_NAME = "uLoopMCP";
|
||||
|
||||
// JSON configuration keys
|
||||
public const string JSON_KEY_MCP_SERVERS = "mcpServers";
|
||||
public const string JSON_KEY_COMMAND = "command";
|
||||
public const string JSON_KEY_ARGS = "args";
|
||||
public const string JSON_KEY_ENV = "env";
|
||||
|
||||
// Editor settings
|
||||
public const string SETTINGS_FILE_NAME = "UnityMcpSettings.json";
|
||||
public const string USER_SETTINGS_FOLDER = "UserSettings";
|
||||
|
||||
// Server configuration
|
||||
public const string NODE_COMMAND = "node";
|
||||
public const string UNITY_TCP_PORT_ENV_KEY = "UNITY_TCP_PORT";
|
||||
|
||||
// Scripting define symbols
|
||||
public const string SCRIPTING_DEFINE_ULOOPMCP_DEBUG = "ULOOPMCP_DEBUG";
|
||||
|
||||
// Environment variable keys for development mode
|
||||
public const string ENV_KEY_ULOOPMCP_DEBUG = "ULOOPMCP_DEBUG";
|
||||
public const string ENV_KEY_ULOOPMCP_PRODUCTION = "ULOOPMCP_PRODUCTION";
|
||||
public const string ENV_KEY_MCP_DEBUG = "MCP_DEBUG";
|
||||
public const string ENV_KEY_NODE_OPTIONS = "NODE_OPTIONS";
|
||||
// MCP_CLIENT_NAME removed - now using clientInfo.name from MCP protocol
|
||||
|
||||
// Environment variable values
|
||||
public const string ENV_VALUE_TRUE = "true";
|
||||
public const string NODE_OPTIONS_ENABLE_SOURCE_MAPS = "--enable-source-maps";
|
||||
|
||||
// Client names for different editors
|
||||
public const string CLIENT_NAME_CURSOR = "Cursor";
|
||||
public const string CLIENT_NAME_CLAUDE_CODE = "Claude Code";
|
||||
public const string CLIENT_NAME_VSCODE = "VSCode";
|
||||
public const string CLIENT_NAME_GEMINI_CLI = "Gemini CLI";
|
||||
public const string CLIENT_NAME_WINDSURF = "Windsurf";
|
||||
public const string CLIENT_NAME_CODEX = "Codex";
|
||||
public const string CLIENT_NAME_MCP_INSPECTOR = "MCP Inspector";
|
||||
public const string UNKNOWN_CLIENT_NAME = "Unknown Client";
|
||||
|
||||
// Command messages
|
||||
public const string CLIENT_SUCCESS_MESSAGE_TEMPLATE = "Client name registered successfully: {0}";
|
||||
|
||||
// Reconnection settings
|
||||
public const int RECONNECTION_TIMEOUT_SECONDS = 10;
|
||||
|
||||
// TypeScript server related constants
|
||||
public const string TYPESCRIPT_SERVER_DIR = "TypeScriptServer~";
|
||||
public const string DIST_DIR = "dist";
|
||||
public const string SERVER_BUNDLE_FILE = "server.bundle.js";
|
||||
|
||||
// Package path constants
|
||||
public const string PACKAGES_DIR = "Packages";
|
||||
public const string SRC_DIR = "src";
|
||||
public const string LIBRARY_DIR = "Library";
|
||||
public const string TEMP_DIR = "Temp";
|
||||
public const string PACKAGE_CACHE_DIR = "PackageCache";
|
||||
public const string ULOOPMCP_DIR = "uLoopMCP";
|
||||
public const string COMPILE_RESULTS_DIR = "compile-results";
|
||||
public const string JSON_FILE_EXTENSION = ".json";
|
||||
public const string RUNTIME_ASSEMBLY_FILE_NAME = "uLoopMCP.Runtime.dll";
|
||||
|
||||
// .uloop directory
|
||||
public const string ULOOP_DIR = ".uloop";
|
||||
public const string ULOOP_SETTINGS_FILE_NAME = "settings.permissions.json";
|
||||
public const string ULOOP_TOOL_SETTINGS_FILE_NAME = "settings.tools.json";
|
||||
|
||||
// Tool name constants
|
||||
public const string TOOL_NAME_EXECUTE_DYNAMIC_CODE = "execute-dynamic-code";
|
||||
public const string TOOL_NAME_RUN_TESTS = "run-tests";
|
||||
|
||||
// File output directories
|
||||
public const string OUTPUT_ROOT_DIR = ".uloop/outputs";
|
||||
public const string TEST_RESULTS_DIR = "TestResults";
|
||||
public const string SEARCH_RESULTS_DIR = "SearchResults";
|
||||
public const string HIERARCHY_RESULTS_DIR = "HierarchyResults";
|
||||
public const string FIND_GAMEOBJECTS_RESULTS_DIR = "FindGameObjectsResults";
|
||||
public const string SCREENSHOTS_DIR = "Screenshots";
|
||||
public const string VIBE_LOGS_DIR = "VibeLogs";
|
||||
|
||||
// Correlation ID constants
|
||||
public const int CORRELATION_ID_LENGTH = 8; // Length for correlation ID generation
|
||||
public const string GUID_FORMAT_NO_HYPHENS = "N"; // GUID format without hyphens
|
||||
|
||||
public const string ERROR_MESSAGE_DUPLICATE_ASMDEF = "Duplicate asmdef assembly name detected. Unity may not start compilation until duplicates are removed.";
|
||||
|
||||
public const string ERROR_MESSAGE_EXECUTION_IN_PROGRESS = "Another execution is already in progress";
|
||||
public const string ERROR_MESSAGE_EXECUTION_CANCELLED = "Execution was cancelled or timed out";
|
||||
public const string ERROR_MESSAGE_NO_COMPILED_ASSEMBLY = "No compiled assembly provided";
|
||||
public const string ERROR_MESSAGE_NO_EXECUTE_METHOD = "No Execute method found in compiled assembly";
|
||||
public const string ERROR_MESSAGE_FAILED_TO_CREATE_INSTANCE = "Failed to create instance of target type";
|
||||
public const string ERROR_MESSAGE_UNSUPPORTED_SIGNATURE = "Execute method signature not supported";
|
||||
|
||||
// Test constants
|
||||
public const int TEST_COMPILE_TIMEOUT_MS = 5000; // 5 seconds for test compilation
|
||||
|
||||
// Screenshot coordinate system values
|
||||
public const string COORDINATE_SYSTEM_GAME_VIEW = "gameView";
|
||||
public const string COORDINATE_SYSTEM_WINDOW = "window";
|
||||
|
||||
// SimulateMouseUi constants
|
||||
public const float SIMULATE_MOUSE_UI_DEFAULT_DRAG_SPEED = 2000f;
|
||||
|
||||
// Compile tool constants
|
||||
public const int COMPILE_START_TIMEOUT_MS = 5000; // Timeout to detect "compile did not start"
|
||||
public const int COMPILE_START_POLL_INTERVAL_MS = 100;
|
||||
public const int COMPILE_DOMAIN_RELOAD_WAIT_TIMEOUT_MS = 10000;
|
||||
public const int COMPILE_DOMAIN_RELOAD_WAIT_POLL_INTERVAL_MS = 100;
|
||||
|
||||
// Security constants
|
||||
public const int MAX_JSON_SIZE_BYTES = 1024 * 1024; // 1MB limit for JSON files
|
||||
public const int MAX_SETTINGS_SIZE_BYTES = 1024 * 16; // 16KB limit for settings files
|
||||
public const string SECURITY_LOG_PREFIX = "[uLoopMCP Security]";
|
||||
|
||||
// Security: Allowed namespaces for reflection operations
|
||||
public static readonly string[] ALLOWED_NAMESPACES = {
|
||||
"UnityEditor",
|
||||
"Unity.EditorCoroutines",
|
||||
"Unity.VisualScripting",
|
||||
PackageNamespace
|
||||
};
|
||||
|
||||
// Security: Denied types for reflection operations
|
||||
public static readonly string[] DENIED_SYSTEM_TYPES = {
|
||||
"System.Diagnostics.Process",
|
||||
"System.IO.File",
|
||||
"System.IO.Directory",
|
||||
"System.Reflection.Assembly",
|
||||
"System.Activator"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Generates a short correlation ID for logging and tracking
|
||||
/// </summary>
|
||||
/// <returns>8-character correlation ID</returns>
|
||||
public static string GenerateCorrelationId()
|
||||
{
|
||||
return System.Guid.NewGuid().ToString(GUID_FORMAT_NO_HYPHENS)[..CORRELATION_ID_LENGTH];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00252dba49c034c9d9f0e68d333ba8b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a284a76514b134e60b502c5e531359e9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public static class CompilationDiagnosticMessageParser
|
||||
{
|
||||
private static readonly Regex TypeNamePattern = new Regex(@"['""]([^'""]+)['""]", RegexOptions.Compiled);
|
||||
|
||||
public static string ExtractTypeNameFromMessage(string message)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Match match = TypeNamePattern.Match(message);
|
||||
if (!match.Success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return NormalizeTypeName(match.Groups[1].Value);
|
||||
}
|
||||
|
||||
private static string NormalizeTypeName(string rawName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(rawName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string normalized = rawName.Trim();
|
||||
int genericIndex = normalized.IndexOf('<');
|
||||
if (genericIndex > 0)
|
||||
{
|
||||
normalized = normalized.Substring(0, genericIndex);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2284b0ac895440efaea20245f59aec3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75c589adcf4f04a2c90deed8ba8578de
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public interface IDomainReloadStateProvider
|
||||
{
|
||||
bool IsDomainReloadInProgress();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36b0d8ce58cd14305b7904e3e1f12aaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public interface IDynamicCompilationService
|
||||
{
|
||||
Task<CompilationResult> CompileAsync(CompilationRequest request, CancellationToken ct = default);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d52a22eb1364461296f82cd4ccaca9e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public interface IDynamicCompilationServiceFactory
|
||||
{
|
||||
IDynamicCompilationService Create(DynamicCodeSecurityLevel securityLevel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82de15f662b864f2f8ba18aa82b83a5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public interface IOverrideDefaultPreloadAssemblySecurityValidation
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 063eaab7859e24ee6862f4196555b7ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public interface IPreloadAssemblySecurityValidator
|
||||
{
|
||||
SecurityValidationResult Validate(byte[] assemblyBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: deb095dc198104f59a71c4279fb7094c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a061ff47efbbd4789bf7cd4c75f83fa4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,357 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// AI-friendly structured logger for Unity MCP
|
||||
///
|
||||
/// Design document reference: Packages/src/TypeScriptServer~/ARCHITECTURE.md
|
||||
///
|
||||
/// Related classes:
|
||||
/// - McpLogger: Traditional Unity logger that this class replaces
|
||||
/// - McpServerController: Uses this logger for domain reload tracking
|
||||
/// - UnityMcpServer: Uses this logger for connection event tracking
|
||||
///
|
||||
/// Key features:
|
||||
/// - Structured JSON logging with operation, context, correlation_id
|
||||
/// - AI-friendly format for Claude Code analysis
|
||||
/// - Automatic file rotation and memory management
|
||||
/// - Correlation ID tracking for related operations
|
||||
/// </summary>
|
||||
public static class VibeLogger
|
||||
{
|
||||
private static readonly string LOG_DIRECTORY = Path.Combine(Application.dataPath, "..", McpConstants.OUTPUT_ROOT_DIR, McpConstants.VIBE_LOGS_DIR);
|
||||
private static readonly string LOG_FILE_PREFIX = "unity_vibe";
|
||||
private static readonly int MAX_FILE_SIZE_MB = 10;
|
||||
private static readonly int MAX_MEMORY_LOGS = 1000;
|
||||
private static readonly int MAX_LOG_FILES = 3;
|
||||
|
||||
private static readonly List<VibeLogEntry> memoryLogs = new();
|
||||
private static readonly object lockObject = new();
|
||||
private static bool hasCleanedUpOnStartup = false;
|
||||
|
||||
[Serializable]
|
||||
public class VibeLogEntry
|
||||
{
|
||||
public string timestamp;
|
||||
public string level;
|
||||
public string operation;
|
||||
public string message;
|
||||
public object context;
|
||||
public string correlation_id;
|
||||
public string source;
|
||||
public string human_note;
|
||||
public string ai_todo;
|
||||
public string stack_trace;
|
||||
public EnvironmentInfo environment;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EnvironmentInfo
|
||||
{
|
||||
public string domain_reload_state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log an info level message with structured context
|
||||
/// Only logs when ULOOPENV_KEY_ULOOPMCP_DEBUG symbol is defined
|
||||
/// </summary>
|
||||
[Conditional(McpConstants.ENV_KEY_ULOOPMCP_DEBUG)]
|
||||
public static void LogInfo(string operation, string message, object context = null,
|
||||
string correlationId = null, string humanNote = null, string aiTodo = null, bool includeStackTrace = false)
|
||||
{
|
||||
Log("INFO", operation, message, context, correlationId, humanNote, aiTodo, includeStackTrace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a warning level message with structured context
|
||||
/// Only logs when ULOOPENV_KEY_ULOOPMCP_DEBUG symbol is defined
|
||||
/// </summary>
|
||||
[Conditional(McpConstants.ENV_KEY_ULOOPMCP_DEBUG)]
|
||||
public static void LogWarning(string operation, string message, object context = null,
|
||||
string correlationId = null, string humanNote = null, string aiTodo = null, bool includeStackTrace = true)
|
||||
{
|
||||
Log("WARNING", operation, message, context, correlationId, humanNote, aiTodo, includeStackTrace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log an error level message with structured context
|
||||
/// Only logs when ULOOPENV_KEY_ULOOPMCP_DEBUG symbol is defined
|
||||
/// </summary>
|
||||
[Conditional(McpConstants.ENV_KEY_ULOOPMCP_DEBUG)]
|
||||
public static void LogError(string operation, string message, object context = null,
|
||||
string correlationId = null, string humanNote = null, string aiTodo = null, bool includeStackTrace = true)
|
||||
{
|
||||
Log("ERROR", operation, message, context, correlationId, humanNote, aiTodo, includeStackTrace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a debug level message with structured context
|
||||
/// Only logs when ULOOPENV_KEY_ULOOPMCP_DEBUG symbol is defined
|
||||
/// </summary>
|
||||
[Conditional(McpConstants.ENV_KEY_ULOOPMCP_DEBUG)]
|
||||
public static void LogDebug(string operation, string message, object context = null,
|
||||
string correlationId = null, string humanNote = null, string aiTodo = null, bool includeStackTrace = false)
|
||||
{
|
||||
Log("DEBUG", operation, message, context, correlationId, humanNote, aiTodo, includeStackTrace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log an exception with structured context
|
||||
/// Only logs when ULOOPENV_KEY_ULOOPMCP_DEBUG symbol is defined
|
||||
/// </summary>
|
||||
[Conditional(McpConstants.ENV_KEY_ULOOPMCP_DEBUG)]
|
||||
public static void LogException(string operation, Exception exception, object context = null,
|
||||
string correlationId = null, string humanNote = null, string aiTodo = null, bool includeStackTrace = true)
|
||||
{
|
||||
var exceptionContext = new Dictionary<string, object>();
|
||||
if (context != null)
|
||||
{
|
||||
exceptionContext["original_context"] = context;
|
||||
}
|
||||
|
||||
exceptionContext["exception"] = new
|
||||
{
|
||||
type = exception.GetType().Name,
|
||||
message = exception.Message,
|
||||
stack_trace = exception.StackTrace,
|
||||
inner_exception = exception.InnerException?.Message
|
||||
};
|
||||
|
||||
Log("ERROR", operation, $"Exception occurred: {exception.Message}", exceptionContext,
|
||||
correlationId, humanNote, aiTodo, includeStackTrace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new correlation ID for tracking related operations
|
||||
/// </summary>
|
||||
public static string GenerateCorrelationId()
|
||||
{
|
||||
return $"unity_{Guid.NewGuid().ToString("N")[..8]}_{DateTime.Now:HHmmss}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get logs for AI analysis (formatted for Claude Code)
|
||||
/// Output directory: {project_root}/.uloop/outputs/VibeLogs/
|
||||
/// </summary>
|
||||
public static string GetLogsForAi(string operation = null, string correlationId = null, int maxCount = 100)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
var filteredLogs = new List<VibeLogEntry>(memoryLogs);
|
||||
|
||||
if (!string.IsNullOrEmpty(operation))
|
||||
{
|
||||
filteredLogs = filteredLogs.FindAll(log => log.operation.Contains(operation));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(correlationId))
|
||||
{
|
||||
filteredLogs = filteredLogs.FindAll(log => log.correlation_id == correlationId);
|
||||
}
|
||||
|
||||
if (filteredLogs.Count > maxCount)
|
||||
{
|
||||
filteredLogs = filteredLogs.GetRange(filteredLogs.Count - maxCount, maxCount);
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(filteredLogs, Formatting.Indented);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all memory logs
|
||||
/// </summary>
|
||||
public static void ClearMemoryLogs()
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
memoryLogs.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core logging method
|
||||
/// </summary>
|
||||
private static void Log(string level, string operation, string message, object context,
|
||||
string correlationId, string humanNote, string aiTodo, bool includeStackTrace = true)
|
||||
{
|
||||
var logEntry = new VibeLogEntry
|
||||
{
|
||||
timestamp = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"),
|
||||
level = level,
|
||||
operation = operation,
|
||||
message = message,
|
||||
context = context,
|
||||
correlation_id = correlationId ?? GenerateCorrelationId(),
|
||||
source = "Unity",
|
||||
human_note = humanNote,
|
||||
ai_todo = aiTodo,
|
||||
stack_trace = includeStackTrace ? new StackTrace(true).ToString() : null,
|
||||
environment = GetEnvironmentInfo()
|
||||
};
|
||||
|
||||
// Add to memory logs
|
||||
lock (lockObject)
|
||||
{
|
||||
memoryLogs.Add(logEntry);
|
||||
|
||||
// Rotate memory logs if too many
|
||||
if (memoryLogs.Count > MAX_MEMORY_LOGS)
|
||||
{
|
||||
memoryLogs.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Save to file
|
||||
try
|
||||
{
|
||||
SaveLogToFile(logEntry);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fallback to Unity console if file logging fails
|
||||
UnityEngine.Debug.LogError($"[VibeLogger] Failed to save log to file: {ex.Message}");
|
||||
UnityEngine.Debug.Log($"[VibeLogger] {level} | {operation} | {message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save log entry to file with file locking for concurrent access
|
||||
/// </summary>
|
||||
private static void SaveLogToFile(VibeLogEntry logEntry)
|
||||
{
|
||||
if (!Directory.Exists(LOG_DIRECTORY))
|
||||
{
|
||||
Directory.CreateDirectory(LOG_DIRECTORY);
|
||||
}
|
||||
|
||||
// Clean up old log files on first access only
|
||||
lock (lockObject)
|
||||
{
|
||||
if (!hasCleanedUpOnStartup)
|
||||
{
|
||||
CleanupOldLogFiles();
|
||||
hasCleanedUpOnStartup = true;
|
||||
}
|
||||
}
|
||||
|
||||
string fileName = $"{LOG_FILE_PREFIX}_{DateTime.UtcNow:yyyyMMdd}.json";
|
||||
string filePath = Path.Combine(LOG_DIRECTORY, fileName);
|
||||
|
||||
// Check file size and rotate if necessary
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var fileInfo = new FileInfo(filePath);
|
||||
if (fileInfo.Length > MAX_FILE_SIZE_MB * 1024 * 1024)
|
||||
{
|
||||
string rotatedFileName = $"{LOG_FILE_PREFIX}_{DateTime.UtcNow:yyyyMMdd_HHmmss}.json";
|
||||
string rotatedFilePath = Path.Combine(LOG_DIRECTORY, rotatedFileName);
|
||||
File.Move(filePath, rotatedFilePath);
|
||||
|
||||
// Clean up old files after rotation
|
||||
CleanupOldLogFiles();
|
||||
}
|
||||
}
|
||||
|
||||
string jsonLog = JsonConvert.SerializeObject(logEntry) + "\n";
|
||||
|
||||
// Use file locking with retry mechanism to handle concurrent access
|
||||
int maxRetries = 3;
|
||||
int retryDelayMs = 50;
|
||||
|
||||
for (int retry = 0; retry < maxRetries; retry++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.Read))
|
||||
using (var writer = new StreamWriter(fileStream))
|
||||
{
|
||||
writer.Write(jsonLog);
|
||||
writer.Flush();
|
||||
return; // Success - exit retry loop
|
||||
}
|
||||
}
|
||||
catch (IOException ex) when (IsFileSharingViolation(ex) && retry < maxRetries - 1)
|
||||
{
|
||||
// Wait and retry for sharing violations
|
||||
System.Threading.Thread.Sleep(retryDelayMs * (retry + 1));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// For other exceptions, throw immediately
|
||||
throw new InvalidOperationException($"Failed to write VibeLogger entry to file: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// If all retries failed, throw with sharing violation context
|
||||
throw new InvalidOperationException($"Failed to write VibeLogger entry after {maxRetries} retries due to file sharing violations");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if exception is a file sharing violation
|
||||
/// </summary>
|
||||
private static bool IsFileSharingViolation(IOException ex)
|
||||
{
|
||||
// ERROR_SHARING_VIOLATION (0x80070020)
|
||||
const int ERROR_SHARING_VIOLATION = unchecked((int)0x80070020);
|
||||
return ex.HResult == ERROR_SHARING_VIOLATION;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up old log files, keeping only the most recent MAX_LOG_FILES
|
||||
/// </summary>
|
||||
private static void CleanupOldLogFiles()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(LOG_DIRECTORY))
|
||||
return;
|
||||
|
||||
// Get all vibe log files, sorted by creation time (newest first)
|
||||
var logFiles = Directory.GetFiles(LOG_DIRECTORY, $"{LOG_FILE_PREFIX}_*.json")
|
||||
.Select(file => new FileInfo(file))
|
||||
.OrderByDescending(file => file.CreationTime)
|
||||
.ToArray();
|
||||
|
||||
// Delete files beyond the limit
|
||||
for (int i = MAX_LOG_FILES; i < logFiles.Length; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
logFiles[i].Delete();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log deletion failure but don't crash
|
||||
UnityEngine.Debug.LogWarning($"[VibeLogger] Failed to delete old log file {logFiles[i].Name}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[VibeLogger] Failed to cleanup old log files: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get current environment information
|
||||
/// </summary>
|
||||
private static EnvironmentInfo GetEnvironmentInfo()
|
||||
{
|
||||
bool isDomainReloadInProgress = EditorApplication.isCompiling || DomainReloadStateRegistry.IsDomainReloadInProgress();
|
||||
|
||||
return new EnvironmentInfo
|
||||
{
|
||||
domain_reload_state = isDomainReloadInProgress ? "InProgress" : "Idle"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d37177f1ffadc40db966c2ea6be1895d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bb81f20f00c84854b5f94e40f513d48
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a compilation error during code compilation
|
||||
///
|
||||
/// Related Classes: RoslynCompiler, CompilationResult
|
||||
/// </summary>
|
||||
public class CompilationError
|
||||
{
|
||||
/// <summary>Line number where the compilation error occurred</summary>
|
||||
public int Line { get; set; }
|
||||
|
||||
/// <summary>Column number where the compilation error occurred</summary>
|
||||
public int Column { get; set; }
|
||||
|
||||
/// <summary>Detailed error message describing the compilation error</summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>Unique error code identifying the specific compilation error</summary>
|
||||
public string ErrorCode { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c60a3998d37ae4373924387a9603af7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Compilation Request
|
||||
///
|
||||
/// Related Class: RoslynCompiler
|
||||
/// </summary>
|
||||
public class CompilationRequest
|
||||
{
|
||||
/// <summary>Source code to compile</summary>
|
||||
public string Code { get; set; }
|
||||
|
||||
/// <summary>Name of the class</summary>
|
||||
public string ClassName { get; set; }
|
||||
|
||||
/// <summary>Namespace of the class</summary>
|
||||
public string Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional references (external DLL file paths)
|
||||
/// Specify external libraries (such as NuGet package DLLs) that are not loaded in the AppDomain
|
||||
/// by providing their file paths to be added as references during compilation
|
||||
/// Example: ["C:/MyLibrary/CustomLibrary.dll", "D:/ThirdParty/SomePackage.dll"]
|
||||
/// </summary>
|
||||
public List<string> AdditionalReferences { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Legacy assembly loading mode hint.
|
||||
/// AssemblyBuilder always compiles against the full reference set, so this value is ignored.
|
||||
/// </summary>
|
||||
public AssemblyLoadingMode AssemblyMode { get; set; } = AssemblyLoadingMode.AllAssemblies;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the mode for loading assemblies during compilation
|
||||
/// </summary>
|
||||
public enum AssemblyLoadingMode
|
||||
{
|
||||
/// <summary>Reference only selected assemblies</summary>
|
||||
SelectiveReference,
|
||||
|
||||
/// <summary>Add all assemblies</summary>
|
||||
AllAssemblies
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ae3bf8a209814a46845da1c0565747b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Compilation Result
|
||||
///
|
||||
/// Related Class: RoslynCompiler
|
||||
/// </summary>
|
||||
public class CompilationResult
|
||||
{
|
||||
/// <summary>Compilation Success</summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>Compiled Assembly</summary>
|
||||
public Assembly CompiledAssembly { get; set; }
|
||||
|
||||
/// <summary>Errors</summary>
|
||||
public List<CompilationError> Errors { get; set; } = new();
|
||||
|
||||
/// <summary>Warnings</summary>
|
||||
public List<string> Warnings { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Code Formatted for Compilation
|
||||
/// (After extracting and moving using statements, applying class/method wrapping)
|
||||
/// </summary>
|
||||
public string UpdatedCode { get; set; }
|
||||
|
||||
/// <summary>Flag for Security Violations</summary>
|
||||
public bool HasSecurityViolations { get; set; }
|
||||
|
||||
/// <summary>Security Violation Details</summary>
|
||||
public List<SecurityViolation> SecurityViolations { get; set; } = new();
|
||||
|
||||
/// <summary>Failure Reason Category</summary>
|
||||
public CompilationFailureReason FailureReason { get; set; } = CompilationFailureReason.None;
|
||||
|
||||
/// <summary>
|
||||
/// Types that could not be auto-resolved because multiple namespace candidates were found.
|
||||
/// Key: type name, Value: list of candidate namespaces.
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>> AmbiguousTypeCandidates { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Without this, callers cannot tell which namespaces were auto-resolved
|
||||
/// and cannot advise users to include them for faster compilation.
|
||||
/// </summary>
|
||||
public List<string> AutoInjectedNamespaces { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Keeps internal phase timings out of normal logs while allowing tests and
|
||||
/// benchmarks to compare where time is spent inside the pipeline.
|
||||
/// </summary>
|
||||
public List<string> Timings { get; set; } = new();
|
||||
|
||||
public List<string> AdvisoryLogs { get; set; } = new();
|
||||
|
||||
public DynamicCompilationBackendKind CompilationBackendKind { get; set; } = DynamicCompilationBackendKind.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compilation Failure Reason Categories
|
||||
/// </summary>
|
||||
public enum CompilationFailureReason
|
||||
{
|
||||
/// <summary>No Failure (Success)</summary>
|
||||
None,
|
||||
|
||||
/// <summary>Compilation Error</summary>
|
||||
CompilationError,
|
||||
|
||||
/// <summary>Security Violation</summary>
|
||||
SecurityViolation,
|
||||
|
||||
/// <summary>Dynamic Assembly Addition Failed</summary>
|
||||
DynamicAssemblyFailed,
|
||||
|
||||
/// <summary>Using Statement Addition Failed</summary>
|
||||
UsingStatementFailed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae9eab1ed36274554be091d4c5e84c0b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
public enum DynamicCompilationBackendKind
|
||||
{
|
||||
Unknown = 0,
|
||||
SharedRoslynWorker = 1,
|
||||
OneShotRoslyn = 2,
|
||||
AssemblyBuilderFallback = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfefff852f0cc4b7c95ca26d2ce85217
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Security Validation Result
|
||||
///
|
||||
/// Related Class: SecurityValidator
|
||||
/// </summary>
|
||||
public class SecurityValidationResult
|
||||
{
|
||||
/// <summary>Validation status</summary>
|
||||
public bool IsValid { get; set; }
|
||||
|
||||
/// <summary>List of security violations</summary>
|
||||
public List<SecurityViolation> Violations { get; set; } = new();
|
||||
|
||||
/// <summary>Compilation errors (extended)</summary>
|
||||
public List<string> CompilationErrors { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve error summary
|
||||
/// </summary>
|
||||
public string GetErrorSummary()
|
||||
{
|
||||
if (IsValid) return "No security violations detected.";
|
||||
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine($"Security validation failed with {Violations.Count} violation(s):");
|
||||
|
||||
foreach (SecurityViolation violation in Violations)
|
||||
{
|
||||
sb.AppendLine($" - [{violation.Type}] {violation.Message}");
|
||||
if (!string.IsNullOrEmpty(violation.ApiName))
|
||||
{
|
||||
sb.AppendLine($" API: {violation.ApiName}");
|
||||
}
|
||||
}
|
||||
|
||||
if (CompilationErrors?.Any() == true)
|
||||
{
|
||||
sb.AppendLine($"Compilation errors: {CompilationErrors.Count}");
|
||||
foreach (string error in CompilationErrors)
|
||||
{
|
||||
sb.AppendLine($" - {error}");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c25f4cda916ef4d17afc470c799ca0c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Security Violation
|
||||
/// Related Classes: SecurityValidator, SecuritySyntaxWalker
|
||||
/// </summary>
|
||||
public class SecurityViolation
|
||||
{
|
||||
/// <summary>Violation Type</summary>
|
||||
public SecurityViolationType Type { get; set; }
|
||||
|
||||
/// <summary>Description of the Security Violation</summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>Line Number where the Violation Occurred</summary>
|
||||
public int LineNumber { get; set; }
|
||||
|
||||
/// <summary>Code Snippet Containing the Violation</summary>
|
||||
public string CodeSnippet { get; set; }
|
||||
|
||||
/// <summary>Violation Message</summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>API Name Involved in the Violation</summary>
|
||||
public string ApiName { get; set; }
|
||||
|
||||
/// <summary>Location of the Violation</summary>
|
||||
public string Location { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Security Violation Types
|
||||
/// </summary>
|
||||
public enum SecurityViolationType
|
||||
{
|
||||
/// <summary>Using Declaration of a Forbidden Namespace</summary>
|
||||
ForbiddenNamespace,
|
||||
|
||||
/// <summary>Dangerous API Call (Method, Property, etc.)</summary>
|
||||
DangerousApiCall,
|
||||
|
||||
/// <summary>Inheritance from a Dangerous Class</summary>
|
||||
DangerousInheritance,
|
||||
|
||||
/// <summary>Instantiation of a Dangerous Type</summary>
|
||||
DangerousTypeCreation,
|
||||
|
||||
/// <summary>Unauthorized Reflection Usage</summary>
|
||||
UnauthorizedReflection
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a9141487fe1641d881e98b773e5acc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d47205a1275834cd8985919c4ca82d52
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Roslyn-independent catalog of dangerous API types and members.
|
||||
/// Data stays shared because both preload and post-load validators must make identical decisions.
|
||||
/// </summary>
|
||||
public static class DangerousApiCatalog
|
||||
{
|
||||
private static readonly HashSet<string> DangerousTypes = new(System.StringComparer.Ordinal)
|
||||
{
|
||||
"System.IO.FileInfo",
|
||||
"System.IO.DirectoryInfo",
|
||||
"System.IO.FileSystemWatcher",
|
||||
"System.Net.Http.HttpClient",
|
||||
"System.Net.WebClient",
|
||||
"System.Net.WebRequest",
|
||||
"System.Net.Sockets.Socket",
|
||||
"System.Net.Sockets.TcpClient",
|
||||
"System.Net.Sockets.TcpListener",
|
||||
"System.Net.Sockets.UdpClient",
|
||||
"System.Net.Sockets.NetworkStream",
|
||||
"System.Net.Dns",
|
||||
"System.Net.IPAddress",
|
||||
"System.Net.HttpWebRequest",
|
||||
"System.Net.Security.SslStream",
|
||||
"System.Net.WebSockets.ClientWebSocket",
|
||||
"System.Net.WebSockets.WebSocket",
|
||||
"System.Net.Mail.SmtpClient",
|
||||
"System.Net.NetworkInformation.Ping",
|
||||
"System.Net.Http.HttpClientHandler",
|
||||
"System.Net.Http.HttpMessageHandler",
|
||||
"System.Net.Http.SocketsHttpHandler",
|
||||
"System.Diagnostics.ProcessStartInfo",
|
||||
"System.Web.HttpContext",
|
||||
"System.Web.HttpRequest",
|
||||
"System.Web.HttpResponse",
|
||||
"UnityEngine.Networking.UnityWebRequest",
|
||||
"UnityEngine.Networking.NetworkTransport",
|
||||
"System.Data.SqlClient.SqlConnection",
|
||||
"System.Data.SqlClient.SqlCommand",
|
||||
"System.Data.DataSet",
|
||||
"System.Runtime.Remoting.RemotingConfiguration",
|
||||
"System.Runtime.Remoting.RemotingServices",
|
||||
"System.Security.Cryptography.X509Certificates.X509Certificate",
|
||||
"System.Security.Cryptography.X509Certificates.X509Store"
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, HashSet<string>> DangerousMembers = new(System.StringComparer.Ordinal)
|
||||
{
|
||||
["System.IO.File"] = new() { "Delete", "WriteAllText", "WriteAllBytes", "Replace", "OpenWrite", "AppendAllText", "AppendText", "SetAttributes", "SetCreationTime", "SetCreationTimeUtc", "SetLastAccessTime", "SetLastAccessTimeUtc", "SetLastWriteTime", "SetLastWriteTimeUtc" },
|
||||
["System.IO.Directory"] = new() { "Delete", "SetCurrentDirectory" },
|
||||
["System.Diagnostics.Process"] = new() { "Start", "Kill" },
|
||||
["System.Reflection.Assembly"] = new() { "Load", "LoadFrom", "LoadFile", "LoadWithPartialName", "GetType" },
|
||||
["System.Type"] = new() { "GetType" },
|
||||
["System.Activator"] = new() { "CreateComInstanceFrom" },
|
||||
["UnityEditor.AssetDatabase"] = new() { "DeleteAsset" },
|
||||
["UnityEditor.FileUtil"] = new() { "DeleteFileOrDirectory" },
|
||||
["System.Environment"] = new() { "Exit", "FailFast" },
|
||||
["System.Threading.Thread"] = new() { "Abort", "Suspend", "Resume" },
|
||||
["io.github.hatayama.uLoopMCP.DynamicCodeSecurityManager"] = new() { "InitializeFromSettings" },
|
||||
["io.github.hatayama.uLoopMCP.ULoopSettings"] = new() { "SetDynamicCodeSecurityLevel" }
|
||||
};
|
||||
|
||||
public static bool IsDangerousType(string fullTypeName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fullTypeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return DangerousTypes.Contains(fullTypeName);
|
||||
}
|
||||
|
||||
public static bool IsDangerousApi(string fullTypeName, string memberName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fullTypeName) || string.IsNullOrEmpty(memberName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DangerousTypes.Contains(fullTypeName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DangerousMembers.TryGetValue(fullTypeName, out HashSet<string> members))
|
||||
{
|
||||
return members.Contains(memberName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> EnumerateDangerousTypes()
|
||||
{
|
||||
return DangerousTypes;
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, IReadOnlyCollection<string>>> EnumerateDangerousMembers()
|
||||
{
|
||||
foreach (KeyValuePair<string, HashSet<string>> entry in DangerousMembers)
|
||||
{
|
||||
yield return new KeyValuePair<string, IReadOnlyCollection<string>>(
|
||||
entry.Key,
|
||||
new List<string>(entry.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce4ee0bcf3aba4cfc9a8301ed6418f82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace io.github.hatayama.uLoopMCP
|
||||
{
|
||||
/// <summary>
|
||||
/// Security level definitions for ExecuteDynamicCodeTool
|
||||
/// Related classes: DynamicCodeSecurityManager, AssemblyReferencePolicy, RoslynCompiler
|
||||
/// </summary>
|
||||
public enum DynamicCodeSecurityLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Dangerous APIs (System.IO, System.Net.Http, Process, reflection, etc.) are blocked.
|
||||
/// Default level for safe Unity development.
|
||||
/// </summary>
|
||||
Restricted = 1,
|
||||
|
||||
/// <summary>
|
||||
/// All APIs available without restrictions.
|
||||
/// Warning: Security risks present - use only with trusted code.
|
||||
/// </summary>
|
||||
FullAccess = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3535bbf0f49344452bf56a69aeb68d7d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "uLoopMCP.Editor.Shared",
|
||||
"rootNamespace": "",
|
||||
"references": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"Newtonsoft.Json.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 290394860909340b7835eb7cc215ee75
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user