using System.Text; using AIStudio.Settings; using AIStudio.Settings.DataModel; using AIStudio.Tools.PluginSystem.Assistants; namespace AIStudio.Tools.PluginSystem; public static partial class PluginFactory { private static readonly List RUNNING_PLUGINS = []; /// /// A list of all running plugins. /// public static IReadOnlyCollection RunningPlugins => RUNNING_PLUGINS; private static async Task> RestartAllPlugins(CancellationToken cancellationToken = default) { LOG.LogInformation("Try to start or restart all plugins."); var configObjects = new List(); RUNNING_PLUGINS.Clear(); // // Get the base language plugin. This is the plugin that will be used to fill in missing keys. // var baseLanguagePluginId = InternalPlugin.LANGUAGE_EN_US.MetaData().Id; var baseLanguagePluginMetaData = AVAILABLE_PLUGINS.FirstOrDefault(p => p.Id == baseLanguagePluginId); if (baseLanguagePluginMetaData is null) LOG.LogError($"Was not able to find the base language plugin: Id='{baseLanguagePluginId}'. Please check your installation."); else { try { var startedBasePlugin = await Start(baseLanguagePluginMetaData, cancellationToken); if (startedBasePlugin is NoPlugin noPlugin) LOG.LogError($"Was not able to start the base language plugin: Id='{baseLanguagePluginId}'. Reason: {noPlugin.Issues.First()}"); if (startedBasePlugin is PluginLanguage languagePlugin) { BaseLanguage = languagePlugin; RUNNING_PLUGINS.Add(languagePlugin); LOG.LogInformation($"Successfully started the base language plugin: Id='{languagePlugin.Id}', Type='{languagePlugin.Type}', Name='{languagePlugin.Name}', Version='{languagePlugin.Version}'"); } else LOG.LogError($"Was not able to start the base language plugin: Id='{baseLanguagePluginId}'. Reason: {string.Join("; ", startedBasePlugin.Issues)}"); } catch (Exception e) { LOG.LogError(e, $"An error occurred while starting the base language plugin: Id='{baseLanguagePluginId}'."); BaseLanguage = NoPluginLanguage.INSTANCE; } } // // Iterate over all available plugins and try to start them. // foreach (var availablePlugin in AVAILABLE_PLUGINS) { if(cancellationToken.IsCancellationRequested) { LOG.LogWarning("Cancellation requested while starting plugins. Stopping the plugin startup process. Probably due to a timeout."); break; } if (availablePlugin.Id == baseLanguagePluginId) continue; try { if (availablePlugin.IsInternal || SettingsManagerAccess.IsPluginEnabled(availablePlugin) || availablePlugin.Type == PluginType.CONFIGURATION || availablePlugin.Type == PluginType.ASSISTANT) if(await Start(availablePlugin, cancellationToken) is { IsValid: true } plugin) { if (plugin is PluginConfiguration configPlugin) configObjects.AddRange(configPlugin.ConfigObjects); RUNNING_PLUGINS.Add(plugin); } } catch (Exception e) { LOG.LogError(e, $"An error occurred while starting the plugin: Id='{availablePlugin.Id}', Type='{availablePlugin.Type}', Name='{availablePlugin.Name}', Version='{availablePlugin.Version}'."); } } LogAssistantPluginStartupState(); // Inform all components that the plugins have been reloaded or started: await MessageBus.INSTANCE.SendMessage(null, Event.PLUGINS_RELOADED); return configObjects; } private static void LogAssistantPluginStartupState() { ManagedConfiguration.TryGet(x => x.AssistantPluginAudit, x => x.EnterpriseApprovedPlugins, out ConfigMeta> configMeta); var approvedByConfigPluginId = configMeta is { IsLocked: true } ? configMeta.LockedByConfigPluginId : Guid.Empty; var approvedByConfigPluginName = approvedByConfigPluginId == Guid.Empty ? string.Empty : AVAILABLE_PLUGINS.FirstOrDefault(x => x.Id == approvedByConfigPluginId)?.Name ?? string.Empty; foreach (var assistantPlugin in RUNNING_PLUGINS.OfType()) { var securityState = PluginAssistantSecurityResolver.Resolve(SettingsManagerAccess, assistantPlugin); if (securityState.IsEnterpriseApproved) { LOG.LogInformation( $"Successfully started assistant plugin: Id='{assistantPlugin.Id}', Type='{assistantPlugin.Type}', Name='{assistantPlugin.Name}', Version='{assistantPlugin.Version}', SecuritySource='EnterpriseApproval', ApprovedByConfigPluginId='{approvedByConfigPluginId}', ApprovedByConfigPluginName='{approvedByConfigPluginName}'"); continue; } LOG.LogInformation( $"Successfully started assistant plugin: Id='{assistantPlugin.Id}', Type='{assistantPlugin.Type}', Name='{assistantPlugin.Name}', Version='{assistantPlugin.Version}'"); } } private static async Task Start(IAvailablePlugin meta, CancellationToken cancellationToken = default) { var pluginMainFile = Path.Join(meta.LocalPath, "plugin.lua"); if(!File.Exists(pluginMainFile)) { LOG.LogError($"Was not able to start plugin: Id='{meta.Id}', Type='{meta.Type}', Name='{meta.Name}', Version='{meta.Version}'. Reason: The plugin file does not exist."); return new NoPlugin($"The plugin file does not exist: {pluginMainFile}"); } var code = await File.ReadAllTextAsync(pluginMainFile, Encoding.UTF8, cancellationToken); var plugin = await Load(meta.LocalPath, code, cancellationToken); plugin.PluginPath = meta.LocalPath; if (plugin is NoPlugin noPlugin) { LOG.LogError($"Was not able to start plugin: Id='{meta.Id}', Type='{meta.Type}', Name='{meta.Name}', Version='{meta.Version}'. Reason: {noPlugin.Issues.First()}"); return noPlugin; } if (plugin.IsValid) { // // When this is a language plugin, we need to set the base language plugin. // if (plugin is PluginLanguage languagePlugin && BaseLanguage != NoPluginLanguage.INSTANCE) languagePlugin.SetBaseLanguage(BaseLanguage); if(plugin is PluginConfiguration configPlugin) await configPlugin.InitializeAsync(false); LOG.LogInformation($"Successfully started plugin: Id='{plugin.Id}', Type='{plugin.Type}', Name='{plugin.Name}', Version='{plugin.Version}'"); return plugin; } LOG.LogError($"Was not able to start plugin: Id='{meta.Id}', Type='{meta.Type}', Name='{meta.Name}', Version='{meta.Version}'. Reasons: {string.Join("; ", plugin.Issues)}"); return new NoPlugin($"Was not able to start plugin: Id='{meta.Id}', Type='{meta.Type}', Name='{meta.Name}', Version='{meta.Version}'. Reasons: {string.Join("; ", plugin.Issues)}"); } }