feat: Action Dashboard und App Store Filter
This commit is contained in:
@@ -8,7 +8,7 @@ password = 'Tu77ceu2zzvx!'
|
|||||||
print("Connecting to server...")
|
print("Connecting to server...")
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
ssh.connect(host, username=user, password=password)
|
ssh.connect(host, username=user, password=password, look_for_keys=False, allow_agent=False)
|
||||||
|
|
||||||
print("Uploading update.tar.gz...")
|
print("Uploading update.tar.gz...")
|
||||||
sftp = ssh.open_sftp()
|
sftp = ssh.open_sftp()
|
||||||
|
|||||||
@@ -41,19 +41,38 @@ const CURATED_MODELS = [
|
|||||||
|
|
||||||
let lastSys = null;
|
let lastSys = null;
|
||||||
let currentResults = [];
|
let currentResults = [];
|
||||||
|
let currentAnalysis = null;
|
||||||
|
let activeFilter = "";
|
||||||
|
|
||||||
|
const FILTERS = [
|
||||||
|
{ id: "", label: "Alle" },
|
||||||
|
{ id: "coder", label: "Coding" },
|
||||||
|
{ id: "vision", label: "Vision / Multimodal" },
|
||||||
|
{ id: "roleplay", label: "Roleplay" },
|
||||||
|
{ id: "german", label: "Deutsch" }
|
||||||
|
];
|
||||||
|
|
||||||
function mount() {
|
function mount() {
|
||||||
const c = $(".view[data-view='cookbook']");
|
const c = $(".view[data-view='cookbook']");
|
||||||
|
|
||||||
|
const filtersHtml = FILTERS.map(f =>
|
||||||
|
`<button class="ghost filter-btn ${activeFilter === f.id ? 'active' : ''}" style="margin-right:8px; border-radius:16px; padding:6px 16px; ${activeFilter === f.id ? 'background:var(--hi); color:var(--bg)' : 'background:var(--bg); border:1px solid var(--line)'}" onclick="window.setFilter('${f.id}')">${f.label}</button>`
|
||||||
|
).join("");
|
||||||
|
|
||||||
c.innerHTML = `
|
c.innerHTML = `
|
||||||
<div class="card" style="padding-bottom:32px">
|
<div class="card" style="padding-bottom:32px">
|
||||||
<div style="max-width:600px; margin:0 auto; text-align:center;">
|
<div style="max-width:600px; margin:0 auto; text-align:center;">
|
||||||
<h2 style="margin-top:20px; margin-bottom:8px">HuggingFace Suche</h2>
|
<h2 style="margin-top:20px; margin-bottom:8px">App Store für Modelle</h2>
|
||||||
<p class="meta" style="margin-bottom:24px">Finde GGUF-Modelle direkt auf HuggingFace und lade sie in Mission Control herunter.</p>
|
<p class="meta" style="margin-bottom:24px">Entdecke Modelle live auf HuggingFace, Hardware-Fit inklusive.</p>
|
||||||
|
|
||||||
<div style="display:flex; gap:12px; position:relative">
|
<div style="display:flex; gap:12px; position:relative">
|
||||||
<input id="cb-search" class="tokin" style="flex:1; padding:12px 18px; font-size:16px; border-radius:12px;" placeholder="Nach Modellen suchen (z.B. Llama 3)...">
|
<input id="cb-search" class="tokin" style="flex:1; padding:12px 18px; font-size:16px; border-radius:12px;" placeholder="Nach Modellen suchen (z.B. Llama 3)...">
|
||||||
<button class="primary" id="cb-btn-search" style="border-radius:12px; padding:0 24px;">Suchen</button>
|
<button class="primary" id="cb-btn-search" style="border-radius:12px; padding:0 24px;">Suchen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="cb-filters" style="margin-top:20px; display:flex; justify-content:center; flex-wrap:wrap; gap:8px">
|
||||||
|
${filtersHtml}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -157,8 +176,32 @@ async function reanalyzeCtx() {
|
|||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.setFilter = (filterId) => {
|
||||||
|
activeFilter = filterId;
|
||||||
|
const buttons = document.querySelectorAll('.filter-btn');
|
||||||
|
buttons.forEach(b => {
|
||||||
|
b.style.background = 'var(--bg)';
|
||||||
|
b.style.color = '';
|
||||||
|
b.style.border = '1px solid var(--line)';
|
||||||
|
});
|
||||||
|
|
||||||
|
const idx = FILTERS.findIndex(f => f.id === filterId);
|
||||||
|
if (idx !== -1 && buttons[idx]) {
|
||||||
|
buttons[idx].style.background = 'var(--hi)';
|
||||||
|
buttons[idx].style.color = 'var(--bg)';
|
||||||
|
buttons[idx].style.border = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch();
|
||||||
|
};
|
||||||
|
|
||||||
async function doSearch() {
|
async function doSearch() {
|
||||||
const q = $("#cb-search").value.trim();
|
let q = $("#cb-search").value.trim();
|
||||||
|
|
||||||
|
if (activeFilter) {
|
||||||
|
q = q ? (q + " " + activeFilter) : activeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
if (!q) return renderCurated();
|
if (!q) return renderCurated();
|
||||||
|
|
||||||
const btn = $("#cb-btn-search");
|
const btn = $("#cb-btn-search");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// overview.js — Dashboard: Quick Actions, Modelle & Recent Jobs
|
// overview.js — Dashboard: Quick Actions, Modelle & Recent Jobs
|
||||||
|
|
||||||
import { $, esc, icon } from "../core/ui.js";
|
import { api } from "../core/api.js";
|
||||||
|
import { $, esc, icon, toast } from "../core/ui.js";
|
||||||
|
|
||||||
let S = null; // letzter Status
|
let S = null; // letzter Status
|
||||||
let J = []; // letzte Job-Liste
|
let J = []; // letzte Job-Liste
|
||||||
@@ -26,9 +27,57 @@ function renderHero() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function triggerAction(action) {
|
||||||
|
if (action === "restart_llama") {
|
||||||
|
toast("Neustart ausgelöst...");
|
||||||
|
try {
|
||||||
|
await api("/api/service/llama-swap/restart", { method: "POST" });
|
||||||
|
toast("llama-swap wird neugestartet.");
|
||||||
|
} catch(e) {
|
||||||
|
toast("Fehler: " + e.message, true);
|
||||||
|
}
|
||||||
|
} else if (action === "update_mc") {
|
||||||
|
toast("Update gestartet! Siehe Aktivitäten.");
|
||||||
|
try {
|
||||||
|
await api("/api/update", { method: "POST" });
|
||||||
|
document.querySelector('.nav-item[data-view=\\'activity\\']').click();
|
||||||
|
} catch(e) {
|
||||||
|
toast("Fehler: " + e.message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global hook für onclick
|
||||||
|
window.triggerAction = triggerAction;
|
||||||
|
|
||||||
function renderQuickActions() {
|
function renderQuickActions() {
|
||||||
// 3 Kacheln (Cookbook, Server-Status, Aktivität/Guides)
|
let actionsHtml = "";
|
||||||
$("#ov-quick").innerHTML = `
|
|
||||||
|
if (SYS) {
|
||||||
|
const ram_percent = SYS.ram.percent || 0;
|
||||||
|
// Wenn RAM über 90% ist, zeige Warnung.
|
||||||
|
// Wir nehmen 90 für Produktion, aber für den Test könnte es angepasst werden.
|
||||||
|
if (ram_percent >= 90) {
|
||||||
|
actionsHtml += `
|
||||||
|
<div class="card" style="background:var(--red-dim); border:1px solid var(--red); grid-column:1/-1;">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<h3 style="color:var(--red); margin:0;">⚠️ Arbeitsspeicher kritisch (${ram_percent.toFixed(0)}%)</h3>
|
||||||
|
<p style="color:var(--red); margin-top:4px;">Der RAM/VRAM ist fast voll. Dies kann zu Systeminstabilität führen.</p>
|
||||||
|
</div>
|
||||||
|
<button class="primary warn" onclick="window.triggerAction('restart_llama')">VRAM leeren (Neustart)</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulierter Update-Check (Idealerweise vom Backend, hier als permanenter Button wenn man manuell checken will,
|
||||||
|
// oder wir blenden ihn ein wenn ein lokales flag gesetzt ist. Wir zeigen ihn hier als Feature-Highlight)
|
||||||
|
// Da wir aktuell keinen echten Git-Check im Backend haben, zeigen wir einen "Update Prüfen" Button in den QuickActions.
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 Standard Kacheln (Cookbook, Server-Status, Aktivität/Guides)
|
||||||
|
$("#ov-quick").innerHTML = actionsHtml + `
|
||||||
<button class="card-btn" onclick="document.querySelector('.nav-item[data-view=\\'cookbook\\']').click()">
|
<button class="card-btn" onclick="document.querySelector('.nav-item[data-view=\\'cookbook\\']').click()">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h3>Modell finden</h3>
|
<h3>Modell finden</h3>
|
||||||
@@ -37,12 +86,12 @@ function renderQuickActions() {
|
|||||||
<p>Durchsuche HuggingFace nach neuen Modellen im Cookbook.</p>
|
<p>Durchsuche HuggingFace nach neuen Modellen im Cookbook.</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="card-btn" onclick="document.querySelector('.nav-item[data-view=\\'activity\\']').click()">
|
<button class="card-btn" onclick="window.triggerAction('update_mc')">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h3>Live Metriken</h3>
|
<h3>Container Updates</h3>
|
||||||
<span class="text-act">${icon("pulse")}</span>
|
<span class="text-act">${icon("download")}</span>
|
||||||
</div>
|
</div>
|
||||||
<p>${SYS ? `System läuft (RAM: ${SYS.ram.percent.toFixed(0)}%, CPU: ${SYS.cpu.percent.toFixed(0)}%)` : 'Lade Metriken...'}</p>
|
<p>Prüfe auf Updates für Mission-Control und Llama.cpp.</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="card-btn" onclick="document.querySelector('.nav-item[data-view=\\'server\\']').click()">
|
<button class="card-btn" onclick="document.querySelector('.nav-item[data-view=\\'server\\']').click()">
|
||||||
@@ -50,7 +99,7 @@ function renderQuickActions() {
|
|||||||
<h3>Wartung</h3>
|
<h3>Wartung</h3>
|
||||||
<span class="text-act">${icon("server")}</span>
|
<span class="text-act">${icon("server")}</span>
|
||||||
</div>
|
</div>
|
||||||
<p>Server neustarten, VRAM leeren oder Engine aktualisieren.</p>
|
<p>Server neustarten, VRAM leeren oder OS aktualisieren.</p>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import paramiko
|
||||||
|
|
||||||
|
host = '192.168.178.153'
|
||||||
|
user = 'hitonabi'
|
||||||
|
password = 'Tu77ceu2zzvx!'
|
||||||
|
|
||||||
|
print("Connecting to server...")
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
ssh.connect(host, username=user, password=password, look_for_keys=False, allow_agent=False)
|
||||||
|
|
||||||
|
cmd = f"echo {password} | sudo -S bash /home/hitonabi/mission-control/deploy_bosgame.sh"
|
||||||
|
|
||||||
|
print(f"Executing deployment script on server...")
|
||||||
|
stdin, stdout, stderr = ssh.exec_command(cmd)
|
||||||
|
exit_status = stdout.channel.recv_exit_status()
|
||||||
|
print("STDOUT:", stdout.read().decode())
|
||||||
|
print("STDERR:", stderr.read().decode())
|
||||||
|
|
||||||
|
ssh.close()
|
||||||
|
print("Deployment complete!")
|
||||||
Binary file not shown.
Reference in New Issue
Block a user