@lifeaitools/clauth 1.30.25 → 1.30.26

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.
@@ -685,6 +685,9 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
685
685
  .btn-unlock:hover{background:#2563eb}
686
686
  .btn-unlock:disabled{background:#1e3a5f;color:#4a6fa5;cursor:not-allowed}
687
687
  .lock-err{color:#f87171;font-size:.82rem;margin-top:.75rem;min-height:1.2em}
688
+ #write-unlock-overlay{display:none;position:fixed;inset:0;background:rgba(2,6,23,.72);z-index:9999;align-items:center;justify-content:center;padding:2rem}
689
+ .write-unlock-cancel{width:100%;background:transparent;color:#94a3b8;border:1px solid #334155;border-radius:8px;padding:8px;font-size:.85rem;cursor:pointer;margin-top:.6rem;transition:border-color .15s}
690
+ .write-unlock-cancel:hover{border-color:#64748b;color:#cbd5e1}
688
691
  /* ── Main view ── */
689
692
  #main-view{display:none;padding:2rem}
690
693
  .header{display:flex;align-items:center;gap:10px;margin-bottom:1.5rem;flex-wrap:wrap}
@@ -959,6 +962,24 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
959
962
  </div>
960
963
  </div>
961
964
 
965
+ <!-- Write-unlock modal (replaces window.prompt(), which silently no-ops in
966
+ embedded/webview browser contexts and after a browser suppresses repeated
967
+ native dialogs) -->
968
+ <div id="write-unlock-overlay">
969
+ <div class="lock-card">
970
+ <div class="lock-icon">🔒</div>
971
+ <div class="lock-title">Enable writes</div>
972
+ <div class="lock-sub">Enter your vault password to enable saving changes (30-minute write session)</div>
973
+ <form onsubmit="submitWriteUnlock();return false;" autocomplete="on">
974
+ <input type="text" name="username" value="clauth" autocomplete="username" style="display:none">
975
+ <input class="lock-input" id="write-unlock-input" type="password" placeholder="••••••••••••" autocomplete="current-password">
976
+ <button class="btn-unlock" id="write-unlock-btn" type="submit">Unlock Writes</button>
977
+ </form>
978
+ <div class="lock-err" id="write-unlock-err"></div>
979
+ <button type="button" class="write-unlock-cancel" onclick="closeWriteUnlockModal()">Cancel</button>
980
+ </div>
981
+ </div>
982
+
962
983
  <!-- ── Main view (shown after unlock) ──────── -->
963
984
  <div id="main-view">
964
985
  <div id="upgrade-banner" style="display:none" class="upgrade-banner">
@@ -1174,12 +1195,6 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
1174
1195
  <div id="supervisor-status" class="supervisor-sub">Loading supervisor state…</div>
1175
1196
  <div id="supervisor-feedback" class="supervisor-feedback" role="status"></div>
1176
1197
  <div class="supervisor-grid" style="margin-top:10px">
1177
- <div class="supervisor-card">
1178
- <h4>Plugins</h4>
1179
- <div class="supervisor-kpi" id="supervisor-plugin-count">—</div>
1180
- <div class="supervisor-meta" id="supervisor-plugin-meta">awaiting data</div>
1181
- <div class="supervisor-list" id="supervisor-plugins" style="margin-top:8px"></div>
1182
- </div>
1183
1198
  <div class="supervisor-card">
1184
1199
  <h4>Surfaces</h4>
1185
1200
  <div class="supervisor-kpi" id="supervisor-surface-count">—</div>
@@ -1507,21 +1522,16 @@ async function loadSupervisorCockpit() {
1507
1522
  const status = document.getElementById("supervisor-status");
1508
1523
  if (status) status.textContent = "Loading supervisor state…";
1509
1524
  try {
1510
- const [health, plugins, surfaces, logs] = await Promise.all([
1525
+ const [health, surfaces, logs] = await Promise.all([
1511
1526
  supervisorJson("/health"),
1512
- supervisorJson("/v1/plugins"),
1513
1527
  supervisorJson("/v1/surfaces"),
1514
1528
  supervisorJson("/v1/logs?limit=40"),
1515
1529
  ]);
1516
- const pluginRows = plugins.plugins || [];
1517
1530
  const surfaceRows = surfaces.surfaces || [];
1518
- document.getElementById("supervisor-plugin-count").textContent = String(pluginRows.length);
1519
1531
  document.getElementById("supervisor-surface-count").textContent = String(surfaceRows.length);
1520
1532
  document.getElementById("supervisor-operation-count").textContent = String((logs.operations || []).length);
1521
- document.getElementById("supervisor-plugin-meta").textContent = "current " + (pluginRows.filter(p => p.state === "current").length) + " · awaiting " + (pluginRows.filter(p => p.state === "awaiting_enable").length);
1522
1533
  document.getElementById("supervisor-surface-meta").textContent = "pm2 home: " + (health.pm2_home || "—");
1523
1534
  document.getElementById("supervisor-log-path").textContent = logs.log_path || "events.jsonl";
1524
- document.getElementById("supervisor-plugins").innerHTML = pluginRows.length ? pluginRows.map(renderSupervisorPlugin).join("") : '<div class="supervisor-sub">No plugins discovered.</div>';
1525
1535
  document.getElementById("supervisor-surfaces").innerHTML = surfaceRows.length ? surfaceRows.map(renderSupervisorSurface).join("") : '<div class="supervisor-sub">No surfaces declared.</div>';
1526
1536
  document.getElementById("supervisor-events").textContent = (logs.events || []).slice(-20).reverse().map(e => {
1527
1537
  const label = e.kind === "operation" ? (e.action + " " + JSON.stringify(e.target || {})) : (e.kind + " " + (e.plugin_id || ""));
@@ -1534,14 +1544,6 @@ async function loadSupervisorCockpit() {
1534
1544
  }
1535
1545
  }
1536
1546
 
1537
- function renderSupervisorPlugin(plugin) {
1538
- const stateKind = plugin.state === "current" ? "ok" : (plugin.state === "manifest_invalid" || plugin.state === "missing" ? "bad" : "warn");
1539
- return '<div class="supervisor-row"><div class="supervisor-row-top"><span class="supervisor-name">' + htmlEscape(plugin.id) + '</span>' + supervisorBadge(plugin.state || "unknown", stateKind) + '</div>' +
1540
- '<div class="supervisor-meta">' + htmlEscape(plugin.publisher || "unknown") + ' · ' + htmlEscape(plugin.version || "—") + ' · ' + htmlEscape(plugin.source || "—") + '</div>' +
1541
- '<div class="supervisor-meta">' + htmlEscape((plugin.manifest_hash || "").slice(0, 12)) + '</div>' +
1542
- '<div class="supervisor-row-actions"><button class="supervisor-action" onclick="runSupervisorPlugin(' + jsArg(plugin.id) + ',' + jsArg(plugin.enabled ? "disable" : "enable") + ')">' + (plugin.enabled ? "Disable" : "Enable") + '</button><button class="supervisor-action" onclick="runSupervisorPlugin(' + jsArg(plugin.id) + ',' + jsArg("test") + ')">Test</button><button class="supervisor-action" onclick="runSupervisorPlugin(' + jsArg(plugin.id) + ',' + jsArg("promote") + ')">Promote</button></div></div>';
1543
- }
1544
-
1545
1547
  function renderSupervisorSurface(surface) {
1546
1548
  const ownerKind = surface.lifecycle_owner === "clauth" ? "ok" : (surface.lifecycle_owner === "plugin" ? "warn" : "");
1547
1549
  const stateKind = surface.state === "current" || surface.status === "healthy" ? "ok" : (surface.state === "unavailable" ? "bad" : "warn");
@@ -1559,14 +1561,6 @@ async function rescanSupervisorPlugins() {
1559
1561
  } catch (err) { supervisorFeedback("Plugin rescan failed: " + (err.message || err), true); }
1560
1562
  }
1561
1563
 
1562
- async function runSupervisorPlugin(id, action) {
1563
- try {
1564
- const receipt = await supervisorJson("/v1/plugins/" + encodeURIComponent(id) + "/" + encodeURIComponent(action), { method: "POST", headers: writeHeaders() });
1565
- supervisorFeedback("Plugin " + action + " receipt " + (receipt.operationId || "recorded") + " · " + ((receipt.resulting_state && receipt.resulting_state.state) || "completed"), false);
1566
- await loadSupervisorCockpit();
1567
- } catch (err) { supervisorFeedback("Plugin " + action + " failed: " + (err.message || err), true); }
1568
- }
1569
-
1570
1564
  async function runSupervisorSurface(id, action) {
1571
1565
  try {
1572
1566
  const receipt = await supervisorJson("/v1/surfaces/" + encodeURIComponent(id) + "/actions", { method: "POST", headers: writeHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ action }) });
@@ -1657,21 +1651,51 @@ async function lockVault() {
1657
1651
  // ── Unlock writes (re-establish write scope without locking) ──
1658
1652
  // Needed when the daemon auto-unlocks via --pw/boot.key: the page never sees the
1659
1653
  // unlock screen, so it holds no write token. POST /auth mints one (10-min TTL).
1660
- async function unlockWrites() {
1661
- if (writeToken && !confirm("Writes are already unlocked this session. Re-unlock?")) return;
1662
- const pw = prompt("Enter your vault password to enable saving changes (10-minute write session):");
1663
- if (!pw) return;
1654
+ function unlockWrites() {
1655
+ openWriteUnlockModal();
1656
+ }
1657
+
1658
+ function openWriteUnlockModal() {
1659
+ const overlay = document.getElementById("write-unlock-overlay");
1660
+ const input = document.getElementById("write-unlock-input");
1661
+ const err = document.getElementById("write-unlock-err");
1662
+ if (err) err.textContent = "";
1663
+ if (input) { input.value = ""; input.className = "lock-input"; }
1664
+ if (overlay) overlay.style.display = "flex";
1665
+ if (input) setTimeout(() => input.focus(), 50);
1666
+ }
1667
+
1668
+ function closeWriteUnlockModal() {
1669
+ const overlay = document.getElementById("write-unlock-overlay");
1670
+ if (overlay) overlay.style.display = "none";
1671
+ }
1672
+
1673
+ async function submitWriteUnlock() {
1674
+ const input = document.getElementById("write-unlock-input");
1675
+ const btn = document.getElementById("write-unlock-btn");
1676
+ const err = document.getElementById("write-unlock-err");
1677
+ const pw = input ? input.value : "";
1678
+ if (!pw) { if (err) err.textContent = "Password is required."; return; }
1679
+ if (btn) { btn.disabled = true; btn.textContent = "Verifying..."; }
1664
1680
  try {
1665
1681
  const r = await fetch(BASE + "/auth", {
1666
1682
  method: "POST",
1667
1683
  headers: { "Content-Type": "application/json" },
1668
1684
  body: JSON.stringify({ password: pw }),
1669
1685
  }).then(r => r.json());
1670
- if (r.error) { alert("Unlock failed: " + r.error); return; }
1686
+ if (r.error) {
1687
+ if (input) { input.className = "lock-input error"; setTimeout(() => input.className = "lock-input", 600); }
1688
+ if (err) err.textContent = "Invalid: " + (r.error || "Invalid password");
1689
+ return;
1690
+ }
1671
1691
  writeToken = r.write_token || null;
1672
1692
  refreshWriteLockUi();
1673
- alert(writeToken ? "Writes unlocked for 10 minutes." : "Unlock did not return a write token.");
1674
- } catch (e) { alert("Unlock error: " + (e.message || e)); }
1693
+ closeWriteUnlockModal();
1694
+ } catch (e) {
1695
+ if (err) err.textContent = "Unlock error: " + (e.message || e);
1696
+ } finally {
1697
+ if (btn) { btn.disabled = false; btn.textContent = "Unlock Writes"; }
1698
+ }
1675
1699
  }
1676
1700
 
1677
1701
  // Reflect write-lock state on the button so it is obvious when a save will fail.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.30.25",
3
+ "version": "1.30.26",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {