using AIStudio.Settings; using AIStudio.Tools.AssistantSessions; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem.Assistants; namespace AIStudio.Tools.Services; /// /// Installs, updates, and removes the plugins AI Studio manages locally. /// /// /// The implementation is split across several files:
/// - PluginInstallService.AssistantBuilder.cs: installing generated assistant plugin code
/// - PluginInstallService.Editing.cs: editing an installed assistant plugin
/// - PluginInstallService.Import.cs: importing plugin archives
/// - PluginInstallService.Delete.cs: removing installed plugins
/// - PluginInstallService.Installation.cs: the shared validation and installation steps
/// - PluginInstallService.FileSystem.cs: the shared path and directory helpers ///
public sealed partial class PluginInstallService { private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginInstallService).Namespace, nameof(PluginInstallService)); private const string PLUGIN_FILE_NAME = "plugin.lua"; private const string ASSISTANT_BUILDER_DIRECTORY_PREFIX = "assistant-builder"; private const string DELETE_BACKUP_DIRECTORY = ".plugin-delete-backups"; private const string INSTALL_BACKUP_DIRECTORY = ".plugin-install-backups"; private const int DIRECTORY_PREFIX_MAX_LEN = 80; private readonly ILogger logger; private readonly SettingsManager settingsManager; private readonly AssistantSessionService assistantSessionService; private readonly MediaTranscriptionService mediaTranscriptionService; private readonly SemaphoreSlim installSemaphore = new(1, 1); private static AssistantPluginInstallResult Error(string issue) => new(false, Guid.Empty, string.Empty, string.Empty, false, issue); private static AssistantPluginInstallResult CancelledByUser() => new(false, Guid.Empty, string.Empty, string.Empty, false, string.Empty, true); private static AssistantPluginCheckResult CheckError(string issue) => new(false, Guid.Empty, string.Empty, issue); private static PluginDeleteResult DeleteError(IPluginMetadata plugin, string pluginDirectory, string issue) => new(false, plugin.Id, plugin.Name, pluginDirectory, issue); private static AssistantPluginUpdateResult UpdateError(IPluginMetadata plugin, string pluginDirectory, string issue) => new(false, plugin.Id, plugin.Name, pluginDirectory, issue); public PluginInstallService(ILogger logger, SettingsManager settingsManager, AssistantSessionService assistantSessionService, MediaTranscriptionService mediaTranscriptionService) { this.logger = logger; this.settingsManager = settingsManager; this.assistantSessionService = assistantSessionService; this.mediaTranscriptionService = mediaTranscriptionService; this.logger.LogInformation("The plugin install service has been initialized."); } private sealed record PluginValidationResult(bool Success, string StagingDirectory, PluginBase? Plugin, string Issue) { public static PluginValidationResult Failure(string issue) => new(false, string.Empty, null, issue); /// /// The validated plugin as an assistant plugin, or null when it has another type. /// public PluginAssistants? AssistantPlugin => this.Plugin as PluginAssistants; } }