diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index b302b23e..32d9e981 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -1,6 +1,7 @@ using AIStudio.Dialogs; using AIStudio.Settings; using AIStudio.Settings.DataModel; +using AIStudio.Tools.PluginSystem; using AIStudio.Tools.Rust; using AIStudio.Tools.Services; @@ -81,6 +82,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis // Ensure that all settings are loaded: await this.SettingsManager.LoadSettings(); + // Ensure that all internal plugins are present: + await PluginFactory.EnsureInternalPlugins(); + // Register this component with the message bus: this.MessageBus.RegisterComponent(this); this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.USER_SEARCH_FOR_UPDATE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED ]); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/InternalPlugin.cs b/app/MindWork AI Studio/Tools/PluginSystem/InternalPlugin.cs new file mode 100644 index 00000000..1cbd8ca7 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/InternalPlugin.cs @@ -0,0 +1,7 @@ +namespace AIStudio.Tools.PluginSystem; + +public enum InternalPlugin +{ + LANGUAGE_EN_US, + LANGUAGE_DE_DE, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginData.cs b/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginData.cs new file mode 100644 index 00000000..d7595f2a --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginData.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.PluginSystem; + +public readonly record struct InternalPluginData(PluginType Type, Guid Id, string ShortName) +{ + public string ResourcePath => $"{this.Type.GetDirectory()}/{this.ShortName.ToLowerInvariant()}-{this.Id}"; + + public string ResourceName => $"{this.ShortName.ToLowerInvariant()}-{this.Id}"; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginExtensions.cs b/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginExtensions.cs new file mode 100644 index 00000000..3a25aa18 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/InternalPluginExtensions.cs @@ -0,0 +1,12 @@ +namespace AIStudio.Tools.PluginSystem; + +public static class InternalPluginExtensions +{ + public static InternalPluginData MetaData(this InternalPlugin plugin) => plugin switch + { + InternalPlugin.LANGUAGE_EN_US => new (PluginType.LANGUAGE, new("97dfb1ba-50c4-4440-8dfa-6575daf543c8"), "en-us"), + InternalPlugin.LANGUAGE_DE_DE => new(PluginType.LANGUAGE, new("43065dbc-78d0-45b7-92be-f14c2926e2dc"), "de-de"), + + _ => new InternalPluginData(PluginType.NONE, Guid.Empty, "unknown") + }; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs index dd14b8fe..9b1d1ebb 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.cs @@ -1,11 +1,90 @@ -using System.Text; +using System.Reflection; + +using AIStudio.Settings; using Lua; +using Microsoft.Extensions.FileProviders; + namespace AIStudio.Tools.PluginSystem; public static class PluginFactory { + private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger("PluginFactory"); + private static readonly string DATA_DIR = SettingsManager.DataDirectory!; + + public static async Task EnsureInternalPlugins() + { + LOG.LogInformation("Start ensuring internal plugins."); + foreach (var plugin in Enum.GetValues()) + { + LOG.LogInformation($"Ensure plugin: {plugin}"); + await EnsurePlugin(plugin); + } + } + + private static async Task EnsurePlugin(InternalPlugin plugin) + { + try + { + #if DEBUG + var basePath = Path.Join(Environment.CurrentDirectory, "Plugins"); + var resourceFileProvider = new PhysicalFileProvider(basePath); + #else + var resourceFileProvider = new ManifestEmbeddedFileProvider(Assembly.GetAssembly(type: typeof(Program))!, "Plugins"); + #endif + + var metaData = plugin.MetaData(); + var mainResourcePath = $"{metaData.ResourcePath}/plugin.lua"; + var resourceInfo = resourceFileProvider.GetFileInfo(mainResourcePath); + + if(!resourceInfo.Exists) + { + LOG.LogError($"The plugin {plugin} does not exist. This should not happen, since the plugin is an integral part of AI Studio."); + return; + } + + // Ensure that the additional resources exist: + foreach (var content in resourceFileProvider.GetDirectoryContents(metaData.ResourcePath)) + { + if(content.IsDirectory) + { + LOG.LogError("The plugin contains a directory. This is not allowed."); + continue; + } + + await CopyPluginFile(content, metaData); + } + } + catch + { + LOG.LogError($"Was not able to ensure the plugin: {plugin}"); + } + } + + private static async Task CopyPluginFile(IFileInfo resourceInfo, InternalPluginData metaData) + { + await using var inputStream = resourceInfo.CreateReadStream(); + + var pluginsRoot = Path.Join(DATA_DIR, "plugins"); + var pluginTypeBasePath = Path.Join(pluginsRoot, metaData.Type.GetDirectory()); + + if (!Directory.Exists(pluginsRoot)) + Directory.CreateDirectory(pluginsRoot); + + if (!Directory.Exists(pluginTypeBasePath)) + Directory.CreateDirectory(pluginTypeBasePath); + + var pluginPath = Path.Join(pluginTypeBasePath, metaData.ResourceName); + if (!Directory.Exists(pluginPath)) + Directory.CreateDirectory(pluginPath); + + var pluginFilePath = Path.Join(pluginPath, resourceInfo.Name); + + await using var outputStream = File.Create(pluginFilePath); + await inputStream.CopyToAsync(outputStream); + } + public static async Task LoadAll() { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginTypeExtensions.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginTypeExtensions.cs index a26cb03c..b65eb502 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginTypeExtensions.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginTypeExtensions.cs @@ -11,4 +11,14 @@ public static class PluginTypeExtensions _ => "Unknown plugin type", }; + + public static string GetDirectory(this PluginType type) => type switch + { + PluginType.LANGUAGE => "languages", + PluginType.ASSISTANT => "assistants", + PluginType.CONFIGURATION => "configurations", + PluginType.THEME => "themes", + + _ => "unknown", + }; } \ No newline at end of file