@adhdev/daemon-standalone 1.0.39-rc.1 → 1.0.39-rc.10
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/dist/index.js +362 -170
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/public/assets/{index-69StxR7K.css → index-BRvsAKU_.css} +1 -1
- package/public/assets/index-Hsc3-7gC.js +121 -0
- package/public/assets/{terminal-BZj_X5zy.js → terminal-BiinNPQS.js} +1 -1
- package/public/assets/{vendor-cL4V2vaO.js → vendor-DgFmqOGd.js} +1 -1
- package/public/index.html +3 -3
- package/vendor/mcp-server/index.js +175 -17
- package/vendor/mcp-server/index.js.map +1 -1
- package/public/assets/index-B7t7s0FW.js +0 -121
package/public/index.html
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
<meta name="description" content="ADHDev self-hosted dashboard for controlling AI agents" />
|
|
8
8
|
<link rel="icon" href="/otter-logo.png" />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-Hsc3-7gC.js"></script>
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-DgFmqOGd.js">
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BRvsAKU_.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<!-- Apply theme immediately to prevent FOIT (Flash of Incorrect Theme) -->
|
|
@@ -937,7 +937,7 @@ function isLocalControlPlaneNode(ctx, node) {
|
|
|
937
937
|
// src/tools/mesh-tool-schemas.ts
|
|
938
938
|
var MESH_STATUS_TOOL = {
|
|
939
939
|
name: "mesh_status",
|
|
940
|
-
description:
|
|
940
|
+
description: `Get the current status of all nodes in the repo mesh \u2014 health, git state, active sessions, recovery hints, and recommended next steps. Use this to decide which node to send work to or how to recover from failures. Also reports the running daemon build per daemonId under top-level daemonBuilds ({commit, commitShort, version}); when a live daemon was built from a commit BEHIND its workspace HEAD it adds staleDaemonBuilds[] + staleDaemonBuildWarning \u2014 meaning a just-merged refinery/mesh-tool fix is NOT yet live on that daemon (awaiting deploy/restart; a local dist rebuild does not update a cloud daemon). When a daemon has a durable failed-upgrade notice on record it adds daemonUpgradeFailures{daemonId \u2192 {summary, recordedAt, ageLabel, targetVersion, noticePath, logPath}} + daemonUpgradeFailureWarning \u2014 meaning that daemon's LAST upgrade attempt failed and was rolled back, so it is still on the PREVIOUS version (an upgrade/restart response only ever reports "scheduled", never success). Do not repeatedly call this to wait for generating delegated work; wait for pendingCoordinatorEvents/completion events or an explicit user status request.`,
|
|
941
941
|
inputSchema: {
|
|
942
942
|
type: "object",
|
|
943
943
|
properties: {
|
|
@@ -967,7 +967,7 @@ var MESH_ENQUEUE_TASK_TOOL = {
|
|
|
967
967
|
type: "object",
|
|
968
968
|
properties: {
|
|
969
969
|
message: { type: "string", description: "The task instruction for the agent." },
|
|
970
|
-
task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before dispatch." },
|
|
970
|
+
task_mode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "Optional task-mode contract. live_debug_readonly rejects obvious write/commit/push/deploy/destructive instructions before dispatch \u2014 and in exchange runs without the one-active-per-node write isolation (N read-only tasks may run in parallel on one busy node, no worktree needed) under a separate, larger read-only concurrency cap. Prefer it for investigation/diagnosis: it is the cheaper mode to schedule, not just the restricted one." },
|
|
971
971
|
taskMode: { type: "string", enum: ["code_change", "validation", "live_debug_readonly", "launch_app", "convergence"], description: "CamelCase alias for task_mode." },
|
|
972
972
|
readonly: { type: "boolean", description: "Optional read-only axis (orthogonal to task_mode). When true the task runs without the one-active-per-node write isolation (N read-only tasks may run in parallel on one node), is counted under the read-only safety cap, and rejects write/commit/push/deploy/destructive instructions like live_debug_readonly. Equivalent to task_mode=live_debug_readonly but composable with any task_mode." },
|
|
973
973
|
read_only: { type: "boolean", description: "Snake-case alias for readonly." },
|
|
@@ -1884,7 +1884,7 @@ function summarizeCompactSubmodules(submodules) {
|
|
|
1884
1884
|
...outOfSync.length > 0 ? { outOfSyncPaths: outOfSync } : {}
|
|
1885
1885
|
};
|
|
1886
1886
|
}
|
|
1887
|
-
var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness", "quota"];
|
|
1887
|
+
var MESH_COMPACT_PRESERVED_MARKER_FIELDS = ["dataFreshness", "quota", "isLocalWorktree"];
|
|
1888
1888
|
function summarizeNodeQuota(quota) {
|
|
1889
1889
|
if (!quota || typeof quota !== "object" || Array.isArray(quota)) return void 0;
|
|
1890
1890
|
const out = {};
|
|
@@ -1960,6 +1960,27 @@ function compactNodeSeverity(entry) {
|
|
|
1960
1960
|
if (entry.staleDaemonBuild || entry.submodulesOutOfSync || entry.recoveryHints) return 1;
|
|
1961
1961
|
return 0;
|
|
1962
1962
|
}
|
|
1963
|
+
function pinnedRepresentativeNodeIds(compacted) {
|
|
1964
|
+
const byDaemon = /* @__PURE__ */ new Map();
|
|
1965
|
+
for (const n of compacted) {
|
|
1966
|
+
if (!n || typeof n !== "object") continue;
|
|
1967
|
+
const daemonId = typeof n.daemonId === "string" && n.daemonId ? n.daemonId : "";
|
|
1968
|
+
if (!daemonId) continue;
|
|
1969
|
+
const current = byDaemon.get(daemonId);
|
|
1970
|
+
if (!current) {
|
|
1971
|
+
byDaemon.set(daemonId, n);
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
if (current.isLocalWorktree === true && n.isLocalWorktree !== true) {
|
|
1975
|
+
byDaemon.set(daemonId, n);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
const pinned = /* @__PURE__ */ new Set();
|
|
1979
|
+
for (const n of byDaemon.values()) {
|
|
1980
|
+
if (n?.nodeId !== void 0) pinned.add(String(n.nodeId));
|
|
1981
|
+
}
|
|
1982
|
+
return pinned;
|
|
1983
|
+
}
|
|
1963
1984
|
function isNoteworthyCompactNode(entry) {
|
|
1964
1985
|
if (!entry || typeof entry !== "object") return true;
|
|
1965
1986
|
if (entry.health && entry.health !== "online") return true;
|
|
@@ -2383,14 +2404,32 @@ function findNode(mesh, nodeId) {
|
|
|
2383
2404
|
}
|
|
2384
2405
|
var DUPLICATE_DISPATCH_WINDOW_MS = 6e4;
|
|
2385
2406
|
async function refreshMeshFromDaemon(ctx) {
|
|
2407
|
+
const settledNodeIds = /* @__PURE__ */ new Set();
|
|
2386
2408
|
try {
|
|
2387
2409
|
const result = await ctx.transport.command("get_mesh", { meshId: ctx.mesh.id });
|
|
2388
|
-
if (!result?.success || !Array.isArray(result.mesh?.nodes)) return;
|
|
2410
|
+
if (!result?.success || !Array.isArray(result.mesh?.nodes)) return { settledNodeIds };
|
|
2389
2411
|
const refreshedNodes = result.mesh.nodes.filter((n) => n?.id).map((n) => n);
|
|
2390
|
-
|
|
2412
|
+
const merged = [...refreshedNodes];
|
|
2413
|
+
for (const existing of ctx.mesh.nodes) {
|
|
2414
|
+
const existingId = existing?.id;
|
|
2415
|
+
if (!existingId) continue;
|
|
2416
|
+
if (merged.some((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, existingId))) continue;
|
|
2417
|
+
if (isLocalControlPlaneNode(ctx, existing)) {
|
|
2418
|
+
settledNodeIds.add(existingId);
|
|
2419
|
+
continue;
|
|
2420
|
+
}
|
|
2421
|
+
const clonedFromNodeId = readString(existing?.clonedFromNodeId) || readString(existing?.cloned_from_node_id);
|
|
2422
|
+
if (clonedFromNodeId && refreshedNodes.some((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, clonedFromNodeId))) {
|
|
2423
|
+
settledNodeIds.add(existingId);
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
merged.push(existing);
|
|
2427
|
+
}
|
|
2428
|
+
ctx.mesh.nodes.splice(0, ctx.mesh.nodes.length, ...merged);
|
|
2391
2429
|
ctx.mesh.updatedAt = result.mesh.updatedAt ?? ctx.mesh.updatedAt;
|
|
2392
2430
|
} catch {
|
|
2393
2431
|
}
|
|
2432
|
+
return { settledNodeIds };
|
|
2394
2433
|
}
|
|
2395
2434
|
async function syncCoordinatorDaemonMeshCache(ctx) {
|
|
2396
2435
|
if (!(ctx.transport instanceof IpcTransport)) return;
|
|
@@ -2402,19 +2441,71 @@ async function syncCoordinatorDaemonMeshCache(ctx) {
|
|
|
2402
2441
|
} catch {
|
|
2403
2442
|
}
|
|
2404
2443
|
}
|
|
2444
|
+
async function resolveNodeFromOwningDaemons(ctx, nodeId) {
|
|
2445
|
+
const transport = ctx.transport;
|
|
2446
|
+
if (typeof transport?.meshCommand !== "function") return { node: null, ownerUnreachable: false };
|
|
2447
|
+
const localDaemonId = ctx.localDaemonId;
|
|
2448
|
+
const candidates = [];
|
|
2449
|
+
const pushCandidate = (id) => {
|
|
2450
|
+
if (typeof id !== "string" || !id.trim()) return;
|
|
2451
|
+
if (localDaemonId && (0, import_daemon_core4.daemonIdsEquivalent)(id, localDaemonId)) return;
|
|
2452
|
+
if (!candidates.includes(id)) candidates.push(id);
|
|
2453
|
+
};
|
|
2454
|
+
for (const node of ctx.mesh.nodes) pushCandidate(node?.daemonId);
|
|
2455
|
+
pushCandidate(ctx.mesh?.meshHost?.hostDaemonId);
|
|
2456
|
+
if (candidates.length === 0) return { node: null, ownerUnreachable: false };
|
|
2457
|
+
let ownerUnreachable = false;
|
|
2458
|
+
for (const daemonId of candidates) {
|
|
2459
|
+
let result;
|
|
2460
|
+
try {
|
|
2461
|
+
result = await transport.meshCommand(daemonId, "get_mesh", { meshId: ctx.mesh.id });
|
|
2462
|
+
} catch {
|
|
2463
|
+
ownerUnreachable = true;
|
|
2464
|
+
continue;
|
|
2465
|
+
}
|
|
2466
|
+
const nodes = result?.mesh?.nodes ?? result?.result?.mesh?.nodes;
|
|
2467
|
+
if (!result?.success || !Array.isArray(nodes)) {
|
|
2468
|
+
if (result && result.success === false) ownerUnreachable = true;
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
const found = nodes.find((n) => n?.id && (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2472
|
+
if (!found) continue;
|
|
2473
|
+
const existingIndex = ctx.mesh.nodes.findIndex((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2474
|
+
if (existingIndex >= 0) ctx.mesh.nodes[existingIndex] = found;
|
|
2475
|
+
else ctx.mesh.nodes.push(found);
|
|
2476
|
+
return { node: found, ownerUnreachable: false };
|
|
2477
|
+
}
|
|
2478
|
+
return { node: null, ownerUnreachable };
|
|
2479
|
+
}
|
|
2405
2480
|
async function findNodeWithRefresh(ctx, nodeId) {
|
|
2406
2481
|
const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2407
2482
|
if (hit && !hit.isLocalWorktree) return hit;
|
|
2408
|
-
await refreshMeshFromDaemon(ctx);
|
|
2483
|
+
const { settledNodeIds } = await refreshMeshFromDaemon(ctx);
|
|
2409
2484
|
const refreshed = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2410
|
-
if (
|
|
2411
|
-
|
|
2485
|
+
if (refreshed) return refreshed;
|
|
2486
|
+
if (settledNodeIds.has(nodeId)) {
|
|
2487
|
+
throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
2488
|
+
}
|
|
2489
|
+
const owned = await resolveNodeFromOwningDaemons(ctx, nodeId);
|
|
2490
|
+
if (owned.node) return owned.node;
|
|
2491
|
+
if (owned.ownerUnreachable) {
|
|
2492
|
+
const err = new Error(
|
|
2493
|
+
`Node '${nodeId}' could not be resolved: the daemon that owns it is unreachable. This is NOT proof of non-membership \u2014 retry once the owning daemon is online.`
|
|
2494
|
+
);
|
|
2495
|
+
err.code = "mesh_node_owner_unreachable";
|
|
2496
|
+
throw err;
|
|
2497
|
+
}
|
|
2498
|
+
throw new Error(`Node '${nodeId}' is not a member of mesh '${ctx.mesh.name}'`);
|
|
2412
2499
|
}
|
|
2413
2500
|
async function findOptionalNodeWithRefresh(ctx, nodeId) {
|
|
2414
2501
|
const hit = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2415
2502
|
if (hit && !hit.isLocalWorktree) return hit;
|
|
2416
|
-
await refreshMeshFromDaemon(ctx);
|
|
2417
|
-
|
|
2503
|
+
const { settledNodeIds } = await refreshMeshFromDaemon(ctx);
|
|
2504
|
+
const refreshed = ctx.mesh.nodes.find((n) => (0, import_daemon_core4.meshNodeIdMatches)(n, nodeId));
|
|
2505
|
+
if (refreshed) return refreshed;
|
|
2506
|
+
if (settledNodeIds.has(nodeId)) return null;
|
|
2507
|
+
const owned = await resolveNodeFromOwningDaemons(ctx, nodeId);
|
|
2508
|
+
return owned.node;
|
|
2418
2509
|
}
|
|
2419
2510
|
function hasRecentDuplicateDispatch(ctx, args) {
|
|
2420
2511
|
const now = Date.now();
|
|
@@ -2832,8 +2923,8 @@ function assignFullGitSnapshot(entry, status) {
|
|
|
2832
2923
|
if (!status || typeof status !== "object" || Array.isArray(status)) return;
|
|
2833
2924
|
entry.git = status;
|
|
2834
2925
|
}
|
|
2835
|
-
var COMPACT_DETAILED_NODES_BYTE_BUDGET =
|
|
2836
|
-
var COMPACT_NODES_TOTAL_BYTE_BUDGET =
|
|
2926
|
+
var COMPACT_DETAILED_NODES_BYTE_BUDGET = 32e3;
|
|
2927
|
+
var COMPACT_NODES_TOTAL_BYTE_BUDGET = 4e4;
|
|
2837
2928
|
var COMPACT_MISSIONS_BYTE_BUDGET = 6e3;
|
|
2838
2929
|
function extractLaunchPayload(value) {
|
|
2839
2930
|
return findNestedPayload(value, (payload) => Boolean(payload?.sessionId || payload?.id || payload?.runtimeSessionId));
|
|
@@ -3348,12 +3439,35 @@ async function collectLiveStatusProbe(ctx, node) {
|
|
|
3348
3439
|
const statusResult = await commandForNode(ctx, node, "get_status_metadata", {}, { statusProbe: true });
|
|
3349
3440
|
return {
|
|
3350
3441
|
sessions: extractStatusMetadataSessions(statusResult),
|
|
3351
|
-
daemonBuild: extractDaemonBuildInfo(statusResult)
|
|
3442
|
+
daemonBuild: extractDaemonBuildInfo(statusResult),
|
|
3443
|
+
upgradeFailure: extractUpgradeFailureSummary(statusResult)
|
|
3352
3444
|
};
|
|
3353
3445
|
} catch {
|
|
3354
3446
|
return { sessions: [] };
|
|
3355
3447
|
}
|
|
3356
3448
|
}
|
|
3449
|
+
var UPGRADE_FAILURE_SUMMARY_MAX_CHARS = 200;
|
|
3450
|
+
function extractUpgradeFailureSummary(value) {
|
|
3451
|
+
const payload = unwrapCommandPayload(value);
|
|
3452
|
+
const raw = payload?.upgradeFailure && typeof payload.upgradeFailure === "object" ? payload.upgradeFailure : value?.upgradeFailure && typeof value.upgradeFailure === "object" ? value.upgradeFailure : void 0;
|
|
3453
|
+
if (!raw) return void 0;
|
|
3454
|
+
const notice = readString(raw.notice) || "";
|
|
3455
|
+
const noticePath = readString(raw.noticePath) || "";
|
|
3456
|
+
if (!notice && !noticePath) return void 0;
|
|
3457
|
+
const bodyLine = notice.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^\[[^\]\n]+\]$/.test(line)) || "";
|
|
3458
|
+
const summary = bodyLine.length > UPGRADE_FAILURE_SUMMARY_MAX_CHARS ? `${bodyLine.slice(0, UPGRADE_FAILURE_SUMMARY_MAX_CHARS)}\u2026` : bodyLine;
|
|
3459
|
+
const recordedAt = readString(raw.recordedAt);
|
|
3460
|
+
const ageLabel = readString(raw.ageLabel);
|
|
3461
|
+
const targetVersion = readString(raw.targetVersion);
|
|
3462
|
+
return {
|
|
3463
|
+
summary,
|
|
3464
|
+
...recordedAt ? { recordedAt } : {},
|
|
3465
|
+
...ageLabel ? { ageLabel } : {},
|
|
3466
|
+
...targetVersion ? { targetVersion } : {},
|
|
3467
|
+
noticePath,
|
|
3468
|
+
logPath: readString(raw.logPath) || ""
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3357
3471
|
function extractDaemonBuildInfo(value) {
|
|
3358
3472
|
const payload = unwrapCommandPayload(value);
|
|
3359
3473
|
const build = payload?.daemonBuild && typeof payload.daemonBuild === "object" ? payload.daemonBuild : value?.daemonBuild && typeof value.daemonBuild === "object" ? value.daemonBuild : void 0;
|
|
@@ -3787,6 +3901,10 @@ async function meshStatus(ctx, args = {}) {
|
|
|
3787
3901
|
machine: buildNodeMachineIdentity(ctx, node),
|
|
3788
3902
|
daemonId: readNodeDaemonId(node),
|
|
3789
3903
|
machineId: readNodeMachineId(node),
|
|
3904
|
+
// Needed by the compact fold to distinguish a machine (repo-root) node
|
|
3905
|
+
// from a worktree node: the per-daemon representative pin keeps machine
|
|
3906
|
+
// nodes out of the fold so a deploy roster can never lose a machine.
|
|
3907
|
+
isLocalWorktree: node.isLocalWorktree === true,
|
|
3790
3908
|
...getNodeLaunchReadiness(node),
|
|
3791
3909
|
...buildNodeCapabilityExposure(node)
|
|
3792
3910
|
};
|
|
@@ -3896,6 +4014,7 @@ async function meshStatus(ctx, args = {}) {
|
|
|
3896
4014
|
const statusProbe = await collectLiveStatusProbe(ctx, node);
|
|
3897
4015
|
const liveSessions = statusProbe.sessions;
|
|
3898
4016
|
if (statusProbe.daemonBuild) entry.daemonBuild = statusProbe.daemonBuild;
|
|
4017
|
+
if (statusProbe.upgradeFailure) entry.upgradeFailure = statusProbe.upgradeFailure;
|
|
3899
4018
|
if (liveSessions.length > 0) {
|
|
3900
4019
|
entry.sessions = liveSessions.map((s) => {
|
|
3901
4020
|
const coordinatorMeshId = typeof s.coordinator?.meshId === "string" ? s.coordinator.meshId : void 0;
|
|
@@ -3993,6 +4112,21 @@ async function meshStatus(ctx, args = {}) {
|
|
|
3993
4112
|
daemonBuilds[daemonId] = entry.daemonBuild;
|
|
3994
4113
|
}
|
|
3995
4114
|
}
|
|
4115
|
+
const daemonMachines = {};
|
|
4116
|
+
const daemonQuotas = {};
|
|
4117
|
+
for (const entry of results) {
|
|
4118
|
+
const daemonId = typeof entry?.daemonId === "string" && entry.daemonId ? entry.daemonId : "";
|
|
4119
|
+
if (!daemonId) continue;
|
|
4120
|
+
if (entry?.machine && !(daemonId in daemonMachines)) daemonMachines[daemonId] = entry.machine;
|
|
4121
|
+
if (entry?.quota && !(daemonId in daemonQuotas)) daemonQuotas[daemonId] = entry.quota;
|
|
4122
|
+
}
|
|
4123
|
+
const daemonUpgradeFailures = {};
|
|
4124
|
+
for (const entry of results) {
|
|
4125
|
+
const daemonId = typeof entry?.daemonId === "string" && entry.daemonId ? entry.daemonId : "";
|
|
4126
|
+
if (daemonId && entry?.upgradeFailure && !(daemonId in daemonUpgradeFailures)) {
|
|
4127
|
+
daemonUpgradeFailures[daemonId] = entry.upgradeFailure;
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
3996
4130
|
const staleDaemonBuilds = [];
|
|
3997
4131
|
const seenStale = /* @__PURE__ */ new Set();
|
|
3998
4132
|
for (const entry of results) {
|
|
@@ -4055,20 +4189,36 @@ async function meshStatus(ctx, args = {}) {
|
|
|
4055
4189
|
if (!includeSessions) delete next.sessions;
|
|
4056
4190
|
}
|
|
4057
4191
|
if (next.daemonBuild !== void 0) delete next.daemonBuild;
|
|
4192
|
+
if (next.machine && typeof next.machine === "object") {
|
|
4193
|
+
const m = next.machine;
|
|
4194
|
+
next.machine = {
|
|
4195
|
+
daemonId: m.daemonId,
|
|
4196
|
+
displayName: m.displayName,
|
|
4197
|
+
sameMachine: m.sameMachine,
|
|
4198
|
+
seeDaemonMachines: true
|
|
4199
|
+
};
|
|
4200
|
+
}
|
|
4201
|
+
if (next.upgradeFailure !== void 0) delete next.upgradeFailure;
|
|
4058
4202
|
return next;
|
|
4059
4203
|
});
|
|
4204
|
+
const pinnedIds = pinnedRepresentativeNodeIds(compacted);
|
|
4060
4205
|
const noteworthy = compacted.filter((n) => n && typeof n === "object" && isNoteworthyCompactNode(n));
|
|
4061
|
-
const
|
|
4206
|
+
const bySeverityDesc = (a, b) => compactNodeSeverity(b) - compactNodeSeverity(a);
|
|
4207
|
+
const isPinned = (n) => pinnedIds.has(String(n?.nodeId));
|
|
4208
|
+
const ranked = [
|
|
4209
|
+
...compacted.filter((n) => n && typeof n === "object" && isPinned(n)).sort(bySeverityDesc),
|
|
4210
|
+
...noteworthy.filter((n) => !isPinned(n)).sort(bySeverityDesc)
|
|
4211
|
+
];
|
|
4062
4212
|
const detailedIds = /* @__PURE__ */ new Set();
|
|
4063
4213
|
let detailSpent = 0;
|
|
4064
4214
|
for (const n of ranked) {
|
|
4065
4215
|
const cost = JSON.stringify(n).length + 1;
|
|
4066
|
-
if (detailedIds.size === 0 || detailSpent + cost <= COMPACT_DETAILED_NODES_BYTE_BUDGET) {
|
|
4216
|
+
if (detailedIds.size === 0 || isPinned(n) || detailSpent + cost <= COMPACT_DETAILED_NODES_BYTE_BUDGET) {
|
|
4067
4217
|
detailedIds.add(String(n.nodeId));
|
|
4068
4218
|
detailSpent += cost;
|
|
4069
4219
|
}
|
|
4070
4220
|
}
|
|
4071
|
-
const stubOrder = [...compacted].filter((n) => n && typeof n === "object").sort(
|
|
4221
|
+
const stubOrder = [...compacted].filter((n) => n && typeof n === "object").sort(bySeverityDesc);
|
|
4072
4222
|
const keptIds = new Set(detailedIds);
|
|
4073
4223
|
let totalSpent = detailSpent;
|
|
4074
4224
|
for (const n of stubOrder) {
|
|
@@ -4141,6 +4291,14 @@ async function meshStatus(ctx, args = {}) {
|
|
|
4141
4291
|
...compact && foldedNodesSummary ? { foldedNodes: foldedNodesSummary } : {},
|
|
4142
4292
|
...compact && Object.keys(daemonSessions).length > 0 ? { daemonSessions } : {},
|
|
4143
4293
|
...Object.keys(daemonBuilds).length > 0 ? { daemonBuilds } : {},
|
|
4294
|
+
// Per-daemon machine identity / provider quota, recorded once per daemonId
|
|
4295
|
+
// instead of repeated on every node sharing that daemon. Both modes.
|
|
4296
|
+
...Object.keys(daemonMachines).length > 0 ? { daemonMachines } : {},
|
|
4297
|
+
...Object.keys(daemonQuotas).length > 0 ? { daemonQuotas } : {},
|
|
4298
|
+
...Object.keys(daemonUpgradeFailures).length > 0 ? {
|
|
4299
|
+
daemonUpgradeFailures,
|
|
4300
|
+
daemonUpgradeFailureWarning: `One or more daemons have a failed-upgrade notice on record: that daemon's LAST upgrade failed and rolled back, so it is still on the PREVIOUS version. An upgrade/restart response only reports "scheduled", never success \u2014 do not read a prior success as proof the version changed. The notice persists until a later upgrade succeeds, so check targetVersion/recordedAt: a target other than the running version is a stale earlier attempt. Full body at noticePath, trace at logPath.`
|
|
4301
|
+
} : {},
|
|
4144
4302
|
...staleDaemonBuilds.length > 0 ? { staleDaemonBuilds } : {},
|
|
4145
4303
|
...daemonAffectingStaleBuilds.length > 0 ? {
|
|
4146
4304
|
staleDaemonBuildWarning: "One or more live daemons were built from a commit behind the workspace HEAD with daemon-runtime package changes. Merged refinery/mesh-tool fixes are NOT live on those daemons until they are rebuilt/redeployed and restarted \u2014 a local daemon-core dist rebuild does not update a cloud daemon. Do not assume a just-merged fix is active."
|
|
@@ -4271,7 +4429,7 @@ async function meshStatus(ctx, args = {}) {
|
|
|
4271
4429
|
}
|
|
4272
4430
|
} catch {
|
|
4273
4431
|
}
|
|
4274
|
-
return JSON.stringify(response
|
|
4432
|
+
return JSON.stringify(response);
|
|
4275
4433
|
}
|
|
4276
4434
|
async function meshListNodes(ctx) {
|
|
4277
4435
|
await refreshMeshFromDaemon(ctx);
|