@enigmax/dashboard 0.1.34 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/index.html +77 -1
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -348,6 +348,7 @@
|
|
|
348
348
|
<a href="#/accounts" data-view="accounts" class="tab">Accounts</a>
|
|
349
349
|
<a href="#/skills" data-view="skills" class="tab">Skills</a>
|
|
350
350
|
<a href="#/settings" data-view="settings" class="tab">Settings</a>
|
|
351
|
+
<a href="#/resources" data-view="resources" class="tab">Resources</a>
|
|
351
352
|
</nav>
|
|
352
353
|
<div class="nav">
|
|
353
354
|
<span class="live"><span id="dot" class="dot stale"></span><span id="updated">Connecting...</span></span>
|
|
@@ -566,6 +567,24 @@
|
|
|
566
567
|
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
567
568
|
</section>
|
|
568
569
|
|
|
570
|
+
<section id="view-resources" style="display:none">
|
|
571
|
+
<div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
|
|
572
|
+
<div style="flex:1">
|
|
573
|
+
<h1>Resources</h1>
|
|
574
|
+
<p>Free up your machine: shut down WSL (the <code>vmmemWSL</code> RAM hog), quit Docker Desktop when it won't, free a stuck port, or kill any process. These actions are destructive and ask before running.</p>
|
|
575
|
+
</div>
|
|
576
|
+
<button id="resRefresh" type="button" class="iconbtn" title="Refresh" aria-label="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></button>
|
|
577
|
+
</div>
|
|
578
|
+
<div id="resNote" class="set-note"></div>
|
|
579
|
+
<div class="panel"><div class="set-cat-h">System</div><div id="resSystem" class="set-grid"></div></div>
|
|
580
|
+
<div class="panel">
|
|
581
|
+
<div class="set-cat-h">Free a port</div>
|
|
582
|
+
<div class="ladd" style="max-width:320px"><input id="resPort" class="linput" type="number" min="1" max="65535" placeholder="port number, e.g. 3000"><button id="resFreePort" type="button" class="toggle on">Free</button></div>
|
|
583
|
+
</div>
|
|
584
|
+
<div class="panel"><div class="set-cat-h">Listening ports</div><div id="resPorts"><div class="empty" style="padding:12px 0">Loading...</div></div></div>
|
|
585
|
+
<div class="panel"><div class="set-cat-h">Processes <small>by memory</small></div><div id="resProcs"><div class="empty" style="padding:12px 0">Loading...</div></div></div>
|
|
586
|
+
</section>
|
|
587
|
+
|
|
569
588
|
<footer>
|
|
570
589
|
Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
|
|
571
590
|
Data source: <code>~/.enigma/ccr/stats.json</code> + <code>history.jsonl</code> - reset it with <code>enigma compress --clear</code>. Enigma <span id="ver"></span>
|
|
@@ -2683,7 +2702,7 @@ export {
|
|
|
2683
2702
|
|
|
2684
2703
|
// --- hash routing between the subpages ---
|
|
2685
2704
|
// Single source of truth for the subpage ids (savings is the default/fallback).
|
|
2686
|
-
const VIEWS = ["savings", "usage", "accounts", "skills", "settings"];
|
|
2705
|
+
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources"];
|
|
2687
2706
|
function route() {
|
|
2688
2707
|
const h = (location.hash || "").replace(/^#\/?/, "");
|
|
2689
2708
|
const v = VIEWS.includes(h) ? h : "savings";
|
|
@@ -2692,10 +2711,66 @@ export {
|
|
|
2692
2711
|
if (v === "settings" && !settingsLoaded) { settingsLoaded = true; loadSettings(); }
|
|
2693
2712
|
if (v === "skills" && !skillsLoaded) { skillsLoaded = true; loadSkills(); }
|
|
2694
2713
|
if (v === "accounts" && !accountsLoaded) { accountsLoaded = true; loadAccounts(); }
|
|
2714
|
+
if (v === "resources") loadResources();
|
|
2695
2715
|
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
2696
2716
|
if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
2697
2717
|
}
|
|
2698
2718
|
|
|
2719
|
+
// --- resources subpage (system cleanup; destructive, origin-guarded) ---
|
|
2720
|
+
function fmtMB(kb) { return kb >= 1048576 ? (kb / 1048576).toFixed(1) + " GB" : Math.round(kb / 1024) + " MB"; }
|
|
2721
|
+
function renderResources(s) {
|
|
2722
|
+
s = s || {};
|
|
2723
|
+
const mem = s.totalMem ? Math.round((s.totalMem - s.freeMem) / 1048576) + " / " + Math.round(s.totalMem / 1048576) + " MB used" : "-";
|
|
2724
|
+
const wsl = s.wslAvailable
|
|
2725
|
+
? '<button type="button" class="btn-danger" data-res="wsl-shutdown">Shut down WSL</button>'
|
|
2726
|
+
: '<span class="tag">not available</span>';
|
|
2727
|
+
const dock = s.dockerRunning ? '<button type="button" class="btn-danger" data-res="docker-quit">Quit Docker</button>' : '<span class="tag">not running</span>';
|
|
2728
|
+
$("resSystem").innerHTML =
|
|
2729
|
+
'<div class="set-row"><div class="set-meta"><div class="set-name">Memory</div><div class="set-hint">' + esc(mem) + "</div></div></div>"
|
|
2730
|
+
+ '<div class="set-row"><div class="set-meta"><div class="set-name">WSL / vmmemWSL</div><div class="set-hint">' + (s.vmmemRunning ? "running - eating RAM; shut down to free it" : "not running") + '</div></div><div class="set-ctl">' + wsl + "</div></div>"
|
|
2731
|
+
+ '<div class="set-row"><div class="set-meta"><div class="set-name">Docker Desktop</div><div class="set-hint">' + (s.dockerRunning ? "running" : "not running") + '</div></div><div class="set-ctl">' + dock + "</div></div>";
|
|
2732
|
+
const ports = s.ports || [];
|
|
2733
|
+
$("resPorts").innerHTML = ports.length
|
|
2734
|
+
? ports.map((p) => '<div class="set-row"><div class="set-meta"><div class="set-name">:' + p.port + ' <span class="tag">' + esc(p.name || "pid " + p.pid) + '</span></div><div class="set-hint">pid ' + p.pid + " · " + esc(p.proto) + '</div></div><div class="set-ctl"><button type="button" class="btn-danger" data-res="free-port" data-value="' + p.port + '">Free</button></div></div>').join("")
|
|
2735
|
+
: '<div class="empty" style="padding:12px 0">No listening ports found.</div>';
|
|
2736
|
+
const procs = s.topProcesses || [];
|
|
2737
|
+
$("resProcs").innerHTML = procs.length
|
|
2738
|
+
? procs.map((p) => '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(p.name) + ' <span class="tag">' + fmtMB(p.memKB) + '</span></div><div class="set-hint">pid ' + p.pid + '</div></div><div class="set-ctl"><button type="button" class="btn-danger" data-res="kill" data-value="' + p.pid + '">Kill</button></div></div>').join("")
|
|
2739
|
+
: '<div class="empty" style="padding:12px 0">No processes found.</div>';
|
|
2740
|
+
}
|
|
2741
|
+
async function loadResources() {
|
|
2742
|
+
$("resNote").textContent = "";
|
|
2743
|
+
try { renderResources(await (await fetch("/api/resources", { cache: "no-store" })).json()); }
|
|
2744
|
+
catch { $("resSystem").innerHTML = '<div class="empty" style="padding:12px 0">Resources unavailable.</div>'; }
|
|
2745
|
+
}
|
|
2746
|
+
async function resAction(op, value, confirmMsg) {
|
|
2747
|
+
if (confirmMsg && !(await askConfirm("Confirm", confirmMsg, true))) return;
|
|
2748
|
+
$("resNote").textContent = "Running...";
|
|
2749
|
+
try {
|
|
2750
|
+
const out = await (await fetch("/api/resources", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ op, value }) })).json();
|
|
2751
|
+
$("resNote").textContent = out.message || (out.ok ? "Done." : "Failed.");
|
|
2752
|
+
if (out.status) renderResources(out.status);
|
|
2753
|
+
} catch { $("resNote").textContent = "Could not reach the server."; }
|
|
2754
|
+
}
|
|
2755
|
+
function wireResources() {
|
|
2756
|
+
$("resRefresh").addEventListener("click", loadResources);
|
|
2757
|
+
$("resFreePort").addEventListener("click", () => {
|
|
2758
|
+
const port = Number($("resPort").value);
|
|
2759
|
+
if (!port) { $("resNote").textContent = "Enter a port number."; return; }
|
|
2760
|
+
resAction("free-port", port, "Kill whatever is listening on port " + port + "?");
|
|
2761
|
+
});
|
|
2762
|
+
$("view-resources").addEventListener("click", (e) => {
|
|
2763
|
+
const b = e.target.closest("button[data-res]");
|
|
2764
|
+
if (!b) return;
|
|
2765
|
+
const op = b.dataset.res, value = b.dataset.value ? Number(b.dataset.value) : undefined;
|
|
2766
|
+
const msg = op === "wsl-shutdown" ? "Shut down WSL? This stops every WSL distro and frees vmmemWSL."
|
|
2767
|
+
: op === "docker-quit" ? "Force-quit Docker Desktop and its WSL backend?"
|
|
2768
|
+
: op === "free-port" ? "Kill the process listening on port " + value + "?"
|
|
2769
|
+
: op === "kill" ? "Force-kill process " + value + "?" : "Run this action?";
|
|
2770
|
+
resAction(op, value, msg);
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2699
2774
|
initChart();
|
|
2700
2775
|
poll(true);
|
|
2701
2776
|
loadClaudeStatus();
|
|
@@ -2708,6 +2783,7 @@ export {
|
|
|
2708
2783
|
wireSkills();
|
|
2709
2784
|
wireAccounts();
|
|
2710
2785
|
wireUsage();
|
|
2786
|
+
wireResources();
|
|
2711
2787
|
$("cfgExport").addEventListener("click", exportConfig);
|
|
2712
2788
|
$("cfgImport").addEventListener("click", importConfig);
|
|
2713
2789
|
$("updateBtn").addEventListener("click", runUpdate);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"description": "Local browser dashboard UI for enigma: the static page and chart assets enigma serves on its loopback dashboard (savings, real tool usage, in-browser settings). Installed on demand by enigma-cli; not a runtime dependency.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|