@lifeaitools/clauth 1.30.19 → 1.30.21
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
CHANGED
|
@@ -933,7 +933,7 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
933
933
|
.supervisor-name{font-size:.82rem;color:#e2e8f0;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
934
934
|
.supervisor-row-actions{display:flex;gap:5px;flex-wrap:wrap;margin-top:7px}
|
|
935
935
|
.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}
|
|
936
|
-
.supervisor-feedback{margin-top:8px;padding:
|
|
936
|
+
.supervisor-feedback{margin-top:8px;padding:9px 12px;border-radius:6px;font-family:'Courier New',monospace;font-size:.88rem;font-weight:600;color:#86efac;background:rgba(34,197,94,.12);border:1px solid rgba(74,222,128,.35);border-left:4px solid #4ade80;display:none}
|
|
937
937
|
.supervisor-feedback.bad{color:#fecaca;background:rgba(248,113,113,.08);border-color:rgba(248,113,113,.25)}
|
|
938
938
|
</style>
|
|
939
939
|
</head>
|
|
@@ -1187,12 +1187,6 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
1187
1187
|
<div id="supervisor-status" class="supervisor-sub">Loading supervisor state…</div>
|
|
1188
1188
|
<div id="supervisor-feedback" class="supervisor-feedback" role="status"></div>
|
|
1189
1189
|
<div class="supervisor-grid" style="margin-top:10px">
|
|
1190
|
-
<div class="supervisor-card">
|
|
1191
|
-
<h4>Plugins</h4>
|
|
1192
|
-
<div class="supervisor-kpi" id="supervisor-plugin-count">—</div>
|
|
1193
|
-
<div class="supervisor-meta" id="supervisor-plugin-meta">awaiting data</div>
|
|
1194
|
-
<div class="supervisor-list" id="supervisor-plugins" style="margin-top:8px"></div>
|
|
1195
|
-
</div>
|
|
1196
1190
|
<div class="supervisor-card">
|
|
1197
1191
|
<h4>Surfaces</h4>
|
|
1198
1192
|
<div class="supervisor-kpi" id="supervisor-surface-count">—</div>
|
|
@@ -1515,21 +1509,16 @@ async function loadSupervisorCockpit() {
|
|
|
1515
1509
|
const status = document.getElementById("supervisor-status");
|
|
1516
1510
|
if (status) status.textContent = "Loading supervisor state…";
|
|
1517
1511
|
try {
|
|
1518
|
-
const [health,
|
|
1512
|
+
const [health, surfaces, logs] = await Promise.all([
|
|
1519
1513
|
supervisorJson("/health"),
|
|
1520
|
-
supervisorJson("/v1/plugins"),
|
|
1521
1514
|
supervisorJson("/v1/surfaces"),
|
|
1522
1515
|
supervisorJson("/v1/logs?limit=40"),
|
|
1523
1516
|
]);
|
|
1524
|
-
const pluginRows = plugins.plugins || [];
|
|
1525
1517
|
const surfaceRows = surfaces.surfaces || [];
|
|
1526
|
-
document.getElementById("supervisor-plugin-count").textContent = String(pluginRows.length);
|
|
1527
1518
|
document.getElementById("supervisor-surface-count").textContent = String(surfaceRows.length);
|
|
1528
1519
|
document.getElementById("supervisor-operation-count").textContent = String((logs.operations || []).length);
|
|
1529
|
-
document.getElementById("supervisor-plugin-meta").textContent = "current " + (pluginRows.filter(p => p.state === "current").length) + " · awaiting " + (pluginRows.filter(p => p.state === "awaiting_enable").length);
|
|
1530
1520
|
document.getElementById("supervisor-surface-meta").textContent = "pm2 home: " + (health.pm2_home || "—");
|
|
1531
1521
|
document.getElementById("supervisor-log-path").textContent = logs.log_path || "events.jsonl";
|
|
1532
|
-
document.getElementById("supervisor-plugins").innerHTML = pluginRows.length ? pluginRows.map(renderSupervisorPlugin).join("") : '<div class="supervisor-sub">No plugins discovered.</div>';
|
|
1533
1522
|
document.getElementById("supervisor-surfaces").innerHTML = surfaceRows.length ? surfaceRows.map(renderSupervisorSurface).join("") : '<div class="supervisor-sub">No surfaces declared.</div>';
|
|
1534
1523
|
document.getElementById("supervisor-events").textContent = (logs.events || []).slice(-20).reverse().map(e => {
|
|
1535
1524
|
const label = e.kind === "operation" ? (e.action + " " + JSON.stringify(e.target || {})) : (e.kind + " " + (e.plugin_id || ""));
|
|
@@ -1542,21 +1531,13 @@ async function loadSupervisorCockpit() {
|
|
|
1542
1531
|
}
|
|
1543
1532
|
}
|
|
1544
1533
|
|
|
1545
|
-
function renderSupervisorPlugin(plugin) {
|
|
1546
|
-
const stateKind = plugin.state === "current" ? "ok" : (plugin.state === "manifest_invalid" || plugin.state === "missing" ? "bad" : "warn");
|
|
1547
|
-
return '<div class="supervisor-row"><div class="supervisor-row-top"><span class="supervisor-name">' + htmlEscape(plugin.id) + '</span>' + supervisorBadge(plugin.state || "unknown", stateKind) + '</div>' +
|
|
1548
|
-
'<div class="supervisor-meta">' + htmlEscape(plugin.publisher || "unknown") + ' · ' + htmlEscape(plugin.version || "—") + ' · ' + htmlEscape(plugin.source || "—") + '</div>' +
|
|
1549
|
-
'<div class="supervisor-meta">' + htmlEscape((plugin.manifest_hash || "").slice(0, 12)) + '</div>' +
|
|
1550
|
-
'<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>';
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
1534
|
function renderSupervisorSurface(surface) {
|
|
1554
1535
|
const ownerKind = surface.lifecycle_owner === "clauth" ? "ok" : (surface.lifecycle_owner === "plugin" ? "warn" : "");
|
|
1555
1536
|
const stateKind = surface.state === "current" || surface.status === "healthy" ? "ok" : (surface.state === "unavailable" ? "bad" : "warn");
|
|
1556
1537
|
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>' +
|
|
1557
1538
|
'<div class="supervisor-meta">' + htmlEscape(surface.destination || "—") + ' · port ' + htmlEscape(surface.port || "—") + '</div>' +
|
|
1558
1539
|
'<div class="supervisor-meta">' + htmlEscape(surface.health || surface.health_url || "no health") + '</div>' +
|
|
1559
|
-
'<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>';
|
|
1540
|
+
'<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.plugin_id + ":" + surface.id) + ',' + jsArg(a) + ',this)">' + a + '</button>').join("") + '</div></div>';
|
|
1560
1541
|
}
|
|
1561
1542
|
|
|
1562
1543
|
async function rescanSupervisorPlugins() {
|
|
@@ -1567,21 +1548,23 @@ async function rescanSupervisorPlugins() {
|
|
|
1567
1548
|
} catch (err) { supervisorFeedback("Plugin rescan failed: " + (err.message || err), true); }
|
|
1568
1549
|
}
|
|
1569
1550
|
|
|
1570
|
-
async function
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
await loadSupervisorCockpit();
|
|
1575
|
-
} catch (err) { supervisorFeedback("Plugin " + action + " failed: " + (err.message || err), true); }
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
async function runSupervisorSurface(id, action) {
|
|
1551
|
+
async function runSupervisorSurface(id, action, btn) {
|
|
1552
|
+
const originalLabel = btn ? btn.textContent : null;
|
|
1553
|
+
if (btn) { btn.disabled = true; btn.textContent = "..."; btn.style.opacity = "0.6"; }
|
|
1554
|
+
supervisorFeedback(action + " " + id + ": sending...", false);
|
|
1579
1555
|
try {
|
|
1580
1556
|
const receipt = await supervisorJson("/v1/surfaces/" + encodeURIComponent(id) + "/actions", { method: "POST", headers: writeHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ action }) });
|
|
1581
1557
|
const result = receipt.resulting_state || {};
|
|
1582
|
-
|
|
1558
|
+
const ok = result.ok !== false;
|
|
1559
|
+
if (btn) { btn.textContent = ok ? "done" : "failed"; btn.style.background = ok ? "#14532d" : "#7f1d1d"; }
|
|
1560
|
+
supervisorFeedback(id + " " + action + " " + (ok ? "succeeded" : "failed") + " " + [char]0x2192 + " " + (result.state || result.reason || "completed"), !ok);
|
|
1561
|
+
await new Promise((resolve) => setTimeout(resolve, 700));
|
|
1583
1562
|
await loadSupervisorCockpit();
|
|
1584
|
-
} catch (err) {
|
|
1563
|
+
} catch (err) {
|
|
1564
|
+
if (btn) { btn.textContent = "failed"; btn.style.background = "#7f1d1d"; }
|
|
1565
|
+
supervisorFeedback(id + " " + action + " failed: " + (err.message || err), true);
|
|
1566
|
+
if (btn) { setTimeout(() => { btn.disabled = false; btn.textContent = originalLabel; btn.style.opacity = ""; btn.style.background = ""; }, 2500); }
|
|
1567
|
+
}
|
|
1585
1568
|
}
|
|
1586
1569
|
|
|
1587
1570
|
// ── Unlock ──────────────────────────────────
|
|
@@ -1,16 +1,94 @@
|
|
|
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
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
import { listSurfaces, listPlugins, runSurfaceAction, runPluginAction } from './supervisor-registry.js';
|
|
7
|
+
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const serveSource = fs.readFileSync(path.join(here, 'commands', 'serve.js'), 'utf8');
|
|
10
|
+
|
|
11
|
+
// Regression coverage for the 2026-08-06 incident: every surface across every
|
|
12
|
+
// plugin shares the bare id "primary", and the dashboard's onclick handlers
|
|
13
|
+
// sent that bare id instead of the plugin-qualified "plugin_id:id" that
|
|
14
|
+
// findSurface() already supported. The server-side .find() always resolved
|
|
15
|
+
// to the FIRST surface in the array (codeflow-mcp), which unconditionally
|
|
16
|
+
// rejects every action as self-owned -- so all 49 buttons (7 surfaces x 7
|
|
17
|
+
// actions) silently hit the same surface and appeared to do nothing, on
|
|
18
|
+
// every surface, for every action. The prior test in this file only
|
|
19
|
+
// regex-matched that the action-name strings existed in source; it would
|
|
20
|
+
// pass under this exact bug. These tests exercise the real resolution path
|
|
21
|
+
// through the same exported functions the HTTP routes call (POST
|
|
22
|
+
// /v1/surfaces/:id/actions -> runSurfaceAction; POST /v1/plugins/:id/:action
|
|
23
|
+
// -> runPluginAction), which is the same simulate-a-button-click contract as
|
|
24
|
+
// the dashboard onclick handlers use, without needing a browser.
|
|
25
|
+
|
|
26
|
+
test('the dashboard encodes a plugin-qualified surface id, not the bare (colliding) surface.id', () => {
|
|
27
|
+
assert.match(
|
|
28
|
+
serveSource,
|
|
29
|
+
/jsArg\(surface\.plugin_id \+ ":" \+ surface\.id\)/,
|
|
30
|
+
'renderSupervisorSurface must send plugin_id:id, not the bare surface.id shared by every surface'
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('every real surface exists and every surface id collides with at least one other (documents why plugin_id is required)', () => {
|
|
35
|
+
const surfaces = listSurfaces();
|
|
36
|
+
assert.ok(surfaces.length > 1, 'expected more than one discovered surface to make this regression possible');
|
|
37
|
+
const bareIds = new Set(surfaces.map((s) => s.id));
|
|
38
|
+
assert.equal(bareIds.size, 1, 'expected every discovered surface to share the same bare id ("primary") -- if this ever stops being true, the plugin_id qualifier is still correct but this documentation test should be updated');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('runSurfaceAction resolves the plugin-qualified id to that exact surface, not a different one', () => {
|
|
42
|
+
const surfaces = listSurfaces();
|
|
43
|
+
assert.ok(surfaces.length >= 1, 'no surfaces discovered -- cannot verify resolution');
|
|
44
|
+
for (const surface of surfaces) {
|
|
45
|
+
const compositeId = surface.plugin_id + ':' + surface.id;
|
|
46
|
+
// action "test" is the designed no-op/safe action -- it never touches a
|
|
47
|
+
// real process, it only records a receipt, which is exactly what a
|
|
48
|
+
// verification harness should use.
|
|
49
|
+
const receipt = runSurfaceAction(compositeId, 'test', 'supervisor-ui.test.js');
|
|
50
|
+
assert.ok(!receipt.error, 'surface ' + compositeId + ' resolution failed: ' + receipt.error);
|
|
51
|
+
assert.equal(
|
|
52
|
+
receipt.prior_state?.plugin_id,
|
|
53
|
+
surface.plugin_id,
|
|
54
|
+
'requested surface ' + compositeId + ' but runSurfaceAction resolved a different surface owned by "' + receipt.prior_state?.plugin_id + '" -- this is the exact 2026-08-06 regression'
|
|
55
|
+
);
|
|
56
|
+
assert.equal(receipt.target.surface_id, compositeId);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('bare (unqualified) surface id is ambiguous: it always resolves to the same single surface regardless of which surface was intended', () => {
|
|
61
|
+
const surfaces = listSurfaces();
|
|
62
|
+
assert.ok(surfaces.length > 1, 'need multiple surfaces to demonstrate ambiguity');
|
|
63
|
+
const bareId = surfaces[0].id;
|
|
64
|
+
const receiptsByBareId = surfaces.map(() => runSurfaceAction(bareId, 'test', 'supervisor-ui.test.js'));
|
|
65
|
+
const resolvedOwners = new Set(receiptsByBareId.map((r) => r.prior_state?.plugin_id));
|
|
66
|
+
assert.equal(
|
|
67
|
+
resolvedOwners.size,
|
|
68
|
+
1,
|
|
69
|
+
'expected the bare id to be ambiguous (always resolve to one surface) -- if this now resolves multiple owners, the collision this test guards against has already been fixed at the id level and this test can be removed'
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('every one of the 7 button actions is exercised end-to-end through runSurfaceAction for every discovered surface', () => {
|
|
74
|
+
const actions = ['start', 'stop', 'restart', 'reconcile', 'test', 'promote', 'rollback'];
|
|
75
|
+
const surfaces = listSurfaces();
|
|
76
|
+
for (const surface of surfaces) {
|
|
77
|
+
const compositeId = surface.plugin_id + ':' + surface.id;
|
|
78
|
+
for (const action of actions) {
|
|
79
|
+
const receipt = runSurfaceAction(compositeId, action, 'supervisor-ui.test.js');
|
|
80
|
+
assert.notEqual(receipt.error, 'surface_not_found', compositeId + ' / ' + action + ' could not resolve the surface');
|
|
81
|
+
assert.notEqual(receipt.error, 'invalid_action', compositeId + ' / ' + action + ' is not a recognized action');
|
|
82
|
+
assert.equal(receipt.prior_state?.plugin_id, surface.plugin_id, compositeId + ' / ' + action + ' resolved the wrong surface');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('every plugin-level action is reachable through runPluginAction for every discovered plugin', () => {
|
|
88
|
+
const plugins = listPlugins();
|
|
89
|
+
assert.ok(plugins.length > 0, 'no plugins discovered');
|
|
90
|
+
for (const plugin of plugins) {
|
|
91
|
+
const receipt = runPluginAction(plugin.id, 'test', 'supervisor-ui.test.js');
|
|
92
|
+
assert.notEqual(receipt.error, 'plugin_not_found', plugin.id + ' could not be resolved by its own id');
|
|
93
|
+
}
|
|
94
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifeaitools/clauth",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.21",
|
|
4
4
|
"description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "bash scripts/build.sh",
|
|
11
|
-
"test": "node --test cli/commands/scrub.test.js cli/api.classify.test.js test/studio-debug.test.mjs test/dashboard-key-generator.test.mjs && node test-auth-verdict.mjs",
|
|
11
|
+
"test": "node --test cli/commands/scrub.test.js cli/api.classify.test.js cli/supervisor-registry.test.js cli/supervisor-ui.test.js test/studio-debug.test.mjs test/dashboard-key-generator.test.mjs && node test-auth-verdict.mjs",
|
|
12
12
|
"test:studio-debug": "node --test test/studio-debug.test.mjs",
|
|
13
13
|
"test:agent-pool": "node test/agent-pool.test.mjs",
|
|
14
14
|
"test:call-agent-10": "node test/call-agent-10-skills.test.mjs",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|