MC2 Final (D16 a/b/d): verständliche Updates + ehrliche Speicher-Zahlen
a) Update-Meldungen: generischer Release-Summarizer in Lucys Stimme mit
strukturiertem Aktions-Verdikt ("Musst du etwas tun? NEIN/JA") für
Hermes, Engine (llama.cpp) und llama-swap; Fangnetz-Hinweis verheiratet
Breaking-Change-Sorge mit dem Postcheck.
b) OS ehrlich: zurückgestellte Pakete (Phasen-Rollout / kept back) werden
ausgewiesen statt scheinbar zu hängen.
d) Ehrliche Speicher-Zahlen: KV-Cache aus echten GGUF-Architektur-Daten
(Layer × KV-Köpfe × head_dim) + KV-Quant aus dem cmd statt params-blinder
Schätzung — footprint_gb als eine Zahlensprache (Zentrale, Modell-Manager,
fits-Check auf warmset+largest). Auto-Rewarm-Nudge nach Config-Reload.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ArrowRight, Bot, ExternalLink, GitCommit, Package, RefreshCw, Server, Shield, Shuffle, X } from "lucide-react"
|
||||
import { AlertTriangle, ArrowRight, Bot, CheckCircle2, Clock, ExternalLink, GitCommit, Package, RefreshCw, Server, Shield, ShieldCheck, Shuffle, X } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
import type { Job, UpdateDetails } from "@/lib/api"
|
||||
|
||||
@@ -47,72 +47,135 @@ export function UpdateDetailModal({ detail, maintenanceJob, onClose, onApply }:
|
||||
</div>
|
||||
) : d?.error ? (
|
||||
<div className="rounded-lg border border-red-500/30 bg-red-500/5 p-3 text-red-400">{d.error}</div>
|
||||
) : detail.kind === "os" ? (
|
||||
(d?.count ?? 0) === 0 ? (
|
||||
<div className="text-muted-foreground">Keine Pakete zu aktualisieren — System ist aktuell.</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-muted-foreground">{d!.count} Paket(e) werden aktualisiert:</div>
|
||||
<div className="space-y-1">
|
||||
{d!.packages!.map((p) => (
|
||||
<div key={p.name} className="flex items-center justify-between gap-2 rounded-md border border-border/40 bg-background/30 px-2.5 py-1.5">
|
||||
<span className="flex items-center gap-1.5 font-mono text-[11px] truncate">
|
||||
<Package className="h-3 w-3 text-cyan-400 shrink-0" />{p.name}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 font-mono text-[10px] text-muted-foreground shrink-0">
|
||||
<span>{p.current}</span><ArrowRight className="h-3 w-3" /><span className="text-emerald-400">{p.candidate}</span>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : detail.kind === "engine" || detail.kind === "swap" ? (
|
||||
<>
|
||||
<div className="flex items-center gap-2 font-mono text-[11px]">
|
||||
<span className="rounded-md border border-border/40 bg-background/30 px-2 py-1">Build {d?.installed_build ?? "?"}</span>
|
||||
<ArrowRight className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="rounded-md border border-emerald-500/30 bg-emerald-500/5 px-2 py-1 text-emerald-400">Build {d?.latest_build ?? "?"}</span>
|
||||
</div>
|
||||
{(d?.name || d?.latest_tag) && (
|
||||
<div className="text-muted-foreground">Release: <span className="text-foreground">{d?.name}</span>{d?.latest_tag ? ` (${d.latest_tag})` : ""}</div>
|
||||
)}
|
||||
{d?.url && (
|
||||
<a href={d.url} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-primary hover:underline">
|
||||
Release-Notes auf GitHub <ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
)}
|
||||
{d?.body && (
|
||||
<pre className="whitespace-pre-wrap rounded-lg border border-border/40 bg-background/30 p-3 text-[10px] leading-relaxed text-muted-foreground max-h-60 overflow-y-auto scrollbar-thin">{d.body}</pre>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// hermes
|
||||
(d?.commits?.length ?? 0) === 0 ? (
|
||||
<div className="text-muted-foreground">Keine neuen Commits — Hermes-Agent ist bereits aktuell.</div>
|
||||
) : (
|
||||
<>
|
||||
{d?.summary && (
|
||||
<div className="rounded-lg border border-primary/25 bg-primary/5 p-3 space-y-1">
|
||||
<div className="text-[10px] font-bold uppercase tracking-wider text-primary">Was dieses Update bedeutet (Zusammenfassung der Box)</div>
|
||||
<pre className="whitespace-pre-wrap text-[11px] leading-relaxed text-foreground/90 font-sans">{d.summary}</pre>
|
||||
<>
|
||||
{/* Aktions-Verdikt in Lucys Stimme — "Musst du etwas tun?" (engine/swap/hermes) */}
|
||||
{d?.action_needed != null && (
|
||||
<div className={cn("flex items-start gap-2 rounded-lg border p-3",
|
||||
d.action_needed ? "border-amber-500/40 bg-amber-500/10" : "border-emerald-500/40 bg-emerald-500/10")}>
|
||||
{d.action_needed
|
||||
? <AlertTriangle className="h-4 w-4 text-amber-400 shrink-0 mt-0.5" />
|
||||
: <CheckCircle2 className="h-4 w-4 text-emerald-400 shrink-0 mt-0.5" />}
|
||||
<div className="min-w-0">
|
||||
<div className={cn("text-xs font-semibold", d.action_needed ? "text-amber-300" : "text-emerald-300")}>
|
||||
Musst du etwas tun? {d.action_needed ? "Ja" : "Nein — die Box regelt das (Fangnetz)"}
|
||||
</div>
|
||||
{d.action_needed && d.action_text && (
|
||||
<div className="mt-0.5 text-[11px] text-foreground/90">{d.action_text}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-muted-foreground">{d!.behind} neue Commit(s) auf <span className="font-mono text-foreground">origin/{d!.branch}</span>:</div>
|
||||
<div className="space-y-1">
|
||||
{d!.commits!.map((c) => (
|
||||
<div key={c.hash} className="flex items-start gap-2 rounded-md border border-border/40 bg-background/30 px-2.5 py-1.5">
|
||||
<GitCommit className="h-3.5 w-3.5 text-primary shrink-0 mt-0.5" />
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] truncate">{c.subject}</div>
|
||||
<div className="font-mono text-[9px] text-muted-foreground">{c.hash} · {c.when}</div>
|
||||
</div>
|
||||
)}
|
||||
{/* Zusammenfassung der Box (Breaking Changes zuerst) */}
|
||||
{d?.summary && (
|
||||
<div className="rounded-lg border border-primary/25 bg-primary/5 p-3 space-y-1">
|
||||
<div className="text-[10px] font-bold uppercase tracking-wider text-primary">Was dieses Update bedeutet (Zusammenfassung der Box)</div>
|
||||
<pre className="whitespace-pre-wrap text-[11px] leading-relaxed text-foreground/90 font-sans">{d.summary}</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{detail.kind === "os" ? (
|
||||
<>
|
||||
{(d?.count ?? 0) === 0 ? (
|
||||
<div className="text-muted-foreground">Keine Pakete zu aktualisieren — System ist aktuell.</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-muted-foreground">{d!.count} Paket(e) werden aktualisiert:</div>
|
||||
<div className="space-y-1">
|
||||
{d!.packages!.map((p) => (
|
||||
<div key={p.name} className="flex items-center justify-between gap-2 rounded-md border border-border/40 bg-background/30 px-2.5 py-1.5">
|
||||
<span className="flex items-center gap-1.5 font-mono text-[11px] truncate">
|
||||
<Package className="h-3 w-3 text-cyan-400 shrink-0" />{p.name}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 font-mono text-[10px] text-muted-foreground shrink-0">
|
||||
<span>{p.current}</span><ArrowRight className="h-3 w-3" /><span className="text-emerald-400">{p.candidate}</span>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{/* Ehrlich: vom System zurückgestellte Pakete (Phasen-Rollout / kept back) */}
|
||||
{(d?.held_back?.length ?? 0) > 0 && (
|
||||
<div className="space-y-1.5 rounded-lg border border-border/40 bg-background/20 p-3">
|
||||
<div className="flex items-center gap-1.5 text-[11px] font-semibold text-muted-foreground">
|
||||
<Clock className="h-3.5 w-3.5" /> Vom Hersteller zurückgestellt — kein Handeln nötig
|
||||
</div>
|
||||
<p className="text-[10px] text-muted-foreground leading-normal">
|
||||
Diese Pakete gäbe es bereits, Ubuntu spielt sie aber gestaffelt aus (Phasen-Rollout)
|
||||
bzw. hält sie kurz zurück. Sie kommen bei einem der nächsten automatischen Läufe von
|
||||
selbst — das ist kein Fehler und nichts hängt fest.
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{d!.held_back!.map((p) => (
|
||||
<div key={p.name} className="flex items-center justify-between gap-2 rounded-md border border-border/30 bg-background/30 px-2.5 py-1.5">
|
||||
<span className="flex items-center gap-1.5 font-mono text-[11px] truncate">
|
||||
<Package className="h-3 w-3 text-muted-foreground shrink-0" />{p.name}
|
||||
</span>
|
||||
<span className="text-[9px] text-muted-foreground shrink-0">
|
||||
{p.reason === "phasing" ? "Phasen-Rollout" : "vorerst zurückgehalten"}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
</>
|
||||
) : detail.kind === "engine" || detail.kind === "swap" ? (
|
||||
<>
|
||||
<div className="flex items-center gap-2 font-mono text-[11px]">
|
||||
<span className="rounded-md border border-border/40 bg-background/30 px-2 py-1">Build {d?.installed_build ?? "?"}</span>
|
||||
<ArrowRight className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span className="rounded-md border border-emerald-500/30 bg-emerald-500/5 px-2 py-1 text-emerald-400">Build {d?.latest_build ?? "?"}</span>
|
||||
</div>
|
||||
{(d?.name || d?.latest_tag) && (
|
||||
<div className="text-muted-foreground">Release: <span className="text-foreground">{d?.name}</span>{d?.latest_tag ? ` (${d.latest_tag})` : ""}</div>
|
||||
)}
|
||||
{d?.url && (
|
||||
<a href={d.url} target="_blank" rel="noreferrer" className="inline-flex items-center gap-1 text-primary hover:underline">
|
||||
Original-Release-Notes auf GitHub <ExternalLink className="h-3 w-3" />
|
||||
</a>
|
||||
)}
|
||||
{d?.body && (
|
||||
<details className="group">
|
||||
<summary className="cursor-pointer text-[10px] text-muted-foreground hover:text-foreground">Original-Notizen (englisch) anzeigen</summary>
|
||||
<pre className="mt-1.5 whitespace-pre-wrap rounded-lg border border-border/40 bg-background/30 p-3 text-[10px] leading-relaxed text-muted-foreground max-h-60 overflow-y-auto scrollbar-thin">{d.body}</pre>
|
||||
</details>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// hermes
|
||||
(d?.commits?.length ?? 0) === 0 ? (
|
||||
<div className="text-muted-foreground">Keine neuen Commits — Hermes-Agent ist bereits aktuell.</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-muted-foreground">{d!.behind} neue Commit(s) auf <span className="font-mono text-foreground">origin/{d!.branch}</span>:</div>
|
||||
<div className="space-y-1">
|
||||
{d!.commits!.map((c) => (
|
||||
<div key={c.hash} className="flex items-start gap-2 rounded-md border border-border/40 bg-background/30 px-2.5 py-1.5">
|
||||
<GitCommit className="h-3.5 w-3.5 text-primary shrink-0 mt-0.5" />
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] truncate">{c.subject}</div>
|
||||
<div className="font-mono text-[9px] text-muted-foreground">{c.hash} · {c.when}</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Fangnetz-Hinweis: verheiratet Breaking-Change-Sorge mit dem Postcheck (engine/swap/hermes) */}
|
||||
{detail.kind !== "os" && !nothing && (
|
||||
<div className="flex items-start gap-2 rounded-lg border border-border/40 bg-background/20 p-2.5">
|
||||
<ShieldCheck className="h-3.5 w-3.5 text-emerald-400 shrink-0 mt-0.5" />
|
||||
<p className="text-[10px] text-muted-foreground leading-normal">
|
||||
Vor dem Update sichert die Box automatisch den alten Stand. Danach prüft sie den ganzen
|
||||
Stack per echter Anfrage — läuft etwas nicht, rollt sie von selbst zurück{detail.kind === "hermes" ? " und startet den Gateway neu" : ""}.
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-[10px] text-muted-foreground leading-normal">Vor dem Update wird automatisch ein Backup erstellt; danach startet der Hermes-Gateway neu.</p>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user