@adhdev/daemon-core 0.9.82-rc.5 → 0.9.82-rc.51
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/boot/daemon-lifecycle.d.ts +2 -0
- package/dist/commands/router.d.ts +13 -0
- package/dist/config/mesh-config.d.ts +66 -1
- package/dist/git/git-commands.d.ts +1 -0
- package/dist/git/git-status.d.ts +5 -0
- package/dist/git/git-types.d.ts +10 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +2483 -434
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2463 -427
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +17 -5
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +11 -5
- package/dist/mesh/refine-config.d.ts +119 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/repo-mesh-types.d.ts +160 -0
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +4 -0
- package/src/commands/router.ts +1831 -296
- package/src/config/mesh-config.ts +244 -1
- package/src/git/git-commands.ts +3 -3
- package/src/git/git-status.ts +97 -6
- package/src/git/git-summary.ts +3 -0
- package/src/git/git-types.ts +11 -0
- package/src/index.ts +32 -2
- package/src/mesh/mesh-events.ts +168 -30
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-work-queue.ts +149 -122
- package/src/mesh/refine-config.ts +306 -0
- package/src/providers/chat-message-normalization.ts +3 -1
- package/src/repo-mesh-types.ts +174 -0
package/dist/index.mjs
CHANGED
|
@@ -657,23 +657,97 @@ var init_config = __esm({
|
|
|
657
657
|
}
|
|
658
658
|
});
|
|
659
659
|
|
|
660
|
+
// src/mesh/mesh-host-ownership.ts
|
|
661
|
+
function readObject(value) {
|
|
662
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
663
|
+
}
|
|
664
|
+
function readString(value) {
|
|
665
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
666
|
+
}
|
|
667
|
+
function normalizeMeshDaemonRole(value) {
|
|
668
|
+
return value === "host" || value === "member" ? value : void 0;
|
|
669
|
+
}
|
|
670
|
+
function resolveMeshHostStatus(mesh) {
|
|
671
|
+
const meshRecord = readObject(mesh);
|
|
672
|
+
const raw = readObject(meshRecord?.meshHost);
|
|
673
|
+
const role = normalizeMeshDaemonRole(raw?.role) ?? "host";
|
|
674
|
+
const pairing = readObject(raw?.pairing);
|
|
675
|
+
const normalized = {
|
|
676
|
+
role,
|
|
677
|
+
canOwnCoordinator: role === "host",
|
|
678
|
+
canOwnQueue: role === "host",
|
|
679
|
+
defaulted: !raw
|
|
680
|
+
};
|
|
681
|
+
const hostDaemonId = readString(raw?.hostDaemonId);
|
|
682
|
+
const hostNodeId = readString(raw?.hostNodeId);
|
|
683
|
+
const hostAddress = readString(raw?.hostAddress);
|
|
684
|
+
if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
|
|
685
|
+
if (hostNodeId) normalized.hostNodeId = hostNodeId;
|
|
686
|
+
if (hostAddress) normalized.hostAddress = hostAddress;
|
|
687
|
+
if (pairing) {
|
|
688
|
+
const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
|
|
689
|
+
normalized.pairing = {
|
|
690
|
+
status,
|
|
691
|
+
...readString(pairing.tokenId) ? { tokenId: readString(pairing.tokenId) } : {},
|
|
692
|
+
...readString(pairing.joinedAt) ? { joinedAt: readString(pairing.joinedAt) } : {},
|
|
693
|
+
...readString(pairing.lastPairedAt) ? { lastPairedAt: readString(pairing.lastPairedAt) } : {},
|
|
694
|
+
...readString(pairing.lastRejectedAt) ? { lastRejectedAt: readString(pairing.lastRejectedAt) } : {},
|
|
695
|
+
...readString(pairing.expiresAt) ? { expiresAt: readString(pairing.expiresAt) } : {}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
return normalized;
|
|
699
|
+
}
|
|
700
|
+
function isMeshHostOwner(mesh) {
|
|
701
|
+
return resolveMeshHostStatus(mesh).role === "host";
|
|
702
|
+
}
|
|
703
|
+
function buildMeshHostRequiredFailure(mesh, operation) {
|
|
704
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
705
|
+
return {
|
|
706
|
+
success: false,
|
|
707
|
+
code: "mesh_host_required",
|
|
708
|
+
error: `Mesh Host daemon required for ${operation}; member daemons must pair with the host and cannot own coordinator/queue mutations.`,
|
|
709
|
+
meshHost
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function requireMeshHostQueueOwner(opts) {
|
|
713
|
+
if (opts?.ownerRole === "member") {
|
|
714
|
+
throw new Error("Mesh Host daemon required to mutate mesh queue; member daemons must use the host-owned queue.");
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
function createDefaultMeshHostMetadata() {
|
|
718
|
+
return {
|
|
719
|
+
role: "host",
|
|
720
|
+
pairing: { status: "not_configured" }
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
var init_mesh_host_ownership = __esm({
|
|
724
|
+
"src/mesh/mesh-host-ownership.ts"() {
|
|
725
|
+
"use strict";
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
660
729
|
// src/config/mesh-config.ts
|
|
661
730
|
var mesh_config_exports = {};
|
|
662
731
|
__export(mesh_config_exports, {
|
|
663
732
|
addNode: () => addNode,
|
|
733
|
+
applyMeshHostJoinRequest: () => applyMeshHostJoinRequest,
|
|
734
|
+
configureMeshHostPairing: () => configureMeshHostPairing,
|
|
664
735
|
createMesh: () => createMesh,
|
|
736
|
+
createMeshHostPairingToken: () => createMeshHostPairingToken,
|
|
665
737
|
deleteMesh: () => deleteMesh,
|
|
666
738
|
getMesh: () => getMesh,
|
|
667
739
|
getMeshByRepo: () => getMeshByRepo,
|
|
668
740
|
listMeshes: () => listMeshes,
|
|
741
|
+
markMeshHostPairingJoined: () => markMeshHostPairingJoined,
|
|
669
742
|
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
670
743
|
removeNode: () => removeNode,
|
|
744
|
+
tokenIdForManualPairing: () => tokenIdForManualPairing,
|
|
671
745
|
updateMesh: () => updateMesh,
|
|
672
746
|
updateNode: () => updateNode
|
|
673
747
|
});
|
|
674
748
|
import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
675
749
|
import { join as join4 } from "path";
|
|
676
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
750
|
+
import { createHash, randomBytes, randomUUID as randomUUID3 } from "crypto";
|
|
677
751
|
function getMeshConfigPath() {
|
|
678
752
|
return join4(getConfigDir(), "meshes.json");
|
|
679
753
|
}
|
|
@@ -746,6 +820,7 @@ function createMesh(opts) {
|
|
|
746
820
|
defaultBranch: opts.defaultBranch,
|
|
747
821
|
policy: mergeMeshPolicy(void 0, opts.policy),
|
|
748
822
|
coordinator: opts.coordinator || {},
|
|
823
|
+
meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
|
|
749
824
|
nodes: [],
|
|
750
825
|
createdAt: now,
|
|
751
826
|
updatedAt: now
|
|
@@ -762,6 +837,7 @@ function updateMesh(meshId, opts) {
|
|
|
762
837
|
if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
|
|
763
838
|
if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
|
|
764
839
|
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
840
|
+
if (opts.meshHost) mesh.meshHost = opts.meshHost;
|
|
765
841
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
766
842
|
saveMeshConfig(config);
|
|
767
843
|
return mesh;
|
|
@@ -774,6 +850,186 @@ function deleteMesh(meshId) {
|
|
|
774
850
|
saveMeshConfig(config);
|
|
775
851
|
return true;
|
|
776
852
|
}
|
|
853
|
+
function normalizeManualHostAddress(hostAddress) {
|
|
854
|
+
const normalized = hostAddress.trim().replace(/\/+$/, "");
|
|
855
|
+
if (!normalized) throw new Error("hostAddress required");
|
|
856
|
+
let parsed;
|
|
857
|
+
try {
|
|
858
|
+
parsed = new URL(normalized);
|
|
859
|
+
} catch {
|
|
860
|
+
throw new Error("hostAddress must be a valid http(s) or ws(s) URL");
|
|
861
|
+
}
|
|
862
|
+
if (!["http:", "https:", "ws:", "wss:"].includes(parsed.protocol)) {
|
|
863
|
+
throw new Error("hostAddress must use http, https, ws, or wss");
|
|
864
|
+
}
|
|
865
|
+
return normalized;
|
|
866
|
+
}
|
|
867
|
+
function tokenIdForManualPairing(token) {
|
|
868
|
+
return `tok_${createHash("sha256").update(token).digest("hex").slice(0, 16)}`;
|
|
869
|
+
}
|
|
870
|
+
function normalizeTokenExpiry(value) {
|
|
871
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
872
|
+
const date = new Date(value);
|
|
873
|
+
if (Number.isNaN(date.getTime())) throw new Error("expiresAt must be a valid ISO date");
|
|
874
|
+
return date.toISOString();
|
|
875
|
+
}
|
|
876
|
+
function assertPairingTokenValid(pairing, rawToken, nowIso) {
|
|
877
|
+
const token = rawToken.trim();
|
|
878
|
+
if (!token) return { ok: false, reason: "token required" };
|
|
879
|
+
const presentedTokenId = tokenIdForManualPairing(token);
|
|
880
|
+
const expectedTokenId = pairing?.tokenId;
|
|
881
|
+
if (!expectedTokenId || pairing?.status === "not_configured" || pairing?.status === "revoked") {
|
|
882
|
+
return { ok: false, reason: "host pairing token is not configured", presentedTokenId };
|
|
883
|
+
}
|
|
884
|
+
if (pairing.expiresAt && new Date(pairing.expiresAt).getTime() <= new Date(nowIso).getTime()) {
|
|
885
|
+
return { ok: false, reason: "host pairing token expired", expectedTokenId, presentedTokenId };
|
|
886
|
+
}
|
|
887
|
+
if (presentedTokenId !== expectedTokenId) {
|
|
888
|
+
return { ok: false, reason: "invalid pairing token", expectedTokenId, presentedTokenId };
|
|
889
|
+
}
|
|
890
|
+
return { ok: true, tokenId: presentedTokenId };
|
|
891
|
+
}
|
|
892
|
+
function configureMeshHostPairing(meshId, opts) {
|
|
893
|
+
const hostAddress = normalizeManualHostAddress(opts.hostAddress);
|
|
894
|
+
const token = opts.token.trim();
|
|
895
|
+
if (!token) throw new Error("token required");
|
|
896
|
+
const config = loadMeshConfig();
|
|
897
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
898
|
+
if (!mesh) return void 0;
|
|
899
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
900
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
901
|
+
const meshHost = {
|
|
902
|
+
...previous,
|
|
903
|
+
role: "member",
|
|
904
|
+
hostAddress,
|
|
905
|
+
pairing: {
|
|
906
|
+
status: "pairing",
|
|
907
|
+
tokenId: tokenIdForManualPairing(token),
|
|
908
|
+
lastPairedAt: now
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
mesh.meshHost = meshHost;
|
|
912
|
+
mesh.updatedAt = now;
|
|
913
|
+
saveMeshConfig(config);
|
|
914
|
+
return { mesh, meshHost, hostAddress };
|
|
915
|
+
}
|
|
916
|
+
function createMeshHostPairingToken(meshId, opts = {}) {
|
|
917
|
+
const config = loadMeshConfig();
|
|
918
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
919
|
+
if (!mesh) return void 0;
|
|
920
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
921
|
+
const token = (opts.token || `mhj_${randomBytes(24).toString("base64url")}`).trim();
|
|
922
|
+
if (!token) throw new Error("token required");
|
|
923
|
+
const tokenId = tokenIdForManualPairing(token);
|
|
924
|
+
const expiresAt = normalizeTokenExpiry(opts.expiresAt);
|
|
925
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
926
|
+
if (previous.role === "member") {
|
|
927
|
+
throw new Error("Mesh Host daemon required to create host pairing tokens; member daemons cannot mint host join tokens.");
|
|
928
|
+
}
|
|
929
|
+
const meshHost = {
|
|
930
|
+
...previous,
|
|
931
|
+
role: "host",
|
|
932
|
+
pairing: {
|
|
933
|
+
status: "pairing",
|
|
934
|
+
tokenId,
|
|
935
|
+
lastPairedAt: now,
|
|
936
|
+
...expiresAt ? { expiresAt } : {}
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
mesh.meshHost = meshHost;
|
|
940
|
+
mesh.updatedAt = now;
|
|
941
|
+
saveMeshConfig(config);
|
|
942
|
+
return { mesh, meshHost, token, tokenId, ...expiresAt ? { expiresAt } : {} };
|
|
943
|
+
}
|
|
944
|
+
function applyMeshHostJoinRequest(meshId, opts) {
|
|
945
|
+
const config = loadMeshConfig();
|
|
946
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
947
|
+
if (!mesh) return void 0;
|
|
948
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
949
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
950
|
+
if (previous.role === "member") {
|
|
951
|
+
return { accepted: false, mesh, meshHost: previous, reason: "Mesh Host daemon required to accept join requests" };
|
|
952
|
+
}
|
|
953
|
+
const meshHost = { ...previous, role: "host" };
|
|
954
|
+
const validation = assertPairingTokenValid(meshHost.pairing, opts.token, now);
|
|
955
|
+
if (!validation.ok) {
|
|
956
|
+
mesh.meshHost = {
|
|
957
|
+
...meshHost,
|
|
958
|
+
pairing: {
|
|
959
|
+
...meshHost.pairing || { status: "not_configured" },
|
|
960
|
+
status: "rejected",
|
|
961
|
+
lastRejectedAt: now
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
mesh.updatedAt = now;
|
|
965
|
+
saveMeshConfig(config);
|
|
966
|
+
return { accepted: false, mesh, meshHost: mesh.meshHost, tokenId: validation.presentedTokenId, reason: validation.reason };
|
|
967
|
+
}
|
|
968
|
+
const workspace = opts.memberNode.workspace.trim();
|
|
969
|
+
if (!workspace) throw new Error("memberNode.workspace required");
|
|
970
|
+
const memberId = opts.memberNode.id?.trim();
|
|
971
|
+
let node = mesh.nodes.find((n) => memberId && n.id === memberId || n.workspace === workspace);
|
|
972
|
+
if (node) {
|
|
973
|
+
node.workspace = workspace;
|
|
974
|
+
node.repoRoot = opts.memberNode.repoRoot;
|
|
975
|
+
node.daemonId = opts.memberNode.daemonId;
|
|
976
|
+
node.machineId = opts.memberNode.machineId;
|
|
977
|
+
node.userOverrides = opts.memberNode.userOverrides || node.userOverrides || {};
|
|
978
|
+
node.policy = { ...node.policy || {}, ...opts.memberNode.policy || {} };
|
|
979
|
+
node.role = "member";
|
|
980
|
+
} else {
|
|
981
|
+
if (mesh.nodes.length >= 10) throw new Error("Maximum 10 nodes per mesh");
|
|
982
|
+
node = {
|
|
983
|
+
id: memberId || `node_${randomUUID3().replace(/-/g, "")}`,
|
|
984
|
+
workspace,
|
|
985
|
+
repoRoot: opts.memberNode.repoRoot,
|
|
986
|
+
daemonId: opts.memberNode.daemonId,
|
|
987
|
+
machineId: opts.memberNode.machineId,
|
|
988
|
+
userOverrides: opts.memberNode.userOverrides || {},
|
|
989
|
+
policy: opts.memberNode.policy || {},
|
|
990
|
+
role: "member"
|
|
991
|
+
};
|
|
992
|
+
mesh.nodes.push(node);
|
|
993
|
+
}
|
|
994
|
+
mesh.meshHost = {
|
|
995
|
+
...meshHost,
|
|
996
|
+
pairing: {
|
|
997
|
+
...meshHost.pairing || {},
|
|
998
|
+
status: "paired",
|
|
999
|
+
tokenId: validation.tokenId,
|
|
1000
|
+
joinedAt: now,
|
|
1001
|
+
lastPairedAt: meshHost.pairing?.lastPairedAt || now,
|
|
1002
|
+
...meshHost.pairing?.expiresAt ? { expiresAt: meshHost.pairing.expiresAt } : {}
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
mesh.updatedAt = now;
|
|
1006
|
+
saveMeshConfig(config);
|
|
1007
|
+
return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
|
|
1008
|
+
}
|
|
1009
|
+
function markMeshHostPairingJoined(meshId, opts) {
|
|
1010
|
+
const config = loadMeshConfig();
|
|
1011
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
1012
|
+
if (!mesh) return void 0;
|
|
1013
|
+
const now = opts.joinedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
1014
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
1015
|
+
const tokenId = opts.tokenId || (opts.token ? tokenIdForManualPairing(opts.token) : previous.pairing?.tokenId);
|
|
1016
|
+
mesh.meshHost = {
|
|
1017
|
+
...previous,
|
|
1018
|
+
role: "member",
|
|
1019
|
+
...opts.hostDaemonId ? { hostDaemonId: opts.hostDaemonId } : {},
|
|
1020
|
+
...opts.hostNodeId ? { hostNodeId: opts.hostNodeId } : {},
|
|
1021
|
+
pairing: {
|
|
1022
|
+
...previous.pairing || {},
|
|
1023
|
+
status: "paired",
|
|
1024
|
+
...tokenId ? { tokenId } : {},
|
|
1025
|
+
joinedAt: now,
|
|
1026
|
+
lastPairedAt: previous.pairing?.lastPairedAt || now
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
mesh.updatedAt = now;
|
|
1030
|
+
saveMeshConfig(config);
|
|
1031
|
+
return { mesh, meshHost: mesh.meshHost };
|
|
1032
|
+
}
|
|
777
1033
|
function addNode(meshId, opts) {
|
|
778
1034
|
const config = loadMeshConfig();
|
|
779
1035
|
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
@@ -794,7 +1050,8 @@ function addNode(meshId, opts) {
|
|
|
794
1050
|
policy: opts.policy || {},
|
|
795
1051
|
isLocalWorktree: opts.isLocalWorktree,
|
|
796
1052
|
worktreeBranch: opts.worktreeBranch,
|
|
797
|
-
clonedFromNodeId: opts.clonedFromNodeId
|
|
1053
|
+
clonedFromNodeId: opts.clonedFromNodeId,
|
|
1054
|
+
role: opts.role
|
|
798
1055
|
};
|
|
799
1056
|
mesh.nodes.push(node);
|
|
800
1057
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -830,6 +1087,7 @@ var init_mesh_config = __esm({
|
|
|
830
1087
|
"use strict";
|
|
831
1088
|
init_config();
|
|
832
1089
|
init_repo_mesh_types();
|
|
1090
|
+
init_mesh_host_ownership();
|
|
833
1091
|
SESSION_CLEANUP_MODES = /* @__PURE__ */ new Set(["preserve", "stop", "delete_stopped", "stop_and_delete"]);
|
|
834
1092
|
SPAWNED_SESSION_VISIBILITY_MODES = /* @__PURE__ */ new Set(["visible", "hidden"]);
|
|
835
1093
|
}
|
|
@@ -1007,8 +1265,8 @@ __export(mesh_ledger_exports, {
|
|
|
1007
1265
|
readLedgerEntries: () => readLedgerEntries,
|
|
1008
1266
|
readLedgerSlice: () => readLedgerSlice
|
|
1009
1267
|
});
|
|
1010
|
-
import { existsSync as
|
|
1011
|
-
import { join as
|
|
1268
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, appendFileSync, statSync as statSync2, renameSync } from "fs";
|
|
1269
|
+
import { join as join6 } from "path";
|
|
1012
1270
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
1013
1271
|
import { EventEmitter } from "events";
|
|
1014
1272
|
function isIntentionalCleanupStopEntry(entry) {
|
|
@@ -1017,19 +1275,19 @@ function isIntentionalCleanupStopEntry(entry) {
|
|
|
1017
1275
|
return payload.intentional === true && (payload.reason === "operator_cleanup" || payload.intentionalStopReason === "operator_cleanup" || payload.source === "mesh_cleanup_sessions" || payload.source === "mesh_remove_node");
|
|
1018
1276
|
}
|
|
1019
1277
|
function getLedgerDir() {
|
|
1020
|
-
const dir =
|
|
1021
|
-
if (!
|
|
1278
|
+
const dir = join6(getConfigDir(), LEDGER_DIR_NAME);
|
|
1279
|
+
if (!existsSync6(dir)) {
|
|
1022
1280
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
1023
1281
|
}
|
|
1024
1282
|
return dir;
|
|
1025
1283
|
}
|
|
1026
1284
|
function getLedgerPath(meshId) {
|
|
1027
1285
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1028
|
-
return
|
|
1286
|
+
return join6(getLedgerDir(), `${safe}.jsonl`);
|
|
1029
1287
|
}
|
|
1030
1288
|
function getRotatedPath(meshId, index) {
|
|
1031
1289
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1032
|
-
return
|
|
1290
|
+
return join6(getLedgerDir(), `${safe}.${index}.jsonl`);
|
|
1033
1291
|
}
|
|
1034
1292
|
function buildTaskCompletionEvidence(opts) {
|
|
1035
1293
|
const providerSessionId = opts.providerSessionId?.trim() || void 0;
|
|
@@ -1070,7 +1328,7 @@ function appendLedgerEntry(meshId, partial) {
|
|
|
1070
1328
|
...partial
|
|
1071
1329
|
};
|
|
1072
1330
|
const filePath = getLedgerPath(meshId);
|
|
1073
|
-
if (
|
|
1331
|
+
if (existsSync6(filePath)) {
|
|
1074
1332
|
try {
|
|
1075
1333
|
const stat2 = statSync2(filePath);
|
|
1076
1334
|
if (stat2.size >= MAX_FILE_SIZE_BYTES) {
|
|
@@ -1137,10 +1395,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
|
|
|
1137
1395
|
}
|
|
1138
1396
|
function readLedgerEntries(meshId, opts) {
|
|
1139
1397
|
const filePath = getLedgerPath(meshId);
|
|
1140
|
-
if (!
|
|
1398
|
+
if (!existsSync6(filePath)) return [];
|
|
1141
1399
|
let content;
|
|
1142
1400
|
try {
|
|
1143
|
-
content =
|
|
1401
|
+
content = readFileSync4(filePath, "utf-8");
|
|
1144
1402
|
} catch {
|
|
1145
1403
|
return [];
|
|
1146
1404
|
}
|
|
@@ -1312,7 +1570,7 @@ function getSessionRecoveryContext(meshId, opts) {
|
|
|
1312
1570
|
}
|
|
1313
1571
|
function rotateLedgerFile(meshId, currentPath) {
|
|
1314
1572
|
let index = 1;
|
|
1315
|
-
while (
|
|
1573
|
+
while (existsSync6(getRotatedPath(meshId, index))) {
|
|
1316
1574
|
index++;
|
|
1317
1575
|
if (index > 10) break;
|
|
1318
1576
|
}
|
|
@@ -1351,18 +1609,48 @@ __export(mesh_work_queue_exports, {
|
|
|
1351
1609
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
1352
1610
|
updateTaskStatus: () => updateTaskStatus
|
|
1353
1611
|
});
|
|
1354
|
-
import { existsSync as
|
|
1355
|
-
import { join as
|
|
1612
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync3, readFileSync as readFileSync5, openSync, closeSync, unlinkSync } from "fs";
|
|
1613
|
+
import { join as join7 } from "path";
|
|
1356
1614
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1357
1615
|
function getQueuePath(meshId) {
|
|
1358
1616
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1359
|
-
return
|
|
1617
|
+
return join7(getLedgerDir(), `${safe}.queue.json`);
|
|
1618
|
+
}
|
|
1619
|
+
function getLockPath(meshId) {
|
|
1620
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1621
|
+
return join7(getLedgerDir(), `${safe}.queue.lock`);
|
|
1622
|
+
}
|
|
1623
|
+
function withQueueLock(meshId, fn) {
|
|
1624
|
+
const lockPath = getLockPath(meshId);
|
|
1625
|
+
let fd = -1;
|
|
1626
|
+
for (let i = 0; i < 10; i++) {
|
|
1627
|
+
try {
|
|
1628
|
+
fd = openSync(lockPath, "wx");
|
|
1629
|
+
break;
|
|
1630
|
+
} catch {
|
|
1631
|
+
const deadline = Date.now() + 30;
|
|
1632
|
+
while (Date.now() < deadline) {
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
try {
|
|
1637
|
+
return fn();
|
|
1638
|
+
} finally {
|
|
1639
|
+
if (fd !== -1) try {
|
|
1640
|
+
closeSync(fd);
|
|
1641
|
+
} catch {
|
|
1642
|
+
}
|
|
1643
|
+
try {
|
|
1644
|
+
unlinkSync(lockPath);
|
|
1645
|
+
} catch {
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1360
1648
|
}
|
|
1361
1649
|
function readQueue(meshId) {
|
|
1362
1650
|
const path28 = getQueuePath(meshId);
|
|
1363
|
-
if (!
|
|
1651
|
+
if (!existsSync7(path28)) return [];
|
|
1364
1652
|
try {
|
|
1365
|
-
const content =
|
|
1653
|
+
const content = readFileSync5(path28, "utf-8");
|
|
1366
1654
|
return JSON.parse(content);
|
|
1367
1655
|
} catch {
|
|
1368
1656
|
return [];
|
|
@@ -1373,20 +1661,23 @@ function writeQueue(meshId, queue) {
|
|
|
1373
1661
|
writeFileSync3(path28, JSON.stringify(queue, null, 2), "utf-8");
|
|
1374
1662
|
}
|
|
1375
1663
|
function enqueueTask(meshId, message, opts) {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1664
|
+
requireMeshHostQueueOwner(opts);
|
|
1665
|
+
return withQueueLock(meshId, () => {
|
|
1666
|
+
const queue = readQueue(meshId);
|
|
1667
|
+
const entry = {
|
|
1668
|
+
id: randomUUID5(),
|
|
1669
|
+
meshId,
|
|
1670
|
+
message,
|
|
1671
|
+
status: "pending",
|
|
1672
|
+
targetNodeId: opts?.targetNodeId,
|
|
1673
|
+
targetSessionId: opts?.targetSessionId,
|
|
1674
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1675
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1676
|
+
};
|
|
1677
|
+
queue.push(entry);
|
|
1678
|
+
writeQueue(meshId, queue);
|
|
1679
|
+
return entry;
|
|
1680
|
+
});
|
|
1390
1681
|
}
|
|
1391
1682
|
function getQueue(meshId, opts) {
|
|
1392
1683
|
let queue = readQueue(meshId);
|
|
@@ -1397,100 +1688,114 @@ function getQueue(meshId, opts) {
|
|
|
1397
1688
|
return queue;
|
|
1398
1689
|
}
|
|
1399
1690
|
function claimNextTask(meshId, nodeId, sessionId) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1691
|
+
return withQueueLock(meshId, () => {
|
|
1692
|
+
const queue = readQueue(meshId);
|
|
1693
|
+
const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
|
|
1694
|
+
if (hasActiveAssignment) return null;
|
|
1695
|
+
let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
|
|
1696
|
+
if (targetIdx === -1) {
|
|
1697
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
|
|
1698
|
+
}
|
|
1699
|
+
if (targetIdx === -1) {
|
|
1700
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
|
|
1701
|
+
}
|
|
1702
|
+
if (targetIdx === -1) return null;
|
|
1703
|
+
const entry = queue[targetIdx];
|
|
1704
|
+
entry.status = "assigned";
|
|
1705
|
+
entry.assignedNodeId = nodeId;
|
|
1706
|
+
entry.assignedSessionId = sessionId;
|
|
1707
|
+
entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1708
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1709
|
+
writeQueue(meshId, queue);
|
|
1710
|
+
return entry;
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
function updateTaskStatus(meshId, taskId, status, opts) {
|
|
1714
|
+
requireMeshHostQueueOwner(opts);
|
|
1715
|
+
return withQueueLock(meshId, () => {
|
|
1716
|
+
const queue = readQueue(meshId);
|
|
1717
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1718
|
+
if (idx === -1) return null;
|
|
1719
|
+
queue[idx].status = status;
|
|
1720
|
+
queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1721
|
+
writeQueue(meshId, queue);
|
|
1722
|
+
return queue[idx];
|
|
1723
|
+
});
|
|
1428
1724
|
}
|
|
1429
1725
|
function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
...autoLaunch,
|
|
1436
|
-
updatedAt
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
return queue[idx];
|
|
1726
|
+
return withQueueLock(meshId, () => {
|
|
1727
|
+
const queue = readQueue(meshId);
|
|
1728
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1729
|
+
if (idx === -1) return null;
|
|
1730
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1731
|
+
queue[idx].autoLaunch = { ...autoLaunch, updatedAt: now };
|
|
1732
|
+
queue[idx].updatedAt = now;
|
|
1733
|
+
writeQueue(meshId, queue);
|
|
1734
|
+
return queue[idx];
|
|
1735
|
+
});
|
|
1441
1736
|
}
|
|
1442
1737
|
function cancelTask(meshId, taskId, opts) {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1738
|
+
requireMeshHostQueueOwner(opts);
|
|
1739
|
+
return withQueueLock(meshId, () => {
|
|
1740
|
+
const queue = readQueue(meshId);
|
|
1741
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1742
|
+
if (idx === -1) return null;
|
|
1743
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1744
|
+
queue[idx].status = "cancelled";
|
|
1745
|
+
queue[idx].updatedAt = now;
|
|
1746
|
+
queue[idx].cancelledAt = now;
|
|
1747
|
+
if (opts?.reason) queue[idx].cancelReason = opts.reason;
|
|
1748
|
+
writeQueue(meshId, queue);
|
|
1749
|
+
return queue[idx];
|
|
1750
|
+
});
|
|
1453
1751
|
}
|
|
1454
1752
|
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
|
-
|
|
1753
|
+
requireMeshHostQueueOwner(opts);
|
|
1754
|
+
return withQueueLock(meshId, () => {
|
|
1755
|
+
const queue = readQueue(meshId);
|
|
1756
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1757
|
+
if (idx === -1) return null;
|
|
1758
|
+
const entry = queue[idx];
|
|
1759
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1760
|
+
entry.status = "pending";
|
|
1761
|
+
delete entry.assignedNodeId;
|
|
1762
|
+
delete entry.assignedSessionId;
|
|
1763
|
+
delete entry.cancelledAt;
|
|
1764
|
+
delete entry.cancelReason;
|
|
1765
|
+
if (opts?.clearTargetNode) delete entry.targetNodeId;
|
|
1766
|
+
if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
|
|
1767
|
+
if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
|
|
1768
|
+
if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
|
|
1769
|
+
entry.updatedAt = now;
|
|
1770
|
+
entry.requeuedAt = now;
|
|
1771
|
+
entry.requeueCount = (entry.requeueCount || 0) + 1;
|
|
1772
|
+
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
1773
|
+
writeQueue(meshId, queue);
|
|
1774
|
+
return entry;
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
1778
|
+
return withQueueLock(meshId, () => {
|
|
1779
|
+
const queue = readQueue(meshId);
|
|
1780
|
+
const occurredAtTime = opts?.occurredAt ? new Date(opts.occurredAt).getTime() : Number.NaN;
|
|
1781
|
+
const hasOccurredAt = Number.isFinite(occurredAtTime);
|
|
1782
|
+
let bestIdx = -1;
|
|
1783
|
+
let bestTime = 0;
|
|
1784
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
1785
|
+
if (queue[i].assignedSessionId !== sessionId || queue[i].status !== "assigned") continue;
|
|
1482
1786
|
const time = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
|
|
1787
|
+
if (hasOccurredAt && Number.isFinite(time) && time > occurredAtTime) continue;
|
|
1483
1788
|
if (time > bestTime) {
|
|
1484
1789
|
bestTime = time;
|
|
1485
1790
|
bestIdx = i;
|
|
1486
1791
|
}
|
|
1487
1792
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1793
|
+
if (bestIdx === -1) return null;
|
|
1794
|
+
queue[bestIdx].status = status;
|
|
1795
|
+
queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1796
|
+
writeQueue(meshId, queue);
|
|
1797
|
+
return queue[bestIdx];
|
|
1798
|
+
});
|
|
1494
1799
|
}
|
|
1495
1800
|
function getMeshQueueStats(meshId) {
|
|
1496
1801
|
const queue = readQueue(meshId);
|
|
@@ -1530,6 +1835,7 @@ var init_mesh_work_queue = __esm({
|
|
|
1530
1835
|
"src/mesh/mesh-work-queue.ts"() {
|
|
1531
1836
|
"use strict";
|
|
1532
1837
|
init_mesh_ledger();
|
|
1838
|
+
init_mesh_host_ownership();
|
|
1533
1839
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
1534
1840
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
1535
1841
|
}
|
|
@@ -1539,7 +1845,7 @@ var init_mesh_work_queue = __esm({
|
|
|
1539
1845
|
import { exec } from "child_process";
|
|
1540
1846
|
import * as os2 from "os";
|
|
1541
1847
|
import * as path8 from "path";
|
|
1542
|
-
import { existsSync as
|
|
1848
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1543
1849
|
function parseVersion(raw) {
|
|
1544
1850
|
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
1545
1851
|
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
@@ -1563,7 +1869,7 @@ function resolveCommandPath(command) {
|
|
|
1563
1869
|
if (isExplicitCommandPath(trimmed)) {
|
|
1564
1870
|
const expanded = expandHome(trimmed);
|
|
1565
1871
|
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
1566
|
-
return
|
|
1872
|
+
return existsSync8(candidate) ? candidate : null;
|
|
1567
1873
|
}
|
|
1568
1874
|
return null;
|
|
1569
1875
|
}
|
|
@@ -1890,18 +2196,77 @@ __export(mesh_events_exports, {
|
|
|
1890
2196
|
drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
|
|
1891
2197
|
getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
|
|
1892
2198
|
handleMeshForwardEvent: () => handleMeshForwardEvent,
|
|
2199
|
+
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
1893
2200
|
setupMeshEventForwarding: () => setupMeshEventForwarding,
|
|
1894
2201
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
1895
2202
|
tryAssignQueueTask: () => tryAssignQueueTask
|
|
1896
2203
|
});
|
|
1897
|
-
|
|
1898
|
-
|
|
2204
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync10, readFileSync as readFileSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
2205
|
+
import { join as join10 } from "path";
|
|
2206
|
+
function sweepExpiredRemoteIdleSessions() {
|
|
2207
|
+
const now = Date.now();
|
|
2208
|
+
for (const [key, session] of remoteIdleSessions) {
|
|
2209
|
+
if (session.expiresAt <= now) remoteIdleSessions.delete(key);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
function getPendingEventsPath(meshId) {
|
|
2213
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2214
|
+
return join10(getLedgerDir(), `${safe}.pending-events.jsonl`);
|
|
2215
|
+
}
|
|
2216
|
+
function queuePendingMeshCoordinatorEvent(event) {
|
|
2217
|
+
try {
|
|
2218
|
+
appendFileSync3(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
|
|
2219
|
+
return true;
|
|
2220
|
+
} catch (e) {
|
|
2221
|
+
LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
2222
|
+
return false;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function drainPendingMeshCoordinatorEvents(meshId) {
|
|
2226
|
+
if (!meshId) return [];
|
|
2227
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2228
|
+
if (!existsSync10(path28)) return [];
|
|
2229
|
+
try {
|
|
2230
|
+
const raw = readFileSync6(path28, "utf-8");
|
|
2231
|
+
try {
|
|
2232
|
+
unlinkSync3(path28);
|
|
2233
|
+
} catch {
|
|
2234
|
+
}
|
|
2235
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
2236
|
+
try {
|
|
2237
|
+
return [JSON.parse(line)];
|
|
2238
|
+
} catch {
|
|
2239
|
+
return [];
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
} catch {
|
|
2243
|
+
return [];
|
|
2244
|
+
}
|
|
1899
2245
|
}
|
|
1900
|
-
function getPendingMeshCoordinatorEvents() {
|
|
1901
|
-
|
|
2246
|
+
function getPendingMeshCoordinatorEvents(meshId) {
|
|
2247
|
+
if (!meshId) return [];
|
|
2248
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2249
|
+
if (!existsSync10(path28)) return [];
|
|
2250
|
+
try {
|
|
2251
|
+
const raw = readFileSync6(path28, "utf-8");
|
|
2252
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
2253
|
+
try {
|
|
2254
|
+
return [JSON.parse(line)];
|
|
2255
|
+
} catch {
|
|
2256
|
+
return [];
|
|
2257
|
+
}
|
|
2258
|
+
});
|
|
2259
|
+
} catch {
|
|
2260
|
+
return [];
|
|
2261
|
+
}
|
|
1902
2262
|
}
|
|
1903
|
-
function clearPendingMeshCoordinatorEvents() {
|
|
1904
|
-
|
|
2263
|
+
function clearPendingMeshCoordinatorEvents(meshId) {
|
|
2264
|
+
if (!meshId) return;
|
|
2265
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2266
|
+
if (existsSync10(path28)) try {
|
|
2267
|
+
unlinkSync3(path28);
|
|
2268
|
+
} catch {
|
|
2269
|
+
}
|
|
1905
2270
|
}
|
|
1906
2271
|
function readNonEmptyString(value) {
|
|
1907
2272
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
@@ -1947,6 +2312,38 @@ function shouldSuppressIntentionalCleanupStop(args) {
|
|
|
1947
2312
|
if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
|
|
1948
2313
|
return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
|
|
1949
2314
|
}
|
|
2315
|
+
function readEventTimestamp(value) {
|
|
2316
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2317
|
+
if (typeof value === "string" && value.trim()) {
|
|
2318
|
+
const numeric = Number(value);
|
|
2319
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
2320
|
+
const parsed = Date.parse(value);
|
|
2321
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
2322
|
+
}
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
function buildMeshCompletionFingerprint(args) {
|
|
2326
|
+
const timestampPart = Number.isFinite(args.timestamp) ? String(args.timestamp) : readNonEmptyString(args.finalSummary).slice(0, 200);
|
|
2327
|
+
return [
|
|
2328
|
+
args.meshId,
|
|
2329
|
+
args.event,
|
|
2330
|
+
args.sessionId,
|
|
2331
|
+
args.providerType || "",
|
|
2332
|
+
args.providerSessionId || "",
|
|
2333
|
+
timestampPart
|
|
2334
|
+
].join("::");
|
|
2335
|
+
}
|
|
2336
|
+
function isDuplicateMeshCompletionEvent(args) {
|
|
2337
|
+
const fingerprint = buildMeshCompletionFingerprint(args);
|
|
2338
|
+
if (!fingerprint) return false;
|
|
2339
|
+
const now = Date.now();
|
|
2340
|
+
for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
|
|
2341
|
+
if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
|
|
2342
|
+
}
|
|
2343
|
+
if (recentCompletionFingerprints.has(fingerprint)) return true;
|
|
2344
|
+
recentCompletionFingerprints.set(fingerprint, now);
|
|
2345
|
+
return false;
|
|
2346
|
+
}
|
|
1950
2347
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
1951
2348
|
const task = claimNextTask(meshId, nodeId, sessionId);
|
|
1952
2349
|
if (!task) {
|
|
@@ -1965,7 +2362,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
1965
2362
|
message: task.message
|
|
1966
2363
|
}).catch((e) => {
|
|
1967
2364
|
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
1968
|
-
updateTaskStatus(meshId, task.id, "
|
|
2365
|
+
updateTaskStatus(meshId, task.id, "pending");
|
|
2366
|
+
try {
|
|
2367
|
+
appendLedgerEntry(meshId, {
|
|
2368
|
+
kind: "dispatch_failed",
|
|
2369
|
+
nodeId,
|
|
2370
|
+
sessionId,
|
|
2371
|
+
payload: { taskId: task.id, error: e?.message, retryable: true }
|
|
2372
|
+
});
|
|
2373
|
+
} catch {
|
|
2374
|
+
}
|
|
1969
2375
|
});
|
|
1970
2376
|
return true;
|
|
1971
2377
|
}
|
|
@@ -2299,18 +2705,36 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2299
2705
|
LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
|
|
2300
2706
|
return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
|
|
2301
2707
|
}
|
|
2708
|
+
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
2709
|
+
if (args.event === "agent:generating_completed" && eventSessionId) {
|
|
2710
|
+
const duplicateCompletion = isDuplicateMeshCompletionEvent({
|
|
2711
|
+
meshId: args.meshId,
|
|
2712
|
+
event: args.event,
|
|
2713
|
+
sessionId: eventSessionId,
|
|
2714
|
+
providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
|
|
2715
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
|
|
2716
|
+
timestamp: eventTimestamp,
|
|
2717
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
|
|
2718
|
+
});
|
|
2719
|
+
if (duplicateCompletion) {
|
|
2720
|
+
LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
|
|
2721
|
+
return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2302
2724
|
let completedTaskForLedger = null;
|
|
2303
2725
|
if (args.event === "agent:generating_completed") {
|
|
2304
2726
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
2305
2727
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
2306
2728
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
2307
2729
|
if (sessionId) {
|
|
2308
|
-
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed"
|
|
2730
|
+
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed", {
|
|
2731
|
+
occurredAt: eventTimestamp !== null ? new Date(eventTimestamp).toISOString() : void 0
|
|
2732
|
+
});
|
|
2309
2733
|
completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
|
|
2310
2734
|
if (nodeId && providerType) {
|
|
2311
|
-
|
|
2735
|
+
setImmediate(() => {
|
|
2312
2736
|
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2313
|
-
}
|
|
2737
|
+
});
|
|
2314
2738
|
}
|
|
2315
2739
|
}
|
|
2316
2740
|
} else if (args.event === "agent:ready") {
|
|
@@ -2348,13 +2772,17 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2348
2772
|
}
|
|
2349
2773
|
}
|
|
2350
2774
|
if (sessionId && nodeId && providerType) {
|
|
2351
|
-
|
|
2352
|
-
|
|
2775
|
+
sweepExpiredRemoteIdleSessions();
|
|
2776
|
+
remoteIdleSessions.set(`${nodeId}:${sessionId}`, {
|
|
2777
|
+
nodeId,
|
|
2778
|
+
sessionId,
|
|
2779
|
+
providerType,
|
|
2780
|
+
expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
|
|
2781
|
+
});
|
|
2782
|
+
setImmediate(() => {
|
|
2353
2783
|
const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2354
|
-
if (assigned) {
|
|
2355
|
-
|
|
2356
|
-
}
|
|
2357
|
-
}, 500);
|
|
2784
|
+
if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
2785
|
+
});
|
|
2358
2786
|
}
|
|
2359
2787
|
} else if (args.event === "agent:generating_started") {
|
|
2360
2788
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
@@ -2465,17 +2893,18 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2465
2893
|
return true;
|
|
2466
2894
|
});
|
|
2467
2895
|
if (coordinatorInstances.length === 0) {
|
|
2468
|
-
if (
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2896
|
+
if (queuePendingMeshCoordinatorEvent({
|
|
2897
|
+
event: args.event,
|
|
2898
|
+
meshId: args.meshId,
|
|
2899
|
+
nodeLabel: args.nodeLabel,
|
|
2900
|
+
nodeId: args.nodeId || void 0,
|
|
2901
|
+
workspace: readNonEmptyString(args.metadataEvent.workspace),
|
|
2902
|
+
metadataEvent: {
|
|
2903
|
+
...args.metadataEvent,
|
|
2904
|
+
...recoveryContext ? { recoveryContext } : {}
|
|
2905
|
+
},
|
|
2906
|
+
queuedAt: Date.now()
|
|
2907
|
+
})) {
|
|
2479
2908
|
LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
2480
2909
|
}
|
|
2481
2910
|
return { success: true, forwarded: 0 };
|
|
@@ -2514,6 +2943,7 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
2514
2943
|
providerType: readNonEmptyString(payload.providerType),
|
|
2515
2944
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
2516
2945
|
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
|
|
2946
|
+
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
2517
2947
|
intentional: payload.intentional === true,
|
|
2518
2948
|
intentionalStop: payload.intentionalStop === true,
|
|
2519
2949
|
operatorCleanup: payload.operatorCleanup === true,
|
|
@@ -2556,7 +2986,7 @@ function setupMeshEventForwarding(components) {
|
|
|
2556
2986
|
});
|
|
2557
2987
|
});
|
|
2558
2988
|
}
|
|
2559
|
-
var
|
|
2989
|
+
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;
|
|
2560
2990
|
var init_mesh_events = __esm({
|
|
2561
2991
|
"src/mesh/mesh-events.ts"() {
|
|
2562
2992
|
"use strict";
|
|
@@ -2566,9 +2996,8 @@ var init_mesh_events = __esm({
|
|
|
2566
2996
|
init_logger();
|
|
2567
2997
|
init_mesh_ledger();
|
|
2568
2998
|
init_mesh_work_queue();
|
|
2999
|
+
REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
|
|
2569
3000
|
remoteIdleSessions = /* @__PURE__ */ new Map();
|
|
2570
|
-
MAX_PENDING_EVENTS = 50;
|
|
2571
|
-
pendingMeshCoordinatorEvents = [];
|
|
2572
3001
|
MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
|
|
2573
3002
|
"agent:generating_started",
|
|
2574
3003
|
"agent:generating_completed",
|
|
@@ -2584,6 +3013,8 @@ var init_mesh_events = __esm({
|
|
|
2584
3013
|
"monitor:long_generating": "task_stalled"
|
|
2585
3014
|
};
|
|
2586
3015
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
3016
|
+
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
3017
|
+
recentCompletionFingerprints = /* @__PURE__ */ new Map();
|
|
2587
3018
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
2588
3019
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
2589
3020
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -5676,8 +6107,14 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5676
6107
|
const includeSubmodules = options.includeSubmodules !== false;
|
|
5677
6108
|
try {
|
|
5678
6109
|
const repo = await resolveGitRepository(workspace, options);
|
|
5679
|
-
|
|
5680
|
-
|
|
6110
|
+
let parsed = await readPorcelainStatus(repo, options);
|
|
6111
|
+
let upstreamProbe = getInitialUpstreamProbe(parsed);
|
|
6112
|
+
if (options.refreshUpstream) {
|
|
6113
|
+
upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
|
|
6114
|
+
if (upstreamProbe.upstreamStatus === "fresh") {
|
|
6115
|
+
parsed = await readPorcelainStatus(repo, options);
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
5681
6118
|
const head = await readHead(repo, options);
|
|
5682
6119
|
const stashCount = await readStashCount(repo, options);
|
|
5683
6120
|
let submodules;
|
|
@@ -5692,6 +6129,9 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5692
6129
|
headCommit: head.commit,
|
|
5693
6130
|
headMessage: head.message,
|
|
5694
6131
|
upstream: parsed.upstream,
|
|
6132
|
+
upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
|
|
6133
|
+
upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
|
|
6134
|
+
upstreamFetchError: upstreamProbe.upstreamFetchError,
|
|
5695
6135
|
ahead: parsed.ahead,
|
|
5696
6136
|
behind: parsed.behind,
|
|
5697
6137
|
staged: parsed.staged,
|
|
@@ -5716,6 +6156,60 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5716
6156
|
);
|
|
5717
6157
|
}
|
|
5718
6158
|
}
|
|
6159
|
+
async function readPorcelainStatus(repo, options) {
|
|
6160
|
+
const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
|
|
6161
|
+
return parsePorcelainV2Status(statusOutput.stdout);
|
|
6162
|
+
}
|
|
6163
|
+
function getInitialUpstreamProbe(parsed) {
|
|
6164
|
+
return {
|
|
6165
|
+
upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
|
|
6166
|
+
};
|
|
6167
|
+
}
|
|
6168
|
+
async function refreshTrackedUpstream(repo, parsed, options) {
|
|
6169
|
+
if (!parsed.upstream || !parsed.branch) {
|
|
6170
|
+
return { upstreamStatus: "no_upstream" };
|
|
6171
|
+
}
|
|
6172
|
+
const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
|
|
6173
|
+
if (!remoteName) {
|
|
6174
|
+
return {
|
|
6175
|
+
upstreamStatus: "stale",
|
|
6176
|
+
upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
try {
|
|
6180
|
+
await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
|
|
6181
|
+
return {
|
|
6182
|
+
upstreamStatus: "fresh",
|
|
6183
|
+
upstreamFetchedAt: Date.now()
|
|
6184
|
+
};
|
|
6185
|
+
} catch (error) {
|
|
6186
|
+
return {
|
|
6187
|
+
upstreamStatus: "stale",
|
|
6188
|
+
upstreamFetchError: formatGitError(error)
|
|
6189
|
+
};
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
async function readBranchRemote(repo, branch, options) {
|
|
6193
|
+
try {
|
|
6194
|
+
const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
|
|
6195
|
+
return result.stdout.trim() || null;
|
|
6196
|
+
} catch {
|
|
6197
|
+
return null;
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
function inferRemoteName(upstream) {
|
|
6201
|
+
const [remoteName] = upstream.split("/");
|
|
6202
|
+
return remoteName?.trim() || null;
|
|
6203
|
+
}
|
|
6204
|
+
function formatGitError(error) {
|
|
6205
|
+
if (error instanceof GitCommandError) {
|
|
6206
|
+
return error.stderr || error.message;
|
|
6207
|
+
}
|
|
6208
|
+
if (error instanceof Error) {
|
|
6209
|
+
return error.message;
|
|
6210
|
+
}
|
|
6211
|
+
return String(error);
|
|
6212
|
+
}
|
|
5719
6213
|
function parsePorcelainV2Status(output) {
|
|
5720
6214
|
const parsed = {
|
|
5721
6215
|
branch: null,
|
|
@@ -5810,6 +6304,7 @@ function emptyStatus(workspace, lastCheckedAt, error) {
|
|
|
5810
6304
|
headCommit: null,
|
|
5811
6305
|
headMessage: null,
|
|
5812
6306
|
upstream: null,
|
|
6307
|
+
upstreamStatus: "unavailable",
|
|
5813
6308
|
ahead: 0,
|
|
5814
6309
|
behind: 0,
|
|
5815
6310
|
staged: 0,
|
|
@@ -6090,6 +6585,9 @@ function createGitCompactSummary(status, diffSummary) {
|
|
|
6090
6585
|
isGitRepo: status.isGitRepo,
|
|
6091
6586
|
repoRoot: status.repoRoot,
|
|
6092
6587
|
branch: status.branch,
|
|
6588
|
+
upstreamStatus: status.upstreamStatus,
|
|
6589
|
+
upstreamFetchedAt: status.upstreamFetchedAt,
|
|
6590
|
+
upstreamFetchError: status.upstreamFetchError,
|
|
6093
6591
|
dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
|
|
6094
6592
|
changedFiles,
|
|
6095
6593
|
ahead: status.ahead,
|
|
@@ -6434,7 +6932,7 @@ var defaultSnapshotStore = createGitSnapshotStore({
|
|
|
6434
6932
|
});
|
|
6435
6933
|
function createDefaultGitCommandServices() {
|
|
6436
6934
|
return {
|
|
6437
|
-
getStatus: ({ workspace }) => getGitRepoStatus(workspace),
|
|
6935
|
+
getStatus: ({ workspace, refreshUpstream }) => getGitRepoStatus(workspace, { refreshUpstream }),
|
|
6438
6936
|
getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
|
|
6439
6937
|
getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
|
|
6440
6938
|
createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
|
|
@@ -6520,7 +7018,7 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
6520
7018
|
switch (command) {
|
|
6521
7019
|
case "git_status": {
|
|
6522
7020
|
if (!services.getStatus) return serviceNotImplemented(command);
|
|
6523
|
-
const status = await runService(() => services.getStatus({ workspace }));
|
|
7021
|
+
const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
|
|
6524
7022
|
return "success" in status ? status : { success: true, status };
|
|
6525
7023
|
}
|
|
6526
7024
|
case "git_diff_summary": {
|
|
@@ -7326,6 +7824,238 @@ function getSavedProviderSessions(state, filters) {
|
|
|
7326
7824
|
init_mesh_config();
|
|
7327
7825
|
init_coordinator_prompt();
|
|
7328
7826
|
|
|
7827
|
+
// src/mesh/refine-config.ts
|
|
7828
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
7829
|
+
import { join as join5 } from "path";
|
|
7830
|
+
import * as yaml from "js-yaml";
|
|
7831
|
+
var MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
7832
|
+
var MESH_REFINE_CONFIG_LOCATIONS = [
|
|
7833
|
+
".adhdev/refine.json",
|
|
7834
|
+
".adhdev/refine.yaml",
|
|
7835
|
+
".adhdev/refine.yml",
|
|
7836
|
+
".adhdev/repo-mesh-refine.json",
|
|
7837
|
+
".adhdev/repo-mesh-refine.yaml",
|
|
7838
|
+
".adhdev/repo-mesh-refine.yml",
|
|
7839
|
+
"repo-mesh.refine.json",
|
|
7840
|
+
"repo-mesh.refine.yaml",
|
|
7841
|
+
"repo-mesh.refine.yml"
|
|
7842
|
+
];
|
|
7843
|
+
var MESH_REFINE_CONFIG_SCHEMA = {
|
|
7844
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
7845
|
+
title: "ADHDev Repo Mesh Refinery Config",
|
|
7846
|
+
type: "object",
|
|
7847
|
+
additionalProperties: false,
|
|
7848
|
+
required: ["version"],
|
|
7849
|
+
properties: {
|
|
7850
|
+
version: { const: 1 },
|
|
7851
|
+
validation: {
|
|
7852
|
+
type: "object",
|
|
7853
|
+
additionalProperties: false,
|
|
7854
|
+
properties: {
|
|
7855
|
+
required: { type: "boolean", default: true },
|
|
7856
|
+
commands: {
|
|
7857
|
+
type: "array",
|
|
7858
|
+
minItems: 1,
|
|
7859
|
+
maxItems: 8,
|
|
7860
|
+
items: {
|
|
7861
|
+
type: "object",
|
|
7862
|
+
additionalProperties: false,
|
|
7863
|
+
required: ["command"],
|
|
7864
|
+
properties: {
|
|
7865
|
+
command: { type: "string", minLength: 1 },
|
|
7866
|
+
args: { type: "array", items: { type: "string" } },
|
|
7867
|
+
category: { enum: [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"] },
|
|
7868
|
+
cwd: { type: "string" },
|
|
7869
|
+
timeoutMs: { type: "number", minimum: 1e3, maximum: 6e5 },
|
|
7870
|
+
env: { type: "object", additionalProperties: { type: "string" } }
|
|
7871
|
+
}
|
|
7872
|
+
}
|
|
7873
|
+
}
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
}
|
|
7877
|
+
};
|
|
7878
|
+
function isRecord(value) {
|
|
7879
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7880
|
+
}
|
|
7881
|
+
function tokenizeCommandString(command) {
|
|
7882
|
+
const trimmed = command.trim();
|
|
7883
|
+
if (!trimmed) return null;
|
|
7884
|
+
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
7885
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
7886
|
+
if (!tokens.length) return null;
|
|
7887
|
+
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
7888
|
+
return tokens;
|
|
7889
|
+
}
|
|
7890
|
+
function validateCategory(value) {
|
|
7891
|
+
return typeof value === "string" && [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"].includes(value) ? value : "custom";
|
|
7892
|
+
}
|
|
7893
|
+
function normalizeCommandConfig(entry, source) {
|
|
7894
|
+
if (!isRecord(entry) || typeof entry.command !== "string") {
|
|
7895
|
+
return { rejected: { source, reason: "validation command must be an object with a command string" } };
|
|
7896
|
+
}
|
|
7897
|
+
const commandText = entry.command.trim();
|
|
7898
|
+
const explicitArgs = Array.isArray(entry.args) ? entry.args : void 0;
|
|
7899
|
+
if (explicitArgs && !explicitArgs.every((arg) => typeof arg === "string")) {
|
|
7900
|
+
return { rejected: { source, command: commandText, reason: "args must be an array of strings" } };
|
|
7901
|
+
}
|
|
7902
|
+
let command = commandText;
|
|
7903
|
+
let args = explicitArgs ? [...explicitArgs] : [];
|
|
7904
|
+
if (!explicitArgs) {
|
|
7905
|
+
const tokens = tokenizeCommandString(commandText);
|
|
7906
|
+
if (!tokens) return { rejected: { source, command: commandText, reason: "unsafe command string is not allowlisted" } };
|
|
7907
|
+
command = tokens[0];
|
|
7908
|
+
args = tokens.slice(1);
|
|
7909
|
+
} else if (!tokenizeCommandString(command)) {
|
|
7910
|
+
return { rejected: { source, command: commandText, reason: "unsafe executable name is not allowlisted" } };
|
|
7911
|
+
}
|
|
7912
|
+
if (args.some((arg) => /[\n\r\0]/.test(arg))) {
|
|
7913
|
+
return { rejected: { source, command: commandText, reason: "args cannot contain control characters" } };
|
|
7914
|
+
}
|
|
7915
|
+
if (entry.cwd !== void 0 && typeof entry.cwd !== "string") {
|
|
7916
|
+
return { rejected: { source, command: commandText, reason: "cwd must be a string when provided" } };
|
|
7917
|
+
}
|
|
7918
|
+
if (entry.timeoutMs !== void 0 && (typeof entry.timeoutMs !== "number" || !Number.isFinite(entry.timeoutMs) || entry.timeoutMs < 1e3 || entry.timeoutMs > 6e5)) {
|
|
7919
|
+
return { rejected: { source, command: commandText, reason: "timeoutMs must be between 1000 and 600000" } };
|
|
7920
|
+
}
|
|
7921
|
+
if (entry.env !== void 0 && (!isRecord(entry.env) || !Object.values(entry.env).every((value) => typeof value === "string"))) {
|
|
7922
|
+
return { rejected: { source, command: commandText, reason: "env must be an object of string values" } };
|
|
7923
|
+
}
|
|
7924
|
+
return {
|
|
7925
|
+
command: {
|
|
7926
|
+
command,
|
|
7927
|
+
args,
|
|
7928
|
+
displayCommand: [command, ...args].join(" "),
|
|
7929
|
+
category: validateCategory(entry.category),
|
|
7930
|
+
source,
|
|
7931
|
+
...typeof entry.cwd === "string" && entry.cwd.trim() ? { cwd: entry.cwd.trim() } : {},
|
|
7932
|
+
...typeof entry.timeoutMs === "number" ? { timeoutMs: entry.timeoutMs } : {},
|
|
7933
|
+
...isRecord(entry.env) ? { env: entry.env } : {}
|
|
7934
|
+
}
|
|
7935
|
+
};
|
|
7936
|
+
}
|
|
7937
|
+
function validateMeshRefineConfig(config, source = "inline") {
|
|
7938
|
+
const errors = [];
|
|
7939
|
+
const commands = [];
|
|
7940
|
+
const rejectedCommands = [];
|
|
7941
|
+
if (!isRecord(config)) return { valid: false, errors: ["config must be an object"], commands, rejectedCommands };
|
|
7942
|
+
if (config.version !== 1) errors.push("version must be 1");
|
|
7943
|
+
const validation = config.validation;
|
|
7944
|
+
if (validation !== void 0 && !isRecord(validation)) errors.push("validation must be an object");
|
|
7945
|
+
const rawCommands = isRecord(validation) ? validation.commands : void 0;
|
|
7946
|
+
if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
|
|
7947
|
+
if (Array.isArray(rawCommands)) {
|
|
7948
|
+
rawCommands.forEach((entry, index) => {
|
|
7949
|
+
const normalized = normalizeCommandConfig(entry, `${source}:validation.commands[${index}]`);
|
|
7950
|
+
if (normalized.command) commands.push(normalized.command);
|
|
7951
|
+
if (normalized.rejected) rejectedCommands.push(normalized.rejected);
|
|
7952
|
+
});
|
|
7953
|
+
}
|
|
7954
|
+
if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
|
|
7955
|
+
return { valid: errors.length === 0, errors, commands, rejectedCommands };
|
|
7956
|
+
}
|
|
7957
|
+
function parseConfigText(path28, text) {
|
|
7958
|
+
if (/\.json$/i.test(path28)) return JSON.parse(text);
|
|
7959
|
+
return yaml.load(text);
|
|
7960
|
+
}
|
|
7961
|
+
function loadMeshRefineConfig(mesh, workspace) {
|
|
7962
|
+
const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
|
|
7963
|
+
const inline = mesh?.refineConfig || policy.refineConfig || policy.refine;
|
|
7964
|
+
if (inline !== void 0) {
|
|
7965
|
+
const validation = validateMeshRefineConfig(inline, "mesh.policy.refineConfig");
|
|
7966
|
+
if (!validation.valid) return { source: "mesh.policy.refineConfig", sourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
|
|
7967
|
+
return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
|
|
7968
|
+
}
|
|
7969
|
+
for (const relative3 of MESH_REFINE_CONFIG_LOCATIONS) {
|
|
7970
|
+
const configPath = join5(workspace, relative3);
|
|
7971
|
+
if (!existsSync5(configPath)) continue;
|
|
7972
|
+
try {
|
|
7973
|
+
const parsed = parseConfigText(configPath, readFileSync3(configPath, "utf-8"));
|
|
7974
|
+
const validation = validateMeshRefineConfig(parsed, relative3);
|
|
7975
|
+
if (!validation.valid) return { source: relative3, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
|
|
7976
|
+
return { config: parsed, source: relative3, sourceType: "repo_file", path: configPath };
|
|
7977
|
+
} catch (error) {
|
|
7978
|
+
return { source: relative3, sourceType: "invalid", path: configPath, error: error?.message || String(error) };
|
|
7979
|
+
}
|
|
7980
|
+
}
|
|
7981
|
+
return {
|
|
7982
|
+
source: "unavailable",
|
|
7983
|
+
sourceType: "unavailable",
|
|
7984
|
+
error: `No repo mesh/refine config found. Checked: ${MESH_REFINE_CONFIG_LOCATIONS.join(", ")}`
|
|
7985
|
+
};
|
|
7986
|
+
}
|
|
7987
|
+
function readPackageScripts(workspace) {
|
|
7988
|
+
try {
|
|
7989
|
+
const parsed = JSON.parse(readFileSync3(join5(workspace, "package.json"), "utf-8"));
|
|
7990
|
+
return isRecord(parsed?.scripts) ? parsed.scripts : {};
|
|
7991
|
+
} catch {
|
|
7992
|
+
return {};
|
|
7993
|
+
}
|
|
7994
|
+
}
|
|
7995
|
+
function collectProjectContextSuggestions(mesh) {
|
|
7996
|
+
const commands = mesh?.projectContext?.commands;
|
|
7997
|
+
if (!isRecord(commands)) return [];
|
|
7998
|
+
const suggestions = [];
|
|
7999
|
+
for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
|
|
8000
|
+
const entries = Array.isArray(commands[category]) ? commands[category] : [];
|
|
8001
|
+
for (const entry of entries) {
|
|
8002
|
+
if (isRecord(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
return suggestions;
|
|
8006
|
+
}
|
|
8007
|
+
function collectPackageScriptSuggestions(workspace) {
|
|
8008
|
+
const scripts = readPackageScripts(workspace);
|
|
8009
|
+
const suggestions = [];
|
|
8010
|
+
for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
|
|
8011
|
+
for (const scriptName of Object.keys(scripts)) {
|
|
8012
|
+
if (scriptName === category || scriptName.startsWith(`${category}:`)) {
|
|
8013
|
+
suggestions.push({ command: "npm", args: ["run", scriptName], category });
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
}
|
|
8017
|
+
return suggestions;
|
|
8018
|
+
}
|
|
8019
|
+
function suggestMeshRefineConfig(mesh, workspace) {
|
|
8020
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8021
|
+
const suggestions = [];
|
|
8022
|
+
for (const entry of [...collectProjectContextSuggestions(mesh), ...collectPackageScriptSuggestions(workspace)]) {
|
|
8023
|
+
const key = `${entry.command} ${(entry.args || []).join(" ")}`.trim();
|
|
8024
|
+
if (seen.has(key)) continue;
|
|
8025
|
+
seen.add(key);
|
|
8026
|
+
suggestions.push(entry);
|
|
8027
|
+
}
|
|
8028
|
+
return {
|
|
8029
|
+
suggestions,
|
|
8030
|
+
suggestedConfig: suggestions.length ? { version: 1, validation: { required: true, commands: suggestions.slice(0, 4) } } : void 0
|
|
8031
|
+
};
|
|
8032
|
+
}
|
|
8033
|
+
function resolveMeshRefineValidationPlan(mesh, workspace) {
|
|
8034
|
+
const loaded = loadMeshRefineConfig(mesh, workspace);
|
|
8035
|
+
const suggestion = suggestMeshRefineConfig(mesh, workspace);
|
|
8036
|
+
if (!loaded.config) {
|
|
8037
|
+
return {
|
|
8038
|
+
source: loaded.source,
|
|
8039
|
+
sourceType: loaded.sourceType,
|
|
8040
|
+
commands: [],
|
|
8041
|
+
rejectedCommands: loaded.error ? [{ source: loaded.source, reason: loaded.error }] : [],
|
|
8042
|
+
suggestions: suggestion.suggestions,
|
|
8043
|
+
suggestedConfig: suggestion.suggestedConfig,
|
|
8044
|
+
unavailableReason: loaded.error || "validation_unavailable: repo mesh/refine config missing"
|
|
8045
|
+
};
|
|
8046
|
+
}
|
|
8047
|
+
const validation = validateMeshRefineConfig(loaded.config, loaded.source);
|
|
8048
|
+
return {
|
|
8049
|
+
source: loaded.path || loaded.source,
|
|
8050
|
+
sourceType: loaded.sourceType,
|
|
8051
|
+
commands: validation.commands,
|
|
8052
|
+
rejectedCommands: validation.rejectedCommands,
|
|
8053
|
+
suggestions: suggestion.suggestions,
|
|
8054
|
+
suggestedConfig: suggestion.suggestedConfig,
|
|
8055
|
+
unavailableReason: validation.commands.length ? void 0 : "validation_unavailable: repo mesh/refine config has no validation.commands"
|
|
8056
|
+
};
|
|
8057
|
+
}
|
|
8058
|
+
|
|
7329
8059
|
// src/mesh/mesh-sync.ts
|
|
7330
8060
|
init_mesh_config();
|
|
7331
8061
|
async function syncMeshes(transport) {
|
|
@@ -7445,6 +8175,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
|
|
|
7445
8175
|
|
|
7446
8176
|
// src/index.ts
|
|
7447
8177
|
init_mesh_work_queue();
|
|
8178
|
+
init_mesh_host_ownership();
|
|
7448
8179
|
init_mesh_events();
|
|
7449
8180
|
|
|
7450
8181
|
// src/mesh/p2p-relay-failure.ts
|
|
@@ -7559,8 +8290,8 @@ var P2pRelayFailureError = class extends Error {
|
|
|
7559
8290
|
|
|
7560
8291
|
// src/config/state-store.ts
|
|
7561
8292
|
init_config();
|
|
7562
|
-
import { existsSync as
|
|
7563
|
-
import { join as
|
|
8293
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
8294
|
+
import { join as join11 } from "path";
|
|
7564
8295
|
var DEFAULT_STATE = {
|
|
7565
8296
|
recentActivity: [],
|
|
7566
8297
|
savedProviderSessions: [],
|
|
@@ -7573,7 +8304,7 @@ function isPlainObject2(value) {
|
|
|
7573
8304
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7574
8305
|
}
|
|
7575
8306
|
function getStatePath() {
|
|
7576
|
-
return
|
|
8307
|
+
return join11(getConfigDir(), "state.json");
|
|
7577
8308
|
}
|
|
7578
8309
|
function normalizeState(raw) {
|
|
7579
8310
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -7609,11 +8340,11 @@ function normalizeState(raw) {
|
|
|
7609
8340
|
}
|
|
7610
8341
|
function loadState() {
|
|
7611
8342
|
const statePath = getStatePath();
|
|
7612
|
-
if (!
|
|
8343
|
+
if (!existsSync11(statePath)) {
|
|
7613
8344
|
return { ...DEFAULT_STATE };
|
|
7614
8345
|
}
|
|
7615
8346
|
try {
|
|
7616
|
-
const raw =
|
|
8347
|
+
const raw = readFileSync7(statePath, "utf-8");
|
|
7617
8348
|
return normalizeState(JSON.parse(raw));
|
|
7618
8349
|
} catch {
|
|
7619
8350
|
return { ...DEFAULT_STATE };
|
|
@@ -7630,7 +8361,7 @@ function resetState() {
|
|
|
7630
8361
|
|
|
7631
8362
|
// src/detection/ide-detector.ts
|
|
7632
8363
|
import { execSync } from "child_process";
|
|
7633
|
-
import { existsSync as
|
|
8364
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7634
8365
|
import { platform as platform2, homedir as homedir5 } from "os";
|
|
7635
8366
|
import * as path10 from "path";
|
|
7636
8367
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
@@ -7654,7 +8385,7 @@ function findCliCommand(command) {
|
|
|
7654
8385
|
if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
7655
8386
|
const candidate = trimmed.startsWith("~") ? path10.join(homedir5(), trimmed.slice(1)) : trimmed;
|
|
7656
8387
|
const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
|
|
7657
|
-
return
|
|
8388
|
+
return existsSync12(resolved) ? resolved : null;
|
|
7658
8389
|
}
|
|
7659
8390
|
try {
|
|
7660
8391
|
const result = execSync(
|
|
@@ -7685,9 +8416,9 @@ function checkPathExists(paths) {
|
|
|
7685
8416
|
if (normalized.includes("*")) {
|
|
7686
8417
|
const username = home.split(/[\\/]/).pop() || "";
|
|
7687
8418
|
const resolved = normalized.replace("*", username);
|
|
7688
|
-
if (
|
|
8419
|
+
if (existsSync12(resolved)) return resolved;
|
|
7689
8420
|
} else {
|
|
7690
|
-
if (
|
|
8421
|
+
if (existsSync12(normalized)) return normalized;
|
|
7691
8422
|
}
|
|
7692
8423
|
}
|
|
7693
8424
|
return null;
|
|
@@ -7701,7 +8432,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7701
8432
|
let resolvedCli = cliPath;
|
|
7702
8433
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
7703
8434
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
7704
|
-
if (
|
|
8435
|
+
if (existsSync12(bundledCli)) resolvedCli = bundledCli;
|
|
7705
8436
|
}
|
|
7706
8437
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
7707
8438
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -7714,7 +8445,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7714
8445
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
7715
8446
|
];
|
|
7716
8447
|
for (const c of candidates) {
|
|
7717
|
-
if (
|
|
8448
|
+
if (existsSync12(c)) {
|
|
7718
8449
|
resolvedCli = c;
|
|
7719
8450
|
break;
|
|
7720
8451
|
}
|
|
@@ -9606,7 +10337,8 @@ var StatusMonitor = class {
|
|
|
9606
10337
|
};
|
|
9607
10338
|
|
|
9608
10339
|
// src/providers/chat-message-normalization.ts
|
|
9609
|
-
|
|
10340
|
+
var DEFAULT_FINAL_SUMMARY_MAX_CHARS = 4e3;
|
|
10341
|
+
function extractFinalSummaryFromMessages(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
9610
10342
|
if (!Array.isArray(messages) || messages.length === 0) return "";
|
|
9611
10343
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
9612
10344
|
const msg = messages[i];
|
|
@@ -16869,7 +17601,7 @@ init_config();
|
|
|
16869
17601
|
import * as os13 from "os";
|
|
16870
17602
|
import * as path18 from "path";
|
|
16871
17603
|
import * as crypto4 from "crypto";
|
|
16872
|
-
import { existsSync as
|
|
17604
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
16873
17605
|
import { execFileSync } from "child_process";
|
|
16874
17606
|
import chalk from "chalk";
|
|
16875
17607
|
|
|
@@ -19343,7 +20075,7 @@ function commandExists(command) {
|
|
|
19343
20075
|
const trimmed = command.trim();
|
|
19344
20076
|
if (!trimmed) return false;
|
|
19345
20077
|
if (isExplicitCommand(trimmed)) {
|
|
19346
|
-
return
|
|
20078
|
+
return existsSync16(expandExecutable(trimmed));
|
|
19347
20079
|
}
|
|
19348
20080
|
try {
|
|
19349
20081
|
execFileSync(process.platform === "win32" ? "where" : "which", [trimmed], {
|
|
@@ -22607,15 +23339,15 @@ cleanOldFiles();
|
|
|
22607
23339
|
|
|
22608
23340
|
// src/commands/router.ts
|
|
22609
23341
|
init_logger();
|
|
22610
|
-
import * as
|
|
23342
|
+
import * as yaml2 from "js-yaml";
|
|
22611
23343
|
|
|
22612
23344
|
// src/commands/mesh-coordinator.ts
|
|
22613
23345
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
22614
|
-
import { createHash as
|
|
22615
|
-
import { existsSync as
|
|
23346
|
+
import { createHash as createHash3 } from "crypto";
|
|
23347
|
+
import { existsSync as existsSync19, readdirSync as readdirSync7, realpathSync as realpathSync2 } from "fs";
|
|
22616
23348
|
import { createRequire as createRequire2 } from "module";
|
|
22617
23349
|
import * as os17 from "os";
|
|
22618
|
-
import { dirname as dirname4, isAbsolute as isAbsolute11, join as
|
|
23350
|
+
import { dirname as dirname4, isAbsolute as isAbsolute11, join as join22, resolve as resolve13 } from "path";
|
|
22619
23351
|
var DEFAULT_SERVER_NAME = "adhdev-mesh";
|
|
22620
23352
|
var DEFAULT_ADHDEV_MCP_COMMAND = "adhdev-mcp";
|
|
22621
23353
|
var HERMES_CLI_TYPE = "hermes-cli";
|
|
@@ -22638,7 +23370,7 @@ function resolveHermesMeshCoordinatorSetup(options) {
|
|
|
22638
23370
|
reason: "Could not resolve the ADHDev MCP server entrypoint and a Node runtime with WebSocket support for daemon IPC mode"
|
|
22639
23371
|
};
|
|
22640
23372
|
}
|
|
22641
|
-
const configPath =
|
|
23373
|
+
const configPath = join22(resolveHermesCoordinatorHome(options.meshId, options.workspace), "config.yaml");
|
|
22642
23374
|
if (!configPath.trim()) {
|
|
22643
23375
|
return createHermesManualMeshCoordinatorSetup(options.meshId, options.workspace);
|
|
22644
23376
|
}
|
|
@@ -22758,15 +23490,15 @@ function renderMeshCoordinatorTemplate(template, values) {
|
|
|
22758
23490
|
function resolveHermesCoordinatorHome(meshId, workspace) {
|
|
22759
23491
|
const key = `${meshId || "mesh"}
|
|
22760
23492
|
${resolve13(workspace || os17.tmpdir())}`;
|
|
22761
|
-
const hash =
|
|
22762
|
-
return
|
|
23493
|
+
const hash = createHash3("sha256").update(key).digest("hex").slice(0, 16);
|
|
23494
|
+
return join22(os17.tmpdir(), `adhdev-hermes-mesh-coordinator-${hash}`);
|
|
22763
23495
|
}
|
|
22764
23496
|
function resolveMcpConfigPath(configPath, workspace) {
|
|
22765
23497
|
const trimmed = configPath.trim();
|
|
22766
23498
|
if (trimmed === "~") return os17.homedir();
|
|
22767
|
-
if (trimmed.startsWith("~/")) return
|
|
23499
|
+
if (trimmed.startsWith("~/")) return join22(os17.homedir(), trimmed.slice(2));
|
|
22768
23500
|
if (isAbsolute11(trimmed)) return trimmed;
|
|
22769
|
-
return
|
|
23501
|
+
return join22(workspace, trimmed);
|
|
22770
23502
|
}
|
|
22771
23503
|
function resolveAdhdevMcpServerLaunch(options) {
|
|
22772
23504
|
const entryPath = resolveAdhdevMcpEntryPath(options.adhdevMcpEntryPath);
|
|
@@ -22822,15 +23554,15 @@ function addNodeCandidatesFromPath(pathValue, addCandidate) {
|
|
|
22822
23554
|
for (const entry of (pathValue || "").split(":")) {
|
|
22823
23555
|
const dir = entry.trim();
|
|
22824
23556
|
if (!dir) continue;
|
|
22825
|
-
addCandidate(
|
|
23557
|
+
addCandidate(join22(dir, "node"));
|
|
22826
23558
|
}
|
|
22827
23559
|
}
|
|
22828
23560
|
function addNodeCandidatesFromNvm(homeDir, addCandidate) {
|
|
22829
|
-
const versionsDir =
|
|
23561
|
+
const versionsDir = join22(homeDir, ".nvm", "versions", "node");
|
|
22830
23562
|
try {
|
|
22831
23563
|
const versionDirs = readdirSync7(versionsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort(compareNodeVersionNamesDescending);
|
|
22832
23564
|
for (const versionDir of versionDirs) {
|
|
22833
|
-
addCandidate(
|
|
23565
|
+
addCandidate(join22(versionsDir, versionDir, "bin", "node"));
|
|
22834
23566
|
}
|
|
22835
23567
|
} catch {
|
|
22836
23568
|
}
|
|
@@ -22881,7 +23613,7 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
|
|
|
22881
23613
|
if (normalized) return normalized;
|
|
22882
23614
|
}
|
|
22883
23615
|
try {
|
|
22884
|
-
const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] :
|
|
23616
|
+
const requireBase = process.argv[1] ? normalizeExistingPath(process.argv[1]) || process.argv[1] : join22(process.cwd(), "adhdev-daemon.js");
|
|
22885
23617
|
const req = createRequire2(requireBase);
|
|
22886
23618
|
const resolvedModule = req.resolve("@adhdev/mcp-server");
|
|
22887
23619
|
return normalizeExistingPath(resolvedModule) || resolvedModule;
|
|
@@ -22891,7 +23623,7 @@ function resolveAdhdevMcpEntryPath(explicitPath) {
|
|
|
22891
23623
|
}
|
|
22892
23624
|
function normalizeExistingPath(filePath) {
|
|
22893
23625
|
try {
|
|
22894
|
-
if (!
|
|
23626
|
+
if (!existsSync19(filePath)) return null;
|
|
22895
23627
|
return realpathSync2.native(filePath);
|
|
22896
23628
|
} catch {
|
|
22897
23629
|
return null;
|
|
@@ -22900,6 +23632,7 @@ function normalizeExistingPath(filePath) {
|
|
|
22900
23632
|
|
|
22901
23633
|
// src/commands/router.ts
|
|
22902
23634
|
init_mesh_events();
|
|
23635
|
+
init_mesh_host_ownership();
|
|
22903
23636
|
|
|
22904
23637
|
// src/status/snapshot.ts
|
|
22905
23638
|
init_config();
|
|
@@ -23603,13 +24336,85 @@ function readBooleanValue(...values) {
|
|
|
23603
24336
|
}
|
|
23604
24337
|
return void 0;
|
|
23605
24338
|
}
|
|
23606
|
-
function
|
|
24339
|
+
function summarizeRepoMeshDebugGit(git) {
|
|
24340
|
+
const record = readObjectRecord(git);
|
|
24341
|
+
if (!Object.keys(record).length) return null;
|
|
24342
|
+
const submodules = Array.isArray(record.submodules) ? record.submodules.map((entry) => ({
|
|
24343
|
+
path: readStringValue(entry?.path) ?? null,
|
|
24344
|
+
commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
|
|
24345
|
+
dirty: readBooleanValue(entry?.dirty) ?? false,
|
|
24346
|
+
outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false
|
|
24347
|
+
})) : [];
|
|
24348
|
+
return {
|
|
24349
|
+
isGitRepo: readBooleanValue(record.isGitRepo),
|
|
24350
|
+
workspace: readStringValue(record.workspace) ?? null,
|
|
24351
|
+
repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
|
|
24352
|
+
branch: readStringValue(record.branch) ?? null,
|
|
24353
|
+
upstream: readStringValue(record.upstream) ?? null,
|
|
24354
|
+
upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
|
|
24355
|
+
headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
|
|
24356
|
+
ahead: readNumberValue(record.ahead) ?? null,
|
|
24357
|
+
behind: readNumberValue(record.behind) ?? null,
|
|
24358
|
+
dirtyCounts: {
|
|
24359
|
+
staged: readNumberValue(record.staged) ?? 0,
|
|
24360
|
+
modified: readNumberValue(record.modified) ?? 0,
|
|
24361
|
+
untracked: readNumberValue(record.untracked) ?? 0,
|
|
24362
|
+
deleted: readNumberValue(record.deleted) ?? 0,
|
|
24363
|
+
renamed: readNumberValue(record.renamed) ?? 0
|
|
24364
|
+
},
|
|
24365
|
+
lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
|
|
24366
|
+
submoduleCount: submodules.length,
|
|
24367
|
+
submodules
|
|
24368
|
+
};
|
|
24369
|
+
}
|
|
24370
|
+
function summarizeRepoMeshStatusDebug(status) {
|
|
24371
|
+
const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
|
|
24372
|
+
return {
|
|
24373
|
+
success: status?.success,
|
|
24374
|
+
meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
|
|
24375
|
+
refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
|
|
24376
|
+
sourceOfTruth: status?.sourceOfTruth ?? null,
|
|
24377
|
+
nodeCount: nodes.length,
|
|
24378
|
+
nodes: nodes.map((node) => ({
|
|
24379
|
+
nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
|
|
24380
|
+
daemonId: readStringValue(node?.daemonId, node?.daemon_id) ?? null,
|
|
24381
|
+
workspace: readStringValue(node?.workspace, node?.git?.workspace) ?? null,
|
|
24382
|
+
health: readStringValue(node?.health) ?? null,
|
|
24383
|
+
machineStatus: readStringValue(node?.machineStatus, node?.machine_status) ?? null,
|
|
24384
|
+
connection: node?.connection && typeof node.connection === "object" ? {
|
|
24385
|
+
state: readStringValue(node.connection.state) ?? null,
|
|
24386
|
+
transport: readStringValue(node.connection.transport) ?? null,
|
|
24387
|
+
source: readStringValue(node.connection.source) ?? null,
|
|
24388
|
+
reported: readBooleanValue(node.connection.reported) ?? null
|
|
24389
|
+
} : null,
|
|
24390
|
+
gitProbePending: node?.gitProbePending === true,
|
|
24391
|
+
launchReady: node?.launchReady === true,
|
|
24392
|
+
git: summarizeRepoMeshDebugGit(node?.git)
|
|
24393
|
+
}))
|
|
24394
|
+
};
|
|
24395
|
+
}
|
|
24396
|
+
function logRepoMeshStatusDebug(event, fields) {
|
|
24397
|
+
try {
|
|
24398
|
+
LOG.info("MeshStatusDebug", `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
|
|
24399
|
+
} catch {
|
|
24400
|
+
LOG.info("MeshStatusDebug", `[RepoMeshStatusDebug] ${event}`);
|
|
24401
|
+
}
|
|
24402
|
+
}
|
|
24403
|
+
function joinRepoPath(root, relativePath) {
|
|
24404
|
+
const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
|
|
24405
|
+
const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
|
|
24406
|
+
if (!normalizedPath) return void 0;
|
|
24407
|
+
if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
|
|
24408
|
+
if (!normalizedRoot) return void 0;
|
|
24409
|
+
return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
|
|
24410
|
+
}
|
|
24411
|
+
function readGitSubmodules(value, parentRepoRoot) {
|
|
23607
24412
|
if (!Array.isArray(value)) return void 0;
|
|
23608
24413
|
const submodules = value.map((entry) => {
|
|
23609
24414
|
const submodule = readObjectRecord(entry);
|
|
23610
24415
|
const path28 = readStringValue(submodule.path);
|
|
23611
24416
|
const commit = readStringValue(submodule.commit);
|
|
23612
|
-
const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
|
|
24417
|
+
const repoPath = readStringValue(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path28);
|
|
23613
24418
|
if (!path28 || !commit || !repoPath) return null;
|
|
23614
24419
|
return {
|
|
23615
24420
|
path: path28,
|
|
@@ -23623,58 +24428,17 @@ function readGitSubmodules(value) {
|
|
|
23623
24428
|
}).filter((entry) => entry !== null);
|
|
23624
24429
|
return submodules.length > 0 ? submodules : void 0;
|
|
23625
24430
|
}
|
|
23626
|
-
function
|
|
23627
|
-
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
23628
|
-
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
23629
|
-
if (Object.keys(cachedGit).length) {
|
|
23630
|
-
const conflictFiles2 = Array.isArray(cachedGit.conflictFiles) ? cachedGit.conflictFiles.filter((value) => typeof value === "string") : [];
|
|
23631
|
-
const conflictCount2 = readNumberValue(cachedGit.conflicts) ?? conflictFiles2.length;
|
|
23632
|
-
const hasConflicts2 = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount2 > 0;
|
|
23633
|
-
const isGitRepo2 = readBooleanValue(cachedGit.isGitRepo);
|
|
23634
|
-
if (isGitRepo2 !== void 0) {
|
|
23635
|
-
const submodules2 = readGitSubmodules(cachedGit.submodules);
|
|
23636
|
-
return {
|
|
23637
|
-
workspace: readStringValue(cachedGit.workspace, node?.workspace) || "",
|
|
23638
|
-
repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
|
|
23639
|
-
isGitRepo: isGitRepo2,
|
|
23640
|
-
branch: readStringValue(cachedGit.branch) ?? null,
|
|
23641
|
-
headCommit: readStringValue(cachedGit.headCommit) ?? null,
|
|
23642
|
-
headMessage: readStringValue(cachedGit.headMessage) ?? null,
|
|
23643
|
-
upstream: readStringValue(cachedGit.upstream) ?? null,
|
|
23644
|
-
ahead: readNumberValue(cachedGit.ahead) ?? 0,
|
|
23645
|
-
behind: readNumberValue(cachedGit.behind) ?? 0,
|
|
23646
|
-
staged: readNumberValue(cachedGit.staged) ?? 0,
|
|
23647
|
-
modified: readNumberValue(cachedGit.modified) ?? 0,
|
|
23648
|
-
untracked: readNumberValue(cachedGit.untracked) ?? 0,
|
|
23649
|
-
deleted: readNumberValue(cachedGit.deleted) ?? 0,
|
|
23650
|
-
renamed: readNumberValue(cachedGit.renamed) ?? 0,
|
|
23651
|
-
hasConflicts: hasConflicts2,
|
|
23652
|
-
conflictFiles: conflictFiles2,
|
|
23653
|
-
stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
|
|
23654
|
-
lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now(),
|
|
23655
|
-
...submodules2 ? { submodules: submodules2 } : {}
|
|
23656
|
-
};
|
|
23657
|
-
}
|
|
23658
|
-
}
|
|
23659
|
-
const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
|
|
23660
|
-
const gitResult = readObjectRecord(rawGit.result);
|
|
23661
|
-
const directStatus = readObjectRecord(rawGit.status);
|
|
23662
|
-
const nestedStatus = readObjectRecord(gitResult.status);
|
|
23663
|
-
const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
|
|
23664
|
-
const probeGit = readObjectRecord(rawProbe.git);
|
|
23665
|
-
const probeGitResult = readObjectRecord(probeGit.result);
|
|
23666
|
-
const probeDirectStatus = readObjectRecord(probeGit.status);
|
|
23667
|
-
const probeNestedStatus = readObjectRecord(probeGitResult.status);
|
|
23668
|
-
const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
|
|
24431
|
+
function normalizeInlineMeshGitStatus(status, node, options) {
|
|
23669
24432
|
const isGitRepo = readBooleanValue(status.isGitRepo);
|
|
23670
24433
|
if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
|
|
23671
24434
|
const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((value) => typeof value === "string") : [];
|
|
23672
24435
|
const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
|
|
23673
24436
|
const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
|
|
23674
|
-
const
|
|
24437
|
+
const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || void 0;
|
|
24438
|
+
const submodules = readGitSubmodules(status.submodules, repoRoot);
|
|
23675
24439
|
return {
|
|
23676
24440
|
workspace: readStringValue(status.workspace, node?.workspace) || "",
|
|
23677
|
-
repoRoot:
|
|
24441
|
+
repoRoot: repoRoot ?? null,
|
|
23678
24442
|
isGitRepo,
|
|
23679
24443
|
branch: readStringValue(status.branch) ?? null,
|
|
23680
24444
|
headCommit: readStringValue(status.headCommit) ?? null,
|
|
@@ -23690,30 +24454,456 @@ function buildCachedInlineMeshGitStatus(node) {
|
|
|
23690
24454
|
hasConflicts,
|
|
23691
24455
|
conflictFiles,
|
|
23692
24456
|
stashCount: readNumberValue(status.stashCount) ?? 0,
|
|
23693
|
-
lastCheckedAt: Date.now(),
|
|
24457
|
+
lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
|
|
23694
24458
|
...submodules ? { submodules } : {}
|
|
23695
24459
|
};
|
|
23696
24460
|
}
|
|
23697
|
-
function
|
|
24461
|
+
function scoreInlineMeshGitStatus(git) {
|
|
24462
|
+
if (!git) return Number.NEGATIVE_INFINITY;
|
|
24463
|
+
let score = 0;
|
|
24464
|
+
if (readBooleanValue(git.isGitRepo) === true) score += 50;
|
|
24465
|
+
if (readBooleanValue(git.isGitRepo) === false) score -= 10;
|
|
24466
|
+
if (readStringValue(git.branch)) score += 20;
|
|
24467
|
+
if (readStringValue(git.headCommit)) score += 20;
|
|
24468
|
+
if (readStringValue(git.upstream)) score += 10;
|
|
24469
|
+
if (readStringValue(git.upstreamStatus)) score += 5;
|
|
24470
|
+
if (readNumberValue(git.ahead) !== void 0) score += 2;
|
|
24471
|
+
if (readNumberValue(git.behind) !== void 0) score += 2;
|
|
24472
|
+
if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
|
|
24473
|
+
if (readStringValue(git.error)) score -= 20;
|
|
24474
|
+
return score;
|
|
24475
|
+
}
|
|
24476
|
+
function buildInlineMeshTransitGitStatus(node) {
|
|
24477
|
+
const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
|
|
24478
|
+
const gitResult = readObjectRecord(rawGit.result);
|
|
24479
|
+
const directStatus = readObjectRecord(rawGit.status);
|
|
24480
|
+
const nestedStatus = readObjectRecord(gitResult.status);
|
|
24481
|
+
const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
|
|
24482
|
+
const probeGit = readObjectRecord(rawProbe.git);
|
|
24483
|
+
const probeGitResult = readObjectRecord(probeGit.result);
|
|
24484
|
+
const probeDirectStatus = readObjectRecord(probeGit.status);
|
|
24485
|
+
const probeNestedStatus = readObjectRecord(probeGitResult.status);
|
|
24486
|
+
const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
|
|
24487
|
+
let best = null;
|
|
24488
|
+
for (const status of candidates) {
|
|
24489
|
+
const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
|
|
24490
|
+
if (!normalized) continue;
|
|
24491
|
+
const score = scoreInlineMeshGitStatus(normalized);
|
|
24492
|
+
if (!best || score > best.score) best = { git: normalized, score };
|
|
24493
|
+
}
|
|
24494
|
+
return best?.git;
|
|
24495
|
+
}
|
|
24496
|
+
function shouldRefreshStalePendingAggregate(snapshot, options) {
|
|
24497
|
+
if (options?.requireDirectPeerTruth !== true || !Array.isArray(snapshot?.nodes)) return false;
|
|
24498
|
+
return snapshot.nodes.some((node) => {
|
|
24499
|
+
if (node?.gitProbePending !== true) return false;
|
|
24500
|
+
const git = readObjectRecord(node?.git);
|
|
24501
|
+
return !readBooleanValue(git.isGitRepo) && !readStringValue(git.branch, git.headCommit, git.upstream);
|
|
24502
|
+
});
|
|
24503
|
+
}
|
|
24504
|
+
function buildLivePeerGitConnection(connection, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
24505
|
+
const source = readStringValue(connection.source);
|
|
24506
|
+
const transport = readStringValue(connection.transport);
|
|
24507
|
+
return {
|
|
24508
|
+
...connection,
|
|
24509
|
+
perspective: readStringValue(connection.perspective) ?? "selected_coordinator",
|
|
24510
|
+
source: source && source !== "not_reported" ? source : "mesh_peer_status",
|
|
24511
|
+
state: "connected",
|
|
24512
|
+
transport: transport && transport !== "unknown" ? transport : "direct",
|
|
24513
|
+
reported: true,
|
|
24514
|
+
reason: "Live peer git snapshot reported by the selected coordinator.",
|
|
24515
|
+
lastStateChangeAt: readStringValue(connection.lastStateChangeAt) ?? timestamp
|
|
24516
|
+
};
|
|
24517
|
+
}
|
|
24518
|
+
function recordInlineMeshDirectGitTruth(node, git, source) {
|
|
24519
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return;
|
|
24520
|
+
const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
|
|
24521
|
+
const updatedAt = new Date(checkedAt).toISOString();
|
|
24522
|
+
const nextGit = {
|
|
24523
|
+
...git,
|
|
24524
|
+
lastCheckedAt: checkedAt
|
|
24525
|
+
};
|
|
24526
|
+
node.lastGit = {
|
|
24527
|
+
source,
|
|
24528
|
+
checkedAt,
|
|
24529
|
+
status: nextGit
|
|
24530
|
+
};
|
|
24531
|
+
node.last_git = node.lastGit;
|
|
24532
|
+
node.machineStatus = "online";
|
|
24533
|
+
node.updatedAt = updatedAt;
|
|
24534
|
+
node.lastSeenAt = updatedAt;
|
|
24535
|
+
const repoRoot = readStringValue(nextGit.repoRoot);
|
|
24536
|
+
if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
|
|
24537
|
+
}
|
|
24538
|
+
function buildCachedInlineMeshGitStatus(node) {
|
|
24539
|
+
const liveGit = buildInlineMeshTransitGitStatus(node);
|
|
24540
|
+
if (liveGit) return liveGit;
|
|
24541
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24542
|
+
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
24543
|
+
if (!Object.keys(cachedGit).length) return void 0;
|
|
24544
|
+
return normalizeInlineMeshGitStatus(cachedGit, node);
|
|
24545
|
+
}
|
|
24546
|
+
function shouldDiscardCachedInlineMeshStatus(node) {
|
|
24547
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24548
|
+
if (!Object.keys(cachedStatus).length) return false;
|
|
24549
|
+
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
24550
|
+
const workspaceError = readStringValue(cachedStatus.error, node?.error);
|
|
24551
|
+
if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
|
|
24552
|
+
const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
|
|
24553
|
+
const branch = readStringValue(cachedGit.branch);
|
|
24554
|
+
const headCommit = readStringValue(cachedGit.headCommit);
|
|
24555
|
+
return isGitRepo === false && !branch && !headCommit;
|
|
24556
|
+
}
|
|
24557
|
+
function stripInlineMeshTransientNodeState(node) {
|
|
24558
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return node;
|
|
24559
|
+
const {
|
|
24560
|
+
cachedStatus,
|
|
24561
|
+
lastGit: _lastGit,
|
|
24562
|
+
last_git: _lastGitLegacy,
|
|
24563
|
+
lastProbe: _lastProbe,
|
|
24564
|
+
last_probe: _lastProbeLegacy,
|
|
24565
|
+
error: _error,
|
|
24566
|
+
health: _health,
|
|
24567
|
+
machineStatus: _machineStatus,
|
|
24568
|
+
lastSeenAt: _lastSeenAt,
|
|
24569
|
+
last_seen_at: _lastSeenAtLegacy,
|
|
24570
|
+
updatedAt: _updatedAt,
|
|
24571
|
+
updated_at: _updatedAtLegacy,
|
|
24572
|
+
activeSession: _activeSession,
|
|
24573
|
+
active_session: _activeSessionLegacy,
|
|
24574
|
+
activeSessionId: _activeSessionId,
|
|
24575
|
+
active_session_id: _activeSessionIdLegacy,
|
|
24576
|
+
sessionId: _sessionId,
|
|
24577
|
+
session_id: _sessionIdLegacy,
|
|
24578
|
+
providerType: _providerType,
|
|
24579
|
+
provider_type: _providerTypeLegacy,
|
|
24580
|
+
...rest
|
|
24581
|
+
} = node;
|
|
24582
|
+
if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
|
|
24583
|
+
return { ...rest, cachedStatus };
|
|
24584
|
+
}
|
|
24585
|
+
return rest;
|
|
24586
|
+
}
|
|
24587
|
+
function hasInlineMeshTransientNodeState(node) {
|
|
24588
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return false;
|
|
24589
|
+
return "cachedStatus" in node || "lastGit" in node || "last_git" in node || "lastProbe" in node || "last_probe" in node || "error" in node || "health" in node || "machineStatus" in node || "lastSeenAt" in node || "last_seen_at" in node || "updatedAt" in node || "updated_at" in node || "activeSession" in node || "active_session" in node || "activeSessionId" in node || "active_session_id" in node || "sessionId" in node || "session_id" in node || "providerType" in node || "provider_type" in node;
|
|
24590
|
+
}
|
|
24591
|
+
function inlineMeshCarriesTransientNodeTruth(inlineMesh) {
|
|
24592
|
+
if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return false;
|
|
24593
|
+
if (!Array.isArray(inlineMesh.nodes) || inlineMesh.nodes.length === 0) return false;
|
|
24594
|
+
return inlineMesh.nodes.some((node) => hasInlineMeshTransientNodeState(node));
|
|
24595
|
+
}
|
|
24596
|
+
function readInlineMeshNodeId(node) {
|
|
24597
|
+
return readStringValue(node?.id, node?.nodeId) || "";
|
|
24598
|
+
}
|
|
24599
|
+
function sanitizeInlineMesh(inlineMesh) {
|
|
24600
|
+
if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return inlineMesh;
|
|
24601
|
+
if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
|
|
24602
|
+
let changed = false;
|
|
24603
|
+
const nodes = inlineMesh.nodes.map((node) => {
|
|
24604
|
+
if (!hasInlineMeshTransientNodeState(node)) return node;
|
|
24605
|
+
changed = true;
|
|
24606
|
+
return stripInlineMeshTransientNodeState(node);
|
|
24607
|
+
});
|
|
24608
|
+
if (!changed) return inlineMesh;
|
|
24609
|
+
return {
|
|
24610
|
+
...inlineMesh,
|
|
24611
|
+
nodes
|
|
24612
|
+
};
|
|
24613
|
+
}
|
|
24614
|
+
function reconcileInlineMeshCache(cached, incoming) {
|
|
24615
|
+
if (!cached || typeof cached !== "object" || Array.isArray(cached)) return incoming;
|
|
24616
|
+
if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached;
|
|
24617
|
+
const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
|
|
24618
|
+
const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
|
|
24619
|
+
if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
|
|
24620
|
+
const incomingById = /* @__PURE__ */ new Map();
|
|
24621
|
+
for (const node of incomingNodes) {
|
|
24622
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
24623
|
+
if (nodeId) incomingById.set(nodeId, node);
|
|
24624
|
+
}
|
|
24625
|
+
const nodes = cachedNodes.map((cachedNode) => {
|
|
24626
|
+
const nodeId = readInlineMeshNodeId(cachedNode);
|
|
24627
|
+
const incomingNode = nodeId ? incomingById.get(nodeId) : void 0;
|
|
24628
|
+
if (!incomingNode) return cachedNode;
|
|
24629
|
+
if (hasInlineMeshTransientNodeState(incomingNode)) {
|
|
24630
|
+
return { ...cachedNode, ...incomingNode };
|
|
24631
|
+
}
|
|
24632
|
+
return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
|
|
24633
|
+
});
|
|
24634
|
+
return {
|
|
24635
|
+
...cached,
|
|
24636
|
+
...incoming,
|
|
24637
|
+
nodes
|
|
24638
|
+
};
|
|
24639
|
+
}
|
|
24640
|
+
function hasGitWorktreeChanges(git) {
|
|
24641
|
+
if (!git) return false;
|
|
24642
|
+
return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
|
|
24643
|
+
}
|
|
24644
|
+
function getGitSubmoduleDriftState(git) {
|
|
24645
|
+
const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
|
|
24646
|
+
let dirty = false;
|
|
24647
|
+
let outOfSync = false;
|
|
24648
|
+
for (const entry of submodules) {
|
|
24649
|
+
const submodule = readObjectRecord(entry);
|
|
24650
|
+
if (readBooleanValue(submodule.dirty) === true) dirty = true;
|
|
24651
|
+
if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
|
|
24652
|
+
}
|
|
24653
|
+
return { dirty, outOfSync };
|
|
24654
|
+
}
|
|
24655
|
+
function deriveMeshNodeHealthFromGit(git) {
|
|
24656
|
+
if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
|
|
24657
|
+
const branch = readStringValue(git.branch);
|
|
24658
|
+
if (!branch) return "degraded";
|
|
24659
|
+
const submoduleDrift = getGitSubmoduleDriftState(git);
|
|
24660
|
+
if (submoduleDrift.outOfSync) return "degraded";
|
|
24661
|
+
if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return "dirty";
|
|
24662
|
+
return "online";
|
|
24663
|
+
}
|
|
24664
|
+
function readCachedInlineMeshActiveSessions(node) {
|
|
24665
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24666
|
+
const activeSession = readObjectRecord(cachedStatus.activeSession);
|
|
24667
|
+
const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
|
|
24668
|
+
const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
|
|
24669
|
+
return sessionId ? [sessionId] : [];
|
|
24670
|
+
}
|
|
24671
|
+
function readCachedInlineMeshActiveSessionDetails(node) {
|
|
24672
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24673
|
+
const activeSession = readObjectRecord(cachedStatus.activeSession);
|
|
24674
|
+
const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
|
|
24675
|
+
const sessionId = readStringValue(
|
|
24676
|
+
fallbackSession.id,
|
|
24677
|
+
fallbackSession.sessionId,
|
|
24678
|
+
fallbackSession.session_id,
|
|
24679
|
+
node?.activeSessionId,
|
|
24680
|
+
node?.active_session_id,
|
|
24681
|
+
node?.sessionId,
|
|
24682
|
+
node?.session_id
|
|
24683
|
+
);
|
|
24684
|
+
if (!sessionId) return [];
|
|
24685
|
+
return [{
|
|
24686
|
+
sessionId,
|
|
24687
|
+
providerType: readStringValue(
|
|
24688
|
+
fallbackSession.providerType,
|
|
24689
|
+
fallbackSession.provider_type,
|
|
24690
|
+
fallbackSession.cliType,
|
|
24691
|
+
fallbackSession.cli_type,
|
|
24692
|
+
fallbackSession.provider,
|
|
24693
|
+
node?.providerType,
|
|
24694
|
+
node?.provider_type
|
|
24695
|
+
),
|
|
24696
|
+
state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
|
|
24697
|
+
lifecycle: readStringValue(fallbackSession.lifecycle),
|
|
24698
|
+
title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
|
|
24699
|
+
workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
|
|
24700
|
+
lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
|
|
24701
|
+
recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
|
|
24702
|
+
isCached: true
|
|
24703
|
+
}];
|
|
24704
|
+
}
|
|
24705
|
+
function readLiveMeshSessionState(record) {
|
|
24706
|
+
return readStringValue(
|
|
24707
|
+
record?.meta?.sessionStatus,
|
|
24708
|
+
record?.meta?.status,
|
|
24709
|
+
record?.meta?.providerStatus,
|
|
24710
|
+
record?.status,
|
|
24711
|
+
record?.state,
|
|
24712
|
+
record?.lifecycle
|
|
24713
|
+
);
|
|
24714
|
+
}
|
|
24715
|
+
function toIsoTimestamp(value) {
|
|
24716
|
+
if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
|
|
24717
|
+
const stringValue = readStringValue(value);
|
|
24718
|
+
return stringValue || null;
|
|
24719
|
+
}
|
|
24720
|
+
function synthesizeMeshNodeFreshnessFromConnection(status) {
|
|
24721
|
+
const connection = readObjectRecord(status.connection);
|
|
24722
|
+
const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
|
|
24723
|
+
const git = readObjectRecord(status.git);
|
|
24724
|
+
const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
|
|
24725
|
+
if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
|
|
24726
|
+
if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
|
|
24727
|
+
status.updatedAt = gitCheckedAt ?? connectionFreshAt;
|
|
24728
|
+
}
|
|
24729
|
+
}
|
|
24730
|
+
function finalizeMeshNodeStatus(args) {
|
|
24731
|
+
const { status, node, daemonId, isSelfNode } = args;
|
|
24732
|
+
if (!readStringValue(status.machineStatus)) {
|
|
24733
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24734
|
+
const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
|
|
24735
|
+
if (machineStatus) status.machineStatus = machineStatus;
|
|
24736
|
+
}
|
|
24737
|
+
synthesizeMeshNodeFreshnessFromConnection(status);
|
|
24738
|
+
const connectionState = readStringValue(readObjectRecord(status.connection).state);
|
|
24739
|
+
status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || connectionState === "connected" || isSelfNode);
|
|
24740
|
+
}
|
|
24741
|
+
async function probeRemoteMeshGitStatus(args) {
|
|
24742
|
+
if (!args.dispatchMeshCommand) return null;
|
|
24743
|
+
const remoteResult = await Promise.race([
|
|
24744
|
+
args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace }),
|
|
24745
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), args.timeoutMs))
|
|
24746
|
+
]);
|
|
24747
|
+
const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
|
|
24748
|
+
return remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean" ? remoteGit : null;
|
|
24749
|
+
}
|
|
24750
|
+
async function hydrateInlineMeshDirectTruth(args) {
|
|
24751
|
+
const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
|
|
24752
|
+
if (!nodes.length) {
|
|
24753
|
+
return {
|
|
24754
|
+
directEvidenceCount: 0,
|
|
24755
|
+
localConfirmedCount: 0,
|
|
24756
|
+
peerAttemptedCount: 0,
|
|
24757
|
+
peerConfirmedCount: 0,
|
|
24758
|
+
unavailableNodeIds: []
|
|
24759
|
+
};
|
|
24760
|
+
}
|
|
24761
|
+
const selectedCoordinatorNodeId = readStringValue(
|
|
24762
|
+
args.mesh?.coordinator?.preferredNodeId,
|
|
24763
|
+
nodes[0]?.id,
|
|
24764
|
+
nodes[0]?.nodeId
|
|
24765
|
+
);
|
|
24766
|
+
let localConfirmedCount = 0;
|
|
24767
|
+
let peerAttemptedCount = 0;
|
|
24768
|
+
let peerConfirmedCount = 0;
|
|
24769
|
+
const unavailableNodeIds = [];
|
|
24770
|
+
for (const [nodeIndex, node] of nodes.entries()) {
|
|
24771
|
+
const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
|
|
24772
|
+
const workspace = readStringValue(node?.workspace);
|
|
24773
|
+
const daemonId = readStringValue(node?.daemonId);
|
|
24774
|
+
const isSelfNode = Boolean(
|
|
24775
|
+
nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
|
|
24776
|
+
) || Boolean(
|
|
24777
|
+
daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId)
|
|
24778
|
+
) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
|
|
24779
|
+
if (!workspace) {
|
|
24780
|
+
if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
|
|
24781
|
+
continue;
|
|
24782
|
+
}
|
|
24783
|
+
if (isSelfNode && fs10.existsSync(workspace)) {
|
|
24784
|
+
try {
|
|
24785
|
+
const localGit = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
|
|
24786
|
+
if (localGit?.isGitRepo) {
|
|
24787
|
+
recordInlineMeshDirectGitTruth(node, localGit, "selected_coordinator_local_git");
|
|
24788
|
+
localConfirmedCount += 1;
|
|
24789
|
+
continue;
|
|
24790
|
+
}
|
|
24791
|
+
} catch {
|
|
24792
|
+
}
|
|
24793
|
+
}
|
|
24794
|
+
if (!daemonId || !args.dispatchMeshCommand) {
|
|
24795
|
+
if (!isSelfNode) unavailableNodeIds.push(nodeId);
|
|
24796
|
+
continue;
|
|
24797
|
+
}
|
|
24798
|
+
peerAttemptedCount += 1;
|
|
24799
|
+
try {
|
|
24800
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
24801
|
+
dispatchMeshCommand: args.dispatchMeshCommand,
|
|
24802
|
+
daemonId,
|
|
24803
|
+
workspace,
|
|
24804
|
+
timeoutMs: 8e3
|
|
24805
|
+
});
|
|
24806
|
+
if (remoteGit) {
|
|
24807
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
24808
|
+
peerConfirmedCount += 1;
|
|
24809
|
+
continue;
|
|
24810
|
+
}
|
|
24811
|
+
} catch {
|
|
24812
|
+
}
|
|
24813
|
+
unavailableNodeIds.push(nodeId);
|
|
24814
|
+
}
|
|
24815
|
+
return {
|
|
24816
|
+
directEvidenceCount: localConfirmedCount + peerConfirmedCount,
|
|
24817
|
+
localConfirmedCount,
|
|
24818
|
+
peerAttemptedCount,
|
|
24819
|
+
peerConfirmedCount,
|
|
24820
|
+
unavailableNodeIds
|
|
24821
|
+
};
|
|
24822
|
+
}
|
|
24823
|
+
function summarizeMeshSessionRecord(record) {
|
|
24824
|
+
return {
|
|
24825
|
+
sessionId: readStringValue(record?.sessionId) || "unknown",
|
|
24826
|
+
providerType: readStringValue(record?.providerType),
|
|
24827
|
+
state: readLiveMeshSessionState(record),
|
|
24828
|
+
lifecycle: readStringValue(record?.lifecycle),
|
|
24829
|
+
surfaceKind: getSessionHostSurfaceKind(record),
|
|
24830
|
+
recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
|
|
24831
|
+
workspace: readStringValue(record?.workspace) ?? null,
|
|
24832
|
+
title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
|
|
24833
|
+
lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
|
|
24834
|
+
isCached: false
|
|
24835
|
+
};
|
|
24836
|
+
}
|
|
24837
|
+
function liveSessionRecordMatchesMeshNode(record, meshId, nodeId) {
|
|
24838
|
+
const recordNodeId = readStringValue(record?.meta?.meshNodeId);
|
|
24839
|
+
if (!recordNodeId || recordNodeId !== nodeId) return false;
|
|
24840
|
+
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
24841
|
+
return !recordMeshId || recordMeshId === meshId;
|
|
24842
|
+
}
|
|
24843
|
+
function liveSessionRecordMatchesMeshWorkspace(record, meshId, workspace) {
|
|
24844
|
+
const recordWorkspace = readStringValue(record?.workspace);
|
|
24845
|
+
if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
|
|
24846
|
+
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
24847
|
+
if (recordMeshId) return recordMeshId === meshId;
|
|
24848
|
+
return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
|
|
24849
|
+
}
|
|
24850
|
+
function readLiveMeshNodeWorkspace(args) {
|
|
24851
|
+
const directNodeWorkspace = args.liveSessionRecords.find((record) => liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId) && readStringValue(record?.workspace));
|
|
24852
|
+
if (directNodeWorkspace) {
|
|
24853
|
+
return readStringValue(directNodeWorkspace.workspace) || "";
|
|
24854
|
+
}
|
|
24855
|
+
if (args.allowCoordinatorSession) {
|
|
24856
|
+
const coordinatorWorkspace = args.liveSessionRecords.find((record) => readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId && readStringValue(record?.workspace));
|
|
24857
|
+
if (coordinatorWorkspace) {
|
|
24858
|
+
return readStringValue(coordinatorWorkspace.workspace) || "";
|
|
24859
|
+
}
|
|
24860
|
+
}
|
|
24861
|
+
return "";
|
|
24862
|
+
}
|
|
24863
|
+
function collectLiveMeshSessionRecords(args) {
|
|
24864
|
+
const matches = args.liveSessionRecords.filter((record) => {
|
|
24865
|
+
const nodeWorkspace = readStringValue(args.node?.workspace);
|
|
24866
|
+
if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)) return true;
|
|
24867
|
+
return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
|
|
24868
|
+
});
|
|
24869
|
+
if (args.allowCoordinatorSession) {
|
|
24870
|
+
for (const record of args.liveSessionRecords) {
|
|
24871
|
+
if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
|
|
24872
|
+
const sessionId = readStringValue(record?.sessionId);
|
|
24873
|
+
if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
|
|
24874
|
+
matches.push(record);
|
|
24875
|
+
}
|
|
24876
|
+
}
|
|
24877
|
+
return matches;
|
|
24878
|
+
}
|
|
24879
|
+
function applyCachedInlineMeshNodeStatus(status, node, options) {
|
|
23698
24880
|
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
23699
|
-
const
|
|
23700
|
-
const
|
|
23701
|
-
const
|
|
24881
|
+
const liveGit = buildInlineMeshTransitGitStatus(node);
|
|
24882
|
+
const git = options?.skipGit ? void 0 : liveGit ?? buildCachedInlineMeshGitStatus(node);
|
|
24883
|
+
const error = options?.skipError ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.error, node?.error);
|
|
24884
|
+
const health = options?.skipHealth ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.health, node?.health);
|
|
23702
24885
|
const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
|
|
23703
|
-
|
|
23704
|
-
|
|
24886
|
+
const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
|
|
24887
|
+
const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
|
|
24888
|
+
const activeSessions = readCachedInlineMeshActiveSessions(node);
|
|
24889
|
+
const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
|
|
24890
|
+
if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
|
|
23705
24891
|
if (git) status.git = git;
|
|
23706
24892
|
if (error) status.error = error;
|
|
24893
|
+
if (machineStatus) status.machineStatus = machineStatus;
|
|
24894
|
+
if (lastSeenAt) status.lastSeenAt = lastSeenAt;
|
|
24895
|
+
if (updatedAt) status.updatedAt = updatedAt;
|
|
24896
|
+
if (activeSessions.length > 0) status.activeSessions = activeSessions;
|
|
24897
|
+
if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
|
|
23707
24898
|
if (health) {
|
|
23708
24899
|
status.health = health;
|
|
23709
24900
|
return true;
|
|
23710
24901
|
}
|
|
23711
24902
|
if (git) {
|
|
23712
|
-
|
|
23713
|
-
status.health = git.isGitRepo === false ? "degraded" : dirty ? "dirty" : "online";
|
|
24903
|
+
status.health = deriveMeshNodeHealthFromGit(git);
|
|
23714
24904
|
return true;
|
|
23715
24905
|
}
|
|
23716
|
-
return
|
|
24906
|
+
return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
|
|
23717
24907
|
}
|
|
23718
24908
|
async function resolveProviderTypeFromPriority(args) {
|
|
23719
24909
|
if (!args.providerPriority.length) {
|
|
@@ -23738,152 +24928,116 @@ async function resolveProviderTypeFromPriority(args) {
|
|
|
23738
24928
|
}
|
|
23739
24929
|
return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
|
|
23740
24930
|
}
|
|
23741
|
-
var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
23742
24931
|
var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
23743
24932
|
var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
23744
24933
|
var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
|
|
23745
|
-
var
|
|
24934
|
+
var REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
23746
24935
|
function truncateValidationOutput(value) {
|
|
23747
24936
|
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
23748
24937
|
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
23749
24938
|
return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
|
|
23750
24939
|
[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
|
|
23751
24940
|
}
|
|
23752
|
-
function
|
|
23753
|
-
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
|
|
23757
|
-
|
|
23758
|
-
|
|
23759
|
-
}
|
|
23760
|
-
}
|
|
23761
|
-
function tokenizeValidationCommand(command) {
|
|
23762
|
-
const trimmed = command.trim();
|
|
23763
|
-
if (!trimmed) return null;
|
|
23764
|
-
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
23765
|
-
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
23766
|
-
if (!tokens.length) return null;
|
|
23767
|
-
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
23768
|
-
return tokens;
|
|
23769
|
-
}
|
|
23770
|
-
function scriptMatchesValidationCategory(scriptName, category) {
|
|
23771
|
-
return scriptName === category || scriptName.startsWith(`${category}:`);
|
|
23772
|
-
}
|
|
23773
|
-
function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
|
|
23774
|
-
const tokens = tokenizeValidationCommand(rawCommand);
|
|
23775
|
-
if (!tokens) {
|
|
23776
|
-
return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
|
|
23777
|
-
}
|
|
23778
|
-
const [binary, second, third, ...rest] = tokens;
|
|
23779
|
-
let scriptName = "";
|
|
23780
|
-
let command = binary;
|
|
23781
|
-
let args = [];
|
|
23782
|
-
if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
|
|
23783
|
-
scriptName = third;
|
|
23784
|
-
args = ["run", scriptName, ...rest];
|
|
23785
|
-
} else if (binary === "npm" && second === "test" && !third) {
|
|
23786
|
-
scriptName = "test";
|
|
23787
|
-
args = ["test"];
|
|
23788
|
-
} else if (binary === "yarn" && second === "run" && third) {
|
|
23789
|
-
scriptName = third;
|
|
23790
|
-
args = ["run", scriptName, ...rest];
|
|
23791
|
-
} else if (binary === "yarn" && second && !third) {
|
|
23792
|
-
scriptName = second;
|
|
23793
|
-
args = [scriptName];
|
|
23794
|
-
} else {
|
|
23795
|
-
return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
|
|
23796
|
-
}
|
|
23797
|
-
if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
|
|
23798
|
-
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
|
|
23799
|
-
}
|
|
23800
|
-
if (!scriptMatchesValidationCategory(scriptName, category)) {
|
|
23801
|
-
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
|
|
23802
|
-
}
|
|
23803
|
-
return {
|
|
23804
|
-
command: {
|
|
23805
|
-
command,
|
|
23806
|
-
args,
|
|
23807
|
-
displayCommand: [command, ...args].join(" "),
|
|
23808
|
-
category,
|
|
23809
|
-
source
|
|
23810
|
-
}
|
|
23811
|
-
};
|
|
24941
|
+
function recordMeshRefineStage(stages, stage, status, startedAt, details) {
|
|
24942
|
+
stages.push({
|
|
24943
|
+
stage,
|
|
24944
|
+
status,
|
|
24945
|
+
durationMs: Date.now() - startedAt,
|
|
24946
|
+
...details || {}
|
|
24947
|
+
});
|
|
23812
24948
|
}
|
|
23813
|
-
function
|
|
23814
|
-
const
|
|
23815
|
-
|
|
23816
|
-
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
for (const entry of entries) {
|
|
23820
|
-
if (typeof entry?.command !== "string") continue;
|
|
23821
|
-
candidates.push({
|
|
23822
|
-
command: entry.command,
|
|
23823
|
-
category,
|
|
23824
|
-
source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
|
|
23825
|
-
confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
|
|
23826
|
-
});
|
|
23827
|
-
}
|
|
23828
|
-
}
|
|
23829
|
-
return candidates.sort((a, b) => {
|
|
23830
|
-
const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
|
|
23831
|
-
return rank(a.confidence) - rank(b.confidence);
|
|
24949
|
+
async function computeGitPatchId(cwd, fromRef, toRef) {
|
|
24950
|
+
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
24951
|
+
const diff = execFileSync4("git", ["diff", "--patch", "--full-index", fromRef, toRef], {
|
|
24952
|
+
cwd,
|
|
24953
|
+
encoding: "utf8",
|
|
24954
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
23832
24955
|
});
|
|
24956
|
+
if (!diff.trim()) return "";
|
|
24957
|
+
const patchId = execFileSync4("git", ["patch-id", "--stable"], {
|
|
24958
|
+
cwd,
|
|
24959
|
+
input: diff,
|
|
24960
|
+
encoding: "utf8",
|
|
24961
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
24962
|
+
}).trim();
|
|
24963
|
+
return patchId.split(/\s+/)[0] || "";
|
|
23833
24964
|
}
|
|
23834
|
-
function
|
|
23835
|
-
const
|
|
23836
|
-
|
|
23837
|
-
|
|
23838
|
-
const
|
|
23839
|
-
|
|
23840
|
-
|
|
23841
|
-
|
|
23842
|
-
}
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
23846
|
-
|
|
23847
|
-
|
|
23848
|
-
|
|
23849
|
-
|
|
23850
|
-
|
|
23851
|
-
|
|
23852
|
-
|
|
23853
|
-
|
|
23854
|
-
|
|
23855
|
-
|
|
23856
|
-
|
|
23857
|
-
|
|
23858
|
-
if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
|
|
23859
|
-
selected.push(parsed.command);
|
|
23860
|
-
seen.add(parsed.command.displayCommand);
|
|
23861
|
-
if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
|
|
23862
|
-
}
|
|
23863
|
-
if (!selected.length && candidates.length === 0) {
|
|
23864
|
-
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
23865
|
-
if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
|
|
23866
|
-
const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
|
|
23867
|
-
if (fallback.command && !seen.has(fallback.command.displayCommand)) {
|
|
23868
|
-
selected.push(fallback.command);
|
|
23869
|
-
seen.add(fallback.command.displayCommand);
|
|
23870
|
-
} else if (fallback.rejected) {
|
|
23871
|
-
rejectedCommands.push(fallback.rejected);
|
|
23872
|
-
}
|
|
23873
|
-
if (selected.length >= 2) break;
|
|
24965
|
+
async function runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead) {
|
|
24966
|
+
const startedAt = Date.now();
|
|
24967
|
+
try {
|
|
24968
|
+
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
24969
|
+
const git = (args) => execFileSync4("git", args, {
|
|
24970
|
+
cwd: repoRoot,
|
|
24971
|
+
encoding: "utf8",
|
|
24972
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
24973
|
+
});
|
|
24974
|
+
const mergeBase = git(["merge-base", baseHead, branchHead]).trim();
|
|
24975
|
+
const mergeTreeStdout = git(["merge-tree", "--write-tree", baseHead, branchHead]);
|
|
24976
|
+
const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || "";
|
|
24977
|
+
if (!mergeBase || !mergedTree) {
|
|
24978
|
+
return {
|
|
24979
|
+
status: "failed",
|
|
24980
|
+
equivalent: false,
|
|
24981
|
+
baseHead,
|
|
24982
|
+
branchHead,
|
|
24983
|
+
mergeBase: mergeBase || void 0,
|
|
24984
|
+
mergedTree: mergedTree || void 0,
|
|
24985
|
+
durationMs: Date.now() - startedAt,
|
|
24986
|
+
error: "patch equivalence preflight could not resolve merge-base or synthetic merge tree",
|
|
24987
|
+
stdout: truncateValidationOutput(mergeTreeStdout)
|
|
24988
|
+
};
|
|
23874
24989
|
}
|
|
24990
|
+
const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
|
|
24991
|
+
const actualPatchId = await computeGitPatchId(repoRoot, baseHead, mergedTree);
|
|
24992
|
+
const equivalent = expectedPatchId === actualPatchId;
|
|
24993
|
+
return {
|
|
24994
|
+
status: equivalent ? "passed" : "failed",
|
|
24995
|
+
equivalent,
|
|
24996
|
+
baseHead,
|
|
24997
|
+
branchHead,
|
|
24998
|
+
mergeBase,
|
|
24999
|
+
mergedTree,
|
|
25000
|
+
expectedPatchId,
|
|
25001
|
+
actualPatchId,
|
|
25002
|
+
durationMs: Date.now() - startedAt
|
|
25003
|
+
};
|
|
25004
|
+
} catch (e) {
|
|
25005
|
+
return {
|
|
25006
|
+
status: "failed",
|
|
25007
|
+
equivalent: false,
|
|
25008
|
+
baseHead,
|
|
25009
|
+
branchHead,
|
|
25010
|
+
durationMs: Date.now() - startedAt,
|
|
25011
|
+
error: e?.message || String(e),
|
|
25012
|
+
stdout: truncateValidationOutput(e?.stdout),
|
|
25013
|
+
stderr: truncateValidationOutput(e?.stderr)
|
|
25014
|
+
};
|
|
23875
25015
|
}
|
|
25016
|
+
}
|
|
25017
|
+
function buildMeshRefineValidationPlan(mesh, workspace) {
|
|
25018
|
+
const plan = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
23876
25019
|
return {
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
25020
|
+
source: plan.source,
|
|
25021
|
+
sourceType: plan.sourceType,
|
|
25022
|
+
commands: plan.commands.map((command) => ({
|
|
25023
|
+
displayCommand: command.displayCommand,
|
|
25024
|
+
category: command.category,
|
|
25025
|
+
source: command.source,
|
|
25026
|
+
cwd: command.cwd,
|
|
25027
|
+
timeoutMs: command.timeoutMs
|
|
25028
|
+
})),
|
|
25029
|
+
unavailableReason: plan.unavailableReason,
|
|
25030
|
+
rejectedCommands: plan.rejectedCommands,
|
|
25031
|
+
suggestions: plan.suggestions,
|
|
25032
|
+
suggestedConfig: plan.suggestedConfig,
|
|
25033
|
+
note: plan.sourceType === "unavailable" ? "No validation command will be executed until a repo mesh/refine config is provided. Heuristics are suggestions only." : "Validation commands are resolved from repo mesh/refine config; heuristics are suggestions only."
|
|
23880
25034
|
};
|
|
23881
25035
|
}
|
|
23882
25036
|
async function runMeshRefineValidationGate(mesh, workspace) {
|
|
23883
25037
|
const { execFile: execFile3 } = await import("child_process");
|
|
23884
25038
|
const { promisify: promisify3 } = await import("util");
|
|
23885
25039
|
const execFileAsync3 = promisify3(execFile3);
|
|
23886
|
-
const selection =
|
|
25040
|
+
const selection = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
23887
25041
|
const summary = {
|
|
23888
25042
|
status: "skipped",
|
|
23889
25043
|
required: true,
|
|
@@ -23891,21 +25045,27 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
23891
25045
|
rejectedCommands: selection.rejectedCommands,
|
|
23892
25046
|
skippedReason: void 0,
|
|
23893
25047
|
timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
|
|
23894
|
-
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
|
|
25048
|
+
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
25049
|
+
configSource: selection.source,
|
|
25050
|
+
configSourceType: selection.sourceType,
|
|
25051
|
+
suggestions: selection.suggestions,
|
|
25052
|
+
suggestedConfig: selection.suggestedConfig
|
|
23895
25053
|
};
|
|
23896
25054
|
if (!selection.commands.length) {
|
|
23897
|
-
summary.skippedReason = "validation_unavailable:
|
|
25055
|
+
summary.skippedReason = selection.unavailableReason || "validation_unavailable: repo mesh/refine config did not provide executable validation.commands";
|
|
23898
25056
|
return summary;
|
|
23899
25057
|
}
|
|
23900
25058
|
for (const candidate of selection.commands) {
|
|
23901
25059
|
const startedAt = Date.now();
|
|
25060
|
+
const cwd = candidate.cwd ? pathResolve(workspace, candidate.cwd) : workspace;
|
|
25061
|
+
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
23902
25062
|
try {
|
|
23903
25063
|
const result = await execFileAsync3(candidate.command, candidate.args, {
|
|
23904
|
-
cwd
|
|
25064
|
+
cwd,
|
|
23905
25065
|
encoding: "utf8",
|
|
23906
|
-
timeout
|
|
25066
|
+
timeout,
|
|
23907
25067
|
maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
23908
|
-
env: { ...process.env, CI: process.env.CI || "1" }
|
|
25068
|
+
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} }
|
|
23909
25069
|
});
|
|
23910
25070
|
summary.commandsRun.push({
|
|
23911
25071
|
command: candidate.command,
|
|
@@ -23913,6 +25073,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
23913
25073
|
displayCommand: candidate.displayCommand,
|
|
23914
25074
|
category: candidate.category,
|
|
23915
25075
|
source: candidate.source,
|
|
25076
|
+
cwd,
|
|
23916
25077
|
passed: true,
|
|
23917
25078
|
exitCode: 0,
|
|
23918
25079
|
durationMs: Date.now() - startedAt,
|
|
@@ -23926,6 +25087,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
23926
25087
|
displayCommand: candidate.displayCommand,
|
|
23927
25088
|
category: candidate.category,
|
|
23928
25089
|
source: candidate.source,
|
|
25090
|
+
cwd,
|
|
23929
25091
|
passed: false,
|
|
23930
25092
|
exitCode: typeof error?.code === "number" ? error.code : null,
|
|
23931
25093
|
signal: typeof error?.signal === "string" ? error.signal : null,
|
|
@@ -23942,7 +25104,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
23942
25104
|
return summary;
|
|
23943
25105
|
}
|
|
23944
25106
|
function loadYamlModule() {
|
|
23945
|
-
return
|
|
25107
|
+
return yaml2;
|
|
23946
25108
|
}
|
|
23947
25109
|
function getMcpServersKey(format) {
|
|
23948
25110
|
return format === "hermes_config_yaml" ? "mcp_servers" : "mcpServers";
|
|
@@ -24100,36 +25262,216 @@ function summarizeSessionHostPruneResult(result) {
|
|
|
24100
25262
|
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
24101
25263
|
};
|
|
24102
25264
|
}
|
|
25265
|
+
function normalizeStandaloneHostCommandUrl(hostAddress) {
|
|
25266
|
+
const raw = hostAddress.trim();
|
|
25267
|
+
if (!raw) throw new Error("hostAddress required");
|
|
25268
|
+
const url = new URL(raw.replace(/^ws:/, "http:").replace(/^wss:/, "https:"));
|
|
25269
|
+
url.pathname = "/api/v1/command";
|
|
25270
|
+
url.search = "";
|
|
25271
|
+
url.hash = "";
|
|
25272
|
+
return url.toString();
|
|
25273
|
+
}
|
|
25274
|
+
function buildMemberJoinNode(mesh, args, fallbackDaemonId) {
|
|
25275
|
+
const requestedNodeId = typeof args?.memberNodeId === "string" ? args.memberNodeId.trim() : "";
|
|
25276
|
+
const explicit = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
|
|
25277
|
+
const configured = Array.isArray(mesh?.nodes) ? requestedNodeId ? mesh.nodes.find((node) => node?.id === requestedNodeId || node?.nodeId === requestedNodeId) : mesh.nodes[0] : null;
|
|
25278
|
+
const source = explicit || configured;
|
|
25279
|
+
const workspace = typeof source?.workspace === "string" && source.workspace.trim() ? source.workspace.trim() : typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
25280
|
+
if (!workspace) return null;
|
|
25281
|
+
const nodeId = typeof source?.id === "string" && source.id.trim() ? source.id.trim() : typeof source?.nodeId === "string" && source.nodeId.trim() ? source.nodeId.trim() : void 0;
|
|
25282
|
+
return {
|
|
25283
|
+
...nodeId ? { id: nodeId } : {},
|
|
25284
|
+
workspace,
|
|
25285
|
+
...typeof source?.repoRoot === "string" && source.repoRoot.trim() ? { repoRoot: source.repoRoot.trim() } : {},
|
|
25286
|
+
...typeof source?.daemonId === "string" && source.daemonId.trim() ? { daemonId: source.daemonId.trim() } : fallbackDaemonId ? { daemonId: fallbackDaemonId } : {},
|
|
25287
|
+
...typeof source?.machineId === "string" && source.machineId.trim() ? { machineId: source.machineId.trim() } : {},
|
|
25288
|
+
userOverrides: source?.userOverrides && typeof source.userOverrides === "object" && !Array.isArray(source.userOverrides) ? source.userOverrides : {},
|
|
25289
|
+
policy: source?.policy && typeof source.policy === "object" && !Array.isArray(source.policy) ? source.policy : {},
|
|
25290
|
+
role: "member"
|
|
25291
|
+
};
|
|
25292
|
+
}
|
|
24103
25293
|
var DaemonCommandRouter = class {
|
|
24104
25294
|
deps;
|
|
24105
25295
|
/** In-memory cache for cloud-originating meshes passed via inlineMesh.
|
|
24106
25296
|
* Allows the MCP server to query mesh data via get_mesh even when
|
|
24107
25297
|
* the mesh doesn't exist in the local meshes.json file. */
|
|
24108
25298
|
inlineMeshCache = /* @__PURE__ */ new Map();
|
|
25299
|
+
/** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
|
|
25300
|
+
aggregateMeshStatusCache = /* @__PURE__ */ new Map();
|
|
24109
25301
|
constructor(deps) {
|
|
24110
25302
|
this.deps = deps;
|
|
24111
25303
|
}
|
|
25304
|
+
cloneJsonValue(value) {
|
|
25305
|
+
if (typeof structuredClone === "function") return structuredClone(value);
|
|
25306
|
+
return JSON.parse(JSON.stringify(value));
|
|
25307
|
+
}
|
|
25308
|
+
hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options) {
|
|
25309
|
+
if (!mesh || typeof mesh !== "object" || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
|
|
25310
|
+
const inlineNodesById = /* @__PURE__ */ new Map();
|
|
25311
|
+
for (const node of mesh.nodes) {
|
|
25312
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
25313
|
+
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
25314
|
+
}
|
|
25315
|
+
if (!inlineNodesById.size) return snapshot;
|
|
25316
|
+
let changed = false;
|
|
25317
|
+
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
25318
|
+
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
25319
|
+
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
25320
|
+
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
25321
|
+
const nodeId = readStringValue(entry);
|
|
25322
|
+
if (nodeId) unavailableNodeIds.add(nodeId);
|
|
25323
|
+
}
|
|
25324
|
+
const nodes = snapshot.nodes.map((statusNode) => {
|
|
25325
|
+
const nodeId = readStringValue(statusNode?.nodeId, statusNode?.id);
|
|
25326
|
+
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
|
|
25327
|
+
if (!inlineNode) return statusNode;
|
|
25328
|
+
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
25329
|
+
if (!liveGit) return statusNode;
|
|
25330
|
+
const nextStatus = { ...statusNode };
|
|
25331
|
+
nextStatus.git = liveGit;
|
|
25332
|
+
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
25333
|
+
nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
|
|
25334
|
+
const connection = readObjectRecord(nextStatus.connection);
|
|
25335
|
+
const connectionState = readStringValue(connection.state);
|
|
25336
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
25337
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
25338
|
+
nextStatus.connection = buildLivePeerGitConnection(connection);
|
|
25339
|
+
}
|
|
25340
|
+
delete nextStatus.gitProbePending;
|
|
25341
|
+
const error = readStringValue(nextStatus.error);
|
|
25342
|
+
if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
|
|
25343
|
+
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
|
|
25344
|
+
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
25345
|
+
changed = true;
|
|
25346
|
+
return nextStatus;
|
|
25347
|
+
});
|
|
25348
|
+
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0)) return snapshot;
|
|
25349
|
+
const nextSourceOfTruth = {
|
|
25350
|
+
...sourceOfTruth,
|
|
25351
|
+
...Object.keys(directPeerTruth).length ? {
|
|
25352
|
+
directPeerTruth: {
|
|
25353
|
+
...directPeerTruth,
|
|
25354
|
+
satisfied: options?.requireDirectPeerTruth === true ? unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
25355
|
+
unavailableNodeIds: [...unavailableNodeIds]
|
|
25356
|
+
},
|
|
25357
|
+
...options?.requireDirectPeerTruth === true ? {
|
|
25358
|
+
coordinatorOwnsLiveTruth: unavailableNodeIds.size === 0,
|
|
25359
|
+
currentStatus: unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
|
|
25360
|
+
} : {}
|
|
25361
|
+
} : {}
|
|
25362
|
+
};
|
|
25363
|
+
return {
|
|
25364
|
+
...snapshot,
|
|
25365
|
+
...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 ? {
|
|
25366
|
+
success: false,
|
|
25367
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
25368
|
+
error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
|
|
25369
|
+
} : {},
|
|
25370
|
+
sourceOfTruth: nextSourceOfTruth,
|
|
25371
|
+
nodes
|
|
25372
|
+
};
|
|
25373
|
+
}
|
|
25374
|
+
getCachedAggregateMeshStatus(meshId, mesh, options) {
|
|
25375
|
+
const cached = this.aggregateMeshStatusCache.get(meshId);
|
|
25376
|
+
if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
|
|
25377
|
+
let snapshot = this.cloneJsonValue(cached.snapshot);
|
|
25378
|
+
snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
|
|
25379
|
+
if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
|
|
25380
|
+
const ageMs = Math.max(0, Date.now() - cached.builtAt);
|
|
25381
|
+
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
|
|
25382
|
+
snapshot.sourceOfTruth = {
|
|
25383
|
+
...sourceOfTruth,
|
|
25384
|
+
aggregateSnapshot: {
|
|
25385
|
+
...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
|
|
25386
|
+
owner: "coordinator_daemon_memory",
|
|
25387
|
+
cached: true,
|
|
25388
|
+
source: "memory",
|
|
25389
|
+
refreshReason: "memory_cache_hit",
|
|
25390
|
+
ageMs,
|
|
25391
|
+
cachedAt: new Date(cached.builtAt).toISOString(),
|
|
25392
|
+
returnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25393
|
+
}
|
|
25394
|
+
};
|
|
25395
|
+
return snapshot;
|
|
25396
|
+
}
|
|
25397
|
+
rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
|
|
25398
|
+
if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
|
|
25399
|
+
const builtAt = Date.now();
|
|
25400
|
+
const next = this.cloneJsonValue(snapshot);
|
|
25401
|
+
const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
|
|
25402
|
+
next.sourceOfTruth = {
|
|
25403
|
+
...sourceOfTruth,
|
|
25404
|
+
aggregateSnapshot: {
|
|
25405
|
+
owner: "coordinator_daemon_memory",
|
|
25406
|
+
cached: false,
|
|
25407
|
+
source: "live_refresh",
|
|
25408
|
+
refreshReason,
|
|
25409
|
+
ageMs: 0,
|
|
25410
|
+
cachedAt: new Date(builtAt).toISOString(),
|
|
25411
|
+
returnedAt: new Date(builtAt).toISOString()
|
|
25412
|
+
}
|
|
25413
|
+
};
|
|
25414
|
+
this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next) });
|
|
25415
|
+
return next;
|
|
25416
|
+
}
|
|
24112
25417
|
getCachedInlineMesh(meshId, inlineMesh) {
|
|
24113
25418
|
if (inlineMesh && typeof inlineMesh === "object") {
|
|
24114
|
-
this.
|
|
24115
|
-
return inlineMesh;
|
|
25419
|
+
return this.warmInlineMeshCache(meshId, inlineMesh);
|
|
24116
25420
|
}
|
|
24117
25421
|
return this.inlineMeshCache.get(meshId);
|
|
24118
25422
|
}
|
|
25423
|
+
warmInlineMeshCache(meshId, inlineMesh) {
|
|
25424
|
+
if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
|
|
25425
|
+
const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
|
|
25426
|
+
const cached = this.inlineMeshCache.get(meshId);
|
|
25427
|
+
if (cached) {
|
|
25428
|
+
const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
|
|
25429
|
+
this.inlineMeshCache.set(meshId, merged);
|
|
25430
|
+
return merged;
|
|
25431
|
+
}
|
|
25432
|
+
this.inlineMeshCache.set(meshId, sanitizedInlineMesh);
|
|
25433
|
+
return sanitizedInlineMesh;
|
|
25434
|
+
}
|
|
24119
25435
|
async getMeshForCommand(meshId, inlineMesh, options) {
|
|
24120
25436
|
const preferInline = options?.preferInline === true;
|
|
24121
25437
|
if (preferInline) {
|
|
24122
|
-
const cached2 = this.getCachedInlineMesh(meshId
|
|
24123
|
-
if (cached2)
|
|
25438
|
+
const cached2 = this.getCachedInlineMesh(meshId);
|
|
25439
|
+
if (cached2) {
|
|
25440
|
+
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
25441
|
+
const merged = reconcileInlineMeshCache(cached2, inlineMesh);
|
|
25442
|
+
this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
|
|
25443
|
+
return { mesh: merged, inline: true, source: "inline_cache" };
|
|
25444
|
+
}
|
|
25445
|
+
return { mesh: cached2, inline: true, source: "inline_cache" };
|
|
25446
|
+
}
|
|
25447
|
+
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
25448
|
+
this.warmInlineMeshCache(meshId, inlineMesh);
|
|
25449
|
+
return { mesh: inlineMesh, inline: true, source: "inline_bootstrap" };
|
|
25450
|
+
}
|
|
24124
25451
|
}
|
|
24125
25452
|
try {
|
|
24126
25453
|
const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
24127
25454
|
const mesh = getMesh3(meshId);
|
|
24128
|
-
if (mesh) return { mesh, inline: false };
|
|
25455
|
+
if (mesh) return { mesh, inline: false, source: "local_config" };
|
|
24129
25456
|
} catch {
|
|
24130
25457
|
}
|
|
24131
|
-
const cached = this.getCachedInlineMesh(meshId
|
|
24132
|
-
|
|
25458
|
+
const cached = this.getCachedInlineMesh(meshId);
|
|
25459
|
+
if (cached) return { mesh: cached, inline: true, source: "inline_cache" };
|
|
25460
|
+
const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
|
|
25461
|
+
return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
|
|
25462
|
+
}
|
|
25463
|
+
invalidateAggregateMeshStatus(meshId) {
|
|
25464
|
+
this.aggregateMeshStatusCache.delete(meshId);
|
|
25465
|
+
}
|
|
25466
|
+
async requireMeshHostMutationOwner(meshId, inlineMesh, operation) {
|
|
25467
|
+
const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
|
|
25468
|
+
const mesh = meshRecord?.mesh;
|
|
25469
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
25470
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
25471
|
+
if (!meshHost.canOwnCoordinator || !meshHost.canOwnQueue) {
|
|
25472
|
+
return { ...buildMeshHostRequiredFailure(mesh, operation), success: false, meshId };
|
|
25473
|
+
}
|
|
25474
|
+
return null;
|
|
24133
25475
|
}
|
|
24134
25476
|
updateInlineMeshNode(meshId, mesh, node) {
|
|
24135
25477
|
if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
|
|
@@ -24138,6 +25480,7 @@ var DaemonCommandRouter = class {
|
|
|
24138
25480
|
else mesh.nodes.push(node);
|
|
24139
25481
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24140
25482
|
this.inlineMeshCache.set(meshId, mesh);
|
|
25483
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
24141
25484
|
}
|
|
24142
25485
|
removeInlineMeshNode(meshId, mesh, nodeId) {
|
|
24143
25486
|
if (!mesh || !Array.isArray(mesh.nodes)) return false;
|
|
@@ -24146,6 +25489,7 @@ var DaemonCommandRouter = class {
|
|
|
24146
25489
|
mesh.nodes.splice(idx, 1);
|
|
24147
25490
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24148
25491
|
this.inlineMeshCache.set(meshId, mesh);
|
|
25492
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
24149
25493
|
return true;
|
|
24150
25494
|
}
|
|
24151
25495
|
normalizeMeshSessionCleanupMode(value) {
|
|
@@ -24358,6 +25702,7 @@ var DaemonCommandRouter = class {
|
|
|
24358
25702
|
const deletedSessionIds = [];
|
|
24359
25703
|
const skippedSessionIds = [];
|
|
24360
25704
|
const skippedLiveSessionIds = [];
|
|
25705
|
+
const skippedCoordinatorSessionIds = [];
|
|
24361
25706
|
const deleteUnsupportedSessionIds = [];
|
|
24362
25707
|
const recordsRemainSessionIds = [];
|
|
24363
25708
|
const errors = [];
|
|
@@ -24390,6 +25735,12 @@ var DaemonCommandRouter = class {
|
|
|
24390
25735
|
const completed = this.isCompletedHostedSession(record);
|
|
24391
25736
|
const surfaceKind = getSessionHostSurfaceKind(record);
|
|
24392
25737
|
const liveRuntime = surfaceKind === "live_runtime";
|
|
25738
|
+
const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
|
|
25739
|
+
if (!hasExplicitSessionIds && coordinatorSession) {
|
|
25740
|
+
skippedSessionIds.push(sessionId);
|
|
25741
|
+
skippedCoordinatorSessionIds.push(sessionId);
|
|
25742
|
+
continue;
|
|
25743
|
+
}
|
|
24393
25744
|
if (!hasExplicitSessionIds && liveRuntime) {
|
|
24394
25745
|
skippedSessionIds.push(sessionId);
|
|
24395
25746
|
skippedLiveSessionIds.push(sessionId);
|
|
@@ -24455,6 +25806,7 @@ var DaemonCommandRouter = class {
|
|
|
24455
25806
|
deletedSessionIds,
|
|
24456
25807
|
skippedSessionIds,
|
|
24457
25808
|
skippedLiveSessionIds,
|
|
25809
|
+
skippedCoordinatorSessionIds,
|
|
24458
25810
|
...deleteUnsupported ? {
|
|
24459
25811
|
deleteUnsupported: true,
|
|
24460
25812
|
effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
|
|
@@ -24587,7 +25939,8 @@ var DaemonCommandRouter = class {
|
|
|
24587
25939
|
return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
|
|
24588
25940
|
}
|
|
24589
25941
|
case "get_pending_mesh_events": {
|
|
24590
|
-
const
|
|
25942
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25943
|
+
const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
|
|
24591
25944
|
return { success: true, events };
|
|
24592
25945
|
}
|
|
24593
25946
|
case "launch_cli":
|
|
@@ -25116,15 +26469,39 @@ var DaemonCommandRouter = class {
|
|
|
25116
26469
|
case "get_mesh": {
|
|
25117
26470
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25118
26471
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
26472
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26473
|
+
if (!meshRecord?.mesh) return { success: false, error: "Mesh not found" };
|
|
26474
|
+
const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
|
|
26475
|
+
const directTruth = await hydrateInlineMeshDirectTruth({
|
|
26476
|
+
mesh: meshRecord.mesh,
|
|
26477
|
+
meshSource: meshRecord.source,
|
|
26478
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
26479
|
+
statusInstanceId: this.deps.statusInstanceId,
|
|
26480
|
+
localMachineId: loadConfig().machineId || ""
|
|
26481
|
+
});
|
|
26482
|
+
const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
|
|
26483
|
+
const sourceOfTruth = {
|
|
26484
|
+
membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
26485
|
+
coordinatorOwnsLiveTruth: directTruthSatisfied,
|
|
26486
|
+
directPeerTruth: {
|
|
26487
|
+
required: requireDirectPeerTruth,
|
|
26488
|
+
satisfied: directTruthSatisfied,
|
|
26489
|
+
directEvidenceCount: directTruth.directEvidenceCount,
|
|
26490
|
+
localConfirmedCount: directTruth.localConfirmedCount,
|
|
26491
|
+
peerAttemptedCount: directTruth.peerAttemptedCount,
|
|
26492
|
+
peerConfirmedCount: directTruth.peerConfirmedCount,
|
|
26493
|
+
unavailableNodeIds: directTruth.unavailableNodeIds
|
|
26494
|
+
}
|
|
26495
|
+
};
|
|
26496
|
+
if (requireDirectPeerTruth && !directTruthSatisfied) {
|
|
26497
|
+
return {
|
|
26498
|
+
success: false,
|
|
26499
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
26500
|
+
error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.",
|
|
26501
|
+
sourceOfTruth
|
|
26502
|
+
};
|
|
25124
26503
|
}
|
|
25125
|
-
|
|
25126
|
-
if (cached) return { success: true, mesh: cached };
|
|
25127
|
-
return { success: false, error: "Mesh not found" };
|
|
26504
|
+
return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
|
|
25128
26505
|
}
|
|
25129
26506
|
case "create_mesh": {
|
|
25130
26507
|
const name = typeof args?.name === "string" ? args.name.trim() : "";
|
|
@@ -25134,7 +26511,8 @@ var DaemonCommandRouter = class {
|
|
|
25134
26511
|
if (!name) return { success: false, error: "name required" };
|
|
25135
26512
|
try {
|
|
25136
26513
|
const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25137
|
-
const
|
|
26514
|
+
const meshHost = args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost) ? args.meshHost : void 0;
|
|
26515
|
+
const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
|
|
25138
26516
|
return { success: true, mesh };
|
|
25139
26517
|
} catch (e) {
|
|
25140
26518
|
return { success: false, error: e.message };
|
|
@@ -25150,15 +26528,226 @@ var DaemonCommandRouter = class {
|
|
|
25150
26528
|
if (typeof args?.defaultBranch === "string") patch.defaultBranch = args.defaultBranch;
|
|
25151
26529
|
if (args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy)) patch.policy = args.policy;
|
|
25152
26530
|
if (args?.coordinator && typeof args.coordinator === "object" && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
|
|
26531
|
+
if (args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost)) patch.meshHost = args.meshHost;
|
|
25153
26532
|
if (!Object.keys(patch).length) return { success: false, error: "No updates provided" };
|
|
25154
26533
|
const mesh = updateMesh2(meshId, patch);
|
|
25155
26534
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
25156
26535
|
this.inlineMeshCache.set(meshId, mesh);
|
|
26536
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
25157
26537
|
return { success: true, mesh };
|
|
25158
26538
|
} catch (e) {
|
|
25159
26539
|
return { success: false, error: e.message };
|
|
25160
26540
|
}
|
|
25161
26541
|
}
|
|
26542
|
+
case "get_mesh_host_pairing": {
|
|
26543
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26544
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26545
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26546
|
+
const mesh = meshRecord?.mesh;
|
|
26547
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
26548
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
26549
|
+
const pairingStatus = meshHost.pairing?.status || "not_configured";
|
|
26550
|
+
return {
|
|
26551
|
+
success: true,
|
|
26552
|
+
code: pairingStatus === "not_configured" ? "mesh_host_pairing_not_configured" : "mesh_host_pairing_pending",
|
|
26553
|
+
meshId,
|
|
26554
|
+
hostAddress: meshHost.hostAddress,
|
|
26555
|
+
meshHost,
|
|
26556
|
+
manualPairing: {
|
|
26557
|
+
status: pairingStatus,
|
|
26558
|
+
joinImplemented: true,
|
|
26559
|
+
protocol: "standalone_command_direct_v1",
|
|
26560
|
+
description: "Standalone manual pairing can save address/token metadata, apply a host join over direct standalone command HTTP or injected mesh command dispatch, and check persisted status. P2P signaling remains outside this slice."
|
|
26561
|
+
}
|
|
26562
|
+
};
|
|
26563
|
+
}
|
|
26564
|
+
case "configure_mesh_host_pairing": {
|
|
26565
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26566
|
+
const hostAddress = typeof args?.hostAddress === "string" ? args.hostAddress.trim() : "";
|
|
26567
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26568
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26569
|
+
if (!hostAddress || !token) return { success: false, error: "hostAddress and token required" };
|
|
26570
|
+
try {
|
|
26571
|
+
const { configureMeshHostPairing: configureMeshHostPairing2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26572
|
+
const configured = configureMeshHostPairing2(meshId, { hostAddress, token });
|
|
26573
|
+
if (!configured) return { success: false, error: "Mesh not found" };
|
|
26574
|
+
this.inlineMeshCache.set(meshId, configured.mesh);
|
|
26575
|
+
const meshHost = resolveMeshHostStatus(configured.mesh);
|
|
26576
|
+
return {
|
|
26577
|
+
success: true,
|
|
26578
|
+
code: "mesh_host_pairing_pending",
|
|
26579
|
+
meshId,
|
|
26580
|
+
hostAddress: configured.hostAddress,
|
|
26581
|
+
meshHost,
|
|
26582
|
+
manualPairing: {
|
|
26583
|
+
status: meshHost.pairing?.status || "pairing",
|
|
26584
|
+
joinImplemented: true,
|
|
26585
|
+
protocol: "standalone_command_direct_v1",
|
|
26586
|
+
description: "Manual Mesh Host pairing config was saved locally. Use join_mesh_host_pairing to apply it to the host. Raw token was not persisted."
|
|
26587
|
+
}
|
|
26588
|
+
};
|
|
26589
|
+
} catch (e) {
|
|
26590
|
+
return { success: false, code: "mesh_host_pairing_invalid", meshId, hostAddress, error: e.message };
|
|
26591
|
+
}
|
|
26592
|
+
}
|
|
26593
|
+
case "create_mesh_host_pairing_token": {
|
|
26594
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26595
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26596
|
+
try {
|
|
26597
|
+
const { createMeshHostPairingToken: createMeshHostPairingToken2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26598
|
+
const created = createMeshHostPairingToken2(meshId, {
|
|
26599
|
+
token: typeof args?.token === "string" ? args.token : void 0,
|
|
26600
|
+
expiresAt: typeof args?.expiresAt === "string" ? args.expiresAt : void 0
|
|
26601
|
+
});
|
|
26602
|
+
if (!created) return { success: false, error: "Mesh not found" };
|
|
26603
|
+
this.inlineMeshCache.set(meshId, created.mesh);
|
|
26604
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26605
|
+
return {
|
|
26606
|
+
success: true,
|
|
26607
|
+
code: "mesh_host_pairing_token_created",
|
|
26608
|
+
meshId,
|
|
26609
|
+
token: created.token,
|
|
26610
|
+
tokenId: created.tokenId,
|
|
26611
|
+
expiresAt: created.expiresAt,
|
|
26612
|
+
meshHost: resolveMeshHostStatus(created.mesh),
|
|
26613
|
+
warning: "Raw token is returned once and is not persisted; share it with member daemons over a trusted channel."
|
|
26614
|
+
};
|
|
26615
|
+
} catch (e) {
|
|
26616
|
+
return { success: false, code: "mesh_host_pairing_token_invalid", meshId, error: e.message };
|
|
26617
|
+
}
|
|
26618
|
+
}
|
|
26619
|
+
case "apply_mesh_host_join": {
|
|
26620
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26621
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26622
|
+
const memberNode = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
|
|
26623
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26624
|
+
if (!token || !memberNode) return { success: false, error: "token and memberNode required" };
|
|
26625
|
+
try {
|
|
26626
|
+
const { applyMeshHostJoinRequest: applyMeshHostJoinRequest2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26627
|
+
const applied = applyMeshHostJoinRequest2(meshId, {
|
|
26628
|
+
token,
|
|
26629
|
+
memberNode,
|
|
26630
|
+
memberMeshId: typeof args?.memberMeshId === "string" ? args.memberMeshId : void 0
|
|
26631
|
+
});
|
|
26632
|
+
if (!applied) return { success: false, error: "Mesh not found" };
|
|
26633
|
+
if (!applied.accepted) {
|
|
26634
|
+
return {
|
|
26635
|
+
success: false,
|
|
26636
|
+
code: "mesh_host_join_rejected",
|
|
26637
|
+
meshId,
|
|
26638
|
+
tokenId: applied.tokenId,
|
|
26639
|
+
meshHost: applied.meshHost ? resolveMeshHostStatus({ meshHost: applied.meshHost }) : void 0,
|
|
26640
|
+
error: applied.reason
|
|
26641
|
+
};
|
|
26642
|
+
}
|
|
26643
|
+
this.inlineMeshCache.set(meshId, applied.mesh);
|
|
26644
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26645
|
+
try {
|
|
26646
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
26647
|
+
appendLedgerEntry2(meshId, {
|
|
26648
|
+
kind: "node_joined",
|
|
26649
|
+
nodeId: applied.node.id,
|
|
26650
|
+
payload: { role: "member", tokenId: applied.tokenId, workspace: applied.node.workspace }
|
|
26651
|
+
});
|
|
26652
|
+
} catch {
|
|
26653
|
+
}
|
|
26654
|
+
return {
|
|
26655
|
+
success: true,
|
|
26656
|
+
code: "mesh_host_join_accepted",
|
|
26657
|
+
meshId,
|
|
26658
|
+
node: applied.node,
|
|
26659
|
+
tokenId: applied.tokenId,
|
|
26660
|
+
meshHost: resolveMeshHostStatus(applied.mesh)
|
|
26661
|
+
};
|
|
26662
|
+
} catch (e) {
|
|
26663
|
+
return { success: false, code: "mesh_host_join_failed", meshId, error: e.message };
|
|
26664
|
+
}
|
|
26665
|
+
}
|
|
26666
|
+
case "join_mesh_host_pairing": {
|
|
26667
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26668
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26669
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26670
|
+
if (!token) return { success: false, error: "token required because raw pairing tokens are not persisted" };
|
|
26671
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26672
|
+
const mesh = meshRecord?.mesh;
|
|
26673
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
26674
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
26675
|
+
if (meshHost.role !== "member") {
|
|
26676
|
+
return { success: false, code: "mesh_host_join_not_member", meshId, meshHost, error: "join_mesh_host_pairing must run from a member daemon configured with a Mesh Host address/token." };
|
|
26677
|
+
}
|
|
26678
|
+
try {
|
|
26679
|
+
const { tokenIdForManualPairing: tokenIdForManualPairing2, markMeshHostPairingJoined: markMeshHostPairingJoined2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26680
|
+
const tokenId = tokenIdForManualPairing2(token);
|
|
26681
|
+
if (meshHost.pairing?.tokenId && meshHost.pairing.tokenId !== tokenId) {
|
|
26682
|
+
return { success: false, code: "mesh_host_join_rejected", meshId, tokenId, meshHost, error: "invalid pairing token" };
|
|
26683
|
+
}
|
|
26684
|
+
const memberNode = buildMemberJoinNode(mesh, args, this.deps.statusInstanceId);
|
|
26685
|
+
if (!memberNode) return { success: false, error: "member node metadata unavailable" };
|
|
26686
|
+
const hostMeshId = typeof args?.hostMeshId === "string" && args.hostMeshId.trim() ? args.hostMeshId.trim() : meshId;
|
|
26687
|
+
const hostDaemonId = typeof args?.hostDaemonId === "string" && args.hostDaemonId.trim() ? args.hostDaemonId.trim() : meshHost.hostDaemonId;
|
|
26688
|
+
let hostResult;
|
|
26689
|
+
let transport;
|
|
26690
|
+
if (hostDaemonId && this.deps.dispatchMeshCommand) {
|
|
26691
|
+
transport = "mesh_command_dispatch";
|
|
26692
|
+
hostResult = await this.deps.dispatchMeshCommand(hostDaemonId, "apply_mesh_host_join", {
|
|
26693
|
+
meshId: hostMeshId,
|
|
26694
|
+
token,
|
|
26695
|
+
memberMeshId: meshId,
|
|
26696
|
+
memberNode
|
|
26697
|
+
});
|
|
26698
|
+
} else if (meshHost.hostAddress) {
|
|
26699
|
+
transport = "standalone_http_command";
|
|
26700
|
+
const commandUrl = normalizeStandaloneHostCommandUrl(meshHost.hostAddress);
|
|
26701
|
+
const response = await fetch(commandUrl, {
|
|
26702
|
+
method: "POST",
|
|
26703
|
+
headers: { "Content-Type": "application/json" },
|
|
26704
|
+
body: JSON.stringify({ type: "apply_mesh_host_join", payload: { meshId: hostMeshId, token, memberMeshId: meshId, memberNode } })
|
|
26705
|
+
});
|
|
26706
|
+
hostResult = await response.json().catch(() => ({ success: false, error: `Host returned HTTP ${response.status}` }));
|
|
26707
|
+
if (!response.ok && hostResult?.success !== false) hostResult = { success: false, error: `Host returned HTTP ${response.status}` };
|
|
26708
|
+
} else {
|
|
26709
|
+
return {
|
|
26710
|
+
success: false,
|
|
26711
|
+
code: "mesh_host_join_transport_unavailable",
|
|
26712
|
+
meshId,
|
|
26713
|
+
meshHost,
|
|
26714
|
+
error: "No hostDaemonId dispatch path or hostAddress HTTP command path is available. P2P signaling join is not implemented in this slice."
|
|
26715
|
+
};
|
|
26716
|
+
}
|
|
26717
|
+
if (!hostResult?.success) {
|
|
26718
|
+
return { success: false, code: hostResult?.code || "mesh_host_join_rejected", meshId, meshHost, transport, error: hostResult?.error || "Mesh Host rejected join request", hostResult };
|
|
26719
|
+
}
|
|
26720
|
+
const joined = meshRecord.inline ? null : markMeshHostPairingJoined2(meshId, {
|
|
26721
|
+
tokenId: hostResult.tokenId || tokenId,
|
|
26722
|
+
hostDaemonId: hostResult.meshHost?.hostDaemonId || hostDaemonId,
|
|
26723
|
+
hostNodeId: hostResult.meshHost?.hostNodeId,
|
|
26724
|
+
joinedAt: hostResult.meshHost?.pairing?.joinedAt
|
|
26725
|
+
});
|
|
26726
|
+
if (joined) {
|
|
26727
|
+
this.inlineMeshCache.set(meshId, joined.mesh);
|
|
26728
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26729
|
+
}
|
|
26730
|
+
return {
|
|
26731
|
+
success: true,
|
|
26732
|
+
code: "mesh_host_join_applied",
|
|
26733
|
+
meshId,
|
|
26734
|
+
hostMeshId,
|
|
26735
|
+
transport,
|
|
26736
|
+
node: hostResult.node,
|
|
26737
|
+
tokenId: hostResult.tokenId || tokenId,
|
|
26738
|
+
meshHost: joined ? resolveMeshHostStatus(joined.mesh) : { ...meshHost, pairing: { ...meshHost.pairing || {}, status: "paired", tokenId: hostResult.tokenId || tokenId } },
|
|
26739
|
+
hostResult,
|
|
26740
|
+
manualPairing: {
|
|
26741
|
+
status: "paired",
|
|
26742
|
+
joinImplemented: true,
|
|
26743
|
+
protocol: "standalone_command_direct_v1",
|
|
26744
|
+
description: "Mesh Host accepted the join and local member pairing status was marked paired. P2P runtime signaling remains outside this slice."
|
|
26745
|
+
}
|
|
26746
|
+
};
|
|
26747
|
+
} catch (e) {
|
|
26748
|
+
return { success: false, code: "mesh_host_join_failed", meshId, meshHost, error: e.message };
|
|
26749
|
+
}
|
|
26750
|
+
}
|
|
25162
26751
|
case "delete_mesh": {
|
|
25163
26752
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25164
26753
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
@@ -25241,6 +26830,8 @@ var DaemonCommandRouter = class {
|
|
|
25241
26830
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25242
26831
|
const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
|
|
25243
26832
|
if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
|
|
26833
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue cancellation");
|
|
26834
|
+
if (ownerFailure) return ownerFailure;
|
|
25244
26835
|
try {
|
|
25245
26836
|
const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
25246
26837
|
const reason = typeof args?.reason === "string" ? args.reason : void 0;
|
|
@@ -25255,6 +26846,8 @@ var DaemonCommandRouter = class {
|
|
|
25255
26846
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25256
26847
|
const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
|
|
25257
26848
|
if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
|
|
26849
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue requeue");
|
|
26850
|
+
if (ownerFailure) return ownerFailure;
|
|
25258
26851
|
try {
|
|
25259
26852
|
const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
25260
26853
|
const task = requeueTask2(meshId, taskId, {
|
|
@@ -25275,6 +26868,8 @@ var DaemonCommandRouter = class {
|
|
|
25275
26868
|
const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
25276
26869
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25277
26870
|
if (!workspace) return { success: false, error: "workspace required" };
|
|
26871
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node addition");
|
|
26872
|
+
if (ownerFailure) return ownerFailure;
|
|
25278
26873
|
try {
|
|
25279
26874
|
const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25280
26875
|
const providerPriority = Array.isArray(args?.providerPriority) ? args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
|
|
@@ -25283,7 +26878,8 @@ var DaemonCommandRouter = class {
|
|
|
25283
26878
|
...readOnly ? { readOnly: true } : {},
|
|
25284
26879
|
...providerPriority.length ? { providerPriority } : {}
|
|
25285
26880
|
};
|
|
25286
|
-
const
|
|
26881
|
+
const role = normalizeMeshDaemonRole(args?.role);
|
|
26882
|
+
const node = addNode3(meshId, { workspace, ...policy ? { policy } : {}, ...role ? { role } : {} });
|
|
25287
26883
|
if (!node) return { success: false, error: "Mesh not found" };
|
|
25288
26884
|
return { success: true, node };
|
|
25289
26885
|
} catch (e) {
|
|
@@ -25294,6 +26890,8 @@ var DaemonCommandRouter = class {
|
|
|
25294
26890
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25295
26891
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25296
26892
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
26893
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node update");
|
|
26894
|
+
if (ownerFailure) return ownerFailure;
|
|
25297
26895
|
try {
|
|
25298
26896
|
const { updateNode: updateNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25299
26897
|
const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
|
|
@@ -25317,6 +26915,8 @@ var DaemonCommandRouter = class {
|
|
|
25317
26915
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25318
26916
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25319
26917
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
26918
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node removal");
|
|
26919
|
+
if (ownerFailure) return ownerFailure;
|
|
25320
26920
|
try {
|
|
25321
26921
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25322
26922
|
const mesh = meshRecord?.mesh;
|
|
@@ -25339,30 +26939,88 @@ var DaemonCommandRouter = class {
|
|
|
25339
26939
|
return { success: false, error: e.message };
|
|
25340
26940
|
}
|
|
25341
26941
|
}
|
|
26942
|
+
case "get_mesh_refine_config_schema": {
|
|
26943
|
+
return {
|
|
26944
|
+
success: true,
|
|
26945
|
+
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
26946
|
+
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
26947
|
+
sourceOfTruth: "repo mesh/refine config",
|
|
26948
|
+
heuristicRole: "suggestions_only_not_execution_path"
|
|
26949
|
+
};
|
|
26950
|
+
}
|
|
26951
|
+
case "validate_mesh_refine_config": {
|
|
26952
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
26953
|
+
const mesh = args?.inlineMesh || {};
|
|
26954
|
+
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
26955
|
+
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
26956
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
26957
|
+
}
|
|
26958
|
+
case "suggest_mesh_refine_config": {
|
|
26959
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
26960
|
+
const mesh = args?.inlineMesh || {};
|
|
26961
|
+
return {
|
|
26962
|
+
success: true,
|
|
26963
|
+
...suggestMeshRefineConfig(mesh, workspace),
|
|
26964
|
+
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
26965
|
+
};
|
|
26966
|
+
}
|
|
26967
|
+
case "plan_mesh_refine_node": {
|
|
26968
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26969
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
26970
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
26971
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
26972
|
+
const mesh = meshRecord?.mesh;
|
|
26973
|
+
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
26974
|
+
if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
|
|
26975
|
+
return {
|
|
26976
|
+
success: true,
|
|
26977
|
+
dryRun: true,
|
|
26978
|
+
nodeId,
|
|
26979
|
+
workspace: node.workspace,
|
|
26980
|
+
validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
|
|
26981
|
+
mergeWillRun: false,
|
|
26982
|
+
cleanupWillRun: false
|
|
26983
|
+
};
|
|
26984
|
+
}
|
|
25342
26985
|
case "refine_mesh_node": {
|
|
25343
26986
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25344
26987
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25345
26988
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
26989
|
+
const refineStages = [];
|
|
25346
26990
|
try {
|
|
25347
26991
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25348
26992
|
const mesh = meshRecord?.mesh;
|
|
25349
26993
|
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
25350
|
-
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh
|
|
26994
|
+
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
|
|
25351
26995
|
if (!node.isLocalWorktree || !node.workspace) {
|
|
25352
|
-
return { success: false, error: `Refinery requires a local worktree node
|
|
26996
|
+
return { success: false, error: `Refinery requires a local worktree node`, refineStages };
|
|
25353
26997
|
}
|
|
25354
26998
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
25355
26999
|
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
25356
|
-
if (!repoRoot) return { success: false, error: "Source node repoRoot not found" };
|
|
27000
|
+
if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
|
|
25357
27001
|
const { execFile: execFile3 } = await import("child_process");
|
|
25358
27002
|
const { promisify: promisify3 } = await import("util");
|
|
25359
27003
|
const execFileAsync3 = promisify3(execFile3);
|
|
27004
|
+
const resolveStarted = Date.now();
|
|
25360
27005
|
const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
25361
27006
|
const branch = branchStdout.trim();
|
|
25362
|
-
if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
|
|
27007
|
+
if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
|
|
25363
27008
|
const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
25364
27009
|
const baseBranch = baseBranchStdout.trim();
|
|
27010
|
+
const { stdout: baseHeadStdout } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
27011
|
+
const { stdout: branchHeadStdout } = await execFileAsync3("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
27012
|
+
const baseHead = baseHeadStdout.trim();
|
|
27013
|
+
const branchHead = branchHeadStdout.trim();
|
|
27014
|
+
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead });
|
|
27015
|
+
const validationStarted = Date.now();
|
|
25365
27016
|
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
|
|
27017
|
+
recordMeshRefineStage(
|
|
27018
|
+
refineStages,
|
|
27019
|
+
"validation",
|
|
27020
|
+
validationSummary.status === "passed" ? "passed" : validationSummary.status === "failed" ? "failed" : "skipped",
|
|
27021
|
+
validationStarted,
|
|
27022
|
+
{ validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length }
|
|
27023
|
+
);
|
|
25366
27024
|
if (validationSummary.status === "failed") {
|
|
25367
27025
|
return {
|
|
25368
27026
|
success: false,
|
|
@@ -25372,6 +27030,7 @@ var DaemonCommandRouter = class {
|
|
|
25372
27030
|
branch,
|
|
25373
27031
|
into: baseBranch,
|
|
25374
27032
|
validationSummary,
|
|
27033
|
+
refineStages,
|
|
25375
27034
|
finalBranchConvergenceState: {
|
|
25376
27035
|
branch,
|
|
25377
27036
|
baseBranch,
|
|
@@ -25391,6 +27050,7 @@ var DaemonCommandRouter = class {
|
|
|
25391
27050
|
branch,
|
|
25392
27051
|
into: baseBranch,
|
|
25393
27052
|
validationSummary,
|
|
27053
|
+
refineStages,
|
|
25394
27054
|
finalBranchConvergenceState: {
|
|
25395
27055
|
branch,
|
|
25396
27056
|
baseBranch,
|
|
@@ -25401,37 +27061,121 @@ var DaemonCommandRouter = class {
|
|
|
25401
27061
|
}
|
|
25402
27062
|
};
|
|
25403
27063
|
}
|
|
27064
|
+
const patchEquivalenceStarted = Date.now();
|
|
27065
|
+
const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
27066
|
+
recordMeshRefineStage(refineStages, "patch_equivalence", patchEquivalence.status, patchEquivalenceStarted, {
|
|
27067
|
+
equivalent: patchEquivalence.equivalent,
|
|
27068
|
+
expectedPatchId: patchEquivalence.expectedPatchId,
|
|
27069
|
+
actualPatchId: patchEquivalence.actualPatchId,
|
|
27070
|
+
error: patchEquivalence.error
|
|
27071
|
+
});
|
|
27072
|
+
if (!patchEquivalence.equivalent) {
|
|
27073
|
+
return {
|
|
27074
|
+
success: false,
|
|
27075
|
+
code: "patch_equivalence_failed",
|
|
27076
|
+
convergenceStatus: "blocked_review",
|
|
27077
|
+
error: "Refinery patch-equivalence preflight failed; merge/refine was not attempted.",
|
|
27078
|
+
branch,
|
|
27079
|
+
into: baseBranch,
|
|
27080
|
+
validationSummary,
|
|
27081
|
+
patchEquivalence,
|
|
27082
|
+
refineStages,
|
|
27083
|
+
finalBranchConvergenceState: {
|
|
27084
|
+
branch,
|
|
27085
|
+
baseBranch,
|
|
27086
|
+
merged: false,
|
|
27087
|
+
removed: false,
|
|
27088
|
+
validation: "passed",
|
|
27089
|
+
patchEquivalence: "failed",
|
|
27090
|
+
status: "blocked_review"
|
|
27091
|
+
}
|
|
27092
|
+
};
|
|
27093
|
+
}
|
|
27094
|
+
let mergeResult;
|
|
27095
|
+
const mergeStarted = Date.now();
|
|
25404
27096
|
try {
|
|
25405
|
-
await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
27097
|
+
const result = await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
27098
|
+
mergeResult = {
|
|
27099
|
+
stdout: truncateValidationOutput(result.stdout),
|
|
27100
|
+
stderr: truncateValidationOutput(result.stderr),
|
|
27101
|
+
durationMs: Date.now() - mergeStarted
|
|
27102
|
+
};
|
|
27103
|
+
recordMeshRefineStage(refineStages, "merge", "passed", mergeStarted, mergeResult);
|
|
25406
27104
|
} catch (e) {
|
|
27105
|
+
recordMeshRefineStage(refineStages, "merge", "failed", mergeStarted, {
|
|
27106
|
+
error: e?.message || String(e),
|
|
27107
|
+
stdout: truncateValidationOutput(e?.stdout),
|
|
27108
|
+
stderr: truncateValidationOutput(e?.stderr)
|
|
27109
|
+
});
|
|
25407
27110
|
return {
|
|
25408
27111
|
success: false,
|
|
25409
27112
|
error: `Merge failed (conflicts?): ${e.message}`,
|
|
25410
27113
|
validationSummary,
|
|
27114
|
+
patchEquivalence,
|
|
27115
|
+
refineStages,
|
|
25411
27116
|
finalBranchConvergenceState: {
|
|
25412
27117
|
branch,
|
|
25413
27118
|
baseBranch,
|
|
25414
27119
|
merged: false,
|
|
25415
27120
|
removed: false,
|
|
25416
27121
|
validation: "passed",
|
|
27122
|
+
patchEquivalence: "passed",
|
|
25417
27123
|
status: "not_mergeable"
|
|
25418
27124
|
}
|
|
25419
27125
|
};
|
|
25420
27126
|
}
|
|
27127
|
+
const cleanupStarted = Date.now();
|
|
25421
27128
|
const removeResult = await this.execute("remove_mesh_node", {
|
|
25422
27129
|
meshId,
|
|
25423
27130
|
nodeId,
|
|
25424
|
-
sessionCleanupMode: "
|
|
27131
|
+
sessionCleanupMode: "preserve",
|
|
25425
27132
|
inlineMesh: args?.inlineMesh
|
|
25426
27133
|
});
|
|
27134
|
+
recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
|
|
27135
|
+
removed: removeResult?.removed,
|
|
27136
|
+
code: removeResult?.code,
|
|
27137
|
+
error: removeResult?.error
|
|
27138
|
+
});
|
|
27139
|
+
let ledgerError;
|
|
27140
|
+
const ledgerStarted = Date.now();
|
|
25427
27141
|
try {
|
|
25428
27142
|
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
25429
27143
|
appendLedgerEntry2(meshId, {
|
|
25430
27144
|
kind: "node_removed",
|
|
25431
27145
|
nodeId,
|
|
25432
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
|
|
27146
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence }
|
|
25433
27147
|
});
|
|
25434
|
-
|
|
27148
|
+
recordMeshRefineStage(refineStages, "ledger", "passed", ledgerStarted);
|
|
27149
|
+
} catch (e) {
|
|
27150
|
+
ledgerError = e?.message || String(e);
|
|
27151
|
+
recordMeshRefineStage(refineStages, "ledger", "failed", ledgerStarted, { error: ledgerError });
|
|
27152
|
+
}
|
|
27153
|
+
const finalBranchConvergenceState = {
|
|
27154
|
+
branch: baseBranch,
|
|
27155
|
+
mergedBranch: branch,
|
|
27156
|
+
baseBranch,
|
|
27157
|
+
merged: true,
|
|
27158
|
+
removed: removeResult?.success !== false,
|
|
27159
|
+
validation: "passed",
|
|
27160
|
+
patchEquivalence: "passed",
|
|
27161
|
+
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
27162
|
+
};
|
|
27163
|
+
if (removeResult?.success === false) {
|
|
27164
|
+
return {
|
|
27165
|
+
success: false,
|
|
27166
|
+
code: "cleanup_failed",
|
|
27167
|
+
error: "Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.",
|
|
27168
|
+
merged: true,
|
|
27169
|
+
branch,
|
|
27170
|
+
into: baseBranch,
|
|
27171
|
+
removeResult,
|
|
27172
|
+
validationSummary,
|
|
27173
|
+
patchEquivalence,
|
|
27174
|
+
mergeResult,
|
|
27175
|
+
refineStages,
|
|
27176
|
+
...ledgerError ? { ledgerError } : {},
|
|
27177
|
+
finalBranchConvergenceState
|
|
27178
|
+
};
|
|
25435
27179
|
}
|
|
25436
27180
|
return {
|
|
25437
27181
|
success: true,
|
|
@@ -25440,18 +27184,14 @@ var DaemonCommandRouter = class {
|
|
|
25440
27184
|
into: baseBranch,
|
|
25441
27185
|
removeResult,
|
|
25442
27186
|
validationSummary,
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25447
|
-
|
|
25448
|
-
removed: removeResult?.success !== false,
|
|
25449
|
-
validation: "passed",
|
|
25450
|
-
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
25451
|
-
}
|
|
27187
|
+
patchEquivalence,
|
|
27188
|
+
mergeResult,
|
|
27189
|
+
refineStages,
|
|
27190
|
+
...ledgerError ? { ledgerError } : {},
|
|
27191
|
+
finalBranchConvergenceState
|
|
25452
27192
|
};
|
|
25453
27193
|
} catch (e) {
|
|
25454
|
-
return { success: false, error: e.message };
|
|
27194
|
+
return { success: false, error: e.message, refineStages };
|
|
25455
27195
|
}
|
|
25456
27196
|
}
|
|
25457
27197
|
case "remove_mesh_node": {
|
|
@@ -25492,6 +27232,7 @@ var DaemonCommandRouter = class {
|
|
|
25492
27232
|
} else {
|
|
25493
27233
|
const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25494
27234
|
removed = removeNode3(meshId, nodeId);
|
|
27235
|
+
if (removed) this.invalidateAggregateMeshStatus(meshId);
|
|
25495
27236
|
}
|
|
25496
27237
|
if (removed) {
|
|
25497
27238
|
try {
|
|
@@ -25526,6 +27267,8 @@ var DaemonCommandRouter = class {
|
|
|
25526
27267
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25527
27268
|
if (!sourceNodeId) return { success: false, error: "sourceNodeId required" };
|
|
25528
27269
|
if (!branch) return { success: false, error: "branch required" };
|
|
27270
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "worktree clone");
|
|
27271
|
+
if (ownerFailure) return ownerFailure;
|
|
25529
27272
|
try {
|
|
25530
27273
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25531
27274
|
const mesh = meshRecord?.mesh;
|
|
@@ -25570,6 +27313,7 @@ var DaemonCommandRouter = class {
|
|
|
25570
27313
|
policy: { ...sourceNode.policy || {} }
|
|
25571
27314
|
});
|
|
25572
27315
|
if (!node) return { success: false, error: "Failed to register worktree node" };
|
|
27316
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
25573
27317
|
}
|
|
25574
27318
|
const initSubmodules = sourceNode.policy?.initSubmodulesOnClone !== false;
|
|
25575
27319
|
if (initSubmodules) {
|
|
@@ -25606,6 +27350,8 @@ var DaemonCommandRouter = class {
|
|
|
25606
27350
|
case "trigger_mesh_queue": {
|
|
25607
27351
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25608
27352
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
27353
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue trigger");
|
|
27354
|
+
if (ownerFailure) return ownerFailure;
|
|
25609
27355
|
try {
|
|
25610
27356
|
const { triggerMeshQueue: triggerMeshQueue2 } = await Promise.resolve().then(() => (init_mesh_events(), mesh_events_exports));
|
|
25611
27357
|
if (meshId) {
|
|
@@ -25632,6 +27378,15 @@ var DaemonCommandRouter = class {
|
|
|
25632
27378
|
mesh = getMesh3(meshId);
|
|
25633
27379
|
}
|
|
25634
27380
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
27381
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
27382
|
+
if (!meshHost.canOwnCoordinator) {
|
|
27383
|
+
return {
|
|
27384
|
+
success: false,
|
|
27385
|
+
...buildMeshHostRequiredFailure(mesh, "coordinator launch"),
|
|
27386
|
+
meshId,
|
|
27387
|
+
cliType
|
|
27388
|
+
};
|
|
27389
|
+
}
|
|
25635
27390
|
if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: "No nodes in mesh" };
|
|
25636
27391
|
const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === "string" ? args.coordinatorNodeId.trim() : "";
|
|
25637
27392
|
const preferredCoordinatorNodeId = requestedCoordinatorNodeId || (typeof mesh.coordinator?.preferredNodeId === "string" ? mesh.coordinator.preferredNodeId.trim() : "");
|
|
@@ -25645,7 +27400,14 @@ var DaemonCommandRouter = class {
|
|
|
25645
27400
|
cliType
|
|
25646
27401
|
};
|
|
25647
27402
|
}
|
|
25648
|
-
const
|
|
27403
|
+
const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
|
|
27404
|
+
const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
|
|
27405
|
+
const workspace = readLiveMeshNodeWorkspace({
|
|
27406
|
+
meshId,
|
|
27407
|
+
nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ""),
|
|
27408
|
+
liveSessionRecords: liveMeshSessions,
|
|
27409
|
+
allowCoordinatorSession: true
|
|
27410
|
+
}) || (typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "");
|
|
25649
27411
|
if (!workspace) return { success: false, error: "Coordinator node workspace required", meshId, cliType };
|
|
25650
27412
|
if (!cliType) {
|
|
25651
27413
|
const resolved = await resolveProviderTypeFromPriority({
|
|
@@ -25807,7 +27569,7 @@ ${block}`);
|
|
|
25807
27569
|
workspace
|
|
25808
27570
|
};
|
|
25809
27571
|
}
|
|
25810
|
-
const { existsSync:
|
|
27572
|
+
const { existsSync: existsSync27, readFileSync: readFileSync19, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
|
|
25811
27573
|
const { dirname: dirname9 } = await import("path");
|
|
25812
27574
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
25813
27575
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -25850,14 +27612,14 @@ ${block}`);
|
|
|
25850
27612
|
if (hermesManualFallback) return returnManualFallback(message);
|
|
25851
27613
|
return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
|
|
25852
27614
|
}
|
|
25853
|
-
const hadExistingMcpConfig =
|
|
27615
|
+
const hadExistingMcpConfig = existsSync27(mcpConfigPath);
|
|
25854
27616
|
let existingMcpConfig = hermesBaseConfig?.config || {};
|
|
25855
27617
|
if (hermesBaseConfig) {
|
|
25856
27618
|
copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
|
|
25857
27619
|
}
|
|
25858
27620
|
if (hadExistingMcpConfig) {
|
|
25859
27621
|
try {
|
|
25860
|
-
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(
|
|
27622
|
+
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync19(mcpConfigPath, "utf-8"), configFormat);
|
|
25861
27623
|
const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
|
|
25862
27624
|
existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
|
|
25863
27625
|
copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
|
|
@@ -25947,62 +27709,321 @@ ${block}`);
|
|
|
25947
27709
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
25948
27710
|
const mesh = meshRecord?.mesh;
|
|
25949
27711
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
27712
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
27713
|
+
const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
|
|
27714
|
+
const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
|
|
27715
|
+
if (!refreshRequested) {
|
|
27716
|
+
const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
|
|
27717
|
+
if (cachedStatus) {
|
|
27718
|
+
logRepoMeshStatusDebug("return_cached", {
|
|
27719
|
+
meshId,
|
|
27720
|
+
command: "mesh_status",
|
|
27721
|
+
refreshRequested,
|
|
27722
|
+
summary: summarizeRepoMeshStatusDebug(cachedStatus)
|
|
27723
|
+
});
|
|
27724
|
+
return cachedStatus;
|
|
27725
|
+
}
|
|
27726
|
+
}
|
|
27727
|
+
const refreshReason = refreshRequested ? "explicit_refresh" : hadAggregateCache ? "stale_pending_cache_refresh" : "cold_cache_miss";
|
|
25950
27728
|
const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
25951
27729
|
const queue = getQueue2(meshId);
|
|
25952
27730
|
const queueSummary = getMeshQueueStats2(meshId);
|
|
25953
27731
|
const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
25954
27732
|
const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
|
|
25955
27733
|
const ledgerSummary = getLedgerSummary2(meshId);
|
|
27734
|
+
const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
|
|
27735
|
+
const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
|
|
27736
|
+
const localMachineId = loadConfig().machineId || "";
|
|
27737
|
+
const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
|
|
27738
|
+
const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
|
|
27739
|
+
mesh,
|
|
27740
|
+
meshSource: meshRecord.source,
|
|
27741
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
27742
|
+
statusInstanceId: this.deps.statusInstanceId,
|
|
27743
|
+
localMachineId
|
|
27744
|
+
}) : {
|
|
27745
|
+
directEvidenceCount: 0,
|
|
27746
|
+
localConfirmedCount: 0,
|
|
27747
|
+
peerAttemptedCount: 0,
|
|
27748
|
+
peerConfirmedCount: 0,
|
|
27749
|
+
unavailableNodeIds: []
|
|
27750
|
+
};
|
|
27751
|
+
const passivePeerTruthNotAttempted = requireDirectPeerTruth && !refreshRequested && directTruth.directEvidenceCount > 0 && directTruth.peerAttemptedCount === 0;
|
|
27752
|
+
const effectiveDirectTruth = passivePeerTruthNotAttempted ? { ...directTruth, unavailableNodeIds: [] } : directTruth;
|
|
27753
|
+
const directTruthSatisfied = !requireDirectPeerTruth || effectiveDirectTruth.directEvidenceCount > 0 && effectiveDirectTruth.unavailableNodeIds.length === 0;
|
|
27754
|
+
if (requireDirectPeerTruth && !directTruthSatisfied) {
|
|
27755
|
+
const failureResult = {
|
|
27756
|
+
success: false,
|
|
27757
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
27758
|
+
error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.",
|
|
27759
|
+
sourceOfTruth: {
|
|
27760
|
+
membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
27761
|
+
coordinatorOwnsLiveTruth: false,
|
|
27762
|
+
currentStatus: "direct_peer_truth_unavailable",
|
|
27763
|
+
directPeerTruth: {
|
|
27764
|
+
required: true,
|
|
27765
|
+
satisfied: false,
|
|
27766
|
+
directEvidenceCount: directTruth.directEvidenceCount,
|
|
27767
|
+
localConfirmedCount: directTruth.localConfirmedCount,
|
|
27768
|
+
peerAttemptedCount: directTruth.peerAttemptedCount,
|
|
27769
|
+
peerConfirmedCount: directTruth.peerConfirmedCount,
|
|
27770
|
+
unavailableNodeIds: directTruth.unavailableNodeIds
|
|
27771
|
+
}
|
|
27772
|
+
}
|
|
27773
|
+
};
|
|
27774
|
+
logRepoMeshStatusDebug("direct_truth_unavailable", {
|
|
27775
|
+
meshId,
|
|
27776
|
+
command: "mesh_status",
|
|
27777
|
+
refreshRequested,
|
|
27778
|
+
meshSource: meshRecord.source,
|
|
27779
|
+
directTruth
|
|
27780
|
+
});
|
|
27781
|
+
return failureResult;
|
|
27782
|
+
}
|
|
27783
|
+
const directTruthUnavailableNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
|
|
27784
|
+
const selectedCoordinatorNodeId = readStringValue(
|
|
27785
|
+
mesh.coordinator?.preferredNodeId,
|
|
27786
|
+
mesh.nodes?.[0]?.id,
|
|
27787
|
+
mesh.nodes?.[0]?.nodeId
|
|
27788
|
+
);
|
|
27789
|
+
const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? selectedCoordinatorNodeId : void 0;
|
|
27790
|
+
const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25956
27791
|
const nodeStatuses = [];
|
|
25957
|
-
for (const node of mesh.nodes || []) {
|
|
27792
|
+
for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
|
|
27793
|
+
const nodeId = String(node.id || node.nodeId || "");
|
|
27794
|
+
const daemonId = readStringValue(node.daemonId);
|
|
27795
|
+
const providerPriority = readProviderPriorityFromPolicy(node.policy);
|
|
27796
|
+
const isSelfNode = Boolean(
|
|
27797
|
+
nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
|
|
27798
|
+
) || Boolean(
|
|
27799
|
+
daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
|
|
27800
|
+
) || Boolean(meshRecord?.inline && nodeIndex === 0);
|
|
25958
27801
|
const status = {
|
|
25959
|
-
nodeId
|
|
27802
|
+
nodeId,
|
|
25960
27803
|
machineLabel: node.machineLabel || node.id || node.nodeId,
|
|
25961
27804
|
workspace: node.workspace,
|
|
25962
27805
|
repoRoot: node.repoRoot,
|
|
25963
27806
|
isLocalWorktree: node.isLocalWorktree,
|
|
25964
27807
|
worktreeBranch: node.worktreeBranch,
|
|
25965
|
-
|
|
27808
|
+
role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? "host" : void 0),
|
|
27809
|
+
daemonId,
|
|
25966
27810
|
machineId: node.machineId,
|
|
27811
|
+
machineStatus: node.machineStatus,
|
|
25967
27812
|
health: "unknown",
|
|
25968
27813
|
providers: node.providers || [],
|
|
25969
|
-
|
|
27814
|
+
providerPriority,
|
|
27815
|
+
activeSessions: [],
|
|
27816
|
+
activeSessionDetails: [],
|
|
27817
|
+
launchReady: false
|
|
25970
27818
|
};
|
|
25971
|
-
if (
|
|
25972
|
-
|
|
25973
|
-
|
|
25974
|
-
|
|
27819
|
+
if (isSelfNode) {
|
|
27820
|
+
status.connection = {
|
|
27821
|
+
perspective: "selected_coordinator",
|
|
27822
|
+
source: "mesh_peer_status",
|
|
27823
|
+
state: "self",
|
|
27824
|
+
transport: "local",
|
|
27825
|
+
reported: true,
|
|
27826
|
+
reason: "Selected coordinator daemon",
|
|
27827
|
+
lastStateChangeAt: refreshedAt
|
|
27828
|
+
};
|
|
27829
|
+
} else if (daemonId) {
|
|
27830
|
+
const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
|
|
27831
|
+
status.connection = connection ?? {
|
|
27832
|
+
perspective: "selected_coordinator",
|
|
27833
|
+
source: "not_reported",
|
|
27834
|
+
state: "unknown",
|
|
27835
|
+
transport: "unknown",
|
|
27836
|
+
reported: false,
|
|
27837
|
+
reason: "No live mesh peer telemetry reported by the selected coordinator yet."
|
|
27838
|
+
};
|
|
27839
|
+
} else {
|
|
27840
|
+
status.connection = {
|
|
27841
|
+
perspective: "selected_coordinator",
|
|
27842
|
+
source: "not_reported",
|
|
27843
|
+
state: "unknown",
|
|
27844
|
+
transport: "unknown",
|
|
27845
|
+
reported: false,
|
|
27846
|
+
reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
|
|
27847
|
+
};
|
|
27848
|
+
}
|
|
27849
|
+
const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
|
|
27850
|
+
meshId,
|
|
27851
|
+
node,
|
|
27852
|
+
nodeId,
|
|
27853
|
+
liveSessionRecords: liveMeshSessions,
|
|
27854
|
+
allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
|
|
27855
|
+
});
|
|
27856
|
+
const workspace = readLiveMeshNodeWorkspace({
|
|
27857
|
+
meshId,
|
|
27858
|
+
nodeId,
|
|
27859
|
+
liveSessionRecords: matchedLiveSessionRecords,
|
|
27860
|
+
allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
|
|
27861
|
+
}) || (typeof node.workspace === "string" ? node.workspace : "");
|
|
27862
|
+
status.workspace = workspace || node.workspace;
|
|
27863
|
+
if (matchedLiveSessionRecords.length > 0) {
|
|
27864
|
+
const sessionIds = matchedLiveSessionRecords.map((record) => typeof record?.sessionId === "string" ? record.sessionId : "").filter(Boolean);
|
|
27865
|
+
const providerTypes = matchedLiveSessionRecords.map((record) => readStringValue(record?.providerType)).filter(Boolean);
|
|
27866
|
+
status.activeSessions = sessionIds;
|
|
27867
|
+
status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
|
|
27868
|
+
if (providerTypes.length > 0) {
|
|
27869
|
+
status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
|
|
25975
27870
|
}
|
|
25976
|
-
|
|
25977
|
-
|
|
25978
|
-
|
|
25979
|
-
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
|
|
25983
|
-
status.health = "degraded";
|
|
25984
|
-
|
|
27871
|
+
}
|
|
27872
|
+
if (workspace) {
|
|
27873
|
+
if (!fs10.existsSync(workspace)) {
|
|
27874
|
+
const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
|
|
27875
|
+
let remoteProbeApplied = false;
|
|
27876
|
+
if (inlineTransitGit) {
|
|
27877
|
+
status.git = inlineTransitGit;
|
|
27878
|
+
status.health = inlineTransitGit.isGitRepo ? deriveMeshNodeHealthFromGit(inlineTransitGit) : "degraded";
|
|
27879
|
+
const connection = readObjectRecord(status.connection);
|
|
27880
|
+
const connectionState = readStringValue(connection.state);
|
|
27881
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
27882
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
27883
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
27884
|
+
}
|
|
27885
|
+
remoteProbeApplied = true;
|
|
27886
|
+
} else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
|
|
27887
|
+
try {
|
|
27888
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
27889
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
27890
|
+
daemonId,
|
|
27891
|
+
workspace,
|
|
27892
|
+
timeoutMs: 8e3
|
|
27893
|
+
});
|
|
27894
|
+
if (remoteGit) {
|
|
27895
|
+
status.git = remoteGit;
|
|
27896
|
+
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|
|
27897
|
+
const connection = readObjectRecord(status.connection);
|
|
27898
|
+
const connectionState = readStringValue(connection.state);
|
|
27899
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
27900
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
27901
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
27902
|
+
}
|
|
27903
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
27904
|
+
remoteProbeApplied = true;
|
|
27905
|
+
}
|
|
27906
|
+
} catch {
|
|
27907
|
+
const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
|
|
27908
|
+
const refreshedConnectionState = readStringValue(refreshedConnection?.state);
|
|
27909
|
+
if (refreshedConnection && refreshedConnectionState === "connected") {
|
|
27910
|
+
status.connection = refreshedConnection;
|
|
27911
|
+
try {
|
|
27912
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
27913
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
27914
|
+
daemonId,
|
|
27915
|
+
workspace,
|
|
27916
|
+
timeoutMs: 12e3
|
|
27917
|
+
});
|
|
27918
|
+
if (remoteGit) {
|
|
27919
|
+
status.git = remoteGit;
|
|
27920
|
+
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|
|
27921
|
+
const connection = readObjectRecord(status.connection);
|
|
27922
|
+
const connectionState = readStringValue(connection.state);
|
|
27923
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
27924
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
27925
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
27926
|
+
}
|
|
27927
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
27928
|
+
remoteProbeApplied = true;
|
|
27929
|
+
}
|
|
27930
|
+
} catch {
|
|
27931
|
+
}
|
|
27932
|
+
}
|
|
27933
|
+
}
|
|
25985
27934
|
}
|
|
25986
|
-
|
|
25987
|
-
|
|
25988
|
-
status.health
|
|
27935
|
+
if (!remoteProbeApplied) {
|
|
27936
|
+
const connectionState = readStringValue(status.connection?.state);
|
|
27937
|
+
const pendingPeerGitProbe = !inlineTransitGit && !isSelfNode && !!daemonId && (readStringValue(status.machineStatus) === "online" || readStringValue(status.health) === "online" || connectionState === "connecting" || connectionState === "connected" || connectionState === "unknown");
|
|
27938
|
+
if (pendingPeerGitProbe) {
|
|
27939
|
+
status.gitProbePending = true;
|
|
27940
|
+
status.health = "unknown";
|
|
27941
|
+
}
|
|
27942
|
+
if (applyCachedInlineMeshNodeStatus(
|
|
27943
|
+
status,
|
|
27944
|
+
node,
|
|
27945
|
+
pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
|
|
27946
|
+
)) {
|
|
27947
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
27948
|
+
nodeStatuses.push(status);
|
|
27949
|
+
continue;
|
|
27950
|
+
}
|
|
27951
|
+
if (meshRecord?.source === "inline_cache" && !isSelfNode) {
|
|
27952
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
27953
|
+
nodeStatuses.push(status);
|
|
27954
|
+
continue;
|
|
27955
|
+
}
|
|
27956
|
+
}
|
|
27957
|
+
} else {
|
|
27958
|
+
try {
|
|
27959
|
+
const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
|
|
27960
|
+
status.git = gitStatus;
|
|
27961
|
+
recordInlineMeshDirectGitTruth(node, gitStatus, "selected_coordinator_local_git");
|
|
27962
|
+
if (gitStatus.isGitRepo) {
|
|
27963
|
+
status.health = deriveMeshNodeHealthFromGit(gitStatus);
|
|
27964
|
+
} else {
|
|
27965
|
+
status.health = "degraded";
|
|
27966
|
+
if (gitStatus.error && !status.error) status.error = gitStatus.error;
|
|
27967
|
+
}
|
|
27968
|
+
} catch {
|
|
27969
|
+
if (!applyCachedInlineMeshNodeStatus(status, node)) {
|
|
27970
|
+
status.health = "degraded";
|
|
27971
|
+
}
|
|
25989
27972
|
}
|
|
25990
27973
|
}
|
|
25991
27974
|
} else {
|
|
25992
27975
|
applyCachedInlineMeshNodeStatus(status, node);
|
|
25993
27976
|
}
|
|
27977
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
25994
27978
|
nodeStatuses.push(status);
|
|
25995
27979
|
}
|
|
25996
|
-
|
|
27980
|
+
const statusResult = {
|
|
25997
27981
|
success: true,
|
|
25998
27982
|
meshId: mesh.id,
|
|
25999
27983
|
meshName: mesh.name,
|
|
26000
27984
|
repoIdentity: mesh.repoIdentity,
|
|
26001
27985
|
defaultBranch: mesh.defaultBranch,
|
|
27986
|
+
refreshedAt,
|
|
27987
|
+
meshHost,
|
|
27988
|
+
sourceOfTruth: {
|
|
27989
|
+
membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
27990
|
+
coordinatorOwnsLiveTruth: directTruthSatisfied,
|
|
27991
|
+
meshHost: {
|
|
27992
|
+
owner: "mesh_host_daemon",
|
|
27993
|
+
localRole: meshHost.role,
|
|
27994
|
+
hostDaemonId: meshHost.hostDaemonId,
|
|
27995
|
+
hostNodeId: meshHost.hostNodeId,
|
|
27996
|
+
hostAddress: meshHost.hostAddress
|
|
27997
|
+
},
|
|
27998
|
+
...requireDirectPeerTruth ? {
|
|
27999
|
+
currentStatus: directTruthSatisfied ? "live_git_and_session_probes" : "direct_peer_truth_unavailable",
|
|
28000
|
+
directPeerTruth: {
|
|
28001
|
+
required: true,
|
|
28002
|
+
satisfied: directTruthSatisfied,
|
|
28003
|
+
directEvidenceCount: effectiveDirectTruth.directEvidenceCount,
|
|
28004
|
+
localConfirmedCount: effectiveDirectTruth.localConfirmedCount,
|
|
28005
|
+
peerAttemptedCount: effectiveDirectTruth.peerAttemptedCount,
|
|
28006
|
+
peerConfirmedCount: effectiveDirectTruth.peerConfirmedCount,
|
|
28007
|
+
unavailableNodeIds: effectiveDirectTruth.unavailableNodeIds
|
|
28008
|
+
}
|
|
28009
|
+
} : {},
|
|
28010
|
+
historicalEvidenceOnly: ["recoveryHints", "ledger.summary", "queue.summary"]
|
|
28011
|
+
},
|
|
26002
28012
|
nodes: nodeStatuses,
|
|
26003
28013
|
queue: { tasks: queue, summary: queueSummary },
|
|
26004
28014
|
ledger: { entries: ledgerEntries, summary: ledgerSummary }
|
|
26005
28015
|
};
|
|
28016
|
+
const rememberedStatus = this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
|
|
28017
|
+
logRepoMeshStatusDebug("return_live", {
|
|
28018
|
+
meshId,
|
|
28019
|
+
command: "mesh_status",
|
|
28020
|
+
refreshRequested,
|
|
28021
|
+
refreshReason,
|
|
28022
|
+
meshSource: meshRecord.source,
|
|
28023
|
+
directTruth,
|
|
28024
|
+
summary: summarizeRepoMeshStatusDebug(rememberedStatus)
|
|
28025
|
+
});
|
|
28026
|
+
return rememberedStatus;
|
|
26006
28027
|
} catch (e) {
|
|
26007
28028
|
return { success: false, error: e.message };
|
|
26008
28029
|
}
|
|
@@ -33933,6 +35954,8 @@ async function initDaemonComponents(config) {
|
|
|
33933
35954
|
sessionHostControl: config.sessionHostControl,
|
|
33934
35955
|
statusInstanceId: config.statusInstanceId,
|
|
33935
35956
|
statusVersion: config.statusVersion,
|
|
35957
|
+
getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
|
|
35958
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
33936
35959
|
getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
|
|
33937
35960
|
});
|
|
33938
35961
|
poller = new AgentStreamPoller({
|
|
@@ -34060,6 +36083,8 @@ export {
|
|
|
34060
36083
|
InMemoryGitSnapshotStore,
|
|
34061
36084
|
LOG,
|
|
34062
36085
|
MAX_LEDGER_SLICE_LIMIT,
|
|
36086
|
+
MESH_REFINE_CONFIG_LOCATIONS,
|
|
36087
|
+
MESH_REFINE_CONFIG_SCHEMA,
|
|
34063
36088
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
34064
36089
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
34065
36090
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
@@ -34082,6 +36107,7 @@ export {
|
|
|
34082
36107
|
buildChatTailDeliverySignature,
|
|
34083
36108
|
buildCoordinatorSystemPrompt,
|
|
34084
36109
|
buildMachineInfo,
|
|
36110
|
+
buildMeshHostRequiredFailure,
|
|
34085
36111
|
buildMeshLedgerReconciliationEvidence,
|
|
34086
36112
|
buildMeshLedgerReplicaEvidence,
|
|
34087
36113
|
buildP2pRelayFailurePayload,
|
|
@@ -34107,6 +36133,7 @@ export {
|
|
|
34107
36133
|
connectCdpManager,
|
|
34108
36134
|
createDebugTraceStore,
|
|
34109
36135
|
createDefaultGitCommandServices,
|
|
36136
|
+
createDefaultMeshHostMetadata,
|
|
34110
36137
|
createGitCompactSummary,
|
|
34111
36138
|
createGitSnapshotStore,
|
|
34112
36139
|
createGitWorkspaceMonitor,
|
|
@@ -34170,6 +36197,7 @@ export {
|
|
|
34170
36197
|
isInternalChatMessage,
|
|
34171
36198
|
isManagedStatusWaiting,
|
|
34172
36199
|
isManagedStatusWorking,
|
|
36200
|
+
isMeshHostOwner,
|
|
34173
36201
|
isP2pRelayTransportFailure,
|
|
34174
36202
|
isPathInside,
|
|
34175
36203
|
isSessionHostLiveRuntime,
|
|
@@ -34183,6 +36211,7 @@ export {
|
|
|
34183
36211
|
listMeshes,
|
|
34184
36212
|
listWorktrees,
|
|
34185
36213
|
loadConfig,
|
|
36214
|
+
loadMeshRefineConfig,
|
|
34186
36215
|
loadState,
|
|
34187
36216
|
logCommand,
|
|
34188
36217
|
markSetupComplete,
|
|
@@ -34196,6 +36225,7 @@ export {
|
|
|
34196
36225
|
normalizeGitWorkspaceSubscriptionParams,
|
|
34197
36226
|
normalizeInputEnvelope,
|
|
34198
36227
|
normalizeManagedStatus,
|
|
36228
|
+
normalizeMeshDaemonRole,
|
|
34199
36229
|
normalizeMessageParts,
|
|
34200
36230
|
normalizeRepoIdentity,
|
|
34201
36231
|
normalizeSessionModalFields,
|
|
@@ -34207,6 +36237,7 @@ export {
|
|
|
34207
36237
|
prepareSessionChatTailUpdate,
|
|
34208
36238
|
prepareSessionModalUpdate,
|
|
34209
36239
|
probeCdpPort,
|
|
36240
|
+
queuePendingMeshCoordinatorEvent,
|
|
34210
36241
|
readChatHistory,
|
|
34211
36242
|
readLedgerEntries,
|
|
34212
36243
|
readLedgerSlice,
|
|
@@ -34215,6 +36246,7 @@ export {
|
|
|
34215
36246
|
removeNode,
|
|
34216
36247
|
removeWorktree,
|
|
34217
36248
|
requeueTask,
|
|
36249
|
+
requireMeshHostQueueOwner,
|
|
34218
36250
|
resetConfig,
|
|
34219
36251
|
resetDebugRuntimeConfig,
|
|
34220
36252
|
resetState,
|
|
@@ -34222,6 +36254,8 @@ export {
|
|
|
34222
36254
|
resolveCurrentGlobalInstallSurface,
|
|
34223
36255
|
resolveDebugRuntimeConfig,
|
|
34224
36256
|
resolveGitRepository,
|
|
36257
|
+
resolveMeshHostStatus,
|
|
36258
|
+
resolveMeshRefineValidationPlan,
|
|
34225
36259
|
resolveSessionHostAppName,
|
|
34226
36260
|
resolveSessionHostAppNameResolution,
|
|
34227
36261
|
resolveWorktreePath,
|
|
@@ -34237,6 +36271,7 @@ export {
|
|
|
34237
36271
|
shutdownDaemonComponents,
|
|
34238
36272
|
spawnDetachedDaemonUpgradeHelper,
|
|
34239
36273
|
startDaemonDevSupport,
|
|
36274
|
+
suggestMeshRefineConfig,
|
|
34240
36275
|
summarizeGitStatus,
|
|
34241
36276
|
syncMeshes,
|
|
34242
36277
|
triggerMeshQueue,
|
|
@@ -34245,6 +36280,7 @@ export {
|
|
|
34245
36280
|
updateNode,
|
|
34246
36281
|
updateSessionTaskStatus,
|
|
34247
36282
|
updateTaskStatus,
|
|
34248
|
-
upsertSavedProviderSession
|
|
36283
|
+
upsertSavedProviderSession,
|
|
36284
|
+
validateMeshRefineConfig
|
|
34249
36285
|
};
|
|
34250
36286
|
//# sourceMappingURL=index.mjs.map
|