@adhdev/daemon-core 0.9.82-rc.5 → 0.9.82-rc.50
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.js
CHANGED
|
@@ -662,17 +662,91 @@ var init_config = __esm({
|
|
|
662
662
|
}
|
|
663
663
|
});
|
|
664
664
|
|
|
665
|
+
// src/mesh/mesh-host-ownership.ts
|
|
666
|
+
function readObject(value) {
|
|
667
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
668
|
+
}
|
|
669
|
+
function readString(value) {
|
|
670
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
671
|
+
}
|
|
672
|
+
function normalizeMeshDaemonRole(value) {
|
|
673
|
+
return value === "host" || value === "member" ? value : void 0;
|
|
674
|
+
}
|
|
675
|
+
function resolveMeshHostStatus(mesh) {
|
|
676
|
+
const meshRecord = readObject(mesh);
|
|
677
|
+
const raw = readObject(meshRecord?.meshHost);
|
|
678
|
+
const role = normalizeMeshDaemonRole(raw?.role) ?? "host";
|
|
679
|
+
const pairing = readObject(raw?.pairing);
|
|
680
|
+
const normalized = {
|
|
681
|
+
role,
|
|
682
|
+
canOwnCoordinator: role === "host",
|
|
683
|
+
canOwnQueue: role === "host",
|
|
684
|
+
defaulted: !raw
|
|
685
|
+
};
|
|
686
|
+
const hostDaemonId = readString(raw?.hostDaemonId);
|
|
687
|
+
const hostNodeId = readString(raw?.hostNodeId);
|
|
688
|
+
const hostAddress = readString(raw?.hostAddress);
|
|
689
|
+
if (hostDaemonId) normalized.hostDaemonId = hostDaemonId;
|
|
690
|
+
if (hostNodeId) normalized.hostNodeId = hostNodeId;
|
|
691
|
+
if (hostAddress) normalized.hostAddress = hostAddress;
|
|
692
|
+
if (pairing) {
|
|
693
|
+
const status = pairing.status === "pairing" || pairing.status === "paired" || pairing.status === "rejected" || pairing.status === "revoked" ? pairing.status : "not_configured";
|
|
694
|
+
normalized.pairing = {
|
|
695
|
+
status,
|
|
696
|
+
...readString(pairing.tokenId) ? { tokenId: readString(pairing.tokenId) } : {},
|
|
697
|
+
...readString(pairing.joinedAt) ? { joinedAt: readString(pairing.joinedAt) } : {},
|
|
698
|
+
...readString(pairing.lastPairedAt) ? { lastPairedAt: readString(pairing.lastPairedAt) } : {},
|
|
699
|
+
...readString(pairing.lastRejectedAt) ? { lastRejectedAt: readString(pairing.lastRejectedAt) } : {},
|
|
700
|
+
...readString(pairing.expiresAt) ? { expiresAt: readString(pairing.expiresAt) } : {}
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
return normalized;
|
|
704
|
+
}
|
|
705
|
+
function isMeshHostOwner(mesh) {
|
|
706
|
+
return resolveMeshHostStatus(mesh).role === "host";
|
|
707
|
+
}
|
|
708
|
+
function buildMeshHostRequiredFailure(mesh, operation) {
|
|
709
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
710
|
+
return {
|
|
711
|
+
success: false,
|
|
712
|
+
code: "mesh_host_required",
|
|
713
|
+
error: `Mesh Host daemon required for ${operation}; member daemons must pair with the host and cannot own coordinator/queue mutations.`,
|
|
714
|
+
meshHost
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
function requireMeshHostQueueOwner(opts) {
|
|
718
|
+
if (opts?.ownerRole === "member") {
|
|
719
|
+
throw new Error("Mesh Host daemon required to mutate mesh queue; member daemons must use the host-owned queue.");
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function createDefaultMeshHostMetadata() {
|
|
723
|
+
return {
|
|
724
|
+
role: "host",
|
|
725
|
+
pairing: { status: "not_configured" }
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
var init_mesh_host_ownership = __esm({
|
|
729
|
+
"src/mesh/mesh-host-ownership.ts"() {
|
|
730
|
+
"use strict";
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
|
|
665
734
|
// src/config/mesh-config.ts
|
|
666
735
|
var mesh_config_exports = {};
|
|
667
736
|
__export(mesh_config_exports, {
|
|
668
737
|
addNode: () => addNode,
|
|
738
|
+
applyMeshHostJoinRequest: () => applyMeshHostJoinRequest,
|
|
739
|
+
configureMeshHostPairing: () => configureMeshHostPairing,
|
|
669
740
|
createMesh: () => createMesh,
|
|
741
|
+
createMeshHostPairingToken: () => createMeshHostPairingToken,
|
|
670
742
|
deleteMesh: () => deleteMesh,
|
|
671
743
|
getMesh: () => getMesh,
|
|
672
744
|
getMeshByRepo: () => getMeshByRepo,
|
|
673
745
|
listMeshes: () => listMeshes,
|
|
746
|
+
markMeshHostPairingJoined: () => markMeshHostPairingJoined,
|
|
674
747
|
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
675
748
|
removeNode: () => removeNode,
|
|
749
|
+
tokenIdForManualPairing: () => tokenIdForManualPairing,
|
|
676
750
|
updateMesh: () => updateMesh,
|
|
677
751
|
updateNode: () => updateNode
|
|
678
752
|
});
|
|
@@ -748,6 +822,7 @@ function createMesh(opts) {
|
|
|
748
822
|
defaultBranch: opts.defaultBranch,
|
|
749
823
|
policy: mergeMeshPolicy(void 0, opts.policy),
|
|
750
824
|
coordinator: opts.coordinator || {},
|
|
825
|
+
meshHost: opts.meshHost || createDefaultMeshHostMetadata(),
|
|
751
826
|
nodes: [],
|
|
752
827
|
createdAt: now,
|
|
753
828
|
updatedAt: now
|
|
@@ -764,6 +839,7 @@ function updateMesh(meshId, opts) {
|
|
|
764
839
|
if (opts.defaultBranch !== void 0) mesh.defaultBranch = opts.defaultBranch;
|
|
765
840
|
if (opts.policy) mesh.policy = mergeMeshPolicy(mesh.policy, opts.policy);
|
|
766
841
|
if (opts.coordinator) mesh.coordinator = opts.coordinator;
|
|
842
|
+
if (opts.meshHost) mesh.meshHost = opts.meshHost;
|
|
767
843
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
768
844
|
saveMeshConfig(config);
|
|
769
845
|
return mesh;
|
|
@@ -776,6 +852,186 @@ function deleteMesh(meshId) {
|
|
|
776
852
|
saveMeshConfig(config);
|
|
777
853
|
return true;
|
|
778
854
|
}
|
|
855
|
+
function normalizeManualHostAddress(hostAddress) {
|
|
856
|
+
const normalized = hostAddress.trim().replace(/\/+$/, "");
|
|
857
|
+
if (!normalized) throw new Error("hostAddress required");
|
|
858
|
+
let parsed;
|
|
859
|
+
try {
|
|
860
|
+
parsed = new URL(normalized);
|
|
861
|
+
} catch {
|
|
862
|
+
throw new Error("hostAddress must be a valid http(s) or ws(s) URL");
|
|
863
|
+
}
|
|
864
|
+
if (!["http:", "https:", "ws:", "wss:"].includes(parsed.protocol)) {
|
|
865
|
+
throw new Error("hostAddress must use http, https, ws, or wss");
|
|
866
|
+
}
|
|
867
|
+
return normalized;
|
|
868
|
+
}
|
|
869
|
+
function tokenIdForManualPairing(token) {
|
|
870
|
+
return `tok_${(0, import_crypto3.createHash)("sha256").update(token).digest("hex").slice(0, 16)}`;
|
|
871
|
+
}
|
|
872
|
+
function normalizeTokenExpiry(value) {
|
|
873
|
+
if (typeof value !== "string" || !value.trim()) return void 0;
|
|
874
|
+
const date = new Date(value);
|
|
875
|
+
if (Number.isNaN(date.getTime())) throw new Error("expiresAt must be a valid ISO date");
|
|
876
|
+
return date.toISOString();
|
|
877
|
+
}
|
|
878
|
+
function assertPairingTokenValid(pairing, rawToken, nowIso) {
|
|
879
|
+
const token = rawToken.trim();
|
|
880
|
+
if (!token) return { ok: false, reason: "token required" };
|
|
881
|
+
const presentedTokenId = tokenIdForManualPairing(token);
|
|
882
|
+
const expectedTokenId = pairing?.tokenId;
|
|
883
|
+
if (!expectedTokenId || pairing?.status === "not_configured" || pairing?.status === "revoked") {
|
|
884
|
+
return { ok: false, reason: "host pairing token is not configured", presentedTokenId };
|
|
885
|
+
}
|
|
886
|
+
if (pairing.expiresAt && new Date(pairing.expiresAt).getTime() <= new Date(nowIso).getTime()) {
|
|
887
|
+
return { ok: false, reason: "host pairing token expired", expectedTokenId, presentedTokenId };
|
|
888
|
+
}
|
|
889
|
+
if (presentedTokenId !== expectedTokenId) {
|
|
890
|
+
return { ok: false, reason: "invalid pairing token", expectedTokenId, presentedTokenId };
|
|
891
|
+
}
|
|
892
|
+
return { ok: true, tokenId: presentedTokenId };
|
|
893
|
+
}
|
|
894
|
+
function configureMeshHostPairing(meshId, opts) {
|
|
895
|
+
const hostAddress = normalizeManualHostAddress(opts.hostAddress);
|
|
896
|
+
const token = opts.token.trim();
|
|
897
|
+
if (!token) throw new Error("token required");
|
|
898
|
+
const config = loadMeshConfig();
|
|
899
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
900
|
+
if (!mesh) return void 0;
|
|
901
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
902
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
903
|
+
const meshHost = {
|
|
904
|
+
...previous,
|
|
905
|
+
role: "member",
|
|
906
|
+
hostAddress,
|
|
907
|
+
pairing: {
|
|
908
|
+
status: "pairing",
|
|
909
|
+
tokenId: tokenIdForManualPairing(token),
|
|
910
|
+
lastPairedAt: now
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
mesh.meshHost = meshHost;
|
|
914
|
+
mesh.updatedAt = now;
|
|
915
|
+
saveMeshConfig(config);
|
|
916
|
+
return { mesh, meshHost, hostAddress };
|
|
917
|
+
}
|
|
918
|
+
function createMeshHostPairingToken(meshId, opts = {}) {
|
|
919
|
+
const config = loadMeshConfig();
|
|
920
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
921
|
+
if (!mesh) return void 0;
|
|
922
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
923
|
+
const token = (opts.token || `mhj_${(0, import_crypto3.randomBytes)(24).toString("base64url")}`).trim();
|
|
924
|
+
if (!token) throw new Error("token required");
|
|
925
|
+
const tokenId = tokenIdForManualPairing(token);
|
|
926
|
+
const expiresAt = normalizeTokenExpiry(opts.expiresAt);
|
|
927
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
928
|
+
if (previous.role === "member") {
|
|
929
|
+
throw new Error("Mesh Host daemon required to create host pairing tokens; member daemons cannot mint host join tokens.");
|
|
930
|
+
}
|
|
931
|
+
const meshHost = {
|
|
932
|
+
...previous,
|
|
933
|
+
role: "host",
|
|
934
|
+
pairing: {
|
|
935
|
+
status: "pairing",
|
|
936
|
+
tokenId,
|
|
937
|
+
lastPairedAt: now,
|
|
938
|
+
...expiresAt ? { expiresAt } : {}
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
mesh.meshHost = meshHost;
|
|
942
|
+
mesh.updatedAt = now;
|
|
943
|
+
saveMeshConfig(config);
|
|
944
|
+
return { mesh, meshHost, token, tokenId, ...expiresAt ? { expiresAt } : {} };
|
|
945
|
+
}
|
|
946
|
+
function applyMeshHostJoinRequest(meshId, opts) {
|
|
947
|
+
const config = loadMeshConfig();
|
|
948
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
949
|
+
if (!mesh) return void 0;
|
|
950
|
+
const now = opts.now || (/* @__PURE__ */ new Date()).toISOString();
|
|
951
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
952
|
+
if (previous.role === "member") {
|
|
953
|
+
return { accepted: false, mesh, meshHost: previous, reason: "Mesh Host daemon required to accept join requests" };
|
|
954
|
+
}
|
|
955
|
+
const meshHost = { ...previous, role: "host" };
|
|
956
|
+
const validation = assertPairingTokenValid(meshHost.pairing, opts.token, now);
|
|
957
|
+
if (!validation.ok) {
|
|
958
|
+
mesh.meshHost = {
|
|
959
|
+
...meshHost,
|
|
960
|
+
pairing: {
|
|
961
|
+
...meshHost.pairing || { status: "not_configured" },
|
|
962
|
+
status: "rejected",
|
|
963
|
+
lastRejectedAt: now
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
mesh.updatedAt = now;
|
|
967
|
+
saveMeshConfig(config);
|
|
968
|
+
return { accepted: false, mesh, meshHost: mesh.meshHost, tokenId: validation.presentedTokenId, reason: validation.reason };
|
|
969
|
+
}
|
|
970
|
+
const workspace = opts.memberNode.workspace.trim();
|
|
971
|
+
if (!workspace) throw new Error("memberNode.workspace required");
|
|
972
|
+
const memberId = opts.memberNode.id?.trim();
|
|
973
|
+
let node = mesh.nodes.find((n) => memberId && n.id === memberId || n.workspace === workspace);
|
|
974
|
+
if (node) {
|
|
975
|
+
node.workspace = workspace;
|
|
976
|
+
node.repoRoot = opts.memberNode.repoRoot;
|
|
977
|
+
node.daemonId = opts.memberNode.daemonId;
|
|
978
|
+
node.machineId = opts.memberNode.machineId;
|
|
979
|
+
node.userOverrides = opts.memberNode.userOverrides || node.userOverrides || {};
|
|
980
|
+
node.policy = { ...node.policy || {}, ...opts.memberNode.policy || {} };
|
|
981
|
+
node.role = "member";
|
|
982
|
+
} else {
|
|
983
|
+
if (mesh.nodes.length >= 10) throw new Error("Maximum 10 nodes per mesh");
|
|
984
|
+
node = {
|
|
985
|
+
id: memberId || `node_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`,
|
|
986
|
+
workspace,
|
|
987
|
+
repoRoot: opts.memberNode.repoRoot,
|
|
988
|
+
daemonId: opts.memberNode.daemonId,
|
|
989
|
+
machineId: opts.memberNode.machineId,
|
|
990
|
+
userOverrides: opts.memberNode.userOverrides || {},
|
|
991
|
+
policy: opts.memberNode.policy || {},
|
|
992
|
+
role: "member"
|
|
993
|
+
};
|
|
994
|
+
mesh.nodes.push(node);
|
|
995
|
+
}
|
|
996
|
+
mesh.meshHost = {
|
|
997
|
+
...meshHost,
|
|
998
|
+
pairing: {
|
|
999
|
+
...meshHost.pairing || {},
|
|
1000
|
+
status: "paired",
|
|
1001
|
+
tokenId: validation.tokenId,
|
|
1002
|
+
joinedAt: now,
|
|
1003
|
+
lastPairedAt: meshHost.pairing?.lastPairedAt || now,
|
|
1004
|
+
...meshHost.pairing?.expiresAt ? { expiresAt: meshHost.pairing.expiresAt } : {}
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
mesh.updatedAt = now;
|
|
1008
|
+
saveMeshConfig(config);
|
|
1009
|
+
return { accepted: true, mesh, meshHost: mesh.meshHost, node, tokenId: validation.tokenId };
|
|
1010
|
+
}
|
|
1011
|
+
function markMeshHostPairingJoined(meshId, opts) {
|
|
1012
|
+
const config = loadMeshConfig();
|
|
1013
|
+
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
1014
|
+
if (!mesh) return void 0;
|
|
1015
|
+
const now = opts.joinedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
1016
|
+
const previous = mesh.meshHost || createDefaultMeshHostMetadata();
|
|
1017
|
+
const tokenId = opts.tokenId || (opts.token ? tokenIdForManualPairing(opts.token) : previous.pairing?.tokenId);
|
|
1018
|
+
mesh.meshHost = {
|
|
1019
|
+
...previous,
|
|
1020
|
+
role: "member",
|
|
1021
|
+
...opts.hostDaemonId ? { hostDaemonId: opts.hostDaemonId } : {},
|
|
1022
|
+
...opts.hostNodeId ? { hostNodeId: opts.hostNodeId } : {},
|
|
1023
|
+
pairing: {
|
|
1024
|
+
...previous.pairing || {},
|
|
1025
|
+
status: "paired",
|
|
1026
|
+
...tokenId ? { tokenId } : {},
|
|
1027
|
+
joinedAt: now,
|
|
1028
|
+
lastPairedAt: previous.pairing?.lastPairedAt || now
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
mesh.updatedAt = now;
|
|
1032
|
+
saveMeshConfig(config);
|
|
1033
|
+
return { mesh, meshHost: mesh.meshHost };
|
|
1034
|
+
}
|
|
779
1035
|
function addNode(meshId, opts) {
|
|
780
1036
|
const config = loadMeshConfig();
|
|
781
1037
|
const mesh = config.meshes.find((m) => m.id === meshId);
|
|
@@ -796,7 +1052,8 @@ function addNode(meshId, opts) {
|
|
|
796
1052
|
policy: opts.policy || {},
|
|
797
1053
|
isLocalWorktree: opts.isLocalWorktree,
|
|
798
1054
|
worktreeBranch: opts.worktreeBranch,
|
|
799
|
-
clonedFromNodeId: opts.clonedFromNodeId
|
|
1055
|
+
clonedFromNodeId: opts.clonedFromNodeId,
|
|
1056
|
+
role: opts.role
|
|
800
1057
|
};
|
|
801
1058
|
mesh.nodes.push(node);
|
|
802
1059
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -835,6 +1092,7 @@ var init_mesh_config = __esm({
|
|
|
835
1092
|
import_crypto3 = require("crypto");
|
|
836
1093
|
init_config();
|
|
837
1094
|
init_repo_mesh_types();
|
|
1095
|
+
init_mesh_host_ownership();
|
|
838
1096
|
SESSION_CLEANUP_MODES = /* @__PURE__ */ new Set(["preserve", "stop", "delete_stopped", "stop_and_delete"]);
|
|
839
1097
|
SPAWNED_SESSION_VISIBILITY_MODES = /* @__PURE__ */ new Set(["visible", "hidden"]);
|
|
840
1098
|
}
|
|
@@ -1018,19 +1276,19 @@ function isIntentionalCleanupStopEntry(entry) {
|
|
|
1018
1276
|
return payload.intentional === true && (payload.reason === "operator_cleanup" || payload.intentionalStopReason === "operator_cleanup" || payload.source === "mesh_cleanup_sessions" || payload.source === "mesh_remove_node");
|
|
1019
1277
|
}
|
|
1020
1278
|
function getLedgerDir() {
|
|
1021
|
-
const dir = (0,
|
|
1022
|
-
if (!(0,
|
|
1023
|
-
(0,
|
|
1279
|
+
const dir = (0, import_path4.join)(getConfigDir(), LEDGER_DIR_NAME);
|
|
1280
|
+
if (!(0, import_fs4.existsSync)(dir)) {
|
|
1281
|
+
(0, import_fs4.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
1024
1282
|
}
|
|
1025
1283
|
return dir;
|
|
1026
1284
|
}
|
|
1027
1285
|
function getLedgerPath(meshId) {
|
|
1028
1286
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1029
|
-
return (0,
|
|
1287
|
+
return (0, import_path4.join)(getLedgerDir(), `${safe}.jsonl`);
|
|
1030
1288
|
}
|
|
1031
1289
|
function getRotatedPath(meshId, index) {
|
|
1032
1290
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1033
|
-
return (0,
|
|
1291
|
+
return (0, import_path4.join)(getLedgerDir(), `${safe}.${index}.jsonl`);
|
|
1034
1292
|
}
|
|
1035
1293
|
function buildTaskCompletionEvidence(opts) {
|
|
1036
1294
|
const providerSessionId = opts.providerSessionId?.trim() || void 0;
|
|
@@ -1071,9 +1329,9 @@ function appendLedgerEntry(meshId, partial) {
|
|
|
1071
1329
|
...partial
|
|
1072
1330
|
};
|
|
1073
1331
|
const filePath = getLedgerPath(meshId);
|
|
1074
|
-
if ((0,
|
|
1332
|
+
if ((0, import_fs4.existsSync)(filePath)) {
|
|
1075
1333
|
try {
|
|
1076
|
-
const stat2 = (0,
|
|
1334
|
+
const stat2 = (0, import_fs4.statSync)(filePath);
|
|
1077
1335
|
if (stat2.size >= MAX_FILE_SIZE_BYTES) {
|
|
1078
1336
|
rotateLedgerFile(meshId, filePath);
|
|
1079
1337
|
}
|
|
@@ -1082,7 +1340,7 @@ function appendLedgerEntry(meshId, partial) {
|
|
|
1082
1340
|
}
|
|
1083
1341
|
try {
|
|
1084
1342
|
const line = JSON.stringify(entry) + "\n";
|
|
1085
|
-
(0,
|
|
1343
|
+
(0, import_fs4.appendFileSync)(filePath, line, { encoding: "utf-8", mode: 384 });
|
|
1086
1344
|
meshLedgerEvents.emit("append", meshId, entry);
|
|
1087
1345
|
return entry;
|
|
1088
1346
|
} catch (e) {
|
|
@@ -1127,7 +1385,7 @@ function appendRemoteLedgerEntries(meshId, entries) {
|
|
|
1127
1385
|
}
|
|
1128
1386
|
try {
|
|
1129
1387
|
const lines = validEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
1130
|
-
(0,
|
|
1388
|
+
(0, import_fs4.appendFileSync)(ledgerPath, lines, { encoding: "utf-8", mode: 384 });
|
|
1131
1389
|
for (const entry of validEntries) {
|
|
1132
1390
|
meshLedgerEvents.emit("append", meshId, entry);
|
|
1133
1391
|
}
|
|
@@ -1138,10 +1396,10 @@ function appendRemoteLedgerEntries(meshId, entries) {
|
|
|
1138
1396
|
}
|
|
1139
1397
|
function readLedgerEntries(meshId, opts) {
|
|
1140
1398
|
const filePath = getLedgerPath(meshId);
|
|
1141
|
-
if (!(0,
|
|
1399
|
+
if (!(0, import_fs4.existsSync)(filePath)) return [];
|
|
1142
1400
|
let content;
|
|
1143
1401
|
try {
|
|
1144
|
-
content = (0,
|
|
1402
|
+
content = (0, import_fs4.readFileSync)(filePath, "utf-8");
|
|
1145
1403
|
} catch {
|
|
1146
1404
|
return [];
|
|
1147
1405
|
}
|
|
@@ -1313,22 +1571,22 @@ function getSessionRecoveryContext(meshId, opts) {
|
|
|
1313
1571
|
}
|
|
1314
1572
|
function rotateLedgerFile(meshId, currentPath) {
|
|
1315
1573
|
let index = 1;
|
|
1316
|
-
while ((0,
|
|
1574
|
+
while ((0, import_fs4.existsSync)(getRotatedPath(meshId, index))) {
|
|
1317
1575
|
index++;
|
|
1318
1576
|
if (index > 10) break;
|
|
1319
1577
|
}
|
|
1320
1578
|
if (index > 10) index = 10;
|
|
1321
1579
|
try {
|
|
1322
|
-
(0,
|
|
1580
|
+
(0, import_fs4.renameSync)(currentPath, getRotatedPath(meshId, index));
|
|
1323
1581
|
} catch {
|
|
1324
1582
|
}
|
|
1325
1583
|
}
|
|
1326
|
-
var
|
|
1584
|
+
var import_fs4, import_path4, import_crypto4, import_events, LEDGER_DIR_NAME, MAX_FILE_SIZE_BYTES, RECENT_FAILURE_WINDOW_MS, DEFAULT_LEDGER_SLICE_LIMIT, MAX_LEDGER_SLICE_LIMIT, meshLedgerEvents;
|
|
1327
1585
|
var init_mesh_ledger = __esm({
|
|
1328
1586
|
"src/mesh/mesh-ledger.ts"() {
|
|
1329
1587
|
"use strict";
|
|
1330
|
-
|
|
1331
|
-
|
|
1588
|
+
import_fs4 = require("fs");
|
|
1589
|
+
import_path4 = require("path");
|
|
1332
1590
|
import_crypto4 = require("crypto");
|
|
1333
1591
|
init_config();
|
|
1334
1592
|
import_events = require("events");
|
|
@@ -1358,13 +1616,43 @@ __export(mesh_work_queue_exports, {
|
|
|
1358
1616
|
});
|
|
1359
1617
|
function getQueuePath(meshId) {
|
|
1360
1618
|
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1361
|
-
return (0,
|
|
1619
|
+
return (0, import_path5.join)(getLedgerDir(), `${safe}.queue.json`);
|
|
1620
|
+
}
|
|
1621
|
+
function getLockPath(meshId) {
|
|
1622
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1623
|
+
return (0, import_path5.join)(getLedgerDir(), `${safe}.queue.lock`);
|
|
1624
|
+
}
|
|
1625
|
+
function withQueueLock(meshId, fn) {
|
|
1626
|
+
const lockPath = getLockPath(meshId);
|
|
1627
|
+
let fd = -1;
|
|
1628
|
+
for (let i = 0; i < 10; i++) {
|
|
1629
|
+
try {
|
|
1630
|
+
fd = (0, import_fs5.openSync)(lockPath, "wx");
|
|
1631
|
+
break;
|
|
1632
|
+
} catch {
|
|
1633
|
+
const deadline = Date.now() + 30;
|
|
1634
|
+
while (Date.now() < deadline) {
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
try {
|
|
1639
|
+
return fn();
|
|
1640
|
+
} finally {
|
|
1641
|
+
if (fd !== -1) try {
|
|
1642
|
+
(0, import_fs5.closeSync)(fd);
|
|
1643
|
+
} catch {
|
|
1644
|
+
}
|
|
1645
|
+
try {
|
|
1646
|
+
(0, import_fs5.unlinkSync)(lockPath);
|
|
1647
|
+
} catch {
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1362
1650
|
}
|
|
1363
1651
|
function readQueue(meshId) {
|
|
1364
1652
|
const path28 = getQueuePath(meshId);
|
|
1365
|
-
if (!(0,
|
|
1653
|
+
if (!(0, import_fs5.existsSync)(path28)) return [];
|
|
1366
1654
|
try {
|
|
1367
|
-
const content = (0,
|
|
1655
|
+
const content = (0, import_fs5.readFileSync)(path28, "utf-8");
|
|
1368
1656
|
return JSON.parse(content);
|
|
1369
1657
|
} catch {
|
|
1370
1658
|
return [];
|
|
@@ -1372,23 +1660,26 @@ function readQueue(meshId) {
|
|
|
1372
1660
|
}
|
|
1373
1661
|
function writeQueue(meshId, queue) {
|
|
1374
1662
|
const path28 = getQueuePath(meshId);
|
|
1375
|
-
(0,
|
|
1663
|
+
(0, import_fs5.writeFileSync)(path28, JSON.stringify(queue, null, 2), "utf-8");
|
|
1376
1664
|
}
|
|
1377
1665
|
function enqueueTask(meshId, message, opts) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1666
|
+
requireMeshHostQueueOwner(opts);
|
|
1667
|
+
return withQueueLock(meshId, () => {
|
|
1668
|
+
const queue = readQueue(meshId);
|
|
1669
|
+
const entry = {
|
|
1670
|
+
id: (0, import_crypto5.randomUUID)(),
|
|
1671
|
+
meshId,
|
|
1672
|
+
message,
|
|
1673
|
+
status: "pending",
|
|
1674
|
+
targetNodeId: opts?.targetNodeId,
|
|
1675
|
+
targetSessionId: opts?.targetSessionId,
|
|
1676
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1677
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1678
|
+
};
|
|
1679
|
+
queue.push(entry);
|
|
1680
|
+
writeQueue(meshId, queue);
|
|
1681
|
+
return entry;
|
|
1682
|
+
});
|
|
1392
1683
|
}
|
|
1393
1684
|
function getQueue(meshId, opts) {
|
|
1394
1685
|
let queue = readQueue(meshId);
|
|
@@ -1399,100 +1690,114 @@ function getQueue(meshId, opts) {
|
|
|
1399
1690
|
return queue;
|
|
1400
1691
|
}
|
|
1401
1692
|
function claimNextTask(meshId, nodeId, sessionId) {
|
|
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
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1693
|
+
return withQueueLock(meshId, () => {
|
|
1694
|
+
const queue = readQueue(meshId);
|
|
1695
|
+
const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
|
|
1696
|
+
if (hasActiveAssignment) return null;
|
|
1697
|
+
let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
|
|
1698
|
+
if (targetIdx === -1) {
|
|
1699
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
|
|
1700
|
+
}
|
|
1701
|
+
if (targetIdx === -1) {
|
|
1702
|
+
targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
|
|
1703
|
+
}
|
|
1704
|
+
if (targetIdx === -1) return null;
|
|
1705
|
+
const entry = queue[targetIdx];
|
|
1706
|
+
entry.status = "assigned";
|
|
1707
|
+
entry.assignedNodeId = nodeId;
|
|
1708
|
+
entry.assignedSessionId = sessionId;
|
|
1709
|
+
entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1710
|
+
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1711
|
+
writeQueue(meshId, queue);
|
|
1712
|
+
return entry;
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
function updateTaskStatus(meshId, taskId, status, opts) {
|
|
1716
|
+
requireMeshHostQueueOwner(opts);
|
|
1717
|
+
return withQueueLock(meshId, () => {
|
|
1718
|
+
const queue = readQueue(meshId);
|
|
1719
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1720
|
+
if (idx === -1) return null;
|
|
1721
|
+
queue[idx].status = status;
|
|
1722
|
+
queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1723
|
+
writeQueue(meshId, queue);
|
|
1724
|
+
return queue[idx];
|
|
1725
|
+
});
|
|
1430
1726
|
}
|
|
1431
1727
|
function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
...autoLaunch,
|
|
1438
|
-
updatedAt
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
return queue[idx];
|
|
1728
|
+
return withQueueLock(meshId, () => {
|
|
1729
|
+
const queue = readQueue(meshId);
|
|
1730
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1731
|
+
if (idx === -1) return null;
|
|
1732
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1733
|
+
queue[idx].autoLaunch = { ...autoLaunch, updatedAt: now };
|
|
1734
|
+
queue[idx].updatedAt = now;
|
|
1735
|
+
writeQueue(meshId, queue);
|
|
1736
|
+
return queue[idx];
|
|
1737
|
+
});
|
|
1443
1738
|
}
|
|
1444
1739
|
function cancelTask(meshId, taskId, opts) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1740
|
+
requireMeshHostQueueOwner(opts);
|
|
1741
|
+
return withQueueLock(meshId, () => {
|
|
1742
|
+
const queue = readQueue(meshId);
|
|
1743
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1744
|
+
if (idx === -1) return null;
|
|
1745
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1746
|
+
queue[idx].status = "cancelled";
|
|
1747
|
+
queue[idx].updatedAt = now;
|
|
1748
|
+
queue[idx].cancelledAt = now;
|
|
1749
|
+
if (opts?.reason) queue[idx].cancelReason = opts.reason;
|
|
1750
|
+
writeQueue(meshId, queue);
|
|
1751
|
+
return queue[idx];
|
|
1752
|
+
});
|
|
1455
1753
|
}
|
|
1456
1754
|
function requeueTask(meshId, taskId, opts) {
|
|
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
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1755
|
+
requireMeshHostQueueOwner(opts);
|
|
1756
|
+
return withQueueLock(meshId, () => {
|
|
1757
|
+
const queue = readQueue(meshId);
|
|
1758
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1759
|
+
if (idx === -1) return null;
|
|
1760
|
+
const entry = queue[idx];
|
|
1761
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1762
|
+
entry.status = "pending";
|
|
1763
|
+
delete entry.assignedNodeId;
|
|
1764
|
+
delete entry.assignedSessionId;
|
|
1765
|
+
delete entry.cancelledAt;
|
|
1766
|
+
delete entry.cancelReason;
|
|
1767
|
+
if (opts?.clearTargetNode) delete entry.targetNodeId;
|
|
1768
|
+
if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
|
|
1769
|
+
if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
|
|
1770
|
+
if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
|
|
1771
|
+
entry.updatedAt = now;
|
|
1772
|
+
entry.requeuedAt = now;
|
|
1773
|
+
entry.requeueCount = (entry.requeueCount || 0) + 1;
|
|
1774
|
+
if (opts?.reason) entry.requeueReason = opts.reason;
|
|
1775
|
+
writeQueue(meshId, queue);
|
|
1776
|
+
return entry;
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
function updateSessionTaskStatus(meshId, sessionId, status, opts) {
|
|
1780
|
+
return withQueueLock(meshId, () => {
|
|
1781
|
+
const queue = readQueue(meshId);
|
|
1782
|
+
const occurredAtTime = opts?.occurredAt ? new Date(opts.occurredAt).getTime() : Number.NaN;
|
|
1783
|
+
const hasOccurredAt = Number.isFinite(occurredAtTime);
|
|
1784
|
+
let bestIdx = -1;
|
|
1785
|
+
let bestTime = 0;
|
|
1786
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
1787
|
+
if (queue[i].assignedSessionId !== sessionId || queue[i].status !== "assigned") continue;
|
|
1484
1788
|
const time = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
|
|
1789
|
+
if (hasOccurredAt && Number.isFinite(time) && time > occurredAtTime) continue;
|
|
1485
1790
|
if (time > bestTime) {
|
|
1486
1791
|
bestTime = time;
|
|
1487
1792
|
bestIdx = i;
|
|
1488
1793
|
}
|
|
1489
1794
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1795
|
+
if (bestIdx === -1) return null;
|
|
1796
|
+
queue[bestIdx].status = status;
|
|
1797
|
+
queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1798
|
+
writeQueue(meshId, queue);
|
|
1799
|
+
return queue[bestIdx];
|
|
1800
|
+
});
|
|
1496
1801
|
}
|
|
1497
1802
|
function getMeshQueueStats(meshId) {
|
|
1498
1803
|
const queue = readQueue(meshId);
|
|
@@ -1527,14 +1832,15 @@ function getMeshQueueStats(meshId) {
|
|
|
1527
1832
|
}))
|
|
1528
1833
|
};
|
|
1529
1834
|
}
|
|
1530
|
-
var
|
|
1835
|
+
var import_fs5, import_path5, import_crypto5, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES;
|
|
1531
1836
|
var init_mesh_work_queue = __esm({
|
|
1532
1837
|
"src/mesh/mesh-work-queue.ts"() {
|
|
1533
1838
|
"use strict";
|
|
1534
|
-
|
|
1535
|
-
|
|
1839
|
+
import_fs5 = require("fs");
|
|
1840
|
+
import_path5 = require("path");
|
|
1536
1841
|
import_crypto5 = require("crypto");
|
|
1537
1842
|
init_mesh_ledger();
|
|
1843
|
+
init_mesh_host_ownership();
|
|
1538
1844
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
1539
1845
|
HISTORICAL_MESH_QUEUE_STATUSES = ["completed", "failed", "cancelled"];
|
|
1540
1846
|
}
|
|
@@ -1564,7 +1870,7 @@ function resolveCommandPath(command) {
|
|
|
1564
1870
|
if (isExplicitCommandPath(trimmed)) {
|
|
1565
1871
|
const expanded = expandHome(trimmed);
|
|
1566
1872
|
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
1567
|
-
return (0,
|
|
1873
|
+
return (0, import_fs6.existsSync)(candidate) ? candidate : null;
|
|
1568
1874
|
}
|
|
1569
1875
|
return null;
|
|
1570
1876
|
}
|
|
@@ -1664,14 +1970,14 @@ async function detectCLI(cliId, providerLoader, options) {
|
|
|
1664
1970
|
const all = await detectCLIs(providerLoader, options);
|
|
1665
1971
|
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
1666
1972
|
}
|
|
1667
|
-
var import_child_process, os2, path8,
|
|
1973
|
+
var import_child_process, os2, path8, import_fs6;
|
|
1668
1974
|
var init_cli_detector = __esm({
|
|
1669
1975
|
"src/detection/cli-detector.ts"() {
|
|
1670
1976
|
"use strict";
|
|
1671
1977
|
import_child_process = require("child_process");
|
|
1672
1978
|
os2 = __toESM(require("os"));
|
|
1673
1979
|
path8 = __toESM(require("path"));
|
|
1674
|
-
|
|
1980
|
+
import_fs6 = require("fs");
|
|
1675
1981
|
}
|
|
1676
1982
|
});
|
|
1677
1983
|
|
|
@@ -1896,18 +2202,75 @@ __export(mesh_events_exports, {
|
|
|
1896
2202
|
drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
|
|
1897
2203
|
getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
|
|
1898
2204
|
handleMeshForwardEvent: () => handleMeshForwardEvent,
|
|
2205
|
+
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
1899
2206
|
setupMeshEventForwarding: () => setupMeshEventForwarding,
|
|
1900
2207
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
1901
2208
|
tryAssignQueueTask: () => tryAssignQueueTask
|
|
1902
2209
|
});
|
|
1903
|
-
function
|
|
1904
|
-
|
|
2210
|
+
function sweepExpiredRemoteIdleSessions() {
|
|
2211
|
+
const now = Date.now();
|
|
2212
|
+
for (const [key, session] of remoteIdleSessions) {
|
|
2213
|
+
if (session.expiresAt <= now) remoteIdleSessions.delete(key);
|
|
2214
|
+
}
|
|
1905
2215
|
}
|
|
1906
|
-
function
|
|
1907
|
-
|
|
2216
|
+
function getPendingEventsPath(meshId) {
|
|
2217
|
+
const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2218
|
+
return (0, import_path6.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
|
|
1908
2219
|
}
|
|
1909
|
-
function
|
|
1910
|
-
|
|
2220
|
+
function queuePendingMeshCoordinatorEvent(event) {
|
|
2221
|
+
try {
|
|
2222
|
+
(0, import_fs7.appendFileSync)(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
|
|
2223
|
+
return true;
|
|
2224
|
+
} catch (e) {
|
|
2225
|
+
LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
|
|
2226
|
+
return false;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
function drainPendingMeshCoordinatorEvents(meshId) {
|
|
2230
|
+
if (!meshId) return [];
|
|
2231
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2232
|
+
if (!(0, import_fs7.existsSync)(path28)) return [];
|
|
2233
|
+
try {
|
|
2234
|
+
const raw = (0, import_fs7.readFileSync)(path28, "utf-8");
|
|
2235
|
+
try {
|
|
2236
|
+
(0, import_fs7.unlinkSync)(path28);
|
|
2237
|
+
} catch {
|
|
2238
|
+
}
|
|
2239
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
2240
|
+
try {
|
|
2241
|
+
return [JSON.parse(line)];
|
|
2242
|
+
} catch {
|
|
2243
|
+
return [];
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
} catch {
|
|
2247
|
+
return [];
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
function getPendingMeshCoordinatorEvents(meshId) {
|
|
2251
|
+
if (!meshId) return [];
|
|
2252
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2253
|
+
if (!(0, import_fs7.existsSync)(path28)) return [];
|
|
2254
|
+
try {
|
|
2255
|
+
const raw = (0, import_fs7.readFileSync)(path28, "utf-8");
|
|
2256
|
+
return raw.split("\n").filter(Boolean).flatMap((line) => {
|
|
2257
|
+
try {
|
|
2258
|
+
return [JSON.parse(line)];
|
|
2259
|
+
} catch {
|
|
2260
|
+
return [];
|
|
2261
|
+
}
|
|
2262
|
+
});
|
|
2263
|
+
} catch {
|
|
2264
|
+
return [];
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
function clearPendingMeshCoordinatorEvents(meshId) {
|
|
2268
|
+
if (!meshId) return;
|
|
2269
|
+
const path28 = getPendingEventsPath(meshId);
|
|
2270
|
+
if ((0, import_fs7.existsSync)(path28)) try {
|
|
2271
|
+
(0, import_fs7.unlinkSync)(path28);
|
|
2272
|
+
} catch {
|
|
2273
|
+
}
|
|
1911
2274
|
}
|
|
1912
2275
|
function readNonEmptyString(value) {
|
|
1913
2276
|
return typeof value === "string" && value.trim() ? value.trim() : "";
|
|
@@ -1953,6 +2316,38 @@ function shouldSuppressIntentionalCleanupStop(args) {
|
|
|
1953
2316
|
if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
|
|
1954
2317
|
return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
|
|
1955
2318
|
}
|
|
2319
|
+
function readEventTimestamp(value) {
|
|
2320
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2321
|
+
if (typeof value === "string" && value.trim()) {
|
|
2322
|
+
const numeric = Number(value);
|
|
2323
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
2324
|
+
const parsed = Date.parse(value);
|
|
2325
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
2326
|
+
}
|
|
2327
|
+
return null;
|
|
2328
|
+
}
|
|
2329
|
+
function buildMeshCompletionFingerprint(args) {
|
|
2330
|
+
const timestampPart = Number.isFinite(args.timestamp) ? String(args.timestamp) : readNonEmptyString(args.finalSummary).slice(0, 200);
|
|
2331
|
+
return [
|
|
2332
|
+
args.meshId,
|
|
2333
|
+
args.event,
|
|
2334
|
+
args.sessionId,
|
|
2335
|
+
args.providerType || "",
|
|
2336
|
+
args.providerSessionId || "",
|
|
2337
|
+
timestampPart
|
|
2338
|
+
].join("::");
|
|
2339
|
+
}
|
|
2340
|
+
function isDuplicateMeshCompletionEvent(args) {
|
|
2341
|
+
const fingerprint = buildMeshCompletionFingerprint(args);
|
|
2342
|
+
if (!fingerprint) return false;
|
|
2343
|
+
const now = Date.now();
|
|
2344
|
+
for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
|
|
2345
|
+
if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
|
|
2346
|
+
}
|
|
2347
|
+
if (recentCompletionFingerprints.has(fingerprint)) return true;
|
|
2348
|
+
recentCompletionFingerprints.set(fingerprint, now);
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
1956
2351
|
function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
|
|
1957
2352
|
const task = claimNextTask(meshId, nodeId, sessionId);
|
|
1958
2353
|
if (!task) {
|
|
@@ -1971,7 +2366,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
1971
2366
|
message: task.message
|
|
1972
2367
|
}).catch((e) => {
|
|
1973
2368
|
LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
|
|
1974
|
-
updateTaskStatus(meshId, task.id, "
|
|
2369
|
+
updateTaskStatus(meshId, task.id, "pending");
|
|
2370
|
+
try {
|
|
2371
|
+
appendLedgerEntry(meshId, {
|
|
2372
|
+
kind: "dispatch_failed",
|
|
2373
|
+
nodeId,
|
|
2374
|
+
sessionId,
|
|
2375
|
+
payload: { taskId: task.id, error: e?.message, retryable: true }
|
|
2376
|
+
});
|
|
2377
|
+
} catch {
|
|
2378
|
+
}
|
|
1975
2379
|
});
|
|
1976
2380
|
return true;
|
|
1977
2381
|
}
|
|
@@ -2305,18 +2709,36 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2305
2709
|
LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
|
|
2306
2710
|
return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
|
|
2307
2711
|
}
|
|
2712
|
+
const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
|
|
2713
|
+
if (args.event === "agent:generating_completed" && eventSessionId) {
|
|
2714
|
+
const duplicateCompletion = isDuplicateMeshCompletionEvent({
|
|
2715
|
+
meshId: args.meshId,
|
|
2716
|
+
event: args.event,
|
|
2717
|
+
sessionId: eventSessionId,
|
|
2718
|
+
providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
|
|
2719
|
+
providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
|
|
2720
|
+
timestamp: eventTimestamp,
|
|
2721
|
+
finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
|
|
2722
|
+
});
|
|
2723
|
+
if (duplicateCompletion) {
|
|
2724
|
+
LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
|
|
2725
|
+
return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2308
2728
|
let completedTaskForLedger = null;
|
|
2309
2729
|
if (args.event === "agent:generating_completed") {
|
|
2310
2730
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
2311
2731
|
const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
|
|
2312
2732
|
const providerType = readNonEmptyString(args.metadataEvent.providerType);
|
|
2313
2733
|
if (sessionId) {
|
|
2314
|
-
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed"
|
|
2734
|
+
const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed", {
|
|
2735
|
+
occurredAt: eventTimestamp !== null ? new Date(eventTimestamp).toISOString() : void 0
|
|
2736
|
+
});
|
|
2315
2737
|
completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
|
|
2316
2738
|
if (nodeId && providerType) {
|
|
2317
|
-
|
|
2739
|
+
setImmediate(() => {
|
|
2318
2740
|
tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2319
|
-
}
|
|
2741
|
+
});
|
|
2320
2742
|
}
|
|
2321
2743
|
}
|
|
2322
2744
|
} else if (args.event === "agent:ready") {
|
|
@@ -2354,13 +2776,17 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2354
2776
|
}
|
|
2355
2777
|
}
|
|
2356
2778
|
if (sessionId && nodeId && providerType) {
|
|
2357
|
-
|
|
2358
|
-
|
|
2779
|
+
sweepExpiredRemoteIdleSessions();
|
|
2780
|
+
remoteIdleSessions.set(`${nodeId}:${sessionId}`, {
|
|
2781
|
+
nodeId,
|
|
2782
|
+
sessionId,
|
|
2783
|
+
providerType,
|
|
2784
|
+
expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
|
|
2785
|
+
});
|
|
2786
|
+
setImmediate(() => {
|
|
2359
2787
|
const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
|
|
2360
|
-
if (assigned) {
|
|
2361
|
-
|
|
2362
|
-
}
|
|
2363
|
-
}, 500);
|
|
2788
|
+
if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
|
|
2789
|
+
});
|
|
2364
2790
|
}
|
|
2365
2791
|
} else if (args.event === "agent:generating_started") {
|
|
2366
2792
|
const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
@@ -2471,17 +2897,18 @@ function injectMeshSystemMessage(components, args) {
|
|
|
2471
2897
|
return true;
|
|
2472
2898
|
});
|
|
2473
2899
|
if (coordinatorInstances.length === 0) {
|
|
2474
|
-
if (
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2900
|
+
if (queuePendingMeshCoordinatorEvent({
|
|
2901
|
+
event: args.event,
|
|
2902
|
+
meshId: args.meshId,
|
|
2903
|
+
nodeLabel: args.nodeLabel,
|
|
2904
|
+
nodeId: args.nodeId || void 0,
|
|
2905
|
+
workspace: readNonEmptyString(args.metadataEvent.workspace),
|
|
2906
|
+
metadataEvent: {
|
|
2907
|
+
...args.metadataEvent,
|
|
2908
|
+
...recoveryContext ? { recoveryContext } : {}
|
|
2909
|
+
},
|
|
2910
|
+
queuedAt: Date.now()
|
|
2911
|
+
})) {
|
|
2485
2912
|
LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
|
|
2486
2913
|
}
|
|
2487
2914
|
return { success: true, forwarded: 0 };
|
|
@@ -2520,6 +2947,7 @@ function handleMeshForwardEvent(components, payload) {
|
|
|
2520
2947
|
providerType: readNonEmptyString(payload.providerType),
|
|
2521
2948
|
providerSessionId: readNonEmptyString(payload.providerSessionId),
|
|
2522
2949
|
finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
|
|
2950
|
+
...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
|
|
2523
2951
|
intentional: payload.intentional === true,
|
|
2524
2952
|
intentionalStop: payload.intentionalStop === true,
|
|
2525
2953
|
operatorCleanup: payload.operatorCleanup === true,
|
|
@@ -2562,19 +2990,20 @@ function setupMeshEventForwarding(components) {
|
|
|
2562
2990
|
});
|
|
2563
2991
|
});
|
|
2564
2992
|
}
|
|
2565
|
-
var
|
|
2993
|
+
var import_fs7, import_path6, 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;
|
|
2566
2994
|
var init_mesh_events = __esm({
|
|
2567
2995
|
"src/mesh/mesh-events.ts"() {
|
|
2568
2996
|
"use strict";
|
|
2997
|
+
import_fs7 = require("fs");
|
|
2998
|
+
import_path6 = require("path");
|
|
2569
2999
|
init_config();
|
|
2570
3000
|
init_mesh_config();
|
|
2571
3001
|
init_cli_detector();
|
|
2572
3002
|
init_logger();
|
|
2573
3003
|
init_mesh_ledger();
|
|
2574
3004
|
init_mesh_work_queue();
|
|
3005
|
+
REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
|
|
2575
3006
|
remoteIdleSessions = /* @__PURE__ */ new Map();
|
|
2576
|
-
MAX_PENDING_EVENTS = 50;
|
|
2577
|
-
pendingMeshCoordinatorEvents = [];
|
|
2578
3007
|
MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
|
|
2579
3008
|
"agent:generating_started",
|
|
2580
3009
|
"agent:generating_completed",
|
|
@@ -2590,6 +3019,8 @@ var init_mesh_events = __esm({
|
|
|
2590
3019
|
"monitor:long_generating": "task_stalled"
|
|
2591
3020
|
};
|
|
2592
3021
|
INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
|
|
3022
|
+
RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
|
|
3023
|
+
recentCompletionFingerprints = /* @__PURE__ */ new Map();
|
|
2593
3024
|
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
2594
3025
|
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
2595
3026
|
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
@@ -5713,6 +6144,8 @@ __export(index_exports, {
|
|
|
5713
6144
|
InMemoryGitSnapshotStore: () => InMemoryGitSnapshotStore,
|
|
5714
6145
|
LOG: () => LOG,
|
|
5715
6146
|
MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
|
|
6147
|
+
MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
|
|
6148
|
+
MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
|
|
5716
6149
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
5717
6150
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
5718
6151
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
@@ -5735,6 +6168,7 @@ __export(index_exports, {
|
|
|
5735
6168
|
buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
|
|
5736
6169
|
buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
|
|
5737
6170
|
buildMachineInfo: () => buildMachineInfo,
|
|
6171
|
+
buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
|
|
5738
6172
|
buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
|
|
5739
6173
|
buildMeshLedgerReplicaEvidence: () => buildMeshLedgerReplicaEvidence,
|
|
5740
6174
|
buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
|
|
@@ -5760,6 +6194,7 @@ __export(index_exports, {
|
|
|
5760
6194
|
connectCdpManager: () => connectCdpManager,
|
|
5761
6195
|
createDebugTraceStore: () => createDebugTraceStore,
|
|
5762
6196
|
createDefaultGitCommandServices: () => createDefaultGitCommandServices,
|
|
6197
|
+
createDefaultMeshHostMetadata: () => createDefaultMeshHostMetadata,
|
|
5763
6198
|
createGitCompactSummary: () => createGitCompactSummary,
|
|
5764
6199
|
createGitSnapshotStore: () => createGitSnapshotStore,
|
|
5765
6200
|
createGitWorkspaceMonitor: () => createGitWorkspaceMonitor,
|
|
@@ -5823,6 +6258,7 @@ __export(index_exports, {
|
|
|
5823
6258
|
isInternalChatMessage: () => isInternalChatMessage,
|
|
5824
6259
|
isManagedStatusWaiting: () => isManagedStatusWaiting,
|
|
5825
6260
|
isManagedStatusWorking: () => isManagedStatusWorking,
|
|
6261
|
+
isMeshHostOwner: () => isMeshHostOwner,
|
|
5826
6262
|
isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
|
|
5827
6263
|
isPathInside: () => isPathInside,
|
|
5828
6264
|
isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
|
|
@@ -5836,6 +6272,7 @@ __export(index_exports, {
|
|
|
5836
6272
|
listMeshes: () => listMeshes,
|
|
5837
6273
|
listWorktrees: () => listWorktrees,
|
|
5838
6274
|
loadConfig: () => loadConfig,
|
|
6275
|
+
loadMeshRefineConfig: () => loadMeshRefineConfig,
|
|
5839
6276
|
loadState: () => loadState,
|
|
5840
6277
|
logCommand: () => logCommand,
|
|
5841
6278
|
markSetupComplete: () => markSetupComplete,
|
|
@@ -5849,6 +6286,7 @@ __export(index_exports, {
|
|
|
5849
6286
|
normalizeGitWorkspaceSubscriptionParams: () => normalizeGitWorkspaceSubscriptionParams,
|
|
5850
6287
|
normalizeInputEnvelope: () => normalizeInputEnvelope,
|
|
5851
6288
|
normalizeManagedStatus: () => normalizeManagedStatus,
|
|
6289
|
+
normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
|
|
5852
6290
|
normalizeMessageParts: () => normalizeMessageParts,
|
|
5853
6291
|
normalizeRepoIdentity: () => normalizeRepoIdentity,
|
|
5854
6292
|
normalizeSessionModalFields: () => normalizeSessionModalFields,
|
|
@@ -5860,6 +6298,7 @@ __export(index_exports, {
|
|
|
5860
6298
|
prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate,
|
|
5861
6299
|
prepareSessionModalUpdate: () => prepareSessionModalUpdate,
|
|
5862
6300
|
probeCdpPort: () => probeCdpPort,
|
|
6301
|
+
queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
|
|
5863
6302
|
readChatHistory: () => readChatHistory,
|
|
5864
6303
|
readLedgerEntries: () => readLedgerEntries,
|
|
5865
6304
|
readLedgerSlice: () => readLedgerSlice,
|
|
@@ -5868,6 +6307,7 @@ __export(index_exports, {
|
|
|
5868
6307
|
removeNode: () => removeNode,
|
|
5869
6308
|
removeWorktree: () => removeWorktree,
|
|
5870
6309
|
requeueTask: () => requeueTask,
|
|
6310
|
+
requireMeshHostQueueOwner: () => requireMeshHostQueueOwner,
|
|
5871
6311
|
resetConfig: () => resetConfig,
|
|
5872
6312
|
resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
|
|
5873
6313
|
resetState: () => resetState,
|
|
@@ -5875,6 +6315,8 @@ __export(index_exports, {
|
|
|
5875
6315
|
resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
|
|
5876
6316
|
resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
|
|
5877
6317
|
resolveGitRepository: () => resolveGitRepository,
|
|
6318
|
+
resolveMeshHostStatus: () => resolveMeshHostStatus,
|
|
6319
|
+
resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
|
|
5878
6320
|
resolveSessionHostAppName: () => resolveSessionHostAppName,
|
|
5879
6321
|
resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution,
|
|
5880
6322
|
resolveWorktreePath: () => resolveWorktreePath,
|
|
@@ -5890,6 +6332,7 @@ __export(index_exports, {
|
|
|
5890
6332
|
shutdownDaemonComponents: () => shutdownDaemonComponents,
|
|
5891
6333
|
spawnDetachedDaemonUpgradeHelper: () => spawnDetachedDaemonUpgradeHelper,
|
|
5892
6334
|
startDaemonDevSupport: () => startDaemonDevSupport,
|
|
6335
|
+
suggestMeshRefineConfig: () => suggestMeshRefineConfig,
|
|
5893
6336
|
summarizeGitStatus: () => summarizeGitStatus,
|
|
5894
6337
|
syncMeshes: () => syncMeshes,
|
|
5895
6338
|
triggerMeshQueue: () => triggerMeshQueue,
|
|
@@ -5898,7 +6341,8 @@ __export(index_exports, {
|
|
|
5898
6341
|
updateNode: () => updateNode,
|
|
5899
6342
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
5900
6343
|
updateTaskStatus: () => updateTaskStatus,
|
|
5901
|
-
upsertSavedProviderSession: () => upsertSavedProviderSession
|
|
6344
|
+
upsertSavedProviderSession: () => upsertSavedProviderSession,
|
|
6345
|
+
validateMeshRefineConfig: () => validateMeshRefineConfig
|
|
5902
6346
|
});
|
|
5903
6347
|
module.exports = __toCommonJS(index_exports);
|
|
5904
6348
|
init_repo_mesh_types();
|
|
@@ -5913,8 +6357,14 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5913
6357
|
const includeSubmodules = options.includeSubmodules !== false;
|
|
5914
6358
|
try {
|
|
5915
6359
|
const repo = await resolveGitRepository(workspace, options);
|
|
5916
|
-
|
|
5917
|
-
|
|
6360
|
+
let parsed = await readPorcelainStatus(repo, options);
|
|
6361
|
+
let upstreamProbe = getInitialUpstreamProbe(parsed);
|
|
6362
|
+
if (options.refreshUpstream) {
|
|
6363
|
+
upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
|
|
6364
|
+
if (upstreamProbe.upstreamStatus === "fresh") {
|
|
6365
|
+
parsed = await readPorcelainStatus(repo, options);
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
5918
6368
|
const head = await readHead(repo, options);
|
|
5919
6369
|
const stashCount = await readStashCount(repo, options);
|
|
5920
6370
|
let submodules;
|
|
@@ -5929,6 +6379,9 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5929
6379
|
headCommit: head.commit,
|
|
5930
6380
|
headMessage: head.message,
|
|
5931
6381
|
upstream: parsed.upstream,
|
|
6382
|
+
upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
|
|
6383
|
+
upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
|
|
6384
|
+
upstreamFetchError: upstreamProbe.upstreamFetchError,
|
|
5932
6385
|
ahead: parsed.ahead,
|
|
5933
6386
|
behind: parsed.behind,
|
|
5934
6387
|
staged: parsed.staged,
|
|
@@ -5953,6 +6406,60 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5953
6406
|
);
|
|
5954
6407
|
}
|
|
5955
6408
|
}
|
|
6409
|
+
async function readPorcelainStatus(repo, options) {
|
|
6410
|
+
const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
|
|
6411
|
+
return parsePorcelainV2Status(statusOutput.stdout);
|
|
6412
|
+
}
|
|
6413
|
+
function getInitialUpstreamProbe(parsed) {
|
|
6414
|
+
return {
|
|
6415
|
+
upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
|
|
6416
|
+
};
|
|
6417
|
+
}
|
|
6418
|
+
async function refreshTrackedUpstream(repo, parsed, options) {
|
|
6419
|
+
if (!parsed.upstream || !parsed.branch) {
|
|
6420
|
+
return { upstreamStatus: "no_upstream" };
|
|
6421
|
+
}
|
|
6422
|
+
const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
|
|
6423
|
+
if (!remoteName) {
|
|
6424
|
+
return {
|
|
6425
|
+
upstreamStatus: "stale",
|
|
6426
|
+
upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
|
|
6427
|
+
};
|
|
6428
|
+
}
|
|
6429
|
+
try {
|
|
6430
|
+
await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
|
|
6431
|
+
return {
|
|
6432
|
+
upstreamStatus: "fresh",
|
|
6433
|
+
upstreamFetchedAt: Date.now()
|
|
6434
|
+
};
|
|
6435
|
+
} catch (error) {
|
|
6436
|
+
return {
|
|
6437
|
+
upstreamStatus: "stale",
|
|
6438
|
+
upstreamFetchError: formatGitError(error)
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
async function readBranchRemote(repo, branch, options) {
|
|
6443
|
+
try {
|
|
6444
|
+
const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
|
|
6445
|
+
return result.stdout.trim() || null;
|
|
6446
|
+
} catch {
|
|
6447
|
+
return null;
|
|
6448
|
+
}
|
|
6449
|
+
}
|
|
6450
|
+
function inferRemoteName(upstream) {
|
|
6451
|
+
const [remoteName] = upstream.split("/");
|
|
6452
|
+
return remoteName?.trim() || null;
|
|
6453
|
+
}
|
|
6454
|
+
function formatGitError(error) {
|
|
6455
|
+
if (error instanceof GitCommandError) {
|
|
6456
|
+
return error.stderr || error.message;
|
|
6457
|
+
}
|
|
6458
|
+
if (error instanceof Error) {
|
|
6459
|
+
return error.message;
|
|
6460
|
+
}
|
|
6461
|
+
return String(error);
|
|
6462
|
+
}
|
|
5956
6463
|
function parsePorcelainV2Status(output) {
|
|
5957
6464
|
const parsed = {
|
|
5958
6465
|
branch: null,
|
|
@@ -6047,6 +6554,7 @@ function emptyStatus(workspace, lastCheckedAt, error) {
|
|
|
6047
6554
|
headCommit: null,
|
|
6048
6555
|
headMessage: null,
|
|
6049
6556
|
upstream: null,
|
|
6557
|
+
upstreamStatus: "unavailable",
|
|
6050
6558
|
ahead: 0,
|
|
6051
6559
|
behind: 0,
|
|
6052
6560
|
staged: 0,
|
|
@@ -6327,6 +6835,9 @@ function createGitCompactSummary(status, diffSummary) {
|
|
|
6327
6835
|
isGitRepo: status.isGitRepo,
|
|
6328
6836
|
repoRoot: status.repoRoot,
|
|
6329
6837
|
branch: status.branch,
|
|
6838
|
+
upstreamStatus: status.upstreamStatus,
|
|
6839
|
+
upstreamFetchedAt: status.upstreamFetchedAt,
|
|
6840
|
+
upstreamFetchError: status.upstreamFetchError,
|
|
6330
6841
|
dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
|
|
6331
6842
|
changedFiles,
|
|
6332
6843
|
ahead: status.ahead,
|
|
@@ -6671,7 +7182,7 @@ var defaultSnapshotStore = createGitSnapshotStore({
|
|
|
6671
7182
|
});
|
|
6672
7183
|
function createDefaultGitCommandServices() {
|
|
6673
7184
|
return {
|
|
6674
|
-
getStatus: ({ workspace }) => getGitRepoStatus(workspace),
|
|
7185
|
+
getStatus: ({ workspace, refreshUpstream }) => getGitRepoStatus(workspace, { refreshUpstream }),
|
|
6675
7186
|
getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
|
|
6676
7187
|
getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
|
|
6677
7188
|
createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
|
|
@@ -6757,7 +7268,7 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
|
|
|
6757
7268
|
switch (command) {
|
|
6758
7269
|
case "git_status": {
|
|
6759
7270
|
if (!services.getStatus) return serviceNotImplemented(command);
|
|
6760
|
-
const status = await runService(() => services.getStatus({ workspace }));
|
|
7271
|
+
const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
|
|
6761
7272
|
return "success" in status ? status : { success: true, status };
|
|
6762
7273
|
}
|
|
6763
7274
|
case "git_diff_summary": {
|
|
@@ -7563,6 +8074,238 @@ function getSavedProviderSessions(state, filters) {
|
|
|
7563
8074
|
init_mesh_config();
|
|
7564
8075
|
init_coordinator_prompt();
|
|
7565
8076
|
|
|
8077
|
+
// src/mesh/refine-config.ts
|
|
8078
|
+
var import_fs3 = require("fs");
|
|
8079
|
+
var import_path3 = require("path");
|
|
8080
|
+
var yaml = __toESM(require("js-yaml"));
|
|
8081
|
+
var MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
8082
|
+
var MESH_REFINE_CONFIG_LOCATIONS = [
|
|
8083
|
+
".adhdev/refine.json",
|
|
8084
|
+
".adhdev/refine.yaml",
|
|
8085
|
+
".adhdev/refine.yml",
|
|
8086
|
+
".adhdev/repo-mesh-refine.json",
|
|
8087
|
+
".adhdev/repo-mesh-refine.yaml",
|
|
8088
|
+
".adhdev/repo-mesh-refine.yml",
|
|
8089
|
+
"repo-mesh.refine.json",
|
|
8090
|
+
"repo-mesh.refine.yaml",
|
|
8091
|
+
"repo-mesh.refine.yml"
|
|
8092
|
+
];
|
|
8093
|
+
var MESH_REFINE_CONFIG_SCHEMA = {
|
|
8094
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
8095
|
+
title: "ADHDev Repo Mesh Refinery Config",
|
|
8096
|
+
type: "object",
|
|
8097
|
+
additionalProperties: false,
|
|
8098
|
+
required: ["version"],
|
|
8099
|
+
properties: {
|
|
8100
|
+
version: { const: 1 },
|
|
8101
|
+
validation: {
|
|
8102
|
+
type: "object",
|
|
8103
|
+
additionalProperties: false,
|
|
8104
|
+
properties: {
|
|
8105
|
+
required: { type: "boolean", default: true },
|
|
8106
|
+
commands: {
|
|
8107
|
+
type: "array",
|
|
8108
|
+
minItems: 1,
|
|
8109
|
+
maxItems: 8,
|
|
8110
|
+
items: {
|
|
8111
|
+
type: "object",
|
|
8112
|
+
additionalProperties: false,
|
|
8113
|
+
required: ["command"],
|
|
8114
|
+
properties: {
|
|
8115
|
+
command: { type: "string", minLength: 1 },
|
|
8116
|
+
args: { type: "array", items: { type: "string" } },
|
|
8117
|
+
category: { enum: [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"] },
|
|
8118
|
+
cwd: { type: "string" },
|
|
8119
|
+
timeoutMs: { type: "number", minimum: 1e3, maximum: 6e5 },
|
|
8120
|
+
env: { type: "object", additionalProperties: { type: "string" } }
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
};
|
|
8128
|
+
function isRecord(value) {
|
|
8129
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8130
|
+
}
|
|
8131
|
+
function tokenizeCommandString(command) {
|
|
8132
|
+
const trimmed = command.trim();
|
|
8133
|
+
if (!trimmed) return null;
|
|
8134
|
+
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
8135
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
8136
|
+
if (!tokens.length) return null;
|
|
8137
|
+
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
8138
|
+
return tokens;
|
|
8139
|
+
}
|
|
8140
|
+
function validateCategory(value) {
|
|
8141
|
+
return typeof value === "string" && [...MESH_REFINE_VALIDATION_CATEGORIES, "custom"].includes(value) ? value : "custom";
|
|
8142
|
+
}
|
|
8143
|
+
function normalizeCommandConfig(entry, source) {
|
|
8144
|
+
if (!isRecord(entry) || typeof entry.command !== "string") {
|
|
8145
|
+
return { rejected: { source, reason: "validation command must be an object with a command string" } };
|
|
8146
|
+
}
|
|
8147
|
+
const commandText = entry.command.trim();
|
|
8148
|
+
const explicitArgs = Array.isArray(entry.args) ? entry.args : void 0;
|
|
8149
|
+
if (explicitArgs && !explicitArgs.every((arg) => typeof arg === "string")) {
|
|
8150
|
+
return { rejected: { source, command: commandText, reason: "args must be an array of strings" } };
|
|
8151
|
+
}
|
|
8152
|
+
let command = commandText;
|
|
8153
|
+
let args = explicitArgs ? [...explicitArgs] : [];
|
|
8154
|
+
if (!explicitArgs) {
|
|
8155
|
+
const tokens = tokenizeCommandString(commandText);
|
|
8156
|
+
if (!tokens) return { rejected: { source, command: commandText, reason: "unsafe command string is not allowlisted" } };
|
|
8157
|
+
command = tokens[0];
|
|
8158
|
+
args = tokens.slice(1);
|
|
8159
|
+
} else if (!tokenizeCommandString(command)) {
|
|
8160
|
+
return { rejected: { source, command: commandText, reason: "unsafe executable name is not allowlisted" } };
|
|
8161
|
+
}
|
|
8162
|
+
if (args.some((arg) => /[\n\r\0]/.test(arg))) {
|
|
8163
|
+
return { rejected: { source, command: commandText, reason: "args cannot contain control characters" } };
|
|
8164
|
+
}
|
|
8165
|
+
if (entry.cwd !== void 0 && typeof entry.cwd !== "string") {
|
|
8166
|
+
return { rejected: { source, command: commandText, reason: "cwd must be a string when provided" } };
|
|
8167
|
+
}
|
|
8168
|
+
if (entry.timeoutMs !== void 0 && (typeof entry.timeoutMs !== "number" || !Number.isFinite(entry.timeoutMs) || entry.timeoutMs < 1e3 || entry.timeoutMs > 6e5)) {
|
|
8169
|
+
return { rejected: { source, command: commandText, reason: "timeoutMs must be between 1000 and 600000" } };
|
|
8170
|
+
}
|
|
8171
|
+
if (entry.env !== void 0 && (!isRecord(entry.env) || !Object.values(entry.env).every((value) => typeof value === "string"))) {
|
|
8172
|
+
return { rejected: { source, command: commandText, reason: "env must be an object of string values" } };
|
|
8173
|
+
}
|
|
8174
|
+
return {
|
|
8175
|
+
command: {
|
|
8176
|
+
command,
|
|
8177
|
+
args,
|
|
8178
|
+
displayCommand: [command, ...args].join(" "),
|
|
8179
|
+
category: validateCategory(entry.category),
|
|
8180
|
+
source,
|
|
8181
|
+
...typeof entry.cwd === "string" && entry.cwd.trim() ? { cwd: entry.cwd.trim() } : {},
|
|
8182
|
+
...typeof entry.timeoutMs === "number" ? { timeoutMs: entry.timeoutMs } : {},
|
|
8183
|
+
...isRecord(entry.env) ? { env: entry.env } : {}
|
|
8184
|
+
}
|
|
8185
|
+
};
|
|
8186
|
+
}
|
|
8187
|
+
function validateMeshRefineConfig(config, source = "inline") {
|
|
8188
|
+
const errors = [];
|
|
8189
|
+
const commands = [];
|
|
8190
|
+
const rejectedCommands = [];
|
|
8191
|
+
if (!isRecord(config)) return { valid: false, errors: ["config must be an object"], commands, rejectedCommands };
|
|
8192
|
+
if (config.version !== 1) errors.push("version must be 1");
|
|
8193
|
+
const validation = config.validation;
|
|
8194
|
+
if (validation !== void 0 && !isRecord(validation)) errors.push("validation must be an object");
|
|
8195
|
+
const rawCommands = isRecord(validation) ? validation.commands : void 0;
|
|
8196
|
+
if (rawCommands !== void 0 && !Array.isArray(rawCommands)) errors.push("validation.commands must be an array");
|
|
8197
|
+
if (Array.isArray(rawCommands)) {
|
|
8198
|
+
rawCommands.forEach((entry, index) => {
|
|
8199
|
+
const normalized = normalizeCommandConfig(entry, `${source}:validation.commands[${index}]`);
|
|
8200
|
+
if (normalized.command) commands.push(normalized.command);
|
|
8201
|
+
if (normalized.rejected) rejectedCommands.push(normalized.rejected);
|
|
8202
|
+
});
|
|
8203
|
+
}
|
|
8204
|
+
if (rejectedCommands.length) errors.push("one or more validation commands are invalid");
|
|
8205
|
+
return { valid: errors.length === 0, errors, commands, rejectedCommands };
|
|
8206
|
+
}
|
|
8207
|
+
function parseConfigText(path28, text) {
|
|
8208
|
+
if (/\.json$/i.test(path28)) return JSON.parse(text);
|
|
8209
|
+
return yaml.load(text);
|
|
8210
|
+
}
|
|
8211
|
+
function loadMeshRefineConfig(mesh, workspace) {
|
|
8212
|
+
const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
|
|
8213
|
+
const inline = mesh?.refineConfig || policy.refineConfig || policy.refine;
|
|
8214
|
+
if (inline !== void 0) {
|
|
8215
|
+
const validation = validateMeshRefineConfig(inline, "mesh.policy.refineConfig");
|
|
8216
|
+
if (!validation.valid) return { source: "mesh.policy.refineConfig", sourceType: "invalid", error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
|
|
8217
|
+
return { config: inline, source: "mesh.policy.refineConfig", sourceType: "mesh_policy" };
|
|
8218
|
+
}
|
|
8219
|
+
for (const relative3 of MESH_REFINE_CONFIG_LOCATIONS) {
|
|
8220
|
+
const configPath = (0, import_path3.join)(workspace, relative3);
|
|
8221
|
+
if (!(0, import_fs3.existsSync)(configPath)) continue;
|
|
8222
|
+
try {
|
|
8223
|
+
const parsed = parseConfigText(configPath, (0, import_fs3.readFileSync)(configPath, "utf-8"));
|
|
8224
|
+
const validation = validateMeshRefineConfig(parsed, relative3);
|
|
8225
|
+
if (!validation.valid) return { source: relative3, sourceType: "invalid", path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join("; ")) };
|
|
8226
|
+
return { config: parsed, source: relative3, sourceType: "repo_file", path: configPath };
|
|
8227
|
+
} catch (error) {
|
|
8228
|
+
return { source: relative3, sourceType: "invalid", path: configPath, error: error?.message || String(error) };
|
|
8229
|
+
}
|
|
8230
|
+
}
|
|
8231
|
+
return {
|
|
8232
|
+
source: "unavailable",
|
|
8233
|
+
sourceType: "unavailable",
|
|
8234
|
+
error: `No repo mesh/refine config found. Checked: ${MESH_REFINE_CONFIG_LOCATIONS.join(", ")}`
|
|
8235
|
+
};
|
|
8236
|
+
}
|
|
8237
|
+
function readPackageScripts(workspace) {
|
|
8238
|
+
try {
|
|
8239
|
+
const parsed = JSON.parse((0, import_fs3.readFileSync)((0, import_path3.join)(workspace, "package.json"), "utf-8"));
|
|
8240
|
+
return isRecord(parsed?.scripts) ? parsed.scripts : {};
|
|
8241
|
+
} catch {
|
|
8242
|
+
return {};
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
function collectProjectContextSuggestions(mesh) {
|
|
8246
|
+
const commands = mesh?.projectContext?.commands;
|
|
8247
|
+
if (!isRecord(commands)) return [];
|
|
8248
|
+
const suggestions = [];
|
|
8249
|
+
for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
|
|
8250
|
+
const entries = Array.isArray(commands[category]) ? commands[category] : [];
|
|
8251
|
+
for (const entry of entries) {
|
|
8252
|
+
if (isRecord(entry) && typeof entry.command === "string") suggestions.push({ command: entry.command, category });
|
|
8253
|
+
}
|
|
8254
|
+
}
|
|
8255
|
+
return suggestions;
|
|
8256
|
+
}
|
|
8257
|
+
function collectPackageScriptSuggestions(workspace) {
|
|
8258
|
+
const scripts = readPackageScripts(workspace);
|
|
8259
|
+
const suggestions = [];
|
|
8260
|
+
for (const category of MESH_REFINE_VALIDATION_CATEGORIES) {
|
|
8261
|
+
for (const scriptName of Object.keys(scripts)) {
|
|
8262
|
+
if (scriptName === category || scriptName.startsWith(`${category}:`)) {
|
|
8263
|
+
suggestions.push({ command: "npm", args: ["run", scriptName], category });
|
|
8264
|
+
}
|
|
8265
|
+
}
|
|
8266
|
+
}
|
|
8267
|
+
return suggestions;
|
|
8268
|
+
}
|
|
8269
|
+
function suggestMeshRefineConfig(mesh, workspace) {
|
|
8270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8271
|
+
const suggestions = [];
|
|
8272
|
+
for (const entry of [...collectProjectContextSuggestions(mesh), ...collectPackageScriptSuggestions(workspace)]) {
|
|
8273
|
+
const key = `${entry.command} ${(entry.args || []).join(" ")}`.trim();
|
|
8274
|
+
if (seen.has(key)) continue;
|
|
8275
|
+
seen.add(key);
|
|
8276
|
+
suggestions.push(entry);
|
|
8277
|
+
}
|
|
8278
|
+
return {
|
|
8279
|
+
suggestions,
|
|
8280
|
+
suggestedConfig: suggestions.length ? { version: 1, validation: { required: true, commands: suggestions.slice(0, 4) } } : void 0
|
|
8281
|
+
};
|
|
8282
|
+
}
|
|
8283
|
+
function resolveMeshRefineValidationPlan(mesh, workspace) {
|
|
8284
|
+
const loaded = loadMeshRefineConfig(mesh, workspace);
|
|
8285
|
+
const suggestion = suggestMeshRefineConfig(mesh, workspace);
|
|
8286
|
+
if (!loaded.config) {
|
|
8287
|
+
return {
|
|
8288
|
+
source: loaded.source,
|
|
8289
|
+
sourceType: loaded.sourceType,
|
|
8290
|
+
commands: [],
|
|
8291
|
+
rejectedCommands: loaded.error ? [{ source: loaded.source, reason: loaded.error }] : [],
|
|
8292
|
+
suggestions: suggestion.suggestions,
|
|
8293
|
+
suggestedConfig: suggestion.suggestedConfig,
|
|
8294
|
+
unavailableReason: loaded.error || "validation_unavailable: repo mesh/refine config missing"
|
|
8295
|
+
};
|
|
8296
|
+
}
|
|
8297
|
+
const validation = validateMeshRefineConfig(loaded.config, loaded.source);
|
|
8298
|
+
return {
|
|
8299
|
+
source: loaded.path || loaded.source,
|
|
8300
|
+
sourceType: loaded.sourceType,
|
|
8301
|
+
commands: validation.commands,
|
|
8302
|
+
rejectedCommands: validation.rejectedCommands,
|
|
8303
|
+
suggestions: suggestion.suggestions,
|
|
8304
|
+
suggestedConfig: suggestion.suggestedConfig,
|
|
8305
|
+
unavailableReason: validation.commands.length ? void 0 : "validation_unavailable: repo mesh/refine config has no validation.commands"
|
|
8306
|
+
};
|
|
8307
|
+
}
|
|
8308
|
+
|
|
7566
8309
|
// src/mesh/mesh-sync.ts
|
|
7567
8310
|
init_mesh_config();
|
|
7568
8311
|
async function syncMeshes(transport) {
|
|
@@ -7682,6 +8425,7 @@ function buildMeshLedgerReconciliationEvidence(meshId, replicas) {
|
|
|
7682
8425
|
|
|
7683
8426
|
// src/index.ts
|
|
7684
8427
|
init_mesh_work_queue();
|
|
8428
|
+
init_mesh_host_ownership();
|
|
7685
8429
|
init_mesh_events();
|
|
7686
8430
|
|
|
7687
8431
|
// src/mesh/p2p-relay-failure.ts
|
|
@@ -7795,8 +8539,8 @@ var P2pRelayFailureError = class extends Error {
|
|
|
7795
8539
|
};
|
|
7796
8540
|
|
|
7797
8541
|
// src/config/state-store.ts
|
|
7798
|
-
var
|
|
7799
|
-
var
|
|
8542
|
+
var import_fs8 = require("fs");
|
|
8543
|
+
var import_path7 = require("path");
|
|
7800
8544
|
init_config();
|
|
7801
8545
|
var DEFAULT_STATE = {
|
|
7802
8546
|
recentActivity: [],
|
|
@@ -7810,7 +8554,7 @@ function isPlainObject2(value) {
|
|
|
7810
8554
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7811
8555
|
}
|
|
7812
8556
|
function getStatePath() {
|
|
7813
|
-
return (0,
|
|
8557
|
+
return (0, import_path7.join)(getConfigDir(), "state.json");
|
|
7814
8558
|
}
|
|
7815
8559
|
function normalizeState(raw) {
|
|
7816
8560
|
const parsed = isPlainObject2(raw) ? raw : {};
|
|
@@ -7846,11 +8590,11 @@ function normalizeState(raw) {
|
|
|
7846
8590
|
}
|
|
7847
8591
|
function loadState() {
|
|
7848
8592
|
const statePath = getStatePath();
|
|
7849
|
-
if (!(0,
|
|
8593
|
+
if (!(0, import_fs8.existsSync)(statePath)) {
|
|
7850
8594
|
return { ...DEFAULT_STATE };
|
|
7851
8595
|
}
|
|
7852
8596
|
try {
|
|
7853
|
-
const raw = (0,
|
|
8597
|
+
const raw = (0, import_fs8.readFileSync)(statePath, "utf-8");
|
|
7854
8598
|
return normalizeState(JSON.parse(raw));
|
|
7855
8599
|
} catch {
|
|
7856
8600
|
return { ...DEFAULT_STATE };
|
|
@@ -7859,7 +8603,7 @@ function loadState() {
|
|
|
7859
8603
|
function saveState(state) {
|
|
7860
8604
|
const statePath = getStatePath();
|
|
7861
8605
|
const normalized = normalizeState(state);
|
|
7862
|
-
(0,
|
|
8606
|
+
(0, import_fs8.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
|
|
7863
8607
|
}
|
|
7864
8608
|
function resetState() {
|
|
7865
8609
|
saveState({ ...DEFAULT_STATE });
|
|
@@ -7867,7 +8611,7 @@ function resetState() {
|
|
|
7867
8611
|
|
|
7868
8612
|
// src/detection/ide-detector.ts
|
|
7869
8613
|
var import_child_process2 = require("child_process");
|
|
7870
|
-
var
|
|
8614
|
+
var import_fs9 = require("fs");
|
|
7871
8615
|
var import_os2 = require("os");
|
|
7872
8616
|
var path10 = __toESM(require("path"));
|
|
7873
8617
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
@@ -7891,7 +8635,7 @@ function findCliCommand(command) {
|
|
|
7891
8635
|
if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
7892
8636
|
const candidate = trimmed.startsWith("~") ? path10.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
|
|
7893
8637
|
const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
|
|
7894
|
-
return (0,
|
|
8638
|
+
return (0, import_fs9.existsSync)(resolved) ? resolved : null;
|
|
7895
8639
|
}
|
|
7896
8640
|
try {
|
|
7897
8641
|
const result = (0, import_child_process2.execSync)(
|
|
@@ -7922,9 +8666,9 @@ function checkPathExists(paths) {
|
|
|
7922
8666
|
if (normalized.includes("*")) {
|
|
7923
8667
|
const username = home.split(/[\\/]/).pop() || "";
|
|
7924
8668
|
const resolved = normalized.replace("*", username);
|
|
7925
|
-
if ((0,
|
|
8669
|
+
if ((0, import_fs9.existsSync)(resolved)) return resolved;
|
|
7926
8670
|
} else {
|
|
7927
|
-
if ((0,
|
|
8671
|
+
if ((0, import_fs9.existsSync)(normalized)) return normalized;
|
|
7928
8672
|
}
|
|
7929
8673
|
}
|
|
7930
8674
|
return null;
|
|
@@ -7938,7 +8682,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7938
8682
|
let resolvedCli = cliPath;
|
|
7939
8683
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
7940
8684
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
7941
|
-
if ((0,
|
|
8685
|
+
if ((0, import_fs9.existsSync)(bundledCli)) resolvedCli = bundledCli;
|
|
7942
8686
|
}
|
|
7943
8687
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
7944
8688
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -7951,7 +8695,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7951
8695
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
7952
8696
|
];
|
|
7953
8697
|
for (const c of candidates) {
|
|
7954
|
-
if ((0,
|
|
8698
|
+
if ((0, import_fs9.existsSync)(c)) {
|
|
7955
8699
|
resolvedCli = c;
|
|
7956
8700
|
break;
|
|
7957
8701
|
}
|
|
@@ -9843,7 +10587,8 @@ var StatusMonitor = class {
|
|
|
9843
10587
|
};
|
|
9844
10588
|
|
|
9845
10589
|
// src/providers/chat-message-normalization.ts
|
|
9846
|
-
|
|
10590
|
+
var DEFAULT_FINAL_SUMMARY_MAX_CHARS = 4e3;
|
|
10591
|
+
function extractFinalSummaryFromMessages(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
|
|
9847
10592
|
if (!Array.isArray(messages) || messages.length === 0) return "";
|
|
9848
10593
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
9849
10594
|
const msg = messages[i];
|
|
@@ -17103,7 +17848,7 @@ var DaemonCommandHandler = class {
|
|
|
17103
17848
|
var os13 = __toESM(require("os"));
|
|
17104
17849
|
var path18 = __toESM(require("path"));
|
|
17105
17850
|
var crypto4 = __toESM(require("crypto"));
|
|
17106
|
-
var
|
|
17851
|
+
var import_fs10 = require("fs");
|
|
17107
17852
|
var import_child_process6 = require("child_process");
|
|
17108
17853
|
var import_chalk = __toESM(require("chalk"));
|
|
17109
17854
|
init_provider_cli_adapter();
|
|
@@ -19575,7 +20320,7 @@ function commandExists(command) {
|
|
|
19575
20320
|
const trimmed = command.trim();
|
|
19576
20321
|
if (!trimmed) return false;
|
|
19577
20322
|
if (isExplicitCommand(trimmed)) {
|
|
19578
|
-
return (0,
|
|
20323
|
+
return (0, import_fs10.existsSync)(expandExecutable(trimmed));
|
|
19579
20324
|
}
|
|
19580
20325
|
try {
|
|
19581
20326
|
(0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
|
|
@@ -19604,10 +20349,10 @@ function hasCliArg(args, flag) {
|
|
|
19604
20349
|
}
|
|
19605
20350
|
function ensureEmptyDelegatedMcpConfig(workspace) {
|
|
19606
20351
|
const baseDir = path18.join(os13.tmpdir(), "adhdev-delegated-agent-empty-mcp");
|
|
19607
|
-
(0,
|
|
20352
|
+
(0, import_fs10.mkdirSync)(baseDir, { recursive: true });
|
|
19608
20353
|
const workspaceHash = crypto4.createHash("sha256").update(path18.resolve(workspace || os13.tmpdir())).digest("hex").slice(0, 16);
|
|
19609
20354
|
const filePath = path18.join(baseDir, `${workspaceHash}.json`);
|
|
19610
|
-
(0,
|
|
20355
|
+
(0, import_fs10.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
|
|
19611
20356
|
return filePath;
|
|
19612
20357
|
}
|
|
19613
20358
|
function buildCoordinatorDelegatedCliLaunchOptions(input) {
|
|
@@ -22838,7 +23583,7 @@ function getRecentCommands(count = 50) {
|
|
|
22838
23583
|
cleanOldFiles();
|
|
22839
23584
|
|
|
22840
23585
|
// src/commands/router.ts
|
|
22841
|
-
var
|
|
23586
|
+
var yaml2 = __toESM(require("js-yaml"));
|
|
22842
23587
|
init_logger();
|
|
22843
23588
|
|
|
22844
23589
|
// src/commands/mesh-coordinator.ts
|
|
@@ -23132,6 +23877,7 @@ function normalizeExistingPath(filePath) {
|
|
|
23132
23877
|
|
|
23133
23878
|
// src/commands/router.ts
|
|
23134
23879
|
init_mesh_events();
|
|
23880
|
+
init_mesh_host_ownership();
|
|
23135
23881
|
|
|
23136
23882
|
// src/status/snapshot.ts
|
|
23137
23883
|
var os18 = __toESM(require("os"));
|
|
@@ -23786,7 +24532,7 @@ async function maybeRunDaemonUpgradeHelperFromEnv() {
|
|
|
23786
24532
|
|
|
23787
24533
|
// src/commands/router.ts
|
|
23788
24534
|
var import_os3 = require("os");
|
|
23789
|
-
var
|
|
24535
|
+
var import_path8 = require("path");
|
|
23790
24536
|
var fs10 = __toESM(require("fs"));
|
|
23791
24537
|
var CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
|
|
23792
24538
|
var CHANNEL_SERVER_URL = {
|
|
@@ -23835,13 +24581,85 @@ function readBooleanValue(...values) {
|
|
|
23835
24581
|
}
|
|
23836
24582
|
return void 0;
|
|
23837
24583
|
}
|
|
23838
|
-
function
|
|
24584
|
+
function summarizeRepoMeshDebugGit(git) {
|
|
24585
|
+
const record = readObjectRecord(git);
|
|
24586
|
+
if (!Object.keys(record).length) return null;
|
|
24587
|
+
const submodules = Array.isArray(record.submodules) ? record.submodules.map((entry) => ({
|
|
24588
|
+
path: readStringValue(entry?.path) ?? null,
|
|
24589
|
+
commit: readStringValue(entry?.commit)?.slice(0, 12) ?? null,
|
|
24590
|
+
dirty: readBooleanValue(entry?.dirty) ?? false,
|
|
24591
|
+
outOfSync: readBooleanValue(entry?.outOfSync, entry?.out_of_sync) ?? false
|
|
24592
|
+
})) : [];
|
|
24593
|
+
return {
|
|
24594
|
+
isGitRepo: readBooleanValue(record.isGitRepo),
|
|
24595
|
+
workspace: readStringValue(record.workspace) ?? null,
|
|
24596
|
+
repoRoot: readStringValue(record.repoRoot, record.repo_root) ?? null,
|
|
24597
|
+
branch: readStringValue(record.branch) ?? null,
|
|
24598
|
+
upstream: readStringValue(record.upstream) ?? null,
|
|
24599
|
+
upstreamStatus: readStringValue(record.upstreamStatus, record.upstream_status) ?? null,
|
|
24600
|
+
headCommit: readStringValue(record.headCommit, record.head_commit)?.slice(0, 12) ?? null,
|
|
24601
|
+
ahead: readNumberValue(record.ahead) ?? null,
|
|
24602
|
+
behind: readNumberValue(record.behind) ?? null,
|
|
24603
|
+
dirtyCounts: {
|
|
24604
|
+
staged: readNumberValue(record.staged) ?? 0,
|
|
24605
|
+
modified: readNumberValue(record.modified) ?? 0,
|
|
24606
|
+
untracked: readNumberValue(record.untracked) ?? 0,
|
|
24607
|
+
deleted: readNumberValue(record.deleted) ?? 0,
|
|
24608
|
+
renamed: readNumberValue(record.renamed) ?? 0
|
|
24609
|
+
},
|
|
24610
|
+
lastCheckedAt: readNumberValue(record.lastCheckedAt, record.last_checked_at) ?? null,
|
|
24611
|
+
submoduleCount: submodules.length,
|
|
24612
|
+
submodules
|
|
24613
|
+
};
|
|
24614
|
+
}
|
|
24615
|
+
function summarizeRepoMeshStatusDebug(status) {
|
|
24616
|
+
const nodes = Array.isArray(status?.nodes) ? status.nodes : [];
|
|
24617
|
+
return {
|
|
24618
|
+
success: status?.success,
|
|
24619
|
+
meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
|
|
24620
|
+
refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
|
|
24621
|
+
sourceOfTruth: status?.sourceOfTruth ?? null,
|
|
24622
|
+
nodeCount: nodes.length,
|
|
24623
|
+
nodes: nodes.map((node) => ({
|
|
24624
|
+
nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
|
|
24625
|
+
daemonId: readStringValue(node?.daemonId, node?.daemon_id) ?? null,
|
|
24626
|
+
workspace: readStringValue(node?.workspace, node?.git?.workspace) ?? null,
|
|
24627
|
+
health: readStringValue(node?.health) ?? null,
|
|
24628
|
+
machineStatus: readStringValue(node?.machineStatus, node?.machine_status) ?? null,
|
|
24629
|
+
connection: node?.connection && typeof node.connection === "object" ? {
|
|
24630
|
+
state: readStringValue(node.connection.state) ?? null,
|
|
24631
|
+
transport: readStringValue(node.connection.transport) ?? null,
|
|
24632
|
+
source: readStringValue(node.connection.source) ?? null,
|
|
24633
|
+
reported: readBooleanValue(node.connection.reported) ?? null
|
|
24634
|
+
} : null,
|
|
24635
|
+
gitProbePending: node?.gitProbePending === true,
|
|
24636
|
+
launchReady: node?.launchReady === true,
|
|
24637
|
+
git: summarizeRepoMeshDebugGit(node?.git)
|
|
24638
|
+
}))
|
|
24639
|
+
};
|
|
24640
|
+
}
|
|
24641
|
+
function logRepoMeshStatusDebug(event, fields) {
|
|
24642
|
+
try {
|
|
24643
|
+
LOG.info("MeshStatusDebug", `[RepoMeshStatusDebug] ${JSON.stringify({ event, ...fields })}`);
|
|
24644
|
+
} catch {
|
|
24645
|
+
LOG.info("MeshStatusDebug", `[RepoMeshStatusDebug] ${event}`);
|
|
24646
|
+
}
|
|
24647
|
+
}
|
|
24648
|
+
function joinRepoPath(root, relativePath) {
|
|
24649
|
+
const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
|
|
24650
|
+
const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
|
|
24651
|
+
if (!normalizedPath) return void 0;
|
|
24652
|
+
if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
|
|
24653
|
+
if (!normalizedRoot) return void 0;
|
|
24654
|
+
return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
|
|
24655
|
+
}
|
|
24656
|
+
function readGitSubmodules(value, parentRepoRoot) {
|
|
23839
24657
|
if (!Array.isArray(value)) return void 0;
|
|
23840
24658
|
const submodules = value.map((entry) => {
|
|
23841
24659
|
const submodule = readObjectRecord(entry);
|
|
23842
24660
|
const path28 = readStringValue(submodule.path);
|
|
23843
24661
|
const commit = readStringValue(submodule.commit);
|
|
23844
|
-
const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
|
|
24662
|
+
const repoPath = readStringValue(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path28);
|
|
23845
24663
|
if (!path28 || !commit || !repoPath) return null;
|
|
23846
24664
|
return {
|
|
23847
24665
|
path: path28,
|
|
@@ -23855,58 +24673,17 @@ function readGitSubmodules(value) {
|
|
|
23855
24673
|
}).filter((entry) => entry !== null);
|
|
23856
24674
|
return submodules.length > 0 ? submodules : void 0;
|
|
23857
24675
|
}
|
|
23858
|
-
function
|
|
23859
|
-
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
23860
|
-
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
23861
|
-
if (Object.keys(cachedGit).length) {
|
|
23862
|
-
const conflictFiles2 = Array.isArray(cachedGit.conflictFiles) ? cachedGit.conflictFiles.filter((value) => typeof value === "string") : [];
|
|
23863
|
-
const conflictCount2 = readNumberValue(cachedGit.conflicts) ?? conflictFiles2.length;
|
|
23864
|
-
const hasConflicts2 = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount2 > 0;
|
|
23865
|
-
const isGitRepo2 = readBooleanValue(cachedGit.isGitRepo);
|
|
23866
|
-
if (isGitRepo2 !== void 0) {
|
|
23867
|
-
const submodules2 = readGitSubmodules(cachedGit.submodules);
|
|
23868
|
-
return {
|
|
23869
|
-
workspace: readStringValue(cachedGit.workspace, node?.workspace) || "",
|
|
23870
|
-
repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
|
|
23871
|
-
isGitRepo: isGitRepo2,
|
|
23872
|
-
branch: readStringValue(cachedGit.branch) ?? null,
|
|
23873
|
-
headCommit: readStringValue(cachedGit.headCommit) ?? null,
|
|
23874
|
-
headMessage: readStringValue(cachedGit.headMessage) ?? null,
|
|
23875
|
-
upstream: readStringValue(cachedGit.upstream) ?? null,
|
|
23876
|
-
ahead: readNumberValue(cachedGit.ahead) ?? 0,
|
|
23877
|
-
behind: readNumberValue(cachedGit.behind) ?? 0,
|
|
23878
|
-
staged: readNumberValue(cachedGit.staged) ?? 0,
|
|
23879
|
-
modified: readNumberValue(cachedGit.modified) ?? 0,
|
|
23880
|
-
untracked: readNumberValue(cachedGit.untracked) ?? 0,
|
|
23881
|
-
deleted: readNumberValue(cachedGit.deleted) ?? 0,
|
|
23882
|
-
renamed: readNumberValue(cachedGit.renamed) ?? 0,
|
|
23883
|
-
hasConflicts: hasConflicts2,
|
|
23884
|
-
conflictFiles: conflictFiles2,
|
|
23885
|
-
stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
|
|
23886
|
-
lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now(),
|
|
23887
|
-
...submodules2 ? { submodules: submodules2 } : {}
|
|
23888
|
-
};
|
|
23889
|
-
}
|
|
23890
|
-
}
|
|
23891
|
-
const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
|
|
23892
|
-
const gitResult = readObjectRecord(rawGit.result);
|
|
23893
|
-
const directStatus = readObjectRecord(rawGit.status);
|
|
23894
|
-
const nestedStatus = readObjectRecord(gitResult.status);
|
|
23895
|
-
const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
|
|
23896
|
-
const probeGit = readObjectRecord(rawProbe.git);
|
|
23897
|
-
const probeGitResult = readObjectRecord(probeGit.result);
|
|
23898
|
-
const probeDirectStatus = readObjectRecord(probeGit.status);
|
|
23899
|
-
const probeNestedStatus = readObjectRecord(probeGitResult.status);
|
|
23900
|
-
const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
|
|
24676
|
+
function normalizeInlineMeshGitStatus(status, node, options) {
|
|
23901
24677
|
const isGitRepo = readBooleanValue(status.isGitRepo);
|
|
23902
24678
|
if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
|
|
23903
24679
|
const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((value) => typeof value === "string") : [];
|
|
23904
24680
|
const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
|
|
23905
24681
|
const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
|
|
23906
|
-
const
|
|
24682
|
+
const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || void 0;
|
|
24683
|
+
const submodules = readGitSubmodules(status.submodules, repoRoot);
|
|
23907
24684
|
return {
|
|
23908
24685
|
workspace: readStringValue(status.workspace, node?.workspace) || "",
|
|
23909
|
-
repoRoot:
|
|
24686
|
+
repoRoot: repoRoot ?? null,
|
|
23910
24687
|
isGitRepo,
|
|
23911
24688
|
branch: readStringValue(status.branch) ?? null,
|
|
23912
24689
|
headCommit: readStringValue(status.headCommit) ?? null,
|
|
@@ -23922,30 +24699,456 @@ function buildCachedInlineMeshGitStatus(node) {
|
|
|
23922
24699
|
hasConflicts,
|
|
23923
24700
|
conflictFiles,
|
|
23924
24701
|
stashCount: readNumberValue(status.stashCount) ?? 0,
|
|
23925
|
-
lastCheckedAt: Date.now(),
|
|
24702
|
+
lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
|
|
23926
24703
|
...submodules ? { submodules } : {}
|
|
23927
24704
|
};
|
|
23928
24705
|
}
|
|
23929
|
-
function
|
|
24706
|
+
function scoreInlineMeshGitStatus(git) {
|
|
24707
|
+
if (!git) return Number.NEGATIVE_INFINITY;
|
|
24708
|
+
let score = 0;
|
|
24709
|
+
if (readBooleanValue(git.isGitRepo) === true) score += 50;
|
|
24710
|
+
if (readBooleanValue(git.isGitRepo) === false) score -= 10;
|
|
24711
|
+
if (readStringValue(git.branch)) score += 20;
|
|
24712
|
+
if (readStringValue(git.headCommit)) score += 20;
|
|
24713
|
+
if (readStringValue(git.upstream)) score += 10;
|
|
24714
|
+
if (readStringValue(git.upstreamStatus)) score += 5;
|
|
24715
|
+
if (readNumberValue(git.ahead) !== void 0) score += 2;
|
|
24716
|
+
if (readNumberValue(git.behind) !== void 0) score += 2;
|
|
24717
|
+
if (Array.isArray(git.submodules) && git.submodules.length > 0) score += 4 + git.submodules.length;
|
|
24718
|
+
if (readStringValue(git.error)) score -= 20;
|
|
24719
|
+
return score;
|
|
24720
|
+
}
|
|
24721
|
+
function buildInlineMeshTransitGitStatus(node) {
|
|
24722
|
+
const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
|
|
24723
|
+
const gitResult = readObjectRecord(rawGit.result);
|
|
24724
|
+
const directStatus = readObjectRecord(rawGit.status);
|
|
24725
|
+
const nestedStatus = readObjectRecord(gitResult.status);
|
|
24726
|
+
const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
|
|
24727
|
+
const probeGit = readObjectRecord(rawProbe.git);
|
|
24728
|
+
const probeGitResult = readObjectRecord(probeGit.result);
|
|
24729
|
+
const probeDirectStatus = readObjectRecord(probeGit.status);
|
|
24730
|
+
const probeNestedStatus = readObjectRecord(probeGitResult.status);
|
|
24731
|
+
const candidates = [directStatus, nestedStatus, probeDirectStatus, probeNestedStatus];
|
|
24732
|
+
let best = null;
|
|
24733
|
+
for (const status of candidates) {
|
|
24734
|
+
const normalized = normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
|
|
24735
|
+
if (!normalized) continue;
|
|
24736
|
+
const score = scoreInlineMeshGitStatus(normalized);
|
|
24737
|
+
if (!best || score > best.score) best = { git: normalized, score };
|
|
24738
|
+
}
|
|
24739
|
+
return best?.git;
|
|
24740
|
+
}
|
|
24741
|
+
function shouldRefreshStalePendingAggregate(snapshot, options) {
|
|
24742
|
+
if (options?.requireDirectPeerTruth !== true || !Array.isArray(snapshot?.nodes)) return false;
|
|
24743
|
+
return snapshot.nodes.some((node) => {
|
|
24744
|
+
if (node?.gitProbePending !== true) return false;
|
|
24745
|
+
const git = readObjectRecord(node?.git);
|
|
24746
|
+
return !readBooleanValue(git.isGitRepo) && !readStringValue(git.branch, git.headCommit, git.upstream);
|
|
24747
|
+
});
|
|
24748
|
+
}
|
|
24749
|
+
function buildLivePeerGitConnection(connection, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
24750
|
+
const source = readStringValue(connection.source);
|
|
24751
|
+
const transport = readStringValue(connection.transport);
|
|
24752
|
+
return {
|
|
24753
|
+
...connection,
|
|
24754
|
+
perspective: readStringValue(connection.perspective) ?? "selected_coordinator",
|
|
24755
|
+
source: source && source !== "not_reported" ? source : "mesh_peer_status",
|
|
24756
|
+
state: "connected",
|
|
24757
|
+
transport: transport && transport !== "unknown" ? transport : "direct",
|
|
24758
|
+
reported: true,
|
|
24759
|
+
reason: "Live peer git snapshot reported by the selected coordinator.",
|
|
24760
|
+
lastStateChangeAt: readStringValue(connection.lastStateChangeAt) ?? timestamp
|
|
24761
|
+
};
|
|
24762
|
+
}
|
|
24763
|
+
function recordInlineMeshDirectGitTruth(node, git, source) {
|
|
24764
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return;
|
|
24765
|
+
const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
|
|
24766
|
+
const updatedAt = new Date(checkedAt).toISOString();
|
|
24767
|
+
const nextGit = {
|
|
24768
|
+
...git,
|
|
24769
|
+
lastCheckedAt: checkedAt
|
|
24770
|
+
};
|
|
24771
|
+
node.lastGit = {
|
|
24772
|
+
source,
|
|
24773
|
+
checkedAt,
|
|
24774
|
+
status: nextGit
|
|
24775
|
+
};
|
|
24776
|
+
node.last_git = node.lastGit;
|
|
24777
|
+
node.machineStatus = "online";
|
|
24778
|
+
node.updatedAt = updatedAt;
|
|
24779
|
+
node.lastSeenAt = updatedAt;
|
|
24780
|
+
const repoRoot = readStringValue(nextGit.repoRoot);
|
|
24781
|
+
if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
|
|
24782
|
+
}
|
|
24783
|
+
function buildCachedInlineMeshGitStatus(node) {
|
|
24784
|
+
const liveGit = buildInlineMeshTransitGitStatus(node);
|
|
24785
|
+
if (liveGit) return liveGit;
|
|
23930
24786
|
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
23931
|
-
const
|
|
23932
|
-
|
|
23933
|
-
|
|
24787
|
+
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
24788
|
+
if (!Object.keys(cachedGit).length) return void 0;
|
|
24789
|
+
return normalizeInlineMeshGitStatus(cachedGit, node);
|
|
24790
|
+
}
|
|
24791
|
+
function shouldDiscardCachedInlineMeshStatus(node) {
|
|
24792
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24793
|
+
if (!Object.keys(cachedStatus).length) return false;
|
|
24794
|
+
const cachedGit = readObjectRecord(cachedStatus.git);
|
|
24795
|
+
const workspaceError = readStringValue(cachedStatus.error, node?.error);
|
|
24796
|
+
if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
|
|
24797
|
+
const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
|
|
24798
|
+
const branch = readStringValue(cachedGit.branch);
|
|
24799
|
+
const headCommit = readStringValue(cachedGit.headCommit);
|
|
24800
|
+
return isGitRepo === false && !branch && !headCommit;
|
|
24801
|
+
}
|
|
24802
|
+
function stripInlineMeshTransientNodeState(node) {
|
|
24803
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return node;
|
|
24804
|
+
const {
|
|
24805
|
+
cachedStatus,
|
|
24806
|
+
lastGit: _lastGit,
|
|
24807
|
+
last_git: _lastGitLegacy,
|
|
24808
|
+
lastProbe: _lastProbe,
|
|
24809
|
+
last_probe: _lastProbeLegacy,
|
|
24810
|
+
error: _error,
|
|
24811
|
+
health: _health,
|
|
24812
|
+
machineStatus: _machineStatus,
|
|
24813
|
+
lastSeenAt: _lastSeenAt,
|
|
24814
|
+
last_seen_at: _lastSeenAtLegacy,
|
|
24815
|
+
updatedAt: _updatedAt,
|
|
24816
|
+
updated_at: _updatedAtLegacy,
|
|
24817
|
+
activeSession: _activeSession,
|
|
24818
|
+
active_session: _activeSessionLegacy,
|
|
24819
|
+
activeSessionId: _activeSessionId,
|
|
24820
|
+
active_session_id: _activeSessionIdLegacy,
|
|
24821
|
+
sessionId: _sessionId,
|
|
24822
|
+
session_id: _sessionIdLegacy,
|
|
24823
|
+
providerType: _providerType,
|
|
24824
|
+
provider_type: _providerTypeLegacy,
|
|
24825
|
+
...rest
|
|
24826
|
+
} = node;
|
|
24827
|
+
if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
|
|
24828
|
+
return { ...rest, cachedStatus };
|
|
24829
|
+
}
|
|
24830
|
+
return rest;
|
|
24831
|
+
}
|
|
24832
|
+
function hasInlineMeshTransientNodeState(node) {
|
|
24833
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return false;
|
|
24834
|
+
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;
|
|
24835
|
+
}
|
|
24836
|
+
function inlineMeshCarriesTransientNodeTruth(inlineMesh) {
|
|
24837
|
+
if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return false;
|
|
24838
|
+
if (!Array.isArray(inlineMesh.nodes) || inlineMesh.nodes.length === 0) return false;
|
|
24839
|
+
return inlineMesh.nodes.some((node) => hasInlineMeshTransientNodeState(node));
|
|
24840
|
+
}
|
|
24841
|
+
function readInlineMeshNodeId(node) {
|
|
24842
|
+
return readStringValue(node?.id, node?.nodeId) || "";
|
|
24843
|
+
}
|
|
24844
|
+
function sanitizeInlineMesh(inlineMesh) {
|
|
24845
|
+
if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return inlineMesh;
|
|
24846
|
+
if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
|
|
24847
|
+
let changed = false;
|
|
24848
|
+
const nodes = inlineMesh.nodes.map((node) => {
|
|
24849
|
+
if (!hasInlineMeshTransientNodeState(node)) return node;
|
|
24850
|
+
changed = true;
|
|
24851
|
+
return stripInlineMeshTransientNodeState(node);
|
|
24852
|
+
});
|
|
24853
|
+
if (!changed) return inlineMesh;
|
|
24854
|
+
return {
|
|
24855
|
+
...inlineMesh,
|
|
24856
|
+
nodes
|
|
24857
|
+
};
|
|
24858
|
+
}
|
|
24859
|
+
function reconcileInlineMeshCache(cached, incoming) {
|
|
24860
|
+
if (!cached || typeof cached !== "object" || Array.isArray(cached)) return incoming;
|
|
24861
|
+
if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached;
|
|
24862
|
+
const cachedNodes = Array.isArray(cached.nodes) ? cached.nodes : [];
|
|
24863
|
+
const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
|
|
24864
|
+
if (!cachedNodes.length || !incomingNodes.length) return { ...cached, ...incoming };
|
|
24865
|
+
const incomingById = /* @__PURE__ */ new Map();
|
|
24866
|
+
for (const node of incomingNodes) {
|
|
24867
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
24868
|
+
if (nodeId) incomingById.set(nodeId, node);
|
|
24869
|
+
}
|
|
24870
|
+
const nodes = cachedNodes.map((cachedNode) => {
|
|
24871
|
+
const nodeId = readInlineMeshNodeId(cachedNode);
|
|
24872
|
+
const incomingNode = nodeId ? incomingById.get(nodeId) : void 0;
|
|
24873
|
+
if (!incomingNode) return cachedNode;
|
|
24874
|
+
if (hasInlineMeshTransientNodeState(incomingNode)) {
|
|
24875
|
+
return { ...cachedNode, ...incomingNode };
|
|
24876
|
+
}
|
|
24877
|
+
return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
|
|
24878
|
+
});
|
|
24879
|
+
return {
|
|
24880
|
+
...cached,
|
|
24881
|
+
...incoming,
|
|
24882
|
+
nodes
|
|
24883
|
+
};
|
|
24884
|
+
}
|
|
24885
|
+
function hasGitWorktreeChanges(git) {
|
|
24886
|
+
if (!git) return false;
|
|
24887
|
+
return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
|
|
24888
|
+
}
|
|
24889
|
+
function getGitSubmoduleDriftState(git) {
|
|
24890
|
+
const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
|
|
24891
|
+
let dirty = false;
|
|
24892
|
+
let outOfSync = false;
|
|
24893
|
+
for (const entry of submodules) {
|
|
24894
|
+
const submodule = readObjectRecord(entry);
|
|
24895
|
+
if (readBooleanValue(submodule.dirty) === true) dirty = true;
|
|
24896
|
+
if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
|
|
24897
|
+
}
|
|
24898
|
+
return { dirty, outOfSync };
|
|
24899
|
+
}
|
|
24900
|
+
function deriveMeshNodeHealthFromGit(git) {
|
|
24901
|
+
if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
|
|
24902
|
+
const branch = readStringValue(git.branch);
|
|
24903
|
+
if (!branch) return "degraded";
|
|
24904
|
+
const submoduleDrift = getGitSubmoduleDriftState(git);
|
|
24905
|
+
if (submoduleDrift.outOfSync) return "degraded";
|
|
24906
|
+
if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return "dirty";
|
|
24907
|
+
return "online";
|
|
24908
|
+
}
|
|
24909
|
+
function readCachedInlineMeshActiveSessions(node) {
|
|
24910
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24911
|
+
const activeSession = readObjectRecord(cachedStatus.activeSession);
|
|
24912
|
+
const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
|
|
24913
|
+
const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
|
|
24914
|
+
return sessionId ? [sessionId] : [];
|
|
24915
|
+
}
|
|
24916
|
+
function readCachedInlineMeshActiveSessionDetails(node) {
|
|
24917
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24918
|
+
const activeSession = readObjectRecord(cachedStatus.activeSession);
|
|
24919
|
+
const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
|
|
24920
|
+
const sessionId = readStringValue(
|
|
24921
|
+
fallbackSession.id,
|
|
24922
|
+
fallbackSession.sessionId,
|
|
24923
|
+
fallbackSession.session_id,
|
|
24924
|
+
node?.activeSessionId,
|
|
24925
|
+
node?.active_session_id,
|
|
24926
|
+
node?.sessionId,
|
|
24927
|
+
node?.session_id
|
|
24928
|
+
);
|
|
24929
|
+
if (!sessionId) return [];
|
|
24930
|
+
return [{
|
|
24931
|
+
sessionId,
|
|
24932
|
+
providerType: readStringValue(
|
|
24933
|
+
fallbackSession.providerType,
|
|
24934
|
+
fallbackSession.provider_type,
|
|
24935
|
+
fallbackSession.cliType,
|
|
24936
|
+
fallbackSession.cli_type,
|
|
24937
|
+
fallbackSession.provider,
|
|
24938
|
+
node?.providerType,
|
|
24939
|
+
node?.provider_type
|
|
24940
|
+
),
|
|
24941
|
+
state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
|
|
24942
|
+
lifecycle: readStringValue(fallbackSession.lifecycle),
|
|
24943
|
+
title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
|
|
24944
|
+
workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
|
|
24945
|
+
lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
|
|
24946
|
+
recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
|
|
24947
|
+
isCached: true
|
|
24948
|
+
}];
|
|
24949
|
+
}
|
|
24950
|
+
function readLiveMeshSessionState(record) {
|
|
24951
|
+
return readStringValue(
|
|
24952
|
+
record?.meta?.sessionStatus,
|
|
24953
|
+
record?.meta?.status,
|
|
24954
|
+
record?.meta?.providerStatus,
|
|
24955
|
+
record?.status,
|
|
24956
|
+
record?.state,
|
|
24957
|
+
record?.lifecycle
|
|
24958
|
+
);
|
|
24959
|
+
}
|
|
24960
|
+
function toIsoTimestamp(value) {
|
|
24961
|
+
if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
|
|
24962
|
+
const stringValue = readStringValue(value);
|
|
24963
|
+
return stringValue || null;
|
|
24964
|
+
}
|
|
24965
|
+
function synthesizeMeshNodeFreshnessFromConnection(status) {
|
|
24966
|
+
const connection = readObjectRecord(status.connection);
|
|
24967
|
+
const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
|
|
24968
|
+
const git = readObjectRecord(status.git);
|
|
24969
|
+
const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
|
|
24970
|
+
if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
|
|
24971
|
+
if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
|
|
24972
|
+
status.updatedAt = gitCheckedAt ?? connectionFreshAt;
|
|
24973
|
+
}
|
|
24974
|
+
}
|
|
24975
|
+
function finalizeMeshNodeStatus(args) {
|
|
24976
|
+
const { status, node, daemonId, isSelfNode } = args;
|
|
24977
|
+
if (!readStringValue(status.machineStatus)) {
|
|
24978
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
24979
|
+
const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
|
|
24980
|
+
if (machineStatus) status.machineStatus = machineStatus;
|
|
24981
|
+
}
|
|
24982
|
+
synthesizeMeshNodeFreshnessFromConnection(status);
|
|
24983
|
+
const connectionState = readStringValue(readObjectRecord(status.connection).state);
|
|
24984
|
+
status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || connectionState === "connected" || isSelfNode);
|
|
24985
|
+
}
|
|
24986
|
+
async function probeRemoteMeshGitStatus(args) {
|
|
24987
|
+
if (!args.dispatchMeshCommand) return null;
|
|
24988
|
+
const remoteResult = await Promise.race([
|
|
24989
|
+
args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace }),
|
|
24990
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), args.timeoutMs))
|
|
24991
|
+
]);
|
|
24992
|
+
const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
|
|
24993
|
+
return remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean" ? remoteGit : null;
|
|
24994
|
+
}
|
|
24995
|
+
async function hydrateInlineMeshDirectTruth(args) {
|
|
24996
|
+
const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
|
|
24997
|
+
if (!nodes.length) {
|
|
24998
|
+
return {
|
|
24999
|
+
directEvidenceCount: 0,
|
|
25000
|
+
localConfirmedCount: 0,
|
|
25001
|
+
peerAttemptedCount: 0,
|
|
25002
|
+
peerConfirmedCount: 0,
|
|
25003
|
+
unavailableNodeIds: []
|
|
25004
|
+
};
|
|
25005
|
+
}
|
|
25006
|
+
const selectedCoordinatorNodeId = readStringValue(
|
|
25007
|
+
args.mesh?.coordinator?.preferredNodeId,
|
|
25008
|
+
nodes[0]?.id,
|
|
25009
|
+
nodes[0]?.nodeId
|
|
25010
|
+
);
|
|
25011
|
+
let localConfirmedCount = 0;
|
|
25012
|
+
let peerAttemptedCount = 0;
|
|
25013
|
+
let peerConfirmedCount = 0;
|
|
25014
|
+
const unavailableNodeIds = [];
|
|
25015
|
+
for (const [nodeIndex, node] of nodes.entries()) {
|
|
25016
|
+
const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
|
|
25017
|
+
const workspace = readStringValue(node?.workspace);
|
|
25018
|
+
const daemonId = readStringValue(node?.daemonId);
|
|
25019
|
+
const isSelfNode = Boolean(
|
|
25020
|
+
nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
|
|
25021
|
+
) || Boolean(
|
|
25022
|
+
daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId)
|
|
25023
|
+
) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
|
|
25024
|
+
if (!workspace) {
|
|
25025
|
+
if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
|
|
25026
|
+
continue;
|
|
25027
|
+
}
|
|
25028
|
+
if (isSelfNode && fs10.existsSync(workspace)) {
|
|
25029
|
+
try {
|
|
25030
|
+
const localGit = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
|
|
25031
|
+
if (localGit?.isGitRepo) {
|
|
25032
|
+
recordInlineMeshDirectGitTruth(node, localGit, "selected_coordinator_local_git");
|
|
25033
|
+
localConfirmedCount += 1;
|
|
25034
|
+
continue;
|
|
25035
|
+
}
|
|
25036
|
+
} catch {
|
|
25037
|
+
}
|
|
25038
|
+
}
|
|
25039
|
+
if (!daemonId || !args.dispatchMeshCommand) {
|
|
25040
|
+
if (!isSelfNode) unavailableNodeIds.push(nodeId);
|
|
25041
|
+
continue;
|
|
25042
|
+
}
|
|
25043
|
+
peerAttemptedCount += 1;
|
|
25044
|
+
try {
|
|
25045
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
25046
|
+
dispatchMeshCommand: args.dispatchMeshCommand,
|
|
25047
|
+
daemonId,
|
|
25048
|
+
workspace,
|
|
25049
|
+
timeoutMs: 8e3
|
|
25050
|
+
});
|
|
25051
|
+
if (remoteGit) {
|
|
25052
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
25053
|
+
peerConfirmedCount += 1;
|
|
25054
|
+
continue;
|
|
25055
|
+
}
|
|
25056
|
+
} catch {
|
|
25057
|
+
}
|
|
25058
|
+
unavailableNodeIds.push(nodeId);
|
|
25059
|
+
}
|
|
25060
|
+
return {
|
|
25061
|
+
directEvidenceCount: localConfirmedCount + peerConfirmedCount,
|
|
25062
|
+
localConfirmedCount,
|
|
25063
|
+
peerAttemptedCount,
|
|
25064
|
+
peerConfirmedCount,
|
|
25065
|
+
unavailableNodeIds
|
|
25066
|
+
};
|
|
25067
|
+
}
|
|
25068
|
+
function summarizeMeshSessionRecord(record) {
|
|
25069
|
+
return {
|
|
25070
|
+
sessionId: readStringValue(record?.sessionId) || "unknown",
|
|
25071
|
+
providerType: readStringValue(record?.providerType),
|
|
25072
|
+
state: readLiveMeshSessionState(record),
|
|
25073
|
+
lifecycle: readStringValue(record?.lifecycle),
|
|
25074
|
+
surfaceKind: getSessionHostSurfaceKind(record),
|
|
25075
|
+
recoveryState: readStringValue(record?.meta?.runtimeRecoveryState) ?? null,
|
|
25076
|
+
workspace: readStringValue(record?.workspace) ?? null,
|
|
25077
|
+
title: readStringValue(record?.displayName, record?.workspaceLabel) ?? null,
|
|
25078
|
+
lastActivityAt: toIsoTimestamp(record?.updatedAt ?? record?.lastActivityAt ?? record?.last_activity_at),
|
|
25079
|
+
isCached: false
|
|
25080
|
+
};
|
|
25081
|
+
}
|
|
25082
|
+
function liveSessionRecordMatchesMeshNode(record, meshId, nodeId) {
|
|
25083
|
+
const recordNodeId = readStringValue(record?.meta?.meshNodeId);
|
|
25084
|
+
if (!recordNodeId || recordNodeId !== nodeId) return false;
|
|
25085
|
+
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
25086
|
+
return !recordMeshId || recordMeshId === meshId;
|
|
25087
|
+
}
|
|
25088
|
+
function liveSessionRecordMatchesMeshWorkspace(record, meshId, workspace) {
|
|
25089
|
+
const recordWorkspace = readStringValue(record?.workspace);
|
|
25090
|
+
if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
|
|
25091
|
+
const recordMeshId = readStringValue(record?.meta?.meshNodeFor);
|
|
25092
|
+
if (recordMeshId) return recordMeshId === meshId;
|
|
25093
|
+
return record?.meta?.launchedByCoordinator === true || !!readStringValue(record?.meta?.meshNodeId);
|
|
25094
|
+
}
|
|
25095
|
+
function readLiveMeshNodeWorkspace(args) {
|
|
25096
|
+
const directNodeWorkspace = args.liveSessionRecords.find((record) => liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId) && readStringValue(record?.workspace));
|
|
25097
|
+
if (directNodeWorkspace) {
|
|
25098
|
+
return readStringValue(directNodeWorkspace.workspace) || "";
|
|
25099
|
+
}
|
|
25100
|
+
if (args.allowCoordinatorSession) {
|
|
25101
|
+
const coordinatorWorkspace = args.liveSessionRecords.find((record) => readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId && readStringValue(record?.workspace));
|
|
25102
|
+
if (coordinatorWorkspace) {
|
|
25103
|
+
return readStringValue(coordinatorWorkspace.workspace) || "";
|
|
25104
|
+
}
|
|
25105
|
+
}
|
|
25106
|
+
return "";
|
|
25107
|
+
}
|
|
25108
|
+
function collectLiveMeshSessionRecords(args) {
|
|
25109
|
+
const matches = args.liveSessionRecords.filter((record) => {
|
|
25110
|
+
const nodeWorkspace = readStringValue(args.node?.workspace);
|
|
25111
|
+
if (liveSessionRecordMatchesMeshNode(record, args.meshId, args.nodeId)) return true;
|
|
25112
|
+
return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record, args.meshId, nodeWorkspace);
|
|
25113
|
+
});
|
|
25114
|
+
if (args.allowCoordinatorSession) {
|
|
25115
|
+
for (const record of args.liveSessionRecords) {
|
|
25116
|
+
if (readStringValue(record?.meta?.meshCoordinatorFor) !== args.meshId) continue;
|
|
25117
|
+
const sessionId = readStringValue(record?.sessionId);
|
|
25118
|
+
if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
|
|
25119
|
+
matches.push(record);
|
|
25120
|
+
}
|
|
25121
|
+
}
|
|
25122
|
+
return matches;
|
|
25123
|
+
}
|
|
25124
|
+
function applyCachedInlineMeshNodeStatus(status, node, options) {
|
|
25125
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
25126
|
+
const liveGit = buildInlineMeshTransitGitStatus(node);
|
|
25127
|
+
const git = options?.skipGit ? void 0 : liveGit ?? buildCachedInlineMeshGitStatus(node);
|
|
25128
|
+
const error = options?.skipError ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.error, node?.error);
|
|
25129
|
+
const health = options?.skipHealth ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.health, node?.health);
|
|
23934
25130
|
const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
|
|
23935
|
-
|
|
23936
|
-
|
|
25131
|
+
const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
|
|
25132
|
+
const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
|
|
25133
|
+
const activeSessions = readCachedInlineMeshActiveSessions(node);
|
|
25134
|
+
const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
|
|
25135
|
+
if (!git && !error && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
|
|
23937
25136
|
if (git) status.git = git;
|
|
23938
25137
|
if (error) status.error = error;
|
|
25138
|
+
if (machineStatus) status.machineStatus = machineStatus;
|
|
25139
|
+
if (lastSeenAt) status.lastSeenAt = lastSeenAt;
|
|
25140
|
+
if (updatedAt) status.updatedAt = updatedAt;
|
|
25141
|
+
if (activeSessions.length > 0) status.activeSessions = activeSessions;
|
|
25142
|
+
if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
|
|
23939
25143
|
if (health) {
|
|
23940
25144
|
status.health = health;
|
|
23941
25145
|
return true;
|
|
23942
25146
|
}
|
|
23943
25147
|
if (git) {
|
|
23944
|
-
|
|
23945
|
-
status.health = git.isGitRepo === false ? "degraded" : dirty ? "dirty" : "online";
|
|
25148
|
+
status.health = deriveMeshNodeHealthFromGit(git);
|
|
23946
25149
|
return true;
|
|
23947
25150
|
}
|
|
23948
|
-
return
|
|
25151
|
+
return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
|
|
23949
25152
|
}
|
|
23950
25153
|
async function resolveProviderTypeFromPriority(args) {
|
|
23951
25154
|
if (!args.providerPriority.length) {
|
|
@@ -23970,152 +25173,116 @@ async function resolveProviderTypeFromPriority(args) {
|
|
|
23970
25173
|
}
|
|
23971
25174
|
return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
|
|
23972
25175
|
}
|
|
23973
|
-
var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
23974
25176
|
var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
23975
25177
|
var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
23976
25178
|
var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
|
|
23977
|
-
var
|
|
25179
|
+
var REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES = 4 * 1024 * 1024;
|
|
23978
25180
|
function truncateValidationOutput(value) {
|
|
23979
25181
|
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
23980
25182
|
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
23981
25183
|
return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
|
|
23982
25184
|
[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
|
|
23983
25185
|
}
|
|
23984
|
-
function
|
|
23985
|
-
|
|
23986
|
-
|
|
23987
|
-
|
|
23988
|
-
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
}
|
|
23992
|
-
}
|
|
23993
|
-
function tokenizeValidationCommand(command) {
|
|
23994
|
-
const trimmed = command.trim();
|
|
23995
|
-
if (!trimmed) return null;
|
|
23996
|
-
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
23997
|
-
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
23998
|
-
if (!tokens.length) return null;
|
|
23999
|
-
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
24000
|
-
return tokens;
|
|
24001
|
-
}
|
|
24002
|
-
function scriptMatchesValidationCategory(scriptName, category) {
|
|
24003
|
-
return scriptName === category || scriptName.startsWith(`${category}:`);
|
|
24004
|
-
}
|
|
24005
|
-
function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
|
|
24006
|
-
const tokens = tokenizeValidationCommand(rawCommand);
|
|
24007
|
-
if (!tokens) {
|
|
24008
|
-
return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
|
|
24009
|
-
}
|
|
24010
|
-
const [binary, second, third, ...rest] = tokens;
|
|
24011
|
-
let scriptName = "";
|
|
24012
|
-
let command = binary;
|
|
24013
|
-
let args = [];
|
|
24014
|
-
if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
|
|
24015
|
-
scriptName = third;
|
|
24016
|
-
args = ["run", scriptName, ...rest];
|
|
24017
|
-
} else if (binary === "npm" && second === "test" && !third) {
|
|
24018
|
-
scriptName = "test";
|
|
24019
|
-
args = ["test"];
|
|
24020
|
-
} else if (binary === "yarn" && second === "run" && third) {
|
|
24021
|
-
scriptName = third;
|
|
24022
|
-
args = ["run", scriptName, ...rest];
|
|
24023
|
-
} else if (binary === "yarn" && second && !third) {
|
|
24024
|
-
scriptName = second;
|
|
24025
|
-
args = [scriptName];
|
|
24026
|
-
} else {
|
|
24027
|
-
return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
|
|
24028
|
-
}
|
|
24029
|
-
if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
|
|
24030
|
-
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
|
|
24031
|
-
}
|
|
24032
|
-
if (!scriptMatchesValidationCategory(scriptName, category)) {
|
|
24033
|
-
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
|
|
24034
|
-
}
|
|
24035
|
-
return {
|
|
24036
|
-
command: {
|
|
24037
|
-
command,
|
|
24038
|
-
args,
|
|
24039
|
-
displayCommand: [command, ...args].join(" "),
|
|
24040
|
-
category,
|
|
24041
|
-
source
|
|
24042
|
-
}
|
|
24043
|
-
};
|
|
25186
|
+
function recordMeshRefineStage(stages, stage, status, startedAt, details) {
|
|
25187
|
+
stages.push({
|
|
25188
|
+
stage,
|
|
25189
|
+
status,
|
|
25190
|
+
durationMs: Date.now() - startedAt,
|
|
25191
|
+
...details || {}
|
|
25192
|
+
});
|
|
24044
25193
|
}
|
|
24045
|
-
function
|
|
24046
|
-
const
|
|
24047
|
-
|
|
24048
|
-
|
|
24049
|
-
|
|
24050
|
-
|
|
24051
|
-
for (const entry of entries) {
|
|
24052
|
-
if (typeof entry?.command !== "string") continue;
|
|
24053
|
-
candidates.push({
|
|
24054
|
-
command: entry.command,
|
|
24055
|
-
category,
|
|
24056
|
-
source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
|
|
24057
|
-
confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
|
|
24058
|
-
});
|
|
24059
|
-
}
|
|
24060
|
-
}
|
|
24061
|
-
return candidates.sort((a, b) => {
|
|
24062
|
-
const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
|
|
24063
|
-
return rank(a.confidence) - rank(b.confidence);
|
|
25194
|
+
async function computeGitPatchId(cwd, fromRef, toRef) {
|
|
25195
|
+
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
25196
|
+
const diff = execFileSync4("git", ["diff", "--patch", "--full-index", fromRef, toRef], {
|
|
25197
|
+
cwd,
|
|
25198
|
+
encoding: "utf8",
|
|
25199
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
24064
25200
|
});
|
|
25201
|
+
if (!diff.trim()) return "";
|
|
25202
|
+
const patchId = execFileSync4("git", ["patch-id", "--stable"], {
|
|
25203
|
+
cwd,
|
|
25204
|
+
input: diff,
|
|
25205
|
+
encoding: "utf8",
|
|
25206
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
25207
|
+
}).trim();
|
|
25208
|
+
return patchId.split(/\s+/)[0] || "";
|
|
24065
25209
|
}
|
|
24066
|
-
function
|
|
24067
|
-
const
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
const
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
}
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24080
|
-
|
|
24081
|
-
|
|
24082
|
-
|
|
24083
|
-
|
|
24084
|
-
|
|
24085
|
-
|
|
24086
|
-
|
|
24087
|
-
|
|
24088
|
-
|
|
24089
|
-
|
|
24090
|
-
if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
|
|
24091
|
-
selected.push(parsed.command);
|
|
24092
|
-
seen.add(parsed.command.displayCommand);
|
|
24093
|
-
if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
|
|
24094
|
-
}
|
|
24095
|
-
if (!selected.length && candidates.length === 0) {
|
|
24096
|
-
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
24097
|
-
if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
|
|
24098
|
-
const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
|
|
24099
|
-
if (fallback.command && !seen.has(fallback.command.displayCommand)) {
|
|
24100
|
-
selected.push(fallback.command);
|
|
24101
|
-
seen.add(fallback.command.displayCommand);
|
|
24102
|
-
} else if (fallback.rejected) {
|
|
24103
|
-
rejectedCommands.push(fallback.rejected);
|
|
24104
|
-
}
|
|
24105
|
-
if (selected.length >= 2) break;
|
|
25210
|
+
async function runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead) {
|
|
25211
|
+
const startedAt = Date.now();
|
|
25212
|
+
try {
|
|
25213
|
+
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
25214
|
+
const git = (args) => execFileSync4("git", args, {
|
|
25215
|
+
cwd: repoRoot,
|
|
25216
|
+
encoding: "utf8",
|
|
25217
|
+
maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
|
|
25218
|
+
});
|
|
25219
|
+
const mergeBase = git(["merge-base", baseHead, branchHead]).trim();
|
|
25220
|
+
const mergeTreeStdout = git(["merge-tree", "--write-tree", baseHead, branchHead]);
|
|
25221
|
+
const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || "";
|
|
25222
|
+
if (!mergeBase || !mergedTree) {
|
|
25223
|
+
return {
|
|
25224
|
+
status: "failed",
|
|
25225
|
+
equivalent: false,
|
|
25226
|
+
baseHead,
|
|
25227
|
+
branchHead,
|
|
25228
|
+
mergeBase: mergeBase || void 0,
|
|
25229
|
+
mergedTree: mergedTree || void 0,
|
|
25230
|
+
durationMs: Date.now() - startedAt,
|
|
25231
|
+
error: "patch equivalence preflight could not resolve merge-base or synthetic merge tree",
|
|
25232
|
+
stdout: truncateValidationOutput(mergeTreeStdout)
|
|
25233
|
+
};
|
|
24106
25234
|
}
|
|
25235
|
+
const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
|
|
25236
|
+
const actualPatchId = await computeGitPatchId(repoRoot, baseHead, mergedTree);
|
|
25237
|
+
const equivalent = expectedPatchId === actualPatchId;
|
|
25238
|
+
return {
|
|
25239
|
+
status: equivalent ? "passed" : "failed",
|
|
25240
|
+
equivalent,
|
|
25241
|
+
baseHead,
|
|
25242
|
+
branchHead,
|
|
25243
|
+
mergeBase,
|
|
25244
|
+
mergedTree,
|
|
25245
|
+
expectedPatchId,
|
|
25246
|
+
actualPatchId,
|
|
25247
|
+
durationMs: Date.now() - startedAt
|
|
25248
|
+
};
|
|
25249
|
+
} catch (e) {
|
|
25250
|
+
return {
|
|
25251
|
+
status: "failed",
|
|
25252
|
+
equivalent: false,
|
|
25253
|
+
baseHead,
|
|
25254
|
+
branchHead,
|
|
25255
|
+
durationMs: Date.now() - startedAt,
|
|
25256
|
+
error: e?.message || String(e),
|
|
25257
|
+
stdout: truncateValidationOutput(e?.stdout),
|
|
25258
|
+
stderr: truncateValidationOutput(e?.stderr)
|
|
25259
|
+
};
|
|
24107
25260
|
}
|
|
25261
|
+
}
|
|
25262
|
+
function buildMeshRefineValidationPlan(mesh, workspace) {
|
|
25263
|
+
const plan = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
24108
25264
|
return {
|
|
24109
|
-
|
|
24110
|
-
|
|
24111
|
-
|
|
25265
|
+
source: plan.source,
|
|
25266
|
+
sourceType: plan.sourceType,
|
|
25267
|
+
commands: plan.commands.map((command) => ({
|
|
25268
|
+
displayCommand: command.displayCommand,
|
|
25269
|
+
category: command.category,
|
|
25270
|
+
source: command.source,
|
|
25271
|
+
cwd: command.cwd,
|
|
25272
|
+
timeoutMs: command.timeoutMs
|
|
25273
|
+
})),
|
|
25274
|
+
unavailableReason: plan.unavailableReason,
|
|
25275
|
+
rejectedCommands: plan.rejectedCommands,
|
|
25276
|
+
suggestions: plan.suggestions,
|
|
25277
|
+
suggestedConfig: plan.suggestedConfig,
|
|
25278
|
+
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."
|
|
24112
25279
|
};
|
|
24113
25280
|
}
|
|
24114
25281
|
async function runMeshRefineValidationGate(mesh, workspace) {
|
|
24115
25282
|
const { execFile: execFile3 } = await import("child_process");
|
|
24116
25283
|
const { promisify: promisify3 } = await import("util");
|
|
24117
25284
|
const execFileAsync3 = promisify3(execFile3);
|
|
24118
|
-
const selection =
|
|
25285
|
+
const selection = resolveMeshRefineValidationPlan(mesh, workspace);
|
|
24119
25286
|
const summary = {
|
|
24120
25287
|
status: "skipped",
|
|
24121
25288
|
required: true,
|
|
@@ -24123,21 +25290,27 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
24123
25290
|
rejectedCommands: selection.rejectedCommands,
|
|
24124
25291
|
skippedReason: void 0,
|
|
24125
25292
|
timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
|
|
24126
|
-
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
|
|
25293
|
+
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
25294
|
+
configSource: selection.source,
|
|
25295
|
+
configSourceType: selection.sourceType,
|
|
25296
|
+
suggestions: selection.suggestions,
|
|
25297
|
+
suggestedConfig: selection.suggestedConfig
|
|
24127
25298
|
};
|
|
24128
25299
|
if (!selection.commands.length) {
|
|
24129
|
-
summary.skippedReason = "validation_unavailable:
|
|
25300
|
+
summary.skippedReason = selection.unavailableReason || "validation_unavailable: repo mesh/refine config did not provide executable validation.commands";
|
|
24130
25301
|
return summary;
|
|
24131
25302
|
}
|
|
24132
25303
|
for (const candidate of selection.commands) {
|
|
24133
25304
|
const startedAt = Date.now();
|
|
25305
|
+
const cwd = candidate.cwd ? (0, import_path8.resolve)(workspace, candidate.cwd) : workspace;
|
|
25306
|
+
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
24134
25307
|
try {
|
|
24135
25308
|
const result = await execFileAsync3(candidate.command, candidate.args, {
|
|
24136
|
-
cwd
|
|
25309
|
+
cwd,
|
|
24137
25310
|
encoding: "utf8",
|
|
24138
|
-
timeout
|
|
25311
|
+
timeout,
|
|
24139
25312
|
maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
24140
|
-
env: { ...process.env, CI: process.env.CI || "1" }
|
|
25313
|
+
env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} }
|
|
24141
25314
|
});
|
|
24142
25315
|
summary.commandsRun.push({
|
|
24143
25316
|
command: candidate.command,
|
|
@@ -24145,6 +25318,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
24145
25318
|
displayCommand: candidate.displayCommand,
|
|
24146
25319
|
category: candidate.category,
|
|
24147
25320
|
source: candidate.source,
|
|
25321
|
+
cwd,
|
|
24148
25322
|
passed: true,
|
|
24149
25323
|
exitCode: 0,
|
|
24150
25324
|
durationMs: Date.now() - startedAt,
|
|
@@ -24158,6 +25332,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
24158
25332
|
displayCommand: candidate.displayCommand,
|
|
24159
25333
|
category: candidate.category,
|
|
24160
25334
|
source: candidate.source,
|
|
25335
|
+
cwd,
|
|
24161
25336
|
passed: false,
|
|
24162
25337
|
exitCode: typeof error?.code === "number" ? error.code : null,
|
|
24163
25338
|
signal: typeof error?.signal === "string" ? error.signal : null,
|
|
@@ -24174,7 +25349,7 @@ async function runMeshRefineValidationGate(mesh, workspace) {
|
|
|
24174
25349
|
return summary;
|
|
24175
25350
|
}
|
|
24176
25351
|
function loadYamlModule() {
|
|
24177
|
-
return
|
|
25352
|
+
return yaml2;
|
|
24178
25353
|
}
|
|
24179
25354
|
function getMcpServersKey(format) {
|
|
24180
25355
|
return format === "hermes_config_yaml" ? "mcp_servers" : "mcpServers";
|
|
@@ -24191,13 +25366,13 @@ function serializeMeshCoordinatorMcpConfig(config, format) {
|
|
|
24191
25366
|
}
|
|
24192
25367
|
function resolveHermesUserHome() {
|
|
24193
25368
|
const explicitHome = process.env.HERMES_HOME?.trim();
|
|
24194
|
-
return explicitHome || (0,
|
|
25369
|
+
return explicitHome || (0, import_path8.join)((0, import_os3.homedir)(), ".hermes");
|
|
24195
25370
|
}
|
|
24196
25371
|
function loadHermesCoordinatorBaseConfig(targetConfigPath) {
|
|
24197
25372
|
const sourceHome = resolveHermesUserHome();
|
|
24198
|
-
const sourceConfigPath = (0,
|
|
25373
|
+
const sourceConfigPath = (0, import_path8.join)(sourceHome, "config.yaml");
|
|
24199
25374
|
if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
|
|
24200
|
-
if ((0,
|
|
25375
|
+
if ((0, import_path8.resolve)(sourceConfigPath) === (0, import_path8.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
|
|
24201
25376
|
const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
|
|
24202
25377
|
const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
|
|
24203
25378
|
return { config: baseConfig, sourceHome, sourceConfigPath };
|
|
@@ -24231,10 +25406,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config) {
|
|
|
24231
25406
|
return sanitized;
|
|
24232
25407
|
}
|
|
24233
25408
|
function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
|
|
24234
|
-
if ((0,
|
|
25409
|
+
if ((0, import_path8.resolve)(sourceHome) === (0, import_path8.resolve)(targetHome)) return;
|
|
24235
25410
|
for (const fileName of [".env", "auth.json"]) {
|
|
24236
|
-
const sourcePath = (0,
|
|
24237
|
-
const targetPath = (0,
|
|
25411
|
+
const sourcePath = (0, import_path8.join)(sourceHome, fileName);
|
|
25412
|
+
const targetPath = (0, import_path8.join)(targetHome, fileName);
|
|
24238
25413
|
if (!fs10.existsSync(sourcePath)) continue;
|
|
24239
25414
|
try {
|
|
24240
25415
|
fs10.copyFileSync(sourcePath, targetPath);
|
|
@@ -24332,36 +25507,216 @@ function summarizeSessionHostPruneResult(result) {
|
|
|
24332
25507
|
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
24333
25508
|
};
|
|
24334
25509
|
}
|
|
25510
|
+
function normalizeStandaloneHostCommandUrl(hostAddress) {
|
|
25511
|
+
const raw = hostAddress.trim();
|
|
25512
|
+
if (!raw) throw new Error("hostAddress required");
|
|
25513
|
+
const url = new URL(raw.replace(/^ws:/, "http:").replace(/^wss:/, "https:"));
|
|
25514
|
+
url.pathname = "/api/v1/command";
|
|
25515
|
+
url.search = "";
|
|
25516
|
+
url.hash = "";
|
|
25517
|
+
return url.toString();
|
|
25518
|
+
}
|
|
25519
|
+
function buildMemberJoinNode(mesh, args, fallbackDaemonId) {
|
|
25520
|
+
const requestedNodeId = typeof args?.memberNodeId === "string" ? args.memberNodeId.trim() : "";
|
|
25521
|
+
const explicit = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
|
|
25522
|
+
const configured = Array.isArray(mesh?.nodes) ? requestedNodeId ? mesh.nodes.find((node) => node?.id === requestedNodeId || node?.nodeId === requestedNodeId) : mesh.nodes[0] : null;
|
|
25523
|
+
const source = explicit || configured;
|
|
25524
|
+
const workspace = typeof source?.workspace === "string" && source.workspace.trim() ? source.workspace.trim() : typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
25525
|
+
if (!workspace) return null;
|
|
25526
|
+
const nodeId = typeof source?.id === "string" && source.id.trim() ? source.id.trim() : typeof source?.nodeId === "string" && source.nodeId.trim() ? source.nodeId.trim() : void 0;
|
|
25527
|
+
return {
|
|
25528
|
+
...nodeId ? { id: nodeId } : {},
|
|
25529
|
+
workspace,
|
|
25530
|
+
...typeof source?.repoRoot === "string" && source.repoRoot.trim() ? { repoRoot: source.repoRoot.trim() } : {},
|
|
25531
|
+
...typeof source?.daemonId === "string" && source.daemonId.trim() ? { daemonId: source.daemonId.trim() } : fallbackDaemonId ? { daemonId: fallbackDaemonId } : {},
|
|
25532
|
+
...typeof source?.machineId === "string" && source.machineId.trim() ? { machineId: source.machineId.trim() } : {},
|
|
25533
|
+
userOverrides: source?.userOverrides && typeof source.userOverrides === "object" && !Array.isArray(source.userOverrides) ? source.userOverrides : {},
|
|
25534
|
+
policy: source?.policy && typeof source.policy === "object" && !Array.isArray(source.policy) ? source.policy : {},
|
|
25535
|
+
role: "member"
|
|
25536
|
+
};
|
|
25537
|
+
}
|
|
24335
25538
|
var DaemonCommandRouter = class {
|
|
24336
25539
|
deps;
|
|
24337
25540
|
/** In-memory cache for cloud-originating meshes passed via inlineMesh.
|
|
24338
25541
|
* Allows the MCP server to query mesh data via get_mesh even when
|
|
24339
25542
|
* the mesh doesn't exist in the local meshes.json file. */
|
|
24340
25543
|
inlineMeshCache = /* @__PURE__ */ new Map();
|
|
25544
|
+
/** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
|
|
25545
|
+
aggregateMeshStatusCache = /* @__PURE__ */ new Map();
|
|
24341
25546
|
constructor(deps) {
|
|
24342
25547
|
this.deps = deps;
|
|
24343
25548
|
}
|
|
25549
|
+
cloneJsonValue(value) {
|
|
25550
|
+
if (typeof structuredClone === "function") return structuredClone(value);
|
|
25551
|
+
return JSON.parse(JSON.stringify(value));
|
|
25552
|
+
}
|
|
25553
|
+
hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options) {
|
|
25554
|
+
if (!mesh || typeof mesh !== "object" || !Array.isArray(mesh.nodes) || !Array.isArray(snapshot?.nodes)) return snapshot;
|
|
25555
|
+
const inlineNodesById = /* @__PURE__ */ new Map();
|
|
25556
|
+
for (const node of mesh.nodes) {
|
|
25557
|
+
const nodeId = readInlineMeshNodeId(node);
|
|
25558
|
+
if (nodeId) inlineNodesById.set(nodeId, node);
|
|
25559
|
+
}
|
|
25560
|
+
if (!inlineNodesById.size) return snapshot;
|
|
25561
|
+
let changed = false;
|
|
25562
|
+
const unavailableNodeIds = /* @__PURE__ */ new Set();
|
|
25563
|
+
const sourceOfTruth = readObjectRecord(snapshot.sourceOfTruth);
|
|
25564
|
+
const directPeerTruth = readObjectRecord(sourceOfTruth.directPeerTruth);
|
|
25565
|
+
for (const entry of Array.isArray(directPeerTruth.unavailableNodeIds) ? directPeerTruth.unavailableNodeIds : []) {
|
|
25566
|
+
const nodeId = readStringValue(entry);
|
|
25567
|
+
if (nodeId) unavailableNodeIds.add(nodeId);
|
|
25568
|
+
}
|
|
25569
|
+
const nodes = snapshot.nodes.map((statusNode) => {
|
|
25570
|
+
const nodeId = readStringValue(statusNode?.nodeId, statusNode?.id);
|
|
25571
|
+
const inlineNode = nodeId ? inlineNodesById.get(nodeId) : void 0;
|
|
25572
|
+
if (!inlineNode) return statusNode;
|
|
25573
|
+
const liveGit = buildInlineMeshTransitGitStatus(inlineNode);
|
|
25574
|
+
if (!liveGit) return statusNode;
|
|
25575
|
+
const nextStatus = { ...statusNode };
|
|
25576
|
+
nextStatus.git = liveGit;
|
|
25577
|
+
nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
|
|
25578
|
+
nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
|
|
25579
|
+
const connection = readObjectRecord(nextStatus.connection);
|
|
25580
|
+
const connectionState = readStringValue(connection.state);
|
|
25581
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
25582
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
25583
|
+
nextStatus.connection = buildLivePeerGitConnection(connection);
|
|
25584
|
+
}
|
|
25585
|
+
delete nextStatus.gitProbePending;
|
|
25586
|
+
const error = readStringValue(nextStatus.error);
|
|
25587
|
+
if (error && /pending_git|git probe|live peer git snapshot|no peer git snapshot/i.test(error)) delete nextStatus.error;
|
|
25588
|
+
if (!readStringValue(nextStatus.machineStatus)) nextStatus.machineStatus = "online";
|
|
25589
|
+
if (nodeId) unavailableNodeIds.delete(nodeId);
|
|
25590
|
+
changed = true;
|
|
25591
|
+
return nextStatus;
|
|
25592
|
+
});
|
|
25593
|
+
if (!changed && !(options?.requireDirectPeerTruth && unavailableNodeIds.size > 0)) return snapshot;
|
|
25594
|
+
const nextSourceOfTruth = {
|
|
25595
|
+
...sourceOfTruth,
|
|
25596
|
+
...Object.keys(directPeerTruth).length ? {
|
|
25597
|
+
directPeerTruth: {
|
|
25598
|
+
...directPeerTruth,
|
|
25599
|
+
satisfied: options?.requireDirectPeerTruth === true ? unavailableNodeIds.size === 0 : directPeerTruth.satisfied,
|
|
25600
|
+
unavailableNodeIds: [...unavailableNodeIds]
|
|
25601
|
+
},
|
|
25602
|
+
...options?.requireDirectPeerTruth === true ? {
|
|
25603
|
+
coordinatorOwnsLiveTruth: unavailableNodeIds.size === 0,
|
|
25604
|
+
currentStatus: unavailableNodeIds.size === 0 ? "live_git_and_session_probes" : "direct_peer_truth_unavailable"
|
|
25605
|
+
} : {}
|
|
25606
|
+
} : {}
|
|
25607
|
+
};
|
|
25608
|
+
return {
|
|
25609
|
+
...snapshot,
|
|
25610
|
+
...options?.requireDirectPeerTruth === true && unavailableNodeIds.size > 0 ? {
|
|
25611
|
+
success: false,
|
|
25612
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
25613
|
+
error: "Selected coordinator could not confirm direct mesh truth for every remote node yet."
|
|
25614
|
+
} : {},
|
|
25615
|
+
sourceOfTruth: nextSourceOfTruth,
|
|
25616
|
+
nodes
|
|
25617
|
+
};
|
|
25618
|
+
}
|
|
25619
|
+
getCachedAggregateMeshStatus(meshId, mesh, options) {
|
|
25620
|
+
const cached = this.aggregateMeshStatusCache.get(meshId);
|
|
25621
|
+
if (!cached?.snapshot || cached.snapshot.success !== true || !Array.isArray(cached.snapshot.nodes)) return null;
|
|
25622
|
+
let snapshot = this.cloneJsonValue(cached.snapshot);
|
|
25623
|
+
snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
|
|
25624
|
+
if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
|
|
25625
|
+
const ageMs = Math.max(0, Date.now() - cached.builtAt);
|
|
25626
|
+
const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
|
|
25627
|
+
snapshot.sourceOfTruth = {
|
|
25628
|
+
...sourceOfTruth,
|
|
25629
|
+
aggregateSnapshot: {
|
|
25630
|
+
...sourceOfTruth.aggregateSnapshot && typeof sourceOfTruth.aggregateSnapshot === "object" ? sourceOfTruth.aggregateSnapshot : {},
|
|
25631
|
+
owner: "coordinator_daemon_memory",
|
|
25632
|
+
cached: true,
|
|
25633
|
+
source: "memory",
|
|
25634
|
+
refreshReason: "memory_cache_hit",
|
|
25635
|
+
ageMs,
|
|
25636
|
+
cachedAt: new Date(cached.builtAt).toISOString(),
|
|
25637
|
+
returnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25638
|
+
}
|
|
25639
|
+
};
|
|
25640
|
+
return snapshot;
|
|
25641
|
+
}
|
|
25642
|
+
rememberAggregateMeshStatus(meshId, snapshot, refreshReason) {
|
|
25643
|
+
if (!snapshot || typeof snapshot !== "object" || snapshot.success !== true || !Array.isArray(snapshot.nodes)) return snapshot;
|
|
25644
|
+
const builtAt = Date.now();
|
|
25645
|
+
const next = this.cloneJsonValue(snapshot);
|
|
25646
|
+
const sourceOfTruth = next.sourceOfTruth && typeof next.sourceOfTruth === "object" ? next.sourceOfTruth : {};
|
|
25647
|
+
next.sourceOfTruth = {
|
|
25648
|
+
...sourceOfTruth,
|
|
25649
|
+
aggregateSnapshot: {
|
|
25650
|
+
owner: "coordinator_daemon_memory",
|
|
25651
|
+
cached: false,
|
|
25652
|
+
source: "live_refresh",
|
|
25653
|
+
refreshReason,
|
|
25654
|
+
ageMs: 0,
|
|
25655
|
+
cachedAt: new Date(builtAt).toISOString(),
|
|
25656
|
+
returnedAt: new Date(builtAt).toISOString()
|
|
25657
|
+
}
|
|
25658
|
+
};
|
|
25659
|
+
this.aggregateMeshStatusCache.set(meshId, { builtAt, snapshot: this.cloneJsonValue(next) });
|
|
25660
|
+
return next;
|
|
25661
|
+
}
|
|
24344
25662
|
getCachedInlineMesh(meshId, inlineMesh) {
|
|
24345
25663
|
if (inlineMesh && typeof inlineMesh === "object") {
|
|
24346
|
-
this.
|
|
24347
|
-
return inlineMesh;
|
|
25664
|
+
return this.warmInlineMeshCache(meshId, inlineMesh);
|
|
24348
25665
|
}
|
|
24349
25666
|
return this.inlineMeshCache.get(meshId);
|
|
24350
25667
|
}
|
|
25668
|
+
warmInlineMeshCache(meshId, inlineMesh) {
|
|
25669
|
+
if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
|
|
25670
|
+
const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
|
|
25671
|
+
const cached = this.inlineMeshCache.get(meshId);
|
|
25672
|
+
if (cached) {
|
|
25673
|
+
const merged = reconcileInlineMeshCache(cached, sanitizedInlineMesh);
|
|
25674
|
+
this.inlineMeshCache.set(meshId, merged);
|
|
25675
|
+
return merged;
|
|
25676
|
+
}
|
|
25677
|
+
this.inlineMeshCache.set(meshId, sanitizedInlineMesh);
|
|
25678
|
+
return sanitizedInlineMesh;
|
|
25679
|
+
}
|
|
24351
25680
|
async getMeshForCommand(meshId, inlineMesh, options) {
|
|
24352
25681
|
const preferInline = options?.preferInline === true;
|
|
24353
25682
|
if (preferInline) {
|
|
24354
|
-
const cached2 = this.getCachedInlineMesh(meshId
|
|
24355
|
-
if (cached2)
|
|
25683
|
+
const cached2 = this.getCachedInlineMesh(meshId);
|
|
25684
|
+
if (cached2) {
|
|
25685
|
+
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
25686
|
+
const merged = reconcileInlineMeshCache(cached2, inlineMesh);
|
|
25687
|
+
this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
|
|
25688
|
+
return { mesh: merged, inline: true, source: "inline_cache" };
|
|
25689
|
+
}
|
|
25690
|
+
return { mesh: cached2, inline: true, source: "inline_cache" };
|
|
25691
|
+
}
|
|
25692
|
+
if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
|
|
25693
|
+
this.warmInlineMeshCache(meshId, inlineMesh);
|
|
25694
|
+
return { mesh: inlineMesh, inline: true, source: "inline_bootstrap" };
|
|
25695
|
+
}
|
|
24356
25696
|
}
|
|
24357
25697
|
try {
|
|
24358
25698
|
const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
24359
25699
|
const mesh = getMesh3(meshId);
|
|
24360
|
-
if (mesh) return { mesh, inline: false };
|
|
25700
|
+
if (mesh) return { mesh, inline: false, source: "local_config" };
|
|
24361
25701
|
} catch {
|
|
24362
25702
|
}
|
|
24363
|
-
const cached = this.getCachedInlineMesh(meshId
|
|
24364
|
-
|
|
25703
|
+
const cached = this.getCachedInlineMesh(meshId);
|
|
25704
|
+
if (cached) return { mesh: cached, inline: true, source: "inline_cache" };
|
|
25705
|
+
const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
|
|
25706
|
+
return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
|
|
25707
|
+
}
|
|
25708
|
+
invalidateAggregateMeshStatus(meshId) {
|
|
25709
|
+
this.aggregateMeshStatusCache.delete(meshId);
|
|
25710
|
+
}
|
|
25711
|
+
async requireMeshHostMutationOwner(meshId, inlineMesh, operation) {
|
|
25712
|
+
const meshRecord = await this.getMeshForCommand(meshId, inlineMesh, { preferInline: true });
|
|
25713
|
+
const mesh = meshRecord?.mesh;
|
|
25714
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
25715
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
25716
|
+
if (!meshHost.canOwnCoordinator || !meshHost.canOwnQueue) {
|
|
25717
|
+
return { ...buildMeshHostRequiredFailure(mesh, operation), success: false, meshId };
|
|
25718
|
+
}
|
|
25719
|
+
return null;
|
|
24365
25720
|
}
|
|
24366
25721
|
updateInlineMeshNode(meshId, mesh, node) {
|
|
24367
25722
|
if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
|
|
@@ -24370,6 +25725,7 @@ var DaemonCommandRouter = class {
|
|
|
24370
25725
|
else mesh.nodes.push(node);
|
|
24371
25726
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24372
25727
|
this.inlineMeshCache.set(meshId, mesh);
|
|
25728
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
24373
25729
|
}
|
|
24374
25730
|
removeInlineMeshNode(meshId, mesh, nodeId) {
|
|
24375
25731
|
if (!mesh || !Array.isArray(mesh.nodes)) return false;
|
|
@@ -24378,6 +25734,7 @@ var DaemonCommandRouter = class {
|
|
|
24378
25734
|
mesh.nodes.splice(idx, 1);
|
|
24379
25735
|
mesh.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
24380
25736
|
this.inlineMeshCache.set(meshId, mesh);
|
|
25737
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
24381
25738
|
return true;
|
|
24382
25739
|
}
|
|
24383
25740
|
normalizeMeshSessionCleanupMode(value) {
|
|
@@ -24426,7 +25783,7 @@ var DaemonCommandRouter = class {
|
|
|
24426
25783
|
}
|
|
24427
25784
|
const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
24428
25785
|
const normalizePath = (value) => {
|
|
24429
|
-
const resolved = (0,
|
|
25786
|
+
const resolved = (0, import_path8.resolve)(value);
|
|
24430
25787
|
try {
|
|
24431
25788
|
return fs10.realpathSync(resolved);
|
|
24432
25789
|
} catch {
|
|
@@ -24590,6 +25947,7 @@ var DaemonCommandRouter = class {
|
|
|
24590
25947
|
const deletedSessionIds = [];
|
|
24591
25948
|
const skippedSessionIds = [];
|
|
24592
25949
|
const skippedLiveSessionIds = [];
|
|
25950
|
+
const skippedCoordinatorSessionIds = [];
|
|
24593
25951
|
const deleteUnsupportedSessionIds = [];
|
|
24594
25952
|
const recordsRemainSessionIds = [];
|
|
24595
25953
|
const errors = [];
|
|
@@ -24622,6 +25980,12 @@ var DaemonCommandRouter = class {
|
|
|
24622
25980
|
const completed = this.isCompletedHostedSession(record);
|
|
24623
25981
|
const surfaceKind = getSessionHostSurfaceKind(record);
|
|
24624
25982
|
const liveRuntime = surfaceKind === "live_runtime";
|
|
25983
|
+
const coordinatorSession = readStringValue(record?.meta?.meshCoordinatorFor) === args.meshId;
|
|
25984
|
+
if (!hasExplicitSessionIds && coordinatorSession) {
|
|
25985
|
+
skippedSessionIds.push(sessionId);
|
|
25986
|
+
skippedCoordinatorSessionIds.push(sessionId);
|
|
25987
|
+
continue;
|
|
25988
|
+
}
|
|
24625
25989
|
if (!hasExplicitSessionIds && liveRuntime) {
|
|
24626
25990
|
skippedSessionIds.push(sessionId);
|
|
24627
25991
|
skippedLiveSessionIds.push(sessionId);
|
|
@@ -24687,6 +26051,7 @@ var DaemonCommandRouter = class {
|
|
|
24687
26051
|
deletedSessionIds,
|
|
24688
26052
|
skippedSessionIds,
|
|
24689
26053
|
skippedLiveSessionIds,
|
|
26054
|
+
skippedCoordinatorSessionIds,
|
|
24690
26055
|
...deleteUnsupported ? {
|
|
24691
26056
|
deleteUnsupported: true,
|
|
24692
26057
|
effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
|
|
@@ -24819,7 +26184,8 @@ var DaemonCommandRouter = class {
|
|
|
24819
26184
|
return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
|
|
24820
26185
|
}
|
|
24821
26186
|
case "get_pending_mesh_events": {
|
|
24822
|
-
const
|
|
26187
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26188
|
+
const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
|
|
24823
26189
|
return { success: true, events };
|
|
24824
26190
|
}
|
|
24825
26191
|
case "launch_cli":
|
|
@@ -25348,15 +26714,39 @@ var DaemonCommandRouter = class {
|
|
|
25348
26714
|
case "get_mesh": {
|
|
25349
26715
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25350
26716
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25351
|
-
|
|
25352
|
-
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
|
|
26717
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26718
|
+
if (!meshRecord?.mesh) return { success: false, error: "Mesh not found" };
|
|
26719
|
+
const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
|
|
26720
|
+
const directTruth = await hydrateInlineMeshDirectTruth({
|
|
26721
|
+
mesh: meshRecord.mesh,
|
|
26722
|
+
meshSource: meshRecord.source,
|
|
26723
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
26724
|
+
statusInstanceId: this.deps.statusInstanceId,
|
|
26725
|
+
localMachineId: loadConfig().machineId || ""
|
|
26726
|
+
});
|
|
26727
|
+
const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
|
|
26728
|
+
const sourceOfTruth = {
|
|
26729
|
+
membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
26730
|
+
coordinatorOwnsLiveTruth: directTruthSatisfied,
|
|
26731
|
+
directPeerTruth: {
|
|
26732
|
+
required: requireDirectPeerTruth,
|
|
26733
|
+
satisfied: directTruthSatisfied,
|
|
26734
|
+
directEvidenceCount: directTruth.directEvidenceCount,
|
|
26735
|
+
localConfirmedCount: directTruth.localConfirmedCount,
|
|
26736
|
+
peerAttemptedCount: directTruth.peerAttemptedCount,
|
|
26737
|
+
peerConfirmedCount: directTruth.peerConfirmedCount,
|
|
26738
|
+
unavailableNodeIds: directTruth.unavailableNodeIds
|
|
26739
|
+
}
|
|
26740
|
+
};
|
|
26741
|
+
if (requireDirectPeerTruth && !directTruthSatisfied) {
|
|
26742
|
+
return {
|
|
26743
|
+
success: false,
|
|
26744
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
26745
|
+
error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.",
|
|
26746
|
+
sourceOfTruth
|
|
26747
|
+
};
|
|
25356
26748
|
}
|
|
25357
|
-
|
|
25358
|
-
if (cached) return { success: true, mesh: cached };
|
|
25359
|
-
return { success: false, error: "Mesh not found" };
|
|
26749
|
+
return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
|
|
25360
26750
|
}
|
|
25361
26751
|
case "create_mesh": {
|
|
25362
26752
|
const name = typeof args?.name === "string" ? args.name.trim() : "";
|
|
@@ -25366,7 +26756,8 @@ var DaemonCommandRouter = class {
|
|
|
25366
26756
|
if (!name) return { success: false, error: "name required" };
|
|
25367
26757
|
try {
|
|
25368
26758
|
const { createMesh: createMesh2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25369
|
-
const
|
|
26759
|
+
const meshHost = args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost) ? args.meshHost : void 0;
|
|
26760
|
+
const mesh = createMesh2({ name, repoIdentity, repoRemoteUrl, defaultBranch, policy: args?.policy, meshHost });
|
|
25370
26761
|
return { success: true, mesh };
|
|
25371
26762
|
} catch (e) {
|
|
25372
26763
|
return { success: false, error: e.message };
|
|
@@ -25382,15 +26773,226 @@ var DaemonCommandRouter = class {
|
|
|
25382
26773
|
if (typeof args?.defaultBranch === "string") patch.defaultBranch = args.defaultBranch;
|
|
25383
26774
|
if (args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy)) patch.policy = args.policy;
|
|
25384
26775
|
if (args?.coordinator && typeof args.coordinator === "object" && !Array.isArray(args.coordinator)) patch.coordinator = args.coordinator;
|
|
26776
|
+
if (args?.meshHost && typeof args.meshHost === "object" && !Array.isArray(args.meshHost)) patch.meshHost = args.meshHost;
|
|
25385
26777
|
if (!Object.keys(patch).length) return { success: false, error: "No updates provided" };
|
|
25386
26778
|
const mesh = updateMesh2(meshId, patch);
|
|
25387
26779
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
25388
26780
|
this.inlineMeshCache.set(meshId, mesh);
|
|
26781
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
25389
26782
|
return { success: true, mesh };
|
|
25390
26783
|
} catch (e) {
|
|
25391
26784
|
return { success: false, error: e.message };
|
|
25392
26785
|
}
|
|
25393
26786
|
}
|
|
26787
|
+
case "get_mesh_host_pairing": {
|
|
26788
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26789
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26790
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26791
|
+
const mesh = meshRecord?.mesh;
|
|
26792
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
26793
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
26794
|
+
const pairingStatus = meshHost.pairing?.status || "not_configured";
|
|
26795
|
+
return {
|
|
26796
|
+
success: true,
|
|
26797
|
+
code: pairingStatus === "not_configured" ? "mesh_host_pairing_not_configured" : "mesh_host_pairing_pending",
|
|
26798
|
+
meshId,
|
|
26799
|
+
hostAddress: meshHost.hostAddress,
|
|
26800
|
+
meshHost,
|
|
26801
|
+
manualPairing: {
|
|
26802
|
+
status: pairingStatus,
|
|
26803
|
+
joinImplemented: true,
|
|
26804
|
+
protocol: "standalone_command_direct_v1",
|
|
26805
|
+
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."
|
|
26806
|
+
}
|
|
26807
|
+
};
|
|
26808
|
+
}
|
|
26809
|
+
case "configure_mesh_host_pairing": {
|
|
26810
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26811
|
+
const hostAddress = typeof args?.hostAddress === "string" ? args.hostAddress.trim() : "";
|
|
26812
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26813
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26814
|
+
if (!hostAddress || !token) return { success: false, error: "hostAddress and token required" };
|
|
26815
|
+
try {
|
|
26816
|
+
const { configureMeshHostPairing: configureMeshHostPairing2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26817
|
+
const configured = configureMeshHostPairing2(meshId, { hostAddress, token });
|
|
26818
|
+
if (!configured) return { success: false, error: "Mesh not found" };
|
|
26819
|
+
this.inlineMeshCache.set(meshId, configured.mesh);
|
|
26820
|
+
const meshHost = resolveMeshHostStatus(configured.mesh);
|
|
26821
|
+
return {
|
|
26822
|
+
success: true,
|
|
26823
|
+
code: "mesh_host_pairing_pending",
|
|
26824
|
+
meshId,
|
|
26825
|
+
hostAddress: configured.hostAddress,
|
|
26826
|
+
meshHost,
|
|
26827
|
+
manualPairing: {
|
|
26828
|
+
status: meshHost.pairing?.status || "pairing",
|
|
26829
|
+
joinImplemented: true,
|
|
26830
|
+
protocol: "standalone_command_direct_v1",
|
|
26831
|
+
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."
|
|
26832
|
+
}
|
|
26833
|
+
};
|
|
26834
|
+
} catch (e) {
|
|
26835
|
+
return { success: false, code: "mesh_host_pairing_invalid", meshId, hostAddress, error: e.message };
|
|
26836
|
+
}
|
|
26837
|
+
}
|
|
26838
|
+
case "create_mesh_host_pairing_token": {
|
|
26839
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26840
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26841
|
+
try {
|
|
26842
|
+
const { createMeshHostPairingToken: createMeshHostPairingToken2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26843
|
+
const created = createMeshHostPairingToken2(meshId, {
|
|
26844
|
+
token: typeof args?.token === "string" ? args.token : void 0,
|
|
26845
|
+
expiresAt: typeof args?.expiresAt === "string" ? args.expiresAt : void 0
|
|
26846
|
+
});
|
|
26847
|
+
if (!created) return { success: false, error: "Mesh not found" };
|
|
26848
|
+
this.inlineMeshCache.set(meshId, created.mesh);
|
|
26849
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26850
|
+
return {
|
|
26851
|
+
success: true,
|
|
26852
|
+
code: "mesh_host_pairing_token_created",
|
|
26853
|
+
meshId,
|
|
26854
|
+
token: created.token,
|
|
26855
|
+
tokenId: created.tokenId,
|
|
26856
|
+
expiresAt: created.expiresAt,
|
|
26857
|
+
meshHost: resolveMeshHostStatus(created.mesh),
|
|
26858
|
+
warning: "Raw token is returned once and is not persisted; share it with member daemons over a trusted channel."
|
|
26859
|
+
};
|
|
26860
|
+
} catch (e) {
|
|
26861
|
+
return { success: false, code: "mesh_host_pairing_token_invalid", meshId, error: e.message };
|
|
26862
|
+
}
|
|
26863
|
+
}
|
|
26864
|
+
case "apply_mesh_host_join": {
|
|
26865
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26866
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26867
|
+
const memberNode = args?.memberNode && typeof args.memberNode === "object" && !Array.isArray(args.memberNode) ? args.memberNode : null;
|
|
26868
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26869
|
+
if (!token || !memberNode) return { success: false, error: "token and memberNode required" };
|
|
26870
|
+
try {
|
|
26871
|
+
const { applyMeshHostJoinRequest: applyMeshHostJoinRequest2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26872
|
+
const applied = applyMeshHostJoinRequest2(meshId, {
|
|
26873
|
+
token,
|
|
26874
|
+
memberNode,
|
|
26875
|
+
memberMeshId: typeof args?.memberMeshId === "string" ? args.memberMeshId : void 0
|
|
26876
|
+
});
|
|
26877
|
+
if (!applied) return { success: false, error: "Mesh not found" };
|
|
26878
|
+
if (!applied.accepted) {
|
|
26879
|
+
return {
|
|
26880
|
+
success: false,
|
|
26881
|
+
code: "mesh_host_join_rejected",
|
|
26882
|
+
meshId,
|
|
26883
|
+
tokenId: applied.tokenId,
|
|
26884
|
+
meshHost: applied.meshHost ? resolveMeshHostStatus({ meshHost: applied.meshHost }) : void 0,
|
|
26885
|
+
error: applied.reason
|
|
26886
|
+
};
|
|
26887
|
+
}
|
|
26888
|
+
this.inlineMeshCache.set(meshId, applied.mesh);
|
|
26889
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26890
|
+
try {
|
|
26891
|
+
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
26892
|
+
appendLedgerEntry2(meshId, {
|
|
26893
|
+
kind: "node_joined",
|
|
26894
|
+
nodeId: applied.node.id,
|
|
26895
|
+
payload: { role: "member", tokenId: applied.tokenId, workspace: applied.node.workspace }
|
|
26896
|
+
});
|
|
26897
|
+
} catch {
|
|
26898
|
+
}
|
|
26899
|
+
return {
|
|
26900
|
+
success: true,
|
|
26901
|
+
code: "mesh_host_join_accepted",
|
|
26902
|
+
meshId,
|
|
26903
|
+
node: applied.node,
|
|
26904
|
+
tokenId: applied.tokenId,
|
|
26905
|
+
meshHost: resolveMeshHostStatus(applied.mesh)
|
|
26906
|
+
};
|
|
26907
|
+
} catch (e) {
|
|
26908
|
+
return { success: false, code: "mesh_host_join_failed", meshId, error: e.message };
|
|
26909
|
+
}
|
|
26910
|
+
}
|
|
26911
|
+
case "join_mesh_host_pairing": {
|
|
26912
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
26913
|
+
const token = typeof args?.token === "string" ? args.token.trim() : "";
|
|
26914
|
+
if (!meshId) return { success: false, error: "meshId required" };
|
|
26915
|
+
if (!token) return { success: false, error: "token required because raw pairing tokens are not persisted" };
|
|
26916
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26917
|
+
const mesh = meshRecord?.mesh;
|
|
26918
|
+
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
26919
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
26920
|
+
if (meshHost.role !== "member") {
|
|
26921
|
+
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." };
|
|
26922
|
+
}
|
|
26923
|
+
try {
|
|
26924
|
+
const { tokenIdForManualPairing: tokenIdForManualPairing2, markMeshHostPairingJoined: markMeshHostPairingJoined2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
26925
|
+
const tokenId = tokenIdForManualPairing2(token);
|
|
26926
|
+
if (meshHost.pairing?.tokenId && meshHost.pairing.tokenId !== tokenId) {
|
|
26927
|
+
return { success: false, code: "mesh_host_join_rejected", meshId, tokenId, meshHost, error: "invalid pairing token" };
|
|
26928
|
+
}
|
|
26929
|
+
const memberNode = buildMemberJoinNode(mesh, args, this.deps.statusInstanceId);
|
|
26930
|
+
if (!memberNode) return { success: false, error: "member node metadata unavailable" };
|
|
26931
|
+
const hostMeshId = typeof args?.hostMeshId === "string" && args.hostMeshId.trim() ? args.hostMeshId.trim() : meshId;
|
|
26932
|
+
const hostDaemonId = typeof args?.hostDaemonId === "string" && args.hostDaemonId.trim() ? args.hostDaemonId.trim() : meshHost.hostDaemonId;
|
|
26933
|
+
let hostResult;
|
|
26934
|
+
let transport;
|
|
26935
|
+
if (hostDaemonId && this.deps.dispatchMeshCommand) {
|
|
26936
|
+
transport = "mesh_command_dispatch";
|
|
26937
|
+
hostResult = await this.deps.dispatchMeshCommand(hostDaemonId, "apply_mesh_host_join", {
|
|
26938
|
+
meshId: hostMeshId,
|
|
26939
|
+
token,
|
|
26940
|
+
memberMeshId: meshId,
|
|
26941
|
+
memberNode
|
|
26942
|
+
});
|
|
26943
|
+
} else if (meshHost.hostAddress) {
|
|
26944
|
+
transport = "standalone_http_command";
|
|
26945
|
+
const commandUrl = normalizeStandaloneHostCommandUrl(meshHost.hostAddress);
|
|
26946
|
+
const response = await fetch(commandUrl, {
|
|
26947
|
+
method: "POST",
|
|
26948
|
+
headers: { "Content-Type": "application/json" },
|
|
26949
|
+
body: JSON.stringify({ type: "apply_mesh_host_join", payload: { meshId: hostMeshId, token, memberMeshId: meshId, memberNode } })
|
|
26950
|
+
});
|
|
26951
|
+
hostResult = await response.json().catch(() => ({ success: false, error: `Host returned HTTP ${response.status}` }));
|
|
26952
|
+
if (!response.ok && hostResult?.success !== false) hostResult = { success: false, error: `Host returned HTTP ${response.status}` };
|
|
26953
|
+
} else {
|
|
26954
|
+
return {
|
|
26955
|
+
success: false,
|
|
26956
|
+
code: "mesh_host_join_transport_unavailable",
|
|
26957
|
+
meshId,
|
|
26958
|
+
meshHost,
|
|
26959
|
+
error: "No hostDaemonId dispatch path or hostAddress HTTP command path is available. P2P signaling join is not implemented in this slice."
|
|
26960
|
+
};
|
|
26961
|
+
}
|
|
26962
|
+
if (!hostResult?.success) {
|
|
26963
|
+
return { success: false, code: hostResult?.code || "mesh_host_join_rejected", meshId, meshHost, transport, error: hostResult?.error || "Mesh Host rejected join request", hostResult };
|
|
26964
|
+
}
|
|
26965
|
+
const joined = meshRecord.inline ? null : markMeshHostPairingJoined2(meshId, {
|
|
26966
|
+
tokenId: hostResult.tokenId || tokenId,
|
|
26967
|
+
hostDaemonId: hostResult.meshHost?.hostDaemonId || hostDaemonId,
|
|
26968
|
+
hostNodeId: hostResult.meshHost?.hostNodeId,
|
|
26969
|
+
joinedAt: hostResult.meshHost?.pairing?.joinedAt
|
|
26970
|
+
});
|
|
26971
|
+
if (joined) {
|
|
26972
|
+
this.inlineMeshCache.set(meshId, joined.mesh);
|
|
26973
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
26974
|
+
}
|
|
26975
|
+
return {
|
|
26976
|
+
success: true,
|
|
26977
|
+
code: "mesh_host_join_applied",
|
|
26978
|
+
meshId,
|
|
26979
|
+
hostMeshId,
|
|
26980
|
+
transport,
|
|
26981
|
+
node: hostResult.node,
|
|
26982
|
+
tokenId: hostResult.tokenId || tokenId,
|
|
26983
|
+
meshHost: joined ? resolveMeshHostStatus(joined.mesh) : { ...meshHost, pairing: { ...meshHost.pairing || {}, status: "paired", tokenId: hostResult.tokenId || tokenId } },
|
|
26984
|
+
hostResult,
|
|
26985
|
+
manualPairing: {
|
|
26986
|
+
status: "paired",
|
|
26987
|
+
joinImplemented: true,
|
|
26988
|
+
protocol: "standalone_command_direct_v1",
|
|
26989
|
+
description: "Mesh Host accepted the join and local member pairing status was marked paired. P2P runtime signaling remains outside this slice."
|
|
26990
|
+
}
|
|
26991
|
+
};
|
|
26992
|
+
} catch (e) {
|
|
26993
|
+
return { success: false, code: "mesh_host_join_failed", meshId, meshHost, error: e.message };
|
|
26994
|
+
}
|
|
26995
|
+
}
|
|
25394
26996
|
case "delete_mesh": {
|
|
25395
26997
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25396
26998
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
@@ -25473,6 +27075,8 @@ var DaemonCommandRouter = class {
|
|
|
25473
27075
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25474
27076
|
const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
|
|
25475
27077
|
if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
|
|
27078
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue cancellation");
|
|
27079
|
+
if (ownerFailure) return ownerFailure;
|
|
25476
27080
|
try {
|
|
25477
27081
|
const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
25478
27082
|
const reason = typeof args?.reason === "string" ? args.reason : void 0;
|
|
@@ -25487,6 +27091,8 @@ var DaemonCommandRouter = class {
|
|
|
25487
27091
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25488
27092
|
const taskId = typeof args?.taskId === "string" ? args.taskId.trim() : "";
|
|
25489
27093
|
if (!meshId || !taskId) return { success: false, error: "meshId and taskId required" };
|
|
27094
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue requeue");
|
|
27095
|
+
if (ownerFailure) return ownerFailure;
|
|
25490
27096
|
try {
|
|
25491
27097
|
const { requeueTask: requeueTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
25492
27098
|
const task = requeueTask2(meshId, taskId, {
|
|
@@ -25507,6 +27113,8 @@ var DaemonCommandRouter = class {
|
|
|
25507
27113
|
const workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
|
|
25508
27114
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25509
27115
|
if (!workspace) return { success: false, error: "workspace required" };
|
|
27116
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node addition");
|
|
27117
|
+
if (ownerFailure) return ownerFailure;
|
|
25510
27118
|
try {
|
|
25511
27119
|
const { addNode: addNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25512
27120
|
const providerPriority = Array.isArray(args?.providerPriority) ? args.providerPriority.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean) : [];
|
|
@@ -25515,7 +27123,8 @@ var DaemonCommandRouter = class {
|
|
|
25515
27123
|
...readOnly ? { readOnly: true } : {},
|
|
25516
27124
|
...providerPriority.length ? { providerPriority } : {}
|
|
25517
27125
|
};
|
|
25518
|
-
const
|
|
27126
|
+
const role = normalizeMeshDaemonRole(args?.role);
|
|
27127
|
+
const node = addNode3(meshId, { workspace, ...policy ? { policy } : {}, ...role ? { role } : {} });
|
|
25519
27128
|
if (!node) return { success: false, error: "Mesh not found" };
|
|
25520
27129
|
return { success: true, node };
|
|
25521
27130
|
} catch (e) {
|
|
@@ -25526,6 +27135,8 @@ var DaemonCommandRouter = class {
|
|
|
25526
27135
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25527
27136
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25528
27137
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
27138
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node update");
|
|
27139
|
+
if (ownerFailure) return ownerFailure;
|
|
25529
27140
|
try {
|
|
25530
27141
|
const { updateNode: updateNode2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25531
27142
|
const policy = args?.policy && typeof args.policy === "object" && !Array.isArray(args.policy) ? { ...args.policy } : {};
|
|
@@ -25549,6 +27160,8 @@ var DaemonCommandRouter = class {
|
|
|
25549
27160
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25550
27161
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25551
27162
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
27163
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "node removal");
|
|
27164
|
+
if (ownerFailure) return ownerFailure;
|
|
25552
27165
|
try {
|
|
25553
27166
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25554
27167
|
const mesh = meshRecord?.mesh;
|
|
@@ -25571,30 +27184,88 @@ var DaemonCommandRouter = class {
|
|
|
25571
27184
|
return { success: false, error: e.message };
|
|
25572
27185
|
}
|
|
25573
27186
|
}
|
|
27187
|
+
case "get_mesh_refine_config_schema": {
|
|
27188
|
+
return {
|
|
27189
|
+
success: true,
|
|
27190
|
+
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
27191
|
+
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
27192
|
+
sourceOfTruth: "repo mesh/refine config",
|
|
27193
|
+
heuristicRole: "suggestions_only_not_execution_path"
|
|
27194
|
+
};
|
|
27195
|
+
}
|
|
27196
|
+
case "validate_mesh_refine_config": {
|
|
27197
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
27198
|
+
const mesh = args?.inlineMesh || {};
|
|
27199
|
+
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
27200
|
+
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
27201
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
27202
|
+
}
|
|
27203
|
+
case "suggest_mesh_refine_config": {
|
|
27204
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
27205
|
+
const mesh = args?.inlineMesh || {};
|
|
27206
|
+
return {
|
|
27207
|
+
success: true,
|
|
27208
|
+
...suggestMeshRefineConfig(mesh, workspace),
|
|
27209
|
+
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
27210
|
+
};
|
|
27211
|
+
}
|
|
27212
|
+
case "plan_mesh_refine_node": {
|
|
27213
|
+
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
27214
|
+
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
27215
|
+
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
27216
|
+
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
27217
|
+
const mesh = meshRecord?.mesh;
|
|
27218
|
+
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
27219
|
+
if (!node?.workspace) return { success: false, error: `Node '${nodeId}' workspace not found` };
|
|
27220
|
+
return {
|
|
27221
|
+
success: true,
|
|
27222
|
+
dryRun: true,
|
|
27223
|
+
nodeId,
|
|
27224
|
+
workspace: node.workspace,
|
|
27225
|
+
validationPlan: buildMeshRefineValidationPlan(mesh, node.workspace),
|
|
27226
|
+
mergeWillRun: false,
|
|
27227
|
+
cleanupWillRun: false
|
|
27228
|
+
};
|
|
27229
|
+
}
|
|
25574
27230
|
case "refine_mesh_node": {
|
|
25575
27231
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25576
27232
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
25577
27233
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
27234
|
+
const refineStages = [];
|
|
25578
27235
|
try {
|
|
25579
27236
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25580
27237
|
const mesh = meshRecord?.mesh;
|
|
25581
27238
|
const node = mesh?.nodes?.find((n) => n.id === nodeId || n.nodeId === nodeId);
|
|
25582
|
-
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh
|
|
27239
|
+
if (!node) return { success: false, error: `Node '${nodeId}' not found in mesh`, refineStages };
|
|
25583
27240
|
if (!node.isLocalWorktree || !node.workspace) {
|
|
25584
|
-
return { success: false, error: `Refinery requires a local worktree node
|
|
27241
|
+
return { success: false, error: `Refinery requires a local worktree node`, refineStages };
|
|
25585
27242
|
}
|
|
25586
27243
|
const sourceNode = node.clonedFromNodeId ? mesh?.nodes.find((n) => n.id === node.clonedFromNodeId || n.nodeId === node.clonedFromNodeId) : mesh?.nodes.find((n) => !n.isLocalWorktree);
|
|
25587
27244
|
const repoRoot = sourceNode?.repoRoot || sourceNode?.workspace;
|
|
25588
|
-
if (!repoRoot) return { success: false, error: "Source node repoRoot not found" };
|
|
27245
|
+
if (!repoRoot) return { success: false, error: "Source node repoRoot not found", refineStages };
|
|
25589
27246
|
const { execFile: execFile3 } = await import("child_process");
|
|
25590
27247
|
const { promisify: promisify3 } = await import("util");
|
|
25591
27248
|
const execFileAsync3 = promisify3(execFile3);
|
|
27249
|
+
const resolveStarted = Date.now();
|
|
25592
27250
|
const { stdout: branchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: node.workspace, encoding: "utf8" });
|
|
25593
27251
|
const branch = branchStdout.trim();
|
|
25594
|
-
if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
|
|
27252
|
+
if (!branch) return { success: false, error: "Could not determine branch of the worktree node", refineStages };
|
|
25595
27253
|
const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
25596
27254
|
const baseBranch = baseBranchStdout.trim();
|
|
27255
|
+
const { stdout: baseHeadStdout } = await execFileAsync3("git", ["rev-parse", "HEAD"], { cwd: repoRoot, encoding: "utf8" });
|
|
27256
|
+
const { stdout: branchHeadStdout } = await execFileAsync3("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
27257
|
+
const baseHead = baseHeadStdout.trim();
|
|
27258
|
+
const branchHead = branchHeadStdout.trim();
|
|
27259
|
+
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead });
|
|
27260
|
+
const validationStarted = Date.now();
|
|
25597
27261
|
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
|
|
27262
|
+
recordMeshRefineStage(
|
|
27263
|
+
refineStages,
|
|
27264
|
+
"validation",
|
|
27265
|
+
validationSummary.status === "passed" ? "passed" : validationSummary.status === "failed" ? "failed" : "skipped",
|
|
27266
|
+
validationStarted,
|
|
27267
|
+
{ validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length }
|
|
27268
|
+
);
|
|
25598
27269
|
if (validationSummary.status === "failed") {
|
|
25599
27270
|
return {
|
|
25600
27271
|
success: false,
|
|
@@ -25604,6 +27275,7 @@ var DaemonCommandRouter = class {
|
|
|
25604
27275
|
branch,
|
|
25605
27276
|
into: baseBranch,
|
|
25606
27277
|
validationSummary,
|
|
27278
|
+
refineStages,
|
|
25607
27279
|
finalBranchConvergenceState: {
|
|
25608
27280
|
branch,
|
|
25609
27281
|
baseBranch,
|
|
@@ -25623,6 +27295,7 @@ var DaemonCommandRouter = class {
|
|
|
25623
27295
|
branch,
|
|
25624
27296
|
into: baseBranch,
|
|
25625
27297
|
validationSummary,
|
|
27298
|
+
refineStages,
|
|
25626
27299
|
finalBranchConvergenceState: {
|
|
25627
27300
|
branch,
|
|
25628
27301
|
baseBranch,
|
|
@@ -25633,37 +27306,121 @@ var DaemonCommandRouter = class {
|
|
|
25633
27306
|
}
|
|
25634
27307
|
};
|
|
25635
27308
|
}
|
|
27309
|
+
const patchEquivalenceStarted = Date.now();
|
|
27310
|
+
const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
27311
|
+
recordMeshRefineStage(refineStages, "patch_equivalence", patchEquivalence.status, patchEquivalenceStarted, {
|
|
27312
|
+
equivalent: patchEquivalence.equivalent,
|
|
27313
|
+
expectedPatchId: patchEquivalence.expectedPatchId,
|
|
27314
|
+
actualPatchId: patchEquivalence.actualPatchId,
|
|
27315
|
+
error: patchEquivalence.error
|
|
27316
|
+
});
|
|
27317
|
+
if (!patchEquivalence.equivalent) {
|
|
27318
|
+
return {
|
|
27319
|
+
success: false,
|
|
27320
|
+
code: "patch_equivalence_failed",
|
|
27321
|
+
convergenceStatus: "blocked_review",
|
|
27322
|
+
error: "Refinery patch-equivalence preflight failed; merge/refine was not attempted.",
|
|
27323
|
+
branch,
|
|
27324
|
+
into: baseBranch,
|
|
27325
|
+
validationSummary,
|
|
27326
|
+
patchEquivalence,
|
|
27327
|
+
refineStages,
|
|
27328
|
+
finalBranchConvergenceState: {
|
|
27329
|
+
branch,
|
|
27330
|
+
baseBranch,
|
|
27331
|
+
merged: false,
|
|
27332
|
+
removed: false,
|
|
27333
|
+
validation: "passed",
|
|
27334
|
+
patchEquivalence: "failed",
|
|
27335
|
+
status: "blocked_review"
|
|
27336
|
+
}
|
|
27337
|
+
};
|
|
27338
|
+
}
|
|
27339
|
+
let mergeResult;
|
|
27340
|
+
const mergeStarted = Date.now();
|
|
25636
27341
|
try {
|
|
25637
|
-
await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
27342
|
+
const result = await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
27343
|
+
mergeResult = {
|
|
27344
|
+
stdout: truncateValidationOutput(result.stdout),
|
|
27345
|
+
stderr: truncateValidationOutput(result.stderr),
|
|
27346
|
+
durationMs: Date.now() - mergeStarted
|
|
27347
|
+
};
|
|
27348
|
+
recordMeshRefineStage(refineStages, "merge", "passed", mergeStarted, mergeResult);
|
|
25638
27349
|
} catch (e) {
|
|
27350
|
+
recordMeshRefineStage(refineStages, "merge", "failed", mergeStarted, {
|
|
27351
|
+
error: e?.message || String(e),
|
|
27352
|
+
stdout: truncateValidationOutput(e?.stdout),
|
|
27353
|
+
stderr: truncateValidationOutput(e?.stderr)
|
|
27354
|
+
});
|
|
25639
27355
|
return {
|
|
25640
27356
|
success: false,
|
|
25641
27357
|
error: `Merge failed (conflicts?): ${e.message}`,
|
|
25642
27358
|
validationSummary,
|
|
27359
|
+
patchEquivalence,
|
|
27360
|
+
refineStages,
|
|
25643
27361
|
finalBranchConvergenceState: {
|
|
25644
27362
|
branch,
|
|
25645
27363
|
baseBranch,
|
|
25646
27364
|
merged: false,
|
|
25647
27365
|
removed: false,
|
|
25648
27366
|
validation: "passed",
|
|
27367
|
+
patchEquivalence: "passed",
|
|
25649
27368
|
status: "not_mergeable"
|
|
25650
27369
|
}
|
|
25651
27370
|
};
|
|
25652
27371
|
}
|
|
27372
|
+
const cleanupStarted = Date.now();
|
|
25653
27373
|
const removeResult = await this.execute("remove_mesh_node", {
|
|
25654
27374
|
meshId,
|
|
25655
27375
|
nodeId,
|
|
25656
|
-
sessionCleanupMode: "
|
|
27376
|
+
sessionCleanupMode: "preserve",
|
|
25657
27377
|
inlineMesh: args?.inlineMesh
|
|
25658
27378
|
});
|
|
27379
|
+
recordMeshRefineStage(refineStages, "cleanup", removeResult?.success === false ? "failed" : "passed", cleanupStarted, {
|
|
27380
|
+
removed: removeResult?.removed,
|
|
27381
|
+
code: removeResult?.code,
|
|
27382
|
+
error: removeResult?.error
|
|
27383
|
+
});
|
|
27384
|
+
let ledgerError;
|
|
27385
|
+
const ledgerStarted = Date.now();
|
|
25659
27386
|
try {
|
|
25660
27387
|
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
25661
27388
|
appendLedgerEntry2(meshId, {
|
|
25662
27389
|
kind: "node_removed",
|
|
25663
27390
|
nodeId,
|
|
25664
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
|
|
27391
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence }
|
|
25665
27392
|
});
|
|
25666
|
-
|
|
27393
|
+
recordMeshRefineStage(refineStages, "ledger", "passed", ledgerStarted);
|
|
27394
|
+
} catch (e) {
|
|
27395
|
+
ledgerError = e?.message || String(e);
|
|
27396
|
+
recordMeshRefineStage(refineStages, "ledger", "failed", ledgerStarted, { error: ledgerError });
|
|
27397
|
+
}
|
|
27398
|
+
const finalBranchConvergenceState = {
|
|
27399
|
+
branch: baseBranch,
|
|
27400
|
+
mergedBranch: branch,
|
|
27401
|
+
baseBranch,
|
|
27402
|
+
merged: true,
|
|
27403
|
+
removed: removeResult?.success !== false,
|
|
27404
|
+
validation: "passed",
|
|
27405
|
+
patchEquivalence: "passed",
|
|
27406
|
+
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
27407
|
+
};
|
|
27408
|
+
if (removeResult?.success === false) {
|
|
27409
|
+
return {
|
|
27410
|
+
success: false,
|
|
27411
|
+
code: "cleanup_failed",
|
|
27412
|
+
error: "Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.",
|
|
27413
|
+
merged: true,
|
|
27414
|
+
branch,
|
|
27415
|
+
into: baseBranch,
|
|
27416
|
+
removeResult,
|
|
27417
|
+
validationSummary,
|
|
27418
|
+
patchEquivalence,
|
|
27419
|
+
mergeResult,
|
|
27420
|
+
refineStages,
|
|
27421
|
+
...ledgerError ? { ledgerError } : {},
|
|
27422
|
+
finalBranchConvergenceState
|
|
27423
|
+
};
|
|
25667
27424
|
}
|
|
25668
27425
|
return {
|
|
25669
27426
|
success: true,
|
|
@@ -25672,18 +27429,14 @@ var DaemonCommandRouter = class {
|
|
|
25672
27429
|
into: baseBranch,
|
|
25673
27430
|
removeResult,
|
|
25674
27431
|
validationSummary,
|
|
25675
|
-
|
|
25676
|
-
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
|
|
25680
|
-
removed: removeResult?.success !== false,
|
|
25681
|
-
validation: "passed",
|
|
25682
|
-
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
25683
|
-
}
|
|
27432
|
+
patchEquivalence,
|
|
27433
|
+
mergeResult,
|
|
27434
|
+
refineStages,
|
|
27435
|
+
...ledgerError ? { ledgerError } : {},
|
|
27436
|
+
finalBranchConvergenceState
|
|
25684
27437
|
};
|
|
25685
27438
|
} catch (e) {
|
|
25686
|
-
return { success: false, error: e.message };
|
|
27439
|
+
return { success: false, error: e.message, refineStages };
|
|
25687
27440
|
}
|
|
25688
27441
|
}
|
|
25689
27442
|
case "remove_mesh_node": {
|
|
@@ -25724,6 +27477,7 @@ var DaemonCommandRouter = class {
|
|
|
25724
27477
|
} else {
|
|
25725
27478
|
const { removeNode: removeNode3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
|
|
25726
27479
|
removed = removeNode3(meshId, nodeId);
|
|
27480
|
+
if (removed) this.invalidateAggregateMeshStatus(meshId);
|
|
25727
27481
|
}
|
|
25728
27482
|
if (removed) {
|
|
25729
27483
|
try {
|
|
@@ -25758,6 +27512,8 @@ var DaemonCommandRouter = class {
|
|
|
25758
27512
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
25759
27513
|
if (!sourceNodeId) return { success: false, error: "sourceNodeId required" };
|
|
25760
27514
|
if (!branch) return { success: false, error: "branch required" };
|
|
27515
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "worktree clone");
|
|
27516
|
+
if (ownerFailure) return ownerFailure;
|
|
25761
27517
|
try {
|
|
25762
27518
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh);
|
|
25763
27519
|
const mesh = meshRecord?.mesh;
|
|
@@ -25802,6 +27558,7 @@ var DaemonCommandRouter = class {
|
|
|
25802
27558
|
policy: { ...sourceNode.policy || {} }
|
|
25803
27559
|
});
|
|
25804
27560
|
if (!node) return { success: false, error: "Failed to register worktree node" };
|
|
27561
|
+
this.invalidateAggregateMeshStatus(meshId);
|
|
25805
27562
|
}
|
|
25806
27563
|
const initSubmodules = sourceNode.policy?.initSubmodulesOnClone !== false;
|
|
25807
27564
|
if (initSubmodules) {
|
|
@@ -25838,6 +27595,8 @@ var DaemonCommandRouter = class {
|
|
|
25838
27595
|
case "trigger_mesh_queue": {
|
|
25839
27596
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
25840
27597
|
if (!meshId) return { success: false, error: "meshId required" };
|
|
27598
|
+
const ownerFailure = await this.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue trigger");
|
|
27599
|
+
if (ownerFailure) return ownerFailure;
|
|
25841
27600
|
try {
|
|
25842
27601
|
const { triggerMeshQueue: triggerMeshQueue2 } = await Promise.resolve().then(() => (init_mesh_events(), mesh_events_exports));
|
|
25843
27602
|
if (meshId) {
|
|
@@ -25864,6 +27623,15 @@ var DaemonCommandRouter = class {
|
|
|
25864
27623
|
mesh = getMesh3(meshId);
|
|
25865
27624
|
}
|
|
25866
27625
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
27626
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
27627
|
+
if (!meshHost.canOwnCoordinator) {
|
|
27628
|
+
return {
|
|
27629
|
+
success: false,
|
|
27630
|
+
...buildMeshHostRequiredFailure(mesh, "coordinator launch"),
|
|
27631
|
+
meshId,
|
|
27632
|
+
cliType
|
|
27633
|
+
};
|
|
27634
|
+
}
|
|
25867
27635
|
if (!Array.isArray(mesh.nodes) || mesh.nodes.length === 0) return { success: false, error: "No nodes in mesh" };
|
|
25868
27636
|
const requestedCoordinatorNodeId = typeof args?.coordinatorNodeId === "string" ? args.coordinatorNodeId.trim() : "";
|
|
25869
27637
|
const preferredCoordinatorNodeId = requestedCoordinatorNodeId || (typeof mesh.coordinator?.preferredNodeId === "string" ? mesh.coordinator.preferredNodeId.trim() : "");
|
|
@@ -25877,7 +27645,14 @@ var DaemonCommandRouter = class {
|
|
|
25877
27645
|
cliType
|
|
25878
27646
|
};
|
|
25879
27647
|
}
|
|
25880
|
-
const
|
|
27648
|
+
const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
|
|
27649
|
+
const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
|
|
27650
|
+
const workspace = readLiveMeshNodeWorkspace({
|
|
27651
|
+
meshId,
|
|
27652
|
+
nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ""),
|
|
27653
|
+
liveSessionRecords: liveMeshSessions,
|
|
27654
|
+
allowCoordinatorSession: true
|
|
27655
|
+
}) || (typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "");
|
|
25881
27656
|
if (!workspace) return { success: false, error: "Coordinator node workspace required", meshId, cliType };
|
|
25882
27657
|
if (!cliType) {
|
|
25883
27658
|
const resolved = await resolveProviderTypeFromPriority({
|
|
@@ -26039,7 +27814,7 @@ ${block}`);
|
|
|
26039
27814
|
workspace
|
|
26040
27815
|
};
|
|
26041
27816
|
}
|
|
26042
|
-
const { existsSync:
|
|
27817
|
+
const { existsSync: existsSync27, readFileSync: readFileSync19, writeFileSync: writeFileSync15, copyFileSync: copyFileSync4, mkdirSync: mkdirSync17 } = await import("fs");
|
|
26043
27818
|
const { dirname: dirname9 } = await import("path");
|
|
26044
27819
|
const mcpConfigPath = coordinatorSetup.configPath;
|
|
26045
27820
|
const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
|
|
@@ -26082,14 +27857,14 @@ ${block}`);
|
|
|
26082
27857
|
if (hermesManualFallback) return returnManualFallback(message);
|
|
26083
27858
|
return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
|
|
26084
27859
|
}
|
|
26085
|
-
const hadExistingMcpConfig =
|
|
27860
|
+
const hadExistingMcpConfig = existsSync27(mcpConfigPath);
|
|
26086
27861
|
let existingMcpConfig = hermesBaseConfig?.config || {};
|
|
26087
27862
|
if (hermesBaseConfig) {
|
|
26088
27863
|
copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname9(mcpConfigPath));
|
|
26089
27864
|
}
|
|
26090
27865
|
if (hadExistingMcpConfig) {
|
|
26091
27866
|
try {
|
|
26092
|
-
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(
|
|
27867
|
+
const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync19(mcpConfigPath, "utf-8"), configFormat);
|
|
26093
27868
|
const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
|
|
26094
27869
|
existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
|
|
26095
27870
|
copyFileSync4(mcpConfigPath, mcpConfigPath + ".backup");
|
|
@@ -26179,62 +27954,321 @@ ${block}`);
|
|
|
26179
27954
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
26180
27955
|
const mesh = meshRecord?.mesh;
|
|
26181
27956
|
if (!mesh) return { success: false, error: "Mesh not found" };
|
|
27957
|
+
const meshHost = resolveMeshHostStatus(mesh);
|
|
27958
|
+
const refreshRequested = args?.refresh === true || args?.forceRefresh === true;
|
|
27959
|
+
const hadAggregateCache = this.aggregateMeshStatusCache.has(meshId);
|
|
27960
|
+
if (!refreshRequested) {
|
|
27961
|
+
const cachedStatus = this.getCachedAggregateMeshStatus(meshId, mesh, { requireDirectPeerTruth: args?.requireDirectPeerTruth === true });
|
|
27962
|
+
if (cachedStatus) {
|
|
27963
|
+
logRepoMeshStatusDebug("return_cached", {
|
|
27964
|
+
meshId,
|
|
27965
|
+
command: "mesh_status",
|
|
27966
|
+
refreshRequested,
|
|
27967
|
+
summary: summarizeRepoMeshStatusDebug(cachedStatus)
|
|
27968
|
+
});
|
|
27969
|
+
return cachedStatus;
|
|
27970
|
+
}
|
|
27971
|
+
}
|
|
27972
|
+
const refreshReason = refreshRequested ? "explicit_refresh" : hadAggregateCache ? "stale_pending_cache_refresh" : "cold_cache_miss";
|
|
26182
27973
|
const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
26183
27974
|
const queue = getQueue2(meshId);
|
|
26184
27975
|
const queueSummary = getMeshQueueStats2(meshId);
|
|
26185
27976
|
const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
26186
27977
|
const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
|
|
26187
27978
|
const ledgerSummary = getLedgerSummary2(meshId);
|
|
27979
|
+
const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
|
|
27980
|
+
const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
|
|
27981
|
+
const localMachineId = loadConfig().machineId || "";
|
|
27982
|
+
const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
|
|
27983
|
+
const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
|
|
27984
|
+
mesh,
|
|
27985
|
+
meshSource: meshRecord.source,
|
|
27986
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
27987
|
+
statusInstanceId: this.deps.statusInstanceId,
|
|
27988
|
+
localMachineId
|
|
27989
|
+
}) : {
|
|
27990
|
+
directEvidenceCount: 0,
|
|
27991
|
+
localConfirmedCount: 0,
|
|
27992
|
+
peerAttemptedCount: 0,
|
|
27993
|
+
peerConfirmedCount: 0,
|
|
27994
|
+
unavailableNodeIds: []
|
|
27995
|
+
};
|
|
27996
|
+
const passivePeerTruthNotAttempted = requireDirectPeerTruth && !refreshRequested && directTruth.directEvidenceCount > 0 && directTruth.peerAttemptedCount === 0;
|
|
27997
|
+
const effectiveDirectTruth = passivePeerTruthNotAttempted ? { ...directTruth, unavailableNodeIds: [] } : directTruth;
|
|
27998
|
+
const directTruthSatisfied = !requireDirectPeerTruth || effectiveDirectTruth.directEvidenceCount > 0 && effectiveDirectTruth.unavailableNodeIds.length === 0;
|
|
27999
|
+
if (requireDirectPeerTruth && !directTruthSatisfied) {
|
|
28000
|
+
const failureResult = {
|
|
28001
|
+
success: false,
|
|
28002
|
+
code: "mesh_direct_peer_truth_unavailable",
|
|
28003
|
+
error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct mesh_status probes succeed.",
|
|
28004
|
+
sourceOfTruth: {
|
|
28005
|
+
membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
28006
|
+
coordinatorOwnsLiveTruth: false,
|
|
28007
|
+
currentStatus: "direct_peer_truth_unavailable",
|
|
28008
|
+
directPeerTruth: {
|
|
28009
|
+
required: true,
|
|
28010
|
+
satisfied: false,
|
|
28011
|
+
directEvidenceCount: directTruth.directEvidenceCount,
|
|
28012
|
+
localConfirmedCount: directTruth.localConfirmedCount,
|
|
28013
|
+
peerAttemptedCount: directTruth.peerAttemptedCount,
|
|
28014
|
+
peerConfirmedCount: directTruth.peerConfirmedCount,
|
|
28015
|
+
unavailableNodeIds: directTruth.unavailableNodeIds
|
|
28016
|
+
}
|
|
28017
|
+
}
|
|
28018
|
+
};
|
|
28019
|
+
logRepoMeshStatusDebug("direct_truth_unavailable", {
|
|
28020
|
+
meshId,
|
|
28021
|
+
command: "mesh_status",
|
|
28022
|
+
refreshRequested,
|
|
28023
|
+
meshSource: meshRecord.source,
|
|
28024
|
+
directTruth
|
|
28025
|
+
});
|
|
28026
|
+
return failureResult;
|
|
28027
|
+
}
|
|
28028
|
+
const directTruthUnavailableNodeIds = new Set(effectiveDirectTruth.unavailableNodeIds);
|
|
28029
|
+
const selectedCoordinatorNodeId = readStringValue(
|
|
28030
|
+
mesh.coordinator?.preferredNodeId,
|
|
28031
|
+
mesh.nodes?.[0]?.id,
|
|
28032
|
+
mesh.nodes?.[0]?.nodeId
|
|
28033
|
+
);
|
|
28034
|
+
const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? selectedCoordinatorNodeId : void 0;
|
|
28035
|
+
const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26188
28036
|
const nodeStatuses = [];
|
|
26189
|
-
for (const node of mesh.nodes || []) {
|
|
28037
|
+
for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
|
|
28038
|
+
const nodeId = String(node.id || node.nodeId || "");
|
|
28039
|
+
const daemonId = readStringValue(node.daemonId);
|
|
28040
|
+
const providerPriority = readProviderPriorityFromPolicy(node.policy);
|
|
28041
|
+
const isSelfNode = Boolean(
|
|
28042
|
+
nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
|
|
28043
|
+
) || Boolean(
|
|
28044
|
+
daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
|
|
28045
|
+
) || Boolean(meshRecord?.inline && nodeIndex === 0);
|
|
26190
28046
|
const status = {
|
|
26191
|
-
nodeId
|
|
28047
|
+
nodeId,
|
|
26192
28048
|
machineLabel: node.machineLabel || node.id || node.nodeId,
|
|
26193
28049
|
workspace: node.workspace,
|
|
26194
28050
|
repoRoot: node.repoRoot,
|
|
26195
28051
|
isLocalWorktree: node.isLocalWorktree,
|
|
26196
28052
|
worktreeBranch: node.worktreeBranch,
|
|
26197
|
-
|
|
28053
|
+
role: normalizeMeshDaemonRole(node.role) || (meshHost.hostNodeId && nodeId === meshHost.hostNodeId ? "host" : void 0),
|
|
28054
|
+
daemonId,
|
|
26198
28055
|
machineId: node.machineId,
|
|
28056
|
+
machineStatus: node.machineStatus,
|
|
26199
28057
|
health: "unknown",
|
|
26200
28058
|
providers: node.providers || [],
|
|
26201
|
-
|
|
28059
|
+
providerPriority,
|
|
28060
|
+
activeSessions: [],
|
|
28061
|
+
activeSessionDetails: [],
|
|
28062
|
+
launchReady: false
|
|
26202
28063
|
};
|
|
26203
|
-
if (
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
|
|
28064
|
+
if (isSelfNode) {
|
|
28065
|
+
status.connection = {
|
|
28066
|
+
perspective: "selected_coordinator",
|
|
28067
|
+
source: "mesh_peer_status",
|
|
28068
|
+
state: "self",
|
|
28069
|
+
transport: "local",
|
|
28070
|
+
reported: true,
|
|
28071
|
+
reason: "Selected coordinator daemon",
|
|
28072
|
+
lastStateChangeAt: refreshedAt
|
|
28073
|
+
};
|
|
28074
|
+
} else if (daemonId) {
|
|
28075
|
+
const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
|
|
28076
|
+
status.connection = connection ?? {
|
|
28077
|
+
perspective: "selected_coordinator",
|
|
28078
|
+
source: "not_reported",
|
|
28079
|
+
state: "unknown",
|
|
28080
|
+
transport: "unknown",
|
|
28081
|
+
reported: false,
|
|
28082
|
+
reason: "No live mesh peer telemetry reported by the selected coordinator yet."
|
|
28083
|
+
};
|
|
28084
|
+
} else {
|
|
28085
|
+
status.connection = {
|
|
28086
|
+
perspective: "selected_coordinator",
|
|
28087
|
+
source: "not_reported",
|
|
28088
|
+
state: "unknown",
|
|
28089
|
+
transport: "unknown",
|
|
28090
|
+
reported: false,
|
|
28091
|
+
reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
|
|
28092
|
+
};
|
|
28093
|
+
}
|
|
28094
|
+
const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
|
|
28095
|
+
meshId,
|
|
28096
|
+
node,
|
|
28097
|
+
nodeId,
|
|
28098
|
+
liveSessionRecords: liveMeshSessions,
|
|
28099
|
+
allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
|
|
28100
|
+
});
|
|
28101
|
+
const workspace = readLiveMeshNodeWorkspace({
|
|
28102
|
+
meshId,
|
|
28103
|
+
nodeId,
|
|
28104
|
+
liveSessionRecords: matchedLiveSessionRecords,
|
|
28105
|
+
allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
|
|
28106
|
+
}) || (typeof node.workspace === "string" ? node.workspace : "");
|
|
28107
|
+
status.workspace = workspace || node.workspace;
|
|
28108
|
+
if (matchedLiveSessionRecords.length > 0) {
|
|
28109
|
+
const sessionIds = matchedLiveSessionRecords.map((record) => typeof record?.sessionId === "string" ? record.sessionId : "").filter(Boolean);
|
|
28110
|
+
const providerTypes = matchedLiveSessionRecords.map((record) => readStringValue(record?.providerType)).filter(Boolean);
|
|
28111
|
+
status.activeSessions = sessionIds;
|
|
28112
|
+
status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
|
|
28113
|
+
if (providerTypes.length > 0) {
|
|
28114
|
+
status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
|
|
26207
28115
|
}
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
26211
|
-
|
|
26212
|
-
|
|
26213
|
-
|
|
26214
|
-
|
|
26215
|
-
status.health = "degraded";
|
|
26216
|
-
|
|
28116
|
+
}
|
|
28117
|
+
if (workspace) {
|
|
28118
|
+
if (!fs10.existsSync(workspace)) {
|
|
28119
|
+
const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
|
|
28120
|
+
let remoteProbeApplied = false;
|
|
28121
|
+
if (inlineTransitGit) {
|
|
28122
|
+
status.git = inlineTransitGit;
|
|
28123
|
+
status.health = inlineTransitGit.isGitRepo ? deriveMeshNodeHealthFromGit(inlineTransitGit) : "degraded";
|
|
28124
|
+
const connection = readObjectRecord(status.connection);
|
|
28125
|
+
const connectionState = readStringValue(connection.state);
|
|
28126
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
28127
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
28128
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
28129
|
+
}
|
|
28130
|
+
remoteProbeApplied = true;
|
|
28131
|
+
} else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
|
|
28132
|
+
try {
|
|
28133
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
28134
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
28135
|
+
daemonId,
|
|
28136
|
+
workspace,
|
|
28137
|
+
timeoutMs: 8e3
|
|
28138
|
+
});
|
|
28139
|
+
if (remoteGit) {
|
|
28140
|
+
status.git = remoteGit;
|
|
28141
|
+
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|
|
28142
|
+
const connection = readObjectRecord(status.connection);
|
|
28143
|
+
const connectionState = readStringValue(connection.state);
|
|
28144
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
28145
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
28146
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
28147
|
+
}
|
|
28148
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
28149
|
+
remoteProbeApplied = true;
|
|
28150
|
+
}
|
|
28151
|
+
} catch {
|
|
28152
|
+
const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
|
|
28153
|
+
const refreshedConnectionState = readStringValue(refreshedConnection?.state);
|
|
28154
|
+
if (refreshedConnection && refreshedConnectionState === "connected") {
|
|
28155
|
+
status.connection = refreshedConnection;
|
|
28156
|
+
try {
|
|
28157
|
+
const remoteGit = await probeRemoteMeshGitStatus({
|
|
28158
|
+
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
28159
|
+
daemonId,
|
|
28160
|
+
workspace,
|
|
28161
|
+
timeoutMs: 12e3
|
|
28162
|
+
});
|
|
28163
|
+
if (remoteGit) {
|
|
28164
|
+
status.git = remoteGit;
|
|
28165
|
+
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|
|
28166
|
+
const connection = readObjectRecord(status.connection);
|
|
28167
|
+
const connectionState = readStringValue(connection.state);
|
|
28168
|
+
const connectionReported = readBooleanValue(connection.reported) ?? false;
|
|
28169
|
+
if (!connectionReported || connectionState === "unknown") {
|
|
28170
|
+
status.connection = buildLivePeerGitConnection(connection, refreshedAt);
|
|
28171
|
+
}
|
|
28172
|
+
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
28173
|
+
remoteProbeApplied = true;
|
|
28174
|
+
}
|
|
28175
|
+
} catch {
|
|
28176
|
+
}
|
|
28177
|
+
}
|
|
28178
|
+
}
|
|
26217
28179
|
}
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
status.health
|
|
28180
|
+
if (!remoteProbeApplied) {
|
|
28181
|
+
const connectionState = readStringValue(status.connection?.state);
|
|
28182
|
+
const pendingPeerGitProbe = !inlineTransitGit && !isSelfNode && !!daemonId && (readStringValue(status.machineStatus) === "online" || readStringValue(status.health) === "online" || connectionState === "connecting" || connectionState === "connected" || connectionState === "unknown");
|
|
28183
|
+
if (pendingPeerGitProbe) {
|
|
28184
|
+
status.gitProbePending = true;
|
|
28185
|
+
status.health = "unknown";
|
|
28186
|
+
}
|
|
28187
|
+
if (applyCachedInlineMeshNodeStatus(
|
|
28188
|
+
status,
|
|
28189
|
+
node,
|
|
28190
|
+
pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
|
|
28191
|
+
)) {
|
|
28192
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
28193
|
+
nodeStatuses.push(status);
|
|
28194
|
+
continue;
|
|
28195
|
+
}
|
|
28196
|
+
if (meshRecord?.source === "inline_cache" && !isSelfNode) {
|
|
28197
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
28198
|
+
nodeStatuses.push(status);
|
|
28199
|
+
continue;
|
|
28200
|
+
}
|
|
28201
|
+
}
|
|
28202
|
+
} else {
|
|
28203
|
+
try {
|
|
28204
|
+
const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
|
|
28205
|
+
status.git = gitStatus;
|
|
28206
|
+
recordInlineMeshDirectGitTruth(node, gitStatus, "selected_coordinator_local_git");
|
|
28207
|
+
if (gitStatus.isGitRepo) {
|
|
28208
|
+
status.health = deriveMeshNodeHealthFromGit(gitStatus);
|
|
28209
|
+
} else {
|
|
28210
|
+
status.health = "degraded";
|
|
28211
|
+
if (gitStatus.error && !status.error) status.error = gitStatus.error;
|
|
28212
|
+
}
|
|
28213
|
+
} catch {
|
|
28214
|
+
if (!applyCachedInlineMeshNodeStatus(status, node)) {
|
|
28215
|
+
status.health = "degraded";
|
|
28216
|
+
}
|
|
26221
28217
|
}
|
|
26222
28218
|
}
|
|
26223
28219
|
} else {
|
|
26224
28220
|
applyCachedInlineMeshNodeStatus(status, node);
|
|
26225
28221
|
}
|
|
28222
|
+
finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
|
|
26226
28223
|
nodeStatuses.push(status);
|
|
26227
28224
|
}
|
|
26228
|
-
|
|
28225
|
+
const statusResult = {
|
|
26229
28226
|
success: true,
|
|
26230
28227
|
meshId: mesh.id,
|
|
26231
28228
|
meshName: mesh.name,
|
|
26232
28229
|
repoIdentity: mesh.repoIdentity,
|
|
26233
28230
|
defaultBranch: mesh.defaultBranch,
|
|
28231
|
+
refreshedAt,
|
|
28232
|
+
meshHost,
|
|
28233
|
+
sourceOfTruth: {
|
|
28234
|
+
membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
|
|
28235
|
+
coordinatorOwnsLiveTruth: directTruthSatisfied,
|
|
28236
|
+
meshHost: {
|
|
28237
|
+
owner: "mesh_host_daemon",
|
|
28238
|
+
localRole: meshHost.role,
|
|
28239
|
+
hostDaemonId: meshHost.hostDaemonId,
|
|
28240
|
+
hostNodeId: meshHost.hostNodeId,
|
|
28241
|
+
hostAddress: meshHost.hostAddress
|
|
28242
|
+
},
|
|
28243
|
+
...requireDirectPeerTruth ? {
|
|
28244
|
+
currentStatus: directTruthSatisfied ? "live_git_and_session_probes" : "direct_peer_truth_unavailable",
|
|
28245
|
+
directPeerTruth: {
|
|
28246
|
+
required: true,
|
|
28247
|
+
satisfied: directTruthSatisfied,
|
|
28248
|
+
directEvidenceCount: effectiveDirectTruth.directEvidenceCount,
|
|
28249
|
+
localConfirmedCount: effectiveDirectTruth.localConfirmedCount,
|
|
28250
|
+
peerAttemptedCount: effectiveDirectTruth.peerAttemptedCount,
|
|
28251
|
+
peerConfirmedCount: effectiveDirectTruth.peerConfirmedCount,
|
|
28252
|
+
unavailableNodeIds: effectiveDirectTruth.unavailableNodeIds
|
|
28253
|
+
}
|
|
28254
|
+
} : {},
|
|
28255
|
+
historicalEvidenceOnly: ["recoveryHints", "ledger.summary", "queue.summary"]
|
|
28256
|
+
},
|
|
26234
28257
|
nodes: nodeStatuses,
|
|
26235
28258
|
queue: { tasks: queue, summary: queueSummary },
|
|
26236
28259
|
ledger: { entries: ledgerEntries, summary: ledgerSummary }
|
|
26237
28260
|
};
|
|
28261
|
+
const rememberedStatus = this.rememberAggregateMeshStatus(meshId, statusResult, refreshReason);
|
|
28262
|
+
logRepoMeshStatusDebug("return_live", {
|
|
28263
|
+
meshId,
|
|
28264
|
+
command: "mesh_status",
|
|
28265
|
+
refreshRequested,
|
|
28266
|
+
refreshReason,
|
|
28267
|
+
meshSource: meshRecord.source,
|
|
28268
|
+
directTruth,
|
|
28269
|
+
summary: summarizeRepoMeshStatusDebug(rememberedStatus)
|
|
28270
|
+
});
|
|
28271
|
+
return rememberedStatus;
|
|
26238
28272
|
} catch (e) {
|
|
26239
28273
|
return { success: false, error: e.message };
|
|
26240
28274
|
}
|
|
@@ -34160,6 +36194,8 @@ async function initDaemonComponents(config) {
|
|
|
34160
36194
|
sessionHostControl: config.sessionHostControl,
|
|
34161
36195
|
statusInstanceId: config.statusInstanceId,
|
|
34162
36196
|
statusVersion: config.statusVersion,
|
|
36197
|
+
getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
|
|
36198
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
34163
36199
|
getCdpLogFn: config.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
|
|
34164
36200
|
});
|
|
34165
36201
|
poller = new AgentStreamPoller({
|
|
@@ -34288,6 +36324,8 @@ async function shutdownDaemonComponents(components) {
|
|
|
34288
36324
|
InMemoryGitSnapshotStore,
|
|
34289
36325
|
LOG,
|
|
34290
36326
|
MAX_LEDGER_SLICE_LIMIT,
|
|
36327
|
+
MESH_REFINE_CONFIG_LOCATIONS,
|
|
36328
|
+
MESH_REFINE_CONFIG_SCHEMA,
|
|
34291
36329
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
34292
36330
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
34293
36331
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
@@ -34310,6 +36348,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34310
36348
|
buildChatTailDeliverySignature,
|
|
34311
36349
|
buildCoordinatorSystemPrompt,
|
|
34312
36350
|
buildMachineInfo,
|
|
36351
|
+
buildMeshHostRequiredFailure,
|
|
34313
36352
|
buildMeshLedgerReconciliationEvidence,
|
|
34314
36353
|
buildMeshLedgerReplicaEvidence,
|
|
34315
36354
|
buildP2pRelayFailurePayload,
|
|
@@ -34335,6 +36374,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34335
36374
|
connectCdpManager,
|
|
34336
36375
|
createDebugTraceStore,
|
|
34337
36376
|
createDefaultGitCommandServices,
|
|
36377
|
+
createDefaultMeshHostMetadata,
|
|
34338
36378
|
createGitCompactSummary,
|
|
34339
36379
|
createGitSnapshotStore,
|
|
34340
36380
|
createGitWorkspaceMonitor,
|
|
@@ -34398,6 +36438,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34398
36438
|
isInternalChatMessage,
|
|
34399
36439
|
isManagedStatusWaiting,
|
|
34400
36440
|
isManagedStatusWorking,
|
|
36441
|
+
isMeshHostOwner,
|
|
34401
36442
|
isP2pRelayTransportFailure,
|
|
34402
36443
|
isPathInside,
|
|
34403
36444
|
isSessionHostLiveRuntime,
|
|
@@ -34411,6 +36452,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34411
36452
|
listMeshes,
|
|
34412
36453
|
listWorktrees,
|
|
34413
36454
|
loadConfig,
|
|
36455
|
+
loadMeshRefineConfig,
|
|
34414
36456
|
loadState,
|
|
34415
36457
|
logCommand,
|
|
34416
36458
|
markSetupComplete,
|
|
@@ -34424,6 +36466,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34424
36466
|
normalizeGitWorkspaceSubscriptionParams,
|
|
34425
36467
|
normalizeInputEnvelope,
|
|
34426
36468
|
normalizeManagedStatus,
|
|
36469
|
+
normalizeMeshDaemonRole,
|
|
34427
36470
|
normalizeMessageParts,
|
|
34428
36471
|
normalizeRepoIdentity,
|
|
34429
36472
|
normalizeSessionModalFields,
|
|
@@ -34435,6 +36478,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34435
36478
|
prepareSessionChatTailUpdate,
|
|
34436
36479
|
prepareSessionModalUpdate,
|
|
34437
36480
|
probeCdpPort,
|
|
36481
|
+
queuePendingMeshCoordinatorEvent,
|
|
34438
36482
|
readChatHistory,
|
|
34439
36483
|
readLedgerEntries,
|
|
34440
36484
|
readLedgerSlice,
|
|
@@ -34443,6 +36487,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34443
36487
|
removeNode,
|
|
34444
36488
|
removeWorktree,
|
|
34445
36489
|
requeueTask,
|
|
36490
|
+
requireMeshHostQueueOwner,
|
|
34446
36491
|
resetConfig,
|
|
34447
36492
|
resetDebugRuntimeConfig,
|
|
34448
36493
|
resetState,
|
|
@@ -34450,6 +36495,8 @@ async function shutdownDaemonComponents(components) {
|
|
|
34450
36495
|
resolveCurrentGlobalInstallSurface,
|
|
34451
36496
|
resolveDebugRuntimeConfig,
|
|
34452
36497
|
resolveGitRepository,
|
|
36498
|
+
resolveMeshHostStatus,
|
|
36499
|
+
resolveMeshRefineValidationPlan,
|
|
34453
36500
|
resolveSessionHostAppName,
|
|
34454
36501
|
resolveSessionHostAppNameResolution,
|
|
34455
36502
|
resolveWorktreePath,
|
|
@@ -34465,6 +36512,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34465
36512
|
shutdownDaemonComponents,
|
|
34466
36513
|
spawnDetachedDaemonUpgradeHelper,
|
|
34467
36514
|
startDaemonDevSupport,
|
|
36515
|
+
suggestMeshRefineConfig,
|
|
34468
36516
|
summarizeGitStatus,
|
|
34469
36517
|
syncMeshes,
|
|
34470
36518
|
triggerMeshQueue,
|
|
@@ -34473,6 +36521,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
34473
36521
|
updateNode,
|
|
34474
36522
|
updateSessionTaskStatus,
|
|
34475
36523
|
updateTaskStatus,
|
|
34476
|
-
upsertSavedProviderSession
|
|
36524
|
+
upsertSavedProviderSession,
|
|
36525
|
+
validateMeshRefineConfig
|
|
34477
36526
|
});
|
|
34478
36527
|
//# sourceMappingURL=index.js.map
|