Es läuft immer nur ein Modell gleichzeitig — ein größerer Kontext hier beeinflusst deine anderen Modelle nicht.
'),vd=M(''),fd=M('
Rolle festlegen
Wähle eine Rolle als zweiten, sprechenden Namen. In deinen Tools funktionieren danach beide: der echte Modellname und die Rolle.
'),pd=M('
Modelle
Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.
Schnelltest
Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.
Modelle & Ports
Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
',1);function hd(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr (längere Dateien/Chats), braucht aber mehr Speicher und wird etwas langsamer.",r="Die Rolle ist ein zusätzlicher, sprechender Name (z.B. coder). Du kannst in deinen Tools entweder den echten Modellnamen ODER die Rolle angeben — beides führt zum selben Modell.",i=[{id:"coder",label:"Coder",desc:"Programmieren & Code"},{id:"vision",label:"Vision",desc:"Bilder verstehen"},{id:"scout",label:"Scout",desc:"Schneller Allrounder"},{id:"reviewer",label:"Reviewer",desc:"Code/Texte prüfen"},{id:"manager",label:"Manager",desc:"Planen & koordinieren"}],o=N(()=>{var w;return((w=dr.value)==null?void 0:w.models)||[]});function d(){document.dispatchEvent(new Event("mc:refresh"))}let c=R(""),u=R(""),f=R(""),g=R(!1);const $=N(()=>n(o).filter(w=>!w.incomplete));async function h(){var A;const w=n(c)||((A=n($)[0])==null?void 0:A.name);if(!(!w||!n(u).trim())){_(g,!0),_(f,"(wecke Modell, kann beim Laden kurz dauern…)");try{const q=await fe("/api/chat",{method:"POST",body:JSON.stringify({model:w,message:n(u)})});_(f,q.reply,!0)}catch(q){_(f,"Fehler: "+q.message)}_(g,!1),d()}}let b=R(!1),S=R(null),P=R(8192);function E(w){var A;_(S,n(o).find(q=>q.name===w),!0),n(S)&&(_(P,((A=n(S).meta)==null?void 0:A.ctx)||8192,!0),_(b,!0))}async function I(){try{await fe("/api/update_model",{method:"POST",body:JSON.stringify({alias:n(S).name,ctx:n(P)})}),Z("Gespeichert — aktiv beim nächsten Modell-Start."),_(b,!1),d()}catch(w){Z(w.message,!0)}}let de=R(!1),Y=R(""),ce=R(""),me=R("");function U(w){const A=n(o).find(q=>q.name===w);_(Y,w,!0),_(me,(A==null?void 0:A.role)||"",!0),_(ce,""),_(de,!0)}async function F(w){try{await fe("/api/set_role",{method:"POST",body:JSON.stringify({alias:n(Y),role:w})}),Z(w?`Rolle gesetzt: ${w}`:"Rolle entfernt."),_(de,!1),d()}catch(A){Z(A.message,!0)}}async function ie(w){if(await _n({title:`„${w}" entladen?`,body:"Das Modell wird aus dem Grafikspeicher geworfen und lädt beim nächsten Aufruf automatisch neu.",confirmLabel:"Entladen"}))try{await fe("/api/unload?model="+encodeURIComponent(w),{method:"POST"}),Z("Entladen: "+w),setTimeout(d,600)}catch(A){Z(A.message,!0)}}async function he(w){if(await _n({title:`„${w}" wirklich löschen?`,body:"Das Modell wird aus der Konfiguration ausgetragen und seine Dateien werden von der Festplatte gelöscht, um Speicher freizugeben. Das lässt sich nicht rückgängig machen — später brauchst du dafür einen neuen Download.",confirmLabel:"Endgültig löschen",danger:!0}))try{const A=await fe("/api/delete_model",{method:"POST",body:JSON.stringify({alias:w})});Z(A.note||"Gelöscht: "+w),setTimeout(d,600)}catch(A){Z(A.message,!0)}}function _e(w){return w!=null&&w.length?w.map(A=>A==="Code"?'Code':A==="Bild"?'Bild':'Text').join(" "):"–"}function Me(w){if(!w)return"–";const A=w.quant||"?",q=w.ctx?Math.round(w.ctx/1024)+"K":"?",ee=w.size_bytes?(w.size_bytes/1024**3).toFixed(1)+" GB":"?";return`${A} · ${q} · ${ee}`}function He(w){var A;(A=navigator.clipboard)==null||A.writeText(w),Z("Kopiert: "+w)}var T=pd(),J=l(ft(T),2),se=l(s(J),6);Ue(se,21,()=>n($),Ge,(w,A)=>{var q=td(),ee=s(q),O={};z(()=>{m(ee,n(A).name),O!==(O=n(A).name)&&(q.value=(q.__value=n(A).name)??"")}),x(w,q)});var $e=l(se,4),Q=l($e,2),be=s(Q),X=l(Q,2);{var ze=w=>{var A=nd(),q=s(A);z(()=>m(q,n(f))),x(w,A)};V(X,w=>{n(f)&&w(ze)})}var Se=l(J,2),Ie=s(Se),Ve=l(s(Ie)),Fe=s(Ve),mt=l(Ie,4);{var bt=w=>{var A=ad();x(w,A)},Kt=w=>{var A=dd(),q=l(s(A));Ue(q,21,()=>n(o),Ge,(ee,O)=>{var ue=Cr(),B=ft(ue);{var j=ae=>{var De=rd(),Ae=s(De),Ce=s(Ae),qe=l(Ae,5),Ye=s(qe),ot=l(Ye,2);z(()=>m(Ce,n(O).name)),C("click",Ye,()=>{var p;Z("Lade im Cookbook ein Modell und setze den Alias auf: "+n(O).name),(p=document.querySelector(".nav-item[data-view='cookbook']"))==null||p.dispatchEvent(new MouseEvent("click"))}),C("click",ot,()=>he(n(O).name)),x(ae,De)},te=ae=>{var De=od(),Ae=s(De),Ce=s(Ae),qe=s(Ce),Ye=l(qe);{var ot=xe=>{var We=id(),Ze=s(We);z(()=>m(Ze,n(O).role)),x(xe,We)};V(Ye,xe=>{n(O).role&&xe(ot)})}var p=l(Ce,2);{var D=xe=>{var We=sd(),Ze=s(We);z(()=>m(Ze,n(O).meta.filename)),x(xe,We)};V(p,xe=>{var We;(We=n(O).meta)!=null&&We.filename&&xe(D)})}var ge=l(p,2),re=l(s(ge),2);Ue(re,17,()=>{var xe;return(xe=n(O).api_ids)!=null&&xe.length?n(O).api_ids:[n(O).name]},Ge,(xe,We)=>{var Ze=ld(),Rt=s(Ze);z(()=>m(Rt,n(We))),C("click",Ze,()=>He(n(We))),x(xe,Ze)});var Le=l(Ae);Qe(Le,()=>{var xe;return _e((xe=n(O).meta)==null?void 0:xe.caps)},!0);var we=l(Le);Qe(we,()=>Me(n(O).meta),!0);var pt=l(we);Qe(pt,()=>Io(n(O).state,n(O).download_progress),!0);var dt=l(pt),St=s(dt),rt=l(dt),Et=s(rt),Dt=l(Et,2),Ot=l(Dt,2),Qt=l(Ot,2);z(()=>{m(qe,`${n(O).name??""} `),m(St,n(O).port??"auto")}),C("click",Et,()=>U(n(O).name)),C("click",Dt,()=>E(n(O).name)),C("click",Ot,()=>ie(n(O).name)),C("click",Qt,()=>he(n(O).name)),x(ae,De)};V(B,ae=>{n(O).incomplete?ae(j):ae(te,-1)})}x(ee,ue)}),x(w,A)};V(mt,w=>{n(o).length?w(Kt,-1):w(bt)})}var wt=l(Se,2);{var Mt=w=>{var A=ud(),q=s(A),ee=s(q),O=l(ee,4),ue=s(O),B=l(O,2),j=l(s(B));Qe(j,()=>si(a));var te=l(B,2),ae=l(te,2);{var De=Ce=>{var qe=cd(),Ye=s(qe),ot=l(s(Ye),2),p=s(ot),D=l(p,2),ge=l(Ye,2);z((re,Le)=>{m(p,`Optimal ~${re??""}k → ~${n(S).meta.peak_ram_optimal_gb??""} GB Spitzenbedarf.`),m(D,` Aktuell ~${Le??""}k → ~${n(S).meta.peak_ram_gb??""} GB.`)},[()=>Math.round(n(S).meta.optimal_ctx/1024),()=>Math.round((n(S).meta.ctx||0)/1024)]),C("click",ge,()=>_(P,n(S).meta.optimal_ctx,!0)),x(Ce,qe)};V(ae,Ce=>{var qe;(qe=n(S).meta)!=null&&qe.optimal_ctx&&Ce(De)})}var Ae=l(ae,4);z(()=>m(ue,n(S).name)),C("click",A,Ce=>{Ce.target.classList.contains("modal-overlay")&&_(b,!1)}),C("click",ee,()=>_(b,!1)),Mn(te,()=>n(P),Ce=>_(P,Ce)),C("click",Ae,I),x(w,A)};V(wt,w=>{n(b)&&n(S)&&w(Mt)})}var L=l(wt,2);{var K=w=>{var A=fd(),q=s(A),ee=s(q),O=l(ee,2),ue=l(s(O));Qe(ue,()=>si(r));var B=l(O,2),j=s(B),te=l(B,4);Ue(te,21,()=>i,Ge,(qe,Ye)=>{var ot=vd(),p=s(ot);z(()=>{st(ot,1,n(me)===n(Ye).id?"primary":"ghost"),Lr(ot,"title",n(Ye).desc),m(p,n(Ye).label)}),C("click",ot,()=>F(n(Ye).id)),x(qe,ot)});var ae=l(te,4),De=l(ae,2),Ae=s(De),Ce=l(Ae,2);z(()=>m(j,n(Y))),C("click",A,qe=>{qe.target.classList.contains("modal-overlay")&&_(de,!1)}),C("click",ee,()=>_(de,!1)),Mn(ae,()=>n(ce),qe=>_(ce,qe)),C("click",Ae,()=>F("")),C("click",Ce,()=>F(n(ce).trim().toLowerCase())),x(w,A)};V(L,w=>{n(de)&&w(K)})}z(()=>{Q.disabled=n(g),m(be,n(g)?"…":"Senden"),m(Fe,n(o).length?n(o).length+" konfiguriert":"")}),Wa(se,()=>n(c),w=>_(c,w)),Mn($e,()=>n(u),w=>_(u,w)),C("click",Q,h),x(e,T),Zn()}_a(["click"]);var gd=M('
Prozessor (CPU)
%
Arbeitsspeicher
%
Grafikspeicher
%
System-Metriken (Bosgame)
Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
Arbeitsspeicher (RAM)
Grafikspeicher (VRAM + GTT)
Speicherplatz (Disk)
Temperatur (GPU / CPU)
CPU-Verlauf
RAM-Verlauf
VRAM-Verlauf
',1),_d=M(''),md=M('
Gerade nichts los.
Alles ruhig — keine laufenden Aufgaben.
'),bd=M(''),wd=M(''),yd=M(''),xd=M('
'),kd=M('
'),Md=M('
'),Sd=M('
Aktivität
Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
Hintergrund-Aufgaben
Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
LLM-Engine (llama-swap)
Das Herzstück: lädt bei jeder Anfrage automatisch das passende Modell.
Adresse (für deine Tools)
Aktuell geladen
Konfigurierte Modelle
Dienste & Applikation
Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
Betriebssystem (Bosgame)
Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
Live-Konsole
Live mitlesen, was der Dienst gerade tut.
',1);function Rd(e,t){Yn(t,!0);const a=N(()=>dr.value),r=N(()=>ra.value);let i=null,o=R(`Verbinde…
-`),d=R("llama-swap"),c,u=R(null),f=R("");const g=new Set,$=N(()=>n(u)?n(r).find(p=>p.id===n(u)):null);gi(()=>{if(!n($))return;const p=n($),D=p.state==="done",ge=p.state==="failed",re=p.state==="canceled";(D||ge||re)&&!g.has(p.id)&&(g.add(p.id),Z(D?`✓ ${n(f)} abgeschlossen.`:re?`${n(f)} abgebrochen.`:`✗ ${n(f)} fehlgeschlagen.`,ge),D&&setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),800))});function h(p,D){_(u,p,!0),_(f,D,!0),ra.track(p)}or(()=>{b()}),Oo(()=>{i==null||i.close()});function b(){i==null||i.close();const p=n(d);_(o,`Verbinde mit ${p}…
-`);const D=location.protocol==="https:"?"wss:":"ws:";i=new WebSocket(`${D}//${location.host}/api/logs/${p}?token=${encodeURIComponent(Ws())}`),i.onmessage=ge=>{_(o,n(o)+ge.data),c&&(c.scrollTop=c.scrollHeight)},i.onclose=()=>{_(o,n(o)+`
-— Verbindung getrennt —`)},i.onerror=()=>i==null?void 0:i.close()}async function S(p,D,ge){if(await _n({title:D,body:ge,confirmLabel:"Neustarten"}))try{await fe(`/api/service/${p}/restart`,{method:"POST"}),Z("Neustart ausgelöst: "+p),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),2e3)}catch(re){Z(re.message,!0)}}async function P(){if(await _n({title:"Mission Control aktualisieren?",body:"Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)."}))try{const p=await fe("/api/self-update",{method:"POST"});Z("Update läuft — Dashboard startet gleich neu."),h(p.job_id,"Mission Control Update")}catch(p){Z(p.message,!0)}}async function E(){if(await _n({title:"LLM-Engine aktualisieren?",body:"Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar."}))try{const p=await fe("/api/update",{method:"POST"});Z("LLM-Engine-Update läuft."),h(p.job_id,"LLM-Engine-Update")}catch(p){Z(p.message,!0)}}async function I(){if(await _n({title:"Grafikspeicher leeren?",body:"Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Leeren"}))try{await fe("/api/unload",{method:"POST"}),Z("Grafikspeicher geleert."),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),600)}catch(p){Z(p.message,!0)}}async function de(){if(!await _n({title:"OS-Updates installieren?",body:"Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität."}))return;const p=await Ni({title:"sudo-Passwort",body:"Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Installieren"});if(p)try{const D=await fe("/api/os-update",{method:"POST",body:JSON.stringify({password:p})});Z("OS-Update gestartet."),h(D.job_id,"OS-Update")}catch(D){Z(D.message,!0)}}async function Y(){if(!await _n({title:"Server wirklich neu starten?",body:"Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.",confirmLabel:"Reboot",danger:!0}))return;const p=await Ni({title:"sudo-Passwort",body:"Für den Reboot wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Jetzt neustarten",danger:!0});if(p)try{await fe("/api/reboot",{method:"POST",body:JSON.stringify({password:p})}),Z("Reboot ausgelöst — bis gleich.")}catch(D){Z(D.message,!0)}}async function ce(){_(me,"prüfe…");try{const p=await fe("/api/integration/test");_(me,p.ok?`✓ Engine antwortet — ${p.models.length} Modell(e): ${p.models.join(", ")}`:`✗ keine Verbindung: ${p.error}`,!0),_(U,p.ok,!0)}catch(p){_(me,"✗ "+p.message),_(U,!1)}}let me=R(""),U=R(!1);const F=N(()=>{var p;return((p=n(a))==null?void 0:p.models)||[]}),ie=N(()=>n(F).find(p=>p.state==="running"||p.state==="loading"));function he(p){if(!p||p.state!=="running")return null;if(typeof p.progress=="number")return Math.min(100,p.progress);const ge=((p.log||[]).at(-1)||"").match(/(\d+(?:\.\d+)?)\s*%/);return ge?Math.min(100,parseFloat(ge[1])):null}var _e=Od(),Me=l(ft(_e),2),He=s(Me),T=l(s(He),2),J=s(T),se=l(He,4),$e=s(se),Q=l(s($e)),be=s(Q),X=l($e,2),ze=l(s(X),2),Se=s(ze),Ie=l(X,2),Ve=l(s(Ie),2),Fe=s(Ve),mt=l(se,2),bt=s(mt),Kt=l(bt,2);{var wt=p=>{var D=Td(),ge=s(D);z(()=>{jn(D,`color:${n(U)?"#7ee29a":"#ff9b95"}`),m(ge,n(me))}),x(p,D)};V(Kt,p=>{n(me)&&p(wt)})}var Mt=l(Me,2),L=l(s(Mt),4),K=l(L,2),w=l(K,2),A=l(w,2),q=l(A,2),ee=l(Mt,2),O=l(s(ee),4),ue=l(O,2),B=l(ee,2);{var j=p=>{const D=N(()=>n($)),ge=N(()=>n(D).state==="done"),re=N(()=>n(D).state==="failed"),Le=N(()=>n(D).state==="canceled"),we=N(()=>n(ge)||n(re)||n(Le)),pt=N(()=>he(n(D)));var dt=Dd(),St=s(dt),rt=l(s(St),2);{var Et=Oe=>{var Tt=$d();C("click",Tt,async()=>{await _n({title:n(f)+" abbrechen?",body:"Der laufende Vorgang wird gestoppt.",confirmLabel:"Abbrechen",danger:!0})&&fe(`/api/jobs/${n(D).id}/cancel`,{method:"POST"})}),x(Oe,Tt)};V(rt,Oe=>{n(we)||Oe(Et)})}var Dt=l(rt,2),Ot=s(Dt);{var Qt=Oe=>{var Tt=Ad();x(Oe,Tt)},xe=Oe=>{var Tt=Cd();x(Oe,Tt)},We=Oe=>{var Tt=Ld();x(Oe,Tt)},Ze=Oe=>{var Tt=Pd();x(Oe,Tt)};V(Ot,Oe=>{n(ge)?Oe(Qt):n(re)?Oe(xe,1):n(Le)?Oe(We,2):Oe(Ze,-1)})}var Rt=l(St,2),Pn=s(Rt),yn=l(Rt,2);{var rn=Oe=>{var Tt=zd(),Dr=s(Tt);z(()=>jn(Dr,`width:${n(pt)??""}%`)),x(Oe,Tt)};V(yn,Oe=>{n(pt)!=null&&!n(we)&&Oe(rn)})}var yt=l(yn,2),xn=s(yt);z(Oe=>{m(Pn,`${n(f)??""}${n(we)?n(ge)?" — erfolgreich.":n(Le)?" — abgebrochen.":" — fehlgeschlagen.":" läuft…"}`),m(xn,Oe)},[()=>(n(D).log||[]).join(`
-`)]),x(p,dt)};V(B,p=>{n($)&&p(j)})}var te=l(B,2),ae=s(te),De=l(s(ae),2),Ae=s(De);Ae.value=Ae.__value="llama-swap";var Ce=l(Ae);Ce.value=Ce.__value="mission-control";var qe=l(Ce);qe.value=qe.__value="system";var Ye=l(ae,4),ot=s(Ye);Do(Ye,p=>c=p,()=>c),z(p=>{var D,ge,re;jn(T,`margin-left:auto;${(D=n(a))!=null&&D.swap_ok?"background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a":"background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95"}`),m(J,(ge=n(a))!=null&&ge.swap_ok?"● erreichbar":"● offline"),m(be,(((re=n(a))==null?void 0:re.swap_url)||"—")+"/v1"),m(Se,n(ie)?n(ie).name+` (${n(ie).state})`:"nichts geladen — lädt bei der nächsten Anfrage"),m(Fe,p),m(ot,n(o))},[()=>n(F).filter(p=>!p.incomplete).length||"keine"]),C("click",bt,ce),C("click",L,()=>S("llama-swap","LLM-Engine neustarten?","Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder.")),C("click",K,()=>S("mission-control","Dashboard neustarten?","Diese Oberfläche trennt sich kurz und verbindet automatisch wieder.")),C("click",w,P),C("click",A,E),C("click",q,I),C("click",O,de),C("click",ue,Y),C("change",De,b),Wa(De,()=>n(d),p=>_(d,p)),x(e,_e),Zn()}_a(["click","change"]);var Bd=M('
Lade Setups…
'),qd=M('✎×',1),Gr=M('
★ Beste Wahl für dein System
'),Nd=M('
Dein Setup
'),Id=M(''),Fd=M(''),Kd=M('
Frage Quellen ab…
'),Gd=M('
'),Ud=M('✓ installiert'),Hd=M(''),Vd=M('
'),Wd=M('
'),jd=M(''),Jd=M('
'),Qd=M(''),Yd=M('
'),Zd=M('
'),Xd=M(""),ec=M('
'),tc=M('
Größter Spitzenbedarf: . Es läuft immer nur ein Modell gleichzeitig — das größte bestimmt, ob das Setup passt.
'),nc=M('
'),ac=M('
Lade Dateien von HuggingFace…
'),Ui=M(""),rc=M('
Empfohlener Kontext für deine Hardware: — übernehmen
'),ic=M('
Ressourcen-Check
'),sc=M('
',1),lc=M('
Keine GGUF-Dateien im Repo gefunden.
'),oc=M('
'),dc=M('
'),cc=M('
Stell dir aus beliebigen Modellen ein eigenes Setup zusammen.
Die Repo-ID findest du über die Profi-Suche weiter unten (z.B. unsloth/Qwen3-8B-GGUF).
'),uc=M('
Cookbook
Modelle finden, installieren und eigene Setups zusammenstellen.
Use-Case-Setups
Fertige Modell-Bundles für deinen Anwendungsfall. Hardware-Ampel zeigt, ob es auf deinen PC passt.
Aktuell die besten Modelle für dein System
Profi-Suche (HuggingFace)
Direkt HuggingFace durchsuchen — oder eine Modell-URL einfügen für den direkten Sprung.
',1);function vc(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.",r="MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU.",i={perfect:0,marginal:1,too_tight:2},o={vision:"Bilder",coder:"Coden",reasoning:"Logik",agent:"Agenten & Tools",scout:"Allrounder",reviewer:"Review",manager:"Manager"},d=[{label:"Bilder",kw:["-vl-","-vl","vision","llava","multimodal","-mm-","pixtral"]},{label:"Coden",kw:["coder","-code-","code-","codestral","starcoder"]},{label:"Logik",kw:["-r1","deepseek-r1","reasoning","qwq","magistral","-think","thinking","-o1"]},{label:"Agenten & Tools",kw:["hermes","-tool","command-r","watt","-fc-","function"]}];function c(y){const k=(y||"").toLowerCase();for(const H of d)if(H.kw.some(G=>k.includes(G)))return H.label;return"Allrounder"}function u(y=""){const k=y.toLowerCase();return/\d+x\d+(\.\d+)?b/.test(k)||new RegExp("(?{bt(),Kt()});async function bt(){_(P,!0);try{const y=await fe("/api/cookbook/recipes");_(b,y.recipes||[],!0),_(S,y.recommended_id||null,!0)}catch(y){Z(y.message,!0)}_(P,!1)}async function Kt(y=!1){_(I,!0);try{const k=await fe("/api/cookbook/discover"+(y?"?force=true":""));_(E,k,!0),_(de,k.stale,!0)}catch(k){Z("Empfehlungen nicht ladbar: "+k.message,!0)}_(I,!1)}function wt(y){y=y.trim();const k=y.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);return k?k[1]:/^[\w.-]+\/[\w.-]+$/.test(y)?y:null}async function Mt(){const y=n(Y).trim(),k=wt(y);let H=y;if(n(U)&&!k&&(H=H?H+" "+n(U):n(U)),!H&&!k){_(ce,[],!0);return}_(me,!0),_(ie,[],!0),_(he,-1);try{if(k)_(ce,[{id:k,author:k.split("/")[0],downloads:0}],!0);else{const G=`https://huggingface.co/api/models?search=${encodeURIComponent(H)}&filter=gguf&sort=${encodeURIComponent(n(F))}&direction=-1&limit=40`,le=await fetch(G);_(ce,await le.json(),!0)}n(ce).forEach((G,le)=>L(le,G.id))}catch(G){Z(G.message,!0),_(ce,[],!0)}_(me,!1)}async function L(y,k){var H;try{const G=await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:k,ctx:8192})});if(!((H=G.files)!=null&&H.length)){n(ie)[y]=null,K();return}const le=G.files.find(Je=>{var Xe;return(Xe=Je.quant)==null?void 0:Xe.includes("Q4_K_M")})||G.files[0];n(ie)[y]=le.fit.level,K()}catch{n(ie)[y]=null,K()}}function K(){let y=-1,k=99;n(ie).forEach((H,G)=>{if(H==null)return;const le=i[H]??99;le=0&&k<=1?y:-1,!0)}function w(y){_(_e,n(b).find(k=>k.id===y),!0)}async function A(y){const k=document.getElementById("cb-r-install-btn");k&&(k.disabled=!0,k.textContent="Starte…");try{const H=await fe("/api/cookbook/install-recipe",{method:"POST",body:JSON.stringify({recipe_id:y,hf_token:Ja()})});Z(`${H.count} Downloads gestartet — siehe Aktivität.`),_(_e,null),mt("activity")}catch(H){Z(H.message,!0),k&&(k.disabled=!1,k.textContent="Installieren")}}async function q(y,k){var H,G;_(Me,!0),_(He,k,!0),_(T,y,!0),_($e,""),_(Q,8192),_(J,null),_(X,null),_(se,""),_(be,!0);try{if(_(J,await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:y,ctx:8192})}),!0),(G=(H=n(J))==null?void 0:H.files)!=null&&G.length){const le=n(J).files.find(Je=>{var Xe;return(Xe=Je.quant)==null?void 0:Xe.includes("Q4_K_M")})||n(J).files[0];_(se,le.filename,!0),le.optimal_ctx&&n(Q)===8192&&_(Q,le.optimal_ctx,!0),ee()}}catch(le){Z(le.message,!0)}_(be,!1)}function ee(){var y,k,H;!((k=(y=n(J))==null?void 0:y.files)!=null&&k.length)||!n(se)||(_(X,n(J).files.find(G=>G.filename===n(se))||null,!0),(H=n(X))!=null&&H.optimal_ctx&&n(Q)===8192&&_(Q,n(X).optimal_ctx,!0))}async function O(){if(!n(T)||!n(se))return Z("Bitte eine GGUF-Datei wählen.",!0);_(be,!0);try{const y=await fe("/api/download",{method:"POST",body:JSON.stringify({repo:n(T),file:n(se),hf_token:Ja()})});await fe("/api/register",{method:"POST",body:JSON.stringify({role:n($e),model_path:y.expected_path,ctx:n(Q)})}),Z("Download gestartet — siehe Aktivität."),_(Me,!1),mt("activity")}catch(y){Z("Fehler: "+y.message,!0)}_(be,!1)}async function ue(y,k,H,G){G.disabled=!0,G.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:y,role:k,params_b:H,quant:"Q4_K_M",hf_token:Ja()})}),Z("Download gestartet — siehe Aktivität."),mt("activity")}catch(le){Z(le.message,!0),G.disabled=!1,G.textContent="Installieren"}}function B(){_(Fe,null),_(Se,""),_(Ie,""),_(Ve,[{repo:"",role:"",quant:"Q4_K_M"}],!0),_(ze,!0)}function j(y){const k=n(b).find(H=>H.id===y);k&&(_(Fe,y,!0),_(Se,k.title,!0),_(Ie,k.desc||"",!0),_(Ve,k.models.map(H=>({repo:H.repo,role:H.role,quant:H.quant||"Q4_K_M"})),!0),_(ze,!0))}async function te(){if(!n(Se).trim())return Z("Bitte einen Titel angeben.",!0);const y=n(Ve).filter(k=>k.repo.trim());if(!y.length)return Z("Mindestens ein Modell (Repo) angeben.",!0);try{n(Fe)?(await fe("/api/cookbook/user-recipe/"+encodeURIComponent(n(Fe)),{method:"PUT",body:JSON.stringify({title:n(Se),desc:n(Ie),models:y})}),Z("Setup aktualisiert.")):(await fe("/api/cookbook/user-recipe",{method:"POST",body:JSON.stringify({title:n(Se),desc:n(Ie),models:y})}),Z("Setup gespeichert.")),_(ze,!1),bt()}catch(k){Z(k.message,!0)}}async function ae(y){const k=n(b).find(H=>H.id===y);if(await _n({title:`„${(k==null?void 0:k.title)||y}" löschen?`,body:"Dein eigenes Setup wird aus dem Cookbook entfernt. Bereits installierte Modelle bleiben unangetastet.",confirmLabel:"Löschen",danger:!0}))try{await fe("/api/cookbook/user-recipe/"+encodeURIComponent(y),{method:"DELETE"}),Z("Setup gelöscht."),bt()}catch(H){Z(H.message,!0)}}function De(y){if(!u(y))return"";const k=f(y);return`MoE${k?` · ~${k}B aktiv`:""}`}function Ae(y){return y?`${An(y)}`:""}var Ce=uc(),qe=l(ft(Ce),2),Ye=s(qe),ot=l(s(Ye),2),p=l(Ye,4);{var D=y=>{var k=Bd();x(y,k)},ge=y=>{var k=Fd();Ue(k,21,()=>n(b),Ge,(H,G)=>{const le=N(()=>n(G).id===n(S));var Je=Id(),Xe=s(Je);{var Yt=Be=>{var tt=qd(),Zt=ft(tt),ct=l(Zt,2);C("click",Zt,ut=>{ut.stopPropagation(),j(n(G).id)}),C("keydown",Zt,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),j(n(G).id))}),C("click",ct,ut=>{ut.stopPropagation(),ae(n(G).id)}),C("keydown",ct,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),ae(n(G).id))}),x(Be,tt)};V(Xe,Be=>{n(G).user&&Be(Yt)})}var Gt=l(Xe,2);{var Ut=Be=>{var tt=Gr();x(Be,tt)},Ht=Be=>{var tt=Nd();x(Be,tt)};V(Gt,Be=>{n(le)?Be(Ut):n(G).user&&Be(Ht,1)})}var Pe=l(Gt,2),ke=s(Pe),Ne=s(ke),Ke=s(Ne),Re=l(ke,2),ht=s(Re),xt=l(Pe,2),$t=s(xt),At=l(xt,2),et=s(At);z((Be,tt)=>{st(Je,1,`card-btn${n(le)?" cb-best":""}`),m(Ke,n(G).title),st(Re,1,`fit-badge ${Be??""}`),m(ht,tt),m($t,n(G).desc),m(et,`${n(G).models.length??""} Modell${n(G).models.length>1?"e":""} im Setup`)},[()=>g(n(G).fit_level),()=>$(n(G).fit_level)]),C("click",Je,()=>w(n(G).id)),x(H,Je)}),x(y,k)};V(p,y=>{n(P)?y(D):y(ge,-1)})}var re=l(qe,2),Le=s(re),we=l(s(Le),2),pt=s(we),dt=l(we,2),St=s(dt),rt=l(Le,2),Et=s(rt),Dt=l(rt,2);{var Ot=y=>{var k=Kd();x(y,k)},Qt=y=>{var k=Cr(),H=ft(k);Ue(H,17,()=>n(E).categories,Ge,(G,le)=>{var Je=Wd(),Xe=s(Je),Yt=s(Xe),Gt=s(Yt),Ut=l(Xe,2);Ue(Ut,21,()=>n(le).models,Ge,(Ht,Pe)=>{const ke=N(()=>n(le).recommended&&n(Pe).repo===n(le).recommended);var Ne=Vd(),Ke=s(Ne);{var Re=kt=>{var Nt=Gr();x(kt,Nt)};V(Ke,kt=>{n(ke)&&kt(Re)})}var ht=l(Ke,2),xt=s(ht),$t=s(xt),At=s($t),et=l($t,2),Be=s(et),tt=l(xt,2),Zt=s(tt),ct=l(ht,2),ut=s(ct);Qe(ut,()=>Ae(o[n(Pe).role]||c(n(Pe).repo)));var kn=l(ut,2);Qe(kn,()=>De(n(Pe).repo));var qn=l(ct,2),je=s(qn),nt=l(qn,2);Ue(nt,17,()=>n(Pe).requirements||[],Ge,(kt,Nt)=>{var Nn=Gd(),Or=s(Nn);z(()=>m(Or,`⚠ ${n(Nt)??""}`)),x(kt,Nn)});var it=l(nt,4),vt=s(it);{var Xt=kt=>{var Nt=Ud();z(()=>Lr(Nt,"title",`bereits eingerichtet als ${n(Pe).installed??""}`)),x(kt,Nt)},qt=kt=>{var Nt=Hd();C("click",Nt,Nn=>ue(n(Pe).repo,n(Pe).role,n(Pe).params_b,Nn.currentTarget)),x(kt,Nt)};V(vt,kt=>{n(Pe).installed?kt(Xt):kt(qt,-1)})}z((kt,Nt,Nn)=>{st(Ne,1,`card${n(ke)?" res-best":""}`),m(At,n(Pe).name),m(Be,`${n(Pe).author??""} · ${n(Pe).params_b??""}B`),st(tt,1,`fit-badge ${kt??""}`),m(Zt,n(Pe).fit.text),m(je,`${Nt??""} · ⬇ ${Nn??""}`)},[()=>g(n(Pe).fit.level),()=>h(n(Pe).fit),()=>(n(Pe).downloads||0).toLocaleString()]),x(Ht,Ne)}),z(()=>m(Gt,n(le).title)),x(G,Je)}),x(y,k)};V(Dt,y=>{var k;n(I)&&!n(E)?y(Ot):(k=n(E))!=null&&k.categories&&y(Qt,1)})}var xe=l(re,2),We=l(s(xe),4),Ze=s(We),Rt=l(Ze,2),Pn=s(Rt);Pn.value=Pn.__value="downloads";var yn=l(Pn);yn.value=yn.__value="trending";var rn=l(yn);rn.value=rn.__value="created_at";var yt=l(Rt,2),xn=s(yt),Oe=l(We,2);Ue(Oe,20,()=>["","coder","vision","reasoning","chat","agent"],Ge,(y,k)=>{var H=jd(),G=s(H);z(()=>{st(H,1,n(U)===k?"primary":"ghost"),m(G,k||"Alle")}),C("click",H,()=>{_(U,k,!0),Mt()}),x(y,H)});var Tt=l(Oe,2);{var Dr=y=>{var k=Qd();Ue(k,21,()=>n(ce),Ge,(H,G,le)=>{var Je=Jd(),Xe=s(Je);{var Yt=ct=>{var ut=Gr();x(ct,ut)};V(Xe,ct=>{le===n(he)&&ct(Yt)})}var Gt=l(Xe,2),Ut=s(Gt),Ht=s(Ut),Pe=s(Ht),ke=l(Ht,2),Ne=s(ke),Ke=l(Ut,2),Re=s(Ke),ht=l(Gt,2),xt=s(ht);Qe(xt,()=>Ae(c(n(G).id)));var $t=l(xt,2);Qe($t,()=>De(n(G).id));var At=l(ht,4),et=s(At),Be=s(et),tt=l(et,2),Zt=s(tt);z((ct,ut,kn,qn,je)=>{st(Je,1,`card${le===n(he)?" res-best":""}`),m(Pe,ct),m(Ne,n(G).author||""),st(Ke,1,`fit-badge ${ut??""}`),m(Re,kn),m(Be,qn),m(Zt,`⬇ ${je??""}`)},[()=>n(G).id.split("/").pop(),()=>n(ie)[le]?g(n(ie)[le]):"warn",()=>n(ie)[le]?$(n(ie)[le]):"prüfe…",()=>n(ie)[le]?h({req_gb:0,tps:0}):"Hardware-Fit…",()=>(n(G).downloads||0).toLocaleString()]),C("click",Je,()=>q(n(G).id,n(G).id.split("/").pop())),x(H,Je)}),x(y,k)};V(Tt,y=>{n(ce).length&&y(Dr)})}var xi=l(xe,2);{var Ys=y=>{const k=N(()=>n(_e));var H=nc(),G=s(H),le=s(G),Je=l(le,2),Xe=s(Je),Yt=l(Je,2),Gt=s(Yt),Ut=l(Yt,2);Ue(Ut,21,()=>n(k).models,Ge,(Ke,Re)=>{var ht=ec(),xt=s(ht),$t=s(xt),At=s($t),et=s(At),Be=l(At,2),tt=s(Be),Zt=l(Be,2);Qe(Zt,()=>De(n(Re).repo));var ct=l($t,2);{var ut=it=>{var vt=Yd(),Xt=s(vt);z(()=>m(Xt,n(Re).why)),x(it,vt)};V(ct,it=>{n(Re).why&&it(ut)})}var kn=l(ct,2);{var qn=it=>{var vt=Zd(),Xt=s(vt);z((qt,kt,Nt)=>m(Xt,`~${qt??""} GB · ~${kt??""} Tok/s · optimal ~${Nt??""}k Kontext`),[()=>(n(Re).fit.req_gb??0).toFixed(1),()=>Math.round(n(Re).fit.tps??0),()=>Math.round((n(Re).optimal_ctx||8192)/1024)]),x(it,vt)};V(kn,it=>{n(Re).fit&&it(qn)})}var je=l(xt,2);{var nt=it=>{var vt=Xd(),Xt=s(vt);z(qt=>{st(vt,1,`fit-badge ${qt??""}`),m(Xt,n(Re).fit.text)},[()=>g(n(Re).fit.level)]),x(it,vt)};V(je,it=>{n(Re).fit&&it(nt)})}z(()=>{m(et,n(Re).name),m(tt,n(Re).role)}),x(Ke,ht)});var Ht=l(Ut,2);{var Pe=Ke=>{const Re=N(()=>Math.max(...n(k).models.map(At=>{var et;return((et=At.fit)==null?void 0:et.req_gb)??0})));var ht=tc(),xt=l(s(ht)),$t=s(xt);z(At=>m($t,`~${At??""} GB`),[()=>n(Re).toFixed(1)]),x(Ke,ht)};V(Ht,Ke=>{n(k).models.length&&n(k).models[0].fit&&Ke(Pe)})}var ke=l(Ht,2),Ne=s(ke);z(()=>{m(Xe,n(k).title),m(Gt,n(k).desc),st(ke,1,`primary${n(k).fit_level==="too_tight"?" warn":""}`),m(Ne,n(k).fit_level==="too_tight"?"Trotzdem installieren (zu groß)":"Komplettes Setup installieren")}),C("click",H,Ke=>{Ke.target.classList.contains("modal-overlay")&&_(_e,null)}),C("click",le,()=>_(_e,null)),C("click",ke,()=>A(n(k).id)),x(y,H)};V(xi,y=>{n(_e)&&y(Ys)})}var ki=l(xi,2);{var Zs=y=>{var k=oc(),H=s(k),G=s(H),le=l(G,2),Je=s(le),Xe=l(le,2),Yt=s(Xe),Gt=l(Xe,2);{var Ut=ke=>{var Ne=ac();x(ke,Ne)},Ht=ke=>{var Ne=sc(),Ke=l(ft(Ne),2);Ue(Ke,21,()=>n(J).files,Ge,(je,nt)=>{const it=N(()=>n(nt).fit.level==="perfect"?"●":n(nt).fit.level==="marginal"?"◐":"○");var vt=Ui(),Xt=s(vt),qt={};z(()=>{m(Xt,`${n(it)??""} ${n(nt).filename??""}`),qt!==(qt=n(nt).filename)&&(vt.value=(vt.__value=n(nt).filename)??"")}),x(je,vt)});var Re=l(Ke,2),ht=s(Re),xt=l(s(ht),2),$t=l(ht,2),At=s($t),et=l(s(At));Qe(et,()=>si(a));var Be=l(At,2),tt=l(Re,2);{var Zt=je=>{var nt=rc(),it=l(s(nt)),vt=s(it),Xt=l(it,2);z(qt=>m(vt,`~${qt??""}k`),[()=>Math.round(n(X).optimal_ctx/1024)]),C("click",Xt,qt=>{qt.preventDefault(),_(Q,n(X).optimal_ctx,!0)}),x(je,nt)};V(tt,je=>{var nt;(nt=n(X))!=null&&nt.optimal_ctx&&je(Zt)})}var ct=l(tt,2);{var ut=je=>{var nt=ic(),it=s(nt),vt=l(s(it),2),Xt=s(vt),qt=l(it,2),kt=s(qt);z((Nt,Nn,Or)=>{m(Xt,`~${Nt??""} GB · ~${Nn??""} Tok/s · ${(n(X).quant||"GGUF")??""}`),st(qt,1,`fit-badge ${Or??""}`),m(kt,n(X).fit.text)},[()=>(n(X).fit.req_gb??0).toFixed(1),()=>Math.round(n(X).fit.tps??0),()=>g(n(X).fit.level)]),x(je,nt)};V(ct,je=>{n(X)&&je(ut)})}var kn=l(ct,2),qn=s(kn);z(()=>{var je,nt;st(kn,1,`primary${((je=n(X))==null?void 0:je.fit.level)==="too_tight"?" warn":""}`),kn.disabled=n(be),m(qn,n(be)?"Starte…":((nt=n(X))==null?void 0:nt.fit.level)==="too_tight"?"Trotzdem holen (zu groß)":"Herunterladen & Einpflegen")}),C("change",Ke,ee),Wa(Ke,()=>n(se),je=>_(se,je)),Mn(xt,()=>n($e),je=>_($e,je)),Mn(Be,()=>n(Q),je=>_(Q,je)),C("click",kn,O),x(ke,Ne)},Pe=ke=>{var Ne=lc();x(ke,Ne)};V(Gt,ke=>{var Ne,Ke;n(be)&&!n(J)?ke(Ut):(Ke=(Ne=n(J))==null?void 0:Ne.files)!=null&&Ke.length?ke(Ht,1):n(be)||ke(Pe,2)})}z(()=>{m(Je,n(He)),m(Yt,n(T))}),C("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&_(Me,!1)}),C("click",G,()=>_(Me,!1)),x(y,k)};V(ki,y=>{n(Me)&&y(Zs)})}var Xs=l(ki,2);{var el=y=>{var k=cc(),H=s(k),G=s(H),le=l(G,2),Je=s(le),Xe=l(le,6),Yt=l(Xe,4),Gt=l(Yt,4);Ue(Gt,17,()=>n(Ve),Ge,(ke,Ne,Ke)=>{var Re=dc(),ht=s(Re),xt=l(ht,2),$t=l(xt,2);Ue($t,20,()=>["Q4_K_M","Q5_K_M","Q6_K","Q8_0","Q3_K_M"],Ge,(et,Be)=>{var tt=Ui(),Zt=s(tt),ct={};z(()=>{m(Zt,Be),ct!==(ct=Be)&&(tt.__value=Be)}),x(et,tt)});var At=l($t,2);Mn(ht,()=>n(Ne).repo,et=>n(Ne).repo=et),Mn(xt,()=>n(Ne).role,et=>n(Ne).role=et),Wa($t,()=>n(Ne).quant,et=>n(Ne).quant=et),C("click",At,()=>_(Ve,n(Ve).filter((et,Be)=>Be!==Ke),!0)),x(ke,Re)});var Ut=l(Gt,2),Ht=l(Ut,4),Pe=s(Ht);z(()=>{m(Je,n(Fe)?"Setup bearbeiten":"Eigenes Setup erstellen"),m(Pe,n(Fe)?"Änderungen speichern":"Setup speichern")}),C("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&_(ze,!1)}),C("click",G,()=>_(ze,!1)),Mn(Xe,()=>n(Se),ke=>_(Se,ke)),Mn(Yt,()=>n(Ie),ke=>_(Ie,ke)),C("click",Ut,()=>_(Ve,[...n(Ve),{repo:"",role:"",quant:"Q4_K_M"}],!0)),C("click",Ht,te),x(y,k)};V(Xs,y=>{n(ze)&&y(el)})}z(()=>{m(pt,n(E)?"aktuell":""),dt.disabled=n(I),m(St,n(I)?"Suche…":"Aktualisieren"),m(Et,`Täglich aktualisierte Top-Modelle je Kategorie — Hardware-Fit berechnet für dein System. ${n(de)?"⚠ Cache veraltet.":""}`),yt.disabled=n(me),m(xn,n(me)?"Lade…":"Suchen")}),C("click",ot,B),C("click",dt,()=>Kt(!0)),C("keydown",Ze,y=>y.key==="Enter"&&Mt()),Mn(Ze,()=>n(Y),y=>_(Y,y)),Wa(Rt,()=>n(F),y=>_(F,y)),C("click",yt,Mt),x(e,Ce),Zn()}_a(["click","keydown","change"]);var fc=M(`
Du öffnest Mission Control über einen Proxy/Namen. Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame.
- Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
Wähle, welche Modelle im Config-Snippet erscheinen sollen.
'),bc=M(''),wc=M('
Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.
Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren.
API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local).
Es läuft immer nur ein Modell gleichzeitig — ein größerer Kontext hier beeinflusst deine anderen Modelle nicht.
Immer aktiv halten
Modell bleibt dauerhaft im RAM — nie automatisch entladen. Ideal für den kleinen Scout (~5 GB), der immer schnell antworten soll.
'),fd=M(''),pd=M('
Rolle festlegen
Wähle eine Rolle als zweiten, sprechenden Namen. In deinen Tools funktionieren danach beide: der echte Modellname und die Rolle.
'),hd=M('
Modelle
Deine konfigurierten Modelle — testen, Kontext anpassen oder aus dem Speicher werfen.
Schnelltest
Schreib eine Nachricht — das gewählte Modell wird automatisch geweckt.
Modelle & Ports
Modelle laden automatisch, sobald eine Anfrage kommt — du musst nichts manuell starten.
',1);function gd(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr (längere Dateien/Chats), braucht aber mehr Speicher und wird etwas langsamer.",r="Die Rolle ist ein zusätzlicher, sprechender Name (z.B. coder). Du kannst in deinen Tools entweder den echten Modellnamen ODER die Rolle angeben — beides führt zum selben Modell.",i=[{id:"coder",label:"Coder",desc:"Programmieren & Code"},{id:"vision",label:"Vision",desc:"Bilder verstehen"},{id:"scout",label:"Scout",desc:"Schneller Allrounder"},{id:"reviewer",label:"Reviewer",desc:"Code/Texte prüfen"},{id:"manager",label:"Manager",desc:"Planen & koordinieren"}],o=q(()=>{var x;return((x=dr.value)==null?void 0:x.models)||[]});function d(){document.dispatchEvent(new Event("mc:refresh"))}let c=R(""),u=R(""),f=R(""),_=R(!1);const T=q(()=>n(o).filter(x=>!x.incomplete));async function h(){var L;const x=n(c)||((L=n(T)[0])==null?void 0:L.name);if(!(!x||!n(u).trim())){g(_,!0),g(f,"(wecke Modell, kann beim Laden kurz dauern…)");try{const G=await fe("/api/chat",{method:"POST",body:JSON.stringify({model:x,message:n(u)})});g(f,G.reply,!0)}catch(G){g(f,"Fehler: "+G.message)}g(_,!1),d()}}const b=300;let A=R(!1),C=R(null),S=R(8192),B=R(!1);function ye(x){var L;g(C,n(o).find(G=>G.name===x),!0),n(C)&&(g(S,((L=n(C).meta)==null?void 0:L.ctx)||8192,!0),g(B,(n(C).ttl??b)>=99999),g(A,!0))}async function ee(){const x=n(B)?99999:b;try{await fe("/api/update_model",{method:"POST",body:JSON.stringify({alias:n(C).name,ctx:n(S),ttl:x})}),X("Gespeichert — aktiv beim nächsten Modell-Start."),g(A,!1),d()}catch(L){X(L.message,!0)}}let ce=R(!1),_e=R(""),I=R(""),K=R("");function ie(x){const L=n(o).find(G=>G.name===x);g(_e,x,!0),g(K,(L==null?void 0:L.role)||"",!0),g(I,""),g(ce,!0)}async function ve(x){try{await fe("/api/set_role",{method:"POST",body:JSON.stringify({alias:n(_e),role:x})}),X(x?`Rolle gesetzt: ${x}`:"Rolle entfernt."),g(ce,!1),d()}catch(L){X(L.message,!0)}}async function me(x){if(await bn({title:`„${x}" entladen?`,body:"Das Modell wird aus dem Grafikspeicher geworfen und lädt beim nächsten Aufruf automatisch neu.",confirmLabel:"Entladen"}))try{await fe("/api/unload?model="+encodeURIComponent(x),{method:"POST"}),X("Entladen: "+x),setTimeout(d,600)}catch(L){X(L.message,!0)}}async function Me(x){if(await bn({title:`„${x}" wirklich löschen?`,body:"Das Modell wird aus der Konfiguration ausgetragen und seine Dateien werden von der Festplatte gelöscht, um Speicher freizugeben. Das lässt sich nicht rückgängig machen — später brauchst du dafür einen neuen Download.",confirmLabel:"Endgültig löschen",danger:!0}))try{const L=await fe("/api/delete_model",{method:"POST",body:JSON.stringify({alias:x})});X(L.note||"Gelöscht: "+x),setTimeout(d,600)}catch(L){X(L.message,!0)}}function Ue(x){return x!=null&&x.length?x.map(L=>L==="Code"?'Code':L==="Bild"?'Bild':'Text').join(" "):"–"}function E(x){if(!x)return"–";const L=x.quant||"?",G=x.ctx?Math.round(x.ctx/1024)+"K":"?",te=x.size_bytes?(x.size_bytes/1024**3).toFixed(1)+" GB":"?";return`${L} · ${G} · ${te}`}function Q(x){var L;(L=navigator.clipboard)==null||L.writeText(x),X("Kopiert: "+x)}var de=hd(),Ae=l(ft(de),2),Y=l(s(Ae),6);Ge(Y,21,()=>n(T),Ke,(x,L)=>{var G=nd(),te=s(G),$={};O(()=>{m(te,n(L).name),$!==($=n(L).name)&&(G.value=(G.__value=n(L).name)??"")}),w(x,G)});var be=l(Y,4),Z=l(be,2),ze=s(Z),Ce=l(Z,2);{var He=x=>{var L=ad(),G=s(L);O(()=>m(G,n(f))),w(x,L)};V(Ce,x=>{n(f)&&x(He)})}var Ie=l(Ae,2),Re=s(Ie),_t=l(s(Re)),mt=s(_t),Kt=l(Re,4);{var St=x=>{var L=rd();w(x,L)},Et=x=>{var L=cd(),G=l(s(L));Ge(G,21,()=>n(o),Ke,(te,$)=>{var H=Lr(),ne=ft(H);{var he=Se=>{var je=id(),rt=s(je),bt=s(rt),Ze=l(rt,5),p=s(Ze),D=l(p,2);O(()=>m(bt,n($).name)),z("click",p,()=>{var se;X("Lade im Cookbook ein Modell und setze den Alias auf: "+n($).name),(se=document.querySelector(".nav-item[data-view='cookbook']"))==null||se.dispatchEvent(new MouseEvent("click"))}),z("click",D,()=>Me(n($).name)),w(Se,je)},Ne=Se=>{var je=dd(),rt=s(je),bt=s(rt),Ze=s(bt),p=l(Ze);{var D=Ee=>{var Be=sd(),$t=s(Be);O(()=>m($t,n($).role)),w(Ee,Be)};V(p,Ee=>{n($).role&&Ee(D)})}var se=l(bt,2);{var j=Ee=>{var Be=ld(),$t=s(Be);O(()=>m($t,n($).meta.filename)),w(Ee,Be)};V(se,Ee=>{var Be;(Be=n($).meta)!=null&&Be.filename&&Ee(j)})}var xe=l(se,2),ge=l(s(xe),2);Ge(ge,17,()=>{var Ee;return(Ee=n($).api_ids)!=null&&Ee.length?n($).api_ids:[n($).name]},Ke,(Ee,Be)=>{var $t=od(),sn=s($t);O(()=>m(sn,n(Be))),z("click",$t,()=>Q(n(Be))),w(Ee,$t)});var it=l(rt);Ye(it,()=>{var Ee;return Ue((Ee=n($).meta)==null?void 0:Ee.caps)},!0);var Je=l(it);Ye(Je,()=>E(n($).meta),!0);var dt=l(Je);Ye(dt,()=>Fo(n($).state,n($).download_progress),!0);var Ve=l(dt),yt=s(Ve),Tt=l(Ve),Rt=s(Tt),Gt=l(Rt,2),wt=l(Gt,2),Ut=l(wt,2);O(()=>{m(Ze,`${n($).name??""} `),m(yt,n($).port??"auto")}),z("click",Rt,()=>ie(n($).name)),z("click",Gt,()=>ye(n($).name)),z("click",wt,()=>me(n($).name)),z("click",Ut,()=>Me(n($).name)),w(Se,je)};V(ne,Se=>{n($).incomplete?Se(he):Se(Ne,-1)})}w(te,H)}),w(x,L)};V(Kt,x=>{n(o).length?x(Et,-1):x(St)})}var P=l(Ie,2);{var N=x=>{var L=vd(),G=s(L),te=s(G),$=l(te,4),H=s($),ne=l($,2),he=l(s(ne));Ye(he,()=>li(a));var Ne=l(ne,2),Se=l(Ne,2);{var je=j=>{var xe=ud(),ge=s(xe),it=l(s(ge),2),Je=s(it),dt=l(Je,2),Ve=l(ge,2);O((yt,Tt)=>{m(Je,`Optimal ~${yt??""}k → ~${n(C).meta.peak_ram_optimal_gb??""} GB Spitzenbedarf.`),m(dt,` Aktuell ~${Tt??""}k → ~${n(C).meta.peak_ram_gb??""} GB.`)},[()=>Math.round(n(C).meta.optimal_ctx/1024),()=>Math.round((n(C).meta.ctx||0)/1024)]),z("click",Ve,()=>g(S,n(C).meta.optimal_ctx,!0)),w(j,xe)};V(Se,j=>{var xe;(xe=n(C).meta)!=null&&xe.optimal_ctx&&j(je)})}var rt=l(Se,4),bt=l(s(rt),2),Ze=s(bt),p=l(Ze,2),D=s(p),se=l(rt,2);O(()=>{m(H,n(C).name),Bn(p,`color:${n(B)?"var(--accent)":"var(--mut)"}`),m(D,n(B)?"An (TTL = ∞)":"Aus (TTL = 5 min)")}),z("click",L,j=>{j.target.classList.contains("modal-overlay")&&g(A,!1)}),z("click",te,()=>g(A,!1)),Sn(Ne,()=>n(S),j=>g(S,j)),Do(Ze,()=>n(B),j=>g(B,j)),z("click",se,ee),w(x,L)};V(P,x=>{n(A)&&n(C)&&x(N)})}var J=l(P,2);{var re=x=>{var L=pd(),G=s(L),te=s(G),$=l(te,2),H=l(s($));Ye(H,()=>li(r));var ne=l($,2),he=s(ne),Ne=l(ne,4);Ge(Ne,21,()=>i,Ke,(Ze,p)=>{var D=fd(),se=s(D);O(()=>{lt(D,1,n(K)===n(p).id?"primary":"ghost"),zr(D,"title",n(p).desc),m(se,n(p).label)}),z("click",D,()=>ve(n(p).id)),w(Ze,D)});var Se=l(Ne,4),je=l(Se,2),rt=s(je),bt=l(rt,2);O(()=>m(he,n(_e))),z("click",L,Ze=>{Ze.target.classList.contains("modal-overlay")&&g(ce,!1)}),z("click",te,()=>g(ce,!1)),Sn(Se,()=>n(I),Ze=>g(I,Ze)),z("click",rt,()=>ve("")),z("click",bt,()=>ve(n(I).trim().toLowerCase())),w(x,L)};V(J,x=>{n(ce)&&x(re)})}O(()=>{Z.disabled=n(_),m(ze,n(_)?"…":"Senden"),m(mt,n(o).length?n(o).length+" konfiguriert":"")}),ja(Y,()=>n(c),x=>g(c,x)),Sn(be,()=>n(u),x=>g(u,x)),z("click",Z,h),w(e,de),Zn()}_a(["click"]);var _d=M('
Prozessor (CPU)
%
Arbeitsspeicher
%
Grafikspeicher
%
System-Metriken (Bosgame)
Live-Auslastung deines Mini-PCs, alle 0,5 Sekunden.
Arbeitsspeicher (RAM)
Grafikspeicher (VRAM + GTT)
Speicherplatz (Disk)
Temperatur (GPU / CPU)
CPU-Verlauf
RAM-Verlauf
VRAM-Verlauf
',1),md=M(''),bd=M('
Gerade nichts los.
Alles ruhig — keine laufenden Aufgaben.
'),yd=M(''),wd=M(''),xd=M(''),kd=M('
'),Md=M('
'),Sd=M('
'),Ed=M('
Aktivität
Live-Auslastung und laufende Aufgaben (Downloads, Updates) mit Protokoll.
Hintergrund-Aufgaben
Downloads & Updates erscheinen hier mit Live-Protokoll — zum Aufklappen klicken.
Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
LLM-Engine (llama-swap)
Das Herzstück: lädt bei jeder Anfrage automatisch das passende Modell.
Adresse (für deine Tools)
Aktuell geladen
Konfigurierte Modelle
Dienste & Applikation
Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
Betriebssystem (Bosgame)
Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
Live-Konsole
Live mitlesen, was der Dienst gerade tut.
',1);function Bd(e,t){Yn(t,!0);const a=q(()=>dr.value),r=q(()=>ra.value);let i=null,o=R(`Verbinde…
+`),d=R("llama-swap"),c,u=R(null),f=R("");const _=new Set,T=q(()=>n(u)?n(r).find(p=>p.id===n(u)):null);mi(()=>{if(!n(T))return;const p=n(T),D=p.state==="done",se=p.state==="failed",j=p.state==="canceled";(D||se||j)&&!_.has(p.id)&&(_.add(p.id),X(D?`✓ ${n(f)} abgeschlossen.`:j?`${n(f)} abgebrochen.`:`✗ ${n(f)} fehlgeschlagen.`,se),D&&setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),800))});function h(p,D){g(u,p,!0),g(f,D,!0),ra.track(p)}or(()=>{b()}),Ro(()=>{i==null||i.close()});function b(){i==null||i.close();const p=n(d);g(o,`Verbinde mit ${p}…
+`);const D=location.protocol==="https:"?"wss:":"ws:";i=new WebSocket(`${D}//${location.host}/api/logs/${p}?token=${encodeURIComponent(Ws())}`),i.onmessage=se=>{g(o,n(o)+se.data),c&&(c.scrollTop=c.scrollHeight)},i.onclose=()=>{g(o,n(o)+`
+— Verbindung getrennt —`)},i.onerror=()=>i==null?void 0:i.close()}async function A(p,D,se){if(await bn({title:D,body:se,confirmLabel:"Neustarten"}))try{await fe(`/api/service/${p}/restart`,{method:"POST"}),X("Neustart ausgelöst: "+p),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),2e3)}catch(j){X(j.message,!0)}}async function C(){if(await bn({title:"Mission Control aktualisieren?",body:"Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)."}))try{const p=await fe("/api/self-update",{method:"POST"});X("Update läuft — Dashboard startet gleich neu."),h(p.job_id,"Mission Control Update")}catch(p){X(p.message,!0)}}async function S(){if(await bn({title:"LLM-Engine aktualisieren?",body:"Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar."}))try{const p=await fe("/api/update",{method:"POST"});X("LLM-Engine-Update läuft."),h(p.job_id,"LLM-Engine-Update")}catch(p){X(p.message,!0)}}async function B(){if(await bn({title:"Grafikspeicher leeren?",body:"Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.",confirmLabel:"Leeren"}))try{await fe("/api/unload",{method:"POST"}),X("Grafikspeicher geleert."),setTimeout(()=>document.dispatchEvent(new Event("mc:refresh")),600)}catch(p){X(p.message,!0)}}async function ye(){if(!await bn({title:"OS-Updates installieren?",body:"Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität."}))return;const p=await Ni({title:"sudo-Passwort",body:"Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Installieren"});if(p)try{const D=await fe("/api/os-update",{method:"POST",body:JSON.stringify({password:p})});X("OS-Update gestartet."),h(D.job_id,"OS-Update")}catch(D){X(D.message,!0)}}async function ee(){if(!await bn({title:"Server wirklich neu starten?",body:"Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.",confirmLabel:"Reboot",danger:!0}))return;const p=await Ni({title:"sudo-Passwort",body:"Für den Reboot wird einmalig dein sudo-Passwort gebraucht.",placeholder:"sudo-Passwort",password:!0,confirmLabel:"Jetzt neustarten",danger:!0});if(p)try{await fe("/api/reboot",{method:"POST",body:JSON.stringify({password:p})}),X("Reboot ausgelöst — bis gleich.")}catch(D){X(D.message,!0)}}async function ce(){g(_e,"prüfe…");try{const p=await fe("/api/integration/test");g(_e,p.ok?`✓ Engine antwortet — ${p.models.length} Modell(e): ${p.models.join(", ")}`:`✗ keine Verbindung: ${p.error}`,!0),g(I,p.ok,!0)}catch(p){g(_e,"✗ "+p.message),g(I,!1)}}let _e=R(""),I=R(!1);const K=q(()=>{var p;return((p=n(a))==null?void 0:p.models)||[]}),ie=q(()=>n(K).find(p=>p.state==="running"||p.state==="loading"));function ve(p){if(!p||p.state!=="running")return null;if(typeof p.progress=="number")return Math.min(100,p.progress);const se=((p.log||[]).at(-1)||"").match(/(\d+(?:\.\d+)?)\s*%/);return se?Math.min(100,parseFloat(se[1])):null}var me=Rd(),Me=l(ft(me),2),Ue=s(Me),E=l(s(Ue),2),Q=s(E),de=l(Ue,4),Ae=s(de),Y=l(s(Ae)),be=s(Y),Z=l(Ae,2),ze=l(s(Z),2),Ce=s(ze),He=l(Z,2),Ie=l(s(He),2),Re=s(Ie),_t=l(de,2),mt=s(_t),Kt=l(mt,2);{var St=p=>{var D=$d(),se=s(D);O(()=>{Bn(D,`color:${n(I)?"#7ee29a":"#ff9b95"}`),m(se,n(_e))}),w(p,D)};V(Kt,p=>{n(_e)&&p(St)})}var Et=l(Me,2),P=l(s(Et),4),N=l(P,2),J=l(N,2),re=l(J,2),x=l(re,2),L=l(Et,2),G=l(s(L),4),te=l(G,2),$=l(L,2);{var H=p=>{const D=q(()=>n(T)),se=q(()=>n(D).state==="done"),j=q(()=>n(D).state==="failed"),xe=q(()=>n(D).state==="canceled"),ge=q(()=>n(se)||n(j)||n(xe)),it=q(()=>ve(n(D)));var Je=Od(),dt=s(Je),Ve=l(s(dt),2);{var yt=Pe=>{var At=Ad();z("click",At,async()=>{await bn({title:n(f)+" abbrechen?",body:"Der laufende Vorgang wird gestoppt.",confirmLabel:"Abbrechen",danger:!0})&&fe(`/api/jobs/${n(D).id}/cancel`,{method:"POST"})}),w(Pe,At)};V(Ve,Pe=>{n(ge)||Pe(yt)})}var Tt=l(Ve,2),Rt=s(Tt);{var Gt=Pe=>{var At=Cd();w(Pe,At)},wt=Pe=>{var At=Ld();w(Pe,At)},Ut=Pe=>{var At=zd();w(Pe,At)},Ee=Pe=>{var At=Pd();w(Pe,At)};V(Rt,Pe=>{n(se)?Pe(Gt):n(j)?Pe(wt,1):n(xe)?Pe(Ut,2):Pe(Ee,-1)})}var Be=l(dt,2),$t=s(Be),sn=l(Be,2);{var ln=Pe=>{var At=Dd(),Or=s(At);O(()=>Bn(Or,`width:${n(it)??""}%`)),w(Pe,At)};V(sn,Pe=>{n(it)!=null&&!n(ge)&&Pe(ln)})}var xt=l(sn,2),kn=s(xt);O(Pe=>{m($t,`${n(f)??""}${n(ge)?n(se)?" — erfolgreich.":n(xe)?" — abgebrochen.":" — fehlgeschlagen.":" läuft…"}`),m(kn,Pe)},[()=>(n(D).log||[]).join(`
+`)]),w(p,Je)};V($,p=>{n(T)&&p(H)})}var ne=l($,2),he=s(ne),Ne=l(s(he),2),Se=s(Ne);Se.value=Se.__value="llama-swap";var je=l(Se);je.value=je.__value="mission-control";var rt=l(je);rt.value=rt.__value="system";var bt=l(he,4),Ze=s(bt);Oo(bt,p=>c=p,()=>c),O(p=>{var D,se,j;Bn(E,`margin-left:auto;${(D=n(a))!=null&&D.swap_ok?"background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a":"background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95"}`),m(Q,(se=n(a))!=null&&se.swap_ok?"● erreichbar":"● offline"),m(be,(((j=n(a))==null?void 0:j.swap_url)||"—")+"/v1"),m(Ce,n(ie)?n(ie).name+` (${n(ie).state})`:"nichts geladen — lädt bei der nächsten Anfrage"),m(Re,p),m(Ze,n(o))},[()=>n(K).filter(p=>!p.incomplete).length||"keine"]),z("click",mt,ce),z("click",P,()=>A("llama-swap","LLM-Engine neustarten?","Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder.")),z("click",N,()=>A("mission-control","Dashboard neustarten?","Diese Oberfläche trennt sich kurz und verbindet automatisch wieder.")),z("click",J,C),z("click",re,S),z("click",x,B),z("click",G,ye),z("click",te,ee),z("change",Ne,b),ja(Ne,()=>n(d),p=>g(d,p)),w(e,me),Zn()}_a(["click","change"]);var qd=M('
Lade Setups…
'),Id=M('✎×',1),Ur=M('
★ Beste Wahl für dein System
'),Nd=M('
Dein Setup
'),Fd=M(''),Kd=M(''),Gd=M('
Frage Quellen ab…
'),Ud=M('
'),Hd=M('✓ installiert'),Vd=M(''),Wd=M('
'),jd=M('
'),Jd=M(''),Qd=M('
'),Yd=M(''),Zd=M('
'),Xd=M('
'),ec=M(""),tc=M('
'),nc=M('
Größter Spitzenbedarf: . Es läuft immer nur ein Modell gleichzeitig — das größte bestimmt, ob das Setup passt.
'),ac=M('
'),rc=M('
Lade Dateien von HuggingFace…
'),Hi=M(""),ic=M('
Empfohlener Kontext für deine Hardware: — übernehmen
'),sc=M('
Ressourcen-Check
'),lc=M('
',1),oc=M('
Keine GGUF-Dateien im Repo gefunden.
'),dc=M('
'),cc=M('
'),uc=M('
Stell dir aus beliebigen Modellen ein eigenes Setup zusammen.
Die Repo-ID findest du über die Profi-Suche weiter unten (z.B. unsloth/Qwen3-8B-GGUF).
'),vc=M('
Cookbook
Modelle finden, installieren und eigene Setups zusammenstellen.
Use-Case-Setups
Fertige Modell-Bundles für deinen Anwendungsfall. Hardware-Ampel zeigt, ob es auf deinen PC passt.
Aktuell die besten Modelle für dein System
Profi-Suche (HuggingFace)
Direkt HuggingFace durchsuchen — oder eine Modell-URL einfügen für den direkten Sprung.
',1);function fc(e,t){Yn(t,!0);const a="Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.",r="MoE (Mixture of Experts): viele Parameter fürs Wissen, aber pro Wort ist nur ein kleiner Teil aktiv → fast große-Modell-Qualität bei hohem Tempo. Ideal für deine APU.",i={perfect:0,marginal:1,too_tight:2},o={vision:"Bilder",coder:"Coden",reasoning:"Logik",agent:"Agenten & Tools",scout:"Allrounder",reviewer:"Review",manager:"Manager"},d=[{label:"Bilder",kw:["-vl-","-vl","vision","llava","multimodal","-mm-","pixtral"]},{label:"Coden",kw:["coder","-code-","code-","codestral","starcoder"]},{label:"Logik",kw:["-r1","deepseek-r1","reasoning","qwq","magistral","-think","thinking","-o1"]},{label:"Agenten & Tools",kw:["hermes","-tool","command-r","watt","-fc-","function"]}];function c(y){const k=(y||"").toLowerCase();for(const U of d)if(U.kw.some(F=>k.includes(F)))return U.label;return"Allrounder"}function u(y=""){const k=y.toLowerCase();return/\d+x\d+(\.\d+)?b/.test(k)||new RegExp("(?{mt(),Kt()});async function mt(){g(C,!0);try{const y=await fe("/api/cookbook/recipes");g(b,y.recipes||[],!0),g(A,y.recommended_id||null,!0)}catch(y){X(y.message,!0)}g(C,!1)}async function Kt(y=!1){g(B,!0);try{const k=await fe("/api/cookbook/discover"+(y?"?force=true":""));g(S,k,!0),g(ye,k.stale,!0)}catch(k){X("Empfehlungen nicht ladbar: "+k.message,!0)}g(B,!1)}function St(y){y=y.trim();const k=y.match(/huggingface\.co\/([^\s/]+\/[^\s/?#]+)/i);return k?k[1]:/^[\w.-]+\/[\w.-]+$/.test(y)?y:null}async function Et(){const y=n(ee).trim(),k=St(y);let U=y;if(n(I)&&!k&&(U=U?U+" "+n(I):n(I)),!U&&!k){g(ce,[],!0);return}g(_e,!0),g(ie,[],!0),g(ve,-1);try{if(k)g(ce,[{id:k,author:k.split("/")[0],downloads:0}],!0);else{const F=`https://huggingface.co/api/models?search=${encodeURIComponent(U)}&filter=gguf&sort=${encodeURIComponent(n(K))}&direction=-1&limit=40`,le=await fetch(F);g(ce,await le.json(),!0)}n(ce).forEach((F,le)=>P(le,F.id))}catch(F){X(F.message,!0),g(ce,[],!0)}g(_e,!1)}async function P(y,k){var U;try{const F=await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:k,ctx:8192})});if(!((U=F.files)!=null&&U.length)){n(ie)[y]=null,N();return}const le=F.files.find(Qe=>{var Xe;return(Xe=Qe.quant)==null?void 0:Xe.includes("Q4_K_M")})||F.files[0];n(ie)[y]=le.fit.level,N()}catch{n(ie)[y]=null,N()}}function N(){let y=-1,k=99;n(ie).forEach((U,F)=>{if(U==null)return;const le=i[U]??99;le=0&&k<=1?y:-1,!0)}function J(y){g(me,n(b).find(k=>k.id===y),!0)}async function re(y){const k=document.getElementById("cb-r-install-btn");k&&(k.disabled=!0,k.textContent="Starte…");try{const U=await fe("/api/cookbook/install-recipe",{method:"POST",body:JSON.stringify({recipe_id:y,hf_token:Qa()})});X(`${U.count} Downloads gestartet — siehe Aktivität.`),g(me,null),_t("activity")}catch(U){X(U.message,!0),k&&(k.disabled=!1,k.textContent="Installieren")}}async function x(y,k){var U,F;g(Me,!0),g(Ue,k,!0),g(E,y,!0),g(Ae,""),g(Y,8192),g(Q,null),g(Z,null),g(de,""),g(be,!0);try{if(g(Q,await fe("/api/cookbook/analyze",{method:"POST",body:JSON.stringify({repo_id:y,ctx:8192})}),!0),(F=(U=n(Q))==null?void 0:U.files)!=null&&F.length){const le=n(Q).files.find(Qe=>{var Xe;return(Xe=Qe.quant)==null?void 0:Xe.includes("Q4_K_M")})||n(Q).files[0];g(de,le.filename,!0),le.optimal_ctx&&n(Y)===8192&&g(Y,le.optimal_ctx,!0),L()}}catch(le){X(le.message,!0)}g(be,!1)}function L(){var y,k,U;!((k=(y=n(Q))==null?void 0:y.files)!=null&&k.length)||!n(de)||(g(Z,n(Q).files.find(F=>F.filename===n(de))||null,!0),(U=n(Z))!=null&&U.optimal_ctx&&n(Y)===8192&&g(Y,n(Z).optimal_ctx,!0))}async function G(){if(!n(E)||!n(de))return X("Bitte eine GGUF-Datei wählen.",!0);g(be,!0);try{const y=await fe("/api/download",{method:"POST",body:JSON.stringify({repo:n(E),file:n(de),hf_token:Qa()})});await fe("/api/register",{method:"POST",body:JSON.stringify({role:n(Ae),model_path:y.expected_path,ctx:n(Y)})}),X("Download gestartet — siehe Aktivität."),g(Me,!1),_t("activity")}catch(y){X("Fehler: "+y.message,!0)}g(be,!1)}async function te(y,k,U,F){F.disabled=!0,F.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:y,role:k,params_b:U,quant:"Q4_K_M",hf_token:Qa()})}),X("Download gestartet — siehe Aktivität."),_t("activity")}catch(le){X(le.message,!0),F.disabled=!1,F.textContent="Installieren"}}function $(){g(Re,null),g(Ce,""),g(He,""),g(Ie,[{repo:"",role:"",quant:"Q4_K_M"}],!0),g(ze,!0)}function H(y){const k=n(b).find(U=>U.id===y);k&&(g(Re,y,!0),g(Ce,k.title,!0),g(He,k.desc||"",!0),g(Ie,k.models.map(U=>({repo:U.repo,role:U.role,quant:U.quant||"Q4_K_M"})),!0),g(ze,!0))}async function ne(){if(!n(Ce).trim())return X("Bitte einen Titel angeben.",!0);const y=n(Ie).filter(k=>k.repo.trim());if(!y.length)return X("Mindestens ein Modell (Repo) angeben.",!0);try{n(Re)?(await fe("/api/cookbook/user-recipe/"+encodeURIComponent(n(Re)),{method:"PUT",body:JSON.stringify({title:n(Ce),desc:n(He),models:y})}),X("Setup aktualisiert.")):(await fe("/api/cookbook/user-recipe",{method:"POST",body:JSON.stringify({title:n(Ce),desc:n(He),models:y})}),X("Setup gespeichert.")),g(ze,!1),mt()}catch(k){X(k.message,!0)}}async function he(y){const k=n(b).find(U=>U.id===y);if(await bn({title:`„${(k==null?void 0:k.title)||y}" löschen?`,body:"Dein eigenes Setup wird aus dem Cookbook entfernt. Bereits installierte Modelle bleiben unangetastet.",confirmLabel:"Löschen",danger:!0}))try{await fe("/api/cookbook/user-recipe/"+encodeURIComponent(y),{method:"DELETE"}),X("Setup gelöscht."),mt()}catch(U){X(U.message,!0)}}function Ne(y){if(!u(y))return"";const k=f(y);return`MoE${k?` · ~${k}B aktiv`:""}`}function Se(y){return y?`${Cn(y)}`:""}var je=vc(),rt=l(ft(je),2),bt=s(rt),Ze=l(s(bt),2),p=l(bt,4);{var D=y=>{var k=qd();w(y,k)},se=y=>{var k=Kd();Ge(k,21,()=>n(b),Ke,(U,F)=>{const le=q(()=>n(F).id===n(A));var Qe=Fd(),Xe=s(Qe);{var Zt=Oe=>{var tt=Id(),Xt=ft(tt),ct=l(Xt,2);z("click",Xt,ut=>{ut.stopPropagation(),H(n(F).id)}),z("keydown",Xt,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),H(n(F).id))}),z("click",ct,ut=>{ut.stopPropagation(),he(n(F).id)}),z("keydown",ct,ut=>{ut.key==="Enter"&&(ut.stopPropagation(),he(n(F).id))}),w(Oe,tt)};V(Xe,Oe=>{n(F).user&&Oe(Zt)})}var Ht=l(Xe,2);{var Vt=Oe=>{var tt=Ur();w(Oe,tt)},Wt=Oe=>{var tt=Nd();w(Oe,tt)};V(Ht,Oe=>{n(le)?Oe(Vt):n(F).user&&Oe(Wt,1)})}var Le=l(Ht,2),ke=s(Le),qe=s(ke),Fe=s(qe),De=l(ke,2),pt=s(De),kt=l(Le,2),Ct=s(kt),Lt=l(kt,2),et=s(Lt);O((Oe,tt)=>{lt(Qe,1,`card-btn${n(le)?" cb-best":""}`),m(Fe,n(F).title),lt(De,1,`fit-badge ${Oe??""}`),m(pt,tt),m(Ct,n(F).desc),m(et,`${n(F).models.length??""} Modell${n(F).models.length>1?"e":""} im Setup`)},[()=>_(n(F).fit_level),()=>T(n(F).fit_level)]),z("click",Qe,()=>J(n(F).id)),w(U,Qe)}),w(y,k)};V(p,y=>{n(C)?y(D):y(se,-1)})}var j=l(rt,2),xe=s(j),ge=l(s(xe),2),it=s(ge),Je=l(ge,2),dt=s(Je),Ve=l(xe,2),yt=s(Ve),Tt=l(Ve,2);{var Rt=y=>{var k=Gd();w(y,k)},Gt=y=>{var k=Lr(),U=ft(k);Ge(U,17,()=>n(S).categories,Ke,(F,le)=>{var Qe=jd(),Xe=s(Qe),Zt=s(Xe),Ht=s(Zt),Vt=l(Xe,2);Ge(Vt,21,()=>n(le).models,Ke,(Wt,Le)=>{const ke=q(()=>n(le).recommended&&n(Le).repo===n(le).recommended);var qe=Wd(),Fe=s(qe);{var De=Mt=>{var It=Ur();w(Mt,It)};V(Fe,Mt=>{n(ke)&&Mt(De)})}var pt=l(Fe,2),kt=s(pt),Ct=s(kt),Lt=s(Ct),et=l(Ct,2),Oe=s(et),tt=l(kt,2),Xt=s(tt),ct=l(pt,2),ut=s(ct);Ye(ut,()=>Se(o[n(Le).role]||c(n(Le).repo)));var Mn=l(ut,2);Ye(Mn,()=>Ne(n(Le).repo));var In=l(ct,2),We=s(In),nt=l(In,2);Ge(nt,17,()=>n(Le).requirements||[],Ke,(Mt,It)=>{var Nn=Ud(),Rr=s(Nn);O(()=>m(Rr,`⚠ ${n(It)??""}`)),w(Mt,Nn)});var st=l(nt,4),vt=s(st);{var en=Mt=>{var It=Hd();O(()=>zr(It,"title",`bereits eingerichtet als ${n(Le).installed??""}`)),w(Mt,It)},qt=Mt=>{var It=Vd();z("click",It,Nn=>te(n(Le).repo,n(Le).role,n(Le).params_b,Nn.currentTarget)),w(Mt,It)};V(vt,Mt=>{n(Le).installed?Mt(en):Mt(qt,-1)})}O((Mt,It,Nn)=>{lt(qe,1,`card${n(ke)?" res-best":""}`),m(Lt,n(Le).name),m(Oe,`${n(Le).author??""} · ${n(Le).params_b??""}B`),lt(tt,1,`fit-badge ${Mt??""}`),m(Xt,n(Le).fit.text),m(We,`${It??""} · ⬇ ${Nn??""}`)},[()=>_(n(Le).fit.level),()=>h(n(Le).fit),()=>(n(Le).downloads||0).toLocaleString()]),w(Wt,qe)}),O(()=>m(Ht,n(le).title)),w(F,Qe)}),w(y,k)};V(Tt,y=>{var k;n(B)&&!n(S)?y(Rt):(k=n(S))!=null&&k.categories&&y(Gt,1)})}var wt=l(j,2),Ut=l(s(wt),4),Ee=s(Ut),Be=l(Ee,2),$t=s(Be);$t.value=$t.__value="downloads";var sn=l($t);sn.value=sn.__value="trending";var ln=l(sn);ln.value=ln.__value="created_at";var xt=l(Be,2),kn=s(xt),Pe=l(Ut,2);Ge(Pe,20,()=>["","coder","vision","reasoning","chat","agent"],Ke,(y,k)=>{var U=Jd(),F=s(U);O(()=>{lt(U,1,n(I)===k?"primary":"ghost"),m(F,k||"Alle")}),z("click",U,()=>{g(I,k,!0),Et()}),w(y,U)});var At=l(Pe,2);{var Or=y=>{var k=Yd();Ge(k,21,()=>n(ce),Ke,(U,F,le)=>{var Qe=Qd(),Xe=s(Qe);{var Zt=ct=>{var ut=Ur();w(ct,ut)};V(Xe,ct=>{le===n(ve)&&ct(Zt)})}var Ht=l(Xe,2),Vt=s(Ht),Wt=s(Vt),Le=s(Wt),ke=l(Wt,2),qe=s(ke),Fe=l(Vt,2),De=s(Fe),pt=l(Ht,2),kt=s(pt);Ye(kt,()=>Se(c(n(F).id)));var Ct=l(kt,2);Ye(Ct,()=>Ne(n(F).id));var Lt=l(pt,4),et=s(Lt),Oe=s(et),tt=l(et,2),Xt=s(tt);O((ct,ut,Mn,In,We)=>{lt(Qe,1,`card${le===n(ve)?" res-best":""}`),m(Le,ct),m(qe,n(F).author||""),lt(Fe,1,`fit-badge ${ut??""}`),m(De,Mn),m(Oe,In),m(Xt,`⬇ ${We??""}`)},[()=>n(F).id.split("/").pop(),()=>n(ie)[le]?_(n(ie)[le]):"warn",()=>n(ie)[le]?T(n(ie)[le]):"prüfe…",()=>n(ie)[le]?h({req_gb:0,tps:0}):"Hardware-Fit…",()=>(n(F).downloads||0).toLocaleString()]),z("click",Qe,()=>x(n(F).id,n(F).id.split("/").pop())),w(U,Qe)}),w(y,k)};V(At,y=>{n(ce).length&&y(Or)})}var ki=l(wt,2);{var Ys=y=>{const k=q(()=>n(me));var U=ac(),F=s(U),le=s(F),Qe=l(le,2),Xe=s(Qe),Zt=l(Qe,2),Ht=s(Zt),Vt=l(Zt,2);Ge(Vt,21,()=>n(k).models,Ke,(Fe,De)=>{var pt=tc(),kt=s(pt),Ct=s(kt),Lt=s(Ct),et=s(Lt),Oe=l(Lt,2),tt=s(Oe),Xt=l(Oe,2);Ye(Xt,()=>Ne(n(De).repo));var ct=l(Ct,2);{var ut=st=>{var vt=Zd(),en=s(vt);O(()=>m(en,n(De).why)),w(st,vt)};V(ct,st=>{n(De).why&&st(ut)})}var Mn=l(ct,2);{var In=st=>{var vt=Xd(),en=s(vt);O((qt,Mt,It)=>m(en,`~${qt??""} GB · ~${Mt??""} Tok/s · optimal ~${It??""}k Kontext`),[()=>(n(De).fit.req_gb??0).toFixed(1),()=>Math.round(n(De).fit.tps??0),()=>Math.round((n(De).optimal_ctx||8192)/1024)]),w(st,vt)};V(Mn,st=>{n(De).fit&&st(In)})}var We=l(kt,2);{var nt=st=>{var vt=ec(),en=s(vt);O(qt=>{lt(vt,1,`fit-badge ${qt??""}`),m(en,n(De).fit.text)},[()=>_(n(De).fit.level)]),w(st,vt)};V(We,st=>{n(De).fit&&st(nt)})}O(()=>{m(et,n(De).name),m(tt,n(De).role)}),w(Fe,pt)});var Wt=l(Vt,2);{var Le=Fe=>{const De=q(()=>Math.max(...n(k).models.map(Lt=>{var et;return((et=Lt.fit)==null?void 0:et.req_gb)??0})));var pt=nc(),kt=l(s(pt)),Ct=s(kt);O(Lt=>m(Ct,`~${Lt??""} GB`),[()=>n(De).toFixed(1)]),w(Fe,pt)};V(Wt,Fe=>{n(k).models.length&&n(k).models[0].fit&&Fe(Le)})}var ke=l(Wt,2),qe=s(ke);O(()=>{m(Xe,n(k).title),m(Ht,n(k).desc),lt(ke,1,`primary${n(k).fit_level==="too_tight"?" warn":""}`),m(qe,n(k).fit_level==="too_tight"?"Trotzdem installieren (zu groß)":"Komplettes Setup installieren")}),z("click",U,Fe=>{Fe.target.classList.contains("modal-overlay")&&g(me,null)}),z("click",le,()=>g(me,null)),z("click",ke,()=>re(n(k).id)),w(y,U)};V(ki,y=>{n(me)&&y(Ys)})}var Mi=l(ki,2);{var Zs=y=>{var k=dc(),U=s(k),F=s(U),le=l(F,2),Qe=s(le),Xe=l(le,2),Zt=s(Xe),Ht=l(Xe,2);{var Vt=ke=>{var qe=rc();w(ke,qe)},Wt=ke=>{var qe=lc(),Fe=l(ft(qe),2);Ge(Fe,21,()=>n(Q).files,Ke,(We,nt)=>{const st=q(()=>n(nt).fit.level==="perfect"?"●":n(nt).fit.level==="marginal"?"◐":"○");var vt=Hi(),en=s(vt),qt={};O(()=>{m(en,`${n(st)??""} ${n(nt).filename??""}`),qt!==(qt=n(nt).filename)&&(vt.value=(vt.__value=n(nt).filename)??"")}),w(We,vt)});var De=l(Fe,2),pt=s(De),kt=l(s(pt),2),Ct=l(pt,2),Lt=s(Ct),et=l(s(Lt));Ye(et,()=>li(a));var Oe=l(Lt,2),tt=l(De,2);{var Xt=We=>{var nt=ic(),st=l(s(nt)),vt=s(st),en=l(st,2);O(qt=>m(vt,`~${qt??""}k`),[()=>Math.round(n(Z).optimal_ctx/1024)]),z("click",en,qt=>{qt.preventDefault(),g(Y,n(Z).optimal_ctx,!0)}),w(We,nt)};V(tt,We=>{var nt;(nt=n(Z))!=null&&nt.optimal_ctx&&We(Xt)})}var ct=l(tt,2);{var ut=We=>{var nt=sc(),st=s(nt),vt=l(s(st),2),en=s(vt),qt=l(st,2),Mt=s(qt);O((It,Nn,Rr)=>{m(en,`~${It??""} GB · ~${Nn??""} Tok/s · ${(n(Z).quant||"GGUF")??""}`),lt(qt,1,`fit-badge ${Rr??""}`),m(Mt,n(Z).fit.text)},[()=>(n(Z).fit.req_gb??0).toFixed(1),()=>Math.round(n(Z).fit.tps??0),()=>_(n(Z).fit.level)]),w(We,nt)};V(ct,We=>{n(Z)&&We(ut)})}var Mn=l(ct,2),In=s(Mn);O(()=>{var We,nt;lt(Mn,1,`primary${((We=n(Z))==null?void 0:We.fit.level)==="too_tight"?" warn":""}`),Mn.disabled=n(be),m(In,n(be)?"Starte…":((nt=n(Z))==null?void 0:nt.fit.level)==="too_tight"?"Trotzdem holen (zu groß)":"Herunterladen & Einpflegen")}),z("change",Fe,L),ja(Fe,()=>n(de),We=>g(de,We)),Sn(kt,()=>n(Ae),We=>g(Ae,We)),Sn(Oe,()=>n(Y),We=>g(Y,We)),z("click",Mn,G),w(ke,qe)},Le=ke=>{var qe=oc();w(ke,qe)};V(Ht,ke=>{var qe,Fe;n(be)&&!n(Q)?ke(Vt):(Fe=(qe=n(Q))==null?void 0:qe.files)!=null&&Fe.length?ke(Wt,1):n(be)||ke(Le,2)})}O(()=>{m(Qe,n(Ue)),m(Zt,n(E))}),z("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&g(Me,!1)}),z("click",F,()=>g(Me,!1)),w(y,k)};V(Mi,y=>{n(Me)&&y(Zs)})}var Xs=l(Mi,2);{var el=y=>{var k=uc(),U=s(k),F=s(U),le=l(F,2),Qe=s(le),Xe=l(le,6),Zt=l(Xe,4),Ht=l(Zt,4);Ge(Ht,17,()=>n(Ie),Ke,(ke,qe,Fe)=>{var De=cc(),pt=s(De),kt=l(pt,2),Ct=l(kt,2);Ge(Ct,20,()=>["Q4_K_M","Q5_K_M","Q6_K","Q8_0","Q3_K_M"],Ke,(et,Oe)=>{var tt=Hi(),Xt=s(tt),ct={};O(()=>{m(Xt,Oe),ct!==(ct=Oe)&&(tt.__value=Oe)}),w(et,tt)});var Lt=l(Ct,2);Sn(pt,()=>n(qe).repo,et=>n(qe).repo=et),Sn(kt,()=>n(qe).role,et=>n(qe).role=et),ja(Ct,()=>n(qe).quant,et=>n(qe).quant=et),z("click",Lt,()=>g(Ie,n(Ie).filter((et,Oe)=>Oe!==Fe),!0)),w(ke,De)});var Vt=l(Ht,2),Wt=l(Vt,4),Le=s(Wt);O(()=>{m(Qe,n(Re)?"Setup bearbeiten":"Eigenes Setup erstellen"),m(Le,n(Re)?"Änderungen speichern":"Setup speichern")}),z("click",k,ke=>{ke.target.classList.contains("modal-overlay")&&g(ze,!1)}),z("click",F,()=>g(ze,!1)),Sn(Xe,()=>n(Ce),ke=>g(Ce,ke)),Sn(Zt,()=>n(He),ke=>g(He,ke)),z("click",Vt,()=>g(Ie,[...n(Ie),{repo:"",role:"",quant:"Q4_K_M"}],!0)),z("click",Wt,ne),w(y,k)};V(Xs,y=>{n(ze)&&y(el)})}O(()=>{m(it,n(S)?"aktuell":""),Je.disabled=n(B),m(dt,n(B)?"Suche…":"Aktualisieren"),m(yt,`Täglich aktualisierte Top-Modelle je Kategorie — Hardware-Fit berechnet für dein System. ${n(ye)?"⚠ Cache veraltet.":""}`),xt.disabled=n(_e),m(kn,n(_e)?"Lade…":"Suchen")}),z("click",Ze,$),z("click",Je,()=>Kt(!0)),z("keydown",Ee,y=>y.key==="Enter"&&Et()),Sn(Ee,()=>n(ee),y=>g(ee,y)),ja(Be,()=>n(K),y=>g(K,y)),z("click",xt,Et),w(e,je),Zn()}_a(["click","keydown","change"]);var pc=M(`
Du öffnest Mission Control über einen Proxy/Namen. Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame.
+ Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
Wähle, welche Modelle im Config-Snippet erscheinen sollen.
'),yc=M(''),wc=M('
Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.
Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren.
API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local).
Nur den "provider"-Block einfügen. Falls bereits ein "llama-swap"-Eintrag vorhanden: vorher löschen, sonst erscheinen Modelle doppelt.
Danach in OpenCode /models → wählen.
',1),kc=M('
In Cline/Cursor den Provider „OpenAI Compatible" wählen:
API-Key: ein beliebiger Wert (z.B. local).
',1),Mc=M('
Settings → Admin Panel → Connections → neue OpenAI-Verbindung:
',1),Sc=M('
Bilder einbinden (Vision)
Mit Vision-Modellen kannst du Screenshots oder Fehlerbilder direkt mitschicken.
'),Ec=M('
Verbinden
Schritt für Schritt: KI-Modelle in deine Tools einbinden — getestet, erklärt, mit Copy-Paste-Config.
1 · Verbindung prüfen
Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.
Wie funktioniert das Auto-Swap?
3 · Tool wählen
Für welches Werkzeug brauchst du die Konfiguration?
4 · Config-Snippet
',1);function Tc(e,t){Yn(t,!0);const a=q(()=>dr.value),r=q(()=>{var P;return(((P=n(a))==null?void 0:P.models)||[]).filter(N=>!N.incomplete)}),i=q(f);let o=R("zed"),d=R(Bt([])),c=R(""),u=R(!1);function f(){return`http://${location.hostname}:8080/v1`}function _(P){return/^\d{1,3}(\.\d{1,3}){3}$/.test(P)}function T(){return location.protocol==="https:"||!_(location.hostname)}const h=q(()=>n(d).length?n(d):n(r).map(P=>P.name)),b=q(()=>n(h)[0]||"coder"),A=q(()=>{var P;return(P=n(r).find(N=>{var J,re;return(re=(J=N.meta)==null?void 0:J.caps)==null?void 0:re.includes("Bild")}))==null?void 0:P.name});function C(P,N){const re=(N.length?n(r).filter(x=>N.includes(x.name)):n(r).length?n(r):[{name:"coder",meta:{}}]).map(x=>{var te,$;const L=(((te=x.meta)==null?void 0:te.caps)||[]).some(H=>/Bild|Vision/i.test(H)),G=(($=x.meta)==null?void 0:$.ctx)||131072;return` { "name": "${x.name}", "display_name": "${x.name}", "max_tokens": ${G}${L?', "capabilities": { "tools": true, "images": true }':""} }`}).join(`,
`);return`"language_models": {
"openai_compatible": {
"bosgame": {
- "api_url": "${L}",
+ "api_url": "${P}",
"available_models": [
-${A}
+${re}
]
}
}
-}`}function E(L,K){const A=(K.length?K:n(r).length?n(r).map(q=>q.name):["coder"]).map(q=>` "${q}": { "name": "${q}" }`).join(`,
+}`}function S(P,N){const re=(N.length?N:n(r).length?n(r).map(x=>x.name):["coder"]).map(x=>` "${x}": { "name": "${x}" }`).join(`,
`);return`"provider": {
"llama-swap": {
"npm": "@ai-sdk/openai-compatible",
"name": "Bosgame (llama-swap)",
"options": {
- "baseURL": "${L}",
+ "baseURL": "${P}",
"apiKey": "local"
},
"models": {
-${A}
+${re}
}
}
-}`}async function I(){_(c,"Teste…");try{const L=await fe("/api/integration/test");_(u,L.ok,!0),_(c,L.ok?`✓ Verbunden — ${L.models.length} Modell(e): ${L.models.join(", ")}`:`✗ Keine Verbindung: ${L.error}`,!0)}catch(L){_(u,!1),_(c,"✗ "+L.message)}}function de(L){var w;const K=L.querySelector("code");K&&((w=navigator.clipboard)==null||w.writeText(K.textContent||""))}function Y(L){var K;(K=navigator.clipboard)==null||K.writeText(L.value)}var ce=Sc(),me=l(ft(ce),2),U=l(s(me),6),F=s(U),ie=l(F,2),he=l(U,2);{var _e=L=>{var K=fc();x(L,K)},Me=N(()=>$());V(he,L=>{n(Me)&&L(_e)})}var He=l(he,2),T=s(He),J=l(T,2);{var se=L=>{var K=pc(),w=s(K);z(()=>{jn(K,`color:${n(u)?"#7ee29a":"#ff9b95"}`),m(w,n(c))}),x(L,K)};V(J,L=>{n(c)&&L(se)})}var $e=l(me,2),Q=l(s($e),2);Ue(Q,20,()=>[["⚡","Kein manuelles Starten nötig",'Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden.'],["🔄","Modelle wechseln sich automatisch ab","Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) und lädt das neue. Es läuft immer nur eines gleichzeitig."],["🧠","Zwei Modelle gleichzeitig aktiv halten","Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen."]],Ge,(L,K)=>{var w=N(()=>Qa(K,3));let A=()=>n(w)[0],q=()=>n(w)[1],ee=()=>n(w)[2];var O=hc(),ue=s(O),B=s(ue),j=l(ue,2),te=s(j),ae=s(te),De=l(te,2),Ae=s(De);z(()=>{m(B,A()),m(ae,q()),m(Ae,ee())}),x(L,O)});var be=l($e,2);{var X=L=>{var K=mc(),w=s(K),A=l(s(w),2),q=s(A),ee=l(w,4);Ue(ee,21,()=>n(r),Ge,(B,j)=>{const te=N(()=>n(d).includes(n(j).name));var ae=gc(),De=s(ae);z(()=>{jn(ae,`cursor:pointer;${n(te)?"background:rgba(45,212,191,.2);border-color:var(--accent);color:var(--accent)":""}`),m(De,n(j).name)}),C("click",ae,()=>_(d,n(te)?n(d).filter(Ae=>Ae!==n(j).name):[...n(d),n(j).name],!0)),C("keydown",ae,Ae=>Ae.key==="Enter"&&_(d,n(te)?n(d).filter(Ce=>Ce!==n(j).name):[...n(d),n(j).name],!0)),x(B,ae)});var O=l(ee,2);{var ue=B=>{var j=_c(),te=s(j);C("click",te,ae=>{ae.preventDefault(),_(d,[],!0)}),x(B,j)};V(O,B=>{n(d).length&&B(ue)})}z(()=>m(q,n(d).length?`${n(d).length} von ${n(r).length} ausgewählt`:"alle werden eingeschlossen")),x(L,K)};V(be,L=>{n(r).length&&L(X)})}var ze=l(be,2),Se=l(s(ze),4);Ue(Se,20,()=>[["zed","Zed","Schneller nativer Editor — Empfohlen"],["opencode","OpenCode","Open-Source Coding-Agent (Terminal/Desktop)"],["cline","Cline / Cursor","VS Code Extension, Bring-Your-Own-Key"],["openwebui","OpenWebUI","Browser-Chat-Interface"]],Ge,(L,K)=>{var w=N(()=>Qa(K,3));let A=()=>n(w)[0],q=()=>n(w)[1],ee=()=>n(w)[2];var O=bc(),ue=s(O),B=s(ue),j=l(ue,2),te=s(j);z(()=>{st(O,1,`card-btn${n(o)===A()?" cb-best":""}`),m(B,q()),m(te,ee())}),C("click",O,()=>_(o,A(),!0)),x(L,O)});var Ie=l(ze,2),Ve=l(s(Ie),2);{var Fe=L=>{const K=N(()=>P(n(i),n(h)));var w=wc(),A=l(ft(w),4),q=s(A),ee=l(q,2),O=s(ee),ue=s(O),B=l(A,4),j=l(s(B)),te=s(j);z(()=>{m(ue,n(K)),m(te,`bosgame / ${n(b)??""}`)}),C("click",q,ae=>de(ae.currentTarget.parentElement)),x(L,w)},mt=L=>{const K=N(()=>E(n(i),n(h)));var w=yc(),A=l(ft(w),6),q=s(A),ee=l(q,2),O=s(ee),ue=s(O),B=l(A,2),j=l(s(B),3),te=s(j);z(()=>{m(ue,n(K)),m(te,`llama-swap/${n(b)??""}`)}),C("click",q,ae=>de(ae.currentTarget.parentElement)),x(L,w)},bt=L=>{var K=xc(),w=l(ft(K),4),A=s(w),q=l(A,2),ee=l(w,4),O=s(ee),ue=l(O,2);z(()=>{ur(A,n(i)),ur(O,n(b))}),C("click",q,B=>Y(B.currentTarget.previousElementSibling)),C("click",ue,B=>Y(B.currentTarget.previousElementSibling)),x(L,K)},Kt=L=>{var K=kc(),w=l(ft(K),4),A=s(w),q=l(A,2),ee=l(w,4),O=s(ee),ue=l(O,2);z(()=>ur(A,n(i))),C("click",q,B=>Y(B.currentTarget.previousElementSibling)),C("click",ue,B=>Y(B.currentTarget.previousElementSibling)),x(L,K)};V(Ve,L=>{n(o)==="zed"?L(Fe):n(o)==="opencode"?L(mt,1):n(o)==="cline"?L(bt,2):n(o)==="openwebui"&&L(Kt,3)})}var wt=l(Ie,2);{var Mt=L=>{const K=N(()=>`curl ${n(i)}/chat/completions \\
+}`}async function B(){g(c,"Teste…");try{const P=await fe("/api/integration/test");g(u,P.ok,!0),g(c,P.ok?`✓ Verbunden — ${P.models.length} Modell(e): ${P.models.join(", ")}`:`✗ Keine Verbindung: ${P.error}`,!0)}catch(P){g(u,!1),g(c,"✗ "+P.message)}}function ye(P){var J;const N=P.querySelector("code");N&&((J=navigator.clipboard)==null||J.writeText(N.textContent||""))}function ee(P){var N;(N=navigator.clipboard)==null||N.writeText(P.value)}var ce=Ec(),_e=l(ft(ce),2),I=l(s(_e),6),K=s(I),ie=l(K,2),ve=l(I,2);{var me=P=>{var N=pc();w(P,N)},Me=q(()=>T());V(ve,P=>{n(Me)&&P(me)})}var Ue=l(ve,2),E=s(Ue),Q=l(E,2);{var de=P=>{var N=hc(),J=s(N);O(()=>{Bn(N,`color:${n(u)?"#7ee29a":"#ff9b95"}`),m(J,n(c))}),w(P,N)};V(Q,P=>{n(c)&&P(de)})}var Ae=l(_e,2),Y=l(s(Ae),2);Ge(Y,20,()=>[["⚡","Kein manuelles Starten nötig",'Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest, lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden.'],["🔄","Modelle wechseln sich automatisch ab","Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf) und lädt das neue. Es läuft immer nur eines gleichzeitig."],["🧠","Zwei Modelle gleichzeitig aktiv halten","Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen."]],Ke,(P,N)=>{var J=q(()=>Ya(N,3));let re=()=>n(J)[0],x=()=>n(J)[1],L=()=>n(J)[2];var G=gc(),te=s(G),$=s(te),H=l(te,2),ne=s(H),he=s(ne),Ne=l(ne,2),Se=s(Ne);O(()=>{m($,re()),m(he,x()),m(Se,L())}),w(P,G)});var be=l(Ae,2);{var Z=P=>{var N=bc(),J=s(N),re=l(s(J),2),x=s(re),L=l(J,4);Ge(L,21,()=>n(r),Ke,($,H)=>{const ne=q(()=>n(d).includes(n(H).name));var he=_c(),Ne=s(he);O(()=>{Bn(he,`cursor:pointer;${n(ne)?"background:rgba(45,212,191,.2);border-color:var(--accent);color:var(--accent)":""}`),m(Ne,n(H).name)}),z("click",he,()=>g(d,n(ne)?n(d).filter(Se=>Se!==n(H).name):[...n(d),n(H).name],!0)),z("keydown",he,Se=>Se.key==="Enter"&&g(d,n(ne)?n(d).filter(je=>je!==n(H).name):[...n(d),n(H).name],!0)),w($,he)});var G=l(L,2);{var te=$=>{var H=mc(),ne=s(H);z("click",ne,he=>{he.preventDefault(),g(d,[],!0)}),w($,H)};V(G,$=>{n(d).length&&$(te)})}O(()=>m(x,n(d).length?`${n(d).length} von ${n(r).length} ausgewählt`:"alle werden eingeschlossen")),w(P,N)};V(be,P=>{n(r).length&&P(Z)})}var ze=l(be,2),Ce=l(s(ze),4);Ge(Ce,20,()=>[["zed","Zed","Schneller nativer Editor — Empfohlen"],["opencode","OpenCode","Open-Source Coding-Agent (Terminal/Desktop)"],["cline","Cline / Cursor","VS Code Extension, Bring-Your-Own-Key"],["openwebui","OpenWebUI","Browser-Chat-Interface"]],Ke,(P,N)=>{var J=q(()=>Ya(N,3));let re=()=>n(J)[0],x=()=>n(J)[1],L=()=>n(J)[2];var G=yc(),te=s(G),$=s(te),H=l(te,2),ne=s(H);O(()=>{lt(G,1,`card-btn${n(o)===re()?" cb-best":""}`),m($,x()),m(ne,L())}),z("click",G,()=>g(o,re(),!0)),w(P,G)});var He=l(ze,2),Ie=l(s(He),2);{var Re=P=>{const N=q(()=>C(n(i),n(h)));var J=wc(),re=l(ft(J),4),x=s(re),L=l(x,2),G=s(L),te=s(G),$=l(re,4),H=l(s($)),ne=s(H);O(()=>{m(te,n(N)),m(ne,`bosgame / ${n(b)??""}`)}),z("click",x,he=>ye(he.currentTarget.parentElement)),w(P,J)},_t=P=>{const N=q(()=>S(n(i),n(h)));var J=xc(),re=l(ft(J),6),x=s(re),L=l(x,2),G=s(L),te=s(G),$=l(re,2),H=l(s($),3),ne=s(H);O(()=>{m(te,n(N)),m(ne,`llama-swap/${n(b)??""}`)}),z("click",x,he=>ye(he.currentTarget.parentElement)),w(P,J)},mt=P=>{var N=kc(),J=l(ft(N),4),re=s(J),x=l(re,2),L=l(J,4),G=s(L),te=l(G,2);O(()=>{ur(re,n(i)),ur(G,n(b))}),z("click",x,$=>ee($.currentTarget.previousElementSibling)),z("click",te,$=>ee($.currentTarget.previousElementSibling)),w(P,N)},Kt=P=>{var N=Mc(),J=l(ft(N),4),re=s(J),x=l(re,2),L=l(J,4),G=s(L),te=l(G,2);O(()=>ur(re,n(i))),z("click",x,$=>ee($.currentTarget.previousElementSibling)),z("click",te,$=>ee($.currentTarget.previousElementSibling)),w(P,N)};V(Ie,P=>{n(o)==="zed"?P(Re):n(o)==="opencode"?P(_t,1):n(o)==="cline"?P(mt,2):n(o)==="openwebui"&&P(Kt,3)})}var St=l(He,2);{var Et=P=>{const N=q(()=>`curl ${n(i)}/chat/completions \\
-H "Content-Type: application/json" \\
-d '{
- "model": "${n(S)}",
+ "model": "${n(A)}",
"messages": [{"role":"user","content":[{"type":"text","text":"Was ist auf dem Bild falsch?"},{"type":"image_url","image_url":{"url":"data:image/png;base64,"}}]}]
-}'`);var w=Mc(),A=s(w),q=l(s(A),2),ee=s(q);Qe(ee,()=>ea("check"));var O=l(ee),ue=l(A,4),B=s(ue),j=l(B,2),te=s(j),ae=s(te);z(()=>{m(O,` „${n(S)??""}" bereit`),m(ae,n(K))}),C("click",B,De=>de(De.currentTarget.parentElement)),x(L,w)};V(wt,L=>{n(S)&&L(Mt)})}z(()=>ur(F,n(i))),C("click",ie,L=>{const K=L.currentTarget.previousElementSibling;Y(K)}),C("click",T,I),x(e,ce),Zn()}_a(["click","keydown"]);var Tc=M('
'),$c=M('
Für dich: Upgrades
Neuere Modelle, die ein installiertes ersetzen können.
'),Ac=M('
Lade News… (erstes Laden kann kurz dauern)
'),Cc=M('
'),Lc=M('
Keine News geladen
Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
'),Pc=M('für dich'),zc=M('
'),Dc=M(''),Oc=M('
News
Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
',1);function Rc(e,t){Yn(t,!0);let a=R(Bt([])),r=R(Bt([])),i=R(!0),o=R("");const d=U=>U==="perfect"?"ok":U==="marginal"?"warn":"bad";function c(U){const F=Math.max(0,Date.now()/1e3-U);return F<60?F+"s":F<3600?Math.floor(F/60)+"m":F<86400?Math.floor(F/3600)+"h":Math.floor(F/86400)+"d"}async function u(){_(i,!0),_(o,"");try{_(a,(await fe("/api/news")).items||[],!0)}catch(U){_(o,U.message||"Fehler beim Laden",!0)}finally{_(i,!1)}}async function f(){try{_(r,(await fe("/api/cookbook/upgrades")).upgrades||[],!0)}catch{_(r,[],!0)}}async function g(U,F){var ie;F.disabled=!0,F.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:U.repo,role:U.role,params_b:U.params_b,quant:U.quant,hf_token:Ja()})}),(ie=document.querySelector(".nav-item[data-view='activity']"))==null||ie.dispatchEvent(new MouseEvent("click"))}catch(he){F.disabled=!1,F.textContent="Installieren",alert("Fehler: "+he.message)}}function $(){u(),f()}or(()=>{u(),f()});var h=Oc(),b=ft(h),S=l(s(b),2),P=l(b,2);{var E=U=>{var F=$c(),ie=s(F),he=l(s(ie),2),_e=s(he),Me=l(ie,4);Ue(Me,21,()=>n(r),Ge,(He,T)=>{var J=Tc(),se=s(J),$e=s(se),Q=s($e),be=s(Q),X=l(Q,2),ze=s(X),Se=l($e,2),Ie=s(Se),Ve=l(se,2);z(Fe=>{m(be,n(T).name),st(X,1,`fit-badge ${Fe??""}`),m(ze,n(T).fit.text),m(Ie,`Ersetzt dein „${n(T).old??""}". ${n(T).why??""}`)},[()=>d(n(T).fit.level)]),C("click",Ve,Fe=>g(n(T),Fe.currentTarget)),x(He,J)}),z(()=>m(_e,n(r).length)),x(U,F)};V(P,U=>{n(r).length&&U(E)})}var I=l(P,2);{var de=U=>{var F=Ac();x(U,F)},Y=U=>{var F=Cc(),ie=s(F);z(()=>m(ie,n(o))),x(U,F)},ce=U=>{var F=Lc();x(U,F)},me=U=>{var F=Dc();Ue(F,21,()=>n(a),Ge,(ie,he,_e)=>{var Me=zc();st(Me,1,`news-card${_e===0?" featured":""}`);var He=s(Me),T=s(He),J=s(T),se=l(T,2);{var $e=Se=>{var Ie=Pc();x(Se,Ie)};V(se,Se=>{n(he).relevant&&Se($e)})}var Q=l(se,2),be=s(Q),X=l(He,2),ze=s(X);z(Se=>{Lr(Me,"href",n(he).link),m(J,n(he).source),m(be,Se),m(ze,n(he).title)},[()=>n(he).ts?c(n(he).ts):""]),x(ie,Me)}),x(U,F)};V(I,U=>{n(i)?U(de):n(o)?U(Y,1):n(a).length?U(me,-1):U(ce,2)})}C("click",S,$),x(e,h),Zn()}_a(["click"]);const Bc=[["Dein erster Workflow — in 3 Schritten",`
+}'`);var J=Sc(),re=s(J),x=l(s(re),2),L=s(x);Ye(L,()=>ea("check"));var G=l(L),te=l(re,4),$=s(te),H=l($,2),ne=s(H),he=s(ne);O(()=>{m(G,` „${n(A)??""}" bereit`),m(he,n(N))}),z("click",$,Ne=>ye(Ne.currentTarget.parentElement)),w(P,J)};V(St,P=>{n(A)&&P(Et)})}O(()=>ur(K,n(i))),z("click",ie,P=>{const N=P.currentTarget.previousElementSibling;ee(N)}),z("click",E,B),w(e,ce),Zn()}_a(["click","keydown"]);var $c=M('
'),Ac=M('
Für dich: Upgrades
Neuere Modelle, die ein installiertes ersetzen können.
'),Cc=M('
Lade News… (erstes Laden kann kurz dauern)
'),Lc=M('
'),zc=M('
Keine News geladen
Die Quellen sind gerade nicht erreichbar — später nochmal probieren.
'),Pc=M('für dich'),Dc=M('
'),Oc=M(''),Rc=M('
News
Aktuelles aus der Local-LLM-Welt — aus kuratierten Quellen.
',1);function Bc(e,t){Yn(t,!0);let a=R(Bt([])),r=R(Bt([])),i=R(!0),o=R("");const d=I=>I==="perfect"?"ok":I==="marginal"?"warn":"bad";function c(I){const K=Math.max(0,Date.now()/1e3-I);return K<60?K+"s":K<3600?Math.floor(K/60)+"m":K<86400?Math.floor(K/3600)+"h":Math.floor(K/86400)+"d"}async function u(){g(i,!0),g(o,"");try{g(a,(await fe("/api/news")).items||[],!0)}catch(I){g(o,I.message||"Fehler beim Laden",!0)}finally{g(i,!1)}}async function f(){try{g(r,(await fe("/api/cookbook/upgrades")).upgrades||[],!0)}catch{g(r,[],!0)}}async function _(I,K){var ie;K.disabled=!0,K.textContent="Starte…";try{await fe("/api/cookbook/install-model",{method:"POST",body:JSON.stringify({repo:I.repo,role:I.role,params_b:I.params_b,quant:I.quant,hf_token:Qa()})}),(ie=document.querySelector(".nav-item[data-view='activity']"))==null||ie.dispatchEvent(new MouseEvent("click"))}catch(ve){K.disabled=!1,K.textContent="Installieren",alert("Fehler: "+ve.message)}}function T(){u(),f()}or(()=>{u(),f()});var h=Rc(),b=ft(h),A=l(s(b),2),C=l(b,2);{var S=I=>{var K=Ac(),ie=s(K),ve=l(s(ie),2),me=s(ve),Me=l(ie,4);Ge(Me,21,()=>n(r),Ke,(Ue,E)=>{var Q=$c(),de=s(Q),Ae=s(de),Y=s(Ae),be=s(Y),Z=l(Y,2),ze=s(Z),Ce=l(Ae,2),He=s(Ce),Ie=l(de,2);O(Re=>{m(be,n(E).name),lt(Z,1,`fit-badge ${Re??""}`),m(ze,n(E).fit.text),m(He,`Ersetzt dein „${n(E).old??""}". ${n(E).why??""}`)},[()=>d(n(E).fit.level)]),z("click",Ie,Re=>_(n(E),Re.currentTarget)),w(Ue,Q)}),O(()=>m(me,n(r).length)),w(I,K)};V(C,I=>{n(r).length&&I(S)})}var B=l(C,2);{var ye=I=>{var K=Cc();w(I,K)},ee=I=>{var K=Lc(),ie=s(K);O(()=>m(ie,n(o))),w(I,K)},ce=I=>{var K=zc();w(I,K)},_e=I=>{var K=Oc();Ge(K,21,()=>n(a),Ke,(ie,ve,me)=>{var Me=Dc();lt(Me,1,`news-card${me===0?" featured":""}`);var Ue=s(Me),E=s(Ue),Q=s(E),de=l(E,2);{var Ae=Ce=>{var He=Pc();w(Ce,He)};V(de,Ce=>{n(ve).relevant&&Ce(Ae)})}var Y=l(de,2),be=s(Y),Z=l(Ue,2),ze=s(Z);O(Ce=>{zr(Me,"href",n(ve).link),m(Q,n(ve).source),m(be,Ce),m(ze,n(ve).title)},[()=>n(ve).ts?c(n(ve).ts):""]),w(ie,Me)}),w(I,K)};V(B,I=>{n(i)?I(ye):n(o)?I(ee,1):n(a).length?I(_e,-1):I(ce,2)})}z("click",A,T),w(e,h),Zn()}_a(["click"]);const qc=[["Dein erster Workflow — in 3 Schritten",`
Modell holen: Tab Cookbook → wähle deinen Anwendungsfall (z.B. „Coden") →
Komplettes Setup installieren. Der Download läuft im Tab Aktivität.
@@ -109,4 +109,4 @@ ${A}
OpenWebUI: Klassische Chat-Oberfläche für Fragen/Texte.
Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles
- (scout) für einfache — du wählst es einfach im Tool aus.
`]],qc=[["LLM / Modell","Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],["GGUF","Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],["Quantisierung (Q4_K_M)","Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],["Kontextfenster","Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],["Auto-Swap","llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],["Agent","Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."],["MCP","Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],["Skills","Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."],["Rules / Commands","Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."]];var Nc=M(''),Ic=M('
'),Fc=M('
Guide für Einsteiger
Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.
Tutorials & Workflows
Konzepte kurz erklärt
',1);function Kc(e){var t=Fc(),a=l(ft(t),4);Ue(a,21,()=>Bc,Ge,(i,o,d)=>{var c=N(()=>Qa(n(o),2));let u=()=>n(c)[0],f=()=>n(c)[1];var g=Nc();g.open=d===0;var $=s(g),h=s($),b=l($,2);Qe(b,f,!0),z(()=>m(h,u())),x(i,g)});var r=l(a,4);Ue(r,21,()=>qc,Ge,(i,o)=>{var d=N(()=>Qa(n(o),2));let c=()=>n(d)[0],u=()=>n(d)[1];var f=Ic(),g=s(f),$=s(g),h=l(g,2),b=s(h);z(()=>{m($,c()),m(b,u())}),x(i,f)}),x(e,t)}let Hi={};function Gc(){const e=document.getElementById("top-active-text");e&&(e.classList.add("swap-flash"),setTimeout(()=>e.classList.remove("swap-flash"),900))}function Vi(e){const t=document.getElementById("swdot"),a=document.getElementById("swlabel"),r=document.getElementById("top-active-text");if(!e)t&&(t.className="dot off"),a&&(a.textContent="Backend nicht erreichbar"),r&&(r.textContent="Backend offline"),Wi("Backend nicht erreichbar – läuft uvicorn?",!1);else{const i=(e.swap_url||"").replace(/^https?:\/\//,"");t&&(t.className="dot "+(e.swap_ok?"on":"off")),a&&(a.textContent=(e.swap_ok?"LLM-Engine: Online – ":"LLM-Engine: Offline – ")+i);const o=(e.models||[]).find(c=>c.state==="running");r&&(r.innerHTML=o?`Geladen: ${o.name}`:"Kein Modell im VRAM"),e.swap_ok?Wc():Wi(`LLM-Engine nicht erreichbar unter ${i} – läuft der llama-swap Dienst?`,!0);const d={};for(const c of e.models||[])d[c.name]=c.state;for(const[c,u]of Object.entries(d)){const f=Hi[c];if(f&&f!==u&&(u==="running"||u==="loading")){Gc();break}}Hi=d}Uc(e),dr.set(e)}function Uc(e){const t=document.getElementById("sec-chip"),a=document.getElementById("sec-chip-tx");!t||!a||(t.classList.toggle("ok",!!(e!=null&&e.secured)),a.textContent=e!=null&&e.secured?"Geschützt · Heimnetz":"Nur im Heimnetz (offen)")}function Hc(e){ra.set(e||[])}function Vc(e){yi.set(e)}function Wi(e,t){const a=document.getElementById("alert");a.className="alert"+(t?" warn":""),a.innerHTML=`${e}`,a.style.display="flex"}function Wc(){const e=document.getElementById("alert");e&&(e.style.display="none")}const jc=e=>{var t;return(t=document.querySelector(`.nav-item[data-view="${e}"]`))==null?void 0:t.click()};function Ur(e,t,a,r){const i=document.createElement("span");return i.className="upd-badge"+(t?" "+t:""),i.textContent=e,r&&(i.title=r),i.onclick=()=>jc(a),i}async function js(){try{const e=await fe("/api/updates"),t=document.getElementById("update-badges");if(!t)return;const a=e.apt_cache_age_h,r=a!=null&&a>24,i=a!=null?` · apt-Cache: vor ${a}h`:"";t.innerHTML="",t.appendChild(Ur(e.os>0?`OS: ${e.os} Updates`:r?"OS: ?":"OS: aktuell",e.os>0||r?"warn":"ok","server",e.os>0?`${e.os} Pakete${i}`:r?`apt-Cache veraltet (${a}h)`:`Keine Updates${i}`)),t.appendChild(Ur(e.engine>0?"Engine: Update":"Engine: aktuell",e.engine>0?"warn":"ok","server")),t.appendChild(Ur(e.models>0?`Modelle: ${e.models}`:"Modelle: aktuell",e.models>0?"warn":"ok","news"))}catch{}}async function zr(){try{Vi(await fe("/api/status"))}catch{Vi(null)}}async function Js(){try{Hc(await fe("/api/jobs"))}catch{}}function Qs(){const e=window.location.protocol==="https:"?"wss:":"ws:",t=new WebSocket(`${e}//${window.location.host}/api/system/stream`);t.onmessage=a=>{try{Vc(JSON.parse(a.data))}catch{}},t.onclose=()=>{setTimeout(Qs,3e3)},t.onerror=()=>t.close()}var Xi;((Xi=document.getElementById("logo"))==null?void 0:Xi.innerHTML)!==void 0&&(document.getElementById("logo").innerHTML=Pr.logo);document.querySelectorAll("[data-ic]").forEach(e=>{e.innerHTML=Pr[e.dataset.ic||""]||""});const Jc=[["overview",ed],["models",hd],["activity",Ed],["server",Rd],["cookbook",vc],["connect",Ec],["news",Rc],["guides",Kc]];for(const[e,t]of Jc){const a=document.querySelector(`.view[data-view="${e}"]`);a&&wo(t,{target:a})}Fo("overview");const Hr=document.getElementById("token");Hr&&(Hr.value=Ws(),Hr.addEventListener("change",e=>{Ro(e.target.value),zr()}));const Vr=document.getElementById("hf-token");Vr&&(Vr.value=Ja(),Vr.addEventListener("change",e=>qo(e.target.value)));const ji=document.getElementById("nav-settings"),Wr=document.getElementById("settings-modal"),Ji=document.getElementById("sm-close");ji&&Wr&&Ji&&(ji.addEventListener("click",()=>Wr.style.display="flex"),Ji.addEventListener("click",()=>Wr.style.display="none"));function Qc(){const e=document.getElementById("clock");e&&(e.textContent=new Date().toTimeString().slice(0,5))}document.addEventListener("mc:refresh",zr);zr();Js();Qs();setInterval(Qc,1e3);setInterval(zr,3e3);setInterval(Js,1500);js();setInterval(js,6e4);
+ (scout) für einfache — du wählst es einfach im Tool aus.
`]],Ic=[["LLM / Modell","Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],["GGUF","Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],["Quantisierung (Q4_K_M)","Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],["Kontextfenster","Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],["Auto-Swap","llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],["Agent","Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist."],["MCP","Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],["Skills","Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen."],["Rules / Commands","Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue)."]];var Nc=M(''),Fc=M('
'),Kc=M('
Guide für Einsteiger
Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.
Stelle sicher, dass die LLM-Engine erreichbar ist, bevor du Tools konfigurierst.
- ${field("Engine-Adresse (für alle Tools)", url)}
- ${behindProxy() ? `
- Du öffnest Mission Control über einen Proxy/Namen (HTTPS o.ä.). Die Engine läuft auf Port 8080 ohne HTTPS direkt am Bosgame.
- Trag in deinen Tools die LAN-IP ein: http://192.168.178.151:8080/v1 — nicht den Proxy-Namen.
Wenn du in Zed oder OpenCode „coder" auswählst und eine Anfrage sendest,
- lädt llama-swap das Modell automatisch — in ca. 2–5 Sekunden. Du siehst es hier im Dashboard unter „Aktives Modell".
-
-
-
- 🔄
-
-
Modelle wechseln sich automatisch ab
-
Rufst du ein anderes Modell auf, entlädt llama-swap das aktuelle (nach TTL-Ablauf)
- und lädt das neue. Es läuft immer nur eines gleichzeitig — llama-swap verwaltet das komplett selbst.
-
-
-
- 🧠
-
-
Zwei Modelle gleichzeitig aktiv halten
-
Du hast 124 GB RAM — genug für z.B. Scout 8B (~5 GB) dauerhaft geladen + ein
- größeres Modell das swappt. Dafür beim Scout im Modelle-Tab → Konfigurieren die TTL auf 99999 setzen
- (= nie automatisch entladen). Das kleine Modell beantwortet schnelle Fragen sofort, ohne jede Wartezeit.
Öffne die Zed-Settings mit Cmd/Strg + , und füge diesen Block in deine bestehende
- settings.json ein. Falls bereits ein "bosgame"-Eintrag existiert: vorher löschen.
-
- Nur diesen Block einfügen, nicht die ganze Datei ersetzen — sonst gehen andere Einstellungen verloren.
-
-
-
-
${esc(block)}
-
-
API-Key: Öffne den Agent-Panel → Anbieter bosgame → trag irgendeinen Wert ein (z.B. local).
- Alternativ BOSGAME_API_KEY=local als Umgebungsvariable.
-
Danach im Agent-Panel das Modell bosgame / ${esc(firstModel)} auswählen. Das Auto-Swap lädt es beim ersten Request.
Windows: %USERPROFILE%\\.config\\opencode\\opencode.json
- Linux/Mac: ~/.config/opencode/opencode.json
- Oder im Projektordner: opencode.json
-
- Nur den "provider"-Block einfügen oder mergen.
- Falls bereits ein "llama-swap"-Eintrag vorhanden ist: vorher löschen, sonst erscheinen Modelle doppelt.
-
-
-
-
${esc(block)}
-
-
Danach in OpenCode /models → llama-swap/${esc(firstModel)} wählen.
-
Statt alles zu ersetzen: nur den provider-Block in deine bestehende Config einfügen — so bleiben andere Anbieter erhalten.
Modell holen: Tab Cookbook → wähle deinen Anwendungsfall (z.B. „Coden") →
- Komplettes Setup installieren. Der Download läuft im Tab Aktivität.
-
Verbinden: Tab Verbinden → Basis-URL kopieren → in deinem Tool (z.B. OpenCode)
- als „OpenAI-kompatibel" einfügen. Verbindung testen bestätigt, dass es läuft.
-
Arbeiten: Im Tool das Modell (z.B. coder) wählen und loslegen. Das passende
- Modell wird automatisch geladen — du musst nichts starten.
- `],
- ["Wie leere ich den Kontext? Machen die Tools das selbst?", `
-
Kurz: meistens ja, automatisch. Der Kontext (das „Gedächtnis") gilt pro Unterhaltung.
- Startest du im Tool einen neuen Chat / eine neue Session, ist der Kontext wieder leer —
- das Tool verwaltet das Fenster für dich.
-
Du musst nur selten eingreifen: wird ein Gespräch sehr lang, fang einen neuen an. Wichtig:
- „Grafikspeicher leeren" (Tab Server) ist etwas anderes — das wirft das Modell aus dem Speicher,
- nicht den Gesprächsverlauf.
`],
- ["Was passiert, wenn der Kontext volläuft?", `
-
Ist das Kontextfenster voll, passiert je nach Tool eins von dreien: das Modell vergisst den
- Anfang (älteste Nachrichten fallen raus), das Tool kürzt automatisch, oder du bekommst
- einen Fehler wie „context length exceeded".
-
So gehst du vor:
-
-
Neuen Chat starten — der einfachste Reset, Kontext ist wieder leer.
-
Zusammenfassen lassen: „Fasse unseren Stand kurz zusammen", dann im neuen Chat einfügen.
-
Kontext vergrößern: Tab Modelle → Konfigurieren → wir zeigen das Optimum
- für deine Hardware (mehr Kontext = mehr Speicherbedarf).
- `],
- ["Was ist ein Agent — und wann brauche ich einen?", `
-
Ein nacktes Modell schreibt nur Text zurück. Ein Agent ist ein Programm drumherum,
- das das Modell handeln lässt: Dateien lesen und ändern, Befehle ausführen, im Web suchen —
- Schritt für Schritt in einer Schleife, bis die Aufgabe erledigt ist.
-
Bild dazu: Reiner Chat = du fragst einen Experten und tippst seine Antwort selbst ab.
- Agent = der Experte sitzt an deinem PC und macht es selbst — du schaust zu und bestätigst.
-
Wann lohnt sich was?
-
-
Fragen, Texte, Erklärungen, Brainstorming → kein Agent nötig, eine Chat-Oberfläche
- wie OpenWebUI reicht völlig.
-
Coden über mehrere Dateien, Befehle ausführen, recherchieren + umsetzen, automatisieren
- → Agent (OpenCode, Cline, Continue).
-
-
So arbeitest du damit: Du beschreibst ein Ziel in Worten („baue Feature X", „finde den
- Bug in dieser Datei"). Der Agent macht sich einen Plan und arbeitet ihn ab; heikle Schritte bestätigst
- du. Lass ihn am besten in einem Projektordner mit Git arbeiten — dann ist jede Änderung
- nachvollziehbar und rückgängig zu machen.
-
Gut zu wissen: Diese Agenten laufen lokal auf deinem PC und reden mit deinem lokalen
- Modell. Du brauchst keinen Cloud-Dienst — die „empfohlenen Tools" im Tab Verbinden SIND
- die Agenten.
`],
- ["Was sind Skills — und wie installiere ich sie?", `
-
Ein Skill ist verpacktes Können: eine kurze Anleitung (oft + ein paar Hilfsskripte),
- die dem Agenten beibringt, eine wiederkehrende Aufgabe jedes Mal so zu erledigen, wie du
- es willst. Stell es dir wie ein Rezept oder eine Arbeitsanweisung vor, die der Agent liest, sobald sie passt.
-
Skill vs. MCP — nicht verwechseln:MCP gibt dem Agenten neue Werkzeuge (Aktionen,
- z.B. „auf eine Datenbank zugreifen"). Skills geben ihm Know-how (wie er etwas gut macht)
- mit den Werkzeugen, die er schon hat. Kurz: Werkzeuge = MCP, Anleitungen = Skills.
-
Wofür gut?
-
-
Einheitlichkeit: „Commit-Nachrichten immer in diesem Format", „unsere Komponenten sehen so aus".
-
Wiederholung sparen: du musst denselben Kontext nicht jedes Mal neu erklären.
-
Wissen festhalten: „so testen wir", „so wird ein Changelog geschrieben", Fach-Workflows.
-
-
In deinen Tools heißt das konkret:
-
-
OpenCode: Projektregeln in einer AGENTS.md + eigene Befehle (Commands).
-
Cline: Regeln als .clinerules + „Workflows".
-
Continue: „Rules" + „Prompts".
-
Offener Standard „Agent Skills": ein Ordner mit einer SKILL.md (Beschreibung +
- Anleitung, optional Skripte) — zunehmend von mehreren Tools unterstützt.
-
-
Installieren = meist nur eine Datei/einen Ordner ablegen (kein Build, kein Neustart-Zauber):
-
-
Pro Projekt: die Datei in den Projektordner legen (z.B. AGENTS.md,
- .clinerules/) — gilt nur für dieses Projekt.
-
Global: in den Konfig-Ordner des Tools — gilt überall. Das Tool liest es beim nächsten Start.
-
-
Beste Quellen: zuerst die Doku deines Tools (OpenCode/Cline/Continue) — am verlässlichsten.
- Dann „awesome"-Listen auf GitHub (z.B. awesome-claude-skills, awesome-mcp-servers) und
- die quelloffenen Anthropic-Skills als Vorlagen, um das Format zu lernen.
-
Für lokale Modelle wichtig: Skills sind nur Text-Anleitungen
- und funktionieren mit jedem Modell — aber kleinere lokale Modelle befolgen komplexe Skills weniger
- zuverlässig als große. Halte sie kurz, klar und konkret.
MCP (Model Context Protocol) ist ein einheitlicher „Stecker", über den ein Agent zusätzliche
- Werkzeuge bekommt: das Web durchsuchen, Dateien lesen, eine Datenbank abfragen, GitHub bedienen …
- Ohne MCP kann das Modell nur reden; mit MCP kann es etwas tun.
-
So nutzt du es: Du fügst in deinem Agenten einen „MCP-Server" hinzu (eine kleine Zeile in der
- Konfiguration je Werkzeug). Danach entscheidet der Agent selbst, wann er das Werkzeug einsetzt.
-
-
Unterstützt: Cline und Continue haben das beste MCP-Ökosystem; viele fertige Server gibt es
- auf GitHub (Suchbegriff awesome-mcp-servers).
-
Faustregel: Fehlt dem Agenten eine Fähigkeit (z.B. Web-Zugriff) → MCP. Soll er etwas
- nur besser/einheitlicher machen → Skill.
-
`],
- ["Auto-Swap — wie das Laden funktioniert", `
-
llama-swap lädt automatisch das Modell, das gerade angefragt wird, und entlädt es nach einer
- Leerlaufzeit wieder (oder wenn du ein anderes anfragst). Es läuft immer nur eines gleichzeitig.
-
Heißt für dich: Du richtest beliebig viele Modelle ein und wählst im Tool einfach das passende —
- das Laden/Entladen passiert im Hintergrund, ohne dass du etwas tun musst.
OpenCode: Autonomes Coden im Terminal/Editor — ideal für „bau mir Feature X".
-
Cline: Dasselbe als VS-Code-Erweiterung, mit Bestätigung vor jeder Aktion.
-
OpenWebUI: Klassische Chat-Oberfläche für Fragen/Texte.
-
-
Tipp: starkes Modell (coder) für schwere Aufgaben, schnelles
- (coder-fast) für simple — du wählst es einfach im Tool aus.
`],
-];
-
-const CONCEPTS = [
- ["LLM / Modell", "Die KI selbst — versteht und schreibt Text (wie ChatGPT, nur lokal auf deinem PC). Ein Modell ist eine konkrete Datei davon, oft auf etwas spezialisiert (Code, Bilder, Chat)."],
- ["GGUF", "Das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF kann sie nicht nutzen."],
- ["Quantisierung (Q4_K_M)", "Verkleinert ein Modell (16-Bit → 4-Bit), damit es in den Speicher passt — bei kaum Qualitätsverlust. Q4_K_M ist der beliebte Sweetspot."],
- ["Kontextfenster", "Das Kurzzeitgedächtnis: wie viel Text sich das Modell gleichzeitig merkt. Größer = mehr Überblick, mehr Speicher, etwas langsamer."],
- ["Auto-Swap", "llama-swap lädt/entlädt Modelle automatisch — immer nur eins gleichzeitig. Du managst nichts."],
- ["Agent", "Ein Programm (OpenCode, Cline, Continue), das das Modell handeln lässt — Dateien lesen/ändern, Befehle ausführen, planen, im Loop bis die Aufgabe erledigt ist. Reiner Chat schreibt nur Text zurück."],
- ["MCP", "Model Context Protocol — der Anschluss, über den ein Agent zusätzliche Werkzeuge bekommt (Web, Dateien, Datenbank). MCP = Werkzeuge."],
- ["Skills", "Verpackte Anleitung für wiederkehrende Aufgaben — der Agent liest sie und macht es jedes Mal wie du willst. Skills = Anleitungen (das Gegenstück zu MCP-Werkzeugen)."],
- ["Rules / Commands", "Die konkrete Form von Skills je Tool: AGENTS.md (OpenCode), .clinerules (Cline), Rules/Prompts (Continue) — eine Datei im Projekt- oder Konfig-Ordner."],
-];
-
-function mount() {
- const c = $(".view[data-view='guides']"); if (!c) return;
- c.innerHTML = `
-
-
Guide für Einsteiger
-
Praktische Workflows + die Local-LLM-Welt in Klartext — ohne Vorwissen.
-
-
Tutorials & Workflows
-
- ${TUTORIALS.map(([t, b], i) => `
- ${t}
-
${b}
`).join("")}
-
-
-
Konzepte kurz erklärt
-
- ${CONCEPTS.map(([term, text]) => `
${term}
${text}
`).join("")}
-
`;
-}
-
-export default { id: "guides", mount };
diff --git a/static/js/panels/jobs.js b/static/js/panels/jobs.js
deleted file mode 100644
index 39a8001..0000000
--- a/static/js/panels/jobs.js
+++ /dev/null
@@ -1,155 +0,0 @@
-// jobs.js — Aktivität (v3): Live-System-Metriken + Hintergrund-Jobs mit Log.
-// Exportiert track(id), damit andere Panels einen Job auto-aufklappen.
-
-import { api } from "../core/api.js";
-import { $, esc, fmtBytes, toast } from "../core/ui.js";
-
-const tracked = new Set();
-let JOBS = [];
-let SYS = null;
-
-export function track(id) { tracked.add(id); renderJobs(); }
-
-const hist = { cpu: [], ram: [], gpu: [] };
-const MAX_HIST = 60;
-
-function statusBadge(s) { return s === "done" ? 'fertig' : s === "failed" ? 'fehler' : s === "canceled" ? 'abgebrochen' : 'läuft…'; }
-function dotClass(s) { return s === "done" ? "on" : (s === "failed" || s === "canceled") ? "" : "load"; }
-
-// Fortschritt in %: bevorzugt der exakte Wert vom Backend (Download-Datei-Polling),
-// sonst Fallback auf eine %-Angabe in der letzten Log-Zeile.
-function jobPct(j) {
- if (j.state !== "running") return null;
- if (typeof j.progress === "number") return Math.min(100, j.progress);
- const last = (j.log || [])[j.log.length - 1] || "";
- const m = last.match(/(\d+(?:\.\d+)?)\s*%/);
- return m ? Math.min(100, parseFloat(m[1])) : null;
-}
-
-// Restzeit menschlich: "noch ~2 min" / "noch ~45 s".
-function fmtEta(s) {
- if (s == null || s < 0) return "";
- if (s >= 90) return `noch ~${Math.round(s / 60)} min`;
- return `noch ~${Math.max(1, Math.round(s))} s`;
-}
-
-async function cancelJob(id) {
- try { await api(`/api/jobs/${id}/cancel`, { method: "POST" }); toast("Abbruch angefordert…"); renderJobs(); }
- catch (e) { toast(e.message, true); }
-}
-
-async function deleteJob(id) {
- try { await api(`/api/jobs/${id}`, { method: "DELETE" }); renderJobs(); }
- catch (e) { toast(e.message, true); }
-}
-
-async function clearJobs() {
- try { const r = await api("/api/jobs/clear", { method: "POST" }); toast(`${r.removed} Einträge entfernt.`); renderJobs(); }
- catch (e) { toast(e.message, true); }
-}
-
-function tile(label, value, sub) {
- return `
Dienste steuern, Updates einspielen und live mitlesen — ganz ohne SSH oder Terminal.
-
-
-
-
LLM-Engine (llama-swap)
- prüfe…
-
Das Herzstück: llama-swap lädt bei jeder Anfrage automatisch das passende Modell und entlädt die anderen — es läuft immer nur eines gleichzeitig.
-
-
Adresse (für deine Tools)—
-
Aktuell geladen—
-
Konfigurierte Modelle—
-
-
Wie ansteuern? In deinem Tool (Zed/OpenCode) die obige Adresse als OpenAI-Basis-URL eintragen und einen Modellnamen oder eine Rolle angeben (siehe „Verbinden"). Eine einzige Anfrage genügt — das Modell wird automatisch geweckt.
-
-
-
-
-
Dienste & Applikation
-
Diese Aktionen sind harmlos und passwortlos — es geht nichts dabei verloren.
- ${row("w-restart-swap", "refresh", "teal", "LLM-Engine neustarten", "Startet llama-swap neu. Geladene Modelle werden kurz entladen (~5 Sek), laden danach automatisch wieder.")}
- ${row("w-restart-mc", "refresh", "blue", "Dashboard neustarten", "Startet diese Oberfläche neu. Sie trennt kurz und verbindet automatisch wieder.")}
- ${row("w-self-update", "download", "teal", "Mission Control aktualisieren", "Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu (~10 Sek).")}
- ${row("w-update", "bolt", "blue", "LLM-Engine aktualisieren", "Lädt die neueste llama.cpp-Version (ROCm) für deine GPU und installiert sie.")}
- ${row("w-unload", "database", "amber", "Grafikspeicher leeren", "Wirft alle aktuell geladenen Modelle aus dem VRAM. Fragt vorher nach.")}
-
-
-
-
Betriebssystem (Bosgame)
-
Tiefe Eingriffe — das Dashboard fragt einmalig nach deinem sudo-Passwort.
- ${row("w-os-update", "refresh", "amber", "OS-Updates installieren", "Führt apt update & upgrade aus. Kann ein paar Minuten dauern — Fortschritt in der Aktivität.")}
- ${row("w-reboot", "bolt", "red", "Server neustarten (Reboot)", "Bootet den ganzen Bosgame neu. Alles ist für ~1 Minute offline, auch dieses Dashboard.")}
-
-
-
-
Aktueller Vorgang
-
-
-
—
-
-
-
-
-
-
Live-Konsole
-
-
-
Live mitlesen, was der Dienst gerade tut.
-
Verbinde…
-
-
`;
-
- $("#w-restart-swap").addEventListener("click", () => restartService("llama-swap",
- "LLM-Engine neustarten?", "Die Engine startet neu. Geladene Modelle werden kurz entladen (~5 Sekunden), laden danach automatisch wieder."));
- $("#w-restart-mc").addEventListener("click", () => restartService("mission-control",
- "Dashboard neustarten?", "Diese Oberfläche trennt sich kurz und verbindet automatisch wieder. Laufende Downloads laufen weiter."));
- $("#w-self-update").addEventListener("click", selfUpdate);
- $("#w-update").addEventListener("click", update);
- $("#w-unload").addEventListener("click", unloadAll);
- $("#w-os-update").addEventListener("click", osUpdate);
- $("#w-reboot").addEventListener("click", rebootServer);
- $("#w-console-sel").addEventListener("change", connectConsole);
- $("#w-update-cancel").addEventListener("click", cancelActive);
- $("#w-swap-check").addEventListener("click", checkSwap);
- if (lastStatus) renderSwap(lastStatus);
- connectConsole();
-}
-
-// ---- llama-swap-Transparenz ----
-let lastStatus = null;
-function renderSwap(s) {
- const chip = $("#w-swap-state"); if (!chip) return;
- const ok = !!s?.swap_ok;
- chip.textContent = ok ? "● erreichbar" : "● offline";
- chip.style.cssText = "margin-left:auto;" + (ok
- ? "background:rgba(63,185,80,.12);border-color:rgba(63,185,80,.25);color:#7ee29a"
- : "background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3);color:#ff9b95");
- $("#w-swap-url").textContent = (s?.swap_url || "—") + "/v1";
- const models = (s?.models || []);
- const loaded = models.find(m => m.state === "running" || m.state === "loading");
- $("#w-swap-loaded").innerHTML = loaded
- ? `${esc(loaded.name)}(${esc(loaded.state)})`
- : `nichts geladen — lädt bei der nächsten Anfrage`;
- const real = models.filter(m => !m.incomplete).length;
- $("#w-swap-count").textContent = real ? `${real} bereit` : "keine";
-}
-
-async function checkSwap() {
- const res = $("#w-swap-check-res"); res.textContent = "prüfe…"; res.style.color = "var(--mut)";
- try {
- const r = await api("/api/integration/test");
- if (r.ok) { res.style.color = "#7ee29a"; res.textContent = `✓ Engine antwortet — ${r.models.length} Modell(e): ${r.models.join(", ")}`; }
- else { res.style.color = "#ff9b95"; res.textContent = "✗ keine Verbindung: " + r.error; }
- } catch (e) { res.style.color = "#ff9b95"; res.textContent = "✗ " + e.message; }
-}
-
-function onStatus(s) { lastStatus = s; renderSwap(s); }
-
-async function cancelActive() {
- if (!activeJob) return;
- if (!await confirmModal({ title: activeJob.label + " abbrechen?", body: "Der laufende Vorgang wird gestoppt. Bei einem Download bleibt eine unvollständige Datei zurück (kannst du später neu starten).", confirmLabel: "Abbrechen", danger: true })) return;
- try { await api(`/api/jobs/${activeJob.id}/cancel`, { method: "POST" }); toast("Abbruch angefordert…"); }
- catch (e) { toast(e.message, true); }
-}
-
-let ws = null;
-function connectConsole() {
- if (ws) { ws.close(); ws = null; }
- const svc = $("#w-console-sel").value, out = $("#w-console");
- out.textContent = "Verbinde mit " + svc + "…\n";
- const proto = location.protocol === "https:" ? "wss:" : "ws:";
- ws = new WebSocket(`${proto}//${location.host}/api/logs/${svc}?token=${encodeURIComponent(getToken())}`);
- ws.onmessage = e => { out.textContent += e.data; out.scrollTop = out.scrollHeight; };
- ws.onclose = () => { out.textContent += "\n— Verbindung getrennt —"; };
-}
-
-async function restartService(name, title, body) {
- if (!await confirmModal({ title, body, confirmLabel: "Neustarten" })) return;
- try { await api(`/api/service/${name}/restart`, { method: "POST" }); toast("Neustart ausgelöst: " + name); setTimeout(refreshSoon, 2000); }
- catch (e) { toast(e.message, true); }
-}
-
-async function unloadAll() {
- if (!await confirmModal({ title: "Grafikspeicher leeren?", body: "Alle geladenen Modelle werden entladen. Sie laden beim nächsten Aufruf automatisch neu — es geht nichts verloren.", confirmLabel: "Leeren" })) return;
- try { await api("/api/unload", { method: "POST" }); toast("Grafikspeicher geleert."); setTimeout(refreshSoon, 600); }
- catch (e) { toast(e.message, true); }
-}
-
-async function selfUpdate() {
- if (!await confirmModal({ title: "Mission Control aktualisieren?", body: "Holt die neueste Dashboard-Version aus Gitea und startet automatisch neu. Die Seite trennt kurz und verbindet von selbst wieder (~10 Sek)." })) return;
- try { const r = await api("/api/self-update", { method: "POST" }); toast("Update läuft — Dashboard startet gleich neu."); track(r.job_id); }
- catch (e) { toast(e.message, true); }
-}
-
-// ---- Live-Verlauf laufender Vorgaenge (Updates) direkt auf der Server-Seite ----
-let activeJob = null; // { id, label }
-const notified = new Set(); // Job-IDs, fuer die schon eine Abschluss-Meldung kam
-
-function watchJob(id, label) {
- activeJob = { id, label };
- const card = $("#w-update-card");
- if (!card) return;
- card.style.display = "";
- $("#w-update-label").textContent = label + " läuft…";
- $("#w-update-badge").innerHTML = 'läuft…';
- $("#w-update-log").textContent = "Starte…";
- card.scrollIntoView({ behavior: "smooth", block: "nearest" });
-}
-
-function onJobs(jobs) {
- if (!activeJob) return;
- const j = (jobs || []).find(x => x.id === activeJob.id);
- const card = $("#w-update-card");
- if (!j || !card) return;
- card.style.display = "";
- const logEl = $("#w-update-log");
- if (logEl) { logEl.textContent = (j.log || []).join("\n"); logEl.scrollTop = logEl.scrollHeight; }
- const done = j.state === "done", failed = j.state === "failed", canceled = j.state === "canceled";
- const terminal = done || failed || canceled;
- const badge = $("#w-update-badge");
- if (badge) badge.innerHTML = done ? '✓ fertig'
- : failed ? '✗ fehlgeschlagen'
- : canceled ? 'abgebrochen'
- : 'läuft…';
-
- // Fortschrittsbalken: exakter Backend-Wert (Download) bevorzugt, sonst %-Angabe im Log.
- let pval = null;
- if (!terminal) {
- if (typeof j.progress === "number") {
- pval = j.progress;
- } else {
- const last = (j.log || [])[j.log.length - 1] || "";
- const pm = last.match(/(\d+(?:\.\d+)?)\s*%/);
- if (pm) pval = parseFloat(pm[1]);
- }
- }
- const bar = $("#w-update-bar");
- if (bar) {
- if (pval != null) { bar.style.display = ""; bar.querySelector("i").style.width = Math.min(100, pval) + "%"; }
- else bar.style.display = "none";
- }
- $("#w-update-cancel").style.display = terminal ? "none" : "";
-
- if (terminal) {
- $("#w-update-label").textContent = activeJob.label +
- (done ? " — erfolgreich abgeschlossen." : canceled ? " — abgebrochen." : " — fehlgeschlagen (siehe Protokoll).");
- if (!notified.has(j.id)) {
- notified.add(j.id);
- toast(done ? "✓ " + activeJob.label + " abgeschlossen."
- : canceled ? activeJob.label + " abgebrochen."
- : "✗ " + activeJob.label + " fehlgeschlagen.", failed);
- if (done) setTimeout(refreshSoon, 800);
- }
- }
-}
-
-async function update() {
- if (!await confirmModal({ title: "LLM-Engine aktualisieren?", body: "Lädt die neueste llama.cpp-ROCm-Version und installiert sie. Die Engine ist während des Updates kurz nicht erreichbar." })) return;
- try { const r = await api("/api/update", { method: "POST" }); toast("LLM-Engine-Update läuft."); track(r.job_id); watchJob(r.job_id, "LLM-Engine-Update"); }
- catch (e) { toast(e.message, true); }
-}
-
-async function osUpdate() {
- if (!await confirmModal({ title: "OS-Updates installieren?", body: "Führt apt update & upgrade aus. Das kann ein paar Minuten dauern; der Fortschritt erscheint in der Aktivität." })) return;
- const pwd = await promptModal({ title: "sudo-Passwort", body: "Für die System-Updates wird einmalig dein sudo-Passwort gebraucht.", placeholder: "sudo-Passwort", password: true, confirmLabel: "Installieren" });
- if (!pwd) return;
- try { const r = await api("/api/os-update", { method: "POST", body: JSON.stringify({ password: pwd }) }); toast("OS-Update gestartet."); track(r.job_id); watchJob(r.job_id, "OS-Update"); }
- catch (e) { toast(e.message, true); }
-}
-
-async function rebootServer() {
- if (!await confirmModal({ title: "Server wirklich neu starten?", body: "Der ganze Bosgame startet physisch neu. Alles ist für ~1 Minute offline — auch dieses Dashboard.", confirmLabel: "Reboot", danger: true })) return;
- const pwd = await promptModal({ title: "sudo-Passwort", body: "Für den Reboot wird einmalig dein sudo-Passwort gebraucht.", placeholder: "sudo-Passwort", password: true, confirmLabel: "Jetzt neustarten", danger: true });
- if (!pwd) return;
- try { await api("/api/reboot", { method: "POST", body: JSON.stringify({ password: pwd }) }); toast("Reboot ausgelöst — bis gleich."); }
- catch (e) { toast(e.message, true); }
-}
-
-export default { id: "server", mount, onJobs, onStatus };
diff --git a/wird b/wird
deleted file mode 100644
index e69de29..0000000