From 7dbc5d53ae016bfadfbf843b80dbbfa1c9c5804d Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 13 Jan 2024 23:34:50 -0800 Subject: [PATCH 01/13] install node/pnpm and build invoke frontend during install (cherry picked from commit 2f55c8099688a07f452546e0f3b0f845e08498b3) --- .../Helpers/UnixPrerequisiteHelper.cs | 14 +++++ .../Helpers/WindowsPrerequisiteHelper.cs | 47 +++++++++++++- .../Helper/IPrerequisiteHelper.cs | 2 + .../Helper/PrerequisiteHelper.cs | 10 +++ .../Models/Packages/InvokeAI.cs | 63 ++++++++++++++++--- 5 files changed, 126 insertions(+), 10 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index e31be0da..fdc61545 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -247,6 +247,20 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper throw new PlatformNotSupportedException(); } + [UnsupportedOSPlatform("Linux")] + [UnsupportedOSPlatform("macOS")] + public Task RunNpm(ProcessArgs args, string? workingDirectory = null) + { + throw new PlatformNotSupportedException(); + } + + [UnsupportedOSPlatform("Linux")] + [UnsupportedOSPlatform("macOS")] + public Task InstallNodeIfNecessary(IProgress? progress = null) + { + throw new PlatformNotSupportedException(); + } + [UnsupportedOSPlatform("Linux")] [UnsupportedOSPlatform("macOS")] public Task InstallVcRedistIfNecessary(IProgress? progress = null) diff --git a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs index c6d007d0..aca77bd6 100644 --- a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs @@ -26,6 +26,7 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper private const string VcRedistDownloadUrl = "https://aka.ms/vs/16/release/vc_redist.x64.exe"; private const string TkinterDownloadUrl = "https://cdn.lykos.ai/tkinter-cpython-embedded-3.10.11-win-x64.zip"; + private const string NodeDownloadUrl = "https://cdn.lykos.ai/nodejs.zip"; private string HomeDir => settingsManager.LibraryDir; @@ -49,8 +50,10 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper private string TkinterZipPath => Path.Combine(AssetsDir, "tkinter.zip"); private string TkinterExtractPath => PythonDir; private string TkinterExistsPath => Path.Combine(PythonDir, "tkinter"); - public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin"); + private string NodeExistsPath => Path.Combine(AssetsDir, "nodejs", "npm.cmd"); + private string NodeDownloadPath => Path.Combine(AssetsDir, "nodejs.zip"); + public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin"); public bool IsPythonInstalled => File.Exists(PythonDllPath); public WindowsPrerequisiteHelper( @@ -111,12 +114,22 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper return process; } + public async Task RunNpm(ProcessArgs args, string? workingDirectory = null) + { + var result = await ProcessRunner + .GetProcessResultAsync(NodeExistsPath, args, workingDirectory) + .ConfigureAwait(false); + + result.EnsureSuccessExitCode(); + } + public async Task InstallAllIfNecessary(IProgress? progress = null) { await InstallVcRedistIfNecessary(progress); await UnpackResourcesIfNecessary(progress); await InstallPythonIfNecessary(progress); await InstallGitIfNecessary(progress); + await InstallNodeIfNecessary(progress); } public async Task UnpackResourcesIfNecessary(IProgress? progress = null) @@ -372,6 +385,38 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper File.Delete(VcRedistDownloadPath); } + [SupportedOSPlatform("windows")] + public async Task InstallNodeIfNecessary(IProgress? progress = null) + { + if (File.Exists(NodeExistsPath)) + { + Logger.Info("node already installed"); + return; + } + + Logger.Info("Downloading node"); + await downloadService.DownloadToFileAsync(NodeDownloadUrl, NodeDownloadPath, progress: progress); + + Logger.Info("Installing node"); + progress?.Report( + new ProgressReport( + progress: 0.5f, + isIndeterminate: true, + type: ProgressType.Generic, + message: "Installing prerequisites..." + ) + ); + + // unzip + await ArchiveHelper.Extract(NodeDownloadPath, AssetsDir, progress); + + progress?.Report( + new ProgressReport(progress: 1f, message: "Node install complete", type: ProgressType.Generic) + ); + + File.Delete(NodeDownloadPath); + } + private async Task UnzipGit(IProgress? progress = null) { if (progress == null) diff --git a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs index 6c937584..00dd38c9 100644 --- a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs +++ b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs @@ -34,4 +34,6 @@ public interface IPrerequisiteHelper Task GetGitOutput(string? workingDirectory = null, params string[] args); Task InstallTkinterIfNecessary(IProgress? progress = null); + Task RunNpm(ProcessArgs args, string? workingDirectory = null); + Task InstallNodeIfNecessary(IProgress? progress = null); } diff --git a/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs b/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs index de9898aa..29fd4a99 100644 --- a/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs +++ b/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs @@ -107,6 +107,16 @@ public class PrerequisiteHelper : IPrerequisiteHelper throw new NotImplementedException(); } + public Task RunNpm(ProcessArgs args, string? workingDirectory = null) + { + throw new NotImplementedException(); + } + + public Task InstallNodeIfNecessary(IProgress? progress = null) + { + throw new NotImplementedException(); + } + public async Task InstallAllIfNecessary(IProgress? progress = null) { await InstallVcRedistIfNecessary(progress); diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 62107eb1..0254f52f 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -61,10 +61,19 @@ public class InvokeAI : BaseGitPackage public override Dictionary> SharedFolders => new() { - [SharedFolderType.StableDiffusion] = new[] { Path.Combine(RelativeRootPath, "autoimport", "main") }, + [SharedFolderType.StableDiffusion] = new[] + { + Path.Combine(RelativeRootPath, "autoimport", "main") + }, [SharedFolderType.Lora] = new[] { Path.Combine(RelativeRootPath, "autoimport", "lora") }, - [SharedFolderType.TextualInversion] = new[] { Path.Combine(RelativeRootPath, "autoimport", "embedding") }, - [SharedFolderType.ControlNet] = new[] { Path.Combine(RelativeRootPath, "autoimport", "controlnet") }, + [SharedFolderType.TextualInversion] = new[] + { + Path.Combine(RelativeRootPath, "autoimport", "embedding") + }, + [SharedFolderType.ControlNet] = new[] + { + Path.Combine(RelativeRootPath, "autoimport", "controlnet") + }, [SharedFolderType.InvokeIpAdapters15] = new[] { Path.Combine(RelativeRootPath, "models", "sd-1", "ip_adapter") @@ -77,7 +86,10 @@ public class InvokeAI : BaseGitPackage { Path.Combine(RelativeRootPath, "models", "any", "clip_vision") }, - [SharedFolderType.T2IAdapter] = new[] { Path.Combine(RelativeRootPath, "autoimport", "t2i_adapter") } + [SharedFolderType.T2IAdapter] = new[] + { + Path.Combine(RelativeRootPath, "autoimport", "t2i_adapter") + } }; public override Dictionary>? SharedOutputFolders => @@ -168,13 +180,39 @@ public class InvokeAI : BaseGitPackage venvRunner.EnvironmentVariables = GetEnvVars(installLocation); progress?.Report(new ProgressReport(-1f, "Installing Package", isIndeterminate: true)); + await PrerequisiteHelper.InstallNodeIfNecessary(progress).ConfigureAwait(false); + await PrerequisiteHelper.RunNpm(["i", "pnpm"], installLocation).ConfigureAwait(false); + + var pnpmPath = Path.Combine(installLocation, "node_modules", ".bin", "pnpm.cmd"); + var invokeFrontendPath = Path.Combine(installLocation, "invokeai", "frontend", "web"); + + var process = ProcessRunner.StartProcess( + pnpmPath, + "i", + invokeFrontendPath, + s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }) + ); + + await process.WaitForExitAsync().ConfigureAwait(false); + + process = ProcessRunner.StartProcess( + pnpmPath, + "build", + invokeFrontendPath, + s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }) + ); + + await process.WaitForExitAsync().ConfigureAwait(false); + var pipCommandArgs = "-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu"; switch (torchVersion) { // If has Nvidia Gpu, install CUDA version case TorchVersion.Cuda: - progress?.Report(new ProgressReport(-1f, "Installing PyTorch for CUDA", isIndeterminate: true)); + progress?.Report( + new ProgressReport(-1f, "Installing PyTorch for CUDA", isIndeterminate: true) + ); var args = new List(); if (exists) @@ -200,18 +238,23 @@ public class InvokeAI : BaseGitPackage .ConfigureAwait(false); Logger.Info("Starting InvokeAI install (CUDA)..."); - pipCommandArgs = "-e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121"; + pipCommandArgs = + "-e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121"; break; // For AMD, Install ROCm version case TorchVersion.Rocm: await venvRunner .PipInstall( - new PipInstallArgs().WithTorch("==2.0.1").WithTorchVision().WithExtraIndex("rocm5.4.2"), + new PipInstallArgs() + .WithTorch("==2.0.1") + .WithTorchVision() + .WithExtraIndex("rocm5.4.2"), onConsoleOutput ) .ConfigureAwait(false); Logger.Info("Starting InvokeAI install (ROCm)..."); - pipCommandArgs = "-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/rocm5.4.2"; + pipCommandArgs = + "-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/rocm5.4.2"; break; case TorchVersion.Mps: // For Apple silicon, use MPS @@ -320,7 +363,9 @@ public class InvokeAI : BaseGitPackage { onConsoleOutput?.Invoke(s); - if (spam3 && s.Text.Contains("[3] Accept the best guess;", StringComparison.OrdinalIgnoreCase)) + if ( + spam3 && s.Text.Contains("[3] Accept the best guess;", StringComparison.OrdinalIgnoreCase) + ) { VenvRunner.Process?.StandardInput.WriteLine("3"); return; From 713e32473aa26080611dd4e4956c692045ea696d Mon Sep 17 00:00:00 2001 From: JT Date: Sat, 13 Jan 2024 23:55:24 -0800 Subject: [PATCH 02/13] add npm stuff for linux/macos (cherry picked from commit 0325fe01a686e751afea8a22254fb282032f61f1) --- .../Helpers/UnixPrerequisiteHelper.cs | 81 ++++++++++++++++--- .../Helpers/WindowsPrerequisiteHelper.cs | 2 +- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index fdc61545..27266737 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.Versioning; using System.Threading.Tasks; using Avalonia.Controls; +using DynamicData; using FluentAvalonia.UI.Controls; using NLog; using StabilityMatrix.Avalonia.Languages; @@ -32,10 +33,13 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper private DirectoryPath PythonDir => AssetsDir.JoinDir("Python310"); public bool IsPythonInstalled => PythonDir.JoinFile(PyRunner.RelativePythonDllPath).Exists; - private DirectoryPath PortableGitInstallDir => HomeDir + "PortableGit"; public string GitBinPath => PortableGitInstallDir + "bin"; + private DirectoryPath NodeDir => AssetsDir.JoinDir("nodejs"); + private string NpmPath => Path.Combine(NodeDir, "bin", "npm"); + private bool IsNodeInstalled => File.Exists(NpmPath); + // Cached store of whether or not git is installed private bool? isGitInstalled; @@ -240,23 +244,80 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper throw new NotImplementedException(); } - [UnsupportedOSPlatform("Linux")] - [UnsupportedOSPlatform("macOS")] - public Task InstallTkinterIfNecessary(IProgress? progress = null) + [SupportedOSPlatform("Linux")] + [SupportedOSPlatform("macOS")] + public async Task RunNpm(ProcessArgs args, string? workingDirectory = null) { - throw new PlatformNotSupportedException(); + var command = args.Prepend(NpmPath); + + var result = await ProcessRunner.RunBashCommand(command.ToArray(), workingDirectory ?? ""); + if (result.ExitCode != 0) + { + Logger.Error( + "npm command [{Command}] failed with exit code " + "{ExitCode}:\n{StdOut}\n{StdErr}", + command, + result.ExitCode, + result.StandardOutput, + result.StandardError + ); + + throw new ProcessException( + $"npm command [{command}] failed with exit code" + + $" {result.ExitCode}:\n{result.StandardOutput}\n{result.StandardError}" + ); + } } - [UnsupportedOSPlatform("Linux")] - [UnsupportedOSPlatform("macOS")] - public Task RunNpm(ProcessArgs args, string? workingDirectory = null) + [SupportedOSPlatform("Linux")] + [SupportedOSPlatform("macOS")] + public async Task InstallNodeIfNecessary(IProgress? progress = null) { - throw new PlatformNotSupportedException(); + if (IsNodeInstalled) + { + Logger.Info("node already installed"); + return; + } + + Logger.Info("Downloading node"); + + var downloadUrl = Compat.IsMacOS + ? "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-arm64.tar.gz" + : "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz"; + + var nodeDownloadPath = Compat.IsMacOS + ? Path.Combine(AssetsDir, "nodejs.tar.gz") + : Path.Combine(AssetsDir, "nodejs.tar.xz"); + + await downloadService.DownloadToFileAsync(downloadUrl, nodeDownloadPath, progress: progress); + + Logger.Info("Installing node"); + progress?.Report( + new ProgressReport( + progress: 0.5f, + isIndeterminate: true, + type: ProgressType.Generic, + message: "Installing prerequisites..." + ) + ); + + // unzip + await ArchiveHelper.Extract(nodeDownloadPath, AssetsDir, progress); + + var nodeDir = Compat.IsMacOS + ? AssetsDir.JoinDir("node-v20.11.0-darwin-arm64") + : AssetsDir.JoinDir("node-v20.11.0-linux-x64"); + Directory.Move(nodeDir, NodeDir); + + progress?.Report( + new ProgressReport(progress: 1f, message: "Node install complete", type: ProgressType.Generic) + ); + + File.Delete(nodeDownloadPath); } [UnsupportedOSPlatform("Linux")] [UnsupportedOSPlatform("macOS")] - public Task InstallNodeIfNecessary(IProgress? progress = null) + public Task InstallTkinterIfNecessary(IProgress? progress = null) { throw new PlatformNotSupportedException(); } diff --git a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs index aca77bd6..48fd3a22 100644 --- a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs @@ -26,7 +26,7 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper private const string VcRedistDownloadUrl = "https://aka.ms/vs/16/release/vc_redist.x64.exe"; private const string TkinterDownloadUrl = "https://cdn.lykos.ai/tkinter-cpython-embedded-3.10.11-win-x64.zip"; - private const string NodeDownloadUrl = "https://cdn.lykos.ai/nodejs.zip"; + private const string NodeDownloadUrl = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-win-x64.zip"; private string HomeDir => settingsManager.LibraryDir; From bc1b912c7aed1fa49be290d453a52f8982fe84c5 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 00:06:38 -0800 Subject: [PATCH 03/13] extractManaged (cherry picked from commit 7779739ed69182ebd205b25d9909265fde612147) --- StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index 27266737..7db0898c 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -301,7 +301,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper ); // unzip - await ArchiveHelper.Extract(nodeDownloadPath, AssetsDir, progress); + await ArchiveHelper.ExtractManaged(nodeDownloadPath, AssetsDir); var nodeDir = Compat.IsMacOS ? AssetsDir.JoinDir("node-v20.11.0-darwin-arm64") From 30d840afb4e208d207f4de2552120c1a3859e80a Mon Sep 17 00:00:00 2001 From: ionite34 Date: Sun, 14 Jan 2024 16:23:21 +0800 Subject: [PATCH 04/13] Fix visibility (cherry picked from commit 5f874183d1df0d90a67c85f5d025f9253b5af2c7) # Conflicts: # StabilityMatrix.Core/Processes/ProcessArgs.cs --- StabilityMatrix.Core/Processes/ProcessArgs.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/StabilityMatrix.Core/Processes/ProcessArgs.cs b/StabilityMatrix.Core/Processes/ProcessArgs.cs index 8fd296b3..a9154b2a 100644 --- a/StabilityMatrix.Core/Processes/ProcessArgs.cs +++ b/StabilityMatrix.Core/Processes/ProcessArgs.cs @@ -71,3 +71,8 @@ public partial class ProcessArgs : OneOfBase, IEnumerable values) => new(values.ToArray()); +} From 7d2675d18ed0f71b843ec143576a5c495b92a2e7 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 00:25:34 -0800 Subject: [PATCH 05/13] fix npm path & update windows folder name (cherry picked from commit 95c2e43470c5651e6c9ad317d9fba85f631e19f5) --- StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs | 4 ++-- StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index 7db0898c..68038101 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -37,7 +37,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper public string GitBinPath => PortableGitInstallDir + "bin"; private DirectoryPath NodeDir => AssetsDir.JoinDir("nodejs"); - private string NpmPath => Path.Combine(NodeDir, "bin", "npm"); + private string NpmPath => Path.Combine(NodeDir, "lib", "node_modules", "npm", "bin", "npm"); private bool IsNodeInstalled => File.Exists(NpmPath); // Cached store of whether or not git is installed @@ -248,7 +248,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper [SupportedOSPlatform("macOS")] public async Task RunNpm(ProcessArgs args, string? workingDirectory = null) { - var command = args.Prepend(NpmPath); + var command = args.Prepend([NpmPath]); var result = await ProcessRunner.RunBashCommand(command.ToArray(), workingDirectory ?? ""); if (result.ExitCode != 0) diff --git a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs index 48fd3a22..4660a835 100644 --- a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs @@ -410,6 +410,10 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper // unzip await ArchiveHelper.Extract(NodeDownloadPath, AssetsDir, progress); + // move to assets dir + var existingNodeDir = Path.Combine(AssetsDir, "node-v20.11.0-win-x64"); + Directory.Move(existingNodeDir, Path.Combine(AssetsDir, "nodejs")); + progress?.Report( new ProgressReport(progress: 1f, message: "Node install complete", type: ProgressType.Generic) ); From f663b101bab439df54b8cbb985c33c58f9908662 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 00:51:03 -0800 Subject: [PATCH 06/13] moar fixes & add to path (cherry picked from commit c10c04a5c50cbee60c53242454e229503198c61e) --- .../Helpers/UnixPrerequisiteHelper.cs | 8 +++--- .../Models/Packages/InvokeAI.cs | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index 68038101..cea0dac6 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -37,7 +37,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper public string GitBinPath => PortableGitInstallDir + "bin"; private DirectoryPath NodeDir => AssetsDir.JoinDir("nodejs"); - private string NpmPath => Path.Combine(NodeDir, "lib", "node_modules", "npm", "bin", "npm"); + private string NpmPath => Path.Combine(NodeDir, "bin", "npm"); private bool IsNodeInstalled => File.Exists(NpmPath); // Cached store of whether or not git is installed @@ -284,9 +284,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper ? "https://nodejs.org/dist/v20.11.0/node-v20.11.0-darwin-arm64.tar.gz" : "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz"; - var nodeDownloadPath = Compat.IsMacOS - ? Path.Combine(AssetsDir, "nodejs.tar.gz") - : Path.Combine(AssetsDir, "nodejs.tar.xz"); + var nodeDownloadPath = AssetsDir.JoinFile(Path.GetFileName(downloadUrl)); await downloadService.DownloadToFileAsync(downloadUrl, nodeDownloadPath, progress: progress); @@ -301,7 +299,7 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper ); // unzip - await ArchiveHelper.ExtractManaged(nodeDownloadPath, AssetsDir); + await ArchiveHelper.Extract7ZAuto(nodeDownloadPath, AssetsDir); var nodeDir = Compat.IsMacOS ? AssetsDir.JoinDir("node-v20.11.0-darwin-arm64") diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 0254f52f..6f0fc676 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -183,14 +183,20 @@ public class InvokeAI : BaseGitPackage await PrerequisiteHelper.InstallNodeIfNecessary(progress).ConfigureAwait(false); await PrerequisiteHelper.RunNpm(["i", "pnpm"], installLocation).ConfigureAwait(false); - var pnpmPath = Path.Combine(installLocation, "node_modules", ".bin", "pnpm.cmd"); + var pnpmPath = Path.Combine( + installLocation, + "node_modules", + ".bin", + Compat.IsWindows ? "pnpm.cmd" : "pnpm" + ); var invokeFrontendPath = Path.Combine(installLocation, "invokeai", "frontend", "web"); var process = ProcessRunner.StartProcess( pnpmPath, "i", invokeFrontendPath, - s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }) + s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), + venvRunner.EnvironmentVariables ); await process.WaitForExitAsync().ConfigureAwait(false); @@ -199,7 +205,8 @@ public class InvokeAI : BaseGitPackage pnpmPath, "build", invokeFrontendPath, - s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }) + s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), + venvRunner.EnvironmentVariables ); await process.WaitForExitAsync().ConfigureAwait(false); @@ -407,6 +414,20 @@ public class InvokeAI : BaseGitPackage root.Create(); env["INVOKEAI_ROOT"] = root; + if (env.ContainsKey("PATH")) + { + env["PATH"] += $";{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs")}"; + } + else + { + env["PATH"] = Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs"); + } + + if (Compat.IsMacOS || Compat.IsLinux) + { + env["PATH"] += $";{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs", "bin")}"; + } + return env; } } From afa92b7b57e8605fd298db4a828aae8fbd03e0f6 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 00:56:03 -0800 Subject: [PATCH 07/13] path delimiter (cherry picked from commit b70cd8a5491541b4500b9d770948bd5c9c989616) --- StabilityMatrix.Core/Models/Packages/InvokeAI.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 6f0fc676..cf7aa926 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -416,7 +416,8 @@ public class InvokeAI : BaseGitPackage if (env.ContainsKey("PATH")) { - env["PATH"] += $";{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs")}"; + env["PATH"] += + $"{Compat.PathDelimiter}{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs")}"; } else { @@ -425,7 +426,8 @@ public class InvokeAI : BaseGitPackage if (Compat.IsMacOS || Compat.IsLinux) { - env["PATH"] += $";{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs", "bin")}"; + env["PATH"] += + $"{Compat.PathDelimiter}{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs", "bin")}"; } return env; From 57a087f95e3a5280983c6e732d850e1d80a22be4 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 01:36:57 -0800 Subject: [PATCH 08/13] fix windows (cherry picked from commit 3ab6a2cf4122990f44dd1c404e504d0f51843b6a) --- StabilityMatrix.Core/Models/Packages/InvokeAI.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index cf7aa926..c4df11bb 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -424,12 +424,20 @@ public class InvokeAI : BaseGitPackage env["PATH"] = Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs"); } + env["PATH"] += $"{Compat.PathDelimiter}{Path.Combine(installPath, "node_modules", ".bin")}"; + if (Compat.IsMacOS || Compat.IsLinux) { env["PATH"] += $"{Compat.PathDelimiter}{Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs", "bin")}"; } + if (Compat.IsWindows) + { + env["PATH"] += + $"{Compat.PathDelimiter}{Environment.GetFolderPath(Environment.SpecialFolder.System)}"; + } + return env; } } From 5f443ace4be62178c1099e15ba556a197919a126 Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 13:49:13 -0800 Subject: [PATCH 09/13] moar path (cherry picked from commit c242bf143565a8a089dd5a8f3696dca182101e3e) --- StabilityMatrix.Core/Models/Packages/InvokeAI.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index c4df11bb..1236b802 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -193,7 +193,7 @@ public class InvokeAI : BaseGitPackage var process = ProcessRunner.StartProcess( pnpmPath, - "i", + "i --ignore-scripts=true", invokeFrontendPath, s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), venvRunner.EnvironmentVariables @@ -425,6 +425,8 @@ public class InvokeAI : BaseGitPackage } env["PATH"] += $"{Compat.PathDelimiter}{Path.Combine(installPath, "node_modules", ".bin")}"; + env["PATH"] += + $"{Compat.PathDelimiter}{Path.Combine(installPath, "invokeai", "frontend", "web", "node_modules", ".bin")}"; if (Compat.IsMacOS || Compat.IsLinux) { From a40d820d6ae9569741ef73dee04ffab004314a96 Mon Sep 17 00:00:00 2001 From: jt Date: Sun, 14 Jan 2024 15:12:26 -0800 Subject: [PATCH 10/13] finally fix invoke build on mac/linux (cherry picked from commit 4e21fd9c3a3b3ebe1323b93068fc433143cafe9d) --- .../Helpers/UnixPrerequisiteHelper.cs | 9 ++++++++- .../Helpers/WindowsPrerequisiteHelper.cs | 8 +++++++- .../Helper/IPrerequisiteHelper.cs | 6 +++++- .../Helper/PrerequisiteHelper.cs | 6 +++++- .../Models/Packages/InvokeAI.cs | 18 ++++++++++++++---- 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index cea0dac6..fb394335 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -246,7 +246,11 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper [SupportedOSPlatform("Linux")] [SupportedOSPlatform("macOS")] - public async Task RunNpm(ProcessArgs args, string? workingDirectory = null) + public async Task RunNpm( + ProcessArgs args, + string? workingDirectory = null, + Action? onProcessOutput = null + ) { var command = args.Prepend([NpmPath]); @@ -266,6 +270,9 @@ public class UnixPrerequisiteHelper : IPrerequisiteHelper + $" {result.ExitCode}:\n{result.StandardOutput}\n{result.StandardError}" ); } + + onProcessOutput?.Invoke(ProcessOutput.FromStdOutLine(result.StandardOutput)); + onProcessOutput?.Invoke(ProcessOutput.FromStdErrLine(result.StandardError)); } [SupportedOSPlatform("Linux")] diff --git a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs index 4660a835..9bde3220 100644 --- a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs @@ -114,13 +114,19 @@ public class WindowsPrerequisiteHelper : IPrerequisiteHelper return process; } - public async Task RunNpm(ProcessArgs args, string? workingDirectory = null) + public async Task RunNpm( + ProcessArgs args, + string? workingDirectory = null, + Action? onProcessOutput = null + ) { var result = await ProcessRunner .GetProcessResultAsync(NodeExistsPath, args, workingDirectory) .ConfigureAwait(false); result.EnsureSuccessExitCode(); + onProcessOutput?.Invoke(ProcessOutput.FromStdOutLine(result.StandardOutput)); + onProcessOutput?.Invoke(ProcessOutput.FromStdErrLine(result.StandardError)); } public async Task InstallAllIfNecessary(IProgress? progress = null) diff --git a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs index 00dd38c9..1427a546 100644 --- a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs +++ b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs @@ -34,6 +34,10 @@ public interface IPrerequisiteHelper Task GetGitOutput(string? workingDirectory = null, params string[] args); Task InstallTkinterIfNecessary(IProgress? progress = null); - Task RunNpm(ProcessArgs args, string? workingDirectory = null); + Task RunNpm( + ProcessArgs args, + string? workingDirectory = null, + Action? onProcessOutput = null + ); Task InstallNodeIfNecessary(IProgress? progress = null); } diff --git a/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs b/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs index 29fd4a99..26144e76 100644 --- a/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs +++ b/StabilityMatrix.Core/Helper/PrerequisiteHelper.cs @@ -107,7 +107,11 @@ public class PrerequisiteHelper : IPrerequisiteHelper throw new NotImplementedException(); } - public Task RunNpm(ProcessArgs args, string? workingDirectory = null) + public Task RunNpm( + ProcessArgs args, + string? workingDirectory = null, + Action? onProcessOutput = null + ) { throw new NotImplementedException(); } diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 1236b802..0d95a20d 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -183,12 +183,25 @@ public class InvokeAI : BaseGitPackage await PrerequisiteHelper.InstallNodeIfNecessary(progress).ConfigureAwait(false); await PrerequisiteHelper.RunNpm(["i", "pnpm"], installLocation).ConfigureAwait(false); + if (Compat.IsMacOS || Compat.IsLinux) + { + await PrerequisiteHelper.RunNpm(["i", "vite"], installLocation).ConfigureAwait(false); + } + var pnpmPath = Path.Combine( installLocation, "node_modules", ".bin", Compat.IsWindows ? "pnpm.cmd" : "pnpm" ); + + var vitePath = Path.Combine( + installLocation, + "node_modules", + ".bin", + Compat.IsWindows ? "vite.cmd" : "vite" + ); + var invokeFrontendPath = Path.Combine(installLocation, "invokeai", "frontend", "web"); var process = ProcessRunner.StartProcess( @@ -202,7 +215,7 @@ public class InvokeAI : BaseGitPackage await process.WaitForExitAsync().ConfigureAwait(false); process = ProcessRunner.StartProcess( - pnpmPath, + Compat.IsWindows ? pnpmPath : vitePath, "build", invokeFrontendPath, s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), @@ -423,10 +436,7 @@ public class InvokeAI : BaseGitPackage { env["PATH"] = Path.Combine(SettingsManager.LibraryDir, "Assets", "nodejs"); } - env["PATH"] += $"{Compat.PathDelimiter}{Path.Combine(installPath, "node_modules", ".bin")}"; - env["PATH"] += - $"{Compat.PathDelimiter}{Path.Combine(installPath, "invokeai", "frontend", "web", "node_modules", ".bin")}"; if (Compat.IsMacOS || Compat.IsLinux) { From dd1e1cec3c2ba146a3813c5df2574903d29939fe Mon Sep 17 00:00:00 2001 From: JT Date: Sun, 14 Jan 2024 16:48:10 -0800 Subject: [PATCH 11/13] install invoke frontend on launch if its not there (for people who updated before the fix) (cherry picked from commit 6eca39bc5e67724a816198350538d82168551f6b) --- .../Models/Packages/InvokeAI.cs | 119 +++++++++++------- 1 file changed, 75 insertions(+), 44 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs index 0d95a20d..6118149b 100644 --- a/StabilityMatrix.Core/Models/Packages/InvokeAI.cs +++ b/StabilityMatrix.Core/Models/Packages/InvokeAI.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Diagnostics; +using System.Globalization; using System.Text.RegularExpressions; using NLog; using StabilityMatrix.Core.Attributes; @@ -18,6 +19,7 @@ public class InvokeAI : BaseGitPackage { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private const string RelativeRootPath = "invokeai-root"; + private string RelativeFrontendBuildPath = Path.Combine("invokeai", "frontend", "web", "dist"); public override string Name => "InvokeAI"; public override string DisplayName { get; set; } = "InvokeAI"; @@ -180,49 +182,13 @@ public class InvokeAI : BaseGitPackage venvRunner.EnvironmentVariables = GetEnvVars(installLocation); progress?.Report(new ProgressReport(-1f, "Installing Package", isIndeterminate: true)); - await PrerequisiteHelper.InstallNodeIfNecessary(progress).ConfigureAwait(false); - await PrerequisiteHelper.RunNpm(["i", "pnpm"], installLocation).ConfigureAwait(false); - - if (Compat.IsMacOS || Compat.IsLinux) - { - await PrerequisiteHelper.RunNpm(["i", "vite"], installLocation).ConfigureAwait(false); - } - - var pnpmPath = Path.Combine( - installLocation, - "node_modules", - ".bin", - Compat.IsWindows ? "pnpm.cmd" : "pnpm" - ); - - var vitePath = Path.Combine( - installLocation, - "node_modules", - ".bin", - Compat.IsWindows ? "vite.cmd" : "vite" - ); - - var invokeFrontendPath = Path.Combine(installLocation, "invokeai", "frontend", "web"); - - var process = ProcessRunner.StartProcess( - pnpmPath, - "i --ignore-scripts=true", - invokeFrontendPath, - s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), - venvRunner.EnvironmentVariables - ); - - await process.WaitForExitAsync().ConfigureAwait(false); - - process = ProcessRunner.StartProcess( - Compat.IsWindows ? pnpmPath : vitePath, - "build", - invokeFrontendPath, - s => onConsoleOutput?.Invoke(new ProcessOutput { Text = s }), - venvRunner.EnvironmentVariables - ); - - await process.WaitForExitAsync().ConfigureAwait(false); + await SetupAndBuildInvokeFrontend( + installLocation, + progress, + onConsoleOutput, + venvRunner.EnvironmentVariables + ) + .ConfigureAwait(false); var pipCommandArgs = "-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu"; @@ -309,6 +275,58 @@ public class InvokeAI : BaseGitPackage progress?.Report(new ProgressReport(1f, "Done!", isIndeterminate: false)); } + private async Task SetupAndBuildInvokeFrontend( + string installLocation, + IProgress? progress, + Action? onConsoleOutput, + IReadOnlyDictionary? envVars = null + ) + { + await PrerequisiteHelper.InstallNodeIfNecessary(progress).ConfigureAwait(false); + await PrerequisiteHelper.RunNpm(["i", "pnpm"], installLocation).ConfigureAwait(false); + + if (Compat.IsMacOS || Compat.IsLinux) + { + await PrerequisiteHelper.RunNpm(["i", "vite"], installLocation).ConfigureAwait(false); + } + + var pnpmPath = Path.Combine( + installLocation, + "node_modules", + ".bin", + Compat.IsWindows ? "pnpm.cmd" : "pnpm" + ); + + var vitePath = Path.Combine( + installLocation, + "node_modules", + ".bin", + Compat.IsWindows ? "vite.cmd" : "vite" + ); + + var invokeFrontendPath = Path.Combine(installLocation, "invokeai", "frontend", "web"); + + var process = ProcessRunner.StartAnsiProcess( + pnpmPath, + "i --ignore-scripts=true", + invokeFrontendPath, + onConsoleOutput, + envVars + ); + + await process.WaitForExitAsync().ConfigureAwait(false); + + process = ProcessRunner.StartAnsiProcess( + Compat.IsWindows ? pnpmPath : vitePath, + "build", + invokeFrontendPath, + onConsoleOutput, + envVars + ); + + await process.WaitForExitAsync().ConfigureAwait(false); + } + public override Task RunPackage( string installedPackagePath, string command, @@ -340,6 +358,19 @@ public class InvokeAI : BaseGitPackage VenvRunner.EnvironmentVariables = GetEnvVars(installedPackagePath); + // fix frontend build missing for people who updated to v3.6 before the fix + var frontendExistsPath = Path.Combine(installedPackagePath, RelativeFrontendBuildPath); + if (!Directory.Exists(frontendExistsPath)) + { + await SetupAndBuildInvokeFrontend( + installedPackagePath, + null, + onConsoleOutput, + VenvRunner.EnvironmentVariables + ) + .ConfigureAwait(false); + } + // Launch command is for a console entry point, and not a direct script var entryPoint = await VenvRunner.GetEntryPoint(command).ConfigureAwait(false); From f41f5b1fdeec6eeaa977be39f380a566fad36de5 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 15 Jan 2024 10:42:35 +0800 Subject: [PATCH 12/13] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c93fcbe..05818a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.7.9 +### Fixed +- Fixed InvokeAI v3.6.0 `"detail": "Not Found"` error when opening the UI + ## v2.7.8 ### Changed - Python Packages install dialog now allows entering multiple arguments or option flags From c548ea43e3f8ddb3751b44b1e51c7a92a780ed76 Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 15 Jan 2024 10:46:28 +0800 Subject: [PATCH 13/13] Add CollectionBuilder for ProcessArgs --- StabilityMatrix.Core/Processes/ProcessArgs.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/StabilityMatrix.Core/Processes/ProcessArgs.cs b/StabilityMatrix.Core/Processes/ProcessArgs.cs index a9154b2a..9adac6d6 100644 --- a/StabilityMatrix.Core/Processes/ProcessArgs.cs +++ b/StabilityMatrix.Core/Processes/ProcessArgs.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using OneOf; @@ -9,6 +10,7 @@ namespace StabilityMatrix.Core.Processes; /// Implicitly converts between string and string[], /// with no parsing if the input and output types are the same. /// +[CollectionBuilder(typeof(ProcessArgsCollectionBuilder), "Create")] public partial class ProcessArgs : OneOfBase, IEnumerable { ///