Tab-Review-Fixes: toter Gedaechtnis-Knopf, Konsole-Restart, Cockpit-Dopplung

- Gedaechtnis: 'Im Terminal starten' navigierte zu nicht existenter View 'terminal'
  -> 'konsole'; interne Duplikate (AUTO-Set, CAT_LABEL_MAP) entfernt
- Konsole: box-console jetzt in Dienste-Liste + Restart-Allowlist; Offline-Overlay
  bekommt Ein-Klick-'Dienst neu starten' statt SSH-Anweisung
- Cockpit Werkzeuge: Doppel-Eintrag ('Box-Shell' + 'Interaktives Terminal', beide
  -> Konsole) zu EINEM Eintrag 'Konsole (Box-Shell)' zusammengelegt
- Auftragsbuch: 'Nichts zu entscheiden'-Banner beruecksichtigt blockierte
  Ideen-Karten; Abschnitt heisst neutral 'Patch-Vorschlaege'
- Hermes-Diagnose: Klickweg (Dienste-Schublade oeffnen) statt veraltetem
  Drawer-Namen + systemctl-Kommandos
- Entdecken: Upgrade-Knopf uebernimmt fremde Quant nicht mehr blind
- Skills: Status-Badge zeigt Klartext (abgeschaltet/defekt/...) statt rohem State

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-16 00:42:04 +02:00
parent bc60f9254b
commit 26224804ed
34 changed files with 185 additions and 145 deletions
+4
View File
@@ -98,6 +98,10 @@ def services() -> dict:
"ok": _mem0_reachable(), "scope": "user"}, "ok": _mem0_reachable(), "scope": "user"},
{"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL, {"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL,
"ok": _voice_reachable(), "scope": "user"}, "ok": _voice_reachable(), "scope": "user"},
# ttyd hinter dem /console/-Proxy — war als einziger UI-Dienst NICHT in der Liste,
# das Konsole-Overlay schickte den User deshalb ins SSH statt zum Restart-Knopf.
{"name": "Box-Konsole (ttyd)", "unit": "box-console", "url": "/console/",
"ok": bool(a.get("box_console_reachable")), "scope": "user"},
] ]
return { return {
"services": rows, "services": rows,
+1 -1
View File
@@ -19,7 +19,7 @@ from services import catalog, discover, jobengine, llamaswap, system
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user). # System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
SYSTEM_SERVICES = {"llama-swap"} SYSTEM_SERVICES = {"llama-swap"}
USER_SERVICES = {"mission-control-2", "mc2-gateway", "mc2-steward", USER_SERVICES = {"mission-control-2", "mc2-gateway", "mc2-steward", "box-console",
"hermes-gateway", "hermes-builtin-ui", "mem0-service", "voice-service"} "hermes-gateway", "hermes-builtin-ui", "mem0-service", "voice-service"}
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root). # Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as m,ae as S,r as c,j as e,af as z,L as g,U as M,ag as C,ad as D,ah as A,ai as B,aj as Z,e as E,ak as L,u as R,b as q,g as k,q as T}from"./index-D94Dzyvg.js";import{R as j}from"./rotate-ccw--ed6DlAf.js";/** import{c as m,af as S,r as c,j as e,ag as z,L as g,U as M,ah as C,ae as D,ai as A,aj as B,ak as Z,e as E,al as L,u as R,b as q,g as k,q as T}from"./index-CQgUnd1p.js";import{R as j}from"./rotate-ccw-ChVZWN8g.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as v,r as c,I as D,J as I,j as e,K as h,x as B,B as H,D as z,M as E,e as M,L as T,N as F,C as K}from"./index-D94Dzyvg.js";import{F as O}from"./folder-open-DzbxkBSk.js";import{C as R}from"./circle-x-C6D3ABCj.js";import{C as V}from"./copy-Dy-OlKte.js";/** import{c as v,r as c,I as D,J as I,j as e,K as h,x as B,B as H,D as z,M as E,e as M,L as T,N as F,C as K}from"./index-CQgUnd1p.js";import{F as O}from"./folder-open-vcdogQ3l.js";import{C as R}from"./circle-x-DKuGYlQP.js";import{C as V}from"./copy-hJYMa3at.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as p,j as e,r as M,x as C,a4 as z,a5 as k,a6 as y,a0 as v,W as L,a7 as G,a2 as T,e as f,U as D}from"./index-D94Dzyvg.js";import{B as w}from"./book-open-Cc6Rq6Ws.js";import{a as N,b as B,L as P,C as E,Z as R}from"./zap-Ds7DtEXe.js";import{L as I}from"./lightbulb-B-eiNc3G.js";/** import{c as p,j as e,r as M,x as C,a6 as z,a7 as k,a8 as y,a0 as v,W as L,a9 as G,a2 as T,e as f,U as D}from"./index-CQgUnd1p.js";import{B as w}from"./book-open-BFFOe9re.js";import{a as N,b as B,L as P,C as E,Z as R}from"./zap-Df-ORHkh.js";import{L as I}from"./lightbulb-Btbex65P.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
+1
View File
@@ -0,0 +1 @@
import{Y as b,u as f,$ as h,r as d,j as e,e as c,Z as g,T as p,a3 as j,a4 as N,g as v,a5 as w,q as x}from"./index-CQgUnd1p.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
-1
View File
@@ -1 +0,0 @@
import{Y as l,$ as o,j as e,e as a,Z as n,T as d,a3 as i}from"./index-D94Dzyvg.js";function c(){const{data:s}=l(),r=s!=null&&s.box_console_url?o(s.box_console_url):void 0,t=s==null?void 0:s.box_console_reachable;return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:a("h-2 w-2 rounded-full",t?"bg-emerald-500 animate-pulse":"bg-amber-500")}),t?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(n,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[t===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(d,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der ",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),"-Dienst (ttyd) läuft nicht. Auf der Box: ",e.jsx("code",{className:"font-mono",children:"systemctl --user restart box-console"}),"."]})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(i,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{c as KonsoleView};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{an as y,r as x,g as L,j as e,L as v,S,ao as W,U as C,e as w,ap as E}from"./index-D94Dzyvg.js";import{F as M}from"./folder-open-DzbxkBSk.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(` import{ao as y,r as x,g as L,j as e,L as v,S,ap as W,U as C,e as w,aq as E}from"./index-CQgUnd1p.js";import{F as M}from"./folder-open-vcdogQ3l.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(`
`),d=[];let r=[],u=null;const i=a=>{r.length&&(d.push(e.jsx("ul",{className:"mb-3 ml-4 list-disc space-y-1",children:r},a)),r=[])};return f.forEach((a,p)=>{const t=`l${p}`;if(u!==null){a.trimEnd()==="```"?(d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85",children:u.join(` `),d=[];let r=[],u=null;const i=a=>{r.length&&(d.push(e.jsx("ul",{className:"mb-3 ml-4 list-disc space-y-1",children:r},a)),r=[])};return f.forEach((a,p)=>{const t=`l${p}`;if(u!==null){a.trimEnd()==="```"?(d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85",children:u.join(`
`)},t)),u=null):u.push(a);return}if(a.trimStart().startsWith("```")){i(t),u=[];return}const m=a.trimEnd();if(!m.trim()){i(t);return}const g=m.match(/^(#{1,4})\s+(.*)$/);if(g){i(t);const j=g[1].length,s=j===1?"text-lg font-bold mt-1 mb-3":j===2?"text-base font-bold mt-4 mb-2":"text-sm font-bold mt-3 mb-1.5";d.push(e.jsx("p",{className:w(s,"font-space text-foreground"),children:N(g[2],h,l,t)},t));return}const b=m.match(/^\s*[-*•]\s+(.*)$/);if(b){r.push(e.jsx("li",{className:"text-xs leading-relaxed text-foreground/85",children:N(b[1],h,l,t)},t));return}i(t),d.push(e.jsx("p",{className:"mb-2 text-xs leading-relaxed text-foreground/85",children:N(m,h,l,t)},t))}),i("end"),u!==null&&d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]",children:u.join(` `)},t)),u=null):u.push(a);return}if(a.trimStart().startsWith("```")){i(t),u=[];return}const m=a.trimEnd();if(!m.trim()){i(t);return}const g=m.match(/^(#{1,4})\s+(.*)$/);if(g){i(t);const j=g[1].length,s=j===1?"text-lg font-bold mt-1 mb-3":j===2?"text-base font-bold mt-4 mb-2":"text-sm font-bold mt-3 mb-1.5";d.push(e.jsx("p",{className:w(s,"font-space text-foreground"),children:N(g[2],h,l,t)},t));return}const b=m.match(/^\s*[-*•]\s+(.*)$/);if(b){r.push(e.jsx("li",{className:"text-xs leading-relaxed text-foreground/85",children:N(b[1],h,l,t)},t));return}i(t),d.push(e.jsx("p",{className:"mb-2 text-xs leading-relaxed text-foreground/85",children:N(m,h,l,t)},t))}),i("end"),u!==null&&d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]",children:u.join(`
`)},"code-end")),e.jsx("div",{className:"max-w-3xl",children:d})}function N(o,h,l,f){return o.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g).map((r,u)=>{const i=`${f}-${u}`,a=r.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/);if(a){const p=a[1].trim(),t=(a[2]??a[1]).trim();return l.has(p.toLowerCase())?e.jsx("button",{onClick:()=>h(p),className:"rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer",children:t},i):e.jsx("span",{className:"rounded bg-background/40 px-1 text-muted-foreground",title:"Notiz existiert (noch) nicht",children:t},i)}return r.startsWith("**")&&r.endsWith("**")?e.jsx("b",{className:"font-semibold text-foreground",children:r.slice(2,-2)},i):r.startsWith("*")&&r.endsWith("*")&&r.length>2?e.jsx("i",{children:r.slice(1,-1)},i):r.startsWith("`")&&r.endsWith("`")?e.jsx("code",{className:"rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300",children:r.slice(1,-1)},i):e.jsx("span",{children:r},i)})}export{T as WissenView}; `)},"code-end")),e.jsx("div",{className:"max-w-3xl",children:d})}function N(o,h,l,f){return o.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g).map((r,u)=>{const i=`${f}-${u}`,a=r.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/);if(a){const p=a[1].trim(),t=(a[2]??a[1]).trim();return l.has(p.toLowerCase())?e.jsx("button",{onClick:()=>h(p),className:"rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer",children:t},i):e.jsx("span",{className:"rounded bg-background/40 px-1 text-muted-foreground",title:"Notiz existiert (noch) nicht",children:t},i)}return r.startsWith("**")&&r.endsWith("**")?e.jsx("b",{className:"font-semibold text-foreground",children:r.slice(2,-2)},i):r.startsWith("*")&&r.endsWith("*")&&r.length>2?e.jsx("i",{children:r.slice(1,-1)},i):r.startsWith("`")&&r.endsWith("`")?e.jsx("code",{className:"rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300",children:r.slice(1,-1)},i):e.jsx("span",{children:r},i)})}export{T as WissenView};
@@ -1,4 +1,4 @@
import{c as a}from"./index-D94Dzyvg.js";/** import{c as a}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c}from"./index-D94Dzyvg.js";/** import{c}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c}from"./index-D94Dzyvg.js";/** import{c}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-D94Dzyvg.js";/** import{c as a}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as t}from"./index-D94Dzyvg.js";/** import{c as t}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as t}from"./index-D94Dzyvg.js";/** import{c as t}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-D94Dzyvg.js";/** import{c as a}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as a}from"./index-D94Dzyvg.js";/** import{c as a}from"./index-CQgUnd1p.js";/**
* @license lucide-react v0.460.0 - ISC * @license lucide-react v0.460.0 - ISC
* *
* This source code is licensed under the ISC license. * This source code is licensed under the ISC license.
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="manifest" href="/manifest.webmanifest" /> <link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title> <title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-D94Dzyvg.js"></script> <script type="module" crossorigin src="/assets/index-CQgUnd1p.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BH760w1h.css"> <link rel="stylesheet" crossorigin href="/assets/index-BH760w1h.css">
</head> </head>
<body> <body>
+7 -8
View File
@@ -236,15 +236,14 @@ export function AgentView() {
Der Hermes-Dienst ist auf der Box derzeit offline oder kann sich nicht mit dem lokalen llama-swap verbinden. Der Hermes-Dienst ist auf der Box derzeit offline oder kann sich nicht mit dem lokalen llama-swap verbinden.
</p> </p>
<p> <p>
Stelle sicher, dass die systemd-Dienste auf der Box laufen. Du kannst den Status im <strong>OS &amp; Updates Drawer</strong> prüfen und die Dienste bei Bedarf neu starten. Am schnellsten: In der System-Schublade unter <strong>Dienste</strong> den Eintrag{" "}
<strong>Hermes-Gateway</strong> mit einem Klick neu starten (dort gibt es auch die Logs dazu).
</p> </p>
<button
<div className="p-3.5 bg-background/25 rounded-xl border border-border/30 font-mono text-[10px] space-y-1"> onClick={() => window.dispatchEvent(new CustomEvent("open-system-drawer", { detail: { tab: "maintenance" } }))}
<div className="text-muted-foreground/60"># Dienste manuell auf der Box prüfen:</div> className="flex h-9 items-center gap-1.5 rounded-lg border border-primary/40 bg-primary/10 px-4 text-[11px] font-bold uppercase tracking-wide text-primary transition-all hover:bg-primary/20 cursor-pointer">
<div className="text-primary">systemctl --user status hermes-gateway</div> <Wrench className="h-3.5 w-3.5" /> Dienste-Übersicht öffnen
<div className="text-primary">systemctl --user status hermes-builtin-ui</div> </button>
</div>
<p> <p>
Weitere Einrichtungsdetails (Brain-Konfiguration, MCP-Kopplungen, SSH-Tunneling und Such-Engines) findest du in der Dokumentationsdatei: Weitere Einrichtungsdetails (Brain-Konfiguration, MCP-Kopplungen, SSH-Tunneling und Such-Engines) findest du in der Dokumentationsdatei:
<code className="ml-1 px-1.5 py-0.5 rounded bg-muted/40 text-foreground border border-border/30">docs/HERMES_SETUP.md</code>. <code className="ml-1 px-1.5 py-0.5 rounded bg-muted/40 text-foreground border border-border/30">docs/HERMES_SETUP.md</code>.
+7 -3
View File
@@ -36,6 +36,9 @@ function fmtWhen(ts: number | null | undefined): string {
export function AuftragsbuchView() { export function AuftragsbuchView() {
const { data, isLoading, refetch, isFetching } = useAuftragsbuch() const { data, isLoading, refetch, isFetching } = useAuftragsbuch()
// Geteilter Cache mit der IdeenSection — hier nur fürs ehrliche "Nichts zu entscheiden"-
// Banner (eine hängende Karte direkt darunter widerspräche dem grünen Häkchen).
const { data: ideenData } = useIdeen()
const qc = useQueryClient() const qc = useQueryClient()
const { showAlert, showConfirm, dialogElement } = useDialog() const { showAlert, showConfirm, dialogElement } = useDialog()
const [busy, setBusy] = useState<Record<string, boolean>>({}) const [busy, setBusy] = useState<Record<string, boolean>>({})
@@ -57,7 +60,8 @@ export function AuftragsbuchView() {
const items = data?.items ?? [] const items = data?.items ?? []
const kandidaten = data?.skill_kandidaten ?? [] const kandidaten = data?.skill_kandidaten ?? []
const empty = items.length === 0 && kandidaten.length === 0 const blockedIdeen = ideenData?.available ? ideenData.items.filter((i) => i.status === "blocked").length : 0
const empty = items.length === 0 && kandidaten.length === 0 && blockedIdeen === 0
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@@ -102,7 +106,7 @@ export function AuftragsbuchView() {
{items.length > 0 && ( {items.length > 0 && (
<section className="space-y-3"> <section className="space-y-3">
<SectionLabel icon={GitBranch}>Fertige Patches ({items.length})</SectionLabel> <SectionLabel icon={GitBranch}>Patch-Vorschläge ({items.length})</SectionLabel>
{items.map((it) => ( {items.map((it) => (
<ProposalCard key={`${it.repo ?? "mc2"}:${it.branch}`} item={it} busy={busy} onAct={act} showConfirm={showConfirm} /> <ProposalCard key={`${it.repo ?? "mc2"}:${it.branch}`} item={it} busy={busy} onAct={act} showConfirm={showConfirm} />
))} ))}
@@ -243,7 +247,7 @@ function IdeenSection() {
</div> </div>
<p className="text-[11px] text-muted-foreground"> <p className="text-[11px] text-muted-foreground">
Eine Queue, alle Türen: hier, per Telegram oder im Gespräch mit Lucy. Die Box arbeitet Ideen selbst Eine Queue, alle Türen: hier, per Telegram oder im Gespräch mit Lucy. Die Box arbeitet Ideen selbst
aus und baut Code-Ergebnisse erscheinen als Karte unter Fertige Patches, nichts davon geht ohne aus und baut Code-Ergebnisse erscheinen als Karte unter Patch-Vorschläge, nichts davon geht ohne
deinen Klick live. Nur reine Doku-Bagatellen zieht die Box nachts selbst ein (mit Fangnetz und Meldung). deinen Klick live. Nur reine Doku-Bagatellen zieht die Box nachts selbst ein (mit Fangnetz und Meldung).
</p> </p>
+11 -1
View File
@@ -30,6 +30,14 @@ const HERKUNFT_UI: Record<EigenlebenSkill["herkunft"], { icon: LucideIcon; cls:
}, },
} }
// Hermes-Skill-States in Klartext (Quelle: banner/use_count-Daten; unbekannte Werte roh anzeigen).
const STATUS_LABEL: Record<string, string> = {
disabled: "abgeschaltet",
deprecated: "veraltet",
broken: "defekt",
error: "Fehler",
}
function datumKurz(iso: string | null): string { function datumKurz(iso: string | null): string {
if (!iso) return "—" if (!iso) return "—"
const d = new Date(iso) const d = new Date(iso)
@@ -169,7 +177,9 @@ function SkillKarte({ skill }: { skill: EigenlebenSkill }) {
</span> </span>
)} )}
{skill.status !== "active" && ( {skill.status !== "active" && (
<span className="rounded bg-amber-500/10 px-1.5 py-0.5 text-[9px] font-bold text-amber-300">{skill.status}</span> <span className="rounded bg-amber-500/10 px-1.5 py-0.5 text-[9px] font-bold text-amber-300">
{STATUS_LABEL[skill.status] ?? skill.status}
</span>
)} )}
</div> </div>
<p className={cn("mt-1 text-xs leading-relaxed text-foreground/75", !offen && "line-clamp-2")}> <p className={cn("mt-1 text-xs leading-relaxed text-foreground/75", !offen && "line-clamp-2")}>
+30 -4
View File
@@ -1,5 +1,7 @@
import { SquareTerminal, ExternalLink, AlertTriangle } from "lucide-react" import { useState } from "react"
import { useAgentStatus } from "@/lib/queries" import { SquareTerminal, ExternalLink, AlertTriangle, RefreshCw } from "lucide-react"
import { api } from "@/lib/api"
import { useAgentStatus, useQueryClient, qk, invalidate } from "@/lib/queries"
import { resolveExternalUrl, cn } from "@/lib/utils" import { resolveExternalUrl, cn } from "@/lib/utils"
// Konsole = direkter, SSH-artiger Box-Zugriff (ttyd → Login-Shell auf :7682), eingebettet // Konsole = direkter, SSH-artiger Box-Zugriff (ttyd → Login-Shell auf :7682), eingebettet
@@ -7,8 +9,28 @@ import { resolveExternalUrl, cn } from "@/lib/utils"
// Terminal selbst). Die Passwort-Verwaltung liegt in System-Wartung → Einstellungen. // Terminal selbst). Die Passwort-Verwaltung liegt in System-Wartung → Einstellungen.
export function KonsoleView() { export function KonsoleView() {
const { data: agent } = useAgentStatus() const { data: agent } = useAgentStatus()
const qc = useQueryClient()
const url = agent?.box_console_url ? resolveExternalUrl(agent.box_console_url) : undefined const url = agent?.box_console_url ? resolveExternalUrl(agent.box_console_url) : undefined
const ok = agent?.box_console_reachable const ok = agent?.box_console_reachable
const [restarting, setRestarting] = useState(false)
const [restartMsg, setRestartMsg] = useState("")
// Ein-Klick-Reparatur statt SSH-Anweisung: box-console ist ein normaler User-Dienst.
async function restartConsole() {
setRestarting(true)
setRestartMsg("")
try {
const r = await api<{ ok: boolean; err?: string }>("/api/maintenance/restart", {
method: "POST", body: JSON.stringify({ service: "box-console" }),
})
setRestartMsg(r.ok ? "Neu gestartet — einen Moment, dann lädt das Terminal." : `Fehlgeschlagen: ${r.err || "Unbekannter Fehler"}`)
invalidate(qc, qk.agentStatus, qk.services)
} catch (e: any) {
setRestartMsg(`Fehlgeschlagen: ${e?.message || e}`)
} finally {
setRestarting(false)
}
}
return ( return (
<div className="flex h-full flex-col gap-4"> <div className="flex h-full flex-col gap-4">
@@ -42,9 +64,13 @@ export function KonsoleView() {
<AlertTriangle className="h-8 w-8 text-amber-400" /> <AlertTriangle className="h-8 w-8 text-amber-400" />
<div className="text-sm font-semibold text-amber-300">Konsole nicht erreichbar</div> <div className="text-sm font-semibold text-amber-300">Konsole nicht erreichbar</div>
<p className="max-w-sm text-[11px] leading-normal text-muted-foreground"> <p className="max-w-sm text-[11px] leading-normal text-muted-foreground">
Der <code className="font-mono text-primary">box-console</code>-Dienst (ttyd) läuft nicht. Der Terminal-Dienst (<code className="font-mono text-primary">box-console</code>) läuft gerade nicht.
Auf der Box: <code className="font-mono">systemctl --user restart box-console</code>.
</p> </p>
<button onClick={restartConsole} disabled={restarting}
className="flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50">
<RefreshCw className={cn("h-3.5 w-3.5", restarting && "animate-spin")} /> Dienst neu starten
</button>
{restartMsg && <p className="max-w-sm text-[11px] text-muted-foreground">{restartMsg}</p>}
</div> </div>
)} )}
<iframe src={url} title="Box-Konsole" className="h-full w-full border-0" style={{ minHeight: "58vh" }} /> <iframe src={url} title="Box-Konsole" className="h-full w-full border-0" style={{ minHeight: "58vh" }} />
+11 -14
View File
@@ -31,6 +31,12 @@ const BORDER_CLASSES: Record<string, string> = {
rules: "border-l-violet-500/80", events: "border-l-amber-500/80", rules: "border-l-violet-500/80", events: "border-l-amber-500/80",
} }
// Hex-Farben für Inline-Styles (Graph-Punkte, Detail-Panel) — Tailwind-Klassen aus
// CAT_CONFIG greifen dort nicht.
const CAT_COLOR_MAP: Record<string, string> = {
identity: "#00f5ff", knowledge: "#3b82f6", rules: "#d946ef", events: "#fbbf24",
}
// Onboarding-Prompt: der Nutzer startet damit ein Gespräch mit Hermes (Terminal/Telegram). // Onboarding-Prompt: der Nutzer startet damit ein Gespräch mit Hermes (Terminal/Telegram).
const ONBOARDING_PROMPT = `Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu: const ONBOARDING_PROMPT = `Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu:
1) wer ich bin und woran ich gerade arbeite, 1) wer ich bin und woran ich gerade arbeite,
@@ -142,7 +148,8 @@ export function MemoryView() {
function goTerminal() { function goTerminal() {
copyPrompt() copyPrompt()
window.dispatchEvent(new CustomEvent("mc-navigate", { detail: { view: "terminal" } })) // "konsole" ist die echte View-Id (nav.ts) — "terminal" existierte nie, der Knopf lief ins Leere.
window.dispatchEvent(new CustomEvent("mc-navigate", { detail: { view: "konsole" } }))
} }
async function cleanup() { async function cleanup() {
@@ -179,16 +186,6 @@ export function MemoryView() {
return allItems.filter((m) => connectedIds.has(m.id)) return allItems.filter((m) => connectedIds.has(m.id))
}, [sel, graph, allItems]) }, [sel, graph, allItems])
const CAT_COLOR_MAP: Record<string, string> = {
identity: "#00f5ff", knowledge: "#3b82f6", rules: "#d946ef", events: "#fbbf24",
}
const CAT_LABEL_MAP: Record<string, string> = {
identity: "Identität", knowledge: "Wissen", rules: "Regeln", events: "Ereignisse",
}
const AUTO = new Set(["auto", "agent", "hermes"])
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{empty ? ( {empty ? (
@@ -437,10 +434,10 @@ export function MemoryView() {
<div className="flex items-center gap-2 mb-3 shrink-0"> <div className="flex items-center gap-2 mb-3 shrink-0">
<span className="w-2.5 h-2.5 rounded-full" style={{ background: CAT_COLOR_MAP[sel.category] }} /> <span className="w-2.5 h-2.5 rounded-full" style={{ background: CAT_COLOR_MAP[sel.category] }} />
<span className="text-[9px] font-bold uppercase tracking-wider" style={{ color: CAT_COLOR_MAP[sel.category] }}> <span className="text-[9px] font-bold uppercase tracking-wider" style={{ color: CAT_COLOR_MAP[sel.category] }}>
{CAT_LABEL_MAP[sel.category] || sel.category} {CAT_CONFIG[sel.category]?.label ?? sel.category}
</span> </span>
<span className={`ml-auto text-[9px] font-mono flex items-center gap-1 ${AUTO.has(sel.source) ? "text-emerald-400" : "text-muted-foreground/70"}`}> <span className={`ml-auto text-[9px] font-mono flex items-center gap-1 ${AUTO_SOURCES.has(sel.source) ? "text-emerald-400" : "text-muted-foreground/70"}`}>
{AUTO.has(sel.source) && <Sparkles className="h-2.5 w-2.5" />} {AUTO_SOURCES.has(sel.source) && <Sparkles className="h-2.5 w-2.5" />}
{sel.source} {sel.source}
</span> </span>
<button onClick={() => setSelected(null)} className="text-muted-foreground hover:text-foreground ml-1.5 cursor-pointer"><X className="h-4 w-4" /></button> <button onClick={() => setSelected(null)} className="text-muted-foreground hover:text-foreground ml-1.5 cursor-pointer"><X className="h-4 w-4" /></button>
+3 -7
View File
@@ -391,7 +391,9 @@ function ToolListCard({ agent, svcErrors, onNavigate }: {
action: () => onNavigate("agent"), action: () => onNavigate("agent"),
}, },
{ {
label: "Direkte Box-Shell (SSH)", // EIN Eintrag für die Konsole (vorher standen "Direkte Box-Shell (SSH)" UND
// "Interaktives Terminal" in der Liste — zwei Namen, dasselbe Ziel).
label: "Konsole (Box-Shell)",
status: agent ? (agent.box_console_reachable ? "Bereit" : "Offline") : "…", status: agent ? (agent.box_console_reachable ? "Bereit" : "Offline") : "…",
tone: !agent ? "loading" : agent.box_console_reachable ? "ok" : "warn", tone: !agent ? "loading" : agent.box_console_reachable ? "ok" : "warn",
action: () => onNavigate("konsole"), action: () => onNavigate("konsole"),
@@ -402,12 +404,6 @@ function ToolListCard({ agent, svcErrors, onNavigate }: {
tone: svcErrors > 0 ? "alert" : "ok", tone: svcErrors > 0 ? "alert" : "ok",
action: () => openDrawer("logs"), action: () => openDrawer("logs"),
}, },
{
label: "Interaktives Terminal",
status: "Öffnen",
tone: "muted",
action: () => onNavigate("konsole"),
},
{ {
label: "Wissens-Vault", label: "Wissens-Vault",
status: "Öffnen", status: "Öffnen",
+5 -1
View File
@@ -224,7 +224,11 @@ export function Discover() {
<span>Bessere Version in der Registry: {hasUpgrade.repo.split("/").pop()}</span> <span>Bessere Version in der Registry: {hasUpgrade.repo.split("/").pop()}</span>
</div> </div>
<button <button
onClick={() => install(hasUpgrade.repo, cat.role, recommendedModel.quant || "Q4_K_M", recommendedModel.caps.tools !== "no")} // Quant des Empfehlungsmodells nur übernehmen, wenn das Upgrade DASSELBE
// Repo ist — sonst neutraler Default (fremde Quant könnte dort fehlen).
onClick={() => install(hasUpgrade.repo, cat.role,
hasUpgrade.repo === recommendedModel.repo ? (recommendedModel.quant || "Q4_K_M") : "Q4_K_M",
recommendedModel.caps.tools !== "no")}
disabled={!!installing[hasUpgrade.repo]} disabled={!!installing[hasUpgrade.repo]}
className="h-8 w-full flex items-center justify-center gap-1.5 rounded-lg bg-amber-500 text-black text-xs font-bold hover:bg-amber-400 disabled:opacity-50 transition-all cursor-pointer shadow-md shadow-amber-500/10" className="h-8 w-full flex items-center justify-center gap-1.5 rounded-lg bg-amber-500 text-black text-xs font-bold hover:bg-amber-400 disabled:opacity-50 transition-all cursor-pointer shadow-md shadow-amber-500/10"
> >