Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch
Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft. Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch. WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b umbenannten Parameter angeglichen. Dabei behoben: - 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue" gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day. - Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine CSS-Klasse gab - jetzt "success" wie laufende Instanzen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
da4ceac7d5
commit
837a634a22
@@ -1,9 +1,9 @@
|
||||
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
|
||||
|
||||
/// <summary>
|
||||
/// Die Statuswerte, die der Watchdog kennt. <c>stopped</c> und <c>maintenance</c> sind
|
||||
/// angekündigte Zustände — der Evaluator lässt solche Monitore in Ruhe, statt wenige
|
||||
/// Minuten nach einem geplanten Herunterfahren einen Fehlalarm zu erzeugen.
|
||||
/// The status values the watchdog knows. <c>stopped</c> and <c>maintenance</c> are
|
||||
/// announced states — the evaluator leaves such monitors alone instead of producing a
|
||||
/// false alarm a few minutes after a planned shutdown.
|
||||
/// </summary>
|
||||
public static class WatchdogStatus
|
||||
{
|
||||
@@ -15,26 +15,26 @@ public static class WatchdogStatus
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eine selbst ermittelte Teilprüfung. Das Deploymentcenter interpretiert den Namen
|
||||
/// nicht — es liest nur <see cref="Ok"/> und <see cref="Message"/>. Was „gesund"
|
||||
/// bedeutet, entscheidet damit jede Anwendung selbst.
|
||||
/// A self-determined partial check. The Deploymentcenter does not interpret the name
|
||||
/// — it only reads <see cref="Ok"/> and <see cref="Message"/>. What "healthy"
|
||||
/// means is thus decided by each application itself.
|
||||
///
|
||||
/// <para>Schlägt eine Prüfung fehl, stuft der Server einen als <c>ok</c> gemeldeten
|
||||
/// Heartbeat auf <c>warning</c> herab. Das ist der Unterschied zwischen „ein Faden
|
||||
/// läuft" und „die Anwendung tut, was sie soll".</para>
|
||||
/// <para>If a check fails, the server downgrades a heartbeat reported as <c>ok</c>
|
||||
/// to <c>warning</c>. That is the difference between "a thread
|
||||
/// is running" and "the application does what it should".</para>
|
||||
/// </summary>
|
||||
public sealed record HealthCheck(bool Ok, string? Message = null);
|
||||
|
||||
/// <summary>Momentaufnahme des Instanz-Zustands für einen Heartbeat.</summary>
|
||||
/// <param name="Status">Einer der Werte aus <see cref="WatchdogStatus"/>.</param>
|
||||
/// <param name="Message">Kurzbegründung, erscheint im Dashboard.</param>
|
||||
/// <summary>Snapshot of the instance state for a heartbeat.</summary>
|
||||
/// <param name="Status">One of the values from <see cref="WatchdogStatus"/>.</param>
|
||||
/// <param name="Message">Short reason, appears in the dashboard.</param>
|
||||
/// <param name="Metrics">
|
||||
/// Nur Zahlen: Das Deploymentcenter legt sie mit Zeitstempel ab (14 Tage) und vergleicht
|
||||
/// den aktuellen Wert mit dem Sieben-Tage-Schnitt desselben Monitors. Nicht-numerische
|
||||
/// Werte würden dabei stillschweigend verworfen — beschreibende Angaben gehören
|
||||
/// deshalb in <paramref name="Message"/> oder in die Checks.
|
||||
/// Numbers only: the Deploymentcenter stores them with a timestamp (14 days) and compares
|
||||
/// the current value with the seven-day average of the same monitor. Non-numeric
|
||||
/// values would be silently discarded — descriptive details therefore belong
|
||||
/// in <paramref name="Message"/> or in the checks.
|
||||
/// </param>
|
||||
/// <param name="Checks">Selbst ermittelter Gesundheitszustand je Teilbereich.</param>
|
||||
/// <param name="Checks">Self-determined health state per area.</param>
|
||||
public sealed record InstanceHealth(
|
||||
string Status,
|
||||
string? Message,
|
||||
@@ -47,7 +47,7 @@ public sealed record InstanceHealth(
|
||||
new Dictionary<string, HealthCheck>());
|
||||
}
|
||||
|
||||
/// <summary>Liefert vor jedem Heartbeat den aktuellen Instanz-Zustand.</summary>
|
||||
/// <summary>Delivers the current instance state before every heartbeat.</summary>
|
||||
public interface IInstanceHealthProvider
|
||||
{
|
||||
Task<InstanceHealth> GetAsync(CancellationToken ct);
|
||||
|
||||
@@ -4,22 +4,22 @@ using ClawdDotNet.Core.Config;
|
||||
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
|
||||
|
||||
/// <summary>
|
||||
/// Leitet den Instanz-Zustand für den Heartbeat ab.
|
||||
/// Derives the instance state for the heartbeat.
|
||||
///
|
||||
/// <para>Ein Heartbeat allein beweist nur, dass ein Faden läuft. Deshalb geht der
|
||||
/// selbst ermittelte Gesundheitszustand als <c>checks</c> mit — der Server stuft einen
|
||||
/// als <c>ok</c> gemeldeten Beat herab, sobald eine Prüfung fehlschlägt, und nennt in
|
||||
/// der Antwort die betroffene. Der klassische Fall, den das abfängt: Der Takt meldet
|
||||
/// brav <c>ok</c>, während der Aufgaben-Scanner seit einer Stunde tot ist.</para>
|
||||
/// <para>A heartbeat alone only proves that a thread is running. That's why the
|
||||
/// self-determined health state goes along as <c>checks</c> — the server downgrades a
|
||||
/// beat reported as <c>ok</c> as soon as a check fails, and names the affected one in
|
||||
/// the response. The classic case this catches: the timer dutifully reports
|
||||
/// <c>ok</c> while the task scanner has been dead for an hour.</para>
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item><c>error</c> — kein OpenRouter-Key konfiguriert (Agenten deaktiviert).</item>
|
||||
/// <item><c>warning</c> — Tagesbudget der Instanz erschöpft oder Scanner steht.</item>
|
||||
/// <item><c>ok</c> — sonst.</item>
|
||||
/// <item><c>error</c> — no OpenRouter key configured (agents disabled).</item>
|
||||
/// <item><c>warning</c> — daily budget of the instance exhausted or scanner stalled.</item>
|
||||
/// <item><c>ok</c> — otherwise.</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para>Die Metriken sind bewusst schlank und ausschließlich numerisch: keine
|
||||
/// sensiblen Nutzdaten, und nur Zahlen landen im Verlauf.</para>
|
||||
/// <para>The metrics are deliberately lean and exclusively numeric: no
|
||||
/// sensitive payload data, and only numbers end up in the history.</para>
|
||||
/// </summary>
|
||||
public sealed class InstanceHealthProvider : IInstanceHealthProvider
|
||||
{
|
||||
@@ -97,8 +97,8 @@ public sealed class InstanceHealthProvider : IInstanceHealthProvider
|
||||
checks["budget"] = new(budgetProblem is null, budgetProblem);
|
||||
}
|
||||
|
||||
// Der Instanzname steht in der Meldung, nicht in den Metriken: Metriken sind
|
||||
// Zahlen, alles andere würde der Server beim Verdichten ohnehin verwerfen.
|
||||
// The instance name is in the message, not in the metrics: metrics are
|
||||
// numbers, the server would discard everything else when condensing anyway.
|
||||
if (!_agentsEnabled)
|
||||
{
|
||||
return new InstanceHealth(
|
||||
@@ -121,7 +121,7 @@ public sealed class InstanceHealthProvider : IInstanceHealthProvider
|
||||
return new InstanceHealth(WatchdogStatus.Ok, $"{_instanceName}: Betrieb normal.", metrics, checks);
|
||||
}
|
||||
|
||||
/// <summary>0 oder kleiner bedeutet: keine Grenze gesetzt. Deckungsgleich mit BudgetGuard.</summary>
|
||||
/// <summary>0 or less means: no limit set. Consistent with BudgetGuard.</summary>
|
||||
private static bool Exceeds(decimal limit, decimal used) => limit > 0 && used >= limit;
|
||||
|
||||
private static bool Exceeds(long limit, long used) => limit > 0 && used >= limit;
|
||||
|
||||
@@ -2,13 +2,13 @@ using System.Text.Json;
|
||||
|
||||
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
|
||||
|
||||
/// <summary>Was der Server zu einem Heartbeat zurückmeldet.</summary>
|
||||
/// <param name="State">Der daraus abgeleitete Monitor-Zustand (<c>up</c>, <c>warning</c>, …).</param>
|
||||
/// <param name="FailingChecks">Welche der mitgeschickten Prüfungen fehlgeschlagen sind.</param>
|
||||
/// <summary>What the server reports back for a heartbeat.</summary>
|
||||
/// <param name="State">The monitor state derived from it (<c>up</c>, <c>warning</c>, …).</param>
|
||||
/// <param name="FailingChecks">Which of the checks sent along failed.</param>
|
||||
public sealed record WatchdogPingResult(string State, IReadOnlyList<string> FailingChecks);
|
||||
|
||||
/// <summary>
|
||||
/// Sendet Heartbeats und Ereignisse an das Watchdog-Modul des Deploymentcenters.
|
||||
/// Sends heartbeats and events to the Deploymentcenter's watchdog module.
|
||||
/// </summary>
|
||||
public interface IWatchdogClient
|
||||
{
|
||||
@@ -20,19 +20,19 @@ public interface IWatchdogClient
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Watchdog-Anbindung: <c>POST /api/watchdog/v1/ping</c> und
|
||||
/// Watchdog connection: <c>POST /api/watchdog/v1/ping</c> and
|
||||
/// <c>POST /api/watchdog/v1/event</c>.
|
||||
///
|
||||
/// <para><b>Ein Monitor je Instanz.</b> Der Schlüssel des Monitors ist das Paar
|
||||
/// <c>source</c> + <c>instance</c> (so das Datenbankschema:
|
||||
/// <c>UNIQUE KEY uq_monitor (source, instance)</c>). Alle ClawdDotNet-Instanzen melden
|
||||
/// unter derselben <c>source</c> und tragen ihre eigene <c>instance</c> — damit ist jede
|
||||
/// laufende Instanz ein eigener Monitor mit eigenem Zustand, eigenem Intervall und
|
||||
/// eigenem Metrik-Verlauf. Stürzt eine von dreien ab, fällt genau deren Monitor.</para>
|
||||
/// <para><b>One monitor per instance.</b> The key of the monitor is the pair
|
||||
/// <c>source</c> + <c>instance</c> (per the database schema:
|
||||
/// <c>UNIQUE KEY uq_monitor (source, instance)</c>). All ClawdDotNet instances report
|
||||
/// under the same <c>source</c> and carry their own <c>instance</c> — so every
|
||||
/// running instance is its own monitor with its own state, its own interval and
|
||||
/// its own metrics history. If one of three crashes, exactly its monitor fails.</para>
|
||||
///
|
||||
/// <para>Der Monitor entsteht beim ersten Heartbeat von selbst (<c>INSERT … ON DUPLICATE
|
||||
/// KEY UPDATE</c>) — eine Registrierung vorab gibt es nicht mehr und ist auch nicht
|
||||
/// nötig.</para>
|
||||
/// <para>The monitor comes into being on the first heartbeat by itself (<c>INSERT … ON DUPLICATE
|
||||
/// KEY UPDATE</c>) — there is no prior registration any more, and none is
|
||||
/// needed.</para>
|
||||
/// </summary>
|
||||
public sealed class WatchdogClient : IWatchdogClient, IDisposable
|
||||
{
|
||||
@@ -77,15 +77,15 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
|
||||
["group"] = _group,
|
||||
["os"] = _os,
|
||||
|
||||
// Landet in watchdog_monitors.app_version. Damit steht im Dashboard, welche
|
||||
// Fassung eine Instanz gerade fährt — bei mehreren Instanzen der
|
||||
// Unterschied zwischen „läuft" und „läuft noch auf der alten Version".
|
||||
// Ends up in watchdog_monitors.app_version. That way the dashboard shows which
|
||||
// version an instance is currently running — with several instances the
|
||||
// difference between "runs" and "still runs on the old version".
|
||||
["version"] = _version
|
||||
};
|
||||
|
||||
// Leere Objekte weglassen: Der Server übernimmt health_json nur, wenn etwas
|
||||
// mitkommt — ein leeres würde den letzten bekannten Zustand nicht ersetzen,
|
||||
// aber unnötig Platz im Protokoll kosten.
|
||||
// Leave out empty objects: the server only takes over health_json if something
|
||||
// comes along — an empty one would not replace the last known state,
|
||||
// but would needlessly cost space in the log.
|
||||
if (health.Checks.Count > 0)
|
||||
{
|
||||
payload["checks"] = health.Checks.ToDictionary(
|
||||
@@ -127,9 +127,9 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ein einmaliges Vorkommnis statt einer zyklischen Meldung. Zulässige
|
||||
/// <paramref name="kind"/>-Werte siehe <see cref="WatchdogEventKind"/> — der Server
|
||||
/// weist andere ab.
|
||||
/// A one-time occurrence instead of a cyclic report. Permitted
|
||||
/// <paramref name="kind"/> values see <see cref="WatchdogEventKind"/> — the server
|
||||
/// rejects others.
|
||||
/// </summary>
|
||||
public async Task SendEventAsync(
|
||||
string kind, string severity, string? message, object? meta, CancellationToken ct)
|
||||
@@ -155,9 +155,9 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Die vom Server akzeptierten Ereignisarten. Die frühere Anbindung schickte
|
||||
/// <c>start</c> und <c>stop</c> — beide stehen nicht auf dieser Liste und wurden
|
||||
/// stillschweigend als <c>started</c> abgelegt.
|
||||
/// The event kinds accepted by the server. The former connection sent
|
||||
/// <c>start</c> and <c>stop</c> — neither is on this list, and they were
|
||||
/// silently stored as <c>started</c>.
|
||||
/// </summary>
|
||||
public static class WatchdogEventKind
|
||||
{
|
||||
|
||||
@@ -3,15 +3,15 @@ using Microsoft.Extensions.Logging;
|
||||
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
|
||||
|
||||
/// <summary>
|
||||
/// Sendet im festen Takt Heartbeats an das Watchdog-Modul und meldet Start und Ende.
|
||||
/// Ein nicht erreichbares Deploymentcenter darf ClawdDotNet nie beeinträchtigen —
|
||||
/// alle Sendefehler werden geloggt und verschluckt.
|
||||
/// Sends heartbeats to the watchdog module at a fixed interval and reports start and end.
|
||||
/// An unreachable Deploymentcenter must never impair ClawdDotNet —
|
||||
/// all send errors are logged and swallowed.
|
||||
///
|
||||
/// <para><b>Sauberes Beenden.</b> Beim Herunterfahren geht ein Heartbeat mit
|
||||
/// <c>status: "stopped"</c> raus. Der Evaluator lässt einen so gemeldeten Monitor in
|
||||
/// Ruhe; ohne das erzeugte jedes geplante Beenden wenige Minuten später einen
|
||||
/// Fehlalarm. Das reine Ereignis genügt dafür nicht — der Evaluator sieht nur den
|
||||
/// Monitor-Zustand.</para>
|
||||
/// <para><b>Clean shutdown.</b> On shutdown a heartbeat with
|
||||
/// <c>status: "stopped"</c> goes out. The evaluator leaves a monitor reported this way
|
||||
/// alone; without it every planned shutdown would produce a false alarm a few minutes
|
||||
/// later. The plain event is not enough for that — the evaluator only sees the
|
||||
/// monitor state.</para>
|
||||
/// </summary>
|
||||
public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
{
|
||||
@@ -44,8 +44,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Baut Client und Dienst in einem Zug. Wirft nur bei grob falscher Konfiguration
|
||||
/// (fehlende oder nicht-HTTPS-URL).
|
||||
/// Builds client and service in one go. Throws only on grossly wrong configuration
|
||||
/// (missing or non-HTTPS URL).
|
||||
/// </summary>
|
||||
public static WatchdogHeartbeatService Create(
|
||||
string baseUrl, string token, string source, string instance, string group, string os,
|
||||
@@ -58,18 +58,18 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
|
||||
public bool IsRunning => _loop is { IsCompleted: false };
|
||||
|
||||
/// <summary>Der zuletzt vom Server gemeldete Monitor-Zustand — für die Anzeige.</summary>
|
||||
/// <summary>The monitor state last reported by the server — for display.</summary>
|
||||
public string LastState => _lastState;
|
||||
|
||||
/// <summary>
|
||||
/// Meldet das nächste Herunterfahren als <b>Wartung</b> statt als planmäßiges Ende.
|
||||
/// Reports the next shutdown as <b>maintenance</b> instead of a scheduled end.
|
||||
///
|
||||
/// <para>Gedacht für das Einspielen eines Updates: Die Instanz ist gleich weg,
|
||||
/// kommt aber wieder. <c>stopped</c> wäre die falsche Auskunft — es heißt „bewusst
|
||||
/// beendet" und lässt den Monitor liegen, bis jemand ihn wieder anfasst.
|
||||
/// <c>maintenance</c> sagt dasselbe über den Alarm aus, trägt aber die Absicht
|
||||
/// mit: Im Dashboard ist zu sehen, dass hier gerade aktualisiert wird, statt dass
|
||||
/// eine Instanz ohne Grund verschwindet.</para>
|
||||
/// <para>Meant for applying an update: the instance is about to go away,
|
||||
/// but will come back. <c>stopped</c> would be the wrong information — it means "deliberately
|
||||
/// ended" and leaves the monitor alone until someone touches it again.
|
||||
/// <c>maintenance</c> says the same about the alarm, but carries the intent
|
||||
/// along: the dashboard shows that an update is in progress here, instead of
|
||||
/// an instance disappearing for no reason.</para>
|
||||
/// </summary>
|
||||
public void AnnounceMaintenance(string message)
|
||||
{
|
||||
@@ -95,8 +95,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
WatchdogEventKind.Started, "info", "Instanz gestartet.", null, ct),
|
||||
"Start-Ereignis").ConfigureAwait(false);
|
||||
|
||||
// Erster Beat sofort, damit ein neuer Monitor nicht erst nach einem vollen
|
||||
// Intervall im Dashboard auftaucht.
|
||||
// First beat immediately, so a new monitor does not appear in the dashboard only after a full
|
||||
// interval.
|
||||
await BeatAsync(ct).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
@@ -107,7 +107,7 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Regulärer Stopp.
|
||||
// Regular stop.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +124,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Selbst wenn die Zustandsermittlung scheitert, soll ein Lebenszeichen
|
||||
// rausgehen — sonst sieht ein Fehler in unserem Code aus wie ein Ausfall.
|
||||
// Even if determining the state fails, a sign of life should
|
||||
// go out — otherwise an error in our code looks like an outage.
|
||||
_logger.LogWarning(ex, "Watchdog: Zustandsermittlung fehlgeschlagen – melde warning.");
|
||||
health = new InstanceHealth(
|
||||
WatchdogStatus.Warning, "Zustand konnte nicht ermittelt werden.",
|
||||
@@ -162,15 +162,15 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
}
|
||||
catch (DeploymentcenterException ex) when (ex.IsAuthorizationProblem)
|
||||
{
|
||||
// Ein abgelehntes Token ist kein Rauschen: Ohne Eingriff bleibt der Monitor
|
||||
// für immer stumm, und niemand merkt es, weil ja nichts abstürzt.
|
||||
// A rejected token is not noise: without intervention the monitor stays
|
||||
// silent forever, and nobody notices because nothing crashes.
|
||||
_logger.LogWarning(
|
||||
"Watchdog: {What} abgelehnt ({Code}) – Token prüfen (Recht watchdog:ping).",
|
||||
what, ex.Code);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Ausfall des Monitorings darf den Betrieb nie stören.
|
||||
// A failure of monitoring must never disturb operation.
|
||||
_logger.LogDebug(ex, "Watchdog: {What} konnte nicht gesendet werden (ignoriert).", what);
|
||||
}
|
||||
}
|
||||
@@ -185,12 +185,12 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
|
||||
if (_loop is not null)
|
||||
{
|
||||
try { await _loop.ConfigureAwait(false); }
|
||||
catch (OperationCanceledException) { /* erwartet */ }
|
||||
catch (OperationCanceledException) { /* expected */ }
|
||||
catch (Exception ex) { _logger.LogDebug(ex, "Watchdog: Heartbeat-Schleife endete mit Fehler."); }
|
||||
}
|
||||
|
||||
// Angekündigtes Ende, mit kurzer Frist. Hier wird alles geschluckt (auch ein
|
||||
// Zeitüberlauf), damit das Herunterfahren nie hängt oder wirft.
|
||||
// Announced end, with a short deadline. Everything is swallowed here (even a
|
||||
// timeout), so that shutdown never hangs or throws.
|
||||
try
|
||||
{
|
||||
using var stopCts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
|
||||
|
||||
Reference in New Issue
Block a user