From 2f81546cfebfefddd20c840217513fe72e462db2 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 13 Jul 2026 13:35:24 +0200 Subject: [PATCH] Improved enterprise update policies & Flatpak support (#853) --- .../Assistants/I18N/allTexts.lua | 30 ++++++++ .../Settings/SettingsPanelApp.razor | 5 +- .../Settings/SettingsPanelApp.razor.cs | 53 +++++++++++++- .../Dialogs/UpdateInstructionsDialog.razor | 19 +++++ .../Dialogs/UpdateInstructionsDialog.razor.cs | 19 +++++ .../Layout/MainLayout.razor.cs | 11 +++ .../Pages/Information.razor | 7 +- .../Pages/Information.razor.cs | 39 +++++++++-- .../Plugins/configuration/plugin.lua | 5 +- .../plugin.lua | 30 ++++++++ .../plugin.lua | 30 ++++++++ app/MindWork AI Studio/Program.cs | 4 ++ .../ConfigurationSelectDataFactory.cs | 9 +++ .../Settings/DataModel/UpdateInterval.cs | 1 + .../Tools/Services/UpdatePolicy.cs | 23 +++++++ .../Tools/Services/UpdatePolicyMode.cs | 8 +++ .../Tools/Services/UpdateService.cs | 69 +++++++++---------- .../wwwroot/changelog/v26.7.3.md | 1 + documentation/Enterprise IT.md | 11 ++- runtime/src/app_window.rs | 28 ++++++-- 20 files changed, 346 insertions(+), 56 deletions(-) create mode 100644 app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor create mode 100644 app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor.cs create mode 100644 app/MindWork AI Studio/Tools/Services/UpdatePolicy.cs create mode 100644 app/MindWork AI Studio/Tools/Services/UpdatePolicyMode.cs diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 3358d2b1..3ff17464 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -3001,6 +3001,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1059411425"] -- Do you want to show preview features in the app? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1118505044"] = "Do you want to show preview features in the app?" +-- AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1190632518"] = "AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app." + -- Voice recording shortcut UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1278320412"] = "Voice recording shortcut" @@ -3043,9 +3046,15 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1890416390"] -- Which preview features would you like to enable? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898060643"] = "Which preview features would you like to enable?" +-- This setting has no effect while updates are disabled by your organization. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898114759"] = "This setting has no effect while updates are disabled by your organization." + -- Select the language for the app. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"] = "Select the language for the app." +-- Your organization has disabled update checks and installations. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1909339369"] = "Your organization has disabled update checks and installations." + -- When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2013281167"] = "When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization." @@ -3064,6 +3073,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] -- Update installation method UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Update installation method" +-- AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T244540698"] = "AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution." + -- Language UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Language" @@ -6286,6 +6298,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T25417398"] = "Update from v{0 -- Install later UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T2936430090"] = "Install later" +-- Close +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T3448155331"] = "Close" + +-- Show me the latest release page +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T59688848"] = "Show me the latest release page" + -- Create new workspace UI_TEXT_CONTENT["AISTUDIO::DIALOGS::WORKSPACESELECTIONDIALOG::T1541251414"] = "Create new workspace" @@ -6661,6 +6679,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1347508205"] = "Copies the confi -- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1388816916"] = "This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat." +-- Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1402243995"] = "Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio." + -- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1421513382"] = "This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library." @@ -6766,6 +6787,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2435772109"] = "Configuration or -- Configuration slot: UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254943559"] = "Configuration slot:" +-- AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254976975"] = "AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub." + -- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557014401"] = "This library is used to determine the language of the operating system. This is necessary to set the language of the user interface." @@ -7018,6 +7042,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T864851737"] = "Axum is used to p -- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose." +-- How to update +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T906183311"] = "How to update" + -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc" @@ -7423,6 +7450,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107 -- Toggle the sidebar: show the workspaces next to the chat when desired UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3711207137"] = "Toggle the sidebar: show the workspaces next to the chat when desired" +-- Updates disabled by your organization +UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4048970098"] = "Updates disabled by your organization" + -- Also show features in alpha: these are in development; expect bugs and missing features UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4146964761"] = "Also show features in alpha: these are in development; expect bugs and missing features" diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor index 7d2b6801..d26a810e 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor @@ -1,6 +1,7 @@ @using AIStudio.Settings @using AIStudio.Settings.DataModel @using AIStudio.Tools.Rust +@using AIStudio.Tools.Services @inherits SettingsPanelBase @@ -15,8 +16,8 @@ - - + + diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs index 04022738..9c6d9d9a 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs @@ -2,11 +2,62 @@ using AIStudio.Provider; using AIStudio.Settings; using AIStudio.Settings.DataModel; using AIStudio.Tools.Rust; +using AIStudio.Tools.Services; + +using Microsoft.AspNetCore.Components; namespace AIStudio.Components.Settings; public partial class SettingsPanelApp : SettingsPanelBase { + [Inject] + private UpdatePolicy UpdatePolicy { get; init; } = null!; + + private UpdatePolicyMode updatePolicyMode; + + private UpdateInterval DisplayedUpdateInterval => this.updatePolicyMode is UpdatePolicyMode.FLATPAK + ? UpdateInterval.NO_CHECK + : this.SettingsManager.ConfigurationData.App.UpdateInterval; + + private UpdateInstallation DisplayedUpdateInstallation => this.updatePolicyMode is UpdatePolicyMode.FLATPAK + ? UpdateInstallation.MANUAL + : this.SettingsManager.ConfigurationData.App.UpdateInstallation; + + private string UpdateIntervalHelp => this.updatePolicyMode switch + { + UpdatePolicyMode.ENTERPRISE_DISABLED => T("Your organization has disabled update checks and installations."), + UpdatePolicyMode.FLATPAK => T("AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app."), + _ => T("How often should we check for app updates?") + }; + + private string UpdateInstallationHelp => this.updatePolicyMode switch + { + UpdatePolicyMode.ENTERPRISE_DISABLED => T("This setting has no effect while updates are disabled by your organization."), + UpdatePolicyMode.FLATPAK => T("AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution."), + _ => T("Should updates be installed automatically or manually?") + }; + + private bool IsUpdateIntervalLocked() => this.updatePolicyMode is UpdatePolicyMode.ENTERPRISE_DISABLED or UpdatePolicyMode.FLATPAK || + ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInterval, out var meta) && meta.IsLocked; + + private bool IsUpdateInstallationLocked() => this.updatePolicyMode is UpdatePolicyMode.ENTERPRISE_DISABLED or UpdatePolicyMode.FLATPAK || + ManagedConfiguration.TryGet(x => x.App, x => x.UpdateInstallation, out var meta) && meta.IsLocked; + + protected override async Task OnInitializedAsync() + { + this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED ]); + await base.OnInitializedAsync(); + this.updatePolicyMode = this.UpdatePolicy.CurrentMode; + } + + protected override async Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default + { + if (triggeredEvent is Event.CONFIGURATION_CHANGED) + this.updatePolicyMode = this.UpdatePolicy.CurrentMode; + + await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data); + } + private ConfigurationShortcutData VoiceRecordingShortcut => new() { Id = Shortcut.VOICE_RECORDING_TOGGLE, @@ -121,4 +172,4 @@ public partial class SettingsPanelApp : SettingsPanelBase this.SettingsManager.ConfigurationData.App.LanguagePluginId = pluginId; await this.MessageBus.SendMessage(this, Event.PLUGINS_RELOADED); } -} \ No newline at end of file +} diff --git a/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor b/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor new file mode 100644 index 00000000..1be3eddd --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor @@ -0,0 +1,19 @@ +@inherits MSGComponentBase + + + + @this.Message + + + + @if (!string.IsNullOrWhiteSpace(this.ReleaseUrl)) + { + + @T("Show me the latest release page") + + } + + @T("Close") + + + \ No newline at end of file diff --git a/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor.cs b/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor.cs new file mode 100644 index 00000000..034cfa31 --- /dev/null +++ b/app/MindWork AI Studio/Dialogs/UpdateInstructionsDialog.razor.cs @@ -0,0 +1,19 @@ +using AIStudio.Components; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Dialogs; + +public partial class UpdateInstructionsDialog : MSGComponentBase +{ + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } = null!; + + [Parameter] + public string Message { get; set; } = string.Empty; + + [Parameter] + public string? ReleaseUrl { get; set; } + + private void Close() => this.MudDialog.Close(); +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index b7f9aae1..2bac1fd8 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -29,6 +29,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan [Inject] private RustService RustService { get; init; } = null!; + [Inject] + private UpdatePolicy UpdatePolicy { get; init; } = null!; + [Inject] private AIJobService AIJobService { get; init; } = null!; @@ -186,6 +189,8 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan switch (triggeredEvent) { case Event.INSTALL_UPDATE: + if (!this.UpdatePolicy.AllowsInstallations) + break; this.performingUpdate = true; this.StateHasChanged(); break; @@ -371,6 +376,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan private async Task ShowUpdateDialog() { + if (!this.UpdatePolicy.AllowsInstallations) + return; + if(this.currentUpdateResponse is null) return; @@ -397,6 +405,9 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan var dialogResult = await dialogReference.Result; if (dialogResult is null || dialogResult.Canceled) return; + + if (!this.UpdatePolicy.AllowsInstallations) + return; this.performingUpdate = true; this.StateHasChanged(); diff --git a/app/MindWork AI Studio/Pages/Information.razor b/app/MindWork AI Studio/Pages/Information.razor index ac3df15e..18863903 100644 --- a/app/MindWork AI Studio/Pages/Information.razor +++ b/app/MindWork AI Studio/Pages/Information.razor @@ -1,5 +1,6 @@ @attribute [Route(Routes.ABOUT)] @using AIStudio.Tools.PluginSystem +@using AIStudio.Tools.Services @inherits MSGComponentBase
@@ -53,7 +54,7 @@ @this.WorkingDirectory - +
@@ -61,7 +62,7 @@ @this.ExecutablePath - + @if (OperatingSystem.IsLinux()) @@ -193,7 +194,7 @@ - @T("Check for updates") + @(this.updatePolicyMode is UpdatePolicyMode.SELF_UPDATE ? T("Check for updates") : T("How to update")) @this.PandocButtonText diff --git a/app/MindWork AI Studio/Pages/Information.razor.cs b/app/MindWork AI Studio/Pages/Information.razor.cs index 21fe274c..d5d1225d 100644 --- a/app/MindWork AI Studio/Pages/Information.razor.cs +++ b/app/MindWork AI Studio/Pages/Information.razor.cs @@ -28,6 +28,12 @@ public partial class Information : MSGComponentBase [Inject] private ISnackbar Snackbar { get; init; } = null!; + + [Inject] + private UpdatePolicy UpdatePolicy { get; init; } = null!; + + [Inject] + private RuntimeInfoResponse RuntimeInfo { get; init; } [Inject] private DatabaseClientProvider DatabaseClientProvider { get; init; } = null!; @@ -42,7 +48,7 @@ public partial class Information : MSGComponentBase private string osLanguage = string.Empty; private string osUserName = string.Empty; - private RuntimeInfoResponse runtimeInfo; + private UpdatePolicyMode updatePolicyMode; private static string VersionApp => $"MindWork AI Studio: v{META_DATA.Version} (commit {META_DATA.AppCommitHash}, build {META_DATA.BuildNum}, {META_DATA_ARCH.Architecture.ToRID().ToUserFriendlyName()})"; @@ -54,13 +60,13 @@ public partial class Information : MSGComponentBase private string OSUserName => $"{T("Username provided by the OS")}: '{this.osUserName}'"; - private string WorkingDirectory => $"{T("Working directory")}: {this.runtimeInfo.WorkingDirectory}"; + private string WorkingDirectory => $"{T("Working directory")}: {this.RuntimeInfo.WorkingDirectory}"; - private string ExecutablePath => $"{T("Executable path")}: {this.runtimeInfo.ExecutablePath}"; + private string ExecutablePath => $"{T("Executable path")}: {this.RuntimeInfo.ExecutablePath}"; private string LinuxPackageType => $"{T("Linux package")}: {this.LinuxPackageTypeDisplayName}"; - private string LinuxPackageTypeDisplayName => this.runtimeInfo.LinuxPackageType switch + private string LinuxPackageTypeDisplayName => this.RuntimeInfo.LinuxPackageType switch { "appimage" => "AppImage", "flatpak" => "Flatpak", @@ -162,7 +168,7 @@ public partial class Information : MSGComponentBase this.osLanguage = await this.RustService.ReadUserLanguage(); this.osUserName = await this.RustService.ReadUserName(); - this.runtimeInfo = await this.RustService.GetRuntimeInfo(); + this.updatePolicyMode = this.UpdatePolicy.CurrentMode; this.logPaths = await this.RustService.GetLogPaths(); await this.RefreshVectorStoreInfo(CancellationToken.None); @@ -185,6 +191,7 @@ public partial class Information : MSGComponentBase case Event.PLUGINS_RELOADED: case Event.ENTERPRISE_ENVIRONMENTS_CHANGED: case Event.CONFIGURATION_CHANGED: + this.updatePolicyMode = this.UpdatePolicy.CurrentMode; this.RefreshEnterpriseConfigurationState(); await this.InvokeAsync(this.StateHasChanged); break; @@ -604,6 +611,26 @@ public partial class Information : MSGComponentBase private async Task CheckForUpdate() { - await this.MessageBus.SendMessage(this, Event.USER_SEARCH_FOR_UPDATE); + this.updatePolicyMode = this.UpdatePolicy.CurrentMode; + if (this.updatePolicyMode is UpdatePolicyMode.SELF_UPDATE) + { + await this.MessageBus.SendMessage(this, Event.USER_SEARCH_FOR_UPDATE); + return; + } + + var parameters = new DialogParameters(); + if (this.updatePolicyMode is UpdatePolicyMode.ENTERPRISE_DISABLED) + { + parameters.Add(x => x.Message, T("Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio.")); + } + else if (this.updatePolicyMode is UpdatePolicyMode.FLATPAK) + { + parameters.Add(x => x.Message, T("AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub.")); + parameters.Add(x => x.ReleaseUrl, "https://github.com/MindWorkAI/AI-Studio/releases/latest"); + } + else + return; + + await this.DialogService.ShowAsync(T("How to update"), parameters, DialogOptions.FULLSCREEN); } } diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua index 23a128d0..30e042af 100644 --- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua +++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua @@ -200,7 +200,10 @@ CONFIG["DATA_SOURCES"] = {} CONFIG["SETTINGS"] = {} -- Configure the update check interval: --- Allowed values are: NO_CHECK, ONCE_STARTUP, HOURLY, DAILY, WEEKLY +-- Allowed values are: NO_CHECK, DISABLE_UPDATES, ONCE_STARTUP, HOURLY, DAILY, WEEKLY +-- NO_CHECK disables automatic checks, but users can still check and install updates manually. +-- DISABLE_UPDATES is intended for enterprise configurations and disables all update checks +-- and installations. It is not offered as a selectable option in the normal app settings. -- CONFIG["SETTINGS"]["DataApp.UpdateInterval"] = "NO_CHECK" -- Configure how updates are installed: diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua index 5af93419..98099068 100644 --- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua @@ -3003,6 +3003,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1059411425"] -- Do you want to show preview features in the app? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1118505044"] = "Möchten Sie Vorschaufunktionen in der App anzeigen lassen?" +-- AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1190632518"] = "AI Studio kann bei Ausführung als Flatpak nicht nach Updates suchen. Updates werden außerhalb der App verwaltet." + -- Voice recording shortcut UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1278320412"] = "Tastaturkurzbefehl für Sprachaufnahme" @@ -3045,9 +3048,15 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1890416390"] -- Which preview features would you like to enable? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898060643"] = "Welche Vorschaufunktionen möchten Sie aktivieren?" +-- This setting has no effect while updates are disabled by your organization. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898114759"] = "Diese Einstellung hat keine Auswirkungen, solange Updates von Ihrer Organisation deaktiviert sind." + -- Select the language for the app. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"] = "Wählen Sie die Sprache für die App aus." +-- Your organization has disabled update checks and installations. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1909339369"] = "Ihre Organisation hat die Suche nach Updates und deren Installation deaktiviert." + -- When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2013281167"] = "Wenn diese Option aktiviert ist, werden zusätzliche Optionen für die Administration angezeigt. Diese Optionen sind für IT-Mitarbeitende vorgesehen, um organisationsweite Einstellungen zu verwalten, z. B. Anbieter für eine gesamte Organisation zu konfigurieren und zu exportieren." @@ -3066,6 +3075,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] -- Update installation method UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Installationsmethode für Updates" +-- AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T244540698"] = "AI Studio kann keine Updates installieren, wenn es als Flatpak ausgeführt wird. Verwenden Sie die von Ihrer Flatpak-Distribution bereitgestellte Methode zur Aktualisierung." + -- Language UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Sprache" @@ -6288,6 +6300,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T25417398"] = "Aktualisieren v -- Install later UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T2936430090"] = "Später installieren" +-- Close +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T3448155331"] = "Schließen" + +-- Show me the latest release page +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T59688848"] = "Zeig mir die Seite mit der neuesten Version" + -- Create new workspace UI_TEXT_CONTENT["AISTUDIO::DIALOGS::WORKSPACESELECTIONDIALOG::T1541251414"] = "Neuen Arbeitsbereich erstellen" @@ -6663,6 +6681,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1347508205"] = "Kopiert den Slot -- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1388816916"] = "Diese Bibliothek wird verwendet, um PDF-Dateien zu lesen. Das ist zum Beispiel notwendig, um PDFs als Datenquelle für einen Chat zu nutzen." +-- Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1402243995"] = "Updates werden von Ihrer Organisation verwaltet. Wenn Sie Fragen zur Aktualisierung von AI Studio haben, wenden Sie sich an Ihre IT-Abteilung." + -- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1421513382"] = "Diese Bibliothek wird verwendet, um die MudBlazor-Bibliothek zu erweitern. Sie stellt zusätzliche Komponenten bereit, die nicht Teil der MudBlazor-Bibliothek sind." @@ -6768,6 +6789,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2435772109"] = "Ursprung der Kon -- Configuration slot: UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254943559"] = "Slot der Konfiguration:" +-- AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254976975"] = "AI Studio kann sich nicht selbst aktualisieren, wenn es als Flatpak installiert ist. Ein Eintrag auf Flathub ist geplant. Bis dahin finden Sie die neueste Version auf GitHub." + -- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557014401"] = "Diese Bibliothek wird verwendet, um die Sprache des Betriebssystems zu erkennen. Dies ist notwendig, um die Sprache der Benutzeroberfläche einzustellen." @@ -7020,6 +7044,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T864851737"] = "Axum wird verwend -- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "Für einige Datenübertragungen müssen wir die Daten in Base64 kodieren. Diese Rust-Bibliothek eignet sich dafür hervorragend." +-- How to update +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T906183311"] = "Update-Anleitung " + -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Pandoc installieren" @@ -7425,6 +7452,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107 -- Toggle the sidebar: show the workspaces on demand next to the chat UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3711207137"] = "Seitenleiste umschalten: Arbeitsbereiche bei Bedarf neben dem Chat anzeigen" +-- Updates disabled by your organization +UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4048970098"] = "Updates wurden von Ihrer Organisation deaktiviert" + -- Also show features in alpha: these are in development; expect bugs and missing features UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4146964761"] = "Zeige auch Funktionen im Alpha-Stadium an: Diese befinden sich in der Entwicklung; es werden Fehler und fehlende Funktionen auftreten." diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua index 39d4bec7..90677d36 100644 --- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua @@ -3003,6 +3003,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1059411425"] -- Do you want to show preview features in the app? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1118505044"] = "Do you want to show preview features in the app?" +-- AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1190632518"] = "AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app." + -- Voice recording shortcut UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1278320412"] = "Voice recording shortcut" @@ -3045,9 +3048,15 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1890416390"] -- Which preview features would you like to enable? UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898060643"] = "Which preview features would you like to enable?" +-- This setting has no effect while updates are disabled by your organization. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1898114759"] = "This setting has no effect while updates are disabled by your organization." + -- Select the language for the app. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1907446663"] = "Select the language for the app." +-- Your organization has disabled update checks and installations. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T1909339369"] = "Your organization has disabled update checks and installations." + -- When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2013281167"] = "When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization." @@ -3066,6 +3075,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2341504363"] -- Update installation method UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T237706157"] = "Update installation method" +-- AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T244540698"] = "AI Studio cannot install updates when running as a Flatpak. Use the update method provided by your Flatpak distribution." + -- Language UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELAPP::T2591284123"] = "Language" @@ -6288,6 +6300,12 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T25417398"] = "Update from v{0 -- Install later UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEDIALOG::T2936430090"] = "Install later" +-- Close +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T3448155331"] = "Close" + +-- Show me the latest release page +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::UPDATEINSTRUCTIONSDIALOG::T59688848"] = "Show me the latest release page" + -- Create new workspace UI_TEXT_CONTENT["AISTUDIO::DIALOGS::WORKSPACESELECTIONDIALOG::T1541251414"] = "Create new workspace" @@ -6663,6 +6681,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1347508205"] = "Copies the confi -- This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1388816916"] = "This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat." +-- Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1402243995"] = "Updates are managed by your organization. Contact your IT department if you have questions about updating AI Studio." + -- This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1421513382"] = "This library is used to extend the MudBlazor library. It provides additional components that are not part of the MudBlazor library." @@ -6768,6 +6789,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2435772109"] = "Configuration or -- Configuration slot: UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254943559"] = "Configuration slot:" +-- AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub. +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T254976975"] = "AI Studio cannot update itself when installed as a Flatpak. A Flathub listing is planned. Until then, you can find the latest release on GitHub." + -- This library is used to determine the language of the operating system. This is necessary to set the language of the user interface. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2557014401"] = "This library is used to determine the language of the operating system. This is necessary to set the language of the user interface." @@ -7020,6 +7044,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T864851737"] = "Axum is used to p -- For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose. UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data transfers, we need to encode the data in base64. This Rust library is great for this purpose." +-- How to update +UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T906183311"] = "How to update" + -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc" @@ -7425,6 +7452,9 @@ UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3612390107 -- Toggle the sidebar: show the workspaces on demand next to the chat UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T3711207137"] = "Toggle the sidebar: show the workspaces on demand next to the chat" +-- Updates disabled by your organization +UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4048970098"] = "Updates disabled by your organization" + -- Also show features in alpha: these are in development; expect bugs and missing features UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CONFIGURATIONSELECTDATAFACTORY::T4146964761"] = "Also show features in alpha: these are in development; expect bugs and missing features" diff --git a/app/MindWork AI Studio/Program.cs b/app/MindWork AI Studio/Program.cs index b37b729b..d5cdaf5c 100644 --- a/app/MindWork AI Studio/Program.cs +++ b/app/MindWork AI Studio/Program.cs @@ -6,6 +6,7 @@ using AIStudio.Tools.AIJobs; using AIStudio.Tools.AssistantSessions; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem.Assistants; +using AIStudio.Tools.Rust; using AIStudio.Tools.Services; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -88,6 +89,7 @@ internal sealed class Program return; } + var runtimeInfo = await rust.GetRuntimeInfo(); var builder = WebApplication.CreateBuilder(); builder.WebHost.ConfigureKestrel(kestrelServerOptions => { @@ -127,6 +129,7 @@ internal sealed class Program builder.Services.AddSingleton(new MudTheme()); builder.Services.AddSingleton(MessageBus.INSTANCE); builder.Services.AddSingleton(rust); + builder.Services.AddSingleton(typeof(RuntimeInfoResponse), runtimeInfo); builder.Services.AddMudMarkdownClipboardService(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -134,6 +137,7 @@ internal sealed class Program builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddTransient(); diff --git a/app/MindWork AI Studio/Settings/ConfigurationSelectDataFactory.cs b/app/MindWork AI Studio/Settings/ConfigurationSelectDataFactory.cs index 7b2704ac..67a0525d 100644 --- a/app/MindWork AI Studio/Settings/ConfigurationSelectDataFactory.cs +++ b/app/MindWork AI Studio/Settings/ConfigurationSelectDataFactory.cs @@ -74,6 +74,15 @@ public static class ConfigurationSelectDataFactory yield return new (TB("Check every week"), UpdateInterval.WEEKLY); } + public static IEnumerable> GetManagedUpdateIntervalData(UpdateInterval currentValue) + { + if (currentValue is UpdateInterval.DISABLE_UPDATES) + yield return new(TB("Updates disabled by your organization"), UpdateInterval.DISABLE_UPDATES); + + foreach (var option in GetUpdateIntervalData()) + yield return option; + } + public static IEnumerable> GetUpdateBehaviourData() { yield return new(TB("Install updates manually"), UpdateInstallation.MANUAL); diff --git a/app/MindWork AI Studio/Settings/DataModel/UpdateInterval.cs b/app/MindWork AI Studio/Settings/DataModel/UpdateInterval.cs index a7a3ec8c..8b6d9db1 100644 --- a/app/MindWork AI Studio/Settings/DataModel/UpdateInterval.cs +++ b/app/MindWork AI Studio/Settings/DataModel/UpdateInterval.cs @@ -3,6 +3,7 @@ namespace AIStudio.Settings.DataModel; public enum UpdateInterval { NO_CHECK, + DISABLE_UPDATES, ONCE_STARTUP, HOURLY, DAILY, diff --git a/app/MindWork AI Studio/Tools/Services/UpdatePolicy.cs b/app/MindWork AI Studio/Tools/Services/UpdatePolicy.cs new file mode 100644 index 00000000..265b63ec --- /dev/null +++ b/app/MindWork AI Studio/Tools/Services/UpdatePolicy.cs @@ -0,0 +1,23 @@ +using AIStudio.Settings; +using AIStudio.Settings.DataModel; +using AIStudio.Tools.Rust; + +namespace AIStudio.Tools.Services; + +public sealed class UpdatePolicy(SettingsManager settingsManager, RuntimeInfoResponse runtimeInfo) +{ + public UpdatePolicyMode CurrentMode => settingsManager.ConfigurationData.App.UpdateInterval is UpdateInterval.DISABLE_UPDATES + ? UpdatePolicyMode.ENTERPRISE_DISABLED + : runtimeInfo.LinuxPackageType switch + { + "flatpak" => UpdatePolicyMode.FLATPAK, + _ => UpdatePolicyMode.SELF_UPDATE + }; + + public bool AllowsManualChecks => this.CurrentMode is UpdatePolicyMode.SELF_UPDATE; + + public bool AllowsAutomaticChecks => this.CurrentMode is UpdatePolicyMode.SELF_UPDATE && + settingsManager.ConfigurationData.App.UpdateInterval is not UpdateInterval.NO_CHECK; + + public bool AllowsInstallations => this.CurrentMode is UpdatePolicyMode.SELF_UPDATE; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Services/UpdatePolicyMode.cs b/app/MindWork AI Studio/Tools/Services/UpdatePolicyMode.cs new file mode 100644 index 00000000..021e1a6e --- /dev/null +++ b/app/MindWork AI Studio/Tools/Services/UpdatePolicyMode.cs @@ -0,0 +1,8 @@ +namespace AIStudio.Tools.Services; + +public enum UpdatePolicyMode +{ + SELF_UPDATE, + FLATPAK, + ENTERPRISE_DISABLED +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Services/UpdateService.cs b/app/MindWork AI Studio/Tools/Services/UpdateService.cs index 4a873242..b7dd124b 100644 --- a/app/MindWork AI Studio/Tools/Services/UpdateService.cs +++ b/app/MindWork AI Studio/Tools/Services/UpdateService.cs @@ -16,15 +16,15 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver private readonly SettingsManager settingsManager; private readonly MessageBus messageBus; private readonly RustService rust; + private readonly UpdatePolicy updatePolicy; private readonly ILogger logger; - private TimeSpan updateInterval; - - public UpdateService(MessageBus messageBus, SettingsManager settingsManager, RustService rust, ILogger logger) + public UpdateService(MessageBus messageBus, SettingsManager settingsManager, RustService rust, UpdatePolicy updatePolicy, ILogger logger) { this.settingsManager = settingsManager; this.messageBus = messageBus; this.rust = rust; + this.updatePolicy = updatePolicy; this.logger = logger; this.messageBus.RegisterComponent(this); @@ -41,42 +41,21 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver while (!stoppingToken.IsCancellationRequested && !IS_INITIALIZED) await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken); - // - // Set the update interval based on the user's settings. - // - this.updateInterval = this.settingsManager.ConfigurationData.App.UpdateInterval switch - { - UpdateInterval.NO_CHECK => Timeout.InfiniteTimeSpan, - UpdateInterval.ONCE_STARTUP => Timeout.InfiniteTimeSpan, - - UpdateInterval.HOURLY => TimeSpan.FromHours(1), - UpdateInterval.DAILY => TimeSpan.FromDays(1), - UpdateInterval.WEEKLY => TimeSpan.FromDays(7), - - _ => TimeSpan.FromHours(1) - }; - - // - // When the user doesn't want to check for updates, we can - // return early. - // - if(this.settingsManager.ConfigurationData.App.UpdateInterval is UpdateInterval.NO_CHECK) - return; - - // - // Check for updates at the beginning. The user aspects this when the app - // is started. - // - await this.CheckForUpdate(); - - // - // Start the update loop. This will check for updates based on the - // user's settings. - // + DateTimeOffset? lastAutomaticCheck = null; while (!stoppingToken.IsCancellationRequested) { - await Task.Delay(this.updateInterval, stoppingToken); - await this.CheckForUpdate(); + var interval = this.GetCurrentUpdateInterval(); + if (this.updatePolicy.AllowsAutomaticChecks && + ( + lastAutomaticCheck is null || + interval != Timeout.InfiniteTimeSpan && DateTimeOffset.UtcNow - lastAutomaticCheck >= interval) + ) + { + await this.CheckForUpdate(); + lastAutomaticCheck = DateTimeOffset.UtcNow; + } + + await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } } @@ -97,7 +76,8 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver switch (triggeredEvent) { case Event.USER_SEARCH_FOR_UPDATE: - await this.CheckForUpdate(notifyUserWhenNoUpdate: true); + if (this.updatePolicy.AllowsManualChecks) + await this.CheckForUpdate(notifyUserWhenNoUpdate: true); break; } } @@ -143,6 +123,9 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver if (!isDevEnvironment && this.settingsManager.ConfigurationData.App.UpdateInstallation is UpdateInstallation.AUTOMATIC) { + if (!this.updatePolicy.AllowsInstallations) + return; + try { await this.messageBus.SendMessage(null, Event.INSTALL_UPDATE); @@ -174,6 +157,16 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver } } } + + private TimeSpan GetCurrentUpdateInterval() => this.settingsManager.ConfigurationData.App.UpdateInterval switch + { + UpdateInterval.ONCE_STARTUP => Timeout.InfiniteTimeSpan, + UpdateInterval.HOURLY => TimeSpan.FromHours(1), + UpdateInterval.DAILY => TimeSpan.FromDays(1), + UpdateInterval.WEEKLY => TimeSpan.FromDays(7), + + _ => Timeout.InfiniteTimeSpan + }; public static void SetBlazorDependencies(ISnackbar snackbar) { diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.7.3.md b/app/MindWork AI Studio/wwwroot/changelog/v26.7.3.md index 24e7b9e3..3a85b9d5 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.7.3.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.7.3.md @@ -1,5 +1,6 @@ # v26.7.3, build 245 (2026-07-xx xx:xx UTC) - Improved the "My Tasks Assistant": you can now provide one or more documents in addition to text or use documents alone when asking to identify tasks. +- Improved update guidance for Flatpak installations and added an enterprise option that lets organizations manage updates entirely through their IT department. - Fixed an issue that could leave AI Studio unresponsive after waking the computer from sleep. - Fixed enterprise configuration plugins from Windows-created ZIP files not loading correctly on Linux when the ZIP contained plugin files inside a folder. - Upgraded Rust to v1.97.0. diff --git a/documentation/Enterprise IT.md b/documentation/Enterprise IT.md index 889f397e..b8035acf 100644 --- a/documentation/Enterprise IT.md +++ b/documentation/Enterprise IT.md @@ -4,7 +4,7 @@ Do you want to manage MindWork AI Studio in a corporate environment or within an organization? This documentation explains what you need to do and how it works. First, here's an overview of the entire process: - You can distribute MindWork AI Studio to employees' devices using tools like Microsoft System Center Configuration Manager (SCCM). -- Employees can get updates through the built-in update feature. If you want, you can disable automatic updates and control which version gets distributed. +- Employees can get updates through the built-in update feature. Enterprise configuration can disable automatic checks or the entire built-in update feature so that the IT department controls which version gets distributed. - AI Studio checks about every 16 minutes to see where and which configuration it should load. This information is loaded from the local system. On Windows, you might use the registry, for example. - If it finds the necessary metadata, AI Studio downloads the configuration as a ZIP file from the specified server. - The configuration is an AI Studio plugin written in Lua. @@ -12,6 +12,15 @@ Do you want to manage MindWork AI Studio in a corporate environment or within an AI Studio checks about every 16 minutes to see if the configuration ID, the server for the configuration, or the configuration itself has changed. If it finds any changes, it loads the updated configuration from the server and applies it right away. +### Manage app updates + +Set `CONFIG["SETTINGS"]["DataApp.UpdateInterval"]` in the configuration plugin to control update checks: + +- `NO_CHECK` disables automatic update checks. Users can still check for and install updates manually. +- `DISABLE_UPDATES` disables automatic and manual update checks and installations. AI Studio tells users that updates are managed by their organization and directs questions to their IT department. This policy takes effect immediately when the enterprise configuration changes. + +Use `DISABLE_UPDATES` when your organization distributes approved versions through its own software-management process. + ## Configure the devices So that MindWork AI Studio knows where to load which configuration, this information must be provided as metadata on employees' devices. Currently, the following options are available: diff --git a/runtime/src/app_window.rs b/runtime/src/app_window.rs index 2413e0fe..7db9925e 100644 --- a/runtime/src/app_window.rs +++ b/runtime/src/app_window.rs @@ -451,8 +451,9 @@ pub async fn change_location_to(url: &str) { /// Checks for updates. pub async fn check_for_update(_token: APIToken) -> Json { - if is_dev() { - warn!(Source = "Updater"; "The app is running in development mode; skipping update check."); + if !self_update_allowed(is_dev(), is_flatpak()) { + let reason = if is_flatpak() { "Flatpak installations are updated externally" } else { "the app is running in development mode" }; + warn!(Source = "Updater"; "Skipping update check because {reason}."); return Json(CheckUpdateResponse { update_is_available: false, error: false, @@ -536,8 +537,9 @@ pub struct CheckUpdateResponse { /// Installs the update. pub async fn install_update(_token: APIToken) { - if is_dev() { - warn!(Source = "Updater"; "The app is running in development mode; skipping update installation."); + if !self_update_allowed(is_dev(), is_flatpak()) { + let reason = if is_flatpak() { "Flatpak installations are updated externally" } else { "the app is running in development mode" }; + warn!(Source = "Updater"; "Skipping update installation because {reason}."); return; } @@ -595,6 +597,10 @@ pub async fn install_update(_token: APIToken) { } } +fn self_update_allowed(development: bool, flatpak: bool) -> bool { + !development && !flatpak +} + /// Request payload for registering a global shortcut. #[derive(Clone, Deserialize)] pub struct RegisterShortcutRequest { @@ -1027,6 +1033,20 @@ mod tests { use super::*; use std::fs; + #[test] + fn self_update_is_disabled_in_development() { + assert!(!self_update_allowed(true, false)); + } + + #[test] + fn self_update_is_disabled_for_flatpak() { + assert!(!self_update_allowed(false, true)); + } + + #[test] + fn self_update_is_enabled_for_normal_production_installations() { + assert!(self_update_allowed(false, false)); + } #[test] fn pdfium_library_directory_prefers_resources_libraries() { let temp_dir = tempfile::tempdir().unwrap();