@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/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 startup = await waitForStartup(config, executionId, paths);
507
- if (startup.state === "timed_out") await cancelExecution(config, executionId, paths, "startup");
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, paths, info?.id ?? config.sandbox.id ?? "unknown", info?.timeoutAt);
612
+ return deployment(config, executionId, info?.id ?? config.sandbox.id ?? "unknown", info?.timeoutAt);
511
613
  }
512
- function deployment(config, executionId, paths, sandboxId, expiresAt) {
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, paths, options),
518
- readOutput: (stream, options) => readOutput(config.sandbox, executionId, paths, stream, options),
519
- cancel: () => cancelExecution(config, executionId, paths),
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(`exec ${target}${stdinPath ? ` < ${shellQuote(stdinPath)}` : ""}`)} > "$stdout" 2> "$stderr" &`,
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 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)}; fi ;; esac`,
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, paths) {
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, paths);
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, paths, options) {
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, paths, timeoutPhase) {
694
- const current = await readWorkerStatus(config.sandbox, executionId, paths);
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, paths, stream, options) {
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, paths);
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