fix: Windows define UNITY_EDITOR_WIN; async stdout/stderr in TryRun and RepairPythonEnvironment; use EnvironmentVariables for PATH; prepend Unix PATH only on macOS/Linux; fix duplicate .meta GUIDs

main
David Sarno 2025-08-12 11:56:46 -07:00
parent f6f8b24371
commit efd146ab53
5 changed files with 70 additions and 26 deletions

View File

@ -2,6 +2,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEditor; using UnityEditor;
@ -43,7 +44,7 @@ namespace UnityMcpBridge.Editor.Helpers
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
#if UNITY_EDITOR_WINDOWS #if UNITY_EDITOR_WIN
// Common npm global locations // Common npm global locations
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty; string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty;
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty; string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty;
@ -109,16 +110,35 @@ namespace UnityMcpBridge.Editor.Helpers
if (!string.IsNullOrEmpty(extraPathPrepend)) if (!string.IsNullOrEmpty(extraPathPrepend))
{ {
string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
psi.Environment["PATH"] = string.IsNullOrEmpty(currentPath) psi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(currentPath)
? extraPathPrepend ? extraPathPrepend
: (extraPathPrepend + System.IO.Path.PathSeparator + currentPath); : (extraPathPrepend + System.IO.Path.PathSeparator + currentPath);
} }
using var p = Process.Start(psi);
if (p == null) return false; using var process = new Process { StartInfo = psi, EnableRaisingEvents = false };
stdout = p.StandardOutput.ReadToEnd();
stderr = p.StandardError.ReadToEnd(); var so = new StringBuilder();
if (!p.WaitForExit(timeoutMs)) { try { p.Kill(); } catch { } return false; } var se = new StringBuilder();
return p.ExitCode == 0; process.OutputDataReceived += (_, e) => { if (e.Data != null) so.AppendLine(e.Data); };
process.ErrorDataReceived += (_, e) => { if (e.Data != null) se.AppendLine(e.Data); };
if (!process.Start()) return false;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
if (!process.WaitForExit(timeoutMs))
{
try { process.Kill(); } catch { }
return false;
}
// Ensure async buffers are flushed
process.WaitForExit();
stdout = so.ToString();
stderr = se.ToString();
return process.ExitCode == 0;
} }
catch catch
{ {
@ -138,7 +158,7 @@ namespace UnityMcpBridge.Editor.Helpers
CreateNoWindow = true, CreateNoWindow = true,
}; };
string path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; string path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
psi.Environment["PATH"] = string.IsNullOrEmpty(path) ? prependPath : (prependPath + Path.PathSeparator + path); psi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(path) ? prependPath : (prependPath + Path.PathSeparator + path);
using var p = Process.Start(psi); using var p = Process.Start(psi);
string output = p?.StandardOutput.ReadToEnd().Trim(); string output = p?.StandardOutput.ReadToEnd().Trim();
p?.WaitForExit(1500); p?.WaitForExit(1500);
@ -148,7 +168,7 @@ namespace UnityMcpBridge.Editor.Helpers
} }
#endif #endif
#if UNITY_EDITOR_WINDOWS #if UNITY_EDITOR_WIN
private static string Where(string exe) private static string Where(string exe)
{ {
try try

View File

@ -8,6 +8,4 @@ MonoImporter:
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:
assetBundleVariant: assetBundleVariant:
fileFormatVersion: 2
guid: 3f130216be0fd4a57ab7d646a85c6d54

View File

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Reflection; using System.Reflection;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -206,12 +207,35 @@ namespace UnityMcpBridge.Editor.Helpers
CreateNoWindow = true CreateNoWindow = true
}; };
using var p = System.Diagnostics.Process.Start(psi); using var proc = new System.Diagnostics.Process { StartInfo = psi };
string stdout = p.StandardOutput.ReadToEnd(); var sbOut = new StringBuilder();
string stderr = p.StandardError.ReadToEnd(); var sbErr = new StringBuilder();
p.WaitForExit(60000); proc.OutputDataReceived += (_, e) => { if (e.Data != null) sbOut.AppendLine(e.Data); };
proc.ErrorDataReceived += (_, e) => { if (e.Data != null) sbErr.AppendLine(e.Data); };
if (p.ExitCode != 0) if (!proc.Start())
{
Debug.LogError("Failed to start uv process.");
return false;
}
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
if (!proc.WaitForExit(60000))
{
try { proc.Kill(); } catch { }
Debug.LogError("uv sync timed out.");
return false;
}
// Ensure async buffers flushed
proc.WaitForExit();
string stdout = sbOut.ToString();
string stderr = sbErr.ToString();
if (proc.ExitCode != 0)
{ {
Debug.LogError($"uv sync failed: {stderr}\n{stdout}"); Debug.LogError($"uv sync failed: {stderr}\n{stdout}");
return false; return false;
@ -341,7 +365,7 @@ namespace UnityMcpBridge.Editor.Helpers
"/bin" "/bin"
}); });
string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
whichPsi.Environment["PATH"] = string.IsNullOrEmpty(currentPath) ? prepend : (prepend + ":" + currentPath); whichPsi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(currentPath) ? prepend : (prepend + ":" + currentPath);
} }
catch { } catch { }
using var wp = System.Diagnostics.Process.Start(whichPsi); using var wp = System.Diagnostics.Process.Start(whichPsi);

View File

@ -8,6 +8,4 @@ MonoImporter:
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName: assetBundleName:
assetBundleVariant: assetBundleVariant:
fileFormatVersion: 2
guid: 9ac156bc74460420290ab50ed91d3a15

View File

@ -1320,10 +1320,14 @@ namespace UnityMcpBridge.Editor.Windows
string args = $"mcp add UnityMCP -- \"{uvPath}\" run --directory \"{srcDir}\" server.py"; string args = $"mcp add UnityMCP -- \"{uvPath}\" run --directory \"{srcDir}\" server.py";
string projectDir = Path.GetDirectoryName(Application.dataPath); string projectDir = Path.GetDirectoryName(Application.dataPath);
// Ensure PATH includes common Node/npm locations so claude can spawn node internally if needed // Ensure PATH includes common locations on Unix; on Windows leave PATH as-is
string pathPrepend = Application.platform == RuntimePlatform.OSXEditor string pathPrepend = null;
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.LinuxEditor)
: "/usr/local/bin:/usr/bin:/bin"; {
pathPrepend = Application.platform == RuntimePlatform.OSXEditor
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
: "/usr/local/bin:/usr/bin:/bin";
}
if (!ExecPath.TryRun(claudePath, args, projectDir, out var stdout, out var stderr, 15000, pathPrepend)) if (!ExecPath.TryRun(claudePath, args, projectDir, out var stdout, out var stderr, 15000, pathPrepend))
{ {
UnityEngine.Debug.LogError($"UnityMCP: Failed to start Claude CLI.\n{stderr}\n{stdout}"); UnityEngine.Debug.LogError($"UnityMCP: Failed to start Claude CLI.\n{stderr}\n{stdout}");