meshy-node 0.3.8 → 0.4.0
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/dashboard/assets/DashboardPage-DIxwHSA7.js +124 -0
- package/dashboard/assets/{DashboardShared-C3sx-Aj3.js → DashboardShared-BPoKdeND.js} +3 -3
- package/dashboard/assets/{DiffTab-DF380dMh.js → DiffTab-Ds8uRZxS.js} +3 -3
- package/dashboard/assets/{FilesTab-DZ2CIfBe.js → FilesTab-xmmeWdn_.js} +1 -1
- package/dashboard/assets/{PreviewTab-DGzp5vs_.js → PreviewTab-CDc000ji.js} +5 -10
- package/dashboard/assets/{SharedConversationPage-6Y6HiGBv.js → SharedConversationPage-C95MHBQu.js} +3 -3
- package/dashboard/assets/{file-CkjhxpTi.js → file-CdPV4WjM.js} +1 -1
- package/dashboard/assets/{folder-DcAuKKJN.js → folder-Dd4BWbi-.js} +1 -1
- package/dashboard/assets/index-Ch6zIZ6Y.css +1 -0
- package/dashboard/assets/{index-U21aiFFx.js → index-n69fx3B-.js} +7 -7
- package/dashboard/assets/input-DtRxMGzO.js +6 -0
- package/dashboard/index.html +2 -2
- package/main.cjs +312 -204
- package/package.json +1 -1
- package/runtime-metadata.json +5 -5
- package/dashboard/assets/DashboardPage-BGDwH7LG.js +0 -119
- package/dashboard/assets/index-DfiptIs_.css +0 -1
- package/dashboard/assets/input-BA2z1FFh.js +0 -1
package/main.cjs
CHANGED
|
@@ -33297,6 +33297,7 @@ function createNodeMessage(kind, payload, options = {}) {
|
|
|
33297
33297
|
}
|
|
33298
33298
|
|
|
33299
33299
|
// ../../packages/core/src/messaging/node-message-client.ts
|
|
33300
|
+
var RETRYABLE_NODE_MESSAGE_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
33300
33301
|
var NodeMessageClient = class {
|
|
33301
33302
|
constructor(options = {}) {
|
|
33302
33303
|
this.options = options;
|
|
@@ -33335,6 +33336,16 @@ var NodeMessageClient = class {
|
|
|
33335
33336
|
throw error;
|
|
33336
33337
|
}
|
|
33337
33338
|
if (!response.ok) {
|
|
33339
|
+
if (RETRYABLE_NODE_MESSAGE_STATUSES.has(response.status) && this.options.heartbeat?.enqueueNodeMessage) {
|
|
33340
|
+
this.log?.warn("direct node message delivery returned retryable status; queued for keepalive", {
|
|
33341
|
+
nodeId: node.id,
|
|
33342
|
+
messageId: message.id,
|
|
33343
|
+
kind: message.kind,
|
|
33344
|
+
statusCode: response.status
|
|
33345
|
+
});
|
|
33346
|
+
this.options.heartbeat.enqueueNodeMessage(node.id, message);
|
|
33347
|
+
return { queued: true };
|
|
33348
|
+
}
|
|
33338
33349
|
throw new Error(await formatHttpError(response));
|
|
33339
33350
|
}
|
|
33340
33351
|
return { queued: false };
|
|
@@ -34558,6 +34569,10 @@ var NODE_KIND_BY_LEGACY = {
|
|
|
34558
34569
|
"node-workdir-tree": "node.workdir.tree",
|
|
34559
34570
|
"node-workdir-branch-info": "node.workdir.branch-info",
|
|
34560
34571
|
"node-workdir-branch-create": "node.workdir.branch-create",
|
|
34572
|
+
"node-terminal-session-start": "node.terminal.session.start",
|
|
34573
|
+
"node-terminal-session-list": "node.terminal.session.list",
|
|
34574
|
+
"node-terminal-session-get": "node.terminal.session.get",
|
|
34575
|
+
"node-terminal-session-stop": "node.terminal.session.stop",
|
|
34561
34576
|
"node-sessions-list": "node.sessions.list",
|
|
34562
34577
|
devtunnel: "node.transport.set",
|
|
34563
34578
|
"node-agent-upgrade": "node.agent.upgrade",
|
|
@@ -43063,6 +43078,7 @@ var NativeSessionSummarySchema = external_exports.object({
|
|
|
43063
43078
|
summary: external_exports.string(),
|
|
43064
43079
|
updatedAt: external_exports.string().nullable()
|
|
43065
43080
|
});
|
|
43081
|
+
var NodeTerminalSessionStatusSchema = external_exports.enum(["running", "exited", "failed", "stopped"]);
|
|
43066
43082
|
var NodeListQuery = external_exports.object({
|
|
43067
43083
|
status: external_exports.enum(["online", "busy", "offline"]).optional(),
|
|
43068
43084
|
capability: external_exports.string().optional()
|
|
@@ -43096,7 +43112,7 @@ var NodeNativeSessionsQuery = external_exports.object({
|
|
|
43096
43112
|
agent: external_exports.enum(["codex", "claudecode"]),
|
|
43097
43113
|
limit: external_exports.coerce.number().int().min(1).max(100).default(50)
|
|
43098
43114
|
});
|
|
43099
|
-
var
|
|
43115
|
+
var NodeTerminalSessionStartBody = external_exports.object({
|
|
43100
43116
|
command: external_exports.string().trim().min(1),
|
|
43101
43117
|
cwd: external_exports.string().trim().min(1).optional()
|
|
43102
43118
|
});
|
|
@@ -43141,18 +43157,28 @@ var NodeNativeSessionsResponse = external_exports.object({
|
|
|
43141
43157
|
agent: external_exports.enum(["codex", "claudecode"]),
|
|
43142
43158
|
sessions: external_exports.array(NativeSessionSummarySchema)
|
|
43143
43159
|
});
|
|
43144
|
-
var
|
|
43160
|
+
var NodeTerminalSessionResponse = external_exports.object({
|
|
43161
|
+
id: external_exports.string(),
|
|
43145
43162
|
nodeId: external_exports.string(),
|
|
43146
43163
|
cwd: external_exports.string(),
|
|
43147
43164
|
command: external_exports.string(),
|
|
43165
|
+
pid: external_exports.number().int().nullable(),
|
|
43166
|
+
status: NodeTerminalSessionStatusSchema,
|
|
43148
43167
|
exitCode: external_exports.number().int().nullable(),
|
|
43168
|
+
signal: external_exports.string().nullable(),
|
|
43149
43169
|
stdout: external_exports.string(),
|
|
43150
43170
|
stderr: external_exports.string(),
|
|
43171
|
+
startedAt: external_exports.number(),
|
|
43172
|
+
updatedAt: external_exports.number(),
|
|
43173
|
+
completedAt: external_exports.number().optional(),
|
|
43151
43174
|
durationMs: external_exports.number().int().min(0),
|
|
43152
|
-
timedOut: external_exports.boolean(),
|
|
43153
43175
|
stdoutTruncated: external_exports.boolean(),
|
|
43154
43176
|
stderrTruncated: external_exports.boolean()
|
|
43155
43177
|
});
|
|
43178
|
+
var NodeTerminalSessionListResponse = external_exports.object({
|
|
43179
|
+
nodeId: external_exports.string(),
|
|
43180
|
+
sessions: external_exports.array(NodeTerminalSessionResponse)
|
|
43181
|
+
});
|
|
43156
43182
|
var UpdateNodeBody = external_exports.object({
|
|
43157
43183
|
name: external_exports.string().min(1).optional(),
|
|
43158
43184
|
capabilities: external_exports.array(external_exports.string()).optional()
|
|
@@ -45163,7 +45189,10 @@ var LEGACY_KIND_BY_NODE_MESSAGE = {
|
|
|
45163
45189
|
"node.workdir.tree": "node-workdir-tree",
|
|
45164
45190
|
"node.workdir.branch-info": "node-workdir-branch-info",
|
|
45165
45191
|
"node.workdir.branch-create": "node-workdir-branch-create",
|
|
45166
|
-
"node.terminal.
|
|
45192
|
+
"node.terminal.session.start": "node-terminal-session-start",
|
|
45193
|
+
"node.terminal.session.list": "node-terminal-session-list",
|
|
45194
|
+
"node.terminal.session.get": "node-terminal-session-get",
|
|
45195
|
+
"node.terminal.session.stop": "node-terminal-session-stop",
|
|
45167
45196
|
"node.sessions.list": "node-sessions-list",
|
|
45168
45197
|
"node.transport.set": "devtunnel",
|
|
45169
45198
|
"node.agent.upgrade": "node-agent-upgrade",
|
|
@@ -45179,9 +45208,13 @@ var LEGACY_KIND_BY_NODE_MESSAGE = {
|
|
|
45179
45208
|
function canRequestNodeMessage(heartbeat) {
|
|
45180
45209
|
return !!(heartbeat?.requestNodeMessage || heartbeat?.requestWorkerControl);
|
|
45181
45210
|
}
|
|
45182
|
-
function requestFallbackNodeMessage(heartbeat, nodeId, message) {
|
|
45183
|
-
if (heartbeat.requestNodeMessage)
|
|
45184
|
-
|
|
45211
|
+
function requestFallbackNodeMessage(heartbeat, nodeId, message, timeoutMs) {
|
|
45212
|
+
if (heartbeat.requestNodeMessage) {
|
|
45213
|
+
return timeoutMs === void 0 ? heartbeat.requestNodeMessage(nodeId, message) : heartbeat.requestNodeMessage(nodeId, message, timeoutMs);
|
|
45214
|
+
}
|
|
45215
|
+
if (heartbeat.requestWorkerControl) {
|
|
45216
|
+
return timeoutMs === void 0 ? heartbeat.requestWorkerControl(nodeId, toLegacyWorkerControl2(message)) : heartbeat.requestWorkerControl(nodeId, toLegacyWorkerControl2(message), timeoutMs);
|
|
45217
|
+
}
|
|
45185
45218
|
throw new Error("Node message fallback is not available");
|
|
45186
45219
|
}
|
|
45187
45220
|
function toLegacyWorkerControl2(message) {
|
|
@@ -45442,16 +45475,18 @@ async function maybeProxyReadToLeader(req, res, options = {}) {
|
|
|
45442
45475
|
}
|
|
45443
45476
|
}
|
|
45444
45477
|
|
|
45445
|
-
// ../../packages/api/src/node/node-terminal-service.ts
|
|
45478
|
+
// ../../packages/api/src/node/node-terminal-session-service.ts
|
|
45446
45479
|
var import_node_child_process10 = require("child_process");
|
|
45480
|
+
var import_node_crypto8 = require("crypto");
|
|
45481
|
+
|
|
45482
|
+
// ../../packages/api/src/node/node-terminal-service.ts
|
|
45447
45483
|
var fs17 = __toESM(require("fs"), 1);
|
|
45448
45484
|
var path18 = __toESM(require("path"), 1);
|
|
45449
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
45450
45485
|
var DEFAULT_OUTPUT_LIMIT_BYTES = 64 * 1024;
|
|
45451
45486
|
function isAbsolutePath2(value) {
|
|
45452
45487
|
return path18.isAbsolute(value) || /^[A-Za-z]:[\/]/.test(value);
|
|
45453
45488
|
}
|
|
45454
|
-
function
|
|
45489
|
+
function resolveNodeTerminalCwd(nodeId, rootPath, cwd) {
|
|
45455
45490
|
if (!rootPath) {
|
|
45456
45491
|
throw new MeshyError("VALIDATION_ERROR", `Node ${nodeId} does not expose a working directory`, 400);
|
|
45457
45492
|
}
|
|
@@ -45462,74 +45497,170 @@ function resolveCommandCwd(nodeId, rootPath, cwd) {
|
|
|
45462
45497
|
}
|
|
45463
45498
|
return resolved;
|
|
45464
45499
|
}
|
|
45465
|
-
|
|
45466
|
-
|
|
45467
|
-
|
|
45500
|
+
|
|
45501
|
+
// ../../packages/api/src/node/node-terminal-session-service.ts
|
|
45502
|
+
var MAX_COMPLETED_SESSIONS = 10;
|
|
45503
|
+
function createOutputBuffer() {
|
|
45504
|
+
return { chunks: [], bytes: 0, observedBytes: 0 };
|
|
45505
|
+
}
|
|
45506
|
+
function appendTail(buffer, chunk, limitBytes) {
|
|
45507
|
+
buffer.observedBytes += chunk.length;
|
|
45508
|
+
const nextChunk = chunk.length > limitBytes ? chunk.subarray(chunk.length - limitBytes) : chunk;
|
|
45509
|
+
buffer.chunks.push(nextChunk);
|
|
45510
|
+
buffer.bytes += nextChunk.length;
|
|
45511
|
+
while (buffer.bytes > limitBytes && buffer.chunks.length > 0) {
|
|
45512
|
+
const first = buffer.chunks[0];
|
|
45513
|
+
if (!first) break;
|
|
45514
|
+
const overflow = buffer.bytes - limitBytes;
|
|
45515
|
+
if (first.length <= overflow) {
|
|
45516
|
+
buffer.chunks.shift();
|
|
45517
|
+
buffer.bytes -= first.length;
|
|
45518
|
+
continue;
|
|
45519
|
+
}
|
|
45520
|
+
buffer.chunks[0] = first.subarray(overflow);
|
|
45521
|
+
buffer.bytes -= overflow;
|
|
45468
45522
|
}
|
|
45469
|
-
const remaining = limitBytes - currentBytes;
|
|
45470
|
-
chunks.push(chunk.length <= remaining ? chunk : chunk.subarray(0, remaining));
|
|
45471
|
-
return currentBytes + chunk.length;
|
|
45472
45523
|
}
|
|
45473
|
-
function
|
|
45524
|
+
function readBuffer(buffer, limitBytes) {
|
|
45474
45525
|
return {
|
|
45475
|
-
text: Buffer.concat(chunks).toString("utf8"),
|
|
45476
|
-
truncated: observedBytes > limitBytes
|
|
45526
|
+
text: Buffer.concat(buffer.chunks, buffer.bytes).toString("utf8"),
|
|
45527
|
+
truncated: buffer.observedBytes > limitBytes
|
|
45477
45528
|
};
|
|
45478
45529
|
}
|
|
45479
|
-
function
|
|
45480
|
-
|
|
45481
|
-
|
|
45482
|
-
|
|
45530
|
+
function cloneSession(session) {
|
|
45531
|
+
return { ...session };
|
|
45532
|
+
}
|
|
45533
|
+
function killSessionProcess(state3, signal) {
|
|
45534
|
+
const child = state3.process;
|
|
45535
|
+
if (!child) return;
|
|
45536
|
+
const pid = child.pid;
|
|
45537
|
+
if (process.platform !== "win32" && typeof pid === "number") {
|
|
45538
|
+
try {
|
|
45539
|
+
process.kill(-pid, signal);
|
|
45540
|
+
return;
|
|
45541
|
+
} catch {
|
|
45542
|
+
}
|
|
45483
45543
|
}
|
|
45484
|
-
|
|
45485
|
-
|
|
45486
|
-
|
|
45487
|
-
|
|
45488
|
-
|
|
45489
|
-
|
|
45490
|
-
|
|
45491
|
-
|
|
45492
|
-
|
|
45493
|
-
|
|
45544
|
+
try {
|
|
45545
|
+
child.kill(signal);
|
|
45546
|
+
} catch {
|
|
45547
|
+
}
|
|
45548
|
+
}
|
|
45549
|
+
var NodeTerminalSessionService = class {
|
|
45550
|
+
sessions = /* @__PURE__ */ new Map();
|
|
45551
|
+
nextSequence = 0;
|
|
45552
|
+
start(nodeId, rootPath, command, options = {}) {
|
|
45553
|
+
const normalizedCommand = command.trim();
|
|
45554
|
+
if (!normalizedCommand) {
|
|
45555
|
+
throw new MeshyError("VALIDATION_ERROR", "Command must not be empty", 400);
|
|
45556
|
+
}
|
|
45557
|
+
const cwd = resolveNodeTerminalCwd(nodeId, rootPath, options.cwd);
|
|
45558
|
+
const startedAt = Date.now();
|
|
45494
45559
|
const child = (0, import_node_child_process10.spawn)(normalizedCommand, {
|
|
45495
45560
|
cwd,
|
|
45496
45561
|
shell: true,
|
|
45497
45562
|
windowsHide: true,
|
|
45498
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
45563
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
45564
|
+
detached: process.platform !== "win32"
|
|
45499
45565
|
});
|
|
45500
|
-
const
|
|
45501
|
-
|
|
45502
|
-
|
|
45503
|
-
|
|
45566
|
+
const session = {
|
|
45567
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
45568
|
+
nodeId,
|
|
45569
|
+
cwd,
|
|
45570
|
+
command: normalizedCommand,
|
|
45571
|
+
pid: child.pid ?? null,
|
|
45572
|
+
status: "running",
|
|
45573
|
+
exitCode: null,
|
|
45574
|
+
signal: null,
|
|
45575
|
+
stdout: "",
|
|
45576
|
+
stderr: "",
|
|
45577
|
+
startedAt,
|
|
45578
|
+
updatedAt: startedAt,
|
|
45579
|
+
durationMs: 0,
|
|
45580
|
+
stdoutTruncated: false,
|
|
45581
|
+
stderrTruncated: false
|
|
45582
|
+
};
|
|
45583
|
+
const state3 = {
|
|
45584
|
+
session,
|
|
45585
|
+
process: child,
|
|
45586
|
+
stdout: createOutputBuffer(),
|
|
45587
|
+
stderr: createOutputBuffer(),
|
|
45588
|
+
sequence: this.nextSequence++
|
|
45589
|
+
};
|
|
45590
|
+
const outputLimitBytes = options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES;
|
|
45591
|
+
this.sessions.set(session.id, state3);
|
|
45504
45592
|
child.stdout?.on("data", (chunk) => {
|
|
45505
|
-
|
|
45593
|
+
appendTail(state3.stdout, chunk, outputLimitBytes);
|
|
45594
|
+
this.refreshOutput(state3, outputLimitBytes);
|
|
45506
45595
|
});
|
|
45507
45596
|
child.stderr?.on("data", (chunk) => {
|
|
45508
|
-
|
|
45597
|
+
if (state3.session.status === "stopped") return;
|
|
45598
|
+
appendTail(state3.stderr, chunk, outputLimitBytes);
|
|
45599
|
+
this.refreshOutput(state3, outputLimitBytes);
|
|
45509
45600
|
});
|
|
45510
45601
|
child.on("error", (err) => {
|
|
45511
|
-
|
|
45512
|
-
|
|
45513
|
-
|
|
45514
|
-
|
|
45515
|
-
clearTimeout(timeout);
|
|
45516
|
-
const stdout = toCapturedOutput(stdoutChunks, stdoutBytes, outputLimitBytes);
|
|
45517
|
-
const stderr = toCapturedOutput(stderrChunks, stderrBytes, outputLimitBytes);
|
|
45518
|
-
resolve15({
|
|
45519
|
-
nodeId,
|
|
45520
|
-
cwd,
|
|
45521
|
-
command: normalizedCommand,
|
|
45522
|
-
exitCode: code,
|
|
45523
|
-
stdout: stdout.text,
|
|
45524
|
-
stderr: stderr.text,
|
|
45525
|
-
durationMs: Date.now() - startedAt,
|
|
45526
|
-
timedOut,
|
|
45527
|
-
stdoutTruncated: stdout.truncated,
|
|
45528
|
-
stderrTruncated: stderr.truncated
|
|
45529
|
-
});
|
|
45602
|
+
state3.session.status = "failed";
|
|
45603
|
+
state3.session.stderr = state3.session.stderr ? `${state3.session.stderr}
|
|
45604
|
+
${err.message}` : err.message;
|
|
45605
|
+
this.complete(state3, null, null, outputLimitBytes);
|
|
45530
45606
|
});
|
|
45531
|
-
|
|
45532
|
-
|
|
45607
|
+
child.on("close", (code, signal) => {
|
|
45608
|
+
if (state3.session.status !== "stopped") {
|
|
45609
|
+
state3.session.status = code === 0 ? "exited" : "failed";
|
|
45610
|
+
}
|
|
45611
|
+
this.complete(state3, code, signal, outputLimitBytes);
|
|
45612
|
+
});
|
|
45613
|
+
return cloneSession(session);
|
|
45614
|
+
}
|
|
45615
|
+
list() {
|
|
45616
|
+
return Array.from(this.sessions.values()).sort((a, b) => b.sequence - a.sequence).map((state3) => cloneSession(state3.session));
|
|
45617
|
+
}
|
|
45618
|
+
get(id) {
|
|
45619
|
+
const state3 = this.sessions.get(id);
|
|
45620
|
+
return state3 ? cloneSession(state3.session) : null;
|
|
45621
|
+
}
|
|
45622
|
+
stop(id) {
|
|
45623
|
+
const state3 = this.sessions.get(id);
|
|
45624
|
+
if (!state3) return null;
|
|
45625
|
+
if (state3.session.status === "running") {
|
|
45626
|
+
state3.session.status = "stopped";
|
|
45627
|
+
state3.session.updatedAt = Date.now();
|
|
45628
|
+
killSessionProcess(state3, "SIGTERM");
|
|
45629
|
+
setTimeout(() => {
|
|
45630
|
+
if (state3.session.status === "stopped" && !state3.session.completedAt) {
|
|
45631
|
+
killSessionProcess(state3, "SIGKILL");
|
|
45632
|
+
}
|
|
45633
|
+
}, 2e3).unref?.();
|
|
45634
|
+
}
|
|
45635
|
+
return cloneSession(state3.session);
|
|
45636
|
+
}
|
|
45637
|
+
refreshOutput(state3, outputLimitBytes) {
|
|
45638
|
+
const stdout = readBuffer(state3.stdout, outputLimitBytes);
|
|
45639
|
+
const stderr = readBuffer(state3.stderr, outputLimitBytes);
|
|
45640
|
+
state3.session.stdout = stdout.text;
|
|
45641
|
+
state3.session.stderr = stderr.text;
|
|
45642
|
+
state3.session.stdoutTruncated = stdout.truncated;
|
|
45643
|
+
state3.session.stderrTruncated = stderr.truncated;
|
|
45644
|
+
state3.session.updatedAt = Date.now();
|
|
45645
|
+
state3.session.durationMs = state3.session.updatedAt - state3.session.startedAt;
|
|
45646
|
+
}
|
|
45647
|
+
complete(state3, exitCode, signal, outputLimitBytes) {
|
|
45648
|
+
this.refreshOutput(state3, outputLimitBytes);
|
|
45649
|
+
state3.session.exitCode = exitCode;
|
|
45650
|
+
state3.session.signal = signal;
|
|
45651
|
+
state3.session.completedAt = Date.now();
|
|
45652
|
+
state3.session.updatedAt = state3.session.completedAt;
|
|
45653
|
+
state3.session.durationMs = state3.session.completedAt - state3.session.startedAt;
|
|
45654
|
+
state3.process = null;
|
|
45655
|
+
this.pruneCompletedSessions();
|
|
45656
|
+
}
|
|
45657
|
+
pruneCompletedSessions() {
|
|
45658
|
+
const completed = Array.from(this.sessions.values()).filter((state3) => state3.session.status !== "running").sort((a, b) => b.sequence - a.sequence);
|
|
45659
|
+
for (const state3 of completed.slice(MAX_COMPLETED_SESSIONS)) {
|
|
45660
|
+
this.sessions.delete(state3.session.id);
|
|
45661
|
+
}
|
|
45662
|
+
}
|
|
45663
|
+
};
|
|
45533
45664
|
|
|
45534
45665
|
// ../../packages/api/src/node/node-native-session-service.ts
|
|
45535
45666
|
function getLocalNodeNativeSessions(nodeId, agent, limit) {
|
|
@@ -46615,6 +46746,10 @@ function payloadValue(request, key, fallback) {
|
|
|
46615
46746
|
const value = request.payload[key];
|
|
46616
46747
|
return value === void 0 ? fallback : value;
|
|
46617
46748
|
}
|
|
46749
|
+
function getTerminalSessionService(deps) {
|
|
46750
|
+
deps.terminalSessionService ??= new NodeTerminalSessionService();
|
|
46751
|
+
return deps.terminalSessionService;
|
|
46752
|
+
}
|
|
46618
46753
|
function maybeImportTaskSnapshot(deps, request) {
|
|
46619
46754
|
if (!request.kind.startsWith("task.output.") && request.kind !== "task.preview.create" && request.kind !== "task.logs") return;
|
|
46620
46755
|
const task = payloadValue(request, "task", null);
|
|
@@ -46692,12 +46827,12 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
46692
46827
|
);
|
|
46693
46828
|
break;
|
|
46694
46829
|
}
|
|
46695
|
-
case "node.terminal.
|
|
46830
|
+
case "node.terminal.session.start": {
|
|
46696
46831
|
const self2 = deps.nodeRegistry.getSelf();
|
|
46697
46832
|
response = jsonResponse(
|
|
46698
46833
|
request.id,
|
|
46699
|
-
|
|
46700
|
-
|
|
46834
|
+
202,
|
|
46835
|
+
getTerminalSessionService(deps).start(
|
|
46701
46836
|
self2.id,
|
|
46702
46837
|
self2.workDir ?? deps.workDir,
|
|
46703
46838
|
payloadValue(request, "command", ""),
|
|
@@ -46706,6 +46841,28 @@ async function executeWorkerControlRequest(deps, request) {
|
|
|
46706
46841
|
);
|
|
46707
46842
|
break;
|
|
46708
46843
|
}
|
|
46844
|
+
case "node.terminal.session.list": {
|
|
46845
|
+
const self2 = deps.nodeRegistry.getSelf();
|
|
46846
|
+
response = jsonResponse(request.id, 200, {
|
|
46847
|
+
nodeId: self2.id,
|
|
46848
|
+
sessions: getTerminalSessionService(deps).list()
|
|
46849
|
+
});
|
|
46850
|
+
break;
|
|
46851
|
+
}
|
|
46852
|
+
case "node.terminal.session.get": {
|
|
46853
|
+
const sessionId = payloadValue(request, "sessionId", "");
|
|
46854
|
+
const session = getTerminalSessionService(deps).get(sessionId);
|
|
46855
|
+
if (!session) throw new MeshyError("NODE_NOT_FOUND", `Terminal session ${sessionId} not found`, 404);
|
|
46856
|
+
response = jsonResponse(request.id, 200, session);
|
|
46857
|
+
break;
|
|
46858
|
+
}
|
|
46859
|
+
case "node.terminal.session.stop": {
|
|
46860
|
+
const sessionId = payloadValue(request, "sessionId", "");
|
|
46861
|
+
const session = getTerminalSessionService(deps).stop(sessionId);
|
|
46862
|
+
if (!session) throw new MeshyError("NODE_NOT_FOUND", `Terminal session ${sessionId} not found`, 404);
|
|
46863
|
+
response = jsonResponse(request.id, 200, session);
|
|
46864
|
+
break;
|
|
46865
|
+
}
|
|
46709
46866
|
case "node.sessions.list": {
|
|
46710
46867
|
const self2 = deps.nodeRegistry.getSelf();
|
|
46711
46868
|
response = jsonResponse(
|
|
@@ -46954,142 +47111,89 @@ async function sendNodeAgentUpgrade(req, res, nodeId, agentParam) {
|
|
|
46954
47111
|
}
|
|
46955
47112
|
|
|
46956
47113
|
// ../../packages/api/src/routes/node-terminal.ts
|
|
46957
|
-
var
|
|
46958
|
-
function
|
|
46959
|
-
|
|
46960
|
-
|
|
46961
|
-
return {
|
|
46962
|
-
errorName: error.name,
|
|
46963
|
-
errorMessage: error.message,
|
|
46964
|
-
errorCategory
|
|
46965
|
-
};
|
|
46966
|
-
}
|
|
46967
|
-
return {
|
|
46968
|
-
errorName: "UnknownError",
|
|
46969
|
-
errorMessage: String(error),
|
|
46970
|
-
errorCategory: "unknown"
|
|
46971
|
-
};
|
|
47114
|
+
var TERMINAL_SESSION_PROXY_TIMEOUT_MS = 1e4;
|
|
47115
|
+
function getTerminalSessionService2(deps) {
|
|
47116
|
+
deps.terminalSessionService ??= new NodeTerminalSessionService();
|
|
47117
|
+
return deps.terminalSessionService;
|
|
46972
47118
|
}
|
|
46973
|
-
function
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
|
|
46979
|
-
|
|
46980
|
-
|
|
46981
|
-
|
|
46982
|
-
|
|
46983
|
-
|
|
46984
|
-
|
|
46985
|
-
|
|
46986
|
-
|
|
46987
|
-
|
|
46988
|
-
|
|
46989
|
-
|
|
46990
|
-
|
|
46991
|
-
|
|
46992
|
-
|
|
46993
|
-
|
|
46994
|
-
|
|
46995
|
-
}
|
|
46996
|
-
},
|
|
46997
|
-
onError: ({ attempt, endpoint, error, timeoutMs, totalEndpoints }) => {
|
|
46998
|
-
log2.warn("node terminal proxy attempt failed", {
|
|
46999
|
-
nodeId,
|
|
47000
|
-
proxyPath,
|
|
47001
|
-
endpoint,
|
|
47002
|
-
attempt,
|
|
47003
|
-
totalEndpoints,
|
|
47004
|
-
timeoutMs,
|
|
47005
|
-
...describeProxyError2(error)
|
|
47006
|
-
});
|
|
47119
|
+
async function sendRemoteTerminalSessionRequest(req, res, nodeId, message) {
|
|
47120
|
+
const deps = req.app.locals.deps;
|
|
47121
|
+
const node = deps.nodeRegistry.getNode(nodeId);
|
|
47122
|
+
if (!node) throw new MeshyError("NODE_NOT_FOUND", `Node ${nodeId} not found`, 404);
|
|
47123
|
+
const heartbeat = deps.heartbeat;
|
|
47124
|
+
const canPushToNode = heartbeat.canPushToNode?.(nodeId) ?? true;
|
|
47125
|
+
let response;
|
|
47126
|
+
if (!canPushToNode && canRequestNodeMessage(heartbeat)) {
|
|
47127
|
+
response = await requestFallbackNodeMessage(heartbeat, nodeId, message, TERMINAL_SESSION_PROXY_TIMEOUT_MS);
|
|
47128
|
+
} else {
|
|
47129
|
+
try {
|
|
47130
|
+
response = await new NodeMessageClient({
|
|
47131
|
+
logger: deps.logger,
|
|
47132
|
+
timeoutMs: TERMINAL_SESSION_PROXY_TIMEOUT_MS
|
|
47133
|
+
}).request(node, message);
|
|
47134
|
+
} catch (err) {
|
|
47135
|
+
if (canRequestNodeMessage(heartbeat)) {
|
|
47136
|
+
response = await requestFallbackNodeMessage(heartbeat, nodeId, message, TERMINAL_SESSION_PROXY_TIMEOUT_MS);
|
|
47137
|
+
} else {
|
|
47138
|
+
throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} for terminal session control`, 502, {
|
|
47139
|
+
error: err instanceof Error ? err.message : String(err)
|
|
47140
|
+
});
|
|
47141
|
+
}
|
|
47007
47142
|
}
|
|
47008
|
-
};
|
|
47009
|
-
}
|
|
47010
|
-
async function maybeHandleRemoteNodeTerminalExecuteRequest(req, res, nodeId) {
|
|
47011
|
-
const body = NodeTerminalExecuteBody.parse(req.body);
|
|
47012
|
-
const { nodeRegistry, heartbeat, logger: rootLogger } = req.app.locals.deps;
|
|
47013
|
-
const selfId = nodeRegistry.getSelf().id;
|
|
47014
|
-
if (nodeId === selfId) {
|
|
47015
|
-
return false;
|
|
47016
47143
|
}
|
|
47017
|
-
|
|
47018
|
-
|
|
47019
|
-
|
|
47144
|
+
sendWorkerControlResponse(res, response);
|
|
47145
|
+
}
|
|
47146
|
+
async function sendNodeTerminalSessionStart(req, res, nodeId) {
|
|
47147
|
+
const body = NodeTerminalSessionStartBody.parse(req.body);
|
|
47148
|
+
const deps = req.app.locals.deps;
|
|
47149
|
+
const self2 = deps.nodeRegistry.getSelf();
|
|
47150
|
+
if (nodeId !== self2.id) {
|
|
47151
|
+
await sendRemoteTerminalSessionRequest(req, res, nodeId, createNodeMessage("node.terminal.session.start", {
|
|
47152
|
+
command: body.command,
|
|
47153
|
+
cwd: body.cwd
|
|
47154
|
+
}, { expectsResponse: true }));
|
|
47155
|
+
return;
|
|
47020
47156
|
}
|
|
47021
|
-
const
|
|
47022
|
-
|
|
47023
|
-
|
|
47024
|
-
|
|
47025
|
-
|
|
47026
|
-
|
|
47027
|
-
|
|
47028
|
-
|
|
47029
|
-
|
|
47030
|
-
const controlResponse = await requestFallbackNodeMessage(heartbeat, nodeId, fallbackRequest);
|
|
47031
|
-
sendWorkerControlResponse(res, controlResponse);
|
|
47032
|
-
return true;
|
|
47157
|
+
const session = getTerminalSessionService2(deps).start(self2.id, self2.workDir ?? deps.workDir, body.command, { cwd: body.cwd });
|
|
47158
|
+
res.status(202).json(session);
|
|
47159
|
+
}
|
|
47160
|
+
async function sendNodeTerminalSessionList(req, res, nodeId) {
|
|
47161
|
+
const deps = req.app.locals.deps;
|
|
47162
|
+
const self2 = deps.nodeRegistry.getSelf();
|
|
47163
|
+
if (nodeId !== self2.id) {
|
|
47164
|
+
await sendRemoteTerminalSessionRequest(req, res, nodeId, createNodeMessage("node.terminal.session.list", {}, { expectsResponse: true }));
|
|
47165
|
+
return;
|
|
47033
47166
|
}
|
|
47034
|
-
|
|
47035
|
-
|
|
47036
|
-
|
|
47037
|
-
|
|
47038
|
-
|
|
47039
|
-
|
|
47040
|
-
|
|
47041
|
-
|
|
47042
|
-
},
|
|
47043
|
-
NODE_TERMINAL_PROXY_TIMEOUT_MS,
|
|
47044
|
-
createNodeTerminalProxyTrace(log2, nodeId, proxyPath),
|
|
47045
|
-
{ preferPublicEndpoint: true }
|
|
47046
|
-
);
|
|
47047
|
-
log2.debug("proxying node terminal request", { nodeId, endpoint, proxyPath });
|
|
47048
|
-
await sendProxyResponse(res, response);
|
|
47049
|
-
return true;
|
|
47050
|
-
} catch (err) {
|
|
47051
|
-
const errorDetails = describeProxyError2(err);
|
|
47052
|
-
log2.warn("node terminal proxy error", {
|
|
47053
|
-
nodeId,
|
|
47054
|
-
proxyPath,
|
|
47055
|
-
timeoutMs: NODE_TERMINAL_PROXY_TIMEOUT_MS,
|
|
47056
|
-
...errorDetails
|
|
47057
|
-
});
|
|
47058
|
-
if (canRequestNodeMessage(heartbeat)) {
|
|
47059
|
-
log2.warn("node terminal proxy failed, falling back to keepalive control", {
|
|
47060
|
-
nodeId,
|
|
47061
|
-
proxyPath,
|
|
47062
|
-
timeoutMs: NODE_TERMINAL_PROXY_TIMEOUT_MS,
|
|
47063
|
-
...errorDetails
|
|
47064
|
-
});
|
|
47065
|
-
const controlResponse = await requestFallbackNodeMessage(heartbeat, nodeId, fallbackRequest);
|
|
47066
|
-
sendWorkerControlResponse(res, controlResponse);
|
|
47067
|
-
return true;
|
|
47068
|
-
}
|
|
47069
|
-
throw new MeshyError("NODE_OFFLINE", `Cannot reach node ${nodeId} to execute a terminal command`, 502);
|
|
47167
|
+
res.json({ nodeId: self2.id, sessions: getTerminalSessionService2(deps).list() });
|
|
47168
|
+
}
|
|
47169
|
+
async function sendNodeTerminalSessionGet(req, res, nodeId, sessionId) {
|
|
47170
|
+
const deps = req.app.locals.deps;
|
|
47171
|
+
const self2 = deps.nodeRegistry.getSelf();
|
|
47172
|
+
if (nodeId !== self2.id) {
|
|
47173
|
+
await sendRemoteTerminalSessionRequest(req, res, nodeId, createNodeMessage("node.terminal.session.get", { sessionId }, { expectsResponse: true }));
|
|
47174
|
+
return;
|
|
47070
47175
|
}
|
|
47176
|
+
const session = getTerminalSessionService2(deps).get(sessionId);
|
|
47177
|
+
if (!session) throw new MeshyError("NODE_NOT_FOUND", `Terminal session ${sessionId} not found`, 404);
|
|
47178
|
+
res.json(session);
|
|
47071
47179
|
}
|
|
47072
|
-
async function
|
|
47073
|
-
const
|
|
47074
|
-
const
|
|
47075
|
-
const self2 = nodeRegistry.getSelf();
|
|
47180
|
+
async function sendNodeTerminalSessionStop(req, res, nodeId, sessionId) {
|
|
47181
|
+
const deps = req.app.locals.deps;
|
|
47182
|
+
const self2 = deps.nodeRegistry.getSelf();
|
|
47076
47183
|
if (nodeId !== self2.id) {
|
|
47077
|
-
|
|
47184
|
+
await sendRemoteTerminalSessionRequest(req, res, nodeId, createNodeMessage("node.terminal.session.stop", { sessionId }, { expectsResponse: true }));
|
|
47185
|
+
return;
|
|
47078
47186
|
}
|
|
47079
|
-
const
|
|
47080
|
-
|
|
47081
|
-
|
|
47082
|
-
body.command,
|
|
47083
|
-
{ cwd: body.cwd }
|
|
47084
|
-
);
|
|
47085
|
-
res.json(result);
|
|
47187
|
+
const session = getTerminalSessionService2(deps).stop(sessionId);
|
|
47188
|
+
if (!session) throw new MeshyError("NODE_NOT_FOUND", `Terminal session ${sessionId} not found`, 404);
|
|
47189
|
+
res.json(session);
|
|
47086
47190
|
}
|
|
47087
47191
|
|
|
47088
47192
|
// ../../packages/api/src/routes/nodes.ts
|
|
47089
47193
|
var NODE_WORKDIR_PROXY_TIMEOUT_MS = 1e4;
|
|
47090
47194
|
var NODE_WORKDIR_BRANCH_PROXY_TIMEOUT_MS = 25e3;
|
|
47091
47195
|
var NODE_NATIVE_SESSIONS_PROXY_TIMEOUT_MS = 1e4;
|
|
47092
|
-
function
|
|
47196
|
+
function describeProxyError2(error) {
|
|
47093
47197
|
if (error instanceof Error) {
|
|
47094
47198
|
const errorCategory = error.name === "AbortError" || /aborted/i.test(error.message) ? "abort" : /timeout/i.test(error.message) ? "timeout" : "network";
|
|
47095
47199
|
return {
|
|
@@ -47136,7 +47240,7 @@ function createNodeWorkdirProxyTrace(log2, nodeId, proxyPath) {
|
|
|
47136
47240
|
attempt,
|
|
47137
47241
|
totalEndpoints,
|
|
47138
47242
|
timeoutMs,
|
|
47139
|
-
...
|
|
47243
|
+
...describeProxyError2(error)
|
|
47140
47244
|
});
|
|
47141
47245
|
}
|
|
47142
47246
|
};
|
|
@@ -47191,7 +47295,7 @@ async function maybeHandleRemoteNodeWorkDirRequest(req, res, nodeId) {
|
|
|
47191
47295
|
await sendProxyResponse(res, response);
|
|
47192
47296
|
return true;
|
|
47193
47297
|
} catch (err) {
|
|
47194
|
-
const errorDetails =
|
|
47298
|
+
const errorDetails = describeProxyError2(err);
|
|
47195
47299
|
log2.warn("node workdir proxy error", {
|
|
47196
47300
|
nodeId,
|
|
47197
47301
|
proxyPath,
|
|
@@ -47258,7 +47362,7 @@ async function maybeHandleRemoteNodeWorkDirBranchInfoRequest(req, res, nodeId) {
|
|
|
47258
47362
|
await sendProxyResponse(res, response);
|
|
47259
47363
|
return true;
|
|
47260
47364
|
} catch (err) {
|
|
47261
|
-
const errorDetails =
|
|
47365
|
+
const errorDetails = describeProxyError2(err);
|
|
47262
47366
|
log2.warn("node workdir branch proxy error", {
|
|
47263
47367
|
nodeId,
|
|
47264
47368
|
proxyPath,
|
|
@@ -47323,7 +47427,7 @@ async function maybeHandleRemoteNodeNativeSessionsRequest(req, res, nodeId) {
|
|
|
47323
47427
|
await sendProxyResponse(res, response);
|
|
47324
47428
|
return true;
|
|
47325
47429
|
} catch (err) {
|
|
47326
|
-
const errorDetails =
|
|
47430
|
+
const errorDetails = describeProxyError2(err);
|
|
47327
47431
|
log2.warn("node native sessions proxy error", {
|
|
47328
47432
|
nodeId,
|
|
47329
47433
|
proxyPath,
|
|
@@ -47410,13 +47514,17 @@ function createNodeRoutes() {
|
|
|
47410
47514
|
}
|
|
47411
47515
|
sendLocalNodeWorkDirBranchInfo(req, res, nodeId);
|
|
47412
47516
|
}));
|
|
47413
|
-
router.
|
|
47414
|
-
|
|
47415
|
-
|
|
47416
|
-
|
|
47417
|
-
|
|
47418
|
-
|
|
47419
|
-
|
|
47517
|
+
router.get("/:id/terminal/sessions", asyncHandler3(async (req, res) => {
|
|
47518
|
+
await sendNodeTerminalSessionList(req, res, req.params.id);
|
|
47519
|
+
}));
|
|
47520
|
+
router.post("/:id/terminal/sessions", asyncHandler3(async (req, res) => {
|
|
47521
|
+
await sendNodeTerminalSessionStart(req, res, req.params.id);
|
|
47522
|
+
}));
|
|
47523
|
+
router.get("/:id/terminal/sessions/:sessionId", asyncHandler3(async (req, res) => {
|
|
47524
|
+
await sendNodeTerminalSessionGet(req, res, req.params.id, req.params.sessionId);
|
|
47525
|
+
}));
|
|
47526
|
+
router.post("/:id/terminal/sessions/:sessionId/stop", asyncHandler3(async (req, res) => {
|
|
47527
|
+
await sendNodeTerminalSessionStop(req, res, req.params.id, req.params.sessionId);
|
|
47420
47528
|
}));
|
|
47421
47529
|
router.post("/:id/workdir/branch", asyncHandler3(async (req, res) => {
|
|
47422
47530
|
const nodeId = req.params.id;
|
|
@@ -47837,7 +47945,7 @@ function getTaskLogsProxyRequestMetadata(req) {
|
|
|
47837
47945
|
const sourceNodeId = req.get(TASK_LOG_PROXY_SOURCE_HEADER) ?? void 0;
|
|
47838
47946
|
return { isProxy: purpose === TASK_LOG_PROXY_PURPOSE, sourceNodeId, task: decodeTaskSnapshot(req.get(TASK_LOG_PROXY_TASK_HEADER)) };
|
|
47839
47947
|
}
|
|
47840
|
-
function
|
|
47948
|
+
function describeProxyError3(error) {
|
|
47841
47949
|
if (error instanceof Error) {
|
|
47842
47950
|
const errorCategory = error.name === "AbortError" || /aborted/i.test(error.message) ? "abort" : /timeout/i.test(error.message) ? "timeout" : "network";
|
|
47843
47951
|
return {
|
|
@@ -47879,7 +47987,7 @@ function createTaskLogsProxyTrace(log2, taskId, nodeId, proxyPath) {
|
|
|
47879
47987
|
attempt,
|
|
47880
47988
|
totalEndpoints,
|
|
47881
47989
|
timeoutMs,
|
|
47882
|
-
...
|
|
47990
|
+
...describeProxyError3(error)
|
|
47883
47991
|
});
|
|
47884
47992
|
}
|
|
47885
47993
|
};
|
|
@@ -47957,7 +48065,7 @@ async function sendTaskLogsResponse(req, res, taskId) {
|
|
|
47957
48065
|
log2.warn("failed to seed task snapshot before task logs proxy", {
|
|
47958
48066
|
taskId,
|
|
47959
48067
|
assignedTo,
|
|
47960
|
-
...
|
|
48068
|
+
...describeProxyError3(err)
|
|
47961
48069
|
});
|
|
47962
48070
|
return [];
|
|
47963
48071
|
});
|
|
@@ -47990,7 +48098,7 @@ async function sendTaskLogsResponse(req, res, taskId) {
|
|
|
47990
48098
|
taskId,
|
|
47991
48099
|
assignedTo,
|
|
47992
48100
|
timeoutMs: TASK_LOG_PROXY_TIMEOUT_MS,
|
|
47993
|
-
...
|
|
48101
|
+
...describeProxyError3(err)
|
|
47994
48102
|
});
|
|
47995
48103
|
const fallback = await requestTaskLogsOverKeepalive(heartbeat, assignedTo, task, after);
|
|
47996
48104
|
if (fallback) {
|
|
@@ -48067,7 +48175,7 @@ function resolveRequestOrigin(req) {
|
|
|
48067
48175
|
|
|
48068
48176
|
// ../../packages/api/src/routes/task-output.ts
|
|
48069
48177
|
var TASK_OUTPUT_PROXY_TIMEOUT_MS = 1e4;
|
|
48070
|
-
function
|
|
48178
|
+
function describeProxyError4(error) {
|
|
48071
48179
|
if (error instanceof Error) {
|
|
48072
48180
|
const errorCategory = error.name === "AbortError" || /aborted/i.test(error.message) ? "abort" : /timeout/i.test(error.message) ? "timeout" : "network";
|
|
48073
48181
|
return {
|
|
@@ -48120,7 +48228,7 @@ function createTaskOutputProxyTrace(log2, taskId, assignedTo, kind, proxyPath) {
|
|
|
48120
48228
|
attempt,
|
|
48121
48229
|
totalEndpoints,
|
|
48122
48230
|
timeoutMs,
|
|
48123
|
-
...
|
|
48231
|
+
...describeProxyError4(error)
|
|
48124
48232
|
});
|
|
48125
48233
|
}
|
|
48126
48234
|
};
|
|
@@ -48238,7 +48346,7 @@ async function maybeHandleRemoteTaskOutputRequest(req, res, taskId, subPath, ini
|
|
|
48238
48346
|
assignedTo: assignedNodeId,
|
|
48239
48347
|
kind: hydratedFallbackRequest.kind,
|
|
48240
48348
|
timeoutMs: TASK_OUTPUT_PROXY_TIMEOUT_MS,
|
|
48241
|
-
...
|
|
48349
|
+
...describeProxyError4(err)
|
|
48242
48350
|
});
|
|
48243
48351
|
const controlResponse = await requestFallbackNodeMessage(heartbeat, assignedNodeId, hydratedFallbackRequest);
|
|
48244
48352
|
if (hydratedFallbackRequest.kind === "task.preview.create" && controlResponse.bodyEncoding === "json" && isPreviewSessionPayload(controlResponse.body)) {
|
|
@@ -48255,7 +48363,7 @@ async function maybeHandleRemoteTaskOutputRequest(req, res, taskId, subPath, ini
|
|
|
48255
48363
|
assignedTo: assignedNodeId,
|
|
48256
48364
|
kind: messageKind,
|
|
48257
48365
|
timeoutMs: TASK_OUTPUT_PROXY_TIMEOUT_MS,
|
|
48258
|
-
...
|
|
48366
|
+
...describeProxyError4(err)
|
|
48259
48367
|
});
|
|
48260
48368
|
throw new MeshyError("NODE_OFFLINE", "Cannot reach worker for task output", 502);
|
|
48261
48369
|
}
|