@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.
- package/cli/commands/serve.js +59 -35
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
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) {
|
|
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
|
-
|
|
1674
|
-
} catch (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.
|