@enigmax/dashboard 0.1.68 → 0.1.70
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 +103 -7
- package/package.json +1 -1
package/assets/index.html
CHANGED
|
@@ -526,6 +526,7 @@
|
|
|
526
526
|
<a href="#/settings" data-view="settings" class="tab">Settings</a>
|
|
527
527
|
<a href="#/resources" data-view="resources" class="tab">Resources</a>
|
|
528
528
|
<a href="#/recall" data-view="recall" class="tab">Recall</a>
|
|
529
|
+
<a href="#/codegraph" data-view="codegraph" class="tab">Code graph</a>
|
|
529
530
|
<a href="#/packs" data-view="packs" class="tab">Packs</a>
|
|
530
531
|
</nav>
|
|
531
532
|
<div class="nav">
|
|
@@ -873,6 +874,19 @@
|
|
|
873
874
|
<div id="recallList"></div>
|
|
874
875
|
</section>
|
|
875
876
|
|
|
877
|
+
<section id="view-codegraph" style="display:none">
|
|
878
|
+
<div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
|
|
879
|
+
<div style="flex:1">
|
|
880
|
+
<h1>Code graph</h1>
|
|
881
|
+
<p>Structural memory of your codebase - a knowledge graph of symbols, call chains, and routes. Enabling it registers the code-intelligence server in your agents so they can query the graph over MCP. Complements <a href="#/recall">Recall</a> (session memory).</p>
|
|
882
|
+
</div>
|
|
883
|
+
<button id="cgToggle" type="button" class="toggle" role="switch" aria-checked="false" aria-label="Codebase memory">off</button>
|
|
884
|
+
<button id="cgRefresh" 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>
|
|
885
|
+
</div>
|
|
886
|
+
<div id="cgNote" class="set-note"></div>
|
|
887
|
+
<div id="cgBody"><div class="empty" style="padding:12px 0">Loading...</div></div>
|
|
888
|
+
</section>
|
|
889
|
+
|
|
876
890
|
<footer>
|
|
877
891
|
Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
|
|
878
892
|
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>
|
|
@@ -3156,6 +3170,7 @@ export {
|
|
|
3156
3170
|
const guard = (sec.guardProtects || []).map((p) => '<span class="tag">' + esc(p) + "</span>").join("");
|
|
3157
3171
|
el.innerHTML =
|
|
3158
3172
|
item("Context compression (MCP) · measured", boolPill(s.compress))
|
|
3173
|
+
+ item("Codebase memory / code graph (MCP)", boolPill(s.codeGraph))
|
|
3159
3174
|
+ item("Real tool-usage stats · measured", boolPill(s.usageStats))
|
|
3160
3175
|
+ item("Claude Code proxy · experimental", boolPill(s.proxy))
|
|
3161
3176
|
+ item("Live usage windows · real", boolPill(s.usageApi))
|
|
@@ -3503,8 +3518,11 @@ export {
|
|
|
3503
3518
|
const TRASH_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>';
|
|
3504
3519
|
const REFRESH_ICON = '<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>';
|
|
3505
3520
|
function accountRow(a) {
|
|
3521
|
+
const stateTxt = a.loginState === "expired" ? "session expired" : a.loginState === "empty" ? "signed out" : a.loginState === "absent" ? "not logged in" : "";
|
|
3506
3522
|
const badges = (a.active ? '<span class="tag meas">active</span>' : "")
|
|
3507
|
-
+ (a.loggedIn
|
|
3523
|
+
+ (a.loggedIn
|
|
3524
|
+
? '<span class="tag">' + esc(a.email || "logged in") + "</span>"
|
|
3525
|
+
: '<span class="tag warn">' + (a.email ? esc(a.email) + " · " : "") + (stateTxt || "not logged in") + "</span>");
|
|
3508
3526
|
const btn = (act, label, cls) => '<button type="button" class="' + (cls || "toggle") + '" data-acc="' + act + '" data-tool="' + esc(a.tool) + '" data-name="' + esc(a.name) + '">' + label + "</button>";
|
|
3509
3527
|
const removeBtn = '<button type="button" class="btn-danger icon" title="Remove" aria-label="Remove account ' + esc(a.name) + '" data-acc="remove" data-tool="' + esc(a.tool) + '" data-name="' + esc(a.name) + '">' + TRASH_ICON + "</button>";
|
|
3510
3528
|
// Provider override (e.g. MiniMax) only for tools that support it, and never the
|
|
@@ -3512,7 +3530,7 @@ export {
|
|
|
3512
3530
|
const provBtn = (a.supportsProvider && a.removable) ? btn("provider", "Provider") : "";
|
|
3513
3531
|
const acts = '<div class="set-ctl" style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">'
|
|
3514
3532
|
+ (a.active ? "" : btn("activate", "Use"))
|
|
3515
|
-
+ (a.loggedIn ? "" :
|
|
3533
|
+
+ btn("login", a.loggedIn ? "Re-login" : "Log in")
|
|
3516
3534
|
+ provBtn
|
|
3517
3535
|
+ (a.removable ? btn("rename", "Rename") + removeBtn : "")
|
|
3518
3536
|
+ "</div>";
|
|
@@ -3560,7 +3578,7 @@ export {
|
|
|
3560
3578
|
const r = await fetch("/api/accounts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(Object.assign({ op }, payload)) });
|
|
3561
3579
|
const out = await r.json();
|
|
3562
3580
|
if (out.data) renderAccounts(out.data);
|
|
3563
|
-
if (out.ok) { setNote("accountsNote", "Saved.", "ok"); return; }
|
|
3581
|
+
if (out.ok) { setNote("accountsNote", out.note || "Saved.", "ok"); return; }
|
|
3564
3582
|
if (rollback && !out.data) rollback();
|
|
3565
3583
|
setNote("accountsNote", "Error: " + (out.error || "failed"), "err");
|
|
3566
3584
|
} catch { if (rollback) rollback(); setNote("accountsNote", "Could not reach the server.", "err"); }
|
|
@@ -3644,7 +3662,7 @@ export {
|
|
|
3644
3662
|
if (op === "activate") accAction("account.activate", { tool, name });
|
|
3645
3663
|
else if (op === "remove") { if (await askConfirm("Remove account", "Remove account '" + name + "' and delete its local config dir?", true)) accAction("account.remove", { tool, name }); }
|
|
3646
3664
|
else if (op === "rename") { const taken = accAccounts.filter((a) => a.tool === tool).map((a) => a.name); const nn = await askText("Rename account", name, "Rename '" + name + "' to:", nameValidator(taken, name)); if (nn && nn !== name) accAction("account.rename", { tool, name, newName: nn }); }
|
|
3647
|
-
else if (op === "login")
|
|
3665
|
+
else if (op === "login") accAction("account.login", { tool, name });
|
|
3648
3666
|
else if (op === "provider") {
|
|
3649
3667
|
const a = accAccounts.find((x) => x.tool === tool && x.name === name);
|
|
3650
3668
|
if (!a) return;
|
|
@@ -3710,7 +3728,7 @@ export {
|
|
|
3710
3728
|
|
|
3711
3729
|
// --- hash routing between the subpages ---
|
|
3712
3730
|
// Single source of truth for the subpage ids (savings is the default/fallback).
|
|
3713
|
-
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "packs"];
|
|
3731
|
+
const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "codegraph", "packs"];
|
|
3714
3732
|
// Views that apply to a single project; the rest are global/machine-level only.
|
|
3715
3733
|
const PROJECT_VIEWS = ["settings", "skills"];
|
|
3716
3734
|
let activeView = "savings";
|
|
@@ -3731,6 +3749,7 @@ export {
|
|
|
3731
3749
|
else if (v === "usage") { loadProviderStatus(); if (!usageAccountsLoaded) { usageAccountsLoaded = true; loadUsageAccounts(); } }
|
|
3732
3750
|
else if (v === "resources") loadResources();
|
|
3733
3751
|
else if (v === "recall") loadRecall();
|
|
3752
|
+
else if (v === "codegraph") loadCodeGraph();
|
|
3734
3753
|
else if (v === "packs") loadPacks();
|
|
3735
3754
|
// The chart was sized while its view may have been hidden; nudge it on return.
|
|
3736
3755
|
else if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
|
|
@@ -3808,9 +3827,9 @@ export {
|
|
|
3808
3827
|
const stTag = (s) => s === "expired" ? " (login expired)" : s === "empty" ? " (not signed in)" : s === "absent" ? " (no login)" : "";
|
|
3809
3828
|
const opts = '<option value="">follow active (' + esc(p.resolvedAccount || "default") + ")" + stTag(p.resolvedState) + "</option>"
|
|
3810
3829
|
+ (p.accounts || []).map((a) => '<option value="' + esc(a.name) + '"' + (p.defaultAccount === a.name ? " selected" : "") + ">" + esc(a.name) + (a.label && a.label !== a.name ? " - " + esc(a.label) : "") + stTag(a.state) + "</option>").join("");
|
|
3811
|
-
const warn = p.installed && (p.resolvedState === "expired" || p.resolvedState === "empty")
|
|
3830
|
+
const warn = (p.installed && !p.contextReady && (p.resolvedState === "expired" || p.resolvedState === "empty"))
|
|
3812
3831
|
? '<div class="set-hint" style="margin-top:6px;color:var(--warn,#e0a020)">This login is ' + (p.resolvedState === "expired" ? "expired" : "not signed in") + ' - the pack may ask you to log in. Refresh it (launch that account once) or pick another above.</div>'
|
|
3813
|
-
: "";
|
|
3832
|
+
: (p.installed && p.contextReady ? '<div class="set-hint" style="margin-top:6px;color:var(--ok)">Active session ready - launches without asking to log in.</div>' : "");
|
|
3814
3833
|
const accountSel = p.installed
|
|
3815
3834
|
? '<label class="set-hint" style="display:flex;align-items:center;gap:8px;margin-top:10px">Account that seeds this pack:'
|
|
3816
3835
|
+ '<select data-pack-account data-id="' + esc(p.id) + '">' + opts + "</select></label>" + warn
|
|
@@ -3854,6 +3873,82 @@ export {
|
|
|
3854
3873
|
});
|
|
3855
3874
|
}
|
|
3856
3875
|
|
|
3876
|
+
// --- code graph subpage (structural code memory) ---
|
|
3877
|
+
let cgProject = null;
|
|
3878
|
+
// A single architecture/schema field, rendered defensively (shape varies by tool version).
|
|
3879
|
+
function cgItem(x) {
|
|
3880
|
+
if (x && typeof x === "object") return String(x.name || x.label || x.path || x.id || JSON.stringify(x)).slice(0, 60);
|
|
3881
|
+
return String(x);
|
|
3882
|
+
}
|
|
3883
|
+
function cgVal(v) {
|
|
3884
|
+
if (Array.isArray(v)) {
|
|
3885
|
+
if (!v.length) return '<span class="tag">none</span>';
|
|
3886
|
+
return v.slice(0, 12).map((x) => '<span class="tag">' + esc(cgItem(x)) + "</span>").join(" ")
|
|
3887
|
+
+ (v.length > 12 ? ' <span class="set-hint">+' + (v.length - 12) + " more</span>" : "");
|
|
3888
|
+
}
|
|
3889
|
+
if (v && typeof v === "object") return '<span class="set-hint">' + esc(Object.keys(v).length + " field(s)") + "</span>";
|
|
3890
|
+
return esc(String(v));
|
|
3891
|
+
}
|
|
3892
|
+
function cgSection(title, obj) {
|
|
3893
|
+
const entries = obj && typeof obj === "object" ? Object.entries(obj) : [];
|
|
3894
|
+
if (!entries.length) return "";
|
|
3895
|
+
return '<div class="panel"><div class="set-cat-h">' + esc(title) + "</div>"
|
|
3896
|
+
+ entries.map(([k, v]) => '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(k) + '</div><div class="set-hint">' + cgVal(v) + "</div></div></div>").join("")
|
|
3897
|
+
+ "</div>";
|
|
3898
|
+
}
|
|
3899
|
+
function renderCodeGraph(view) {
|
|
3900
|
+
view = view || {};
|
|
3901
|
+
const on = !!view.enabled;
|
|
3902
|
+
const t = $("cgToggle");
|
|
3903
|
+
t.classList.toggle("on", on); t.setAttribute("aria-checked", on ? "true" : "false"); t.textContent = on ? "on" : "off";
|
|
3904
|
+
if (!view.available) {
|
|
3905
|
+
$("cgBody").innerHTML = '<div class="panel"><div class="set-cat-h">Not installed</div>'
|
|
3906
|
+
+ '<div class="set-hint" style="padding:6px 0">The code-intelligence engine is not available yet. When enabled, your agent fetches it automatically on first use (needs <code>npx</code> on PATH). Then tell your agent "Index this project".</div></div>';
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
const projects = view.projects || [];
|
|
3910
|
+
const projHtml = projects.length
|
|
3911
|
+
? projects.map((p) => {
|
|
3912
|
+
const name = p.name || p.root || "";
|
|
3913
|
+
const sel = name === view.selected;
|
|
3914
|
+
return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(name)
|
|
3915
|
+
+ (sel ? ' <span class="tag" style="color:var(--ok)">viewing</span>' : "")
|
|
3916
|
+
+ '</div><div class="set-hint">' + esc(p.root || "") + '</div></div>'
|
|
3917
|
+
+ '<div class="set-ctl"><button type="button" class="toggle' + (sel ? " on" : "") + '" data-cg-project="' + esc(name) + '">View</button></div></div>';
|
|
3918
|
+
}).join("")
|
|
3919
|
+
: '<div class="empty" style="padding:12px 0">No projects indexed yet. In your agent, say "Index this project".</div>';
|
|
3920
|
+
let html = '<div class="panel"><div class="set-cat-h">Indexed projects <small>' + projects.length + "</small></div>" + projHtml + "</div>";
|
|
3921
|
+
if (view.architecture) html += cgSection("Architecture" + (view.selected ? " - " + view.selected : ""), view.architecture);
|
|
3922
|
+
if (view.schema) html += cgSection("Graph schema", view.schema);
|
|
3923
|
+
$("cgBody").innerHTML = html;
|
|
3924
|
+
}
|
|
3925
|
+
async function loadCodeGraph() {
|
|
3926
|
+
$("cgNote").textContent = "";
|
|
3927
|
+
try {
|
|
3928
|
+
const q = cgProject ? "?project=" + encodeURIComponent(cgProject) : "";
|
|
3929
|
+
renderCodeGraph(await (await fetch("/api/codegraph" + q, { cache: "no-store" })).json());
|
|
3930
|
+
} catch { $("cgBody").innerHTML = '<div class="empty" style="padding:12px 0">Code graph unavailable.</div>'; }
|
|
3931
|
+
}
|
|
3932
|
+
async function cgToggleAction() {
|
|
3933
|
+
const turnOn = $("cgToggle").getAttribute("aria-checked") !== "true";
|
|
3934
|
+
setNote("cgNote", "Saving...");
|
|
3935
|
+
try {
|
|
3936
|
+
const out = await (await fetch("/api/codegraph", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ op: "toggle", on: turnOn, project: cgProject || undefined }) })).json();
|
|
3937
|
+
if (out.view) renderCodeGraph(out.view);
|
|
3938
|
+
setNote("cgNote", out.ok ? (turnOn ? "Codebase memory enabled - registered in your agents (restart them to load it)." : "Codebase memory disabled.") : (out.error || "Action failed."), out.ok ? "ok" : "err");
|
|
3939
|
+
} catch { setNote("cgNote", "Could not reach the server.", "err"); }
|
|
3940
|
+
}
|
|
3941
|
+
function wireCodeGraph() {
|
|
3942
|
+
$("cgRefresh").addEventListener("click", loadCodeGraph);
|
|
3943
|
+
$("cgToggle").addEventListener("click", cgToggleAction);
|
|
3944
|
+
$("view-codegraph").addEventListener("click", (e) => {
|
|
3945
|
+
const b = e.target.closest("button[data-cg-project]");
|
|
3946
|
+
if (!b) return;
|
|
3947
|
+
cgProject = b.dataset.cgProject;
|
|
3948
|
+
loadCodeGraph();
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
|
|
3857
3952
|
// --- recall subpage (local session memory; reads your own transcripts) ---
|
|
3858
3953
|
const RECALL_TYPES = ["bugfix", "feature", "refactor", "change", "discovery", "decision", "security"];
|
|
3859
3954
|
let recallProjSig = "";
|
|
@@ -4422,6 +4517,7 @@ export {
|
|
|
4422
4517
|
wireUsage();
|
|
4423
4518
|
wireResources();
|
|
4424
4519
|
wireRecall();
|
|
4520
|
+
wireCodeGraph();
|
|
4425
4521
|
wirePacks();
|
|
4426
4522
|
wireScope();
|
|
4427
4523
|
enhanceAll(); // upgrade every static <select> into the shared pretty dropdown
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/dashboard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.70",
|
|
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": [
|