Weboberflaeche (#531): X-Instanz an der einen fetch-Stelle, Instanzauswahl in der Kopfzeile, Verwaltungsansicht mit Anlegen/Starten/Anhalten/Fenster, Herkunft der Ereignisse, Sicherungs- und Einstellungsansicht. 402/409/502 werden eigen behandelt. Sicherungen im Manager (#530): Auflisten, Anlegen, Wiederherstellen, Loeschen. Das Zurueckspielen haelt die Instanz vorher an und startet sie danach wieder. Geordnetes Ende (Konzept 9.8). Zwei Befunde waren der Anlass: Der Manager konnte eine Instanz MIT FENSTER nie geordnet beenden. Control.ShutdownRequested war nur in HeadlessRun verdrahtet, nicht in der Avalonia-Anwendung - und ein Fenster ist die Vorgabe. Die Bitte wurde mit 202 beantwortet, niemand hoerte zu, und der Manager schoss den Prozess nach Ablauf seiner Frist jedes Mal ab. InstanceSupervisor.StopAsync hielt Socket-Schweigen fuer einen Todesnachweis und meldete "beendet", ohne den Prozess anzufassen. Bei einer verstummten Instanz ist der Socket von vornherein still - der Neustart haette einen zweiten Prozess auf dieselbe state.db gesetzt. Genau der Schaden, gegen den #528 gebaut ist. - ShutdownCoordinator: erst die Tuer schliessen, dann auf offene Arbeiten warten, Frist zwei Minuten. Vorher brach TaskScanner.DisposeAsync mitten in ProcessAsync ab, sodass die Aufgabe bis zum Lease-Ablauf auf "running" stehenblieb. - GET /api/instanz/puls hinter dem Socket-Gate: Phase und offene Arbeiten. Weiter gefragt statt gemeldet - ein Puls, den die Instanz von sich aus schickt, fehlt genau dann, wenn sie haengt. - Eskalationsleiter: drei Durchgaenge ohne Antwort, einmal um ein Ende bitten, nach 30s beenden und den Vollzug abwarten, gemeldet als Absturz. - EnsureDeadAsync vor jedem Start. Weiteres aus dem Review: - .gitignore verankert: "Instances/" passte auch auf src/ClawdDotNet.Core/Instances/. InstanceEndpoint.cs und InstanceLayout.cs waren dadurch nie eingecheckt. - Das WebUI-Token stand im Protokoll des Managers. Konsole ja, Datei nein. - Ein Klick auf "Instanz anlegen" loeste requestSubmit() zweimal aus. - [Obsolete] auf WebServerPort wieder entfernt: sechs Warnungen, die niemand abstellen konnte, solange E-M5 nicht fertig ist. - Die Wiederherstellung bricht nicht mehr ab, wenn der Browser weggeht. - Entwicklungspfade zum Instanzmanager aus Segmenten statt mit Rueckstrichen, und der Port kommt aus manager.json statt fest aus der Zeile. Offen und gemeldet: --managed hebelt die Lizenzpruefung aus (#535). 806 Tests gruen, 0 Warnungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
169 lines
7.3 KiB
C#
169 lines
7.3 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace ClawdDotNet.Core.Config;
|
|
|
|
public sealed class InstanceConfig
|
|
{
|
|
[JsonPropertyName("instanceId")]
|
|
public string InstanceId { get; set; } = Guid.NewGuid().ToString("N")[..8];
|
|
|
|
[JsonPropertyName("instanceName")]
|
|
public string InstanceName { get; set; } = "Default";
|
|
|
|
[JsonPropertyName("openRouterApiKey")]
|
|
public string OpenRouterApiKey { get; set; } = "";
|
|
|
|
[JsonPropertyName("workingDirectory")]
|
|
public string WorkingDirectory { get; set; } = "./data/";
|
|
|
|
/// <summary>
|
|
/// Wohin diese Instanz protokolliert. Leer heißt <c><Instanzverzeichnis>/Logs</c>.
|
|
///
|
|
/// <para><b>Je Instanz und nicht anwendungsweit</b> (#528, Stufe 1). Vorher galt bei
|
|
/// „leer" das Log-Verzeichnis aus den Anwendungseinstellungen — ein Ort für alle
|
|
/// Instanzen. In <c>Startup.log</c> standen dann abwechselnd Zeilen zweier Instanzen
|
|
/// ohne Kennzeichnung, welche gerade spricht; im Mehrinstanzbetrieb war jede
|
|
/// Fehlersuche damit Ratearbeit.</para>
|
|
///
|
|
/// <para>Ein relativer Wert zählt gegen das Instanzverzeichnis, nicht gegen das
|
|
/// Arbeitsverzeichnis des Prozesses (siehe <c>AppHost.ResolveLogDirectory</c>).</para>
|
|
/// </summary>
|
|
[JsonPropertyName("logDirectory")]
|
|
public string LogDirectory { get; set; } = "";
|
|
|
|
/// <summary>
|
|
/// Ob der Instanzmanager diese Instanz am Laufen hält (#528).
|
|
///
|
|
/// <para><b>Zwei Dinge in einem Schalter, weil es dieselbe Zusage ist:</b> Der Manager
|
|
/// startet diese Instanz, wenn er selbst hochkommt, und er startet sie wieder, wenn
|
|
/// sie abstürzt. Wer das nicht will, lässt ihn aus — dann startet sie nur, wenn jemand
|
|
/// es verlangt.</para>
|
|
///
|
|
/// <para><b>Ausgeschaltet vorbelegt</b>, und das ist die vorsichtige Seite: Ein
|
|
/// Manager, der nach einem Neustart der Maschine alles hochfährt, was er findet,
|
|
/// startet auch das, was jemand mit Absicht angehalten hat.</para>
|
|
///
|
|
/// <para>Der Neustart nach einem Absturz ist begrenzt: Nach mehreren Versuchen in
|
|
/// kurzer Folge setzt der Manager aus und meldet es. Was an einer kaputten
|
|
/// Konfiguration scheitert, scheitert beim zehnten Versuch genauso, und dann hat man
|
|
/// statt einer toten Instanz eine tote Instanz und ein volles Protokoll.</para>
|
|
/// </summary>
|
|
[JsonPropertyName("autostartByManager")]
|
|
public bool AutostartByManager { get; set; }
|
|
|
|
/// <summary>
|
|
/// Wohin die Sicherungen dieser Instanz gehen. Leer heißt
|
|
/// <c><Instanzverzeichnis>/Backups</c>.
|
|
///
|
|
/// <para><b>Je Instanz</b> (#528, Stufe 1). Vorher lagen die Archive aller Instanzen
|
|
/// in einem Ordner, und die Aufbewahrung (<see cref="BackupKeepCount"/>) räumt dort
|
|
/// nach dem Präfix <c>backup_<Instanzname>_</c> auf. Liegen zwei Namen
|
|
/// ineinander — <c>Test</c> und <c>Test_2</c> —, zählt die eine Instanz die Archive
|
|
/// der anderen mit und wirft die überzähligen weg.</para>
|
|
/// </summary>
|
|
[JsonPropertyName("backupDirectory")]
|
|
public string BackupDirectory { get; set; } = "";
|
|
|
|
/// <summary>Erstellt täglich zur eingestellten Uhrzeit eine Sicherung dieser Instanz.</summary>
|
|
[JsonPropertyName("autoBackupEnabled")]
|
|
public bool AutoBackupEnabled { get; set; }
|
|
|
|
/// <summary>Tageszeit der automatischen Sicherung, <c>HH:mm</c>.</summary>
|
|
[JsonPropertyName("autoBackupTime")]
|
|
public string AutoBackupTime { get; set; } = "03:00";
|
|
|
|
/// <summary>
|
|
/// Wie viele Sicherungen dieser Instanz aufbewahrt werden. 0 = alle behalten.
|
|
/// </summary>
|
|
[JsonPropertyName("backupKeepCount")]
|
|
public int BackupKeepCount { get; set; } = 14;
|
|
|
|
/// <summary>
|
|
/// Port der Weboberfläche. Entfällt mit dem Instanzmanager (E-M5): Instanzen hören
|
|
/// künftig ausschließlich auf ihrem Socket/Named Pipe; die Weboberfläche trägt der Manager.
|
|
///
|
|
/// <para><b>Noch ohne <c>[Obsolete]</c>.</b> Das Attribut stand hier kurz und hat
|
|
/// sechs Warnungen erzeugt, ohne dass eine davon jemand hätte abstellen können — der
|
|
/// Port ist bis zum Abschluss von E-M5 der einzige Weg, auf dem eine eigenständige
|
|
/// Instanz ihre Oberfläche anbietet. Eine Warnung, die nur wegsehen lässt, ist
|
|
/// schlechter als keine; sie kommt zurück, wenn der Ersatz steht.</para>
|
|
/// </summary>
|
|
[JsonPropertyName("webServerPort")]
|
|
public int WebServerPort { get; set; } = 7001;
|
|
|
|
/// <summary>
|
|
/// Ob die Weboberfläche mitläuft. Ausgeschaltet vorbelegt — sie macht aus einer
|
|
/// lokalen Anwendung einen Netzwerkdienst, der Agenten mit Datei-, Mail- und
|
|
/// FTP-Zugriff steuert. Das schaltet man bewusst ein.
|
|
/// </summary>
|
|
[JsonPropertyName("webUiEnabled")]
|
|
public bool WebUiEnabled { get; set; }
|
|
|
|
/// <summary>
|
|
/// <c>loopback</c> (Vorgabe) oder <c>any</c>. Bei <c>any</c> ist
|
|
/// <see cref="WebUiToken"/> Pflicht, sonst startet die Weboberfläche nicht.
|
|
/// </summary>
|
|
[JsonPropertyName("webUiBinding")]
|
|
public string WebUiBinding { get; set; } = "loopback";
|
|
|
|
/// <summary>
|
|
/// Zugangstoken der Weboberfläche. Steht in <c>ConfigSecrets</c> und liegt damit
|
|
/// geschützt in der Instanzdatei — wie der OpenRouter-Schlüssel daneben.
|
|
/// </summary>
|
|
[JsonPropertyName("webUiToken")]
|
|
public string WebUiToken { get; set; } = "";
|
|
|
|
[JsonPropertyName("telegramClient")]
|
|
public TelegramClientConfig? TelegramClient { get; set; }
|
|
|
|
/// <summary>Anbindung an das Watchdog-Modul des Deploymentcenters (Instanz-Heartbeat).</summary>
|
|
[JsonPropertyName("watchdog")]
|
|
public WatchdogConfig Watchdog { get; set; } = new();
|
|
|
|
/// <summary>Tagesgrenzen über alle Agenten der Instanz hinweg. 0 = keine Grenze.</summary>
|
|
[JsonPropertyName("budget")]
|
|
public InstanceBudget Budget { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// Wie viele Weitergaben eine Delegationskette höchstens haben darf (B8, #206).
|
|
/// A→B ist die Tiefe 1. Zyklen fängt die Kette selbst ab; dieser Wert begrenzt die
|
|
/// Staffel A→B→C→D, die niemand beauftragt hat und die jeden Schritt einen vollen
|
|
/// Lauf kostet.
|
|
///
|
|
/// Instanzweit und nicht je Agent: Eine Kette gehört keinem der Beteiligten.
|
|
/// </summary>
|
|
[JsonPropertyName("maxDelegationDepth")]
|
|
public int MaxDelegationDepth { get; set; } = 3;
|
|
|
|
/// <summary>
|
|
/// Wie lange ein delegierter Aufruf höchstens auf einen belegten Zielagenten
|
|
/// wartet, in Sekunden (#461). Danach kommt eine Absage statt weiterer Wartezeit.
|
|
///
|
|
/// Gilt nur für Delegationen. Ein vom Menschen angestoßener Chat reiht sich
|
|
/// weiterhin ein und wartet, so lange es dauert.
|
|
/// </summary>
|
|
[JsonPropertyName("delegationGateTimeoutSeconds")]
|
|
public int DelegationGateTimeoutSeconds { get; set; } = 120;
|
|
|
|
[JsonPropertyName("agents")]
|
|
public List<AgentConfig> Agents { get; set; } = new();
|
|
|
|
[JsonPropertyName("services")]
|
|
public List<ServiceConfig> Services { get; set; } = new();
|
|
}
|
|
|
|
public sealed class TelegramClientConfig
|
|
{
|
|
[JsonPropertyName("apiId")]
|
|
public int ApiId { get; set; }
|
|
|
|
[JsonPropertyName("apiHash")]
|
|
public string ApiHash { get; set; } = "";
|
|
|
|
[JsonPropertyName("phoneNumber")]
|
|
public string PhoneNumber { get; set; } = "";
|
|
|
|
[JsonPropertyName("password2FA")]
|
|
public string? Password2FA { get; set; }
|
|
}
|