Stufe 1 (#764): Agent bearbeiten, Lizenz und Einstellungen in der WebUI
- PUT /api/agents/{id} und /texts: Stammdaten, Grenzen, Budget, Identity/Soul/
Systemprompt (L6/L7); "Jetzt ausfuehren" in der Agentenansicht (L9)
- Lizenzschluessel ueber die Weboberflaeche (L1): erst vom Lizenzserver
bestaetigt, dann gespeichert; hebt die Sperre ohne Neustart auf
- GET/PUT /api/settings: Instanz- und Anwendungseinstellungen inkl.
OpenRouter-Key (L3/L4/L5), Geheimnisse nur schreibend
- GET/PUT /api/verwaltung/settings: Deploymentcenter und Update-Kanal des Managers
- Einstellungsseite zeigt echte Werte statt fester Texte
- NonInteractiveLicensePrompt verwies auf --license-set-key, den es nie gab
- Sprachpakete werden bei jedem Lauf geprueft (LanguagePackTests)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a9ce72abe2
commit
8552755bb6
@@ -118,6 +118,9 @@ public sealed class ManagerCore : IAsyncDisposable
|
||||
|
||||
public ManagerSettings Settings => _settings.Settings;
|
||||
|
||||
/// <summary>Where <c>manager.json</c> lives — shown in the web interface.</summary>
|
||||
public string SettingsPath => _settings.SettingsPath;
|
||||
|
||||
// ─── Hochfahren ───
|
||||
|
||||
/// <summary>
|
||||
@@ -313,6 +316,73 @@ public sealed class ManagerCore : IAsyncDisposable
|
||||
/// betreiben: Der Weg vom Widerruf zum Anhalten ist die Zusage, nicht die Frage, wer
|
||||
/// ihn ausgelöst hat.</para>
|
||||
/// </summary>
|
||||
/// <summary>Whether a license check is configured at all — without one nothing is locked.</summary>
|
||||
public bool LicenseEnforced => _license is not null;
|
||||
|
||||
/// <summary>The hardware id the license server sees, for support; <c>null</c> without a license check.</summary>
|
||||
public string? LicenseHardwareId => _license?.HardwareId;
|
||||
|
||||
/// <summary>
|
||||
/// #764: A license key from the web interface. The first install of a service has no other
|
||||
/// way to get one: the manager never asks (<c>NonInteractiveLicensePrompt</c>).
|
||||
///
|
||||
/// <para><b>A confirmed key lifts the lock at once</b> — the watch starts and the autostart
|
||||
/// instances come up, as they would have at startup. Without that, a freshly licensed
|
||||
/// manager would still refuse every start until someone restarted the service.</para>
|
||||
///
|
||||
/// <para>The autostart runs without the request's token: whoever closes the browser right
|
||||
/// after saving should not leave half the instances started.</para>
|
||||
/// </summary>
|
||||
public async Task<LicenseApplyResult> ApplyLicenseKeyAsync(string key, CancellationToken ct = default)
|
||||
{
|
||||
if (_license is null)
|
||||
return new LicenseApplyResult(false, false, "This instance manager has no license check configured.");
|
||||
|
||||
var result = await _license.ApplyKeyAsync(key, ct).ConfigureAwait(false);
|
||||
if (!result.Applied)
|
||||
return result;
|
||||
|
||||
var wasLocked = !LicenseHolds;
|
||||
LicenseHolds = true;
|
||||
_watch?.Start();
|
||||
|
||||
if (wasLocked)
|
||||
{
|
||||
_logger.LogInformation("License confirmed - instances may start again.");
|
||||
await StartAutostartInstancesAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #764: Changes the manager's own settings in <c>manager.json</c> that the web interface
|
||||
/// offers. <c>null</c> keeps a value; an empty master token keeps the one there is — it is
|
||||
/// never sent out, so a form cannot send it back.
|
||||
///
|
||||
/// <para>Everything here is read once, when the manager starts (license server, token
|
||||
/// source, update check). The answer says so instead of pretending the change is live.</para>
|
||||
///
|
||||
/// <para>Port, binding and the access token of the web interface are deliberately not
|
||||
/// offered: a wrong value there locks out the very page it was set from.</para>
|
||||
/// </summary>
|
||||
public (bool Ok, string Message) UpdateSettings(string? deploymentcenterUrl, string? masterToken, string? updateChannel)
|
||||
{
|
||||
if (deploymentcenterUrl is { } url && url.Trim().Length > 0
|
||||
&& !(Uri.TryCreate(url.Trim(), UriKind.Absolute, out var uri) && (uri.Scheme == Uri.UriSchemeHttps || uri.Scheme == Uri.UriSchemeHttp)))
|
||||
return (false, "The Deploymentcenter address must be an http(s) address.");
|
||||
if (updateChannel is { } channel && channel.Trim() is not ("prod" or "beta" or "dev"))
|
||||
return (false, "The update channel is one of prod, beta, dev.");
|
||||
|
||||
var settings = _settings.Settings;
|
||||
if (deploymentcenterUrl is not null) settings.DeploymentcenterUrl = deploymentcenterUrl.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(masterToken)) settings.DeploymentcenterMasterToken = masterToken.Trim();
|
||||
if (updateChannel is not null) settings.UpdateChannel = updateChannel.Trim();
|
||||
_settings.Save();
|
||||
|
||||
return (true, "Saved. Takes effect when the instance manager restarts.");
|
||||
}
|
||||
|
||||
public async Task HandleLicenseRevokedAsync(string grund)
|
||||
{
|
||||
LicenseHolds = false;
|
||||
|
||||
Reference in New Issue
Block a user