using System; using System.Runtime.InteropServices; using MCPForUnity.Editor.Helpers; namespace MCPForUnity.Editor.Dependencies { /// /// Runs the official uv installer for the current platform. UI-agnostic: build the command, /// run it (off the main thread), and report the outcome. The caller owns confirmation and /// re-checking dependencies afterwards. /// Installer reference: https://docs.astral.sh/uv/getting-started/installation/ /// public static class UvInstaller { public readonly struct UvInstallResult { public readonly bool Success; public readonly string Output; public UvInstallResult(bool success, string output) { Success = success; Output = output ?? string.Empty; } } /// One-click install is available on Windows, macOS and Linux. public static bool IsSupported => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux); /// /// Build the installer invocation for the current platform. Pure and testable — the /// returned command is exactly what executes and what the confirm /// dialog shows the user. /// public static (string file, string arguments) BuildInstallCommand() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return ("powershell", "-NoProfile -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\""); } // macOS and Linux share the POSIX shell installer. return ("/bin/sh", "-c \"curl -LsSf https://astral.sh/uv/install.sh | sh\""); } /// Human-readable one-line description of the command that will run. public static string DescribeCommand() { var (file, arguments) = BuildInstallCommand(); return $"{file} {arguments}"; } /// /// Run the installer and return the outcome. Blocks until the installer exits or the /// timeout elapses, so call it off the main thread (e.g. via Task.Run). /// public static UvInstallResult Run(int timeoutMs = 180000) { try { var (file, arguments) = BuildInstallCommand(); bool ok = ExecPath.TryRun(file, arguments, null, out string stdout, out string stderr, timeoutMs); return new UvInstallResult(ok, Combine(stdout, stderr)); } catch (Exception ex) { return new UvInstallResult(false, ex.Message); } } private static string Combine(string stdout, string stderr) { string outText = (stdout ?? string.Empty).Trim(); string errText = (stderr ?? string.Empty).Trim(); string combined = string.IsNullOrEmpty(errText) ? outText : string.IsNullOrEmpty(outText) ? errText : outText + "\n" + errText; // Keep dialogs readable — echo only the tail of long installer output. const int max = 1500; if (combined.Length > max) { combined = "…" + combined.Substring(combined.Length - max); } return combined; } } }