@lifeaitools/clauth 1.30.9 → 1.30.11
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
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
} from "../watchdog-registry.js";
|
|
31
31
|
import {
|
|
32
32
|
discoverPlugins,
|
|
33
|
+
getSupervisorDir,
|
|
33
34
|
getSupervisorPort,
|
|
35
|
+
loadSupervisorState,
|
|
34
36
|
listPlugins,
|
|
35
37
|
listRoutes,
|
|
36
38
|
listSurfaces,
|
|
@@ -583,6 +585,69 @@ function validateWriteToken(req, writeSession) {
|
|
|
583
585
|
return a.length === b.length && crypto.timingSafeEqual(a, b);
|
|
584
586
|
}
|
|
585
587
|
|
|
588
|
+
export function isLoopbackAddress(remote) {
|
|
589
|
+
return remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function summarizeSupervisorTarget(target) {
|
|
593
|
+
if (!target || typeof target !== "object") return null;
|
|
594
|
+
return {
|
|
595
|
+
plugin_id: typeof target.plugin_id === "string" ? target.plugin_id : undefined,
|
|
596
|
+
surface_id: typeof target.surface_id === "string" ? target.surface_id : undefined,
|
|
597
|
+
tunnel_id: typeof target.tunnel_id === "string" ? target.tunnel_id : undefined,
|
|
598
|
+
route_id: typeof target.route_id === "string" ? target.route_id : undefined,
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function summarizeSupervisorState(value) {
|
|
603
|
+
if (!value || typeof value !== "object") return null;
|
|
604
|
+
return {
|
|
605
|
+
ok: typeof value.ok === "boolean" ? value.ok : undefined,
|
|
606
|
+
state: typeof value.state === "string" ? value.state : undefined,
|
|
607
|
+
reason: typeof value.reason === "string" ? value.reason : undefined,
|
|
608
|
+
status: typeof value.status === "number" ? value.status : undefined,
|
|
609
|
+
private: typeof value.private === "boolean" ? value.private : undefined,
|
|
610
|
+
public_route: typeof value.public_route === "boolean" ? value.public_route : undefined,
|
|
611
|
+
evidence: Array.isArray(value.evidence) ? value.evidence.map((item) => String(item).slice(0, 200)).slice(0, 5) : [],
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export function supervisorLogDto(event) {
|
|
616
|
+
if (!event || typeof event !== "object") return { kind: "unknown" };
|
|
617
|
+
if (event.kind === "operation") {
|
|
618
|
+
return {
|
|
619
|
+
ts: event.ts || event.created_at,
|
|
620
|
+
kind: "operation",
|
|
621
|
+
operationId: event.operationId,
|
|
622
|
+
actor: event.actor,
|
|
623
|
+
action: event.action,
|
|
624
|
+
target: summarizeSupervisorTarget(event.target),
|
|
625
|
+
resulting_state: summarizeSupervisorState(event.resulting_state),
|
|
626
|
+
completed_at: event.completed_at,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return {
|
|
630
|
+
ts: event.ts,
|
|
631
|
+
kind: event.kind,
|
|
632
|
+
plugin_id: event.plugin_id,
|
|
633
|
+
source: event.source,
|
|
634
|
+
state: event.state,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function supervisorOperationDto(receipt) {
|
|
639
|
+
return {
|
|
640
|
+
operationId: receipt.operationId,
|
|
641
|
+
actor: receipt.actor,
|
|
642
|
+
action: receipt.action,
|
|
643
|
+
target: summarizeSupervisorTarget(receipt.target),
|
|
644
|
+
resulting_state: summarizeSupervisorState(receipt.resulting_state),
|
|
645
|
+
evidence: Array.isArray(receipt.evidence) ? receipt.evidence.map((item) => String(item).slice(0, 200)).slice(0, 5) : [],
|
|
646
|
+
created_at: receipt.created_at,
|
|
647
|
+
completed_at: receipt.completed_at,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
586
651
|
export function supervisorRequiresWriteToken(port = getSupervisorPort(), env = process.env) {
|
|
587
652
|
if (port !== getSupervisorPort()) return true;
|
|
588
653
|
return env.CLAUTH_SUPERVISOR_REQUIRE_WRITE_TOKEN === "1";
|
|
@@ -901,8 +966,30 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
901
966
|
.wiz-link:hover{text-decoration:underline}
|
|
902
967
|
.wiz-test-result{padding:12px 14px;border-radius:8px;font-size:.85rem;margin-top:10px;display:none}
|
|
903
968
|
.wiz-test-result.ok{background:rgba(74,222,128,.08);border:1px solid rgba(74,222,128,.2);color:#4ade80}
|
|
904
|
-
.wiz-test-result.fail{background:rgba(248,113,113,.08);border:1px solid rgba(248,113,113,.2);color:#f87171}
|
|
905
|
-
|
|
969
|
+
.wiz-test-result.fail{background:rgba(248,113,113,.08);border:1px solid rgba(248,113,113,.2);color:#f87171}
|
|
970
|
+
.supervisor-panel{margin:14px 16px;border:1px solid #233249;background:linear-gradient(135deg,rgba(15,23,42,.96),rgba(10,15,26,.98));border-radius:12px;padding:14px}
|
|
971
|
+
.supervisor-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}
|
|
972
|
+
.supervisor-title{font-size:.95rem;font-weight:700;color:#e2e8f0}
|
|
973
|
+
.supervisor-sub{font-size:.76rem;color:#94a3b8;margin-top:3px;line-height:1.35}
|
|
974
|
+
.supervisor-actions{display:flex;gap:8px;flex-wrap:wrap}
|
|
975
|
+
.supervisor-action{background:#0f172a;color:#cbd5e1;border:1px solid #334155;border-radius:6px;padding:6px 10px;font-size:.75rem;cursor:pointer}
|
|
976
|
+
.supervisor-action:hover{border-color:#38bdf8;color:#e0f2fe}
|
|
977
|
+
.supervisor-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}
|
|
978
|
+
.supervisor-card{border:1px solid #1e293b;background:rgba(2,6,23,.52);border-radius:10px;padding:10px;min-width:0}
|
|
979
|
+
.supervisor-card h4{margin:0 0 8px;color:#cbd5e1;font-size:.75rem;letter-spacing:.08em;text-transform:uppercase}
|
|
980
|
+
.supervisor-kpi{font-size:1.35rem;font-weight:700;color:#f8fafc}
|
|
981
|
+
.supervisor-meta{font-size:.72rem;color:#94a3b8;font-family:'Courier New',monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
982
|
+
.supervisor-pill{display:inline-flex;align-items:center;border-radius:999px;padding:2px 7px;margin:2px 4px 2px 0;font-size:.68rem;border:1px solid #334155;color:#cbd5e1;background:#0f172a}
|
|
983
|
+
.supervisor-pill.ok{border-color:rgba(74,222,128,.35);color:#86efac;background:rgba(34,197,94,.08)}
|
|
984
|
+
.supervisor-pill.warn{border-color:rgba(250,204,21,.35);color:#fde68a;background:rgba(250,204,21,.08)}
|
|
985
|
+
.supervisor-pill.bad{border-color:rgba(248,113,113,.35);color:#fecaca;background:rgba(248,113,113,.08)}
|
|
986
|
+
.supervisor-list{display:flex;flex-direction:column;gap:8px;max-height:320px;overflow:auto}
|
|
987
|
+
.supervisor-row{border:1px solid #1e293b;border-radius:8px;padding:8px;background:rgba(15,23,42,.45)}
|
|
988
|
+
.supervisor-row-top{display:flex;justify-content:space-between;gap:8px;align-items:center}
|
|
989
|
+
.supervisor-name{font-size:.82rem;color:#e2e8f0;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
990
|
+
.supervisor-row-actions{display:flex;gap:5px;flex-wrap:wrap;margin-top:7px}
|
|
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
|
+
</style>
|
|
906
993
|
</head>
|
|
907
994
|
<body>
|
|
908
995
|
|
|
@@ -1090,7 +1177,7 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
1090
1177
|
</div>
|
|
1091
1178
|
</div>
|
|
1092
1179
|
|
|
1093
|
-
<div class="monkey-panel" id="monkey-panel">
|
|
1180
|
+
<div class="monkey-panel" id="monkey-panel">
|
|
1094
1181
|
<div class="monkey-head">
|
|
1095
1182
|
<div>
|
|
1096
1183
|
<div class="monkey-title">Monkey local CLI dispatch</div>
|
|
@@ -1115,10 +1202,44 @@ function dashboardHtml(port, whitelist, isStaged = false, initWriteToken = null)
|
|
|
1115
1202
|
<span class="monkey-status" id="monkey-status">idle</span>
|
|
1116
1203
|
</div>
|
|
1117
1204
|
<div class="monkey-result" id="monkey-result"></div>
|
|
1118
|
-
</div>
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1205
|
+
</div>
|
|
1206
|
+
|
|
1207
|
+
<section class="supervisor-panel" id="supervisor-panel">
|
|
1208
|
+
<div class="supervisor-head">
|
|
1209
|
+
<div>
|
|
1210
|
+
<div class="supervisor-title">Local Software Factory Supervisor</div>
|
|
1211
|
+
<div class="supervisor-sub">clauth owns local LIFEAI plugin discovery, candidate tests, clauth-owned surface operations, receipts, and supervisor event logs. Dev Center should call this surface instead of PM2 directly.</div>
|
|
1212
|
+
</div>
|
|
1213
|
+
<div class="supervisor-actions">
|
|
1214
|
+
<button class="supervisor-action" onclick="loadSupervisorCockpit()">Refresh</button>
|
|
1215
|
+
<button class="supervisor-action" onclick="rescanSupervisorPlugins()">Rescan plugins</button>
|
|
1216
|
+
</div>
|
|
1217
|
+
</div>
|
|
1218
|
+
<div id="supervisor-status" class="supervisor-sub">Loading supervisor state…</div>
|
|
1219
|
+
<div class="supervisor-grid" style="margin-top:10px">
|
|
1220
|
+
<div class="supervisor-card">
|
|
1221
|
+
<h4>Plugins</h4>
|
|
1222
|
+
<div class="supervisor-kpi" id="supervisor-plugin-count">—</div>
|
|
1223
|
+
<div class="supervisor-meta" id="supervisor-plugin-meta">awaiting data</div>
|
|
1224
|
+
<div class="supervisor-list" id="supervisor-plugins" style="margin-top:8px"></div>
|
|
1225
|
+
</div>
|
|
1226
|
+
<div class="supervisor-card">
|
|
1227
|
+
<h4>Surfaces</h4>
|
|
1228
|
+
<div class="supervisor-kpi" id="supervisor-surface-count">—</div>
|
|
1229
|
+
<div class="supervisor-meta" id="supervisor-surface-meta">destination + owner matrix</div>
|
|
1230
|
+
<div class="supervisor-list" id="supervisor-surfaces" style="margin-top:8px"></div>
|
|
1231
|
+
</div>
|
|
1232
|
+
<div class="supervisor-card">
|
|
1233
|
+
<h4>Operations + log</h4>
|
|
1234
|
+
<div class="supervisor-kpi" id="supervisor-operation-count">—</div>
|
|
1235
|
+
<div class="supervisor-meta" id="supervisor-log-path">events.jsonl</div>
|
|
1236
|
+
<div class="supervisor-log" id="supervisor-events" style="margin-top:8px">No events loaded.</div>
|
|
1237
|
+
</div>
|
|
1238
|
+
</div>
|
|
1239
|
+
</section>
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
<div class="wizard-panel" id="wizard-panel">
|
|
1122
1243
|
<div class="wizard-header">
|
|
1123
1244
|
<div class="tunnel-dot off" id="wiz-dot" style="width:10px;height:10px;border-radius:50%"></div>
|
|
1124
1245
|
<div class="wizard-title">claude.ai Tunnel Setup</div>
|
|
@@ -1368,10 +1489,89 @@ function showMain(ping) {
|
|
|
1368
1489
|
document.getElementById("s-fails").textContent =
|
|
1369
1490
|
ping.failures + "/" + (ping.failures + ping.failures_remaining);
|
|
1370
1491
|
}
|
|
1371
|
-
pollTunnel();
|
|
1372
|
-
updateBuildStatus();
|
|
1373
|
-
|
|
1374
|
-
|
|
1492
|
+
pollTunnel();
|
|
1493
|
+
updateBuildStatus();
|
|
1494
|
+
loadSupervisorCockpit();
|
|
1495
|
+
refreshWriteLockUi();
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
function supervisorBadge(text, kind) {
|
|
1499
|
+
return '<span class="supervisor-pill ' + (kind || '') + '">' + htmlEscape(text) + '</span>';
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
async function supervisorJson(path, options) {
|
|
1503
|
+
const response = await fetch(BASE + path, { cache: "no-store", ...(options || {}) });
|
|
1504
|
+
const data = await response.json().catch(() => ({}));
|
|
1505
|
+
if (!response.ok || data.error) throw new Error(data.error || ("HTTP " + response.status));
|
|
1506
|
+
return data;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
async function loadSupervisorCockpit() {
|
|
1510
|
+
const status = document.getElementById("supervisor-status");
|
|
1511
|
+
if (status) status.textContent = "Loading supervisor state…";
|
|
1512
|
+
try {
|
|
1513
|
+
const [health, plugins, surfaces, logs] = await Promise.all([
|
|
1514
|
+
supervisorJson("/health"),
|
|
1515
|
+
supervisorJson("/v1/plugins"),
|
|
1516
|
+
supervisorJson("/v1/surfaces"),
|
|
1517
|
+
supervisorJson("/v1/logs?limit=40"),
|
|
1518
|
+
]);
|
|
1519
|
+
const pluginRows = plugins.plugins || [];
|
|
1520
|
+
const surfaceRows = surfaces.surfaces || [];
|
|
1521
|
+
document.getElementById("supervisor-plugin-count").textContent = String(pluginRows.length);
|
|
1522
|
+
document.getElementById("supervisor-surface-count").textContent = String(surfaceRows.length);
|
|
1523
|
+
document.getElementById("supervisor-operation-count").textContent = String((logs.operations || []).length);
|
|
1524
|
+
document.getElementById("supervisor-plugin-meta").textContent = "current " + (pluginRows.filter(p => p.state === "current").length) + " · awaiting " + (pluginRows.filter(p => p.state === "awaiting_enable").length);
|
|
1525
|
+
document.getElementById("supervisor-surface-meta").textContent = "pm2 home: " + (health.pm2_home || "—");
|
|
1526
|
+
document.getElementById("supervisor-log-path").textContent = logs.log_path || "events.jsonl";
|
|
1527
|
+
document.getElementById("supervisor-plugins").innerHTML = pluginRows.length ? pluginRows.map(renderSupervisorPlugin).join("") : '<div class="supervisor-sub">No plugins discovered.</div>';
|
|
1528
|
+
document.getElementById("supervisor-surfaces").innerHTML = surfaceRows.length ? surfaceRows.map(renderSupervisorSurface).join("") : '<div class="supervisor-sub">No surfaces declared.</div>';
|
|
1529
|
+
document.getElementById("supervisor-events").textContent = (logs.events || []).slice(-20).reverse().map(e => {
|
|
1530
|
+
const label = e.kind === "operation" ? (e.action + " " + JSON.stringify(e.target || {})) : (e.kind + " " + (e.plugin_id || ""));
|
|
1531
|
+
return (e.ts || e.created_at || "") + " " + label;
|
|
1532
|
+
}).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}"), "");
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
if (status) status.innerHTML = supervisorBadge("supervisor unreachable", "bad") + " " + htmlEscape(err.message || err);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
function renderSupervisorPlugin(plugin) {
|
|
1540
|
+
const stateKind = plugin.state === "current" ? "ok" : (plugin.state === "manifest_invalid" || plugin.state === "missing" ? "bad" : "warn");
|
|
1541
|
+
return '<div class="supervisor-row"><div class="supervisor-row-top"><span class="supervisor-name">' + htmlEscape(plugin.id) + '</span>' + supervisorBadge(plugin.state || "unknown", stateKind) + '</div>' +
|
|
1542
|
+
'<div class="supervisor-meta">' + htmlEscape(plugin.publisher || "unknown") + ' · ' + htmlEscape(plugin.version || "—") + ' · ' + htmlEscape(plugin.source || "—") + '</div>' +
|
|
1543
|
+
'<div class="supervisor-meta">' + htmlEscape((plugin.manifest_hash || "").slice(0, 12)) + '</div>' +
|
|
1544
|
+
'<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>';
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
function renderSupervisorSurface(surface) {
|
|
1548
|
+
const ownerKind = surface.lifecycle_owner === "clauth" ? "ok" : (surface.lifecycle_owner === "plugin" ? "warn" : "");
|
|
1549
|
+
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) + '</div>' +
|
|
1550
|
+
'<div class="supervisor-meta">' + htmlEscape(surface.destination || "—") + ' · port ' + htmlEscape(surface.port || "—") + '</div>' +
|
|
1551
|
+
'<div class="supervisor-meta">' + htmlEscape(surface.health || surface.health_url || "no health") + '</div>' +
|
|
1552
|
+
'<div class="supervisor-row-actions">' + ["reconcile","restart","test","rollback"].map(a => '<button class="supervisor-action" onclick="runSupervisorSurface(' + jsArg(surface.id) + ',' + jsArg(a) + ')">' + a + '</button>').join("") + '</div></div>';
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
async function rescanSupervisorPlugins() {
|
|
1556
|
+
try {
|
|
1557
|
+
await supervisorJson("/v1/plugins/rescan", { method: "POST", headers: writeHeaders() });
|
|
1558
|
+
await loadSupervisorCockpit();
|
|
1559
|
+
} catch (err) { alert("Supervisor rescan failed: " + (err.message || err)); }
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
async function runSupervisorPlugin(id, action) {
|
|
1563
|
+
try {
|
|
1564
|
+
await supervisorJson("/v1/plugins/" + encodeURIComponent(id) + "/" + encodeURIComponent(action), { method: "POST", headers: writeHeaders() });
|
|
1565
|
+
await loadSupervisorCockpit();
|
|
1566
|
+
} catch (err) { alert("Plugin " + action + " failed: " + (err.message || err)); }
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
async function runSupervisorSurface(id, action) {
|
|
1570
|
+
try {
|
|
1571
|
+
await supervisorJson("/v1/surfaces/" + encodeURIComponent(id) + "/actions", { method: "POST", headers: writeHeaders({ "Content-Type": "application/json" }), body: JSON.stringify({ action }) });
|
|
1572
|
+
await loadSupervisorCockpit();
|
|
1573
|
+
} catch (err) { alert("Surface " + action + " failed: " + (err.message || err)); }
|
|
1574
|
+
}
|
|
1375
1575
|
|
|
1376
1576
|
// ── Unlock ──────────────────────────────────
|
|
1377
1577
|
async function unlock() {
|
|
@@ -3183,6 +3383,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3183
3383
|
function hasSupervisorWrite(req) {
|
|
3184
3384
|
if (validateWriteToken(req, writeSession)) return true;
|
|
3185
3385
|
if (!isSupervisorPort) return false;
|
|
3386
|
+
if (!isLoopbackAddress(req.socket?.remoteAddress)) return false;
|
|
3186
3387
|
return supervisorTestNoToken || !supervisorRequiresWriteToken(port);
|
|
3187
3388
|
}
|
|
3188
3389
|
|
|
@@ -3611,9 +3812,9 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3611
3812
|
}
|
|
3612
3813
|
}
|
|
3613
3814
|
|
|
3614
|
-
const server = http.createServer(async (req, res) => {
|
|
3615
|
-
const remote = req.socket.remoteAddress;
|
|
3616
|
-
const isLocal = remote
|
|
3815
|
+
const server = http.createServer(async (req, res) => {
|
|
3816
|
+
const remote = req.socket.remoteAddress;
|
|
3817
|
+
const isLocal = isLoopbackAddress(remote);
|
|
3617
3818
|
|
|
3618
3819
|
const url = new URL(req.url, `http://127.0.0.1:${port}`);
|
|
3619
3820
|
const reqPath = url.pathname;
|
|
@@ -3705,6 +3906,18 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3705
3906
|
return ok(res, { tunnels: listTunnels() });
|
|
3706
3907
|
}
|
|
3707
3908
|
|
|
3909
|
+
if (method === "GET" && reqPath === "/v1/logs") {
|
|
3910
|
+
const limit = Number(url.searchParams.get("limit") || 100);
|
|
3911
|
+
const boundedLimit = Number.isFinite(limit) ? Math.max(1, Math.min(limit, 500)) : 100;
|
|
3912
|
+
const state = loadSupervisorState();
|
|
3913
|
+
return ok(res, {
|
|
3914
|
+
schema: "clauth.supervisor.logs.v1",
|
|
3915
|
+
log_path: path.join(getSupervisorDir(), "events.jsonl"),
|
|
3916
|
+
events: readSupervisorEvents(boundedLimit).map(supervisorLogDto),
|
|
3917
|
+
operations: (state.operations || []).slice(0, boundedLimit).map(supervisorOperationDto),
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3708
3921
|
const tunnelRoutesMatch = reqPath.match(/^\/v1\/tunnels\/([^/]+)\/routes$/);
|
|
3709
3922
|
if (method === "POST" && tunnelRoutesMatch) {
|
|
3710
3923
|
if (!hasSupervisorWrite(req)) return rejectSupervisorWrite(res);
|
|
@@ -3726,7 +3939,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3726
3939
|
const id = decodeURIComponent(reqPath.split("/").pop());
|
|
3727
3940
|
const operation = (supervisorHealth(), readSupervisorEvents(500)).find((event) => event.operationId === id);
|
|
3728
3941
|
res.writeHead(operation ? 200 : 404, { "Content-Type": "application/json", ...CORS });
|
|
3729
|
-
return res.end(JSON.stringify(operation
|
|
3942
|
+
return res.end(JSON.stringify(operation ? supervisorLogDto(operation) : { error: "operation_not_found" }));
|
|
3730
3943
|
}
|
|
3731
3944
|
|
|
3732
3945
|
if (method === "GET" && reqPath === "/v1/events") {
|
|
@@ -3737,7 +3950,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3737
3950
|
...CORS,
|
|
3738
3951
|
});
|
|
3739
3952
|
for (const event of readSupervisorEvents(Number(url.searchParams.get("limit") || 100))) {
|
|
3740
|
-
res.write(`event: supervisor\ndata: ${JSON.stringify(event)}\n\n`);
|
|
3953
|
+
res.write(`event: supervisor\ndata: ${JSON.stringify(supervisorLogDto(event))}\n\n`);
|
|
3741
3954
|
}
|
|
3742
3955
|
res.end();
|
|
3743
3956
|
return;
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
supervisorHealth,
|
|
18
18
|
validatePluginManifest,
|
|
19
19
|
} from "./supervisor-registry.js";
|
|
20
|
-
import { supervisorRequiresWriteToken } from "./commands/serve.js";
|
|
20
|
+
import { isLoopbackAddress, supervisorLogDto, supervisorRequiresWriteToken } from "./commands/serve.js";
|
|
21
21
|
|
|
22
22
|
function withTempSupervisor(fn) {
|
|
23
23
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-supervisor-"));
|
|
@@ -181,6 +181,39 @@ test("supervisor write-token policy is temporarily relaxed only for the localhos
|
|
|
181
181
|
assert.equal(supervisorRequiresWriteToken(52439, {}), false);
|
|
182
182
|
assert.equal(supervisorRequiresWriteToken(52439, { CLAUTH_SUPERVISOR_REQUIRE_WRITE_TOKEN: "1" }), true);
|
|
183
183
|
assert.equal(supervisorRequiresWriteToken(52437, {}), true);
|
|
184
|
+
assert.equal(isLoopbackAddress("127.0.0.1"), true);
|
|
185
|
+
assert.equal(isLoopbackAddress("::1"), true);
|
|
186
|
+
assert.equal(isLoopbackAddress("::ffff:127.0.0.1"), true);
|
|
187
|
+
assert.equal(isLoopbackAddress("192.168.1.25"), false);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("supervisor log DTO strips raw operation payloads", () => {
|
|
191
|
+
const dto = supervisorLogDto({
|
|
192
|
+
ts: "2026-07-30T00:00:00.000Z",
|
|
193
|
+
kind: "operation",
|
|
194
|
+
operationId: "op-secret",
|
|
195
|
+
actor: "localhost",
|
|
196
|
+
action: "restart",
|
|
197
|
+
target: { surface_id: "demo", command: ["node", "--token=secret"] },
|
|
198
|
+
prior_state: { start: ["node", "--token=secret"], cwd: "C:/secrets" },
|
|
199
|
+
resulting_state: {
|
|
200
|
+
ok: false,
|
|
201
|
+
state: "operation_failed",
|
|
202
|
+
stderr: "SECRET=abc123",
|
|
203
|
+
evidence: ["CLAUTH_PM2_HOME=C:/safe"],
|
|
204
|
+
},
|
|
205
|
+
stderr: "SECRET=abc123",
|
|
206
|
+
completed_at: "2026-07-30T00:00:01.000Z",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const json = JSON.stringify(dto);
|
|
210
|
+
assert.equal(dto.operationId, "op-secret");
|
|
211
|
+
assert.equal(dto.target.surface_id, "demo");
|
|
212
|
+
assert.equal(dto.resulting_state.state, "operation_failed");
|
|
213
|
+
assert.equal(json.includes("prior_state"), false);
|
|
214
|
+
assert.equal(json.includes("stderr"), false);
|
|
215
|
+
assert.equal(json.includes("SECRET"), false);
|
|
216
|
+
assert.equal(json.includes("--token"), false);
|
|
184
217
|
});
|
|
185
218
|
|
|
186
219
|
test("tunnel route add and remove produce reversible operation receipts", () => withTempSupervisor((root) => {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|