@mastra/deployer-sandbox 0.2.2 → 0.3.0-alpha.1
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 +44 -0
- package/README.md +13 -0
- package/dist/index.cjs +178 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +177 -21
- package/dist/index.js.map +1 -1
- package/dist/studio/assets/{core-CbvhbAyq.js → core-Dx7mVglv.js} +1 -1
- package/dist/studio/assets/{index-DUdZoPvJ.js → index-DanFGEb-.js} +2 -2
- package/dist/studio/assets/{main-tYRi5VOV.js → main-CAJWNDlQ.js} +284 -389
- package/dist/studio/assets/style-fSltWkSH.css +1 -0
- package/dist/studio/index.html +2 -2
- package/dist/types.d.ts +41 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts +3 -1
- package/dist/worker.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/studio/assets/style-CUsRHh-q.css +0 -1
package/dist/index.js
CHANGED
|
@@ -379,6 +379,18 @@ var SandboxDeployer = class extends Deployer {
|
|
|
379
379
|
}
|
|
380
380
|
};
|
|
381
381
|
//#endregion
|
|
382
|
+
//#region src/types.ts
|
|
383
|
+
/** Error thrown before deployment when requested resource limits cannot be enforced. */
|
|
384
|
+
var SandboxWorkerCapabilityError = class extends Error {
|
|
385
|
+
capability;
|
|
386
|
+
code = "SANDBOX_WORKER_CAPABILITY_UNAVAILABLE";
|
|
387
|
+
constructor(capability, message, options) {
|
|
388
|
+
super(message ?? `Worker resource-limit capability "${capability}" is unavailable.`, options);
|
|
389
|
+
this.capability = capability;
|
|
390
|
+
this.name = "SandboxWorkerCapabilityError";
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
//#endregion
|
|
382
394
|
//#region src/worker.ts
|
|
383
395
|
const ARCHIVE = ".mastra-worker.tar.gz";
|
|
384
396
|
const RUNTIME_DIR = ".mastra/executions";
|
|
@@ -388,13 +400,17 @@ const ARTIFACT_LOCK = ".mastra-artifact-lock";
|
|
|
388
400
|
const EXECUTION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
389
401
|
const DEFAULT_INPUT_LIMIT = 16 * 1024 * 1024;
|
|
390
402
|
const DEFAULT_OUTPUT_READ_LIMIT = 1024 * 1024;
|
|
403
|
+
const RESOURCE_CAPABILITY_PREFIX = "MASTRA_WORKER_CAPABILITY:";
|
|
391
404
|
async function deployWorkerToSandbox(options) {
|
|
392
405
|
validateOptions(options);
|
|
393
|
-
const { sandbox, dir, executionId, command, mode = "worker", args = [], env = {}, workingDirectory = ".", installCommand = "npm install --omit=dev", startupTimeoutMs = 1e4, executionTimeoutMs, terminationGraceMs = 5e3, inputLimitBytes = DEFAULT_INPUT_LIMIT } = options;
|
|
406
|
+
const { sandbox, dir, executionId, command, mode = "worker", args = [], env = {}, workingDirectory = ".", installCommand = "npm install --omit=dev", startupTimeoutMs = 1e4, executionTimeoutMs, resourceLimits: requestedResourceLimits, terminationGraceMs = 5e3, inputLimitBytes = DEFAULT_INPUT_LIMIT } = options;
|
|
407
|
+
const resourceLimits = normalizeResourceLimits(requestedResourceLimits);
|
|
408
|
+
if (resourceLimits) await preflightResourceLimits(sandbox, resourceLimits);
|
|
394
409
|
const remoteDir = await resolveRemoteDir(sandbox, options.remoteDir);
|
|
395
410
|
const config = {
|
|
396
411
|
sandbox,
|
|
397
412
|
remoteDir,
|
|
413
|
+
resolveRemoteDir: async () => remoteDir,
|
|
398
414
|
command,
|
|
399
415
|
args,
|
|
400
416
|
env,
|
|
@@ -402,6 +418,7 @@ async function deployWorkerToSandbox(options) {
|
|
|
402
418
|
mode,
|
|
403
419
|
startupTimeoutMs,
|
|
404
420
|
executionTimeoutMs,
|
|
421
|
+
resourceLimits,
|
|
405
422
|
terminationGraceMs,
|
|
406
423
|
inputLimitBytes
|
|
407
424
|
};
|
|
@@ -431,6 +448,20 @@ async function deployWorkerToSandbox(options) {
|
|
|
431
448
|
}
|
|
432
449
|
return createExecution(config, executionId, options.input);
|
|
433
450
|
}
|
|
451
|
+
/** Reattach to a persisted worker execution without its original launch configuration. */
|
|
452
|
+
async function attachWorkerDeployment(options) {
|
|
453
|
+
if (!options.sandbox.executeCommand) throw new Error(`Sandbox provider "${options.sandbox.provider}" does not support executeCommand, which is required for worker deploys.`);
|
|
454
|
+
validateExecutionId(options.executionId);
|
|
455
|
+
if (options.terminationGraceMs !== void 0 && (!Number.isFinite(options.terminationGraceMs) || options.terminationGraceMs <= 0)) throw new Error("terminationGraceMs must be greater than zero.");
|
|
456
|
+
let remoteDir;
|
|
457
|
+
const config = {
|
|
458
|
+
sandbox: options.sandbox,
|
|
459
|
+
resolveRemoteDir: async () => remoteDir ??= await resolveRemoteDir(options.sandbox, options.remoteDir),
|
|
460
|
+
terminationGraceMs: options.terminationGraceMs ?? 5e3
|
|
461
|
+
};
|
|
462
|
+
const info = await getInfoSafe(options.sandbox);
|
|
463
|
+
return execution(config, options.executionId, info?.id ?? options.sandbox.id, info?.timeoutAt);
|
|
464
|
+
}
|
|
434
465
|
function validateOptions(options) {
|
|
435
466
|
if (!options.sandbox.executeCommand) throw new Error(`Sandbox provider "${options.sandbox.provider}" does not support executeCommand, which is required for worker deploys.`);
|
|
436
467
|
validateExecutionId(options.executionId);
|
|
@@ -445,6 +476,22 @@ function validateOptions(options) {
|
|
|
445
476
|
["executionTimeoutMs", options.executionTimeoutMs],
|
|
446
477
|
["terminationGraceMs", options.terminationGraceMs]
|
|
447
478
|
]) if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) throw new Error(`${name} must be greater than zero.`);
|
|
479
|
+
const resourceLimits = options.resourceLimits;
|
|
480
|
+
if (resourceLimits) {
|
|
481
|
+
const knownLimits = /* @__PURE__ */ new Set([
|
|
482
|
+
"cpuTimeSeconds",
|
|
483
|
+
"addressSpaceBytes",
|
|
484
|
+
"fileSizeBytes",
|
|
485
|
+
"openFiles"
|
|
486
|
+
]);
|
|
487
|
+
for (const name of Object.keys(resourceLimits)) if (!knownLimits.has(name)) throw new Error(`Unknown worker resource limit: ${name}.`);
|
|
488
|
+
for (const [name, value] of [
|
|
489
|
+
["cpuTimeSeconds", resourceLimits.cpuTimeSeconds],
|
|
490
|
+
["addressSpaceBytes", resourceLimits.addressSpaceBytes],
|
|
491
|
+
["fileSizeBytes", resourceLimits.fileSizeBytes],
|
|
492
|
+
["openFiles", resourceLimits.openFiles]
|
|
493
|
+
]) if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) throw new Error(`Worker resourceLimits.${name} must be a positive safe integer.`);
|
|
494
|
+
}
|
|
448
495
|
}
|
|
449
496
|
function validateRelativePath(value, label) {
|
|
450
497
|
if (!value || posix.isAbsolute(value) || posix.normalize(value).startsWith("..")) throw new Error(`Worker ${label} must stay within the deployed artifact root.`);
|
|
@@ -452,6 +499,60 @@ function validateRelativePath(value, label) {
|
|
|
452
499
|
function validateInput(input) {
|
|
453
500
|
if (input?.type === "file") validateRelativePath(input.path, "input file path");
|
|
454
501
|
}
|
|
502
|
+
function normalizeResourceLimits(limits) {
|
|
503
|
+
if (!limits || Object.values(limits).every((value) => value === void 0)) return void 0;
|
|
504
|
+
return {
|
|
505
|
+
cpuTimeSeconds: limits.cpuTimeSeconds,
|
|
506
|
+
addressSpaceBytes: limits.addressSpaceBytes,
|
|
507
|
+
addressSpaceKilobytes: limits.addressSpaceBytes === void 0 ? void 0 : Math.floor(limits.addressSpaceBytes / 1024),
|
|
508
|
+
fileSizeBytes: limits.fileSizeBytes,
|
|
509
|
+
fileSizeBlocks: limits.fileSizeBytes === void 0 ? void 0 : Math.floor(limits.fileSizeBytes / 512),
|
|
510
|
+
openFiles: limits.openFiles
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
async function preflightResourceLimits(sandbox, resourceLimits) {
|
|
514
|
+
const checks = [];
|
|
515
|
+
if (resourceLimits.cpuTimeSeconds !== void 0) checks.push(`check_limit cpu_time -t ${resourceLimits.cpuTimeSeconds}`);
|
|
516
|
+
if (resourceLimits.addressSpaceKilobytes !== void 0) checks.push(`check_limit address_space -v ${resourceLimits.addressSpaceKilobytes}`);
|
|
517
|
+
if (resourceLimits.fileSizeBlocks !== void 0) checks.push(`check_limit file_size -f ${resourceLimits.fileSizeBlocks}`);
|
|
518
|
+
if (resourceLimits.openFiles !== void 0) checks.push(`check_limit open_files -n ${resourceLimits.openFiles}`);
|
|
519
|
+
if (resourceLimits.cpuTimeSeconds !== void 0) checks.push(`kill -l XCPU >/dev/null 2>&1 || fail cpu_signal`);
|
|
520
|
+
if (resourceLimits.fileSizeBytes !== void 0) checks.push(`kill -l XFSZ >/dev/null 2>&1 || fail file_size_signal`);
|
|
521
|
+
const script = `
|
|
522
|
+
fail() {
|
|
523
|
+
printf '${RESOURCE_CAPABILITY_PREFIX}%s\\n' "$1" >&2
|
|
524
|
+
exit 1
|
|
525
|
+
}
|
|
526
|
+
check_limit() {
|
|
527
|
+
capability="$1"
|
|
528
|
+
flag="$2"
|
|
529
|
+
value="$3"
|
|
530
|
+
(
|
|
531
|
+
ulimit -S "$flag" "$value" >/dev/null 2>&1 || exit 1
|
|
532
|
+
ulimit -H "$flag" "$value" >/dev/null 2>&1 || exit 1
|
|
533
|
+
[ "$(ulimit -S "$flag")" = "$value" ] || exit 1
|
|
534
|
+
[ "$(ulimit -H "$flag")" = "$value" ] || exit 1
|
|
535
|
+
if ulimit -H "$flag" "$((value + 1))" >/dev/null 2>&1; then exit 1; fi
|
|
536
|
+
) || fail "$capability"
|
|
537
|
+
}
|
|
538
|
+
[ "$(uname -s 2>/dev/null)" = Linux ] && [ -r /proc/self/stat ] || fail linux_proc
|
|
539
|
+
command -v setsid >/dev/null 2>&1 || fail process_groups
|
|
540
|
+
setsid sh -c 'kill -0 -$$ 2>/dev/null' || fail process_groups
|
|
541
|
+
${checks.join("\n")}
|
|
542
|
+
`;
|
|
543
|
+
let result;
|
|
544
|
+
try {
|
|
545
|
+
result = await runInSandbox(sandbox, script, {
|
|
546
|
+
allowFailure: true,
|
|
547
|
+
label: "preflight worker resource limits"
|
|
548
|
+
});
|
|
549
|
+
} catch (error) {
|
|
550
|
+
throw new SandboxWorkerCapabilityError("sandbox_command", void 0, { cause: error });
|
|
551
|
+
}
|
|
552
|
+
if (result.exitCode === 0) return;
|
|
553
|
+
const detail = `${result.stderr}\n${result.stdout}`;
|
|
554
|
+
throw new SandboxWorkerCapabilityError(detail.match(new RegExp(`${RESOURCE_CAPABILITY_PREFIX}([a-z_]+)`))?.[1] ?? "sandbox_command", void 0, { cause: new Error(detail.trim() || "Resource-limit preflight command failed.") });
|
|
555
|
+
}
|
|
455
556
|
async function acquireLock(sandbox, lock, timeout, label) {
|
|
456
557
|
const timeoutMs = timeout ?? 6e5;
|
|
457
558
|
const attempts = Math.max(1, Math.ceil(timeoutMs / 1e3));
|
|
@@ -503,25 +604,32 @@ async function createExecution(config, executionId, input) {
|
|
|
503
604
|
await writeFailedStatus(config.sandbox, paths, executionId, "launch", error);
|
|
504
605
|
throw workerPhaseError("launch", error);
|
|
505
606
|
}
|
|
506
|
-
const
|
|
507
|
-
|
|
607
|
+
const resolvePaths = async () => paths;
|
|
608
|
+
const startup = await waitForStartup(config, executionId, resolvePaths);
|
|
609
|
+
if (startup.state === "timed_out") await cancelExecution(config, executionId, resolvePaths, "startup");
|
|
508
610
|
if (startup.state === "failed" || startup.state === "timed_out" || startup.state === "provider_unavailable") throw new Error(`Worker ${startup.state} during startup${"message" in startup && startup.message ? `: ${startup.message}` : ""}.`);
|
|
509
611
|
const info = await getInfoSafe(config.sandbox);
|
|
510
|
-
return deployment(config, executionId,
|
|
612
|
+
return deployment(config, executionId, info?.id ?? config.sandbox.id ?? "unknown", info?.timeoutAt);
|
|
511
613
|
}
|
|
512
|
-
function
|
|
614
|
+
function execution(config, executionId, sandboxId, expiresAt) {
|
|
615
|
+
const resolvePaths = async () => executionPaths(await config.resolveRemoteDir(), executionId);
|
|
513
616
|
return {
|
|
514
617
|
sandboxId,
|
|
515
618
|
executionId,
|
|
516
619
|
expiresAt,
|
|
517
|
-
status: (options) => readWorkerStatus(config.sandbox, executionId,
|
|
518
|
-
readOutput: (stream, options) => readOutput(config.sandbox, executionId,
|
|
519
|
-
cancel: () => cancelExecution(config, executionId,
|
|
620
|
+
status: (options) => readWorkerStatus(config.sandbox, executionId, resolvePaths, options),
|
|
621
|
+
readOutput: (stream, options) => readOutput(config.sandbox, executionId, resolvePaths, stream, options),
|
|
622
|
+
cancel: () => cancelExecution(config, executionId, resolvePaths),
|
|
520
623
|
stop: async () => {
|
|
521
624
|
if (!config.sandbox.stop) throw new Error(`Sandbox provider "${config.sandbox.provider}" does not support stop.`);
|
|
522
625
|
await config.sandbox.stop();
|
|
523
626
|
},
|
|
524
|
-
destroy: (options) => destroyWithRetry(config.sandbox, options)
|
|
627
|
+
destroy: (options) => destroyWithRetry(config.sandbox, options)
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function deployment(config, executionId, sandboxId, expiresAt) {
|
|
631
|
+
return {
|
|
632
|
+
...execution(config, executionId, sandboxId, expiresAt),
|
|
525
633
|
relaunch: async (options) => {
|
|
526
634
|
if (options.executionId === executionId) throw new Error("Relaunch requires a new executionId.");
|
|
527
635
|
validateInput(options.input);
|
|
@@ -543,8 +651,31 @@ function buildExecutionScript(config, paths, stdinPath) {
|
|
|
543
651
|
const envPrefix = Object.entries(config.env).map(([key, value]) => `${key}=${shellQuote(value)}`).join(" ");
|
|
544
652
|
const executable = [shellQuote(config.command), ...config.args.map(shellQuote)].join(" ");
|
|
545
653
|
const target = `${envPrefix ? `env ${envPrefix} ` : ""}${executable}`;
|
|
654
|
+
const limitCommands = [];
|
|
655
|
+
if (config.resourceLimits?.cpuTimeSeconds !== void 0) {
|
|
656
|
+
limitCommands.push(`ulimit -S -t ${config.resourceLimits.cpuTimeSeconds}`);
|
|
657
|
+
limitCommands.push(`ulimit -H -t ${config.resourceLimits.cpuTimeSeconds}`);
|
|
658
|
+
}
|
|
659
|
+
if (config.resourceLimits?.addressSpaceKilobytes !== void 0) {
|
|
660
|
+
limitCommands.push(`ulimit -S -v ${config.resourceLimits.addressSpaceKilobytes}`);
|
|
661
|
+
limitCommands.push(`ulimit -H -v ${config.resourceLimits.addressSpaceKilobytes}`);
|
|
662
|
+
}
|
|
663
|
+
if (config.resourceLimits?.fileSizeBlocks !== void 0) {
|
|
664
|
+
limitCommands.push(`ulimit -S -f ${config.resourceLimits.fileSizeBlocks}`);
|
|
665
|
+
limitCommands.push(`ulimit -H -f ${config.resourceLimits.fileSizeBlocks}`);
|
|
666
|
+
}
|
|
667
|
+
if (config.resourceLimits?.openFiles !== void 0) {
|
|
668
|
+
limitCommands.push(`ulimit -S -n ${config.resourceLimits.openFiles}`);
|
|
669
|
+
limitCommands.push(`ulimit -H -n ${config.resourceLimits.openFiles}`);
|
|
670
|
+
}
|
|
671
|
+
const workload = config.resourceLimits ? `${limitCommands.map((command) => `${command} || exit 125`).join("\n")}\nexec ${target}` : `exec ${target}`;
|
|
546
672
|
const graceAttempts = Math.max(1, Math.ceil(config.terminationGraceMs / 1e3));
|
|
547
673
|
const state = (value) => `tmp=${shellQuote(`${paths.status}.tmp.$$`)}; printf '%s\\n' ${shellQuote(value)} > "$tmp"; mv "$tmp" ${shellQuote(paths.status)};`;
|
|
674
|
+
const normalExitStatus = `signal=''; if [ "$code" -gt 128 ]; then signal="SIG$((code - 128))"; fi; tmp=${shellQuote(`${paths.status}.tmp.$$`)}; printf 'exited|%s|%s|%s\\n' "$execution_id" "$code" "$signal" > "$tmp"; mv "$tmp" ${shellQuote(paths.status)};`;
|
|
675
|
+
const resourceExitBranches = [];
|
|
676
|
+
if (config.resourceLimits?.cpuTimeSeconds !== void 0) resourceExitBranches.push(`if xcpu="$(kill -l XCPU 2>/dev/null)" && [ -n "$xcpu" ] && [ "$code" -eq $((128 + xcpu)) ]; then ${state(`resource_exhausted|${paths.executionId}|cpu|${config.resourceLimits.cpuTimeSeconds}|SIGXCPU`)}`);
|
|
677
|
+
if (config.resourceLimits?.fileSizeBytes !== void 0) resourceExitBranches.push(`${resourceExitBranches.length ? "elif" : "if"} xfsz="$(kill -l XFSZ 2>/dev/null)" && [ -n "$xfsz" ] && [ "$code" -eq $((128 + xfsz)) ]; then ${state(`resource_exhausted|${paths.executionId}|file_size|${config.resourceLimits.fileSizeBytes}|SIGXFSZ`)}`);
|
|
678
|
+
const resourceExitStatus = resourceExitBranches.length ? `${resourceExitBranches.join(" ")} else ${normalExitStatus} fi` : normalExitStatus;
|
|
548
679
|
return [
|
|
549
680
|
"#!/bin/sh",
|
|
550
681
|
`cd ${shellQuote(cwd)}`,
|
|
@@ -555,7 +686,7 @@ function buildExecutionScript(config, paths, stdinPath) {
|
|
|
555
686
|
`tokenfile=${shellQuote(paths.pidToken)}`,
|
|
556
687
|
`: > "$stdout"; : > "$stderr"`,
|
|
557
688
|
state(`starting|${paths.executionId}`),
|
|
558
|
-
`setsid sh -c ${shellQuote(
|
|
689
|
+
`setsid sh -c ${shellQuote(`${workload}${stdinPath ? ` < ${shellQuote(stdinPath)}` : ""}`)} > "$stdout" 2> "$stderr" &`,
|
|
559
690
|
"child=$!",
|
|
560
691
|
"printf %s \"$child\" > \"$pidfile\"",
|
|
561
692
|
`if [ -r "/proc/$child/stat" ]; then awk '{print $22}' "/proc/$child/stat" > "$tokenfile"; else : > "$tokenfile"; fi`,
|
|
@@ -567,7 +698,7 @@ function buildExecutionScript(config, paths, stdinPath) {
|
|
|
567
698
|
"code=$?",
|
|
568
699
|
...config.executionTimeoutMs ? ["kill \"$watchdog\" 2>/dev/null || true"] : [],
|
|
569
700
|
`current="$(cat ${shellQuote(paths.status)} 2>/dev/null || true)"`,
|
|
570
|
-
`case "$current" in timed_out*) ;; *) if [ "$cancelled" -eq 1 ]; then ${state(`cancelled|${paths.executionId}|TERM`)} else
|
|
701
|
+
`case "$current" in timed_out*) ;; *) if [ "$cancelled" -eq 1 ]; then ${state(`cancelled|${paths.executionId}|TERM`)} else ${resourceExitStatus} fi ;; esac`,
|
|
571
702
|
"rm -f \"$pidfile\" \"$tokenfile\"",
|
|
572
703
|
"exit \"$code\""
|
|
573
704
|
].join("\n");
|
|
@@ -575,10 +706,10 @@ function buildExecutionScript(config, paths, stdinPath) {
|
|
|
575
706
|
async function launchExecution(sandbox, paths) {
|
|
576
707
|
await runInSandbox(sandbox, `setsid nohup sh ${shellQuote(paths.script)} >/dev/null 2>&1 & echo $!`, { label: "launch worker execution" });
|
|
577
708
|
}
|
|
578
|
-
async function waitForStartup(config, executionId,
|
|
709
|
+
async function waitForStartup(config, executionId, resolvePaths) {
|
|
579
710
|
const deadline = Date.now() + config.startupTimeoutMs;
|
|
580
711
|
while (Date.now() < deadline) {
|
|
581
|
-
const status = await readWorkerStatus(config.sandbox, executionId,
|
|
712
|
+
const status = await readWorkerStatus(config.sandbox, executionId, resolvePaths);
|
|
582
713
|
if (status.state !== "unknown" && status.state !== "starting") return status;
|
|
583
714
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
584
715
|
}
|
|
@@ -588,7 +719,7 @@ async function waitForStartup(config, executionId, paths) {
|
|
|
588
719
|
phase: "startup"
|
|
589
720
|
};
|
|
590
721
|
}
|
|
591
|
-
async function readWorkerStatus(sandbox, executionId,
|
|
722
|
+
async function readWorkerStatus(sandbox, executionId, resolvePaths, options) {
|
|
592
723
|
const providerState = sandbox.status;
|
|
593
724
|
if (providerState === "destroyed" || providerState === "destroying") return {
|
|
594
725
|
state: "provider_unavailable",
|
|
@@ -613,6 +744,7 @@ async function readWorkerStatus(sandbox, executionId, paths, options) {
|
|
|
613
744
|
}
|
|
614
745
|
}
|
|
615
746
|
try {
|
|
747
|
+
const paths = await resolvePaths();
|
|
616
748
|
const result = await runInSandbox(sandbox, [
|
|
617
749
|
`status="$(cat ${shellQuote(paths.status)} 2>/dev/null || true)"`,
|
|
618
750
|
`if [ -f ${shellQuote(paths.pid)} ]; then`,
|
|
@@ -644,7 +776,7 @@ async function readWorkerStatus(sandbox, executionId, paths, options) {
|
|
|
644
776
|
}
|
|
645
777
|
}
|
|
646
778
|
function parseStatus(executionId, value) {
|
|
647
|
-
const [state, recordedId, first, second] = value.split("|");
|
|
779
|
+
const [state, recordedId, first, second, third] = value.split("|");
|
|
648
780
|
if (recordedId !== executionId || state === "stale") return {
|
|
649
781
|
state: "unknown",
|
|
650
782
|
executionId
|
|
@@ -669,6 +801,27 @@ function parseStatus(executionId, value) {
|
|
|
669
801
|
executionId
|
|
670
802
|
};
|
|
671
803
|
}
|
|
804
|
+
if (state === "resource_exhausted") {
|
|
805
|
+
const limit = Number(second);
|
|
806
|
+
if (first === "cpu" && Number.isSafeInteger(limit) && limit > 0 && third === "SIGXCPU") return {
|
|
807
|
+
state,
|
|
808
|
+
executionId,
|
|
809
|
+
resource: first,
|
|
810
|
+
limit,
|
|
811
|
+
signal: third
|
|
812
|
+
};
|
|
813
|
+
if (first === "file_size" && Number.isSafeInteger(limit) && limit > 0 && third === "SIGXFSZ") return {
|
|
814
|
+
state,
|
|
815
|
+
executionId,
|
|
816
|
+
resource: first,
|
|
817
|
+
limit,
|
|
818
|
+
signal: third
|
|
819
|
+
};
|
|
820
|
+
return {
|
|
821
|
+
state: "unknown",
|
|
822
|
+
executionId
|
|
823
|
+
};
|
|
824
|
+
}
|
|
672
825
|
if (state === "cancelled") return {
|
|
673
826
|
state,
|
|
674
827
|
executionId,
|
|
@@ -690,9 +843,10 @@ function parseStatus(executionId, value) {
|
|
|
690
843
|
executionId
|
|
691
844
|
};
|
|
692
845
|
}
|
|
693
|
-
async function cancelExecution(config, executionId,
|
|
694
|
-
const current = await readWorkerStatus(config.sandbox, executionId,
|
|
846
|
+
async function cancelExecution(config, executionId, resolvePaths, timeoutPhase) {
|
|
847
|
+
const current = await readWorkerStatus(config.sandbox, executionId, resolvePaths);
|
|
695
848
|
if (current.state !== "running" && current.state !== "starting") return current;
|
|
849
|
+
const paths = await resolvePaths();
|
|
696
850
|
const attempts = Math.max(1, Math.ceil(config.terminationGraceMs / 1e3));
|
|
697
851
|
const terminal = timeoutPhase ? `timed_out|${executionId}|startup` : `cancelled|${executionId}|TERM`;
|
|
698
852
|
await runInSandbox(config.sandbox, [
|
|
@@ -713,11 +867,12 @@ async function cancelExecution(config, executionId, paths, timeoutPhase) {
|
|
|
713
867
|
});
|
|
714
868
|
return parseStatus(executionId, terminal);
|
|
715
869
|
}
|
|
716
|
-
async function readOutput(sandbox, executionId,
|
|
870
|
+
async function readOutput(sandbox, executionId, resolvePaths, stream, options) {
|
|
717
871
|
const offset = Math.max(0, Math.floor(options?.offset ?? 0));
|
|
718
872
|
const maxBytes = Math.max(1, Math.floor(options?.maxBytes ?? DEFAULT_OUTPUT_READ_LIMIT));
|
|
719
|
-
const path = stream === "stdout" ? paths.stdout : paths.stderr;
|
|
720
873
|
try {
|
|
874
|
+
const paths = await resolvePaths();
|
|
875
|
+
const path = stream === "stdout" ? paths.stdout : paths.stderr;
|
|
721
876
|
const result = await runInSandbox(sandbox, `size=$(wc -c < ${shellQuote(path)} 2>/dev/null || echo 0); printf '%s\\n' "$size"; tail -c +${offset + 1} ${shellQuote(path)} 2>/dev/null | head -c ${maxBytes} | base64`, {
|
|
722
877
|
allowFailure: true,
|
|
723
878
|
label: `read worker ${stream}`
|
|
@@ -728,9 +883,10 @@ async function readOutput(sandbox, executionId, paths, stream, options) {
|
|
|
728
883
|
const encoded = newline === -1 ? "" : result.stdout.slice(newline + 1).replace(/\s/g, "");
|
|
729
884
|
const data = Buffer.from(encoded, "base64");
|
|
730
885
|
const nextOffset = offset + data.byteLength;
|
|
731
|
-
const status = await readWorkerStatus(sandbox, executionId,
|
|
886
|
+
const status = await readWorkerStatus(sandbox, executionId, resolvePaths);
|
|
732
887
|
const terminal = [
|
|
733
888
|
"exited",
|
|
889
|
+
"resource_exhausted",
|
|
734
890
|
"cancelled",
|
|
735
891
|
"timed_out",
|
|
736
892
|
"failed"
|
|
@@ -819,6 +975,6 @@ function sanitizeStatusValue(value) {
|
|
|
819
975
|
return value.replace(/[|\r\n]/g, " ").slice(0, 500);
|
|
820
976
|
}
|
|
821
977
|
//#endregion
|
|
822
|
-
export { MANIFEST_FILENAME, SandboxDeployer, buildLaunchScript, deployToSandbox, deployWorkerToSandbox, readDeploymentManifest, updateEdgeConfigAlias, writeDeploymentManifest };
|
|
978
|
+
export { MANIFEST_FILENAME, SandboxDeployer, SandboxWorkerCapabilityError, attachWorkerDeployment, buildLaunchScript, deployToSandbox, deployWorkerToSandbox, readDeploymentManifest, updateEdgeConfigAlias, writeDeploymentManifest };
|
|
823
979
|
|
|
824
980
|
//# sourceMappingURL=index.js.map
|