@adhdev/daemon-core 0.9.77-rc.44 → 0.9.77-rc.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/router.d.ts +1 -0
- package/dist/git/git-worktree.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +946 -181
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +942 -182
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-work-queue.d.ts +13 -0
- package/dist/mesh/p2p-relay-failure.d.ts +35 -0
- package/dist/shared-types.d.ts +6 -0
- package/package.json +1 -1
- package/src/commands/router.ts +446 -11
- package/src/config/config.ts +2 -1
- package/src/config/workspaces.ts +1 -1
- package/src/git/git-worktree.ts +35 -1
- package/src/index.d.ts +3 -0
- package/src/index.ts +14 -0
- package/src/mesh/mesh-events.ts +292 -5
- package/src/mesh/mesh-ledger.ts +1 -0
- package/src/mesh/mesh-work-queue.ts +43 -5
- package/src/mesh/p2p-relay-failure.ts +152 -0
- package/src/shared-types.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -123,7 +123,33 @@ async function removeWorktree(repoRoot, worktreePath, opts = {}) {
|
|
|
123
123
|
});
|
|
124
124
|
} catch (error) {
|
|
125
125
|
const stderr = typeof error.stderr === "string" ? error.stderr : "";
|
|
126
|
-
|
|
126
|
+
const stdout = typeof error.stdout === "string" ? error.stdout : "";
|
|
127
|
+
const detail = `${stderr}
|
|
128
|
+
${stdout}
|
|
129
|
+
${error.message || ""}`;
|
|
130
|
+
if (opts.allowSubmoduleForceFallback && SUBMODULE_WORKTREE_REMOVE_RE.test(detail)) {
|
|
131
|
+
try {
|
|
132
|
+
await execFileAsync2("git", ["worktree", "remove", "--force", worktreePath], {
|
|
133
|
+
cwd: repoRoot,
|
|
134
|
+
encoding: "utf8",
|
|
135
|
+
timeout: GIT_TIMEOUT_MS,
|
|
136
|
+
maxBuffer: GIT_MAX_BUFFER,
|
|
137
|
+
windowsHide: true
|
|
138
|
+
});
|
|
139
|
+
} catch (forceError) {
|
|
140
|
+
const forceStderr = typeof forceError.stderr === "string" ? forceError.stderr : "";
|
|
141
|
+
const forceStdout = typeof forceError.stdout === "string" ? forceError.stdout : "";
|
|
142
|
+
throw new Error(`git worktree remove --force fallback failed: ${forceStderr.trim() || forceStdout.trim() || forceError.message}`);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
success: true,
|
|
146
|
+
removedPath: worktreePath,
|
|
147
|
+
fallback: "git_worktree_remove_force_submodule",
|
|
148
|
+
forced: true,
|
|
149
|
+
reason: "working_trees_containing_submodules"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`git worktree remove failed: ${stderr.trim() || stdout.trim() || error.message}`);
|
|
127
153
|
}
|
|
128
154
|
return { success: true, removedPath: worktreePath };
|
|
129
155
|
}
|
|
@@ -173,7 +199,7 @@ async function pruneWorktrees(repoRoot) {
|
|
|
173
199
|
} catch {
|
|
174
200
|
}
|
|
175
201
|
}
|
|
176
|
-
var path4, import_promises3, import_node_fs2, import_node_child_process2, import_node_util2, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER;
|
|
202
|
+
var path4, import_promises3, import_node_fs2, import_node_child_process2, import_node_util2, execFileAsync2, WORKTREE_DIR_NAME, GIT_TIMEOUT_MS, GIT_MAX_BUFFER, SUBMODULE_WORKTREE_REMOVE_RE;
|
|
177
203
|
var init_git_worktree = __esm({
|
|
178
204
|
"src/git/git-worktree.ts"() {
|
|
179
205
|
"use strict";
|
|
@@ -186,6 +212,7 @@ var init_git_worktree = __esm({
|
|
|
186
212
|
WORKTREE_DIR_NAME = ".adhdev-worktrees";
|
|
187
213
|
GIT_TIMEOUT_MS = 3e4;
|
|
188
214
|
GIT_MAX_BUFFER = 4 * 1024 * 1024;
|
|
215
|
+
SUBMODULE_WORKTREE_REMOVE_RE = /working trees containing submodules cannot be moved or removed/i;
|
|
189
216
|
}
|
|
190
217
|
});
|
|
191
218
|
|
|
@@ -295,7 +322,8 @@ function ensureMachineId(config) {
|
|
|
295
322
|
};
|
|
296
323
|
}
|
|
297
324
|
function getConfigDir() {
|
|
298
|
-
const
|
|
325
|
+
const override = process.env.ADHDEV_CONFIG_DIR;
|
|
326
|
+
const dir = override && override.trim() ? override.trim() : (0, import_path.join)((0, import_os.homedir)(), ".adhdev");
|
|
299
327
|
if (!(0, import_fs.existsSync)(dir)) {
|
|
300
328
|
(0, import_fs.mkdirSync)(dir, { recursive: true });
|
|
301
329
|
}
|
|
@@ -1009,6 +1037,7 @@ __export(mesh_work_queue_exports, {
|
|
|
1009
1037
|
enqueueTask: () => enqueueTask,
|
|
1010
1038
|
getMeshQueueStats: () => getMeshQueueStats,
|
|
1011
1039
|
getQueue: () => getQueue,
|
|
1040
|
+
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
1012
1041
|
requeueTask: () => requeueTask,
|
|
1013
1042
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
1014
1043
|
updateTaskStatus: () => updateTaskStatus
|
|
@@ -1084,6 +1113,19 @@ function updateTaskStatus(meshId, taskId, status) {
|
|
|
1084
1113
|
writeQueue(meshId, queue);
|
|
1085
1114
|
return queue[idx];
|
|
1086
1115
|
}
|
|
1116
|
+
function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
|
|
1117
|
+
const queue = readQueue(meshId);
|
|
1118
|
+
const idx = queue.findIndex((q) => q.id === taskId);
|
|
1119
|
+
if (idx === -1) return null;
|
|
1120
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1121
|
+
queue[idx].autoLaunch = {
|
|
1122
|
+
...autoLaunch,
|
|
1123
|
+
updatedAt: now
|
|
1124
|
+
};
|
|
1125
|
+
queue[idx].updatedAt = now;
|
|
1126
|
+
writeQueue(meshId, queue);
|
|
1127
|
+
return queue[idx];
|
|
1128
|
+
}
|
|
1087
1129
|
function cancelTask(meshId, taskId, opts) {
|
|
1088
1130
|
const queue = readQueue(meshId);
|
|
1089
1131
|
const idx = queue.findIndex((q) => q.id === taskId);
|
|
@@ -1132,12 +1174,20 @@ function updateSessionTaskStatus(meshId, sessionId, status) {
|
|
|
1132
1174
|
}
|
|
1133
1175
|
function getMeshQueueStats(meshId) {
|
|
1134
1176
|
const queue = readQueue(meshId);
|
|
1177
|
+
const pending = queue.filter((q) => q.status === "pending").length;
|
|
1178
|
+
const assigned = queue.filter((q) => q.status === "assigned").length;
|
|
1179
|
+
const completed = queue.filter((q) => q.status === "completed").length;
|
|
1180
|
+
const failed = queue.filter((q) => q.status === "failed").length;
|
|
1181
|
+
const cancelled = queue.filter((q) => q.status === "cancelled").length;
|
|
1135
1182
|
return {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1183
|
+
total: queue.length,
|
|
1184
|
+
active: pending + assigned,
|
|
1185
|
+
historical: completed + failed + cancelled,
|
|
1186
|
+
pending,
|
|
1187
|
+
assigned,
|
|
1188
|
+
completed,
|
|
1189
|
+
failed,
|
|
1190
|
+
cancelled,
|
|
1141
1191
|
activeAssignments: queue.filter((q) => q.status === "assigned").map((q) => ({
|
|
1142
1192
|
id: q.id,
|
|
1143
1193
|
nodeId: q.assignedNodeId,
|
|
@@ -1157,6 +1207,141 @@ var init_mesh_work_queue = __esm({
|
|
|
1157
1207
|
}
|
|
1158
1208
|
});
|
|
1159
1209
|
|
|
1210
|
+
// src/detection/cli-detector.ts
|
|
1211
|
+
function parseVersion(raw) {
|
|
1212
|
+
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
1213
|
+
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
1214
|
+
}
|
|
1215
|
+
function shellQuote(value) {
|
|
1216
|
+
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
1217
|
+
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
1218
|
+
}
|
|
1219
|
+
function expandHome(value) {
|
|
1220
|
+
const trimmed = value.trim();
|
|
1221
|
+
if (!trimmed.startsWith("~")) return trimmed;
|
|
1222
|
+
return path8.join(os2.homedir(), trimmed.slice(1));
|
|
1223
|
+
}
|
|
1224
|
+
function isExplicitCommandPath(command) {
|
|
1225
|
+
const trimmed = command.trim();
|
|
1226
|
+
return path8.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
1227
|
+
}
|
|
1228
|
+
function resolveCommandPath(command) {
|
|
1229
|
+
const trimmed = command.trim();
|
|
1230
|
+
if (!trimmed) return null;
|
|
1231
|
+
if (isExplicitCommandPath(trimmed)) {
|
|
1232
|
+
const expanded = expandHome(trimmed);
|
|
1233
|
+
const candidate = path8.isAbsolute(expanded) ? expanded : path8.resolve(expanded);
|
|
1234
|
+
return (0, import_fs5.existsSync)(candidate) ? candidate : null;
|
|
1235
|
+
}
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
function execAsync(cmd, timeoutMs = 5e3) {
|
|
1239
|
+
return new Promise((resolve16) => {
|
|
1240
|
+
const child = (0, import_child_process.exec)(cmd, {
|
|
1241
|
+
encoding: "utf-8",
|
|
1242
|
+
timeout: timeoutMs,
|
|
1243
|
+
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
1244
|
+
}, (err, stdout) => {
|
|
1245
|
+
if (err || !stdout?.trim()) {
|
|
1246
|
+
resolve16(null);
|
|
1247
|
+
} else {
|
|
1248
|
+
resolve16(stdout.trim());
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
child.on("error", () => resolve16(null));
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
async function detectCLIs(providerLoader, options) {
|
|
1255
|
+
const platform10 = os2.platform();
|
|
1256
|
+
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
1257
|
+
const includeVersion = options?.includeVersion !== false;
|
|
1258
|
+
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
1259
|
+
const results = await Promise.all(
|
|
1260
|
+
cliList.map(async (cli) => {
|
|
1261
|
+
try {
|
|
1262
|
+
const explicitPath = resolveCommandPath(cli.command);
|
|
1263
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
1264
|
+
if (!pathResult) return { ...cli, installed: false };
|
|
1265
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
1266
|
+
let version;
|
|
1267
|
+
if (includeVersion) {
|
|
1268
|
+
const versionCommands = [
|
|
1269
|
+
`"${firstPath}" --version`,
|
|
1270
|
+
`"${firstPath}" -V`,
|
|
1271
|
+
`"${firstPath}" -v`,
|
|
1272
|
+
cli.versionCommand
|
|
1273
|
+
].filter((v) => !!v);
|
|
1274
|
+
try {
|
|
1275
|
+
for (const versionCommand of versionCommands) {
|
|
1276
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
1277
|
+
if (versionResult) {
|
|
1278
|
+
version = parseVersion(versionResult);
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
} catch {
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return { ...cli, installed: true, version, path: firstPath };
|
|
1286
|
+
} catch {
|
|
1287
|
+
return { ...cli, installed: false };
|
|
1288
|
+
}
|
|
1289
|
+
})
|
|
1290
|
+
);
|
|
1291
|
+
return results;
|
|
1292
|
+
}
|
|
1293
|
+
async function detectCLI(cliId, providerLoader, options) {
|
|
1294
|
+
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
1295
|
+
if (providerLoader) {
|
|
1296
|
+
const cliList = providerLoader.getCliDetectionList();
|
|
1297
|
+
const target = cliList.find((c) => c.id === resolvedId);
|
|
1298
|
+
if (target) {
|
|
1299
|
+
const platform10 = os2.platform();
|
|
1300
|
+
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
1301
|
+
try {
|
|
1302
|
+
const explicitPath = resolveCommandPath(target.command);
|
|
1303
|
+
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
1304
|
+
if (!pathResult) return null;
|
|
1305
|
+
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
1306
|
+
let version;
|
|
1307
|
+
if (options?.includeVersion !== false) {
|
|
1308
|
+
const versionCommands = [
|
|
1309
|
+
`"${firstPath}" --version`,
|
|
1310
|
+
`"${firstPath}" -V`,
|
|
1311
|
+
`"${firstPath}" -v`,
|
|
1312
|
+
target.versionCommand
|
|
1313
|
+
].filter((v) => !!v);
|
|
1314
|
+
try {
|
|
1315
|
+
for (const versionCommand of versionCommands) {
|
|
1316
|
+
const versionResult = await execAsync(versionCommand, 3e3);
|
|
1317
|
+
if (versionResult) {
|
|
1318
|
+
version = parseVersion(versionResult);
|
|
1319
|
+
break;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
return { ...target, installed: true, version, path: firstPath };
|
|
1326
|
+
} catch {
|
|
1327
|
+
return null;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const all = await detectCLIs(providerLoader, options);
|
|
1332
|
+
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
1333
|
+
}
|
|
1334
|
+
var import_child_process, os2, path8, import_fs5;
|
|
1335
|
+
var init_cli_detector = __esm({
|
|
1336
|
+
"src/detection/cli-detector.ts"() {
|
|
1337
|
+
"use strict";
|
|
1338
|
+
import_child_process = require("child_process");
|
|
1339
|
+
os2 = __toESM(require("os"));
|
|
1340
|
+
path8 = __toESM(require("path"));
|
|
1341
|
+
import_fs5 = require("fs");
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1160
1345
|
// src/logging/logger.ts
|
|
1161
1346
|
function setLogLevel(level) {
|
|
1162
1347
|
currentLevel = level;
|
|
@@ -1172,13 +1357,13 @@ function getDaemonLogDir() {
|
|
|
1172
1357
|
return LOG_DIR;
|
|
1173
1358
|
}
|
|
1174
1359
|
function getCurrentDaemonLogPath(date = /* @__PURE__ */ new Date()) {
|
|
1175
|
-
return
|
|
1360
|
+
return path9.join(LOG_DIR, `daemon-${date.toISOString().slice(0, 10)}.log`);
|
|
1176
1361
|
}
|
|
1177
1362
|
function checkDateRotation() {
|
|
1178
1363
|
const today = getDateStr();
|
|
1179
1364
|
if (today !== currentDate) {
|
|
1180
1365
|
currentDate = today;
|
|
1181
|
-
currentLogFile =
|
|
1366
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
1182
1367
|
cleanOldLogs();
|
|
1183
1368
|
}
|
|
1184
1369
|
}
|
|
@@ -1192,7 +1377,7 @@ function cleanOldLogs() {
|
|
|
1192
1377
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
1193
1378
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
1194
1379
|
try {
|
|
1195
|
-
fs2.unlinkSync(
|
|
1380
|
+
fs2.unlinkSync(path9.join(LOG_DIR, file));
|
|
1196
1381
|
} catch {
|
|
1197
1382
|
}
|
|
1198
1383
|
}
|
|
@@ -1308,17 +1493,17 @@ function installGlobalInterceptor() {
|
|
|
1308
1493
|
writeToFile(`Log file: ${currentLogFile}`);
|
|
1309
1494
|
writeToFile(`Log level: ${currentLevel}`);
|
|
1310
1495
|
}
|
|
1311
|
-
var fs2,
|
|
1496
|
+
var fs2, path9, os3, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH;
|
|
1312
1497
|
var init_logger = __esm({
|
|
1313
1498
|
"src/logging/logger.ts"() {
|
|
1314
1499
|
"use strict";
|
|
1315
1500
|
fs2 = __toESM(require("fs"));
|
|
1316
|
-
|
|
1317
|
-
|
|
1501
|
+
path9 = __toESM(require("path"));
|
|
1502
|
+
os3 = __toESM(require("os"));
|
|
1318
1503
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
1319
1504
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
1320
1505
|
currentLevel = "info";
|
|
1321
|
-
LOG_DIR = process.platform === "win32" ?
|
|
1506
|
+
LOG_DIR = process.platform === "win32" ? path9.join(process.env.LOCALAPPDATA || process.env.APPDATA || path9.join(os3.homedir(), "AppData", "Local"), "adhdev", "logs") : process.platform === "darwin" ? path9.join(os3.homedir(), "Library", "Logs", "adhdev") : path9.join(os3.homedir(), ".local", "share", "adhdev", "logs");
|
|
1322
1507
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
1323
1508
|
MAX_LOG_DAYS = 7;
|
|
1324
1509
|
try {
|
|
@@ -1326,16 +1511,16 @@ var init_logger = __esm({
|
|
|
1326
1511
|
} catch {
|
|
1327
1512
|
}
|
|
1328
1513
|
currentDate = getDateStr();
|
|
1329
|
-
currentLogFile =
|
|
1514
|
+
currentLogFile = path9.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
1330
1515
|
cleanOldLogs();
|
|
1331
1516
|
try {
|
|
1332
|
-
const oldLog =
|
|
1517
|
+
const oldLog = path9.join(LOG_DIR, "daemon.log");
|
|
1333
1518
|
if (fs2.existsSync(oldLog)) {
|
|
1334
1519
|
const stat2 = fs2.statSync(oldLog);
|
|
1335
1520
|
const oldDate = stat2.mtime.toISOString().slice(0, 10);
|
|
1336
|
-
fs2.renameSync(oldLog,
|
|
1521
|
+
fs2.renameSync(oldLog, path9.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
1337
1522
|
}
|
|
1338
|
-
const oldLogBackup =
|
|
1523
|
+
const oldLogBackup = path9.join(LOG_DIR, "daemon.log.old");
|
|
1339
1524
|
if (fs2.existsSync(oldLogBackup)) {
|
|
1340
1525
|
fs2.unlinkSync(oldLogBackup);
|
|
1341
1526
|
}
|
|
@@ -1367,7 +1552,7 @@ var init_logger = __esm({
|
|
|
1367
1552
|
}
|
|
1368
1553
|
};
|
|
1369
1554
|
interceptorInstalled = false;
|
|
1370
|
-
LOG_PATH =
|
|
1555
|
+
LOG_PATH = path9.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
1371
1556
|
}
|
|
1372
1557
|
});
|
|
1373
1558
|
|
|
@@ -1439,7 +1624,235 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
1439
1624
|
});
|
|
1440
1625
|
return true;
|
|
1441
1626
|
}
|
|
1442
|
-
function
|
|
1627
|
+
function normalizeProviderPriority(policy) {
|
|
1628
|
+
const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
|
|
1629
|
+
if (!Array.isArray(raw)) return [];
|
|
1630
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1631
|
+
return raw.map((type) => typeof type === "string" ? type.trim() : "").filter(Boolean).filter((type) => {
|
|
1632
|
+
if (seen.has(type)) return false;
|
|
1633
|
+
seen.add(type);
|
|
1634
|
+
return true;
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
function isTerminalSessionStatus(status) {
|
|
1638
|
+
return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
|
|
1639
|
+
}
|
|
1640
|
+
function isIdleSessionState(state) {
|
|
1641
|
+
const status = readNonEmptyString(state?.status).toLowerCase();
|
|
1642
|
+
if (isTerminalSessionStatus(status)) return false;
|
|
1643
|
+
return status === "idle" || state?.activeChat?.status === "waiting_input";
|
|
1644
|
+
}
|
|
1645
|
+
function isDirtyNode(node) {
|
|
1646
|
+
return node?.health === "dirty" || node?.git?.dirty === true;
|
|
1647
|
+
}
|
|
1648
|
+
function isLaunchableNode(node) {
|
|
1649
|
+
if (!node || node.status === "disabled" || node.status === "removed") return false;
|
|
1650
|
+
const health = readNonEmptyString(node.health).toLowerCase();
|
|
1651
|
+
if (!health) return true;
|
|
1652
|
+
return health === "online" || health === "unknown";
|
|
1653
|
+
}
|
|
1654
|
+
function localAutoLaunchSkipReason(node) {
|
|
1655
|
+
const daemonId = readNonEmptyString(node?.daemonId);
|
|
1656
|
+
const machineId = readNonEmptyString(node?.machineId);
|
|
1657
|
+
const appConfig = loadConfig();
|
|
1658
|
+
const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
|
|
1659
|
+
const cloudDaemonId = localMachineId ? `daemon_${localMachineId}` : "";
|
|
1660
|
+
const standaloneDaemonId = localMachineId ? `standalone_${localMachineId}` : "";
|
|
1661
|
+
const daemonMatchesLocal = !daemonId || daemonId === cloudDaemonId || daemonId === standaloneDaemonId;
|
|
1662
|
+
const machineMatchesLocal = !machineId || localMachineId && machineId === localMachineId;
|
|
1663
|
+
if (node?.isLocalWorktree === true) {
|
|
1664
|
+
return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
|
|
1665
|
+
}
|
|
1666
|
+
if (daemonId || machineId) {
|
|
1667
|
+
return daemonMatchesLocal && machineMatchesLocal ? null : "remote_auto_launch_unsupported";
|
|
1668
|
+
}
|
|
1669
|
+
return null;
|
|
1670
|
+
}
|
|
1671
|
+
function activeAssignedCount(meshId) {
|
|
1672
|
+
return getQueue(meshId, { status: ["assigned"] }).length;
|
|
1673
|
+
}
|
|
1674
|
+
function nodeHasActiveAssignment(meshId, nodeId) {
|
|
1675
|
+
return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
|
|
1676
|
+
}
|
|
1677
|
+
function liveSessionCountForNode(components, meshId, nodeId) {
|
|
1678
|
+
return components.instanceManager.getByCategory("cli").filter((inst) => {
|
|
1679
|
+
const state = inst.getState();
|
|
1680
|
+
const settings = state.settings || {};
|
|
1681
|
+
if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
|
|
1682
|
+
const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
1683
|
+
if (instNodeId !== nodeId) return false;
|
|
1684
|
+
const status = readNonEmptyString(state.status).toLowerCase();
|
|
1685
|
+
return !isTerminalSessionStatus(status);
|
|
1686
|
+
}).length;
|
|
1687
|
+
}
|
|
1688
|
+
function recordAutoLaunchEvent(meshId, args) {
|
|
1689
|
+
try {
|
|
1690
|
+
appendLedgerEntry(meshId, {
|
|
1691
|
+
kind: "session_auto_launch",
|
|
1692
|
+
nodeId: args.nodeId,
|
|
1693
|
+
sessionId: args.sessionId,
|
|
1694
|
+
providerType: args.providerType,
|
|
1695
|
+
payload: {
|
|
1696
|
+
phase: args.phase,
|
|
1697
|
+
taskId: args.taskId,
|
|
1698
|
+
reason: args.reason,
|
|
1699
|
+
error: args.error
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
} catch (e) {
|
|
1703
|
+
LOG.warn("MeshQueue", `Failed to record auto-launch ledger event: ${e?.message || e}`);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
function markAutoLaunch(meshId, taskId, args) {
|
|
1707
|
+
recordTaskAutoLaunch(meshId, taskId, {
|
|
1708
|
+
status: args.status,
|
|
1709
|
+
reason: args.reason || args.error,
|
|
1710
|
+
nodeId: args.nodeId,
|
|
1711
|
+
providerType: args.providerType,
|
|
1712
|
+
sessionId: args.sessionId
|
|
1713
|
+
});
|
|
1714
|
+
recordAutoLaunchEvent(meshId, {
|
|
1715
|
+
phase: args.status,
|
|
1716
|
+
taskId,
|
|
1717
|
+
nodeId: args.nodeId,
|
|
1718
|
+
providerType: args.providerType,
|
|
1719
|
+
sessionId: args.sessionId,
|
|
1720
|
+
reason: args.reason,
|
|
1721
|
+
error: args.error
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
async function resolveUsableProvider(components, nodeId, node) {
|
|
1725
|
+
const providerPriority = normalizeProviderPriority(node?.policy);
|
|
1726
|
+
if (!providerPriority.length) return { reason: "missing_provider_priority" };
|
|
1727
|
+
const providerLoader = components.providerLoader;
|
|
1728
|
+
if (!providerLoader) return { reason: "provider_loader_unavailable" };
|
|
1729
|
+
const failed = [];
|
|
1730
|
+
for (const requestedType of providerPriority) {
|
|
1731
|
+
const normalizedType = typeof providerLoader.resolveAlias === "function" ? providerLoader.resolveAlias(requestedType) : requestedType;
|
|
1732
|
+
if (typeof providerLoader.isMachineProviderEnabled === "function" && !providerLoader.isMachineProviderEnabled(normalizedType)) {
|
|
1733
|
+
failed.push(`${requestedType}: disabled`);
|
|
1734
|
+
continue;
|
|
1735
|
+
}
|
|
1736
|
+
let detected;
|
|
1737
|
+
try {
|
|
1738
|
+
detected = await detectCLI(normalizedType, providerLoader, { includeVersion: false });
|
|
1739
|
+
} catch (e) {
|
|
1740
|
+
failed.push(`${requestedType}: detect failed: ${e?.message || e}`);
|
|
1741
|
+
continue;
|
|
1742
|
+
}
|
|
1743
|
+
if (typeof providerLoader.setCliDetectionResults === "function") {
|
|
1744
|
+
providerLoader.setCliDetectionResults([{
|
|
1745
|
+
id: normalizedType,
|
|
1746
|
+
installed: !!detected,
|
|
1747
|
+
path: detected?.path
|
|
1748
|
+
}], false);
|
|
1749
|
+
}
|
|
1750
|
+
components.onStatusChange?.();
|
|
1751
|
+
if (detected) return { providerType: normalizedType };
|
|
1752
|
+
failed.push(`${requestedType}: not detected`);
|
|
1753
|
+
}
|
|
1754
|
+
return { reason: `provider_priority_unusable: ${failed.join("; ") || nodeId}` };
|
|
1755
|
+
}
|
|
1756
|
+
async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
1757
|
+
const queue = getQueue(meshId);
|
|
1758
|
+
const pending = queue.filter((task) => task.status === "pending");
|
|
1759
|
+
if (!pending.length) return false;
|
|
1760
|
+
const maxParallelTasks = Math.max(1, Math.floor(Number(mesh?.policy?.maxParallelTasks) || 2));
|
|
1761
|
+
for (const task of pending) {
|
|
1762
|
+
if (activeAssignedCount(meshId) >= maxParallelTasks) {
|
|
1763
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_parallel_tasks_reached" });
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
if (task.targetSessionId) {
|
|
1767
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "target_session_constraint" });
|
|
1768
|
+
continue;
|
|
1769
|
+
}
|
|
1770
|
+
const candidateNodes = Array.isArray(mesh?.nodes) ? mesh.nodes.filter((node) => task.targetNodeId ? node?.id === task.targetNodeId : true) : [];
|
|
1771
|
+
if (!candidateNodes.length) {
|
|
1772
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_matching_node", nodeId: task.targetNodeId });
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
for (const node of candidateNodes) {
|
|
1776
|
+
const nodeId = readNonEmptyString(node?.id);
|
|
1777
|
+
if (!nodeId) continue;
|
|
1778
|
+
const launchKey = `${meshId}:${nodeId}`;
|
|
1779
|
+
const cooldownUntil = autoLaunchCooldownUntil.get(launchKey) || 0;
|
|
1780
|
+
if (autoLaunchInProgress.has(launchKey)) {
|
|
1781
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_in_progress", nodeId });
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
if (Date.now() < cooldownUntil) {
|
|
1785
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "auto_launch_cooldown", nodeId });
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
if (isDirtyNode(node)) {
|
|
1789
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "dirty_workspace", nodeId });
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1792
|
+
if (!isLaunchableNode(node)) {
|
|
1793
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
|
|
1794
|
+
continue;
|
|
1795
|
+
}
|
|
1796
|
+
const localSkipReason = localAutoLaunchSkipReason(node);
|
|
1797
|
+
if (localSkipReason) {
|
|
1798
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: localSkipReason, nodeId });
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
if (nodeHasActiveAssignment(meshId, nodeId)) {
|
|
1802
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_has_active_assignment", nodeId });
|
|
1803
|
+
continue;
|
|
1804
|
+
}
|
|
1805
|
+
const maxConcurrentSessions = Number(node?.policy?.maxConcurrentSessions);
|
|
1806
|
+
if (Number.isFinite(maxConcurrentSessions) && maxConcurrentSessions >= 0 && liveSessionCountForNode(components, meshId, nodeId) >= maxConcurrentSessions) {
|
|
1807
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "max_concurrent_sessions_reached", nodeId });
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
autoLaunchInProgress.add(launchKey);
|
|
1811
|
+
try {
|
|
1812
|
+
const resolved = await resolveUsableProvider(components, nodeId, node);
|
|
1813
|
+
if (!resolved.providerType) {
|
|
1814
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: resolved.reason || "provider_unusable", nodeId });
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1817
|
+
markAutoLaunch(meshId, task.id, { status: "started", nodeId, providerType: resolved.providerType });
|
|
1818
|
+
const launchResult = await components.cliManager.handleCliCommand("launch_cli", {
|
|
1819
|
+
cliType: resolved.providerType,
|
|
1820
|
+
dir: node.workspace,
|
|
1821
|
+
settings: {
|
|
1822
|
+
meshNodeFor: meshId,
|
|
1823
|
+
meshNodeId: nodeId,
|
|
1824
|
+
spawnedSessionVisibility: mesh?.policy?.spawnedSessionVisibility || "hidden",
|
|
1825
|
+
launchedByCoordinator: true,
|
|
1826
|
+
autoLaunchedForQueueTaskId: task.id
|
|
1827
|
+
}
|
|
1828
|
+
});
|
|
1829
|
+
if (!launchResult?.success) {
|
|
1830
|
+
const reason = launchResult?.error || "launch_cli_failed";
|
|
1831
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason, nodeId, providerType: resolved.providerType });
|
|
1832
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1833
|
+
return false;
|
|
1834
|
+
}
|
|
1835
|
+
const sessionId = readNonEmptyString(launchResult.sessionId) || readNonEmptyString(launchResult.id) || readNonEmptyString(launchResult.runtimeSessionId);
|
|
1836
|
+
if (!sessionId) {
|
|
1837
|
+
markAutoLaunch(meshId, task.id, { status: "failed", reason: "launch_missing_session_id", nodeId, providerType: resolved.providerType });
|
|
1838
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1839
|
+
return false;
|
|
1840
|
+
}
|
|
1841
|
+
markAutoLaunch(meshId, task.id, { status: "completed", nodeId, providerType: resolved.providerType, sessionId });
|
|
1842
|
+
tryAssignQueueTask(components, meshId, nodeId, sessionId, resolved.providerType);
|
|
1843
|
+
return true;
|
|
1844
|
+
} catch (e) {
|
|
1845
|
+
markAutoLaunch(meshId, task.id, { status: "failed", error: e?.message || String(e), nodeId });
|
|
1846
|
+
autoLaunchCooldownUntil.set(launchKey, Date.now() + AUTO_LAUNCH_COOLDOWN_MS);
|
|
1847
|
+
return false;
|
|
1848
|
+
} finally {
|
|
1849
|
+
autoLaunchInProgress.delete(launchKey);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
return false;
|
|
1854
|
+
}
|
|
1855
|
+
async function triggerMeshQueue(components, meshId) {
|
|
1443
1856
|
const mesh = getMeshWithCache(components, meshId);
|
|
1444
1857
|
if (!mesh) return;
|
|
1445
1858
|
const cliInstances = components.instanceManager.getByCategory("cli");
|
|
@@ -1450,9 +1863,7 @@ function triggerMeshQueue(components, meshId) {
|
|
|
1450
1863
|
if (instMeshId !== meshId) continue;
|
|
1451
1864
|
const nodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
|
|
1452
1865
|
if (!nodeId) continue;
|
|
1453
|
-
|
|
1454
|
-
if (["stopped", "failed", "terminated", "exited", "closed"].includes(status)) continue;
|
|
1455
|
-
if (status !== "idle" && state.activeChat?.status !== "waiting_input") continue;
|
|
1866
|
+
if (!isIdleSessionState(state)) continue;
|
|
1456
1867
|
const sessionId = state.instanceId;
|
|
1457
1868
|
const providerType = state.type || readNonEmptyString(settings.providerType);
|
|
1458
1869
|
if (providerType) {
|
|
@@ -1468,6 +1879,7 @@ function triggerMeshQueue(components, meshId) {
|
|
|
1468
1879
|
}
|
|
1469
1880
|
}
|
|
1470
1881
|
}
|
|
1882
|
+
await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
|
|
1471
1883
|
}
|
|
1472
1884
|
function buildMeshSystemMessage(args) {
|
|
1473
1885
|
const metadata = formatCompletionMetadata(args.metadataEvent);
|
|
@@ -1744,11 +2156,13 @@ function setupMeshEventForwarding(components) {
|
|
|
1744
2156
|
});
|
|
1745
2157
|
});
|
|
1746
2158
|
}
|
|
1747
|
-
var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND;
|
|
2159
|
+
var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
|
|
1748
2160
|
var init_mesh_events = __esm({
|
|
1749
2161
|
"src/mesh/mesh-events.ts"() {
|
|
1750
2162
|
"use strict";
|
|
2163
|
+
init_config();
|
|
1751
2164
|
init_mesh_config();
|
|
2165
|
+
init_cli_detector();
|
|
1752
2166
|
init_logger();
|
|
1753
2167
|
init_mesh_ledger();
|
|
1754
2168
|
init_mesh_work_queue();
|
|
@@ -1769,6 +2183,9 @@ var init_mesh_events = __esm({
|
|
|
1769
2183
|
"agent:stopped": "task_failed",
|
|
1770
2184
|
"monitor:long_generating": "task_stalled"
|
|
1771
2185
|
};
|
|
2186
|
+
autoLaunchInProgress = /* @__PURE__ */ new Set();
|
|
2187
|
+
autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
|
|
2188
|
+
AUTO_LAUNCH_COOLDOWN_MS = 5e3;
|
|
1772
2189
|
}
|
|
1773
2190
|
});
|
|
1774
2191
|
|
|
@@ -4892,6 +5309,7 @@ __export(index_exports, {
|
|
|
4892
5309
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
4893
5310
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
4894
5311
|
NodePtyTransportFactory: () => NodePtyTransportFactory,
|
|
5312
|
+
P2pRelayFailureError: () => P2pRelayFailureError,
|
|
4895
5313
|
ProviderCliAdapter: () => ProviderCliAdapter,
|
|
4896
5314
|
ProviderInstanceManager: () => ProviderInstanceManager,
|
|
4897
5315
|
ProviderLoader: () => ProviderLoader,
|
|
@@ -4908,6 +5326,7 @@ __export(index_exports, {
|
|
|
4908
5326
|
buildChatTailDeliverySignature: () => buildChatTailDeliverySignature,
|
|
4909
5327
|
buildCoordinatorSystemPrompt: () => buildCoordinatorSystemPrompt,
|
|
4910
5328
|
buildMachineInfo: () => buildMachineInfo,
|
|
5329
|
+
buildP2pRelayFailurePayload: () => buildP2pRelayFailurePayload,
|
|
4911
5330
|
buildPinnedGlobalInstallCommand: () => buildPinnedGlobalInstallCommand,
|
|
4912
5331
|
buildRuntimeSystemChatMessage: () => buildRuntimeSystemChatMessage,
|
|
4913
5332
|
buildSessionEntries: () => buildSessionEntries,
|
|
@@ -4922,6 +5341,7 @@ __export(index_exports, {
|
|
|
4922
5341
|
claimNextTask: () => claimNextTask,
|
|
4923
5342
|
classifyChatMessageVisibility: () => classifyChatMessageVisibility,
|
|
4924
5343
|
classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush,
|
|
5344
|
+
classifyP2pRelayFailure: () => classifyP2pRelayFailure,
|
|
4925
5345
|
clearDebugTrace: () => clearDebugTrace,
|
|
4926
5346
|
compareGitSnapshots: () => compareGitSnapshots,
|
|
4927
5347
|
configureDebugTraceStore: () => configureDebugTraceStore,
|
|
@@ -4989,6 +5409,7 @@ __export(index_exports, {
|
|
|
4989
5409
|
isInternalChatMessage: () => isInternalChatMessage,
|
|
4990
5410
|
isManagedStatusWaiting: () => isManagedStatusWaiting,
|
|
4991
5411
|
isManagedStatusWorking: () => isManagedStatusWorking,
|
|
5412
|
+
isP2pRelayTransportFailure: () => isP2pRelayTransportFailure,
|
|
4992
5413
|
isPathInside: () => isPathInside,
|
|
4993
5414
|
isSessionHostLiveRuntime: () => isSessionHostLiveRuntime,
|
|
4994
5415
|
isSessionHostRecoverySnapshot: () => isSessionHostRecoverySnapshot,
|
|
@@ -6545,7 +6966,7 @@ function addWorkspaceEntry(config, rawPath, label, options) {
|
|
|
6545
6966
|
}
|
|
6546
6967
|
}
|
|
6547
6968
|
const v = validateWorkspacePath(abs);
|
|
6548
|
-
if (
|
|
6969
|
+
if (v.ok !== true) return { error: v.error };
|
|
6549
6970
|
const list = [...config.workspaces || []];
|
|
6550
6971
|
if (list.some((w) => path5.resolve(w.path) === abs)) {
|
|
6551
6972
|
return { error: "Workspace already in list" };
|
|
@@ -6954,8 +7375,118 @@ init_mesh_ledger();
|
|
|
6954
7375
|
init_mesh_work_queue();
|
|
6955
7376
|
init_mesh_events();
|
|
6956
7377
|
|
|
7378
|
+
// src/mesh/p2p-relay-failure.ts
|
|
7379
|
+
var NO_FALLBACK_REASON = "Repo Mesh command/data-plane is P2P-only; WS/REST command fallback is intentionally disabled to preserve the transport boundary.";
|
|
7380
|
+
var P2P_NEXT_ACTION = "Check daemon/P2P health, wait briefly for connection establishment, then do one bounded retry or requeue the mesh task after clearing stale target session metadata.";
|
|
7381
|
+
var NON_P2P_NEXT_ACTION = "Inspect the provider/command error and fix the underlying logic or configuration before retrying.";
|
|
7382
|
+
function messageFromError(error) {
|
|
7383
|
+
if (error instanceof Error) return error.message;
|
|
7384
|
+
if (typeof error === "string") return error;
|
|
7385
|
+
if (error && typeof error === "object") {
|
|
7386
|
+
const candidate = error.error ?? error.message ?? error.reason;
|
|
7387
|
+
if (typeof candidate === "string") return candidate;
|
|
7388
|
+
}
|
|
7389
|
+
return String(error || "mesh relay command failed");
|
|
7390
|
+
}
|
|
7391
|
+
function classifyP2pRelayFailure(error, _context = {}) {
|
|
7392
|
+
const message = messageFromError(error);
|
|
7393
|
+
const lower = message.toLowerCase();
|
|
7394
|
+
const hasP2pSignal = /p2p|datachannel|node-datachannel|webrtc|ice|mesh_relay_command|daemon_mesh_p2p_transport/i.test(message);
|
|
7395
|
+
const hasFailureSignal = /unavailable|missing|failed|failure|timeout|timed out|not connected|closed|disconnected|offline|no route|route unavailable|cannot send|cannot establish/i.test(message);
|
|
7396
|
+
if (/requires targetdaemonid and command|providerpriority|no inference provider|permission denied|read-only|not a member/i.test(message)) {
|
|
7397
|
+
return {
|
|
7398
|
+
code: "mesh_logic_or_provider_failure",
|
|
7399
|
+
reason: "mesh_logic_or_provider_failure",
|
|
7400
|
+
transport: "unknown",
|
|
7401
|
+
recoverable: false,
|
|
7402
|
+
retryRecommended: false,
|
|
7403
|
+
nextAction: NON_P2P_NEXT_ACTION,
|
|
7404
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7405
|
+
};
|
|
7406
|
+
}
|
|
7407
|
+
let code = null;
|
|
7408
|
+
let reason = "";
|
|
7409
|
+
if (/timeout|timed out/i.test(message) && (hasP2pSignal || /mesh transport/i.test(message))) {
|
|
7410
|
+
code = "p2p_timeout";
|
|
7411
|
+
reason = "daemon_mesh_p2p_timeout";
|
|
7412
|
+
} else if (/no route|route unavailable/i.test(message)) {
|
|
7413
|
+
code = "p2p_no_route";
|
|
7414
|
+
reason = "daemon_mesh_p2p_no_route";
|
|
7415
|
+
} else if (/offline|not owned|not found|not connected to server/i.test(message) && /daemon|peer|target/i.test(message)) {
|
|
7416
|
+
code = "p2p_daemon_offline";
|
|
7417
|
+
reason = "daemon_mesh_target_offline";
|
|
7418
|
+
} else if (/closed|disconnected/i.test(message) && (hasP2pSignal || /state changed/i.test(message))) {
|
|
7419
|
+
code = "p2p_datachannel_closed";
|
|
7420
|
+
reason = "daemon_mesh_p2p_datachannel_closed";
|
|
7421
|
+
} else if (/not connected|cannot send|cannot establish/i.test(message) && hasP2pSignal) {
|
|
7422
|
+
code = "p2p_not_connected";
|
|
7423
|
+
reason = "daemon_mesh_p2p_not_connected";
|
|
7424
|
+
} else if (hasP2pSignal && hasFailureSignal) {
|
|
7425
|
+
code = "p2p_unavailable";
|
|
7426
|
+
reason = "daemon_mesh_p2p_transport_unavailable";
|
|
7427
|
+
}
|
|
7428
|
+
if (!code) {
|
|
7429
|
+
return {
|
|
7430
|
+
code: "mesh_logic_or_provider_failure",
|
|
7431
|
+
reason: "mesh_logic_or_provider_failure",
|
|
7432
|
+
transport: "unknown",
|
|
7433
|
+
recoverable: false,
|
|
7434
|
+
retryRecommended: false,
|
|
7435
|
+
nextAction: NON_P2P_NEXT_ACTION,
|
|
7436
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7437
|
+
};
|
|
7438
|
+
}
|
|
7439
|
+
return {
|
|
7440
|
+
code,
|
|
7441
|
+
reason,
|
|
7442
|
+
transport: "p2p",
|
|
7443
|
+
recoverable: true,
|
|
7444
|
+
retryRecommended: true,
|
|
7445
|
+
nextAction: P2P_NEXT_ACTION,
|
|
7446
|
+
noFallbackReason: NO_FALLBACK_REASON
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
function isP2pRelayTransportFailure(error) {
|
|
7450
|
+
return classifyP2pRelayFailure(error).recoverable === true;
|
|
7451
|
+
}
|
|
7452
|
+
function buildP2pRelayFailurePayload(error, context = {}) {
|
|
7453
|
+
const classification = classifyP2pRelayFailure(error, context);
|
|
7454
|
+
return {
|
|
7455
|
+
success: false,
|
|
7456
|
+
...classification,
|
|
7457
|
+
error: messageFromError(error),
|
|
7458
|
+
...context.command ? { command: context.command } : {},
|
|
7459
|
+
...context.targetDaemonId ? { targetDaemonId: context.targetDaemonId } : {}
|
|
7460
|
+
};
|
|
7461
|
+
}
|
|
7462
|
+
var P2pRelayFailureError = class extends Error {
|
|
7463
|
+
code;
|
|
7464
|
+
reason;
|
|
7465
|
+
transport;
|
|
7466
|
+
recoverable;
|
|
7467
|
+
retryRecommended;
|
|
7468
|
+
nextAction;
|
|
7469
|
+
noFallbackReason;
|
|
7470
|
+
command;
|
|
7471
|
+
targetDaemonId;
|
|
7472
|
+
constructor(message, context = {}) {
|
|
7473
|
+
super(message);
|
|
7474
|
+
this.name = "P2pRelayFailureError";
|
|
7475
|
+
const payload = buildP2pRelayFailurePayload(message, context);
|
|
7476
|
+
this.code = payload.code;
|
|
7477
|
+
this.reason = payload.reason;
|
|
7478
|
+
this.transport = payload.transport;
|
|
7479
|
+
this.recoverable = payload.recoverable;
|
|
7480
|
+
this.retryRecommended = payload.retryRecommended;
|
|
7481
|
+
this.nextAction = payload.nextAction;
|
|
7482
|
+
this.noFallbackReason = payload.noFallbackReason;
|
|
7483
|
+
this.command = context.command;
|
|
7484
|
+
this.targetDaemonId = context.targetDaemonId;
|
|
7485
|
+
}
|
|
7486
|
+
};
|
|
7487
|
+
|
|
6957
7488
|
// src/config/state-store.ts
|
|
6958
|
-
var
|
|
7489
|
+
var import_fs6 = require("fs");
|
|
6959
7490
|
var import_path5 = require("path");
|
|
6960
7491
|
init_config();
|
|
6961
7492
|
var DEFAULT_STATE = {
|
|
@@ -7006,11 +7537,11 @@ function normalizeState(raw) {
|
|
|
7006
7537
|
}
|
|
7007
7538
|
function loadState() {
|
|
7008
7539
|
const statePath = getStatePath();
|
|
7009
|
-
if (!(0,
|
|
7540
|
+
if (!(0, import_fs6.existsSync)(statePath)) {
|
|
7010
7541
|
return { ...DEFAULT_STATE };
|
|
7011
7542
|
}
|
|
7012
7543
|
try {
|
|
7013
|
-
const raw = (0,
|
|
7544
|
+
const raw = (0, import_fs6.readFileSync)(statePath, "utf-8");
|
|
7014
7545
|
return normalizeState(JSON.parse(raw));
|
|
7015
7546
|
} catch {
|
|
7016
7547
|
return { ...DEFAULT_STATE };
|
|
@@ -7019,17 +7550,17 @@ function loadState() {
|
|
|
7019
7550
|
function saveState(state) {
|
|
7020
7551
|
const statePath = getStatePath();
|
|
7021
7552
|
const normalized = normalizeState(state);
|
|
7022
|
-
(0,
|
|
7553
|
+
(0, import_fs6.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
|
|
7023
7554
|
}
|
|
7024
7555
|
function resetState() {
|
|
7025
7556
|
saveState({ ...DEFAULT_STATE });
|
|
7026
7557
|
}
|
|
7027
7558
|
|
|
7028
7559
|
// src/detection/ide-detector.ts
|
|
7029
|
-
var
|
|
7030
|
-
var
|
|
7560
|
+
var import_child_process2 = require("child_process");
|
|
7561
|
+
var import_fs7 = require("fs");
|
|
7031
7562
|
var import_os2 = require("os");
|
|
7032
|
-
var
|
|
7563
|
+
var path10 = __toESM(require("path"));
|
|
7033
7564
|
var BUILTIN_IDE_DEFINITIONS = [];
|
|
7034
7565
|
var registeredIDEs = /* @__PURE__ */ new Map();
|
|
7035
7566
|
function registerIDEDefinition(def) {
|
|
@@ -7048,13 +7579,13 @@ function getMergedDefinitions() {
|
|
|
7048
7579
|
function findCliCommand(command) {
|
|
7049
7580
|
const trimmed = String(command || "").trim();
|
|
7050
7581
|
if (!trimmed) return null;
|
|
7051
|
-
if (
|
|
7052
|
-
const candidate = trimmed.startsWith("~") ?
|
|
7053
|
-
const resolved =
|
|
7054
|
-
return (0,
|
|
7582
|
+
if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
|
|
7583
|
+
const candidate = trimmed.startsWith("~") ? path10.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
|
|
7584
|
+
const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
|
|
7585
|
+
return (0, import_fs7.existsSync)(resolved) ? resolved : null;
|
|
7055
7586
|
}
|
|
7056
7587
|
try {
|
|
7057
|
-
const result = (0,
|
|
7588
|
+
const result = (0, import_child_process2.execSync)(
|
|
7058
7589
|
(0, import_os2.platform)() === "win32" ? `where ${trimmed}` : `which ${trimmed}`,
|
|
7059
7590
|
{ encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
7060
7591
|
).trim();
|
|
@@ -7065,7 +7596,7 @@ function findCliCommand(command) {
|
|
|
7065
7596
|
}
|
|
7066
7597
|
function getIdeVersion(cliCommand) {
|
|
7067
7598
|
try {
|
|
7068
|
-
const result = (0,
|
|
7599
|
+
const result = (0, import_child_process2.execSync)(`"${cliCommand}" --version`, {
|
|
7069
7600
|
encoding: "utf-8",
|
|
7070
7601
|
timeout: 1e4,
|
|
7071
7602
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -7078,13 +7609,13 @@ function getIdeVersion(cliCommand) {
|
|
|
7078
7609
|
function checkPathExists(paths) {
|
|
7079
7610
|
const home = (0, import_os2.homedir)();
|
|
7080
7611
|
for (const p of paths) {
|
|
7081
|
-
const normalized = p.startsWith("~") ?
|
|
7612
|
+
const normalized = p.startsWith("~") ? path10.join(home, p.slice(1)) : p;
|
|
7082
7613
|
if (normalized.includes("*")) {
|
|
7083
7614
|
const username = home.split(/[\\/]/).pop() || "";
|
|
7084
7615
|
const resolved = normalized.replace("*", username);
|
|
7085
|
-
if ((0,
|
|
7616
|
+
if ((0, import_fs7.existsSync)(resolved)) return resolved;
|
|
7086
7617
|
} else {
|
|
7087
|
-
if ((0,
|
|
7618
|
+
if ((0, import_fs7.existsSync)(normalized)) return normalized;
|
|
7088
7619
|
}
|
|
7089
7620
|
}
|
|
7090
7621
|
return null;
|
|
@@ -7098,7 +7629,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7098
7629
|
let resolvedCli = cliPath;
|
|
7099
7630
|
if (!resolvedCli && appPath && os22 === "darwin") {
|
|
7100
7631
|
const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
|
|
7101
|
-
if ((0,
|
|
7632
|
+
if ((0, import_fs7.existsSync)(bundledCli)) resolvedCli = bundledCli;
|
|
7102
7633
|
}
|
|
7103
7634
|
if (!resolvedCli && appPath && os22 === "win32") {
|
|
7104
7635
|
const { dirname: dirname9 } = await import("path");
|
|
@@ -7111,7 +7642,7 @@ async function detectIDEs(providerLoader) {
|
|
|
7111
7642
|
`${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
|
|
7112
7643
|
];
|
|
7113
7644
|
for (const c of candidates) {
|
|
7114
|
-
if ((0,
|
|
7645
|
+
if ((0, import_fs7.existsSync)(c)) {
|
|
7115
7646
|
resolvedCli = c;
|
|
7116
7647
|
break;
|
|
7117
7648
|
}
|
|
@@ -7133,134 +7664,8 @@ async function detectIDEs(providerLoader) {
|
|
|
7133
7664
|
return results;
|
|
7134
7665
|
}
|
|
7135
7666
|
|
|
7136
|
-
// src/
|
|
7137
|
-
|
|
7138
|
-
var os3 = __toESM(require("os"));
|
|
7139
|
-
var path10 = __toESM(require("path"));
|
|
7140
|
-
var import_fs7 = require("fs");
|
|
7141
|
-
function parseVersion(raw) {
|
|
7142
|
-
const match = raw.match(/v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)/);
|
|
7143
|
-
return match ? match[1] : raw.split("\n")[0].slice(0, 100);
|
|
7144
|
-
}
|
|
7145
|
-
function shellQuote(value) {
|
|
7146
|
-
if (/^[a-zA-Z0-9_./:@%+=,-]+$/.test(value)) return value;
|
|
7147
|
-
return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
7148
|
-
}
|
|
7149
|
-
function expandHome(value) {
|
|
7150
|
-
const trimmed = value.trim();
|
|
7151
|
-
if (!trimmed.startsWith("~")) return trimmed;
|
|
7152
|
-
return path10.join(os3.homedir(), trimmed.slice(1));
|
|
7153
|
-
}
|
|
7154
|
-
function isExplicitCommandPath(command) {
|
|
7155
|
-
const trimmed = command.trim();
|
|
7156
|
-
return path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~");
|
|
7157
|
-
}
|
|
7158
|
-
function resolveCommandPath(command) {
|
|
7159
|
-
const trimmed = command.trim();
|
|
7160
|
-
if (!trimmed) return null;
|
|
7161
|
-
if (isExplicitCommandPath(trimmed)) {
|
|
7162
|
-
const expanded = expandHome(trimmed);
|
|
7163
|
-
const candidate = path10.isAbsolute(expanded) ? expanded : path10.resolve(expanded);
|
|
7164
|
-
return (0, import_fs7.existsSync)(candidate) ? candidate : null;
|
|
7165
|
-
}
|
|
7166
|
-
return null;
|
|
7167
|
-
}
|
|
7168
|
-
function execAsync(cmd, timeoutMs = 5e3) {
|
|
7169
|
-
return new Promise((resolve16) => {
|
|
7170
|
-
const child = (0, import_child_process2.exec)(cmd, {
|
|
7171
|
-
encoding: "utf-8",
|
|
7172
|
-
timeout: timeoutMs,
|
|
7173
|
-
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
7174
|
-
}, (err, stdout) => {
|
|
7175
|
-
if (err || !stdout?.trim()) {
|
|
7176
|
-
resolve16(null);
|
|
7177
|
-
} else {
|
|
7178
|
-
resolve16(stdout.trim());
|
|
7179
|
-
}
|
|
7180
|
-
});
|
|
7181
|
-
child.on("error", () => resolve16(null));
|
|
7182
|
-
});
|
|
7183
|
-
}
|
|
7184
|
-
async function detectCLIs(providerLoader, options) {
|
|
7185
|
-
const platform10 = os3.platform();
|
|
7186
|
-
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
7187
|
-
const includeVersion = options?.includeVersion !== false;
|
|
7188
|
-
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
7189
|
-
const results = await Promise.all(
|
|
7190
|
-
cliList.map(async (cli) => {
|
|
7191
|
-
try {
|
|
7192
|
-
const explicitPath = resolveCommandPath(cli.command);
|
|
7193
|
-
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(cli.command)}`);
|
|
7194
|
-
if (!pathResult) return { ...cli, installed: false };
|
|
7195
|
-
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
7196
|
-
let version;
|
|
7197
|
-
if (includeVersion) {
|
|
7198
|
-
const versionCommands = [
|
|
7199
|
-
`"${firstPath}" --version`,
|
|
7200
|
-
`"${firstPath}" -V`,
|
|
7201
|
-
`"${firstPath}" -v`,
|
|
7202
|
-
cli.versionCommand
|
|
7203
|
-
].filter((v) => !!v);
|
|
7204
|
-
try {
|
|
7205
|
-
for (const versionCommand of versionCommands) {
|
|
7206
|
-
const versionResult = await execAsync(versionCommand, 3e3);
|
|
7207
|
-
if (versionResult) {
|
|
7208
|
-
version = parseVersion(versionResult);
|
|
7209
|
-
break;
|
|
7210
|
-
}
|
|
7211
|
-
}
|
|
7212
|
-
} catch {
|
|
7213
|
-
}
|
|
7214
|
-
}
|
|
7215
|
-
return { ...cli, installed: true, version, path: firstPath };
|
|
7216
|
-
} catch {
|
|
7217
|
-
return { ...cli, installed: false };
|
|
7218
|
-
}
|
|
7219
|
-
})
|
|
7220
|
-
);
|
|
7221
|
-
return results;
|
|
7222
|
-
}
|
|
7223
|
-
async function detectCLI(cliId, providerLoader, options) {
|
|
7224
|
-
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
7225
|
-
if (providerLoader) {
|
|
7226
|
-
const cliList = providerLoader.getCliDetectionList();
|
|
7227
|
-
const target = cliList.find((c) => c.id === resolvedId);
|
|
7228
|
-
if (target) {
|
|
7229
|
-
const platform10 = os3.platform();
|
|
7230
|
-
const whichCmd = platform10 === "win32" ? "where" : "which";
|
|
7231
|
-
try {
|
|
7232
|
-
const explicitPath = resolveCommandPath(target.command);
|
|
7233
|
-
const pathResult = explicitPath || await execAsync(`${whichCmd} ${shellQuote(target.command)}`);
|
|
7234
|
-
if (!pathResult) return null;
|
|
7235
|
-
const firstPath = explicitPath || pathResult.split("\n")[0];
|
|
7236
|
-
let version;
|
|
7237
|
-
if (options?.includeVersion !== false) {
|
|
7238
|
-
const versionCommands = [
|
|
7239
|
-
`"${firstPath}" --version`,
|
|
7240
|
-
`"${firstPath}" -V`,
|
|
7241
|
-
`"${firstPath}" -v`,
|
|
7242
|
-
target.versionCommand
|
|
7243
|
-
].filter((v) => !!v);
|
|
7244
|
-
try {
|
|
7245
|
-
for (const versionCommand of versionCommands) {
|
|
7246
|
-
const versionResult = await execAsync(versionCommand, 3e3);
|
|
7247
|
-
if (versionResult) {
|
|
7248
|
-
version = parseVersion(versionResult);
|
|
7249
|
-
break;
|
|
7250
|
-
}
|
|
7251
|
-
}
|
|
7252
|
-
} catch {
|
|
7253
|
-
}
|
|
7254
|
-
}
|
|
7255
|
-
return { ...target, installed: true, version, path: firstPath };
|
|
7256
|
-
} catch {
|
|
7257
|
-
return null;
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7260
|
-
}
|
|
7261
|
-
const all = await detectCLIs(providerLoader, options);
|
|
7262
|
-
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
7263
|
-
}
|
|
7667
|
+
// src/index.ts
|
|
7668
|
+
init_cli_detector();
|
|
7264
7669
|
|
|
7265
7670
|
// src/system/host-memory.ts
|
|
7266
7671
|
var os4 = __toESM(require("os"));
|
|
@@ -16370,6 +16775,7 @@ var import_fs8 = require("fs");
|
|
|
16370
16775
|
var import_child_process6 = require("child_process");
|
|
16371
16776
|
var import_chalk = __toESM(require("chalk"));
|
|
16372
16777
|
init_provider_cli_adapter();
|
|
16778
|
+
init_cli_detector();
|
|
16373
16779
|
init_config();
|
|
16374
16780
|
|
|
16375
16781
|
// src/providers/cli-provider-instance.ts
|
|
@@ -21951,6 +22357,7 @@ function getAvailableIdeIds() {
|
|
|
21951
22357
|
|
|
21952
22358
|
// src/commands/router.ts
|
|
21953
22359
|
init_config();
|
|
22360
|
+
init_cli_detector();
|
|
21954
22361
|
init_logger();
|
|
21955
22362
|
|
|
21956
22363
|
// src/logging/command-log.ts
|
|
@@ -23097,6 +23504,209 @@ async function resolveProviderTypeFromPriority(args) {
|
|
|
23097
23504
|
}
|
|
23098
23505
|
return { error: `No usable provider detected for node '${args.nodeId}' from providerPriority: ${failed.join("; ")}` };
|
|
23099
23506
|
}
|
|
23507
|
+
var REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
23508
|
+
var REFINE_VALIDATION_TIMEOUT_MS = 12e4;
|
|
23509
|
+
var REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
23510
|
+
var REFINE_VALIDATION_SUMMARY_CHARS = 2e3;
|
|
23511
|
+
var REFINE_VALIDATION_MAX_COMMANDS = 4;
|
|
23512
|
+
function truncateValidationOutput(value) {
|
|
23513
|
+
const text = typeof value === "string" ? value : value == null ? "" : String(value);
|
|
23514
|
+
if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
|
|
23515
|
+
return `${text.slice(0, REFINE_VALIDATION_SUMMARY_CHARS)}
|
|
23516
|
+
[truncated ${text.length - REFINE_VALIDATION_SUMMARY_CHARS} chars]`;
|
|
23517
|
+
}
|
|
23518
|
+
function readPackageScripts(workspace) {
|
|
23519
|
+
try {
|
|
23520
|
+
const packageJsonPath = (0, import_path6.join)(workspace, "package.json");
|
|
23521
|
+
const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
|
|
23522
|
+
return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
|
|
23523
|
+
} catch {
|
|
23524
|
+
return {};
|
|
23525
|
+
}
|
|
23526
|
+
}
|
|
23527
|
+
function tokenizeValidationCommand(command) {
|
|
23528
|
+
const trimmed = command.trim();
|
|
23529
|
+
if (!trimmed) return null;
|
|
23530
|
+
if (/[;&|<>`$\\\n\r'\"]/.test(trimmed)) return null;
|
|
23531
|
+
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
23532
|
+
if (!tokens.length) return null;
|
|
23533
|
+
if (tokens.some((token) => !/^[A-Za-z0-9_@./:=+-]+$/.test(token))) return null;
|
|
23534
|
+
return tokens;
|
|
23535
|
+
}
|
|
23536
|
+
function scriptMatchesValidationCategory(scriptName, category) {
|
|
23537
|
+
return scriptName === category || scriptName.startsWith(`${category}:`);
|
|
23538
|
+
}
|
|
23539
|
+
function parsePackageManagerValidationCommand(rawCommand, category, scripts, source) {
|
|
23540
|
+
const tokens = tokenizeValidationCommand(rawCommand);
|
|
23541
|
+
if (!tokens) {
|
|
23542
|
+
return { rejected: { command: rawCommand, category, source, reason: "unsafe command string is not allowlisted" } };
|
|
23543
|
+
}
|
|
23544
|
+
const [binary, second, third, ...rest] = tokens;
|
|
23545
|
+
let scriptName = "";
|
|
23546
|
+
let command = binary;
|
|
23547
|
+
let args = [];
|
|
23548
|
+
if ((binary === "npm" || binary === "pnpm" || binary === "bun") && second === "run" && third) {
|
|
23549
|
+
scriptName = third;
|
|
23550
|
+
args = ["run", scriptName, ...rest];
|
|
23551
|
+
} else if (binary === "npm" && second === "test" && !third) {
|
|
23552
|
+
scriptName = "test";
|
|
23553
|
+
args = ["test"];
|
|
23554
|
+
} else if (binary === "yarn" && second === "run" && third) {
|
|
23555
|
+
scriptName = third;
|
|
23556
|
+
args = ["run", scriptName, ...rest];
|
|
23557
|
+
} else if (binary === "yarn" && second && !third) {
|
|
23558
|
+
scriptName = second;
|
|
23559
|
+
args = [scriptName];
|
|
23560
|
+
} else {
|
|
23561
|
+
return { rejected: { command: rawCommand, category, source, reason: "command is not a supported package-manager script invocation" } };
|
|
23562
|
+
}
|
|
23563
|
+
if (!scriptName || !Object.prototype.hasOwnProperty.call(scripts, scriptName)) {
|
|
23564
|
+
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script is not declared in package.json" } };
|
|
23565
|
+
}
|
|
23566
|
+
if (!scriptMatchesValidationCategory(scriptName, category)) {
|
|
23567
|
+
return { rejected: { command: rawCommand, category, source, script: scriptName, reason: "script name is outside the validation category allowlist" } };
|
|
23568
|
+
}
|
|
23569
|
+
return {
|
|
23570
|
+
command: {
|
|
23571
|
+
command,
|
|
23572
|
+
args,
|
|
23573
|
+
displayCommand: [command, ...args].join(" "),
|
|
23574
|
+
category,
|
|
23575
|
+
source
|
|
23576
|
+
}
|
|
23577
|
+
};
|
|
23578
|
+
}
|
|
23579
|
+
function collectProjectContextValidationCandidates(mesh) {
|
|
23580
|
+
const commands = mesh?.projectContext?.commands;
|
|
23581
|
+
if (!commands || typeof commands !== "object" || Array.isArray(commands)) return [];
|
|
23582
|
+
const candidates = [];
|
|
23583
|
+
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
23584
|
+
const entries = Array.isArray(commands[category]) ? commands[category] : [];
|
|
23585
|
+
for (const entry of entries) {
|
|
23586
|
+
if (typeof entry?.command !== "string") continue;
|
|
23587
|
+
candidates.push({
|
|
23588
|
+
command: entry.command,
|
|
23589
|
+
category,
|
|
23590
|
+
source: typeof entry.sourcePath === "string" ? entry.sourcePath : "projectContext.commands",
|
|
23591
|
+
confidence: typeof entry.confidence === "string" ? entry.confidence : void 0
|
|
23592
|
+
});
|
|
23593
|
+
}
|
|
23594
|
+
}
|
|
23595
|
+
return candidates.sort((a, b) => {
|
|
23596
|
+
const rank = (value) => value === "high" ? 0 : value === "medium" ? 1 : 2;
|
|
23597
|
+
return rank(a.confidence) - rank(b.confidence);
|
|
23598
|
+
});
|
|
23599
|
+
}
|
|
23600
|
+
function collectPolicyValidationCandidates(mesh) {
|
|
23601
|
+
const policy = mesh?.policy && typeof mesh.policy === "object" && !Array.isArray(mesh.policy) ? mesh.policy : {};
|
|
23602
|
+
const configured = Array.isArray(policy.validationCommands) ? policy.validationCommands : Array.isArray(policy.validationGate?.commands) ? policy.validationGate.commands : [];
|
|
23603
|
+
return configured.map((entry) => typeof entry === "string" ? { command: entry, category: "", source: "mesh.policy.validationCommands" } : entry).filter((entry) => entry && typeof entry.command === "string").map((entry) => {
|
|
23604
|
+
const commandText = entry.command.trim();
|
|
23605
|
+
const category = REFINE_VALIDATION_CATEGORIES.find((cat) => commandText.includes(` ${cat}`)) ?? "";
|
|
23606
|
+
return { command: commandText, category, source: "mesh.policy.validationCommands" };
|
|
23607
|
+
}).filter((entry) => !!entry.category);
|
|
23608
|
+
}
|
|
23609
|
+
function selectMeshRefineValidationCommands(mesh, workspace) {
|
|
23610
|
+
const scripts = readPackageScripts(workspace);
|
|
23611
|
+
const rejectedCommands = [];
|
|
23612
|
+
const selected = [];
|
|
23613
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23614
|
+
const candidates = [
|
|
23615
|
+
...collectPolicyValidationCandidates(mesh),
|
|
23616
|
+
...collectProjectContextValidationCandidates(mesh)
|
|
23617
|
+
];
|
|
23618
|
+
for (const candidate of candidates) {
|
|
23619
|
+
const parsed = parsePackageManagerValidationCommand(candidate.command, candidate.category, scripts, candidate.source);
|
|
23620
|
+
if (parsed.rejected) {
|
|
23621
|
+
rejectedCommands.push(parsed.rejected);
|
|
23622
|
+
continue;
|
|
23623
|
+
}
|
|
23624
|
+
if (!parsed.command || seen.has(parsed.command.displayCommand)) continue;
|
|
23625
|
+
selected.push(parsed.command);
|
|
23626
|
+
seen.add(parsed.command.displayCommand);
|
|
23627
|
+
if (selected.length >= REFINE_VALIDATION_MAX_COMMANDS) break;
|
|
23628
|
+
}
|
|
23629
|
+
if (!selected.length && candidates.length === 0) {
|
|
23630
|
+
for (const category of REFINE_VALIDATION_CATEGORIES) {
|
|
23631
|
+
if (!Object.prototype.hasOwnProperty.call(scripts, category)) continue;
|
|
23632
|
+
const fallback = parsePackageManagerValidationCommand(`npm run ${category}`, category, scripts, "package.json:scripts");
|
|
23633
|
+
if (fallback.command && !seen.has(fallback.command.displayCommand)) {
|
|
23634
|
+
selected.push(fallback.command);
|
|
23635
|
+
seen.add(fallback.command.displayCommand);
|
|
23636
|
+
} else if (fallback.rejected) {
|
|
23637
|
+
rejectedCommands.push(fallback.rejected);
|
|
23638
|
+
}
|
|
23639
|
+
if (selected.length >= 2) break;
|
|
23640
|
+
}
|
|
23641
|
+
}
|
|
23642
|
+
return {
|
|
23643
|
+
commands: selected,
|
|
23644
|
+
rejectedCommands,
|
|
23645
|
+
source: selected.some((command) => command.source === "mesh.policy.validationCommands") ? "mesh_policy" : selected.some((command) => command.source !== "package.json:scripts") ? "project_context" : selected.length ? "package_json_scripts" : "unavailable"
|
|
23646
|
+
};
|
|
23647
|
+
}
|
|
23648
|
+
async function runMeshRefineValidationGate(mesh, workspace) {
|
|
23649
|
+
const { execFile: execFile3 } = await import("child_process");
|
|
23650
|
+
const { promisify: promisify3 } = await import("util");
|
|
23651
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
23652
|
+
const selection = selectMeshRefineValidationCommands(mesh, workspace);
|
|
23653
|
+
const summary = {
|
|
23654
|
+
status: "skipped",
|
|
23655
|
+
required: true,
|
|
23656
|
+
commandsRun: [],
|
|
23657
|
+
rejectedCommands: selection.rejectedCommands,
|
|
23658
|
+
skippedReason: void 0,
|
|
23659
|
+
timeoutMs: REFINE_VALIDATION_TIMEOUT_MS,
|
|
23660
|
+
outputLimitBytes: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES
|
|
23661
|
+
};
|
|
23662
|
+
if (!selection.commands.length) {
|
|
23663
|
+
summary.skippedReason = "validation_unavailable: no allowlisted projectContext, mesh policy, or package.json build/test/typecheck/lint command was available";
|
|
23664
|
+
return summary;
|
|
23665
|
+
}
|
|
23666
|
+
for (const candidate of selection.commands) {
|
|
23667
|
+
const startedAt = Date.now();
|
|
23668
|
+
try {
|
|
23669
|
+
const result = await execFileAsync3(candidate.command, candidate.args, {
|
|
23670
|
+
cwd: workspace,
|
|
23671
|
+
encoding: "utf8",
|
|
23672
|
+
timeout: REFINE_VALIDATION_TIMEOUT_MS,
|
|
23673
|
+
maxBuffer: REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
|
|
23674
|
+
env: { ...process.env, CI: process.env.CI || "1" }
|
|
23675
|
+
});
|
|
23676
|
+
summary.commandsRun.push({
|
|
23677
|
+
command: candidate.command,
|
|
23678
|
+
args: candidate.args,
|
|
23679
|
+
displayCommand: candidate.displayCommand,
|
|
23680
|
+
category: candidate.category,
|
|
23681
|
+
source: candidate.source,
|
|
23682
|
+
passed: true,
|
|
23683
|
+
exitCode: 0,
|
|
23684
|
+
durationMs: Date.now() - startedAt,
|
|
23685
|
+
stdout: truncateValidationOutput(result.stdout),
|
|
23686
|
+
stderr: truncateValidationOutput(result.stderr)
|
|
23687
|
+
});
|
|
23688
|
+
} catch (error) {
|
|
23689
|
+
summary.commandsRun.push({
|
|
23690
|
+
command: candidate.command,
|
|
23691
|
+
args: candidate.args,
|
|
23692
|
+
displayCommand: candidate.displayCommand,
|
|
23693
|
+
category: candidate.category,
|
|
23694
|
+
source: candidate.source,
|
|
23695
|
+
passed: false,
|
|
23696
|
+
exitCode: typeof error?.code === "number" ? error.code : null,
|
|
23697
|
+
signal: typeof error?.signal === "string" ? error.signal : null,
|
|
23698
|
+
timedOut: error?.killed === true || /timed out/i.test(String(error?.message || "")),
|
|
23699
|
+
durationMs: Date.now() - startedAt,
|
|
23700
|
+
stdout: truncateValidationOutput(error?.stdout),
|
|
23701
|
+
stderr: truncateValidationOutput(error?.stderr || error?.message)
|
|
23702
|
+
});
|
|
23703
|
+
summary.status = "failed";
|
|
23704
|
+
return summary;
|
|
23705
|
+
}
|
|
23706
|
+
}
|
|
23707
|
+
summary.status = "passed";
|
|
23708
|
+
return summary;
|
|
23709
|
+
}
|
|
23100
23710
|
function loadYamlModule() {
|
|
23101
23711
|
return yaml;
|
|
23102
23712
|
}
|
|
@@ -23380,20 +23990,98 @@ var DaemonCommandRouter = class {
|
|
|
23380
23990
|
recoveryHint: "Inspect the worktree branch and mesh metadata before retrying cleanup."
|
|
23381
23991
|
};
|
|
23382
23992
|
}
|
|
23993
|
+
const forceFallbackConvergence = await this.getWorktreeForceCleanupConvergence({
|
|
23994
|
+
repoRoot,
|
|
23995
|
+
workspace,
|
|
23996
|
+
node: args.node
|
|
23997
|
+
});
|
|
23383
23998
|
try {
|
|
23384
|
-
const result = await removeWorktree2(repoRoot, workspace, {
|
|
23385
|
-
|
|
23999
|
+
const result = await removeWorktree2(repoRoot, workspace, {
|
|
24000
|
+
requireClean: true,
|
|
24001
|
+
allowSubmoduleForceFallback: forceFallbackConvergence.allow
|
|
24002
|
+
});
|
|
24003
|
+
return {
|
|
24004
|
+
success: true,
|
|
24005
|
+
removedPath: result.removedPath,
|
|
24006
|
+
repoRoot,
|
|
24007
|
+
...result.fallback ? {
|
|
24008
|
+
fallback: result.fallback,
|
|
24009
|
+
forced: result.forced,
|
|
24010
|
+
reason: result.reason,
|
|
24011
|
+
convergence: forceFallbackConvergence
|
|
24012
|
+
} : {}
|
|
24013
|
+
};
|
|
23386
24014
|
} catch (e) {
|
|
23387
24015
|
const message = String(e?.message || e || "worktree cleanup failed");
|
|
23388
24016
|
const dirty = message.includes("dirty worktree") || message.includes("local changes");
|
|
24017
|
+
const submoduleForceBlocked = /working trees containing submodules cannot be moved or removed/i.test(message) && !forceFallbackConvergence.allow;
|
|
23389
24018
|
return {
|
|
23390
24019
|
success: false,
|
|
23391
|
-
code: dirty ? "mesh_worktree_cleanup_dirty" : "mesh_worktree_cleanup_failed",
|
|
23392
|
-
error: message,
|
|
23393
|
-
recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure."
|
|
24020
|
+
code: dirty ? "mesh_worktree_cleanup_dirty" : submoduleForceBlocked ? "mesh_worktree_cleanup_force_fallback_blocked" : "mesh_worktree_cleanup_failed",
|
|
24021
|
+
error: submoduleForceBlocked ? `${message}; refusing --force fallback because convergence could not be verified: ${forceFallbackConvergence.error || "unknown convergence state"}` : message,
|
|
24022
|
+
recoveryHint: dirty ? "Commit, stash, or intentionally discard the worktree changes before retrying mesh_remove_node. The mesh registry entry is preserved until cleanup is safe." : submoduleForceBlocked ? "Verify the worktree branch is merged/contained in the source default branch (for example origin/main) or mark the node with a safe branchConvergence final state before retrying. The mesh registry entry is preserved." : "Inspect git worktree status/list from the source repo and retry after resolving the reported cleanup failure.",
|
|
24023
|
+
...submoduleForceBlocked ? { convergence: forceFallbackConvergence } : {}
|
|
23394
24024
|
};
|
|
23395
24025
|
}
|
|
23396
24026
|
}
|
|
24027
|
+
async getWorktreeForceCleanupConvergence(args) {
|
|
24028
|
+
const metadataStatus = typeof args.node?.branchConvergence?.status === "string" ? args.node.branchConvergence.status : "";
|
|
24029
|
+
if (metadataStatus === "merged_to_main" || metadataStatus === "cleanup_candidate") {
|
|
24030
|
+
return { allow: true, status: metadataStatus, source: "node_branch_convergence" };
|
|
24031
|
+
}
|
|
24032
|
+
const { execFile: execFile3 } = await import("child_process");
|
|
24033
|
+
const { promisify: promisify3 } = await import("util");
|
|
24034
|
+
const execFileAsync3 = promisify3(execFile3);
|
|
24035
|
+
const runGit2 = async (gitArgs, cwd) => {
|
|
24036
|
+
const { stdout } = await execFileAsync3("git", gitArgs, {
|
|
24037
|
+
cwd,
|
|
24038
|
+
encoding: "utf8",
|
|
24039
|
+
timeout: 3e4,
|
|
24040
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
24041
|
+
windowsHide: true
|
|
24042
|
+
});
|
|
24043
|
+
return String(stdout || "").trim();
|
|
24044
|
+
};
|
|
24045
|
+
let head = "";
|
|
24046
|
+
try {
|
|
24047
|
+
head = await runGit2(["rev-parse", "HEAD"], args.workspace);
|
|
24048
|
+
} catch (e) {
|
|
24049
|
+
return { allow: false, error: `could not resolve worktree HEAD: ${e?.message || e}` };
|
|
24050
|
+
}
|
|
24051
|
+
if (!head) return { allow: false, error: "worktree HEAD is empty" };
|
|
24052
|
+
const candidateRefs = [];
|
|
24053
|
+
try {
|
|
24054
|
+
const defaultBranch = await runGit2(["branch", "--show-current"], args.repoRoot);
|
|
24055
|
+
if (defaultBranch) {
|
|
24056
|
+
candidateRefs.push(defaultBranch, `origin/${defaultBranch}`);
|
|
24057
|
+
}
|
|
24058
|
+
} catch {
|
|
24059
|
+
}
|
|
24060
|
+
candidateRefs.push("origin/main", "origin/master", "main", "master");
|
|
24061
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24062
|
+
const checkedRefs = [];
|
|
24063
|
+
for (const ref of candidateRefs) {
|
|
24064
|
+
if (!ref || seen.has(ref)) continue;
|
|
24065
|
+
seen.add(ref);
|
|
24066
|
+
let commit = "";
|
|
24067
|
+
try {
|
|
24068
|
+
commit = await runGit2(["rev-parse", "--verify", `${ref}^{commit}`], args.repoRoot);
|
|
24069
|
+
} catch {
|
|
24070
|
+
continue;
|
|
24071
|
+
}
|
|
24072
|
+
checkedRefs.push(ref);
|
|
24073
|
+
try {
|
|
24074
|
+
await runGit2(["merge-base", "--is-ancestor", head, commit], args.repoRoot);
|
|
24075
|
+
return { allow: true, status: "merged_to_default_ref", source: "git_merge_base", ref };
|
|
24076
|
+
} catch {
|
|
24077
|
+
}
|
|
24078
|
+
}
|
|
24079
|
+
return {
|
|
24080
|
+
allow: false,
|
|
24081
|
+
status: metadataStatus || void 0,
|
|
24082
|
+
error: checkedRefs.length ? `worktree HEAD is not contained in checked refs: ${checkedRefs.join(", ")}` : "no default/main refs were available for convergence verification"
|
|
24083
|
+
};
|
|
24084
|
+
}
|
|
23397
24085
|
isCompletedHostedSession(record) {
|
|
23398
24086
|
return record?.lifecycle === "stopped" || record?.lifecycle === "failed" || record?.lifecycle === "interrupted";
|
|
23399
24087
|
}
|
|
@@ -24353,10 +25041,61 @@ var DaemonCommandRouter = class {
|
|
|
24353
25041
|
if (!branch) return { success: false, error: "Could not determine branch of the worktree node" };
|
|
24354
25042
|
const { stdout: baseBranchStdout } = await execFileAsync3("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
|
|
24355
25043
|
const baseBranch = baseBranchStdout.trim();
|
|
25044
|
+
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace);
|
|
25045
|
+
if (validationSummary.status === "failed") {
|
|
25046
|
+
return {
|
|
25047
|
+
success: false,
|
|
25048
|
+
code: "validation_failed",
|
|
25049
|
+
convergenceStatus: "blocked_review",
|
|
25050
|
+
error: "Refinery validation gate failed; merge/refine was not attempted.",
|
|
25051
|
+
branch,
|
|
25052
|
+
into: baseBranch,
|
|
25053
|
+
validationSummary,
|
|
25054
|
+
finalBranchConvergenceState: {
|
|
25055
|
+
branch,
|
|
25056
|
+
baseBranch,
|
|
25057
|
+
merged: false,
|
|
25058
|
+
removed: false,
|
|
25059
|
+
validation: "failed",
|
|
25060
|
+
status: "blocked_review"
|
|
25061
|
+
}
|
|
25062
|
+
};
|
|
25063
|
+
}
|
|
25064
|
+
if (validationSummary.status === "skipped") {
|
|
25065
|
+
return {
|
|
25066
|
+
success: false,
|
|
25067
|
+
code: "validation_unavailable",
|
|
25068
|
+
convergenceStatus: "blocked_review",
|
|
25069
|
+
error: "Refinery validation gate is required but no allowlisted validation command was available; merge/refine was not attempted.",
|
|
25070
|
+
branch,
|
|
25071
|
+
into: baseBranch,
|
|
25072
|
+
validationSummary,
|
|
25073
|
+
finalBranchConvergenceState: {
|
|
25074
|
+
branch,
|
|
25075
|
+
baseBranch,
|
|
25076
|
+
merged: false,
|
|
25077
|
+
removed: false,
|
|
25078
|
+
validation: "unavailable",
|
|
25079
|
+
status: "blocked_review"
|
|
25080
|
+
}
|
|
25081
|
+
};
|
|
25082
|
+
}
|
|
24356
25083
|
try {
|
|
24357
25084
|
await execFileAsync3("git", ["merge", "--no-ff", branch, "-m", `Auto-merge branch '${branch}' via Refinery`], { cwd: repoRoot, encoding: "utf8" });
|
|
24358
25085
|
} catch (e) {
|
|
24359
|
-
return {
|
|
25086
|
+
return {
|
|
25087
|
+
success: false,
|
|
25088
|
+
error: `Merge failed (conflicts?): ${e.message}`,
|
|
25089
|
+
validationSummary,
|
|
25090
|
+
finalBranchConvergenceState: {
|
|
25091
|
+
branch,
|
|
25092
|
+
baseBranch,
|
|
25093
|
+
merged: false,
|
|
25094
|
+
removed: false,
|
|
25095
|
+
validation: "passed",
|
|
25096
|
+
status: "not_mergeable"
|
|
25097
|
+
}
|
|
25098
|
+
};
|
|
24360
25099
|
}
|
|
24361
25100
|
const removeResult = await this.execute("remove_mesh_node", {
|
|
24362
25101
|
meshId,
|
|
@@ -24369,11 +25108,27 @@ var DaemonCommandRouter = class {
|
|
|
24369
25108
|
appendLedgerEntry2(meshId, {
|
|
24370
25109
|
kind: "node_removed",
|
|
24371
25110
|
nodeId,
|
|
24372
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch }
|
|
25111
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary }
|
|
24373
25112
|
});
|
|
24374
25113
|
} catch {
|
|
24375
25114
|
}
|
|
24376
|
-
return {
|
|
25115
|
+
return {
|
|
25116
|
+
success: true,
|
|
25117
|
+
merged: true,
|
|
25118
|
+
branch,
|
|
25119
|
+
into: baseBranch,
|
|
25120
|
+
removeResult,
|
|
25121
|
+
validationSummary,
|
|
25122
|
+
finalBranchConvergenceState: {
|
|
25123
|
+
branch: baseBranch,
|
|
25124
|
+
mergedBranch: branch,
|
|
25125
|
+
baseBranch,
|
|
25126
|
+
merged: true,
|
|
25127
|
+
removed: removeResult?.success !== false,
|
|
25128
|
+
validation: "passed",
|
|
25129
|
+
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged"
|
|
25130
|
+
}
|
|
25131
|
+
};
|
|
24377
25132
|
} catch (e) {
|
|
24378
25133
|
return { success: false, error: e.message };
|
|
24379
25134
|
}
|
|
@@ -24428,7 +25183,10 @@ var DaemonCommandRouter = class {
|
|
|
24428
25183
|
sessionCleanupMode,
|
|
24429
25184
|
workspace: typeof node?.workspace === "string" ? node.workspace : void 0,
|
|
24430
25185
|
daemonId: typeof node?.daemonId === "string" ? node.daemonId : void 0,
|
|
24431
|
-
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0
|
|
25186
|
+
worktreeBranch: typeof node?.worktreeBranch === "string" ? node.worktreeBranch : void 0,
|
|
25187
|
+
worktreeCleanupFallback: typeof worktreeCleanup?.fallback === "string" ? worktreeCleanup.fallback : void 0,
|
|
25188
|
+
forced: worktreeCleanup?.forced === true ? true : void 0,
|
|
25189
|
+
forceFallbackReason: typeof worktreeCleanup?.reason === "string" ? worktreeCleanup.reason : void 0
|
|
24432
25190
|
}
|
|
24433
25191
|
});
|
|
24434
25192
|
} catch {
|
|
@@ -32560,6 +33318,9 @@ function launchIDE(ide, workspacePath) {
|
|
|
32560
33318
|
}
|
|
32561
33319
|
}
|
|
32562
33320
|
|
|
33321
|
+
// src/boot/daemon-lifecycle.ts
|
|
33322
|
+
init_cli_detector();
|
|
33323
|
+
|
|
32563
33324
|
// src/sessions/registry.ts
|
|
32564
33325
|
var SessionRegistry = class {
|
|
32565
33326
|
bySessionId = /* @__PURE__ */ new Map();
|
|
@@ -32897,6 +33658,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
32897
33658
|
MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS,
|
|
32898
33659
|
MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS,
|
|
32899
33660
|
NodePtyTransportFactory,
|
|
33661
|
+
P2pRelayFailureError,
|
|
32900
33662
|
ProviderCliAdapter,
|
|
32901
33663
|
ProviderInstanceManager,
|
|
32902
33664
|
ProviderLoader,
|
|
@@ -32913,6 +33675,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
32913
33675
|
buildChatTailDeliverySignature,
|
|
32914
33676
|
buildCoordinatorSystemPrompt,
|
|
32915
33677
|
buildMachineInfo,
|
|
33678
|
+
buildP2pRelayFailurePayload,
|
|
32916
33679
|
buildPinnedGlobalInstallCommand,
|
|
32917
33680
|
buildRuntimeSystemChatMessage,
|
|
32918
33681
|
buildSessionEntries,
|
|
@@ -32927,6 +33690,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
32927
33690
|
claimNextTask,
|
|
32928
33691
|
classifyChatMessageVisibility,
|
|
32929
33692
|
classifyHotChatSessionsForSubscriptionFlush,
|
|
33693
|
+
classifyP2pRelayFailure,
|
|
32930
33694
|
clearDebugTrace,
|
|
32931
33695
|
compareGitSnapshots,
|
|
32932
33696
|
configureDebugTraceStore,
|
|
@@ -32994,6 +33758,7 @@ async function shutdownDaemonComponents(components) {
|
|
|
32994
33758
|
isInternalChatMessage,
|
|
32995
33759
|
isManagedStatusWaiting,
|
|
32996
33760
|
isManagedStatusWorking,
|
|
33761
|
+
isP2pRelayTransportFailure,
|
|
32997
33762
|
isPathInside,
|
|
32998
33763
|
isSessionHostLiveRuntime,
|
|
32999
33764
|
isSessionHostRecoverySnapshot,
|