@enigmax/dashboard 0.1.64 → 0.1.66

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.
Files changed (2) hide show
  1. package/assets/index.html +75 -1
  2. 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="#/packs" data-view="packs" class="tab">Packs</a>
529
530
  </nav>
530
531
  <div class="nav">
531
532
  <label class="scope-wrap" title="Scope: global config or a specific project"><span class="scope-lbl">Scope</span><select id="scopeSel" class="scope-sel"><option value="global">Global</option></select></label>
@@ -803,6 +804,18 @@
803
804
  <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>
804
805
  </section>
805
806
 
807
+ <section id="view-packs" style="display:none">
808
+ <div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
809
+ <div style="flex:1">
810
+ <h1>Packs</h1>
811
+ <p>Optional harness packs you can add on demand. Each runs in its own <strong>isolated agent context</strong>, so its skills and commands never load into your normal coding agent. Launch a pack from a terminal with <code>enigma &lt;pack&gt;</code>.</p>
812
+ </div>
813
+ <button id="packsRefresh" 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>
814
+ </div>
815
+ <div id="packsNote" class="set-note"></div>
816
+ <div id="packsBody"><div class="empty" style="padding:12px 0">Loading...</div></div>
817
+ </section>
818
+
806
819
  <section id="view-recall" style="display:none">
807
820
  <div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
808
821
  <div style="flex:1">
@@ -3696,7 +3709,7 @@ export {
3696
3709
 
3697
3710
  // --- hash routing between the subpages ---
3698
3711
  // Single source of truth for the subpage ids (savings is the default/fallback).
3699
- const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall"];
3712
+ const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "packs"];
3700
3713
  // Views that apply to a single project; the rest are global/machine-level only.
3701
3714
  const PROJECT_VIEWS = ["settings", "skills"];
3702
3715
  let activeView = "savings";
@@ -3717,6 +3730,7 @@ export {
3717
3730
  else if (v === "usage") { loadProviderStatus(); if (!usageAccountsLoaded) { usageAccountsLoaded = true; loadUsageAccounts(); } }
3718
3731
  else if (v === "resources") loadResources();
3719
3732
  else if (v === "recall") loadRecall();
3733
+ else if (v === "packs") loadPacks();
3720
3734
  // The chart was sized while its view may have been hidden; nudge it on return.
3721
3735
  else if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
3722
3736
  }
@@ -3776,6 +3790,65 @@ export {
3776
3790
  });
3777
3791
  }
3778
3792
 
3793
+ // --- packs subpage (optional isolated harness packs; marketplace) ---
3794
+ function renderPacks(list) {
3795
+ list = list || [];
3796
+ if (!list.length) { $("packsBody").innerHTML = '<div class="empty" style="padding:12px 0">No packs available.</div>'; return; }
3797
+ $("packsBody").innerHTML = list.map((p) => {
3798
+ const tags = (p.tags || []).map((t) => '<span class="tag">' + esc(t) + '</span>').join(" ");
3799
+ const state = p.installed ? '<span class="tag">' + (p.version ? "installed " + esc(p.version) : "installed") + '</span>' : '<span class="tag">not installed</span>';
3800
+ const added = p.enabled ? '<span class="tag" style="color:var(--ok)">added</span>' : "";
3801
+ const actions = p.installed
3802
+ ? '<button type="button" class="toggle on" data-pack="launch" data-id="' + esc(p.id) + '">Launch command</button>'
3803
+ + '<button type="button" class="toggle" data-pack="setup" data-id="' + esc(p.id) + '" title="Register the pack MCP servers (needs Python 3)">Setup MCP</button>'
3804
+ + '<button type="button" class="toggle" data-pack="update" data-id="' + esc(p.id) + '">Update</button>'
3805
+ + '<button type="button" class="btn-danger" data-pack="remove" data-id="' + esc(p.id) + '">Remove</button>'
3806
+ : '<button type="button" class="toggle on" data-pack="install" data-id="' + esc(p.id) + '">Install</button>';
3807
+ const opts = '<option value="">follow active (' + esc(p.resolvedAccount || "default") + ')</option>'
3808
+ + (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) : "") + "</option>").join("");
3809
+ const accountSel = p.installed
3810
+ ? '<label class="set-hint" style="display:flex;align-items:center;gap:8px;margin-top:10px">Account that seeds this pack:'
3811
+ + '<select data-pack-account data-id="' + esc(p.id) + '">' + opts + "</select></label>"
3812
+ : "";
3813
+ return '<div class="panel"><div class="set-row"><div class="set-meta">'
3814
+ + '<div class="set-name">' + esc(p.label) + " " + state + " " + added + '</div>'
3815
+ + '<div class="set-hint">' + esc(p.description) + '</div>'
3816
+ + '<div class="set-hint" style="margin-top:6px">' + tags + ' &nbsp; <a href="' + esc(p.homepage) + '" target="_blank" rel="noopener">source</a></div>'
3817
+ + accountSel
3818
+ + '</div></div><div class="set-ctl" style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px">' + actions + '</div></div>';
3819
+ }).join("");
3820
+ }
3821
+ async function loadPacks() {
3822
+ $("packsNote").textContent = "";
3823
+ try { renderPacks((await (await fetch("/api/packs", { cache: "no-store" })).json()).packs); }
3824
+ catch { $("packsBody").innerHTML = '<div class="empty" style="padding:12px 0">Packs unavailable.</div>'; }
3825
+ }
3826
+ async function packAction(id, action, value) {
3827
+ const confirmMsg = action === "remove" ? "Remove this pack and its isolated context?" : null;
3828
+ if (confirmMsg && !(await askConfirm("Confirm", confirmMsg, true))) return;
3829
+ setNote("packsNote", action === "install" || action === "update" ? "Working..." : "Saving...");
3830
+ try {
3831
+ const out = await (await fetch("/api/packs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, action, value }) })).json();
3832
+ if (out.packs) renderPacks(out.packs);
3833
+ if (out.command) { try { await navigator.clipboard.writeText(out.command); } catch { /* clipboard blocked */ } }
3834
+ setNote("packsNote", out.note || out.error || (out.ok ? "Done." : "Action failed."), out.ok ? "ok" : "err");
3835
+ } catch { setNote("packsNote", "Could not reach the server.", "err"); }
3836
+ }
3837
+ function wirePacks() {
3838
+ $("packsRefresh").addEventListener("click", loadPacks);
3839
+ $("view-packs").addEventListener("click", (e) => {
3840
+ const b = e.target.closest("button[data-pack]");
3841
+ if (!b) return;
3842
+ packAction(b.dataset.id, b.dataset.pack);
3843
+ });
3844
+ // Pin which account seeds a pack (empty value = follow the active account).
3845
+ $("view-packs").addEventListener("change", (e) => {
3846
+ const sel = e.target.closest("select[data-pack-account]");
3847
+ if (!sel) return;
3848
+ packAction(sel.dataset.id, "set-account", sel.value);
3849
+ });
3850
+ }
3851
+
3779
3852
  // --- recall subpage (local session memory; reads your own transcripts) ---
3780
3853
  const RECALL_TYPES = ["bugfix", "feature", "refactor", "change", "discovery", "decision", "security"];
3781
3854
  let recallProjSig = "";
@@ -4344,6 +4417,7 @@ export {
4344
4417
  wireUsage();
4345
4418
  wireResources();
4346
4419
  wireRecall();
4420
+ wirePacks();
4347
4421
  wireScope();
4348
4422
  enhanceAll(); // upgrade every static <select> into the shared pretty dropdown
4349
4423
  $("cfgExport").addEventListener("click", exportConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
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": [