meshy-node 0.0.8 → 0.0.9
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/README.md +1 -1
- package/main.cjs +203 -64
- package/package.json +1 -1
package/README.md
CHANGED
package/main.cjs
CHANGED
|
@@ -25022,12 +25022,24 @@ function listWorkDirFolders(workDir) {
|
|
|
25022
25022
|
|
|
25023
25023
|
// ../../packages/core/src/heartbeat.ts
|
|
25024
25024
|
var MISSED_HEARTBEAT_THRESHOLD = 3;
|
|
25025
|
+
var DEFAULT_KEEPALIVE_INTERVAL_MS = 3e3;
|
|
25026
|
+
var HEARTBEAT_SUCCESS_LOG_INTERVAL_MS = 6e4;
|
|
25027
|
+
var HEARTBEAT_FAILURE_LOG_INTERVAL_MS = 1e4;
|
|
25025
25028
|
function formatError(error) {
|
|
25026
25029
|
return error instanceof Error ? error.message : String(error);
|
|
25027
25030
|
}
|
|
25028
25031
|
function truncateForLog(value, max = 240) {
|
|
25029
25032
|
return !value || value.length <= max ? value : `${value.slice(0, max - 3)}...`;
|
|
25030
25033
|
}
|
|
25034
|
+
function describeControlRequest(request) {
|
|
25035
|
+
return {
|
|
25036
|
+
requestId: request.requestId,
|
|
25037
|
+
kind: request.kind,
|
|
25038
|
+
taskId: request.taskId,
|
|
25039
|
+
path: request.path,
|
|
25040
|
+
enabled: request.enabled
|
|
25041
|
+
};
|
|
25042
|
+
}
|
|
25031
25043
|
var HeartbeatMonitor = class {
|
|
25032
25044
|
constructor(nodeRegistry, election, eventBus, config, logger) {
|
|
25033
25045
|
this.nodeRegistry = nodeRegistry;
|
|
@@ -25080,6 +25092,8 @@ var HeartbeatMonitor = class {
|
|
|
25080
25092
|
pendingControlResponses = /* @__PURE__ */ new Map();
|
|
25081
25093
|
/** Follower-side callback for executing keepalive-delivered control requests. */
|
|
25082
25094
|
controlRequestHandler;
|
|
25095
|
+
/** Throttle repetitive heartbeat / keepalive logs while preserving periodic visibility. */
|
|
25096
|
+
throttledLogs = /* @__PURE__ */ new Map();
|
|
25083
25097
|
/** Set the callback used to query tasks assigned to a follower for pull-based dispatch. */
|
|
25084
25098
|
setGetAssignedTasks(fn) {
|
|
25085
25099
|
this.getAssignedTasks = fn;
|
|
@@ -25120,10 +25134,9 @@ var HeartbeatMonitor = class {
|
|
|
25120
25134
|
const queued = this.queuedControlRequests.get(nodeId) ?? [];
|
|
25121
25135
|
queued.push(fullRequest);
|
|
25122
25136
|
this.queuedControlRequests.set(nodeId, queued);
|
|
25123
|
-
this.log.info("queued control request
|
|
25137
|
+
this.log.info("queued keepalive control request", {
|
|
25124
25138
|
nodeId,
|
|
25125
|
-
|
|
25126
|
-
kind: fullRequest.kind
|
|
25139
|
+
...describeControlRequest(fullRequest)
|
|
25127
25140
|
});
|
|
25128
25141
|
return new Promise((resolve10, reject) => {
|
|
25129
25142
|
const timeoutId = setTimeout(() => {
|
|
@@ -25132,6 +25145,7 @@ var HeartbeatMonitor = class {
|
|
|
25132
25145
|
}, timeoutMs);
|
|
25133
25146
|
this.pendingControlResponses.set(fullRequest.requestId, {
|
|
25134
25147
|
nodeId,
|
|
25148
|
+
kind: fullRequest.kind,
|
|
25135
25149
|
resolve: resolve10,
|
|
25136
25150
|
reject,
|
|
25137
25151
|
timeoutId
|
|
@@ -25149,6 +25163,13 @@ var HeartbeatMonitor = class {
|
|
|
25149
25163
|
}
|
|
25150
25164
|
clearTimeout(pending.timeoutId);
|
|
25151
25165
|
this.pendingControlResponses.delete(response.requestId);
|
|
25166
|
+
this.log.info("received keepalive control response", {
|
|
25167
|
+
requestId: response.requestId,
|
|
25168
|
+
nodeId: pending.nodeId,
|
|
25169
|
+
kind: pending.kind,
|
|
25170
|
+
ok: response.ok,
|
|
25171
|
+
statusCode: response.statusCode
|
|
25172
|
+
});
|
|
25152
25173
|
pending.resolve(response);
|
|
25153
25174
|
return true;
|
|
25154
25175
|
}
|
|
@@ -25211,7 +25232,7 @@ var HeartbeatMonitor = class {
|
|
|
25211
25232
|
body: JSON.stringify(request)
|
|
25212
25233
|
}, void 0, {
|
|
25213
25234
|
onAttempt: ({ attempt, endpoint: targetEndpoint, totalEndpoints, timeoutMs }) => {
|
|
25214
|
-
this.log.
|
|
25235
|
+
this.log.debug("sending heartbeat request", {
|
|
25215
25236
|
nodeId: node.id,
|
|
25216
25237
|
endpoint: targetEndpoint,
|
|
25217
25238
|
attempt,
|
|
@@ -25221,16 +25242,22 @@ var HeartbeatMonitor = class {
|
|
|
25221
25242
|
});
|
|
25222
25243
|
},
|
|
25223
25244
|
onError: ({ attempt, endpoint: targetEndpoint, error, totalEndpoints }) => {
|
|
25224
|
-
this.
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
25228
|
-
|
|
25229
|
-
|
|
25230
|
-
|
|
25245
|
+
this.logWithThrottle(
|
|
25246
|
+
"warn",
|
|
25247
|
+
`heartbeat-attempt-failure:${node.id}`,
|
|
25248
|
+
HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
|
|
25249
|
+
"heartbeat request attempt failed",
|
|
25250
|
+
{
|
|
25251
|
+
nodeId: node.id,
|
|
25252
|
+
endpoint: targetEndpoint,
|
|
25253
|
+
attempt,
|
|
25254
|
+
totalEndpoints,
|
|
25255
|
+
error: formatError(error)
|
|
25256
|
+
}
|
|
25257
|
+
);
|
|
25231
25258
|
},
|
|
25232
25259
|
onResponse: ({ attempt, endpoint: targetEndpoint, response: heartbeatResponse, totalEndpoints }) => {
|
|
25233
|
-
this.log.
|
|
25260
|
+
this.log.debug("heartbeat response received", {
|
|
25234
25261
|
nodeId: node.id,
|
|
25235
25262
|
endpoint: targetEndpoint,
|
|
25236
25263
|
attempt,
|
|
@@ -25255,13 +25282,19 @@ var HeartbeatMonitor = class {
|
|
|
25255
25282
|
if (result.status !== node.status) {
|
|
25256
25283
|
this.nodeRegistry.updateStatus(node.id, result.status);
|
|
25257
25284
|
}
|
|
25258
|
-
this.
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25285
|
+
this.logWithThrottle(
|
|
25286
|
+
"info",
|
|
25287
|
+
`heartbeat-success:${node.id}`,
|
|
25288
|
+
HEARTBEAT_SUCCESS_LOG_INTERVAL_MS,
|
|
25289
|
+
"heartbeat response applied",
|
|
25290
|
+
{
|
|
25291
|
+
nodeId: node.id,
|
|
25292
|
+
endpoint,
|
|
25293
|
+
reportedStatus: result.status,
|
|
25294
|
+
load: result.load,
|
|
25295
|
+
workDirFolders: result.workDirFolders?.length ?? 0
|
|
25296
|
+
}
|
|
25297
|
+
);
|
|
25265
25298
|
return;
|
|
25266
25299
|
}
|
|
25267
25300
|
const body = truncateForLog(await response.text().catch(() => void 0));
|
|
@@ -25401,6 +25434,15 @@ var HeartbeatMonitor = class {
|
|
|
25401
25434
|
if (controlRequests.length > 0) {
|
|
25402
25435
|
this.queuedControlRequests.delete(nodeId);
|
|
25403
25436
|
}
|
|
25437
|
+
if (dispatchedTasks.length > 0 || followUpMessages.length > 0 || controlRequests.length > 0) {
|
|
25438
|
+
this.log.info("keepalive delivering queued work", {
|
|
25439
|
+
nodeId,
|
|
25440
|
+
dispatchedTaskCount: dispatchedTasks.length,
|
|
25441
|
+
followUpCount: followUpMessages.length,
|
|
25442
|
+
controlRequestCount: controlRequests.length,
|
|
25443
|
+
controlRequestKinds: controlRequests.map((request) => request.kind)
|
|
25444
|
+
});
|
|
25445
|
+
}
|
|
25404
25446
|
const response = {
|
|
25405
25447
|
leaderId: self.id,
|
|
25406
25448
|
term: this.election.getCurrentTerm(),
|
|
@@ -25438,7 +25480,7 @@ var HeartbeatMonitor = class {
|
|
|
25438
25480
|
previewOrigin: self.previewOrigin,
|
|
25439
25481
|
workDirFolders: self.workDir ? listWorkDirFolders(self.workDir) : void 0
|
|
25440
25482
|
};
|
|
25441
|
-
this.log.
|
|
25483
|
+
this.log.debug("sending keepalive request", {
|
|
25442
25484
|
nodeId: self.id,
|
|
25443
25485
|
leaderEndpoint,
|
|
25444
25486
|
status: self.status,
|
|
@@ -25453,14 +25495,22 @@ var HeartbeatMonitor = class {
|
|
|
25453
25495
|
}));
|
|
25454
25496
|
if (response.ok) {
|
|
25455
25497
|
const result = await response.json();
|
|
25456
|
-
|
|
25457
|
-
|
|
25458
|
-
|
|
25459
|
-
|
|
25460
|
-
|
|
25461
|
-
|
|
25462
|
-
|
|
25463
|
-
|
|
25498
|
+
const hasQueuedWork = (result.dispatchedTasks?.length ?? 0) > 0 || (result.followUpMessages?.length ?? 0) > 0 || (result.controlRequests?.length ?? 0) > 0;
|
|
25499
|
+
this.logWithThrottle(
|
|
25500
|
+
"info",
|
|
25501
|
+
`keepalive-success:${leaderEndpoint}`,
|
|
25502
|
+
HEARTBEAT_SUCCESS_LOG_INTERVAL_MS,
|
|
25503
|
+
"keepalive response received",
|
|
25504
|
+
{
|
|
25505
|
+
leaderId: result.leaderId,
|
|
25506
|
+
term: result.term,
|
|
25507
|
+
nodeCount: result.clusterState.nodes.length,
|
|
25508
|
+
dispatchedTaskCount: result.dispatchedTasks?.length ?? 0,
|
|
25509
|
+
followUpCount: result.followUpMessages?.length ?? 0,
|
|
25510
|
+
controlRequestCount: result.controlRequests?.length ?? 0
|
|
25511
|
+
},
|
|
25512
|
+
hasQueuedWork
|
|
25513
|
+
);
|
|
25464
25514
|
this.lastHeartbeatTime = Date.now();
|
|
25465
25515
|
this.missedCount = 0;
|
|
25466
25516
|
this.nodeRegistry.setLeader(result.leaderId, result.term);
|
|
@@ -25485,22 +25535,39 @@ var HeartbeatMonitor = class {
|
|
|
25485
25535
|
}
|
|
25486
25536
|
}
|
|
25487
25537
|
if (result.controlRequests && result.controlRequests.length > 0) {
|
|
25538
|
+
this.log.info("processing keepalive control requests", {
|
|
25539
|
+
leaderEndpoint,
|
|
25540
|
+
controlRequestCount: result.controlRequests.length,
|
|
25541
|
+
controlRequestKinds: result.controlRequests.map((request2) => request2.kind)
|
|
25542
|
+
});
|
|
25488
25543
|
await Promise.all(result.controlRequests.map(
|
|
25489
25544
|
(request2) => this.processControlRequest(leaderEndpoint, request2)
|
|
25490
25545
|
));
|
|
25491
25546
|
}
|
|
25492
25547
|
} else {
|
|
25493
|
-
this.
|
|
25494
|
-
|
|
25495
|
-
|
|
25496
|
-
|
|
25497
|
-
|
|
25548
|
+
this.logWithThrottle(
|
|
25549
|
+
"warn",
|
|
25550
|
+
`keepalive-failure:${leaderEndpoint}`,
|
|
25551
|
+
HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
|
|
25552
|
+
"keepalive request returned non-ok response",
|
|
25553
|
+
{
|
|
25554
|
+
leaderEndpoint,
|
|
25555
|
+
statusCode: response.status,
|
|
25556
|
+
body: truncateForLog(await response.text().catch(() => void 0))
|
|
25557
|
+
}
|
|
25558
|
+
);
|
|
25498
25559
|
}
|
|
25499
25560
|
} catch (err) {
|
|
25500
|
-
this.
|
|
25501
|
-
|
|
25502
|
-
|
|
25503
|
-
|
|
25561
|
+
this.logWithThrottle(
|
|
25562
|
+
"warn",
|
|
25563
|
+
`keepalive-failure:${leaderEndpoint}`,
|
|
25564
|
+
HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
|
|
25565
|
+
"keepalive request failed",
|
|
25566
|
+
{
|
|
25567
|
+
leaderEndpoint,
|
|
25568
|
+
error: formatError(err)
|
|
25569
|
+
}
|
|
25570
|
+
);
|
|
25504
25571
|
}
|
|
25505
25572
|
}
|
|
25506
25573
|
// ── Accessors ───────────────────────────────────────────────────────
|
|
@@ -25547,9 +25614,10 @@ var HeartbeatMonitor = class {
|
|
|
25547
25614
|
this.startKeepalive();
|
|
25548
25615
|
}
|
|
25549
25616
|
startKeepalive() {
|
|
25617
|
+
const keepaliveInterval = Math.min(this.config.heartbeatInterval, DEFAULT_KEEPALIVE_INTERVAL_MS);
|
|
25550
25618
|
this.keepaliveTimer = setInterval(() => {
|
|
25551
25619
|
void this.sendKeepalive();
|
|
25552
|
-
},
|
|
25620
|
+
}, keepaliveInterval);
|
|
25553
25621
|
}
|
|
25554
25622
|
resetFollowerTimer() {
|
|
25555
25623
|
if (this.followerTimer !== null) {
|
|
@@ -25575,16 +25643,22 @@ var HeartbeatMonitor = class {
|
|
|
25575
25643
|
const newCount = current + 1;
|
|
25576
25644
|
this.followerMissedMap.set(node.id, newCount);
|
|
25577
25645
|
const everReached = this.followerEverReachedMap.get(node.id) ?? false;
|
|
25578
|
-
this.
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
25587
|
-
|
|
25646
|
+
this.logWithThrottle(
|
|
25647
|
+
"warn",
|
|
25648
|
+
`heartbeat-miss:${node.id}`,
|
|
25649
|
+
HEARTBEAT_FAILURE_LOG_INTERVAL_MS,
|
|
25650
|
+
"recorded heartbeat miss",
|
|
25651
|
+
{
|
|
25652
|
+
nodeId: node.id,
|
|
25653
|
+
misses: newCount,
|
|
25654
|
+
everReached,
|
|
25655
|
+
endpoint: details.endpoint,
|
|
25656
|
+
statusCode: details.statusCode,
|
|
25657
|
+
reason: details.reason,
|
|
25658
|
+
error: details.error,
|
|
25659
|
+
body: details.body
|
|
25660
|
+
}
|
|
25661
|
+
);
|
|
25588
25662
|
if (newCount >= MISSED_HEARTBEAT_THRESHOLD) {
|
|
25589
25663
|
const freshNode = this.nodeRegistry.getNode(node.id);
|
|
25590
25664
|
if (freshNode) {
|
|
@@ -25634,6 +25708,10 @@ var HeartbeatMonitor = class {
|
|
|
25634
25708
|
}
|
|
25635
25709
|
}
|
|
25636
25710
|
async processControlRequest(leaderEndpoint, request) {
|
|
25711
|
+
this.log.info("processing keepalive control request", {
|
|
25712
|
+
leaderEndpoint,
|
|
25713
|
+
...describeControlRequest(request)
|
|
25714
|
+
});
|
|
25637
25715
|
let response;
|
|
25638
25716
|
if (!this.controlRequestHandler) {
|
|
25639
25717
|
response = {
|
|
@@ -25668,6 +25746,12 @@ var HeartbeatMonitor = class {
|
|
|
25668
25746
|
};
|
|
25669
25747
|
}
|
|
25670
25748
|
}
|
|
25749
|
+
this.log.info("completed keepalive control request", {
|
|
25750
|
+
leaderEndpoint,
|
|
25751
|
+
...describeControlRequest(request),
|
|
25752
|
+
ok: response.ok,
|
|
25753
|
+
statusCode: response.statusCode
|
|
25754
|
+
});
|
|
25671
25755
|
try {
|
|
25672
25756
|
const res = await fetch(`${leaderEndpoint}/api/worker/control-response`, applyRequestAuthHeaders({
|
|
25673
25757
|
method: "POST",
|
|
@@ -25691,6 +25775,21 @@ var HeartbeatMonitor = class {
|
|
|
25691
25775
|
});
|
|
25692
25776
|
}
|
|
25693
25777
|
}
|
|
25778
|
+
logWithThrottle(level, key, intervalMs, message, data, force = false) {
|
|
25779
|
+
const now = Date.now();
|
|
25780
|
+
const existing = this.throttledLogs.get(key);
|
|
25781
|
+
if (existing && !force && now - existing.lastLoggedAt < intervalMs) {
|
|
25782
|
+
existing.suppressedCount += 1;
|
|
25783
|
+
return;
|
|
25784
|
+
}
|
|
25785
|
+
const mergedData = existing && existing.suppressedCount > 0 ? { ...data, suppressedCount: existing.suppressedCount } : data;
|
|
25786
|
+
this.throttledLogs.set(key, { lastLoggedAt: now, suppressedCount: 0 });
|
|
25787
|
+
if (level === "info") {
|
|
25788
|
+
this.log.info(message, mergedData);
|
|
25789
|
+
return;
|
|
25790
|
+
}
|
|
25791
|
+
this.log.warn(message, mergedData);
|
|
25792
|
+
}
|
|
25694
25793
|
};
|
|
25695
25794
|
|
|
25696
25795
|
// ../../packages/core/src/data-router.ts
|
|
@@ -32594,7 +32693,16 @@ async function createPreviewSessionPayload(deps, taskId, entryPath) {
|
|
|
32594
32693
|
};
|
|
32595
32694
|
}
|
|
32596
32695
|
async function executeWorkerControlRequest(deps, request) {
|
|
32696
|
+
const log = deps.logger.child("worker-control");
|
|
32697
|
+
log.info("received worker control request", {
|
|
32698
|
+
requestId: request.requestId,
|
|
32699
|
+
kind: request.kind,
|
|
32700
|
+
taskId: request.taskId,
|
|
32701
|
+
path: request.path,
|
|
32702
|
+
enabled: request.enabled
|
|
32703
|
+
});
|
|
32597
32704
|
try {
|
|
32705
|
+
let response;
|
|
32598
32706
|
switch (request.kind) {
|
|
32599
32707
|
case "devtunnel": {
|
|
32600
32708
|
assertCanChangeNodeDevTunnel(deps.taskEngine, deps.nodeRegistry.getSelf().id);
|
|
@@ -32603,22 +32711,24 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32603
32711
|
throw new MeshyError("VALIDATION_ERROR", "DevTunnel control not available", 501);
|
|
32604
32712
|
}
|
|
32605
32713
|
const devtunnelEndpoint = await deps.enableDevTunnel();
|
|
32606
|
-
|
|
32714
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32607
32715
|
ok: true,
|
|
32608
32716
|
enabled: true,
|
|
32609
32717
|
devtunnelEndpoint,
|
|
32610
32718
|
dashboardOrigin: deps.nodeRegistry.getSelf().dashboardOrigin
|
|
32611
32719
|
});
|
|
32720
|
+
break;
|
|
32612
32721
|
}
|
|
32613
32722
|
if (!deps.disableDevTunnel) {
|
|
32614
32723
|
throw new MeshyError("VALIDATION_ERROR", "DevTunnel control not available", 501);
|
|
32615
32724
|
}
|
|
32616
32725
|
await deps.disableDevTunnel();
|
|
32617
|
-
|
|
32726
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32618
32727
|
ok: true,
|
|
32619
32728
|
enabled: false,
|
|
32620
32729
|
dashboardOrigin: deps.nodeRegistry.getSelf().dashboardOrigin
|
|
32621
32730
|
});
|
|
32731
|
+
break;
|
|
32622
32732
|
}
|
|
32623
32733
|
case "task-cancel": {
|
|
32624
32734
|
const taskId = request.taskId ?? "";
|
|
@@ -32630,7 +32740,7 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32630
32740
|
missingOk: true,
|
|
32631
32741
|
terminalOk: true
|
|
32632
32742
|
});
|
|
32633
|
-
|
|
32743
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32634
32744
|
ok: true,
|
|
32635
32745
|
taskId,
|
|
32636
32746
|
cancelled: result.cancelled,
|
|
@@ -32638,11 +32748,12 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32638
32748
|
missing: result.missing,
|
|
32639
32749
|
terminal: result.terminal
|
|
32640
32750
|
});
|
|
32751
|
+
break;
|
|
32641
32752
|
}
|
|
32642
32753
|
case "task-output-summary": {
|
|
32643
32754
|
const task = getTask(deps.taskEngine, request.taskId ?? "");
|
|
32644
32755
|
if (!task.effectiveProjectPath) {
|
|
32645
|
-
|
|
32756
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32646
32757
|
taskId: task.id,
|
|
32647
32758
|
requestedProject: task.project,
|
|
32648
32759
|
effectiveProjectPath: null,
|
|
@@ -32650,8 +32761,9 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32650
32761
|
available: false,
|
|
32651
32762
|
summary: null
|
|
32652
32763
|
});
|
|
32764
|
+
break;
|
|
32653
32765
|
}
|
|
32654
|
-
|
|
32766
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32655
32767
|
taskId: task.id,
|
|
32656
32768
|
requestedProject: task.project,
|
|
32657
32769
|
effectiveProjectPath: task.effectiveProjectPath,
|
|
@@ -32659,15 +32771,17 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32659
32771
|
available: true,
|
|
32660
32772
|
summary: getOutputSummary(task.effectiveProjectPath)
|
|
32661
32773
|
});
|
|
32774
|
+
break;
|
|
32662
32775
|
}
|
|
32663
32776
|
case "task-output-tree": {
|
|
32664
32777
|
const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
|
|
32665
32778
|
try {
|
|
32666
|
-
|
|
32779
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32667
32780
|
rootPath,
|
|
32668
32781
|
currentPath: request.path ?? ".",
|
|
32669
32782
|
entries: listDirectory(rootPath, request.path ?? ".")
|
|
32670
32783
|
});
|
|
32784
|
+
break;
|
|
32671
32785
|
} catch (err) {
|
|
32672
32786
|
if (err instanceof Error && err.message === "Path traversal detected") {
|
|
32673
32787
|
throw new MeshyError("VALIDATION_ERROR", "Invalid directory path", 400);
|
|
@@ -32679,10 +32793,11 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32679
32793
|
const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
|
|
32680
32794
|
try {
|
|
32681
32795
|
const filePath = request.path ?? "";
|
|
32682
|
-
|
|
32796
|
+
response = jsonResponse(request.requestId, 200, {
|
|
32683
32797
|
...readFileContent(rootPath, filePath),
|
|
32684
32798
|
downloadUrl: `/api/tasks/${request.taskId}/output/download?path=${encodeURIComponent(filePath)}`
|
|
32685
32799
|
});
|
|
32800
|
+
break;
|
|
32686
32801
|
} catch (err) {
|
|
32687
32802
|
if (err instanceof Error && err.message === "Path traversal detected") {
|
|
32688
32803
|
throw new MeshyError("VALIDATION_ERROR", "Invalid file path", 400);
|
|
@@ -32702,11 +32817,12 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32702
32817
|
}
|
|
32703
32818
|
const { mimeType } = classifyFile(absolutePath);
|
|
32704
32819
|
const fileName = path13.basename(absolutePath).replace(/"/g, "");
|
|
32705
|
-
|
|
32820
|
+
response = binaryResponse(request.requestId, 200, fs12.readFileSync(absolutePath), {
|
|
32706
32821
|
"Content-Type": mimeType,
|
|
32707
32822
|
"Content-Disposition": `inline; filename="${fileName}"`,
|
|
32708
32823
|
"Cache-Control": "no-cache"
|
|
32709
32824
|
});
|
|
32825
|
+
break;
|
|
32710
32826
|
} catch (err) {
|
|
32711
32827
|
if (err instanceof MeshyError) throw err;
|
|
32712
32828
|
if (err instanceof Error && err.message === "Path traversal detected") {
|
|
@@ -32717,18 +32833,41 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
32717
32833
|
}
|
|
32718
32834
|
case "task-output-diff": {
|
|
32719
32835
|
const rootPath = getTaskOutputRoot(deps.taskEngine, request.taskId ?? "");
|
|
32720
|
-
|
|
32836
|
+
response = jsonResponse(request.requestId, 200, getGitDiff(rootPath));
|
|
32837
|
+
break;
|
|
32721
32838
|
}
|
|
32722
32839
|
case "task-preview-session": {
|
|
32723
|
-
|
|
32840
|
+
response = jsonResponse(
|
|
32724
32841
|
request.requestId,
|
|
32725
32842
|
200,
|
|
32726
32843
|
await createPreviewSessionPayload(deps, request.taskId ?? "", request.path)
|
|
32727
32844
|
);
|
|
32845
|
+
break;
|
|
32728
32846
|
}
|
|
32729
32847
|
}
|
|
32848
|
+
log.info("completed worker control request", {
|
|
32849
|
+
requestId: request.requestId,
|
|
32850
|
+
kind: request.kind,
|
|
32851
|
+
taskId: request.taskId,
|
|
32852
|
+
path: request.path,
|
|
32853
|
+
enabled: request.enabled,
|
|
32854
|
+
ok: response.ok,
|
|
32855
|
+
statusCode: response.statusCode
|
|
32856
|
+
});
|
|
32857
|
+
return response;
|
|
32730
32858
|
} catch (err) {
|
|
32731
|
-
|
|
32859
|
+
const response = errorResponse(request.requestId, err);
|
|
32860
|
+
log.warn("worker control request failed", {
|
|
32861
|
+
requestId: request.requestId,
|
|
32862
|
+
kind: request.kind,
|
|
32863
|
+
taskId: request.taskId,
|
|
32864
|
+
path: request.path,
|
|
32865
|
+
enabled: request.enabled,
|
|
32866
|
+
ok: response.ok,
|
|
32867
|
+
statusCode: response.statusCode,
|
|
32868
|
+
error: err instanceof Error ? err.message : String(err)
|
|
32869
|
+
});
|
|
32870
|
+
return response;
|
|
32732
32871
|
}
|
|
32733
32872
|
}
|
|
32734
32873
|
function sendWorkerControlResponse(res, response) {
|
|
@@ -33806,13 +33945,13 @@ function createWorkerRoutes() {
|
|
|
33806
33945
|
const { heartbeat, logger: rootLogger } = req.app.locals.deps;
|
|
33807
33946
|
const log = rootLogger.child("worker/heartbeat");
|
|
33808
33947
|
const body = req.body;
|
|
33809
|
-
log.
|
|
33948
|
+
log.debug("received worker heartbeat API call", {
|
|
33810
33949
|
leaderId: body.leaderId,
|
|
33811
33950
|
term: body.term,
|
|
33812
33951
|
nodeCount: body.clusterState?.nodes?.length ?? 0
|
|
33813
33952
|
});
|
|
33814
33953
|
const response = heartbeat.handleHeartbeat(body);
|
|
33815
|
-
log.
|
|
33954
|
+
log.debug("completed worker heartbeat API call", {
|
|
33816
33955
|
nodeId: response.nodeId,
|
|
33817
33956
|
status: response.status,
|
|
33818
33957
|
load: response.load
|
|
@@ -33823,13 +33962,13 @@ function createWorkerRoutes() {
|
|
|
33823
33962
|
const { heartbeat, logger: rootLogger } = req.app.locals.deps;
|
|
33824
33963
|
const log = rootLogger.child("worker/keepalive");
|
|
33825
33964
|
const body = req.body;
|
|
33826
|
-
log.
|
|
33965
|
+
log.debug("received worker keepalive API call", {
|
|
33827
33966
|
nodeId: body.nodeId,
|
|
33828
33967
|
status: body.status,
|
|
33829
33968
|
endpoint: body.endpoint
|
|
33830
33969
|
});
|
|
33831
33970
|
const response = heartbeat.handleKeepalive(body);
|
|
33832
|
-
log.
|
|
33971
|
+
log.debug("completed worker keepalive API call", {
|
|
33833
33972
|
leaderId: response.leaderId,
|
|
33834
33973
|
term: response.term,
|
|
33835
33974
|
nodeCount: response.clusterState.nodes.length,
|
|
@@ -34467,7 +34606,7 @@ function createDefaultConfig(storagePath = resolveDefaultStoragePath(), nodeName
|
|
|
34467
34606
|
return {
|
|
34468
34607
|
node: { name: nodeName, port: DEFAULT_NODE_PORT },
|
|
34469
34608
|
transport: { type: "direct" },
|
|
34470
|
-
cluster: { seeds: [], heartbeatInterval:
|
|
34609
|
+
cluster: { seeds: [], heartbeatInterval: 3e3, electionTimeout: 36e3, apiKey: "" },
|
|
34471
34610
|
storage: { path: storagePath }
|
|
34472
34611
|
};
|
|
34473
34612
|
}
|