@adhdev/daemon-core 0.9.82-rc.23 → 0.9.82-rc.25
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/commands/router.d.ts +2 -0
- package/dist/index.js +345 -166
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +345 -166
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +14 -5
- package/dist/mesh/mesh-work-queue.d.ts +3 -1
- package/package.json +1 -1
- package/src/commands/router.ts +48 -22
- package/src/mesh/mesh-events.ts +157 -30
- package/src/mesh/mesh-work-queue.ts +135 -119
package/dist/index.mjs
CHANGED
|
@@ -1351,13 +1351,43 @@ __export(mesh_work_queue_exports, {
|
|
|
1351
1351
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
1352
1352
|
updateTaskStatus: () => updateTaskStatus
|
|
1353
1353
|
});
|
|
1354
|
-
import { existsSync as existsSync6, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
|
|
1354
|
+
import { existsSync as existsSync6, writeFileSync as writeFileSync3, readFileSync as readFileSync4, openSync, closeSync, unlinkSync } from "fs";
|
|
1355
1355
|
import { join as join6 } from "path";
|
|
1356
1356
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1357
1357
|
function getQueuePath(meshId) {
|
|
1358
1358
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1359
1359
|
return join6(getLedgerDir(), `${safe}.queue.json`);
|
|
1360
1360
|
}
|
|
1361
|
+
function getLockPath(meshId) {
|
|
1362
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1363
|
+
return join6(getLedgerDir(), `${safe}.queue.lock`);
|
|
1364
|
+
}
|
|
1365
|
+
function withQueueLock(meshId, fn) {
|
|
1366
|
+
const lockPath = getLockPath(meshId);
|
|
1367
|
+
let fd = -1;
|
|
1368
|
+
for (let i = 0; i < 10; i++) {
|
|
1369
|
+
try {
|
|
1370
|
+
fd = openSync(lockPath, "wx");
|
|
1371
|
+
break;
|
|
1372
|
+
} catch {
|
|
1373
|
+
const deadline = Date.now() + 30;
|
|
1374
|
+
while (Date.now() < deadline) {
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
try {
|
|
1379
|
+
return fn();
|
|
1380
|
+
} finally {
|
|
1381
|
+
if (fd !== -1) try {
|
|
1382
|
+
closeSync(fd);
|
|
1383
|
+
} catch {
|
|
1384
|
+
}
|
|
1385
|
+
try {
|
|
1386
|
+
unlinkSync(lockPath);
|
|
1387
|
+
} catch {
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1361
1391
|
function readQueue(meshId) {
|
|
1362
1392
|
const path28 = getQueuePath(meshId);
|
|
1363
1393
|
if (!existsSync6(path28)) return [];
|
|
@@ -1373,20 +1403,22 @@ function writeQueue(meshId, queue) {
|
|
|
1373
1403
|
writeFileSync3(path28, JSON.stringify(queue, null, 2), "utf-8");
|
|
1374
1404
|
}
|
|
1375
1405
|
function enqueueTask(meshId, message, opts) {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1406
|
+
return withQueueLock(meshId, () => {
|
|
1407
|
+
const queue = readQueue(meshId);
|
|
1408
|
+
const entry = {
|
|
1409
|
+
id: randomUUID5(),
|
|
1410
|
+
meshId,
|
|
1411
|
+
message,
|
|
1412
|
+
status: "pending",
|
|
1413
|
+
targetNodeId: opts?.targetNodeId,
|
|
1414
|
+
targetSessionId: opts?.targetSessionId,
|
|
1415
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1416
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1417
|
+
};
|
|
1418
|
+
queue.push(entry);
|
|
1419
|
+
writeQueue(meshId, queue);
|
|
1420
|
+
return entry;
|
|
1421
|
+
});
|
|
1390
1422
|
}
|
|
1391
1423
|
function getQueue(meshId, opts) {
|
|
1392
1424
|
let queue = readQueue(meshId);
|
|
@@ -1397,100 +1429,111 @@ function getQueue(meshId, opts) {
|
|
|
1397
1429
|
return queue;
|
|
1398
1430
|
}
|
|
1399
1431
|
function claimNextTask(meshId, nodeId, sessionId) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1432
|
+
return withQueueLock(meshId, () => {
|
|
1433
|
+
const queue = readQueue(meshId);
|
|
1434
|
+
const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
|
|
1435
|
+
if (hasActiveAssignment) return null;
|
|
1436
|
+
let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
|
|
1437
|
+
if (targetIdx === -1) {
|
|
1438
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
|
|
1439
|
+
}
|
|
1440
|
+
if (targetIdx === -1) {
|
|
1441
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
|
|
1442
|
+
}
|
|
1443
|
+
if (targetIdx === -1) return null;
|
|
1444
|
+
const entry = queue[targetIdx];
|
|
1445
|
+
entry.status = "assigned";
|
|
1446
|
+
entry.assignedNodeId = nodeId;
|
|
1447
|
+
entry.assignedSessionId = sessionId;
|
|
1448
|
+
entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1449
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1450
|
+
writeQueue(meshId, queue);
|
|
1451
|
+
return entry;
|
|
1452
|
+
});
|
|
1419
1453
|
}
|
|
1420
1454
|
function updateTaskStatus(meshId, taskId, status) {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1455
|
+
return withQueueLock(meshId, () => {
|
|
1456
|
+
const queue = readQueue(meshId);
|
|
1457
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1458
|
+
if (idx === -1) return null;
|
|
1459
|
+
queue[idx].status = status;
|
|
1460
|
+
queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1461
|
+
writeQueue(meshId, queue);
|
|
1462
|
+
return queue[idx];
|
|
1463
|
+
});
|
|
1428
1464
|
}
|
|
1429
1465
|
function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
...autoLaunch,
|
|
1436
|
-
updatedAt
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
return queue[idx];
|
|
1466
|
+
return withQueueLock(meshId, () => {
|
|
1467
|
+
const queue = readQueue(meshId);
|
|
1468
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1469
|
+
if (idx === -1) return null;
|
|
1470
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1471
|
+
queue[idx].autoLaunch = { ...autoLaunch, updatedAt: now };
|
|
1472
|
+
queue[idx].updatedAt = now;
|
|
1473
|
+
writeQueue(meshId, queue);
|
|
1474
|
+
return queue[idx];
|
|
1475
|
+
});
|
|
1441
1476
|
}
|
|
1442
1477
|
function cancelTask(meshId, taskId, opts) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1478
|
+
return withQueueLock(meshId, () => {
|
|
1479
|
+
const queue = readQueue(meshId);
|
|
1480
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1481
|
+
if (idx === -1) return null;
|
|
1482
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1483
|
+
queue[idx].status = "cancelled";
|
|
1484
|
+
queue[idx].updatedAt = now;
|
|
1485
|
+
queue[idx].cancelledAt = now;
|
|
1486
|
+
if (opts?.reason) queue[idx].cancelReason = opts.reason;
|
|
1487
|
+
writeQueue(meshId, queue);
|
|
1488
|
+
return queue[idx];
|
|
1489
|
+
});
|
|
1453
1490
|
}
|
|
1454
1491
|
function requeueTask(meshId, taskId, opts) {
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1492
|
+
return withQueueLock(meshId, () => {
|
|
1493
|
+
const queue = readQueue(meshId);
|
|
1494
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1495
|
+
if (idx === -1) return null;
|
|
1496
|
+
const entry = queue[idx];
|
|
1497
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1498
|
+
entry.status = "pending";
|
|
1499
|
+
delete entry.assignedNodeId;
|
|
1500
|
+
delete entry.assignedSessionId;
|
|
1501
|
+
delete entry.cancelledAt;
|
|
1502
|
+
delete entry.cancelReason;
|
|
1503
|
+
if (opts?.clearTargetNode) delete entry.targetNodeId;
|
|
1504
|
+
if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
|
|
1505
|
+
if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
|
|
1506
|
+
if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
|
|
1507
|
+
entry.updatedAt = now;
|
|
1508
|
+
entry.requeuedAt = now;
|
|
1509
|
+
entry.requeueCount = (entry.requeueCount || 0) + 1;
|
|
1510
|
+
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
1511
|
+
writeQueue(meshId, queue);
|
|
1512
|
+
return entry;
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
1516
|
+
return withQueueLock(meshId, () => {
|
|
1517
|
+
const queue = readQueue(meshId);
|
|
1518
|
+
const occurredAtTime = opts?.occurredAt ? new Date(opts.occurredAt).getTime() : Number.NaN;
|
|
1519
|
+
const hasOccurredAt = Number.isFinite(occurredAtTime);
|
|
1520
|
+
let bestIdx = -1;
|
|
1521
|
+
let bestTime = 0;
|
|
1522
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
1523
|
+
if (queue[i].assignedSessionId !== sessionId || queue[i].status !== "assigned") continue;
|
|
1482
1524
|
const time = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
|
|
1525
|
+
if (hasOccurredAt && Number.isFinite(time) && time > occurredAtTime) continue;
|
|
1483
1526
|
if (time > bestTime) {
|
|
1484
1527
|
bestTime = time;
|
|
1485
1528
|
bestIdx = i;
|
|
1486
1529
|
}
|
|
1487
1530
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1531
|
+
if (bestIdx === -1) return null;
|
|
1532
|
+
queue[bestIdx].status = status;
|
|
1533
|
+
queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1534
|
+
writeQueue(meshId, queue);
|
|
1535
|
+
return queue[bestIdx];
|
|
1536
|
+
});
|
|
1494
1537
|
}
|
|
1495
1538
|
function getMeshQueueStats(meshId) {
|
|
1496
1539
|
const queue = readQueue(meshId);
|
|
@@ -1895,21 +1938,72 @@ __export(mesh_events_exports, {
|
|
|
1895
1938
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
1896
1939
|
tryAssignQueueTask: () => tryAssignQueueTask
|
|
1897
1940
|
});
|
|
1941
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync9, readFileSync as readFileSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
1942
|
+
import { join as join9 } from "path";
|
|
1943
|
+
function sweepExpiredRemoteIdleSessions() {
|
|
1944
|
+
const now = Date.now();
|
|
1945
|
+
for (const [key, session] of remoteIdleSessions) {
|
|
1946
|
+
if (session.expiresAt <= now) remoteIdleSessions.delete(key);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
function getPendingEventsPath(meshId) {
|
|
1950
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1951
|
+
return join9(getLedgerDir(), `${safe}.pending-events.jsonl`);
|
|
1952
|
+
}
|
|
1898
1953
|
function queuePendingMeshCoordinatorEvent(event) {
|
|
1899
|
-
|
|
1954
|
+
try {
|
|
1955
|
+
appendFileSync3(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
|
|
1956
|
+
return true;
|
|
1957
|
+
} catch (e) {
|
|
1958
|
+
LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
1900
1959
|
return false;
|
|
1901
1960
|
}
|
|
1902
|
-
pendingMeshCoordinatorEvents.push(event);
|
|
1903
|
-
return true;
|
|
1904
1961
|
}
|
|
1905
|
-
function drainPendingMeshCoordinatorEvents() {
|
|
1906
|
-
|
|
1962
|
+
function drainPendingMeshCoordinatorEvents(meshId) {
|
|
1963
|
+
if (!meshId) return [];
|
|
1964
|
+
const path28 = getPendingEventsPath(meshId);
|
|
1965
|
+
if (!existsSync9(path28)) return [];
|
|
1966
|
+
try {
|
|
1967
|
+
const raw = readFileSync5(path28, "utf-8");
|
|
1968
|
+
try {
|
|
1969
|
+
unlinkSync3(path28);
|
|
1970
|
+
} catch {
|
|
1971
|
+
}
|
|
1972
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
1973
|
+
try {
|
|
1974
|
+
return [JSON.parse(line)];
|
|
1975
|
+
} catch {
|
|
1976
|
+
return [];
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
} catch {
|
|
1980
|
+
return [];
|
|
1981
|
+
}
|
|
1907
1982
|
}
|
|
1908
|
-
function getPendingMeshCoordinatorEvents() {
|
|
1909
|
-
|
|
1983
|
+
function getPendingMeshCoordinatorEvents(meshId) {
|
|
1984
|
+
if (!meshId) return [];
|
|
1985
|
+
const path28 = getPendingEventsPath(meshId);
|
|
1986
|
+
if (!existsSync9(path28)) return [];
|
|
1987
|
+
try {
|
|
1988
|
+
const raw = readFileSync5(path28, "utf-8");
|
|
1989
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
1990
|
+
try {
|
|
1991
|
+
return [JSON.parse(line)];
|
|
1992
|
+
} catch {
|
|
1993
|
+
return [];
|
|
1994
|
+
}
|
|
1995
|
+
});
|
|
1996
|
+
} catch {
|
|
1997
|
+
return [];
|
|
1998
|
+
}
|
|
1910
1999
|
}
|
|
1911
|
-
function clearPendingMeshCoordinatorEvents() {
|
|
1912
|
-
|
|
2000
|
+
function clearPendingMeshCoordinatorEvents(meshId) {
|
|
2001
|
+
if (!meshId) return;
|
|
2002
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2003
|
+
if (existsSync9(path28)) try {
|
|
2004
|
+
unlinkSync3(path28);
|
|
2005
|
+
} catch {
|
|
2006
|
+
}
|
|
1913
2007
|
}
|
|
1914
2008
|
function readNonEmptyString(value) {
|
|
1915
2009
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
@@ -1955,6 +2049,38 @@ function shouldSuppressIntentionalCleanupStop(args) {
|
|
|
1955
2049
|
if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
|
|
1956
2050
|
return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
|
|
1957
2051
|
}
|
|
2052
|
+
function readEventTimestamp(value) {
|
|
2053
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2054
|
+
if (typeof value === "string" && value.trim()) {
|
|
2055
|
+
const numeric = Number(value);
|
|
2056
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
2057
|
+
const parsed = Date.parse(value);
|
|
2058
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
2059
|
+
}
|
|
2060
|
+
return null;
|
|
2061
|
+
}
|
|
2062
|
+
function buildMeshCompletionFingerprint(args) {
|
|
2063
|
+
const timestampPart = Number.isFinite(args.timestamp) ? String(args.timestamp) : readNonEmptyString(args.finalSummary).slice(0, 200);
|
|
2064
|
+
return [
|
|
2065
|
+
args.meshId,
|
|
2066
|
+
args.event,
|
|
2067
|
+
args.sessionId,
|
|
2068
|
+
args.providerType || "",
|
|
2069
|
+
args.providerSessionId || "",
|
|
2070
|
+
timestampPart
|
|
2071
|
+
].join("::");
|
|
2072
|
+
}
|
|
2073
|
+
function isDuplicateMeshCompletionEvent(args) {
|
|
2074
|
+
const fingerprint = buildMeshCompletionFingerprint(args);
|
|
2075
|
+
if (!fingerprint) return false;
|
|
2076
|
+
const now = Date.now();
|
|
2077
|
+
for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
|
|
2078
|
+
if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
|
|
2079
|
+
}
|
|
2080
|
+
if (recentCompletionFingerprints.has(fingerprint)) return true;
|
|
2081
|
+
recentCompletionFingerprints.set(fingerprint, now);
|
|
2082
|
+
return false;
|
|
2083
|
+
}
|
|
1958
2084
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
1959
2085
|
const task = claimNextTask(meshId, nodeId, sessionId);
|
|
1960
2086
|
if (!task) {
|
|
@@ -1973,7 +2099,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
1973
2099
|
message: task.message
|
|
1974
2100
|
}).catch((e) => {
|
|
1975
2101
|
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
1976
|
-
updateTaskStatus(meshId, task.id, "
|
|
2102
|
+
updateTaskStatus(meshId, task.id, "pending");
|
|
2103
|
+
try {
|
|
2104
|
+
appendLedgerEntry(meshId, {
|
|
2105
|
+
kind: "dispatch_failed",
|
|
2106
|
+
nodeId,
|
|
2107
|
+
sessionId,
|
|
2108
|
+
payload: { taskId: task.id, error: e?.message, retryable: true }
|
|
2109
|
+
});
|
|
2110
|
+
} catch {
|
|
2111
|
+
}
|
|
1977
2112
|
});
|
|
1978
2113
|
return true;
|
|
1979
2114
|
}
|
|
@@ -2307,18 +2442,36 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2307
2442
|
LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
|
|
2308
2443
|
return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
|
|
2309
2444
|
}
|
|
2445
|
+
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
2446
|
+
if (args.event === "agent:generating_completed" && eventSessionId) {
|
|
2447
|
+
const duplicateCompletion = isDuplicateMeshCompletionEvent({
|
|
2448
|
+
meshId: args.meshId,
|
|
2449
|
+
event: args.event,
|
|
2450
|
+
sessionId: eventSessionId,
|
|
2451
|
+
providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
|
|
2452
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
|
|
2453
|
+
timestamp: eventTimestamp,
|
|
2454
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
|
|
2455
|
+
});
|
|
2456
|
+
if (duplicateCompletion) {
|
|
2457
|
+
LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
|
|
2458
|
+
return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2310
2461
|
let completedTaskForLedger = null;
|
|
2311
2462
|
if (args.event === "agent:generating_completed") {
|
|
2312
2463
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
2313
2464
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
2314
2465
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
2315
2466
|
if (sessionId) {
|
|
2316
|
-
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed"
|
|
2467
|
+
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed", {
|
|
2468
|
+
occurredAt: eventTimestamp !== null ? new Date(eventTimestamp).toISOString() : void 0
|
|
2469
|
+
});
|
|
2317
2470
|
completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
|
|
2318
2471
|
if (nodeId && providerType) {
|
|
2319
|
-
|
|
2472
|
+
setImmediate(() => {
|
|
2320
2473
|
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2321
|
-
}
|
|
2474
|
+
});
|
|
2322
2475
|
}
|
|
2323
2476
|
}
|
|
2324
2477
|
} else if (args.event === "agent:ready") {
|
|
@@ -2356,13 +2509,17 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2356
2509
|
}
|
|
2357
2510
|
}
|
|
2358
2511
|
if (sessionId && nodeId && providerType) {
|
|
2359
|
-
|
|
2360
|
-
|
|
2512
|
+
sweepExpiredRemoteIdleSessions();
|
|
2513
|
+
remoteIdleSessions.set(`${nodeId}:${sessionId}`, {
|
|
2514
|
+
nodeId,
|
|
2515
|
+
sessionId,
|
|
2516
|
+
providerType,
|
|
2517
|
+
expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
|
|
2518
|
+
});
|
|
2519
|
+
setImmediate(() => {
|
|
2361
2520
|
const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2362
|
-
if (assigned) {
|
|
2363
|
-
|
|
2364
|
-
}
|
|
2365
|
-
}, 500);
|
|
2521
|
+
if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
2522
|
+
});
|
|
2366
2523
|
}
|
|
2367
2524
|
} else if (args.event === "agent:generating_started") {
|
|
2368
2525
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
@@ -2523,6 +2680,7 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
2523
2680
|
providerType: readNonEmptyString(payload.providerType),
|
|
2524
2681
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
2525
2682
|
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
|
|
2683
|
+
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
2526
2684
|
intentional: payload.intentional === true,
|
|
2527
2685
|
intentionalStop: payload.intentionalStop === true,
|
|
2528
2686
|
operatorCleanup: payload.operatorCleanup === true,
|
|
@@ -2565,7 +2723,7 @@ function setupMeshEventForwarding(components) {
|
|
|
2565
2723
|
});
|
|
2566
2724
|
});
|
|
2567
2725
|
}
|
|
2568
|
-
var
|
|
2726
|
+
var REMOTE_IDLE_SESSION_TTL_MS, remoteIdleSessions, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, recentCompletionFingerprints, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
|
|
2569
2727
|
var init_mesh_events = __esm({
|
|
2570
2728
|
"src/mesh/mesh-events.ts"() {
|
|
2571
2729
|
"use strict";
|
|
@@ -2575,9 +2733,8 @@ var init_mesh_events = __esm({
|
|
|
2575
2733
|
init_logger();
|
|
2576
2734
|
init_mesh_ledger();
|
|
2577
2735
|
init_mesh_work_queue();
|
|
2736
|
+
REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
|
|
2578
2737
|
remoteIdleSessions = /* @__PURE__ */ new Map();
|
|
2579
|
-
MAX_PENDING_EVENTS = 50;
|
|
2580
|
-
pendingMeshCoordinatorEvents = [];
|
|
2581
2738
|
MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
|
|
2582
2739
|
"agent:generating_started",
|
|
2583
2740
|
"agent:generating_completed",
|
|
@@ -2593,6 +2750,8 @@ var init_mesh_events = __esm({
|
|
|
2593
2750
|
"monitor:long_generating": "task_stalled"
|
|
2594
2751
|
};
|
|
2595
2752
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
2753
|
+
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
2754
|
+
recentCompletionFingerprints = /* @__PURE__ */ new Map();
|
|
2596
2755
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
2597
2756
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
2598
2757
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -7635,8 +7794,8 @@ var P2pRelayFailureError = class extends Error {
|
|
|
7635
7794
|
|
|
7636
7795
|
// src/config/state-store.ts
|
|
7637
7796
|
init_config();
|
|
7638
|
-
import { existsSync as
|
|
7639
|
-
import { join as
|
|
7797
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
7798
|
+
import { join as join10 } from "path";
|
|
7640
7799
|
var DEFAULT_STATE = {
|
|
7641
7800
|
recentActivity: [],
|
|
7642
7801
|
savedProviderSessions: [],
|
|
@@ -7649,7 +7808,7 @@ function isPlainObject2(value) {
|
|
|
7649
7808
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7650
7809
|
}
|
|
7651
7810
|
function getStatePath() {
|
|
7652
|
-
return
|
|
7811
|
+
return join10(getConfigDir(), "state.json");
|
|
7653
7812
|
}
|
|
7654
7813
|
function normalizeState(raw) {
|
|
7655
7814
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -7685,11 +7844,11 @@ function normalizeState(raw) {
|
|
|
7685
7844
|
}
|
|
7686
7845
|
function loadState() {
|
|
7687
7846
|
const statePath = getStatePath();
|
|
7688
|
-
if (!
|
|
7847
|
+
if (!existsSync10(statePath)) {
|
|
7689
7848
|
return { ...DEFAULT_STATE };
|
|
7690
7849
|
}
|
|
7691
7850
|
try {
|
|
7692
|
-
const raw =
|
|
7851
|
+
const raw = readFileSync6(statePath, "utf-8");
|
|
7693
7852
|
return normalizeState(JSON.parse(raw));
|
|
7694
7853
|
} catch {
|
|
7695
7854
|
return { ...DEFAULT_STATE };
|
|
@@ -7706,7 +7865,7 @@ function resetState() {
|
|
|
7706
7865
|
|
|
7707
7866
|
// src/detection/ide-detector.ts
|
|
7708
7867
|
import { execSync } from "child_process";
|
|
7709
|
-
import { existsSync as
|
|
7868
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7710
7869
|
import { platform as platform2, homedir as homedir5 } from "os";
|
|
7711
7870
|
import * as path10 from "path";
|
|
7712
7871
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
@@ -7730,7 +7889,7 @@ function findCliCommand(command) {
|
|
|
7730
7889
|
if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
7731
7890
|
const candidate = trimmed.startsWith("~") ? path10.join(homedir5(), trimmed.slice(1)) : trimmed;
|
|
7732
7891
|
const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
|
|
7733
|
-
return
|
|
7892
|
+
return existsSync11(resolved) ? resolved : null;
|
|
7734
7893
|
}
|
|
7735
7894
|
try {
|
|
7736
7895
|
const result = execSync(
|
|
@@ -7761,9 +7920,9 @@ function checkPathExists(paths) {
|
|
|
7761
7920
|
if (normalized.includes("*")) {
|
|
7762
7921
|
const username = home.split(/[\\/]/).pop() || "";
|
|
7763
7922
|
const resolved = normalized.replace("*", username);
|
|
7764
|
-
if (
|
|
7923
|
+
if (existsSync11(resolved)) return resolved;
|
|
7765
7924
|
} else {
|
|
7766
|
-
if (
|
|
7925
|
+
if (existsSync11(normalized)) return normalized;
|
|
7767
7926
|
}
|
|
7768
7927
|
}
|
|
7769
7928
|
return null;
|
|
@@ -7777,7 +7936,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7777
7936
|
let resolvedCli = cliPath;
|
|
7778
7937
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
7779
7938
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
7780
|
-
if (
|
|
7939
|
+
if (existsSync11(bundledCli)) resolvedCli = bundledCli;
|
|
7781
7940
|
}
|
|
7782
7941
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
7783
7942
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -7790,7 +7949,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7790
7949
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
7791
7950
|
];
|
|
7792
7951
|
for (const c of candidates) {
|
|
7793
|
-
if (
|
|
7952
|
+
if (existsSync11(c)) {
|
|
7794
7953
|
resolvedCli = c;
|
|
7795
7954
|
break;
|
|
7796
7955
|
}
|
|
@@ -16946,7 +17105,7 @@ init_config();
|
|
|
16946
17105
|
import * as os13 from "os";
|
|
16947
17106
|
import * as path18 from "path";
|
|
16948
17107
|
import * as crypto4 from "crypto";
|
|
16949
|
-
import { existsSync as
|
|
17108
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
16950
17109
|
import { execFileSync } from "child_process";
|
|
16951
17110
|
import chalk from "chalk";
|
|
16952
17111
|
|
|
@@ -19420,7 +19579,7 @@ function commandExists(command) {
|
|
|
19420
19579
|
const trimmed = command.trim();
|
|
19421
19580
|
if (!trimmed) return false;
|
|
19422
19581
|
if (isExplicitCommand(trimmed)) {
|
|
19423
|
-
return
|
|
19582
|
+
return existsSync15(expandExecutable(trimmed));
|
|
19424
19583
|
}
|
|
19425
19584
|
try {
|
|
19426
19585
|
execFileSync(process.platform === "win32" ? "where" : "which", [trimmed], {
|
|
@@ -22689,10 +22848,10 @@ import * as yaml from "js-yaml";
|
|
|
22689
22848
|
// src/commands/mesh-coordinator.ts
|
|
22690
22849
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
22691
22850
|
import { createHash as createHash2 } from "crypto";
|
|
22692
|
-
import { existsSync as
|
|
22851
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7, realpathSync as realpathSync2 } from "fs";
|
|
22693
22852
|
import { createRequire as createRequire2 } from "module";
|
|
22694
22853
|
import * as os17 from "os";
|
|
22695
|
-
import { dirname as dirname4, isAbsolute as isAbsolute11, join as
|
|
22854
|
+
import { dirname as dirname4, isAbsolute as isAbsolute11, join as join21, resolve as resolve13 } from "path";
|
|
22696
22855
|
var DEFAULT_SERVER_NAME = "adhdev-mesh";
|
|
22697
22856
|
var DEFAULT_ADHDEV_MCP_COMMAND = "adhdev-mcp";
|
|
22698
22857
|
var HERMES_CLI_TYPE = "hermes-cli";
|
|
@@ -22715,7 +22874,7 @@ function resolveHermesMeshCoordinatorSetup(options) {
|
|
|
22715
22874
|
reason: "Could not resolve the ADHDev MCP server entrypoint and a Node runtime with WebSocket support for daemon IPC mode"
|
|
22716
22875
|
};
|
|
22717
22876
|
}
|
|
22718
|
-
const configPath =
|
|
22877
|
+
const configPath = join21(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
|
|
22719
22878
|
if (!configPath.trim()) {
|
|
22720
22879
|
return createHermesManualMeshCoordinatorSetup(options.meshId, options.workspace);
|
|
22721
22880
|
}
|
|
@@ -22836,14 +22995,14 @@ function resolveHermesCoordinatorHome(meshId, workspace) {
|
|
|
22836
22995
|
const key = `${meshId || "mesh"}
|
|
22837
22996
|
${resolve13(workspace || os17.tmpdir())}`;
|
|
22838
22997
|
const hash = createHash2("sha256").update(key).digest("hex").slice(0, 16);
|
|
22839
|
-
return
|
|
22998
|
+
return join21(os17.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
|
|
22840
22999
|
}
|
|
22841
23000
|
function resolveMcpConfigPath(configPath, workspace) {
|
|
22842
23001
|
const trimmed = configPath.trim();
|
|
22843
23002
|
if (trimmed === "~") return os17.homedir();
|
|
22844
|
-
if (trimmed.startsWith("~/")) return
|
|
23003
|
+
if (trimmed.startsWith("~/")) return join21(os17.homedir(), trimmed.slice(2));
|
|
22845
23004
|
if (isAbsolute11(trimmed)) return trimmed;
|
|
22846
|
-
return
|
|
23005
|
+
return join21(workspace, trimmed);
|
|
22847
23006
|
}
|
|
22848
23007
|
function resolveAdhdevMcpServerLaunch(options) {
|
|
22849
23008
|
const entryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath);
|
|
@@ -22899,15 +23058,15 @@ function addNodeCandidatesFromPath(pathValue, addCandidate) {
|
|
|
22899
23058
|
for (const entry of (pathValue || "").split(":")) {
|
|
22900
23059
|
const dir = entry.trim();
|
|
22901
23060
|
if (!dir) continue;
|
|
22902
|
-
addCandidate(
|
|
23061
|
+
addCandidate(join21(dir, "node"));
|
|
22903
23062
|
}
|
|
22904
23063
|
}
|
|
22905
23064
|
function addNodeCandidatesFromNvm(homeDir, addCandidate) {
|
|
22906
|
-
const versionsDir =
|
|
23065
|
+
const versionsDir = join21(homeDir, ".nvm", "versions", "node");
|
|
22907
23066
|
try {
|
|
22908
23067
|
const versionDirs = readdirSync7(versionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(compareNodeVersionNamesDescending);
|
|
22909
23068
|
for (const versionDir of versionDirs) {
|
|
22910
|
-
addCandidate(
|
|
23069
|
+
addCandidate(join21(versionsDir, versionDir, "bin", "node"));
|
|
22911
23070
|
}
|
|
22912
23071
|
} catch {
|
|
22913
23072
|
}
|
|
@@ -22958,7 +23117,7 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
|
|
|
22958
23117
|
if (normalized) return normalized;
|
|
22959
23118
|
}
|
|
22960
23119
|
try {
|
|
22961
|
-
const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] :
|
|
23120
|
+
const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] : join21(process.cwd(), "adhdev-daemon.js");
|
|
22962
23121
|
const req = createRequire2(requireBase);
|
|
22963
23122
|
const resolvedModule = req.resolve("@adhdev/mcp-server");
|
|
22964
23123
|
return normalizeExistingPath(resolvedModule) || resolvedModule;
|
|
@@ -22968,7 +23127,7 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
|
|
|
22968
23127
|
}
|
|
22969
23128
|
function normalizeExistingPath(filePath) {
|
|
22970
23129
|
try {
|
|
22971
|
-
if (!
|
|
23130
|
+
if (!existsSync18(filePath)) return null;
|
|
22972
23131
|
return realpathSync2.native(filePath);
|
|
22973
23132
|
} catch {
|
|
22974
23133
|
return null;
|
|
@@ -24920,7 +25079,8 @@ var DaemonCommandRouter = class {
|
|
|
24920
25079
|
return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
|
|
24921
25080
|
}
|
|
24922
25081
|
case "get_pending_mesh_events": {
|
|
24923
|
-
const
|
|
25082
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25083
|
+
const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
|
|
24924
25084
|
return { success: true, events };
|
|
24925
25085
|
}
|
|
24926
25086
|
case "launch_cli":
|
|
@@ -26141,7 +26301,7 @@ ${block}`);
|
|
|
26141
26301
|
workspace
|
|
26142
26302
|
};
|
|
26143
26303
|
}
|
|
26144
|
-
const { existsSync:
|
|
26304
|
+
const { existsSync: existsSync26, readFileSync: readFileSync18, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
|
|
26145
26305
|
const { dirname: dirname9 } = await import("path");
|
|
26146
26306
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
26147
26307
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -26184,14 +26344,14 @@ ${block}`);
|
|
|
26184
26344
|
if (hermesManualFallback) return returnManualFallback(message);
|
|
26185
26345
|
return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
|
|
26186
26346
|
}
|
|
26187
|
-
const hadExistingMcpConfig =
|
|
26347
|
+
const hadExistingMcpConfig = existsSync26(mcpConfigPath);
|
|
26188
26348
|
let existingMcpConfig = hermesBaseConfig?.config || {};
|
|
26189
26349
|
if (hermesBaseConfig) {
|
|
26190
26350
|
copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
|
|
26191
26351
|
}
|
|
26192
26352
|
if (hadExistingMcpConfig) {
|
|
26193
26353
|
try {
|
|
26194
|
-
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(
|
|
26354
|
+
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync18(mcpConfigPath, "utf-8"), configFormat);
|
|
26195
26355
|
const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
|
|
26196
26356
|
existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
|
|
26197
26357
|
copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
|
|
@@ -26379,29 +26539,48 @@ ${block}`);
|
|
|
26379
26539
|
}
|
|
26380
26540
|
if (workspace) {
|
|
26381
26541
|
if (!fs10.existsSync(workspace)) {
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26542
|
+
let remoteProbeApplied = false;
|
|
26543
|
+
if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand) {
|
|
26544
|
+
try {
|
|
26545
|
+
const remoteResult = await Promise.race([
|
|
26546
|
+
this.deps.dispatchMeshCommand(daemonId, "git_status", { workspace }),
|
|
26547
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 8e3))
|
|
26548
|
+
]);
|
|
26549
|
+
const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
|
|
26550
|
+
if (remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean") {
|
|
26551
|
+
status.git = remoteGit;
|
|
26552
|
+
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|
|
26553
|
+
remoteProbeApplied = true;
|
|
26554
|
+
}
|
|
26555
|
+
} catch {
|
|
26556
|
+
}
|
|
26391
26557
|
}
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
|
|
26558
|
+
if (!remoteProbeApplied) {
|
|
26559
|
+
if (applyCachedInlineMeshNodeStatus(status, node)) {
|
|
26560
|
+
status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
|
|
26561
|
+
nodeStatuses.push(status);
|
|
26562
|
+
continue;
|
|
26563
|
+
}
|
|
26564
|
+
if (meshRecord?.source === "inline_cache" && !isSelfNode) {
|
|
26565
|
+
status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
|
|
26566
|
+
nodeStatuses.push(status);
|
|
26567
|
+
continue;
|
|
26568
|
+
}
|
|
26401
26569
|
}
|
|
26402
|
-
}
|
|
26403
|
-
|
|
26404
|
-
|
|
26570
|
+
} else {
|
|
26571
|
+
try {
|
|
26572
|
+
const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
|
|
26573
|
+
status.git = gitStatus;
|
|
26574
|
+
if (gitStatus.isGitRepo) {
|
|
26575
|
+
status.health = deriveMeshNodeHealthFromGit(gitStatus);
|
|
26576
|
+
} else {
|
|
26577
|
+
status.health = "degraded";
|
|
26578
|
+
if (gitStatus.error && !status.error) status.error = gitStatus.error;
|
|
26579
|
+
}
|
|
26580
|
+
} catch {
|
|
26581
|
+
if (!applyCachedInlineMeshNodeStatus(status, node)) {
|
|
26582
|
+
status.health = "degraded";
|
|
26583
|
+
}
|
|
26405
26584
|
}
|
|
26406
26585
|
}
|
|
26407
26586
|
} else {
|