@lifeaitools/clauth 1.30.11 → 1.30.12
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 +25 -8
- package/cli/supervisor-ui.test.js +16 -0
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -989,6 +989,8 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
989
989
|
.supervisor-name{font-size:.82rem;color:#e2e8f0;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
990
990
|
.supervisor-row-actions{display:flex;gap:5px;flex-wrap:wrap;margin-top:7px}
|
|
991
991
|
.supervisor-log{font-family:'Courier New',monospace;font-size:.7rem;color:#94a3b8;white-space:pre-wrap;word-break:break-word;max-height:280px;overflow:auto}
|
|
992
|
+
.supervisor-feedback{margin-top:8px;padding:7px 9px;border-radius:6px;font-family:'Courier New',monospace;font-size:.72rem;color:#86efac;background:rgba(34,197,94,.08);border:1px solid rgba(74,222,128,.22);display:none}
|
|
993
|
+
.supervisor-feedback.bad{color:#fecaca;background:rgba(248,113,113,.08);border-color:rgba(248,113,113,.25)}
|
|
992
994
|
</style>
|
|
993
995
|
</head>
|
|
994
996
|
<body>
|
|
@@ -1216,6 +1218,7 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
1216
1218
|
</div>
|
|
1217
1219
|
</div>
|
|
1218
1220
|
<div id="supervisor-status" class="supervisor-sub">Loading supervisor state…</div>
|
|
1221
|
+
<div id="supervisor-feedback" class="supervisor-feedback" role="status"></div>
|
|
1219
1222
|
<div class="supervisor-grid" style="margin-top:10px">
|
|
1220
1223
|
<div class="supervisor-card">
|
|
1221
1224
|
<h4>Plugins</h4>
|
|
@@ -1506,6 +1509,14 @@ async function supervisorJson(path, options) {
|
|
|
1506
1509
|
return data;
|
|
1507
1510
|
}
|
|
1508
1511
|
|
|
1512
|
+
function supervisorFeedback(text, bad) {
|
|
1513
|
+
const el = document.getElementById("supervisor-feedback");
|
|
1514
|
+
if (!el) return;
|
|
1515
|
+
el.textContent = text || "";
|
|
1516
|
+
el.className = "supervisor-feedback" + (bad ? " bad" : "");
|
|
1517
|
+
el.style.display = text ? "block" : "none";
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1509
1520
|
async function loadSupervisorCockpit() {
|
|
1510
1521
|
const status = document.getElementById("supervisor-status");
|
|
1511
1522
|
if (status) status.textContent = "Loading supervisor state…";
|
|
@@ -1530,9 +1541,10 @@ async function loadSupervisorCockpit() {
|
|
|
1530
1541
|
const label = e.kind === "operation" ? (e.action + " " + JSON.stringify(e.target || {})) : (e.kind + " " + (e.plugin_id || ""));
|
|
1531
1542
|
return (e.ts || e.created_at || "") + " " + label;
|
|
1532
1543
|
}).join("\\n") || "No events yet.";
|
|
1533
|
-
if (status) status.innerHTML = supervisorBadge("supervisor " + (health.status || "unknown"), "ok") + supervisorBadge("vault " + (health.vault_locked ? "locked" : "unlocked"), health.vault_locked ? "warn" : "ok") + supervisorBadge("v" + (health.clauth_version || "${VERSION}"), "");
|
|
1544
|
+
if (status) status.innerHTML = supervisorBadge("supervisor " + (health.status === "ok" ? "healthy" : (health.status || "unknown")), health.status === "ok" ? "ok" : "warn") + supervisorBadge("vault " + (health.vault_locked ? "locked" : "unlocked"), health.vault_locked ? "warn" : "ok") + supervisorBadge("v" + (health.clauth_version || "${VERSION}"), "");
|
|
1534
1545
|
} catch (err) {
|
|
1535
1546
|
if (status) status.innerHTML = supervisorBadge("supervisor unreachable", "bad") + " " + htmlEscape(err.message || err);
|
|
1547
|
+
supervisorFeedback("Supervisor unavailable — actions are disabled until the local control plane returns.", true);
|
|
1536
1548
|
}
|
|
1537
1549
|
}
|
|
1538
1550
|
|
|
@@ -1546,31 +1558,36 @@ function renderSupervisorPlugin(plugin) {
|
|
|
1546
1558
|
|
|
1547
1559
|
function renderSupervisorSurface(surface) {
|
|
1548
1560
|
const ownerKind = surface.lifecycle_owner === "clauth" ? "ok" : (surface.lifecycle_owner === "plugin" ? "warn" : "");
|
|
1549
|
-
|
|
1561
|
+
const stateKind = surface.state === "current" || surface.status === "healthy" ? "ok" : (surface.state === "unavailable" ? "bad" : "warn");
|
|
1562
|
+
return '<div class="supervisor-row"><div class="supervisor-row-top"><span class="supervisor-name">' + htmlEscape(surface.plugin_id + ":" + surface.id) + '</span>' + supervisorBadge(surface.lifecycle_owner || "unknown", ownerKind) + supervisorBadge(surface.state || surface.status || "unknown", stateKind) + '</div>' +
|
|
1550
1563
|
'<div class="supervisor-meta">' + htmlEscape(surface.destination || "—") + ' · port ' + htmlEscape(surface.port || "—") + '</div>' +
|
|
1551
1564
|
'<div class="supervisor-meta">' + htmlEscape(surface.health || surface.health_url || "no health") + '</div>' +
|
|
1552
|
-
'<div class="supervisor-row-actions">' + ["
|
|
1565
|
+
'<div class="supervisor-row-actions">' + ["start","stop","restart","reconcile","test","promote","rollback"].map(a => '<button class="supervisor-action" data-supervisor-action="' + a + '" onclick="runSupervisorSurface(' + jsArg(surface.id) + ',' + jsArg(a) + ')">' + a + '</button>').join("") + '</div></div>';
|
|
1553
1566
|
}
|
|
1554
1567
|
|
|
1555
1568
|
async function rescanSupervisorPlugins() {
|
|
1556
1569
|
try {
|
|
1557
1570
|
await supervisorJson("/v1/plugins/rescan", { method: "POST", headers: writeHeaders() });
|
|
1571
|
+
supervisorFeedback("Plugin rescan completed.", false);
|
|
1558
1572
|
await loadSupervisorCockpit();
|
|
1559
|
-
} catch (err) {
|
|
1573
|
+
} catch (err) { supervisorFeedback("Plugin rescan failed: " + (err.message || err), true); }
|
|
1560
1574
|
}
|
|
1561
1575
|
|
|
1562
1576
|
async function runSupervisorPlugin(id, action) {
|
|
1563
1577
|
try {
|
|
1564
|
-
await supervisorJson("/v1/plugins/" + encodeURIComponent(id) + "/" + encodeURIComponent(action), { method: "POST", headers: writeHeaders() });
|
|
1578
|
+
const receipt = await supervisorJson("/v1/plugins/" + encodeURIComponent(id) + "/" + encodeURIComponent(action), { method: "POST", headers: writeHeaders() });
|
|
1579
|
+
supervisorFeedback("Plugin " + action + " receipt " + (receipt.operationId || "recorded") + " · " + ((receipt.resulting_state && receipt.resulting_state.state) || "completed"), false);
|
|
1565
1580
|
await loadSupervisorCockpit();
|
|
1566
|
-
} catch (err) {
|
|
1581
|
+
} catch (err) { supervisorFeedback("Plugin " + action + " failed: " + (err.message || err), true); }
|
|
1567
1582
|
}
|
|
1568
1583
|
|
|
1569
1584
|
async function runSupervisorSurface(id, action) {
|
|
1570
1585
|
try {
|
|
1571
|
-
await supervisorJson("/v1/surfaces/" + encodeURIComponent(id) + "/actions", { method: "POST", headers: writeHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ action }) });
|
|
1586
|
+
const receipt = await supervisorJson("/v1/surfaces/" + encodeURIComponent(id) + "/actions", { method: "POST", headers: writeHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ action }) });
|
|
1587
|
+
const result = receipt.resulting_state || {};
|
|
1588
|
+
supervisorFeedback("Surface " + action + " receipt " + (receipt.operationId || "recorded") + " · " + (result.state || "completed"), result.ok === false);
|
|
1572
1589
|
await loadSupervisorCockpit();
|
|
1573
|
-
} catch (err) {
|
|
1590
|
+
} catch (err) { supervisorFeedback("Surface " + action + " failed: " + (err.message || err), true); }
|
|
1574
1591
|
}
|
|
1575
1592
|
|
|
1576
1593
|
// ── Unlock ──────────────────────────────────
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const serveSource = fs.readFileSync(path.join(here, 'commands', 'serve.js'), 'utf8');
|
|
9
|
+
|
|
10
|
+
test('clauth supervisor UI exposes every lifecycle action and receipt feedback', () => {
|
|
11
|
+
for (const action of ['start', 'stop', 'restart', 'reconcile', 'test', 'promote', 'rollback']) {
|
|
12
|
+
assert.match(serveSource, new RegExp('"' + action + '"'));
|
|
13
|
+
}
|
|
14
|
+
assert.match(serveSource, /supervisorFeedback\(/);
|
|
15
|
+
assert.match(serveSource, /supervisor-feedback/);
|
|
16
|
+
});
|