@mastra/e2b 0.10.0-alpha.2 → 0.10.0-alpha.4
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 +46 -0
- package/dist/index.cjs +95 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +95 -28
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +54 -7
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -517,10 +517,7 @@ var E2BProcessManager = class extends SandboxProcessManager {
|
|
|
517
517
|
async spawn(command, options = {}) {
|
|
518
518
|
return this.sandbox.retryOnDead(async () => {
|
|
519
519
|
const e2b = this.sandbox.e2b;
|
|
520
|
-
const mergedEnv = {
|
|
521
|
-
...this.env,
|
|
522
|
-
...options.env
|
|
523
|
-
};
|
|
520
|
+
const mergedEnv = { ...options.env };
|
|
524
521
|
const envs = Object.fromEntries(Object.entries(mergedEnv).filter((entry) => entry[1] !== void 0));
|
|
525
522
|
let handle;
|
|
526
523
|
handle = new E2BProcessHandle(await e2b.commands.run(command, {
|
|
@@ -648,11 +645,11 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
648
645
|
_isRetrying = false;
|
|
649
646
|
timeout;
|
|
650
647
|
templateSpec;
|
|
651
|
-
env;
|
|
652
648
|
metadata;
|
|
653
649
|
network;
|
|
654
650
|
lifecycle;
|
|
655
651
|
connectionOpts;
|
|
652
|
+
_preferredSandboxId;
|
|
656
653
|
_instructionsOverride;
|
|
657
654
|
_constructorOptions;
|
|
658
655
|
/** Resolved template ID after building (if needed) */
|
|
@@ -663,12 +660,11 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
663
660
|
super({
|
|
664
661
|
...options,
|
|
665
662
|
name: "E2BSandbox",
|
|
666
|
-
processes: new E2BProcessManager(
|
|
663
|
+
processes: new E2BProcessManager()
|
|
667
664
|
});
|
|
668
665
|
this.id = options.id ?? this.generateId();
|
|
669
666
|
this.timeout = options.timeout ?? 3e5;
|
|
670
667
|
this.templateSpec = options.template;
|
|
671
|
-
this.env = options.env ?? {};
|
|
672
668
|
this.metadata = options.metadata ?? {};
|
|
673
669
|
this.network = options.network;
|
|
674
670
|
this.lifecycle = options.lifecycle ?? { onTimeout: "pause" };
|
|
@@ -678,6 +674,7 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
678
674
|
...options.apiKey && { apiKey: options.apiKey },
|
|
679
675
|
...options.accessToken && { accessToken: options.accessToken }
|
|
680
676
|
};
|
|
677
|
+
this._preferredSandboxId = options.sandboxId;
|
|
681
678
|
this._instructionsOverride = options.instructions;
|
|
682
679
|
this._constructorOptions = { ...options };
|
|
683
680
|
this._templatePreparePromise = this.resolveTemplate().catch((err) => {
|
|
@@ -696,13 +693,16 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
696
693
|
* independent sandboxes (e.g. one per project).
|
|
697
694
|
*
|
|
698
695
|
* `options.idleTimeoutMinutes` maps to the E2B sandbox `timeout` (ms);
|
|
699
|
-
* `options.sandboxId`
|
|
696
|
+
* `options.sandboxId` reattaches the clone to that exact E2B sandbox on
|
|
697
|
+
* `start()`. The parent's own preferred provider sandbox ID is never
|
|
698
|
+
* inherited — physical identity is per-instance.
|
|
700
699
|
*/
|
|
701
700
|
clone(options = {}) {
|
|
702
|
-
const { id: _id, ...base } = this._constructorOptions;
|
|
701
|
+
const { id: _id, sandboxId: _sandboxId, ...base } = this._constructorOptions;
|
|
703
702
|
return new E2BSandbox({
|
|
704
703
|
...base,
|
|
705
704
|
...options.id !== void 0 && { id: options.id },
|
|
705
|
+
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
706
706
|
...options.env !== void 0 && { env: options.env },
|
|
707
707
|
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
708
708
|
});
|
|
@@ -734,25 +734,43 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
734
734
|
return this._sandbox;
|
|
735
735
|
}
|
|
736
736
|
/**
|
|
737
|
-
*
|
|
738
|
-
* Handles template preparation, existing sandbox reconnection, and new sandbox creation.
|
|
737
|
+
* The E2B provider sandbox ID resolved after connect or create.
|
|
739
738
|
*
|
|
740
|
-
*
|
|
739
|
+
* Persist this to reattach deterministically later via the `sandboxId`
|
|
740
|
+
* option (or `clone({ sandboxId })`). Undefined until the sandbox has been
|
|
741
|
+
* started (attached) in this process.
|
|
741
742
|
*/
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
743
|
+
get sandboxId() {
|
|
744
|
+
return this._sandbox?.sandboxId;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Acquisition primitives (base-orchestrated start): the base derives
|
|
748
|
+
* `outcome: 'created'` only when a brand-new sandbox VM was created;
|
|
749
|
+
* reconnecting (including resuming a paused sandbox) is `outcome: 'connected'`.
|
|
750
|
+
*
|
|
751
|
+
* `find` returns an already-connected E2B handle: `Sandbox.connect`
|
|
752
|
+
* resumes paused sandboxes, and its failures are deliberately swallowed
|
|
753
|
+
* (unusable handle → create fresh) — that forgiveness is this provider's
|
|
754
|
+
* policy, so it lives here rather than in `connect`. The exception is the
|
|
755
|
+
* `sandboxId` reattach inside {@link acquireExistingSandbox}, which is
|
|
756
|
+
* fail-closed: only a "sandbox gone" error falls through to discovery.
|
|
757
|
+
*/
|
|
758
|
+
async find() {
|
|
759
|
+
if (this._sandbox) return this._sandbox;
|
|
760
|
+
return await this.acquireExistingSandbox() ?? void 0;
|
|
761
|
+
}
|
|
762
|
+
async connect(existingSandbox) {
|
|
763
|
+
if (existingSandbox === this._sandbox) return;
|
|
764
|
+
this._sandbox = existingSandbox;
|
|
765
|
+
this._createdAt = /* @__PURE__ */ new Date();
|
|
766
|
+
this.logger.debug(`${LOG_PREFIX} Reconnected to existing sandbox for: ${this.id}`);
|
|
767
|
+
const expectedPaths = Array.from(this.mounts.entries.keys());
|
|
768
|
+
this.logger.debug(`${LOG_PREFIX} Running mount reconciliation...`);
|
|
769
|
+
await this.reconcileMounts(expectedPaths);
|
|
770
|
+
this.logger.debug(`${LOG_PREFIX} Mount reconciliation complete`);
|
|
771
|
+
}
|
|
772
|
+
async create() {
|
|
773
|
+
let resolvedTemplateId = await (this._templatePreparePromise || this.resolveTemplate());
|
|
756
774
|
if (!resolvedTemplateId) {
|
|
757
775
|
this.logger.debug(`${LOG_PREFIX} Template preparation failed earlier, retrying...`);
|
|
758
776
|
resolvedTemplateId = await this.resolveTemplate();
|
|
@@ -772,7 +790,7 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
772
790
|
} catch (createError) {
|
|
773
791
|
const errorStr = String(createError);
|
|
774
792
|
if (errorStr.includes("404") && errorStr.includes("not found") && !this.templateSpec) {
|
|
775
|
-
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${
|
|
793
|
+
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${resolvedTemplateId}`);
|
|
776
794
|
this._resolvedTemplateId = void 0;
|
|
777
795
|
const rebuiltTemplateId = await this.buildDefaultTemplate();
|
|
778
796
|
this.logger.debug(`${LOG_PREFIX} Retrying sandbox creation with rebuilt template: ${rebuiltTemplateId}`);
|
|
@@ -853,7 +871,10 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
853
871
|
path,
|
|
854
872
|
filesystem: entry.filesystem?.provider ?? entry.config?.type ?? "unknown"
|
|
855
873
|
})),
|
|
856
|
-
metadata: {
|
|
874
|
+
metadata: {
|
|
875
|
+
...this.metadata,
|
|
876
|
+
...this._sandbox && { sandboxId: this._sandbox.sandboxId }
|
|
877
|
+
}
|
|
857
878
|
};
|
|
858
879
|
}
|
|
859
880
|
/**
|
|
@@ -1131,6 +1152,52 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
1131
1152
|
return null;
|
|
1132
1153
|
}
|
|
1133
1154
|
/**
|
|
1155
|
+
* Acquire an existing sandbox: try the preferred provider sandbox ID first
|
|
1156
|
+
* (deterministic reattach), then fall back to logical-id metadata discovery.
|
|
1157
|
+
*/
|
|
1158
|
+
async acquireExistingSandbox() {
|
|
1159
|
+
if (this._preferredSandboxId) {
|
|
1160
|
+
const preferred = await this.connectToPreferredSandbox(this._preferredSandboxId);
|
|
1161
|
+
if (preferred) return preferred;
|
|
1162
|
+
}
|
|
1163
|
+
return this.findExistingSandbox();
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Deterministically reattach to a sandbox by its E2B provider ID.
|
|
1167
|
+
*
|
|
1168
|
+
* Fail-closed: only a typed "sandbox gone" error (not found / killed /
|
|
1169
|
+
* not running) returns null so the caller can fall through to logical-id
|
|
1170
|
+
* discovery or creation. Any other error (auth, quota, rate limit,
|
|
1171
|
+
* timeout, network) propagates so a duplicate sandbox is never created.
|
|
1172
|
+
*
|
|
1173
|
+
* Ownership is validated before connecting: a sandbox tagged with a
|
|
1174
|
+
* different `mastra-sandbox-id` is refused (without resuming it).
|
|
1175
|
+
* Sandboxes without the tag (created outside Mastra) are attachable.
|
|
1176
|
+
*/
|
|
1177
|
+
async connectToPreferredSandbox(preferredSandboxId) {
|
|
1178
|
+
let info;
|
|
1179
|
+
try {
|
|
1180
|
+
info = await Sandbox.getInfo(preferredSandboxId, this.connectionOpts);
|
|
1181
|
+
} catch (e) {
|
|
1182
|
+
if (this.isSandboxDeadError(e)) {
|
|
1183
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} is gone, falling back to logical-id discovery:`, e);
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
throw e;
|
|
1187
|
+
}
|
|
1188
|
+
const owner = info.metadata?.["mastra-sandbox-id"];
|
|
1189
|
+
if (owner !== void 0 && owner !== this.id) throw new Error(`${LOG_PREFIX} Provider sandbox ${preferredSandboxId} belongs to logical sandbox id "${owner}", refusing to attach it to "${this.id}"`);
|
|
1190
|
+
try {
|
|
1191
|
+
return await Sandbox.connect(preferredSandboxId, this.connectionOpts);
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
if (this.isSandboxDeadError(e)) {
|
|
1194
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} vanished before connect, falling back:`, e);
|
|
1195
|
+
return null;
|
|
1196
|
+
}
|
|
1197
|
+
throw e;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1134
1201
|
* Find an existing sandbox with matching mastra-sandbox-id metadata.
|
|
1135
1202
|
* Returns the connected sandbox if found, null otherwise.
|
|
1136
1203
|
* Connecting to a paused sandbox resumes it.
|