@enigmax/dashboard 0.1.33 → 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 +78 -1
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Enigma - Dashboard</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjE4Ny4zNiAyNTAwLjM3IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0xMDk0Ljc2IDY0MiA1MDkuNCAzMDcuMjYtNTA5LjQgMjkwLjc0LTUzMS4zNy0yOTAuNzR6IiBmaWxsPSIjZWEyNTllIi8+PHBhdGggZD0ibTU2My4zOSA5NjAgNTMxLjM3IDI5MC4xOC0yLjc0IDU5MS44Mi01MjguNjMtMjgwLjQ2eiIgZmlsbD0iIzAxNGM5OSIvPjxwYXRoIGQ9Im0xMDk0Ljc2IDEyNTAuMTggNTE1LjQtMjkwLjE4IDE0LjUxIDYwMS41NC01MjIuNTEgMjkxLjU5eiIgZmlsbD0iIzA4YjBmOSIvPjxnIGZpbGw9IiMyZjJmMmYiPjxwYXRoIGQ9Im0yMTUyLjc1IDYwNS4xNGMtMi41Ni0xLjQ2LTUuMTMtMi45My04LjA2LTQuNzZxLTI1NC4yMy0xNDUuNTEtNTA3Ljc2LTI5MS4zOC0yNjYuMzItMTUyLjY5LTUzMi4yOC0zMDUuMzRsLTYuMjMtMy42Ni02LjU5IDIuNTZhMjAyLjUzIDIwMi41MyAwIDAgMCAtMjkuMjggMTMuNTVxLTIzMS43MyAxMzUuNjMtNDYzLjEgMjcwLjg5Yy0xOTEuODMgMTEyLjQtMzg0LjAzIDIyNC40Mi01NzQuMzkgMzM2LjQ0LTIzLjggMTEtMjMuMDcgMzUuODgtMjIuNyA1MC44OSAwIDMuMjkuMzcgNyAwIDEwLjYycS0uNTUgNDI2LjEyLTEuNDYgODUyLjI0bC0uNzQgMzE2LjNjLTEuNDYgMTkgNyAzNS4xNSAyMS4yNCA0MS43NHEzMzAgMTg3LjI0IDY2MC4wNSAzNzUuMjNsMzg5LjE1IDIyMS41NGM4Ljc5IDUuNDkgMTcuNTcgOC40MiAyNS42MyA4LjQyYTI5LjEyIDI5LjEyIDAgMCAwIDE4LjMtNi4yM2w1NjQuNDctMzI1LjEzYzE2MC4zNS05Mi4yNiAzMjAuNy0xODQuODggNDc5Ljk0LTI3Ni40IDE3Ljk0LTguMDUgMjguNTYtMjYgMjYuMzYtNDIuODNsMS44My0xMTk3LjFjMi41Ni0yNy40Ni0yMC41LTQwLjI3LTM0LjQxLTQ3LjU5bS0xMDU4LjMyIDc1LjQxIDQ2NC4xOSAyNzAuMTctNDYzLjQ2IDI2Ny4yOC0xNTcuNzktODkuMzNxLTE1NS45NC04OC40MS0zMTEuOS0xNzYuNDVsNDY5LTI3MS42NG01MjcuNTIgMjMzLjU3LTUwNC4xLTI5My4yNGMtMTMuOTEtOS44OC0zMi4yMS05Ljg4LTQ1LjM5LTEuMWwtNTEwLjczIDI5NS43Ny00NjMuODMtMjYyLjEyIDMyMy42Mi0xODkuMjZxMzM2LjYyLTE5Ny4xNSA2NzMuNi0zOTMuNTUgMzA5LjE1IDE3OC40NyA2MTguNjggMzU1LjQ3IDE4OC45MSAxMDguNzQgMzc3LjggMjE3LjA5bC00NjkuNjkgMjcwLjkxbS0xNTU1Ljg2LTIwNS43NCA0NjEuNjMgMjYxLjM4IDkuNTIgNTc2LjU5Yy0xLjEgMTcuOTMgNy42OSAzNCAyMS42IDQwLjI3cTE1My4xOSA4OC4zOCAzMDcuMTkgMTc2LjQ1bDE5NyAxMTMuMTItLjM2IDUzOC41MS05OTguNzgtNTY4LjE2em01MjUuNyAyOTcuNjIgNDcxLjg4IDI2Ni44OC0uNzMgNTMwLjQ2LTQ2Mi4zNi0yNjUuNzh6bTUzNC44NSA4NzAuNTYgMTk3LjMyLTExMy4xMmMxMDEtNTguMjEgMjAyLjQ1LTExNiAzMDQuMjItMTc0LjYzbDIuOTMtMS44M2M1LjQ5LTMuMjkgMTIuODEtNy42OSAxNy45My0xNWwyLjkzLTR2LTQuNzZjLTEuMDktMTI3LTEuMDktMjU2LjYzLS43My0zODEuODMgMC03MC4yOS4zNy0xNDAuNTggMC0yMTAuODdsNDcyLjI1LTI3Mi4zNi0uNzMgNDY0LjU2LTEuMSA2NzguMzZxLTMxNi4yOSAxODEuNzYtNjMzIDM2NC4yNWwtMzYyLjc5IDIwOSAuNzMtNTM3Ljc4bTAtNzIuODYuMzctNTMwLjgyIDQ2NC45My0yNjh2NTMxLjkycS0yMzIuMjkgMTMyLjg5LTQ2NS4zIDI2Ni44NyIvPjxwYXRoIGQ9Im0xMDgwLjg1IDEzMy4yNmMtMTMuNTUgMS40Ni0yNS42MyAxMC4yNS0zMS44NSAyMy4wNi02LjIyIDEzLjU0LTUuNDkgMjguMTkgMi4yIDM5LjE3IDcuMzIgMTEuNzEgMjAuODYgMTguNjcgMzUuNTEgMTguNjdhNzIuODUgNzIuODUgMCAwIDAgOC40Mi0uNzNjMTcuMi0yLjkzIDMwLTE0LjY1IDMyLjk0LTI5LjY1YTM4LjM0IDM4LjM0IDAgMCAwIC05LjUxLTM3LjM1Yy05LjUyLTEwLjYxLTI0LjE3LTE1Ljc0LTM3LjcxLTEzLjE3bTI2LjcyIDE3NC42MmMtMTQuMjctNy42OS0zMS4xMS03LjMyLTQzLjU2IDEuMS0xMSA2LjIyLTE4LjMgMTcuOTMtMTkuNzcgMzEuNDgtMS4xIDEzLjU0IDQuMzkgMjYgMTMuMTggMzNhNDAuMTcgNDAuMTcgMCAwIDAgMjguMTkgMTEgNDUuNjggNDUuNjggMCAwIDAgMTguMy00YzE1Ljc0LTYuOTUgMjUuMjYtMjEuMjMgMjQuOS0zNi4yNC43My0xNC42NC03LjMyLTI4LjU1LTIxLjI0LTM2LjI0bTEuODMgMTcxLjY5Yy0xMy45MS04Ljc4LTMxLjExLTkuMTUtNDQuMjktMS40Ni0xMi4wOCA2LjIyLTE5Ljc3IDE3Ljk0LTIwLjg3IDMyLjIxczUuMTMgMjguMTkgMTUuNzQgMzUuMTVhNDEuMjkgNDEuMjkgMCAwIDAgMjYgOC43OCA0NS44OSA0NS44OSAwIDAgMCAxOC4zMS00YzE0LjY0LTYuNTkgMjMuNzktMTkgMjQuNTMtMzMuMzIgMS4xLTE1LTYuMjMtMjguOTItMTkuNDEtMzcuMzRtLTY3OS44MyAxMTIxLjJjLTE0LjI3IDQtMjUuMjYgMTUuNzQtMjguNTUgMzEuMTItMy4zIDE1Ljc0IDIuMTkgMzEuNDggMTMuMTggMzkuOWEzNy4wOCAzNy4wOCAwIDAgMCAyNC44OSA5LjUyIDM4LjY3IDM4LjY3IDAgMCAwIDkuMTUtMS4xYzEzLjE4LTIuOTIgMjQuMTctMTIuMDggMjguMTktMjMuMDYgNy0xNC4yOCA0LjQtMzEuNDgtNi45NS00NC4zLTEwLjI1LTEyLjA4LTI1LjYzLTE2LjQ3LTM5LjkxLTEyLjA4bTEyOTIuMjkgODAuOTFhMzQuNjMgMzQuNjMgMCAwIDAgMTAuMjUtMS40N2MxMy4xOC0zLjI5IDIzLjQzLTEyLjQ0IDI2LjcyLTIyLjY5IDcuMzItMTQuNjUgNC4zOS0zMi4yMi03LTQ1LjQtMTEtMTIuMDgtMjYuNzItMTYuNDctNDEtMTEuNzEtMTMuOTEgNC0yNC41MiAxNi4xLTI3LjQ1IDMxLjQ4LTIuOTMgMTUuNzQgMi41NiAzMS4xMiAxMy4xOCAzOS41NCA2LjU5IDcgMTUuNzQgMTAuMjUgMjUuMjYgMTAuMjVtLTE0MzggNS40OWMtMTQuNjQgNS4xMi0yNC41MyAxOC4zLTI2IDM0Ljc4LTEuNDcgMTYuODQgNi41OSAzMi41OCAxOS40IDM5LjlhMzcuOCAzNy44IDAgMCAwIDE5Ljc3IDUuODYgNDIuMjIgNDIuMjIgMCAwIDAgMTcuMjEtNGMxMi44MS01Ljg2IDIxLjYtMTcuNTcgMjIuNjktMjkuMjkgMy4zLTE0LjI3LTIuNTYtMzAuMzgtMTQuNjQtNDAuMjctMTEuMzUtOS44OC0yNS42My0xMi40NC0zOC40NC03bTE1ODAgODAuMTdhMzkuMTkgMzkuMTkgMCAwIDAgMTQuMjctMi41NmMxMy45Mi01LjEzIDIzLjgtMTcuMjEgMjUuMjYtMjkuMjkgNC0xNC4yNy0uNzMtMjkuNjUtMTIuNDQtNDAuMjctMTEtMTAuMjUtMjYuMzYtMTMuNTQtMzkuNTQtOC40Mi0xMy41NCA0Ljc2LTIzLjggMTYuODQtMjYuMzYgMzIuMjItMi41NiAxNS43NCAzLjMgMzEuMTIgMTQuMjggMzkuNTRhNDAuMTcgNDAuMTcgMCAwIDAgMjQuNTMgOC43OG0tMTY4OS44NSA4LjA2Yy0xMi40NS03LTI2LjczLTctMzguNDQgMC0xMi44MiA3LTIwLjg3IDIwLjg2LTIwLjg3IDM2LjYgMCAxNi4xMSA4Ljc5IDMwIDIxLjIzIDM2LjI1YTM0LjczIDM0LjczIDAgMCAwIDE3Ljk0IDQuNzYgNDEuNDggNDEuNDggMCAwIDAgMjItNi41OWMxMy4xNy04LjQyIDIwLjUtMjMuNDMgMTktMzYuMjUuMzctMTQuMjctNy42OS0yNy40NS0yMC44Ni0zNC43N20xODA4LjkxIDY4LjVhMzUuODEgMzUuODEgMCAwIDAgMjMuOCA5LjE2IDM4LjczIDM4LjczIDAgMCAwIDE1LjM4LTMuM2MxNC4yNy02LjIyIDI0LjE2LTE5LjQgMjQuODktMzIuMjEgMi45My0xNS43NS00LjM5LTMxLjg1LTE4LjMtNDEtMTIuODItOC43OC0yOC41Ni05LjE1LTQxLTEuMDlhNDEuNSA0MS41IDAgMCAwIC0yMC4xMyAzNGMtLjczIDE0LjI4IDUuNDkgMjcuODIgMTUuMzcgMzQuNDEiLz48L2c+PC9zdmc+">
|
|
7
8
|
<style>
|
|
8
9
|
:root {
|
|
9
10
|
/* Core palette. --accent / --muted / --border are PINNED: the chart hardcodes
|
|
@@ -347,6 +348,7 @@
|
|
|
347
348
|
<a href="#/accounts" data-view="accounts" class="tab">Accounts</a>
|
|
348
349
|
<a href="#/skills" data-view="skills" class="tab">Skills</a>
|
|
349
350
|
<a href="#/settings" data-view="settings" class="tab">Settings</a>
|
|
351
|
+
<a href="#/resources" data-view="resources" class="tab">Resources</a>
|
|
350
352
|
</nav>
|
|
351
353
|
<div class="nav">
|
|
352
354
|
<span class="live"><span id="dot" class="dot stale"></span><span id="updated">Connecting...</span></span>
|
|
@@ -565,6 +567,24 @@
|
|
|
565
567
|
<div id="settingsBody"><div class="empty" style="padding:24px 0">Loading settings...</div></div>
|
|
566
568
|
</section>
|
|
567
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
|
+
|
|
568
588
|
<footer>
|
|
569
589
|
Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
|
|
570
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>
|
|
@@ -2682,7 +2702,7 @@ export {
|
|
|
2682
2702
|
|
|
2683
2703
|
// --- hash routing between the subpages ---
|
|
2684
2704
|
// Single source of truth for the subpage ids (savings is the default/fallback).
|
|
2685
|
-
const VIEWS = ["savings", "usage", "accounts", "skills", "settings"];
|
|
2705
|
+
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources"];
|
|
2686
2706
|
function route() {
|
|
2687
2707
|
const h = (location.hash || "").replace(/^#\/?/, "");
|
|
2688
2708
|
const v = VIEWS.includes(h) ? h : "savings";
|
|
@@ -2691,10 +2711,66 @@ export {
|
|
|
2691
2711
|
if (v === "settings" && !settingsLoaded) { settingsLoaded = true; loadSettings(); }
|
|
2692
2712
|
if (v === "skills" && !skillsLoaded) { skillsLoaded = true; loadSkills(); }
|
|
2693
2713
|
if (v === "accounts" && !accountsLoaded) { accountsLoaded = true; loadAccounts(); }
|
|
2714
|
+
if (v === "resources") loadResources();
|
|
2694
2715
|
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
2695
2716
|
if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
2696
2717
|
}
|
|
2697
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
|
+
|
|
2698
2774
|
initChart();
|
|
2699
2775
|
poll(true);
|
|
2700
2776
|
loadClaudeStatus();
|
|
@@ -2707,6 +2783,7 @@ export {
|
|
|
2707
2783
|
wireSkills();
|
|
2708
2784
|
wireAccounts();
|
|
2709
2785
|
wireUsage();
|
|
2786
|
+
wireResources();
|
|
2710
2787
|
$("cfgExport").addEventListener("click", exportConfig);
|
|
2711
2788
|
$("cfgImport").addEventListener("click", importConfig);
|
|
2712
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": [
|