@mastra/e2b 0.6.0 → 0.7.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 +77 -0
- package/dist/index.cjs +110 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +110 -16
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +45 -3
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -568,11 +568,34 @@ function validateMountPath(mountPath) {
|
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
570
|
var SAFE_MARKER_NAME = /^mount-[a-z0-9]+$/;
|
|
571
|
-
var E2BSandbox = class extends MastraSandbox {
|
|
571
|
+
var E2BSandbox = class _E2BSandbox extends MastraSandbox {
|
|
572
572
|
id;
|
|
573
573
|
name = "E2BSandbox";
|
|
574
574
|
provider = "e2b";
|
|
575
575
|
status = "pending";
|
|
576
|
+
/**
|
|
577
|
+
* Networking capability: public HTTPS URLs for sandbox ports.
|
|
578
|
+
* E2B exposes every port via `getHost(port)` — no upfront declaration needed.
|
|
579
|
+
*
|
|
580
|
+
* When not attached in this process, the URL is resolved by looking up the
|
|
581
|
+
* existing sandbox by identity (without resuming it) and deriving the host
|
|
582
|
+
* (`{port}-{sandboxId}.{domain}`), so other processes can resolve
|
|
583
|
+
* deployments without waking a paused sandbox.
|
|
584
|
+
*/
|
|
585
|
+
networking = {
|
|
586
|
+
getPortUrl: async (port) => {
|
|
587
|
+
try {
|
|
588
|
+
if (this._sandbox) {
|
|
589
|
+
return `https://${this._sandbox.getHost(port)}`;
|
|
590
|
+
}
|
|
591
|
+
const info = await this.lookupExistingSandboxInfo();
|
|
592
|
+
if (!info) return null;
|
|
593
|
+
return `https://${port}-${info.sandboxId}.${this.sandboxDomain}`;
|
|
594
|
+
} catch {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
};
|
|
576
599
|
_sandbox = null;
|
|
577
600
|
_createdAt = null;
|
|
578
601
|
_isRetrying = false;
|
|
@@ -583,6 +606,7 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
583
606
|
network;
|
|
584
607
|
connectionOpts;
|
|
585
608
|
_instructionsOverride;
|
|
609
|
+
_constructorOptions;
|
|
586
610
|
/** Resolved template ID after building (if needed) */
|
|
587
611
|
_resolvedTemplateId;
|
|
588
612
|
/** Promise for template preparation (started in constructor) */
|
|
@@ -606,11 +630,34 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
606
630
|
...options.accessToken && { accessToken: options.accessToken }
|
|
607
631
|
};
|
|
608
632
|
this._instructionsOverride = options.instructions;
|
|
633
|
+
this._constructorOptions = { ...options };
|
|
609
634
|
this._templatePreparePromise = this.resolveTemplate().catch((err) => {
|
|
610
635
|
this.logger.debug(`${LOG_PREFIX} Template preparation error (will retry on start):`, err);
|
|
611
636
|
return "";
|
|
612
637
|
});
|
|
613
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Construct a sibling `E2BSandbox` that inherits this sandbox's
|
|
641
|
+
* configuration (credentials, template, network, metadata, instructions)
|
|
642
|
+
* with per-instance overrides.
|
|
643
|
+
*
|
|
644
|
+
* Performs no I/O — the sandbox clone provisions (or reconnects to an
|
|
645
|
+
* existing E2B sandbox with the same logical `id`) on its own `start()`.
|
|
646
|
+
* Use it when one configured sandbox acts as the template for a fleet of
|
|
647
|
+
* independent sandboxes (e.g. one per project).
|
|
648
|
+
*
|
|
649
|
+
* `options.idleTimeoutMinutes` maps to the E2B sandbox `timeout` (ms);
|
|
650
|
+
* `options.sandboxId` is ignored because E2B reconnects by logical `id`.
|
|
651
|
+
*/
|
|
652
|
+
clone(options = {}) {
|
|
653
|
+
const { id: _id, ...base } = this._constructorOptions;
|
|
654
|
+
return new _E2BSandbox({
|
|
655
|
+
...base,
|
|
656
|
+
...options.id !== void 0 && { id: options.id },
|
|
657
|
+
...options.env !== void 0 && { env: options.env },
|
|
658
|
+
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
659
|
+
});
|
|
660
|
+
}
|
|
614
661
|
/**
|
|
615
662
|
* Get the underlying E2B Sandbox instance for direct access to E2B APIs.
|
|
616
663
|
*
|
|
@@ -708,22 +755,32 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
708
755
|
this._createdAt = /* @__PURE__ */ new Date();
|
|
709
756
|
}
|
|
710
757
|
/**
|
|
711
|
-
* Stop the E2B sandbox.
|
|
712
|
-
*
|
|
758
|
+
* Stop the E2B sandbox by pausing it (snapshot-stop).
|
|
759
|
+
*
|
|
760
|
+
* Pausing freezes the whole VM — filesystem, memory, and running processes —
|
|
761
|
+
* and stops billing immediately. The next `start()` reconnects and resumes it,
|
|
762
|
+
* with background processes still running. Filesystem mounts are unmounted
|
|
763
|
+
* first (FUSE mounts don't survive pause) and reconciled again on start.
|
|
764
|
+
*
|
|
713
765
|
* Status management is handled by the base class.
|
|
714
766
|
*/
|
|
715
767
|
async stop() {
|
|
716
|
-
try {
|
|
717
|
-
const procs = await this.processes.list();
|
|
718
|
-
await Promise.all(procs.map((p) => this.processes.kill(p.pid)));
|
|
719
|
-
} catch {
|
|
720
|
-
}
|
|
721
768
|
for (const mountPath of [...this.mounts.entries.keys()]) {
|
|
722
769
|
try {
|
|
723
770
|
await this.unmount(mountPath);
|
|
724
771
|
} catch {
|
|
725
772
|
}
|
|
726
773
|
}
|
|
774
|
+
if (this._sandbox) {
|
|
775
|
+
await this._sandbox.pause();
|
|
776
|
+
this.logger.debug(`${LOG_PREFIX} Paused sandbox ${this._sandbox.sandboxId} for: ${this.id}`);
|
|
777
|
+
} else {
|
|
778
|
+
const info = await this.lookupExistingSandboxInfo();
|
|
779
|
+
if (info?.state === "running") {
|
|
780
|
+
await Sandbox.pause(info.sandboxId, this.connectionOpts);
|
|
781
|
+
this.logger.debug(`${LOG_PREFIX} Paused detached sandbox ${info.sandboxId} for: ${this.id}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
727
784
|
this._sandbox = null;
|
|
728
785
|
}
|
|
729
786
|
/**
|
|
@@ -744,11 +801,14 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
744
801
|
} catch {
|
|
745
802
|
}
|
|
746
803
|
}
|
|
747
|
-
|
|
748
|
-
await this._sandbox.kill();
|
|
749
|
-
} catch {
|
|
750
|
-
}
|
|
804
|
+
await this._sandbox.kill();
|
|
751
805
|
this._sandbox = null;
|
|
806
|
+
} else {
|
|
807
|
+
const info = await this.lookupExistingSandboxInfo();
|
|
808
|
+
if (info) {
|
|
809
|
+
await Sandbox.kill(info.sandboxId, this.connectionOpts);
|
|
810
|
+
this.logger.debug(`${LOG_PREFIX} Killed detached sandbox ${info.sandboxId} for: ${this.id}`);
|
|
811
|
+
}
|
|
752
812
|
}
|
|
753
813
|
this.mounts.clear();
|
|
754
814
|
}
|
|
@@ -768,6 +828,21 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
768
828
|
}
|
|
769
829
|
};
|
|
770
830
|
}
|
|
831
|
+
// ---------------------------------------------------------------------------
|
|
832
|
+
// File Upload
|
|
833
|
+
// ---------------------------------------------------------------------------
|
|
834
|
+
/**
|
|
835
|
+
* Bulk-write files into the sandbox filesystem via the SDK's native upload.
|
|
836
|
+
*/
|
|
837
|
+
async writeFiles(files) {
|
|
838
|
+
await this.ensureRunning();
|
|
839
|
+
await this.e2b.files.write(
|
|
840
|
+
files.map((f) => ({
|
|
841
|
+
path: f.path,
|
|
842
|
+
data: typeof f.content === "string" ? f.content : new Blob([new Uint8Array(f.content)])
|
|
843
|
+
}))
|
|
844
|
+
);
|
|
845
|
+
}
|
|
771
846
|
/**
|
|
772
847
|
* Get instructions describing this E2B sandbox.
|
|
773
848
|
* Used by agents to understand the execution environment.
|
|
@@ -1002,11 +1077,15 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
1002
1077
|
generateId() {
|
|
1003
1078
|
return `e2b-sandbox-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1004
1079
|
}
|
|
1080
|
+
/** Domain used to derive public sandbox hosts (self-hosted E2B or e2b.app). */
|
|
1081
|
+
get sandboxDomain() {
|
|
1082
|
+
return this.connectionOpts.domain ?? process.env.E2B_DOMAIN ?? "e2b.app";
|
|
1083
|
+
}
|
|
1005
1084
|
/**
|
|
1006
|
-
*
|
|
1007
|
-
* Returns
|
|
1085
|
+
* Look up an existing sandbox with matching mastra-sandbox-id metadata
|
|
1086
|
+
* WITHOUT connecting or resuming it. Returns its list info or null.
|
|
1008
1087
|
*/
|
|
1009
|
-
async
|
|
1088
|
+
async lookupExistingSandboxInfo() {
|
|
1010
1089
|
try {
|
|
1011
1090
|
const paginator = Sandbox.list({
|
|
1012
1091
|
...this.connectionOpts,
|
|
@@ -1022,13 +1101,28 @@ var E2BSandbox = class extends MastraSandbox {
|
|
|
1022
1101
|
this.logger.debug(
|
|
1023
1102
|
`${LOG_PREFIX} Found existing sandbox for ${this.id}: ${existingSandbox.sandboxId} (state: ${existingSandbox.state})`
|
|
1024
1103
|
);
|
|
1025
|
-
return
|
|
1104
|
+
return existingSandbox;
|
|
1026
1105
|
}
|
|
1027
1106
|
} catch (e) {
|
|
1028
1107
|
this.logger.debug(`${LOG_PREFIX} Error querying for existing sandbox:`, e);
|
|
1029
1108
|
}
|
|
1030
1109
|
return null;
|
|
1031
1110
|
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Find an existing sandbox with matching mastra-sandbox-id metadata.
|
|
1113
|
+
* Returns the connected sandbox if found, null otherwise.
|
|
1114
|
+
* Connecting to a paused sandbox resumes it.
|
|
1115
|
+
*/
|
|
1116
|
+
async findExistingSandbox() {
|
|
1117
|
+
const info = await this.lookupExistingSandboxInfo();
|
|
1118
|
+
if (!info) return null;
|
|
1119
|
+
try {
|
|
1120
|
+
return await Sandbox.connect(info.sandboxId, this.connectionOpts);
|
|
1121
|
+
} catch (e) {
|
|
1122
|
+
this.logger.debug(`${LOG_PREFIX} Error connecting to existing sandbox:`, e);
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1032
1126
|
/**
|
|
1033
1127
|
* Resolve the template specification to a template ID.
|
|
1034
1128
|
*
|