@mastra/blaxel 0.0.2 → 0.1.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/CHANGELOG.md +102 -0
- package/LICENSE.md +15 -0
- package/README.md +1 -1
- package/dist/index.cjs +305 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +306 -33
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +14 -2
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/mounts/gcs.d.ts.map +1 -1
- package/dist/sandbox/mounts/s3.d.ts.map +1 -1
- package/dist/sandbox/mounts/types.d.ts +9 -0
- package/dist/sandbox/mounts/types.d.ts.map +1 -1
- package/dist/sandbox/process-manager.d.ts +27 -0
- package/dist/sandbox/process-manager.d.ts.map +1 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SandboxInstance } from '@blaxel/core';
|
|
2
|
-
import { MastraSandbox, SandboxNotReadyError } from '@mastra/core/workspace';
|
|
2
|
+
import { SandboxProcessManager, MastraSandbox, SandboxNotReadyError, ProcessHandle } from '@mastra/core/workspace';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
4
|
|
|
5
5
|
// src/sandbox/index.ts
|
|
@@ -31,6 +31,16 @@ function validateEndpoint(endpoint) {
|
|
|
31
31
|
throw new Error(`Invalid endpoint URL scheme: "${parsed.protocol}". Only http: and https: are allowed.`);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
async function detectPackageManager(sandbox) {
|
|
35
|
+
const result = await runCommand(
|
|
36
|
+
sandbox,
|
|
37
|
+
'which apt-get >/dev/null 2>&1 && echo "apt" || (which apk >/dev/null 2>&1 && echo "apk" || echo "unknown")'
|
|
38
|
+
);
|
|
39
|
+
const pm = result.stdout.trim();
|
|
40
|
+
if (pm === "apt") return "apt";
|
|
41
|
+
if (pm === "apk") return "apk";
|
|
42
|
+
return "unknown";
|
|
43
|
+
}
|
|
34
44
|
async function runCommand(sandbox, command, options) {
|
|
35
45
|
const result = await sandbox.process.exec({
|
|
36
46
|
command,
|
|
@@ -53,25 +63,45 @@ async function mountS3(mountPath, config, ctx) {
|
|
|
53
63
|
if (checkResult.stdout.includes("not found")) {
|
|
54
64
|
logger.warn(`${LOG_PREFIX} s3fs not found, attempting runtime installation...`);
|
|
55
65
|
logger.info(`${LOG_PREFIX} Tip: For faster startup, pre-install s3fs in your sandbox image`);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
const pm = await detectPackageManager(sandbox);
|
|
67
|
+
logger.debug(`${LOG_PREFIX} Detected package manager: ${pm}`);
|
|
68
|
+
if (pm === "apt") {
|
|
69
|
+
const updateResult = await runCommand(sandbox, "apt-get update 2>&1", { timeout: 6e4 });
|
|
70
|
+
if (updateResult.exitCode !== 0) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Failed to update package lists for s3fs installation.
|
|
60
73
|
Error details: ${updateResult.stderr || updateResult.stdout}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const installResult = await runCommand(
|
|
77
|
+
sandbox,
|
|
78
|
+
"apt-get install -y s3fs fuse 2>&1 || apt-get install -y s3fs-fuse fuse 2>&1",
|
|
79
|
+
{ timeout: 12e4 }
|
|
61
80
|
);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"apt-get install -y s3fs fuse 2>&1 || apt-get install -y s3fs-fuse fuse 2>&1",
|
|
66
|
-
{ timeout: 12e4 }
|
|
67
|
-
);
|
|
68
|
-
if (installResult.exitCode !== 0) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`Failed to install s3fs. For S3 mounting, your sandbox image needs s3fs and fuse packages.
|
|
81
|
+
if (installResult.exitCode !== 0) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Failed to install s3fs. For S3 mounting, your sandbox image needs s3fs and fuse packages.
|
|
71
84
|
|
|
72
85
|
Pre-install in your image: apt-get install -y s3fs fuse
|
|
73
86
|
|
|
74
87
|
Error details: ${installResult.stderr || installResult.stdout}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
} else if (pm === "apk") {
|
|
91
|
+
const installResult = await runCommand(sandbox, "apk add --no-cache s3fs-fuse fuse 2>&1", { timeout: 12e4 });
|
|
92
|
+
if (installResult.exitCode !== 0) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`Failed to install s3fs on Alpine Linux. Ensure the Alpine community repository is enabled.
|
|
95
|
+
|
|
96
|
+
Pre-install in your image: apk add --no-cache s3fs-fuse fuse
|
|
97
|
+
|
|
98
|
+
Error details: ${installResult.stderr || installResult.stdout}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Cannot install s3fs: no supported package manager found (need apt-get or apk).
|
|
104
|
+
Use a Debian-based image (e.g. blaxel/ts-app:latest) or Alpine-based image (e.g. blaxel/node:latest), or pre-install s3fs in your custom image.`
|
|
75
105
|
);
|
|
76
106
|
}
|
|
77
107
|
}
|
|
@@ -133,6 +163,26 @@ async function mountGCS(mountPath, config, ctx) {
|
|
|
133
163
|
const checkResult = await runCommand(sandbox, 'which gcsfuse || echo "not found"');
|
|
134
164
|
if (checkResult.stdout.includes("not found")) {
|
|
135
165
|
logger.warn(`${LOG_PREFIX} gcsfuse not found, attempting runtime installation...`);
|
|
166
|
+
const pm = await detectPackageManager(sandbox);
|
|
167
|
+
logger.debug(`${LOG_PREFIX} Detected package manager: ${pm}`);
|
|
168
|
+
if (pm === "apk") {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`gcsfuse is not available on Alpine Linux. Google only provides gcsfuse packages for Debian/Ubuntu.
|
|
171
|
+
|
|
172
|
+
Use a Debian-based Blaxel image for GCS mounts:
|
|
173
|
+
new BlaxelSandbox({ image: 'blaxel/ts-app:latest' })
|
|
174
|
+
new BlaxelSandbox({ image: 'blaxel/py-app:latest' })`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (pm !== "apt") {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Cannot install gcsfuse: no supported package manager found (need apt-get).
|
|
180
|
+
gcsfuse is only available on Debian/Ubuntu-based images.
|
|
181
|
+
|
|
182
|
+
Use a Debian-based Blaxel image:
|
|
183
|
+
new BlaxelSandbox({ image: 'blaxel/ts-app:latest' })`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
136
186
|
logger.info(`${LOG_PREFIX} Tip: For faster startup, pre-install gcsfuse in your sandbox image`);
|
|
137
187
|
const codenameResult = await runCommand(
|
|
138
188
|
sandbox,
|
|
@@ -203,6 +253,138 @@ ${installResult.stderr}`
|
|
|
203
253
|
throw new Error(`Failed to mount GCS bucket: ${result.stderr || result.stdout}`);
|
|
204
254
|
}
|
|
205
255
|
}
|
|
256
|
+
var BlaxelProcessHandle = class extends ProcessHandle {
|
|
257
|
+
pid;
|
|
258
|
+
_identifier;
|
|
259
|
+
_sandbox;
|
|
260
|
+
_startTime;
|
|
261
|
+
_exitCode;
|
|
262
|
+
_waitPromise = null;
|
|
263
|
+
_streamingDone = null;
|
|
264
|
+
_closeStream = null;
|
|
265
|
+
_killed = false;
|
|
266
|
+
constructor(pid, identifier, sandbox, startTime, options) {
|
|
267
|
+
super(options);
|
|
268
|
+
this.pid = pid;
|
|
269
|
+
this._identifier = identifier;
|
|
270
|
+
this._sandbox = sandbox;
|
|
271
|
+
this._startTime = startTime;
|
|
272
|
+
}
|
|
273
|
+
get exitCode() {
|
|
274
|
+
return this._exitCode;
|
|
275
|
+
}
|
|
276
|
+
/** @internal Set by the process manager after streaming starts. */
|
|
277
|
+
set streamControl(control) {
|
|
278
|
+
this._closeStream = control.close;
|
|
279
|
+
this._streamingDone = control.wait();
|
|
280
|
+
this._streamingDone.then(() => this._resolveExitCode()).catch(() => this._resolveExitCode());
|
|
281
|
+
}
|
|
282
|
+
/** Fetch exit code from Blaxel and set _exitCode. No-op if already set. */
|
|
283
|
+
async _resolveExitCode() {
|
|
284
|
+
if (this._exitCode !== void 0) return;
|
|
285
|
+
try {
|
|
286
|
+
const proc = await this._sandbox.process.get(this._identifier);
|
|
287
|
+
this._exitCode = proc.status === "completed" ? proc.exitCode ?? 0 : proc.exitCode ?? 1;
|
|
288
|
+
} catch {
|
|
289
|
+
if (this._exitCode === void 0) {
|
|
290
|
+
this._exitCode = 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async wait() {
|
|
295
|
+
if (!this._waitPromise) {
|
|
296
|
+
this._waitPromise = this._doWait();
|
|
297
|
+
}
|
|
298
|
+
return this._waitPromise;
|
|
299
|
+
}
|
|
300
|
+
async _doWait() {
|
|
301
|
+
if (this._streamingDone) {
|
|
302
|
+
await this._streamingDone.catch(() => {
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (this._killed) {
|
|
306
|
+
return {
|
|
307
|
+
success: false,
|
|
308
|
+
exitCode: this._exitCode ?? 137,
|
|
309
|
+
stdout: this.stdout,
|
|
310
|
+
stderr: this.stderr,
|
|
311
|
+
executionTimeMs: Date.now() - this._startTime
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
await this._resolveExitCode();
|
|
315
|
+
return {
|
|
316
|
+
success: this._exitCode === 0,
|
|
317
|
+
exitCode: this._exitCode ?? 1,
|
|
318
|
+
stdout: this.stdout,
|
|
319
|
+
stderr: this.stderr,
|
|
320
|
+
executionTimeMs: Date.now() - this._startTime
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
async kill() {
|
|
324
|
+
if (this._exitCode !== void 0) return false;
|
|
325
|
+
this._killed = true;
|
|
326
|
+
this._exitCode = 137;
|
|
327
|
+
this._closeStream?.();
|
|
328
|
+
try {
|
|
329
|
+
await this._sandbox.process.kill(this._identifier);
|
|
330
|
+
} catch {
|
|
331
|
+
}
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
async sendStdin(_data) {
|
|
335
|
+
throw new Error("Blaxel sandboxes do not support stdin");
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
var BlaxelProcessManager = class extends SandboxProcessManager {
|
|
339
|
+
constructor(opts = {}) {
|
|
340
|
+
super({ env: opts.env });
|
|
341
|
+
}
|
|
342
|
+
async spawn(command, options = {}) {
|
|
343
|
+
return this.sandbox.retryOnDead(async () => {
|
|
344
|
+
const blaxel = this.sandbox.instance;
|
|
345
|
+
const mergedEnv = { ...this.env, ...options.env };
|
|
346
|
+
const envs = Object.fromEntries(
|
|
347
|
+
Object.entries(mergedEnv).filter((entry) => entry[1] !== void 0)
|
|
348
|
+
);
|
|
349
|
+
const result = await blaxel.process.exec({
|
|
350
|
+
command,
|
|
351
|
+
waitForCompletion: false,
|
|
352
|
+
workingDir: options.cwd,
|
|
353
|
+
...Object.keys(envs).length > 0 && { env: envs },
|
|
354
|
+
...options.timeout && { timeout: Math.ceil(options.timeout / 1e3) }
|
|
355
|
+
});
|
|
356
|
+
const identifier = result.pid;
|
|
357
|
+
const pid = parseInt(identifier, 10);
|
|
358
|
+
const handle = new BlaxelProcessHandle(pid, identifier, blaxel, Date.now(), options);
|
|
359
|
+
const streamControl = blaxel.process.streamLogs(identifier, {
|
|
360
|
+
onStdout: (data) => handle.emitStdout(data),
|
|
361
|
+
onStderr: (data) => handle.emitStderr(data),
|
|
362
|
+
onError: (err) => {
|
|
363
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
364
|
+
handle.emitStderr(msg);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
handle.streamControl = streamControl;
|
|
368
|
+
this._tracked.set(pid, handle);
|
|
369
|
+
return handle;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
async list() {
|
|
373
|
+
const result = [];
|
|
374
|
+
for (const [pid, handle] of this._tracked) {
|
|
375
|
+
result.push({
|
|
376
|
+
pid,
|
|
377
|
+
command: handle.command,
|
|
378
|
+
running: handle.exitCode === void 0,
|
|
379
|
+
exitCode: handle.exitCode
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
async get(pid) {
|
|
385
|
+
return this._tracked.get(pid);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
206
388
|
|
|
207
389
|
// src/sandbox/index.ts
|
|
208
390
|
var SAFE_MOUNT_PATH = /^\/[a-zA-Z0-9_.\-/]+$/;
|
|
@@ -244,9 +426,13 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
244
426
|
ports;
|
|
245
427
|
// Non-optional (initialized by BaseSandbox)
|
|
246
428
|
constructor(options = {}) {
|
|
247
|
-
super({
|
|
429
|
+
super({
|
|
430
|
+
...options,
|
|
431
|
+
name: "BlaxelSandbox",
|
|
432
|
+
processes: new BlaxelProcessManager({ env: options.env })
|
|
433
|
+
});
|
|
248
434
|
this.id = options.id ?? this.generateId();
|
|
249
|
-
this.image = options.image ?? "blaxel/
|
|
435
|
+
this.image = options.image ?? "blaxel/ts-app:latest";
|
|
250
436
|
this.memory = options.memory ?? 4096;
|
|
251
437
|
this.timeout = options.timeout;
|
|
252
438
|
this.env = options.env ?? {};
|
|
@@ -589,7 +775,6 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
589
775
|
image: this.image,
|
|
590
776
|
memory: this.memory,
|
|
591
777
|
...this.timeout && { ttl: this.timeout },
|
|
592
|
-
envs: Object.entries(this.env).map(([name, value]) => ({ name, value })),
|
|
593
778
|
labels: {
|
|
594
779
|
...this.labels,
|
|
595
780
|
"mastra-sandbox-id": this.id
|
|
@@ -647,6 +832,13 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
647
832
|
* Status management is handled by the base class.
|
|
648
833
|
*/
|
|
649
834
|
async stop() {
|
|
835
|
+
if (this.processes) {
|
|
836
|
+
try {
|
|
837
|
+
const procs = await this.processes.list();
|
|
838
|
+
await Promise.all(procs.filter((p) => p.running).map((p) => this.processes.kill(p.pid)));
|
|
839
|
+
} catch {
|
|
840
|
+
}
|
|
841
|
+
}
|
|
650
842
|
for (const mountPath of [...this.mounts.entries.keys()]) {
|
|
651
843
|
try {
|
|
652
844
|
await this.unmount(mountPath);
|
|
@@ -661,6 +853,13 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
661
853
|
* Status management is handled by the base class.
|
|
662
854
|
*/
|
|
663
855
|
async destroy() {
|
|
856
|
+
if (this.processes) {
|
|
857
|
+
try {
|
|
858
|
+
const procs = await this.processes.list();
|
|
859
|
+
await Promise.all(procs.filter((p) => p.running).map((p) => this.processes.kill(p.pid)));
|
|
860
|
+
} catch {
|
|
861
|
+
}
|
|
862
|
+
}
|
|
664
863
|
for (const mountPath of [...this.mounts.entries.keys()]) {
|
|
665
864
|
try {
|
|
666
865
|
await this.unmount(mountPath);
|
|
@@ -711,7 +910,7 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
711
910
|
getInstructions() {
|
|
712
911
|
const mountCount = this.mounts.entries.size;
|
|
713
912
|
const mountInfo = mountCount > 0 ? ` ${mountCount} filesystem(s) mounted via FUSE.` : "";
|
|
714
|
-
return `Cloud sandbox
|
|
913
|
+
return `Cloud sandbox.${mountInfo}`;
|
|
715
914
|
}
|
|
716
915
|
// ---------------------------------------------------------------------------
|
|
717
916
|
// Internal Helpers
|
|
@@ -731,8 +930,8 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
731
930
|
*/
|
|
732
931
|
isSandboxDeadError(error) {
|
|
733
932
|
if (!error) return false;
|
|
734
|
-
const errorStr = errorToString(error);
|
|
735
|
-
return errorStr.includes("
|
|
933
|
+
const errorStr = errorToString(error).toLowerCase();
|
|
934
|
+
return errorStr.includes("terminated") || errorStr.includes("sandbox was not found") || errorStr.includes("sandbox not found") || errorStr.includes('"not found"');
|
|
736
935
|
}
|
|
737
936
|
/**
|
|
738
937
|
* Handle sandbox timeout by clearing the instance and resetting state.
|
|
@@ -750,6 +949,31 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
750
949
|
}
|
|
751
950
|
this.status = "stopped";
|
|
752
951
|
}
|
|
952
|
+
/**
|
|
953
|
+
* Execute an operation with automatic retry if the sandbox is found to be dead.
|
|
954
|
+
*
|
|
955
|
+
* When the Blaxel sandbox times out or crashes mid-operation, this method
|
|
956
|
+
* resets sandbox state, restarts it, and retries the operation once.
|
|
957
|
+
*
|
|
958
|
+
* @internal Used by BlaxelProcessManager to handle dead sandboxes during spawn.
|
|
959
|
+
*/
|
|
960
|
+
async retryOnDead(fn) {
|
|
961
|
+
try {
|
|
962
|
+
return await fn();
|
|
963
|
+
} catch (error) {
|
|
964
|
+
if (this.isSandboxDeadError(error) && !this._isRetrying) {
|
|
965
|
+
this.handleSandboxTimeout();
|
|
966
|
+
this._isRetrying = true;
|
|
967
|
+
try {
|
|
968
|
+
await this.ensureRunning();
|
|
969
|
+
return await fn();
|
|
970
|
+
} finally {
|
|
971
|
+
this._isRetrying = false;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
753
977
|
// ---------------------------------------------------------------------------
|
|
754
978
|
// Command Execution
|
|
755
979
|
// ---------------------------------------------------------------------------
|
|
@@ -764,26 +988,77 @@ var BlaxelSandbox = class extends MastraSandbox {
|
|
|
764
988
|
const startTime = Date.now();
|
|
765
989
|
const fullCommand = args.length > 0 ? `${command} ${args.map(shellQuote).join(" ")}` : command;
|
|
766
990
|
this.logger.debug(`${LOG_PREFIX} Executing: ${fullCommand}`);
|
|
991
|
+
let capturedStdout = "";
|
|
992
|
+
let capturedStderr = "";
|
|
767
993
|
try {
|
|
768
994
|
const mergedEnv = { ...this.env, ...options.env };
|
|
769
995
|
const envRecord = Object.fromEntries(
|
|
770
996
|
Object.entries(mergedEnv).filter((entry) => entry[1] !== void 0)
|
|
771
997
|
);
|
|
772
|
-
const
|
|
998
|
+
const apiTimeout = options.timeout ? Math.ceil(options.timeout / 1e3) : void 0;
|
|
999
|
+
const execPromise = sandbox.process.exec({
|
|
773
1000
|
command: fullCommand,
|
|
774
1001
|
workingDir: options.cwd,
|
|
775
1002
|
env: envRecord,
|
|
776
1003
|
waitForCompletion: true,
|
|
777
|
-
...
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
}
|
|
1004
|
+
...apiTimeout && { timeout: apiTimeout },
|
|
1005
|
+
onStdout: (data) => {
|
|
1006
|
+
capturedStdout += data;
|
|
1007
|
+
options.onStdout?.(data);
|
|
1008
|
+
},
|
|
1009
|
+
onStderr: (data) => {
|
|
1010
|
+
capturedStderr += data;
|
|
1011
|
+
options.onStderr?.(data);
|
|
1012
|
+
}
|
|
782
1013
|
});
|
|
1014
|
+
const racePromises = [];
|
|
1015
|
+
let timer;
|
|
1016
|
+
let abortHandler;
|
|
1017
|
+
if (options.timeout) {
|
|
1018
|
+
racePromises.push(
|
|
1019
|
+
new Promise((_, reject) => {
|
|
1020
|
+
timer = setTimeout(() => {
|
|
1021
|
+
runCommand(sandbox, `pkill -f ${shellQuote(fullCommand)}`, { timeout: 5e3 }).catch(() => {
|
|
1022
|
+
});
|
|
1023
|
+
reject(new Error(`Command timed out after ${options.timeout}ms`));
|
|
1024
|
+
}, options.timeout);
|
|
1025
|
+
})
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
if (options.abortSignal) {
|
|
1029
|
+
if (options.abortSignal.aborted) {
|
|
1030
|
+
runCommand(sandbox, `pkill -f ${shellQuote(fullCommand)}`, { timeout: 5e3 }).catch(() => {
|
|
1031
|
+
});
|
|
1032
|
+
throw new Error("Process aborted");
|
|
1033
|
+
}
|
|
1034
|
+
racePromises.push(
|
|
1035
|
+
new Promise((_, reject) => {
|
|
1036
|
+
abortHandler = () => {
|
|
1037
|
+
runCommand(sandbox, `pkill -f ${shellQuote(fullCommand)}`, { timeout: 5e3 }).catch(() => {
|
|
1038
|
+
});
|
|
1039
|
+
reject(new Error("Process aborted"));
|
|
1040
|
+
};
|
|
1041
|
+
options.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
1042
|
+
})
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
let result;
|
|
1046
|
+
try {
|
|
1047
|
+
if (racePromises.length > 0) {
|
|
1048
|
+
result = await Promise.race([execPromise, ...racePromises]);
|
|
1049
|
+
} else {
|
|
1050
|
+
result = await execPromise;
|
|
1051
|
+
}
|
|
1052
|
+
} finally {
|
|
1053
|
+
if (timer) clearTimeout(timer);
|
|
1054
|
+
if (abortHandler && options.abortSignal) {
|
|
1055
|
+
options.abortSignal.removeEventListener("abort", abortHandler);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
783
1058
|
const executionTimeMs = Date.now() - startTime;
|
|
784
1059
|
const exitCode = result.exitCode ?? 0;
|
|
785
|
-
const stdout = result.stdout
|
|
786
|
-
const stderr = result.stderr
|
|
1060
|
+
const stdout = capturedStdout || result.stdout || "";
|
|
1061
|
+
const stderr = capturedStderr || result.stderr || "";
|
|
787
1062
|
this.logger.debug(`${LOG_PREFIX} Exit code: ${exitCode} (${executionTimeMs}ms)`);
|
|
788
1063
|
if (stdout) this.logger.debug(`${LOG_PREFIX} stdout:
|
|
789
1064
|
${stdout}`);
|
|
@@ -809,13 +1084,11 @@ ${stderr}`);
|
|
|
809
1084
|
}
|
|
810
1085
|
}
|
|
811
1086
|
const executionTimeMs = Date.now() - startTime;
|
|
812
|
-
const stderr = errorToString(error);
|
|
813
|
-
this.logger.debug(`${LOG_PREFIX} Execution error (${executionTimeMs}ms): ${stderr}`);
|
|
814
1087
|
return {
|
|
815
1088
|
success: false,
|
|
816
1089
|
exitCode: 1,
|
|
817
|
-
stdout:
|
|
818
|
-
stderr,
|
|
1090
|
+
stdout: capturedStdout,
|
|
1091
|
+
stderr: capturedStderr || errorToString(error),
|
|
819
1092
|
executionTimeMs,
|
|
820
1093
|
command,
|
|
821
1094
|
args
|
|
@@ -824,6 +1097,6 @@ ${stderr}`);
|
|
|
824
1097
|
}
|
|
825
1098
|
};
|
|
826
1099
|
|
|
827
|
-
export { BlaxelSandbox };
|
|
1100
|
+
export { BlaxelProcessManager, BlaxelSandbox };
|
|
828
1101
|
//# sourceMappingURL=index.js.map
|
|
829
1102
|
//# sourceMappingURL=index.js.map
|