@adhdev/daemon-core 0.9.82-rc.43 → 0.9.82-rc.45
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 +1 -0
- package/dist/index.js +170 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +170 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/commands/router.ts +180 -18
package/package.json
CHANGED
package/src/commands/router.ts
CHANGED
|
@@ -114,6 +114,75 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
|
|
|
114
114
|
return undefined;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function summarizeRepoMeshDebugGit(git: unknown): Record<string, unknown> | null {
|
|
118
|
+
const record = readObjectRecord(git);
|
|
119
|
+
if (!Object.keys(record).length) return null;
|
|
120
|
+
const submodules = Array.isArray(record.submodules)
|
|
121
|
+
? record.submodules.map((entry: any) => ({
|
|
122
|
+
path: readStringValue(entry?.path) ?? null,
|
|
123
|
+
commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
|
|
124
|
+
dirty: readBooleanValue(entry?.dirty) ?? false,
|
|
125
|
+
outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false,
|
|
126
|
+
}))
|
|
127
|
+
: [];
|
|
128
|
+
return {
|
|
129
|
+
isGitRepo: readBooleanValue(record.isGitRepo),
|
|
130
|
+
workspace: readStringValue(record.workspace) ?? null,
|
|
131
|
+
repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
|
|
132
|
+
branch: readStringValue(record.branch) ?? null,
|
|
133
|
+
upstream: readStringValue(record.upstream) ?? null,
|
|
134
|
+
upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
|
|
135
|
+
headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
|
|
136
|
+
ahead: readNumberValue(record.ahead) ?? null,
|
|
137
|
+
behind: readNumberValue(record.behind) ?? null,
|
|
138
|
+
dirtyCounts: {
|
|
139
|
+
staged: readNumberValue(record.staged) ?? 0,
|
|
140
|
+
modified: readNumberValue(record.modified) ?? 0,
|
|
141
|
+
untracked: readNumberValue(record.untracked) ?? 0,
|
|
142
|
+
deleted: readNumberValue(record.deleted) ?? 0,
|
|
143
|
+
renamed: readNumberValue(record.renamed) ?? 0,
|
|
144
|
+
},
|
|
145
|
+
lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
|
|
146
|
+
submoduleCount: submodules.length,
|
|
147
|
+
submodules,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
|
|
152
|
+
const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
|
|
153
|
+
return {
|
|
154
|
+
success: status?.success,
|
|
155
|
+
meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
|
|
156
|
+
refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
|
|
157
|
+
sourceOfTruth: status?.sourceOfTruth ?? null,
|
|
158
|
+
nodeCount: nodes.length,
|
|
159
|
+
nodes: nodes.map((node: any) => ({
|
|
160
|
+
nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
|
|
161
|
+
daemonId: readStringValue(node?.daemonId, node?.daemon_id) ?? null,
|
|
162
|
+
workspace: readStringValue(node?.workspace, node?.git?.workspace) ?? null,
|
|
163
|
+
health: readStringValue(node?.health) ?? null,
|
|
164
|
+
machineStatus: readStringValue(node?.machineStatus, node?.machine_status) ?? null,
|
|
165
|
+
connection: node?.connection && typeof node.connection === 'object' ? {
|
|
166
|
+
state: readStringValue(node.connection.state) ?? null,
|
|
167
|
+
transport: readStringValue(node.connection.transport) ?? null,
|
|
168
|
+
source: readStringValue(node.connection.source) ?? null,
|
|
169
|
+
reported: readBooleanValue(node.connection.reported) ?? null,
|
|
170
|
+
} : null,
|
|
171
|
+
gitProbePending: node?.gitProbePending === true,
|
|
172
|
+
launchReady: node?.launchReady === true,
|
|
173
|
+
git: summarizeRepoMeshDebugGit(node?.git),
|
|
174
|
+
})),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function logRepoMeshStatusDebug(event: string, fields: Record<string, unknown>): void {
|
|
179
|
+
try {
|
|
180
|
+
LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
|
|
181
|
+
} catch {
|
|
182
|
+
LOG.info('MeshStatusDebug', `[RepoMeshStatusDebug] ${event}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
117
186
|
function joinRepoPath(root: string | undefined, relativePath: string | undefined): string | undefined {
|
|
118
187
|
const normalizedRoot = typeof root === 'string' ? root.trim().replace(/[\\/]+$/, '') : '';
|
|
119
188
|
const normalizedPath = typeof relativePath === 'string' ? relativePath.trim() : '';
|
|
@@ -194,16 +263,12 @@ function buildInlineMeshTransitGitStatus(node: any): Record<string, unknown> | u
|
|
|
194
263
|
const probeGitResult = readObjectRecord(probeGit.result);
|
|
195
264
|
const probeDirectStatus = readObjectRecord(probeGit.status);
|
|
196
265
|
const probeNestedStatus = readObjectRecord(probeGitResult.status);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
: Object.keys(probeNestedStatus).length
|
|
204
|
-
? probeNestedStatus
|
|
205
|
-
: {};
|
|
206
|
-
return normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
|
|
266
|
+
const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
|
|
267
|
+
for (const status of candidates) {
|
|
268
|
+
const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
|
|
269
|
+
if (normalized) return normalized;
|
|
270
|
+
}
|
|
271
|
+
return undefined;
|
|
207
272
|
}
|
|
208
273
|
|
|
209
274
|
function recordInlineMeshDirectGitTruth(
|
|
@@ -1376,10 +1441,73 @@ export class DaemonCommandRouter {
|
|
|
1376
1441
|
return JSON.parse(JSON.stringify(value)) as T;
|
|
1377
1442
|
}
|
|
1378
1443
|
|
|
1379
|
-
private
|
|
1444
|
+
private hydrateCachedAggregateMeshStatusFromInline(snapshot: any, mesh: any, options?: { requireDirectPeerTruth?: boolean }): any {
|
|
1445
|
+
if (!mesh || typeof mesh !== 'object' || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
|
|
1446
|
+
const inlineNodesById = new Map<string, any>();
|
|
1447
|
+
for (const node of mesh.nodes) {
|
|
1448
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
1449
|
+
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
1450
|
+
}
|
|
1451
|
+
if (!inlineNodesById.size) return snapshot;
|
|
1452
|
+
|
|
1453
|
+
let changed = false;
|
|
1454
|
+
const unavailableNodeIds = new Set<string>();
|
|
1455
|
+
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
1456
|
+
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
1457
|
+
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
1458
|
+
const nodeId = readStringValue(entry);
|
|
1459
|
+
if (nodeId) unavailableNodeIds.add(nodeId);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
const nodes = snapshot.nodes.map((statusNode: any) => {
|
|
1463
|
+
const nodeId = readStringValue(statusNode?.nodeId, statusNode?.id);
|
|
1464
|
+
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : undefined;
|
|
1465
|
+
if (!inlineNode) return statusNode;
|
|
1466
|
+
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
1467
|
+
if (!liveGit) return statusNode;
|
|
1468
|
+
const nextStatus = { ...statusNode };
|
|
1469
|
+
nextStatus.git = liveGit;
|
|
1470
|
+
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
1471
|
+
delete nextStatus.gitProbePending;
|
|
1472
|
+
if (readStringValue(nextStatus.error) === 'waiting for live peer git snapshot') delete nextStatus.error;
|
|
1473
|
+
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = 'online';
|
|
1474
|
+
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
1475
|
+
changed = true;
|
|
1476
|
+
return nextStatus;
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0)) return snapshot;
|
|
1480
|
+
const nextSourceOfTruth = {
|
|
1481
|
+
...sourceOfTruth,
|
|
1482
|
+
...(Object.keys(directPeerTruth).length ? {
|
|
1483
|
+
directPeerTruth: {
|
|
1484
|
+
...directPeerTruth,
|
|
1485
|
+
satisfied: options?.requireDirectPeerTruth === true ? unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
1486
|
+
unavailableNodeIds: [...unavailableNodeIds],
|
|
1487
|
+
},
|
|
1488
|
+
...(options?.requireDirectPeerTruth === true ? {
|
|
1489
|
+
coordinatorOwnsLiveTruth: unavailableNodeIds.size === 0,
|
|
1490
|
+
currentStatus: unavailableNodeIds.size === 0 ? 'live_git_and_session_probes' : 'direct_peer_truth_unavailable',
|
|
1491
|
+
} : {}),
|
|
1492
|
+
} : {}),
|
|
1493
|
+
};
|
|
1494
|
+
return {
|
|
1495
|
+
...snapshot,
|
|
1496
|
+
...(options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 ? {
|
|
1497
|
+
success: false,
|
|
1498
|
+
code: 'mesh_direct_peer_truth_unavailable',
|
|
1499
|
+
error: 'Selected coordinator could not confirm direct mesh truth for every remote node yet.',
|
|
1500
|
+
} : {}),
|
|
1501
|
+
sourceOfTruth: nextSourceOfTruth,
|
|
1502
|
+
nodes,
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
private getCachedAggregateMeshStatus(meshId: string, mesh?: any, options?: { requireDirectPeerTruth?: boolean }): any | null {
|
|
1380
1507
|
const cached = this.aggregateMeshStatusCache.get(meshId);
|
|
1381
1508
|
if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
|
|
1382
|
-
|
|
1509
|
+
let snapshot = this.cloneJsonValue(cached.snapshot);
|
|
1510
|
+
snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
|
|
1383
1511
|
const ageMs = Math.max(0, Date.now() - cached.builtAt);
|
|
1384
1512
|
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === 'object'
|
|
1385
1513
|
? snapshot.sourceOfTruth
|
|
@@ -1453,7 +1581,14 @@ export class DaemonCommandRouter {
|
|
|
1453
1581
|
const preferInline = options?.preferInline === true;
|
|
1454
1582
|
if (preferInline) {
|
|
1455
1583
|
const cached = this.getCachedInlineMesh(meshId);
|
|
1456
|
-
if (cached)
|
|
1584
|
+
if (cached) {
|
|
1585
|
+
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
1586
|
+
const merged = reconcileInlineMeshCache(cached, inlineMesh as any);
|
|
1587
|
+
this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
|
|
1588
|
+
return { mesh: merged, inline: true, source: 'inline_cache' };
|
|
1589
|
+
}
|
|
1590
|
+
return { mesh: cached, inline: true, source: 'inline_cache' };
|
|
1591
|
+
}
|
|
1457
1592
|
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
1458
1593
|
this.warmInlineMeshCache(meshId, inlineMesh);
|
|
1459
1594
|
return { mesh: inlineMesh, inline: true, source: 'inline_bootstrap' };
|
|
@@ -3725,8 +3860,16 @@ export class DaemonCommandRouter {
|
|
|
3725
3860
|
|
|
3726
3861
|
const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
|
|
3727
3862
|
if (!refreshRequested) {
|
|
3728
|
-
const cachedStatus = this.getCachedAggregateMeshStatus(meshId);
|
|
3729
|
-
if (cachedStatus)
|
|
3863
|
+
const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
|
|
3864
|
+
if (cachedStatus) {
|
|
3865
|
+
logRepoMeshStatusDebug('return_cached', {
|
|
3866
|
+
meshId,
|
|
3867
|
+
command: 'mesh_status',
|
|
3868
|
+
refreshRequested,
|
|
3869
|
+
summary: summarizeRepoMeshStatusDebug(cachedStatus),
|
|
3870
|
+
});
|
|
3871
|
+
return cachedStatus;
|
|
3872
|
+
}
|
|
3730
3873
|
}
|
|
3731
3874
|
const refreshReason = refreshRequested ? 'explicit_refresh' : 'cold_cache_miss';
|
|
3732
3875
|
|
|
@@ -3759,9 +3902,10 @@ export class DaemonCommandRouter {
|
|
|
3759
3902
|
peerConfirmedCount: 0,
|
|
3760
3903
|
unavailableNodeIds: [] as string[],
|
|
3761
3904
|
};
|
|
3762
|
-
const directTruthSatisfied =
|
|
3905
|
+
const directTruthSatisfied = !requireDirectPeerTruth
|
|
3906
|
+
|| (directTruth.directEvidenceCount > 0 && directTruth.unavailableNodeIds.length === 0);
|
|
3763
3907
|
if (requireDirectPeerTruth && !directTruthSatisfied) {
|
|
3764
|
-
|
|
3908
|
+
const failureResult = {
|
|
3765
3909
|
success: false,
|
|
3766
3910
|
code: 'mesh_direct_peer_truth_unavailable',
|
|
3767
3911
|
error: 'Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.',
|
|
@@ -3784,6 +3928,14 @@ export class DaemonCommandRouter {
|
|
|
3784
3928
|
},
|
|
3785
3929
|
},
|
|
3786
3930
|
};
|
|
3931
|
+
logRepoMeshStatusDebug('direct_truth_unavailable', {
|
|
3932
|
+
meshId,
|
|
3933
|
+
command: 'mesh_status',
|
|
3934
|
+
refreshRequested,
|
|
3935
|
+
meshSource: meshRecord.source,
|
|
3936
|
+
directTruth,
|
|
3937
|
+
});
|
|
3938
|
+
return failureResult;
|
|
3787
3939
|
}
|
|
3788
3940
|
const directTruthUnavailableNodeIds = new Set(directTruth.unavailableNodeIds);
|
|
3789
3941
|
const selectedCoordinatorNodeId = readStringValue(
|
|
@@ -4019,7 +4171,17 @@ export class DaemonCommandRouter {
|
|
|
4019
4171
|
queue: { tasks: queue, summary: queueSummary },
|
|
4020
4172
|
ledger: { entries: ledgerEntries, summary: ledgerSummary },
|
|
4021
4173
|
};
|
|
4022
|
-
|
|
4174
|
+
const rememberedStatus = this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
|
|
4175
|
+
logRepoMeshStatusDebug('return_live', {
|
|
4176
|
+
meshId,
|
|
4177
|
+
command: 'mesh_status',
|
|
4178
|
+
refreshRequested,
|
|
4179
|
+
refreshReason,
|
|
4180
|
+
meshSource: meshRecord.source,
|
|
4181
|
+
directTruth,
|
|
4182
|
+
summary: summarizeRepoMeshStatusDebug(rememberedStatus),
|
|
4183
|
+
});
|
|
4184
|
+
return rememberedStatus;
|
|
4023
4185
|
} catch (e: any) {
|
|
4024
4186
|
return { success: false, error: e.message };
|
|
4025
4187
|
}
|