@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# @mastra/e2b
|
|
2
2
|
|
|
3
|
+
## 0.10.0-alpha.4
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added deterministic reattach to E2B sandboxes by provider sandbox ID. Pass the persisted E2B sandbox ID via the new `sandboxId` option (or `clone({ sandboxId })`) and `start()` connects to that exact sandbox — resuming it if paused — instead of discovering by logical id metadata. Only a typed "sandbox gone" error falls back to the usual lookup-or-create path; auth, quota, rate-limit, timeout, and network errors now propagate instead of silently creating a duplicate sandbox. The resolved provider ID is exposed via the new `sandbox.sandboxId` property so it can be persisted across restarts. ([#22316](https://github.com/mastra-ai/mastra/pull/22316))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const sandbox = new E2BSandbox({ id: 'my-workspace', sandboxId: persistedId });
|
|
11
|
+
await sandbox.start();
|
|
12
|
+
await save(sandbox.sandboxId); // persist for the next process
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Fixes https://github.com/mastra-ai/mastra/issues/22300
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Starting a sandbox now reports whether it created a fresh sandbox or reconnected to an existing one, so an `onStart` handler can run first-time setup only when it's actually needed: ([#21984](https://github.com/mastra-ai/mastra/pull/21984))
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
new E2BSandbox({
|
|
23
|
+
id: 'session-1',
|
|
24
|
+
onStart: async ({ outcome }) => {
|
|
25
|
+
if (outcome === 'created') await cloneRepo();
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`4ff3ee2`](https://github.com/mastra-ai/mastra/commit/4ff3ee2bff7ed07528b4817f8f49639031c72a4d), [`c24754c`](https://github.com/mastra-ai/mastra/commit/c24754c1fb6fe144e5051e536e98c8a18b0214ac), [`45dd6ee`](https://github.com/mastra-ai/mastra/commit/45dd6ee089bd7df0d0c98a10098e483fd388e04a), [`32d3583`](https://github.com/mastra-ai/mastra/commit/32d358332cb8ac2306b83b73cf3536e74dbd435e), [`aca2869`](https://github.com/mastra-ai/mastra/commit/aca2869b2031982f3c4a2f52525c9be7cf123ef8)]:
|
|
31
|
+
- @mastra/core@1.62.0-alpha.11
|
|
32
|
+
|
|
33
|
+
## 0.10.0-alpha.3
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Honor the sandbox runtime environment (`setEnv()`/`getEnv()` from `@mastra/core`) in every workspace sandbox provider. Environment variables set after construction now reach subsequent commands on all providers: ([#22250](https://github.com/mastra-ai/mastra/pull/22250))
|
|
38
|
+
|
|
39
|
+
- Process-manager-routed providers (E2B, Blaxel, Cloudflare, Daytona, Docker, Modal, Vercel microVM spawns) inherit the merge from the core spawn wrapper; their duplicated per-manager env plumbing is removed.
|
|
40
|
+
- Providers with their own exec transports (AgentCore, Apple Container, Railway, Vercel microVM and serverless `executeCommand`, Platform's private-network, WebSocket lease, and E2B lease paths) now merge `getEnv()` under per-call env.
|
|
41
|
+
|
|
42
|
+
Constructor `env` continues to behave as before: it seeds the sandbox runtime environment, and providers that bake env into the VM or container at creation time (Docker, Modal, Railway, Apple Container, Vercel, Platform) still do so. Per-call `env` on `executeCommand` still takes precedence for that command only.
|
|
43
|
+
|
|
44
|
+
Removed exported types (minor bump for these two packages): `BlaxelProcessManagerOptions` from `@mastra/blaxel` and `RailwayProcessManagerOptions` from `@mastra/railway`. Both existed only to pass `env` into a process manager constructed by hand; the core spawn wrapper now owns that merge, so the option and its type are gone.
|
|
45
|
+
|
|
46
|
+
- Updated dependencies [[`aa3a85d`](https://github.com/mastra-ai/mastra/commit/aa3a85daf094c683bb97efdf4b6a696d2e474af5), [`d29d06f`](https://github.com/mastra-ai/mastra/commit/d29d06fe00bbd35b4571150ea04c59d2ed783c71), [`e6516df`](https://github.com/mastra-ai/mastra/commit/e6516dfcdae4f4ac0e7971d84359a81385ee602f), [`0b2a3d1`](https://github.com/mastra-ai/mastra/commit/0b2a3d1783875c5b97b7b36ab3d03d7360e0dde7), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`57de7d6`](https://github.com/mastra-ai/mastra/commit/57de7d644ba7146edb4e9e6111ec4fa98c3a59e9), [`e8e299c`](https://github.com/mastra-ai/mastra/commit/e8e299cc6abdfc39947e2fec25803493015d3882), [`edfc548`](https://github.com/mastra-ai/mastra/commit/edfc548886bc7bae17b681f8b6b41a47eb32bcd2), [`a8a4871`](https://github.com/mastra-ai/mastra/commit/a8a4871215f51da95c47129602157ce5372f634a), [`5165cdc`](https://github.com/mastra-ai/mastra/commit/5165cdcdcf50e144bb8113278535196cc9b07065), [`6bb5d71`](https://github.com/mastra-ai/mastra/commit/6bb5d7193fe9166b219f0fccae17db7a5ae86e65), [`9ee8120`](https://github.com/mastra-ai/mastra/commit/9ee8120ce17f76b9f617489e05a283353742690a), [`d975e92`](https://github.com/mastra-ai/mastra/commit/d975e924d4936f46c386bd3dee39c671720289f6), [`1cfa878`](https://github.com/mastra-ai/mastra/commit/1cfa8784d8da0dfaa0317e5048bc48b6084a5ea5), [`c118318`](https://github.com/mastra-ai/mastra/commit/c1183181c9804303db4b511c2e2648f8b714712b), [`fc07c64`](https://github.com/mastra-ai/mastra/commit/fc07c6465043e08e99193a6751a01c56ffc2e7a1), [`542dee2`](https://github.com/mastra-ai/mastra/commit/542dee254167f974ff8cbbbfc0ce10f9a2616a7b), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`a58483c`](https://github.com/mastra-ai/mastra/commit/a58483cff1a9d41fce7c931843f48cb0ac450f64), [`895e9df`](https://github.com/mastra-ai/mastra/commit/895e9dfc17d6f34299eca64e317ded9e5f5e5ef8)]:
|
|
47
|
+
- @mastra/core@1.62.0-alpha.8
|
|
48
|
+
|
|
3
49
|
## 0.10.0-alpha.2
|
|
4
50
|
|
|
5
51
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -518,10 +518,7 @@ var E2BProcessManager = class extends _mastra_core_workspace.SandboxProcessManag
|
|
|
518
518
|
async spawn(command, options = {}) {
|
|
519
519
|
return this.sandbox.retryOnDead(async () => {
|
|
520
520
|
const e2b = this.sandbox.e2b;
|
|
521
|
-
const mergedEnv = {
|
|
522
|
-
...this.env,
|
|
523
|
-
...options.env
|
|
524
|
-
};
|
|
521
|
+
const mergedEnv = { ...options.env };
|
|
525
522
|
const envs = Object.fromEntries(Object.entries(mergedEnv).filter((entry) => entry[1] !== void 0));
|
|
526
523
|
let handle;
|
|
527
524
|
handle = new E2BProcessHandle(await e2b.commands.run(command, {
|
|
@@ -649,11 +646,11 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
649
646
|
_isRetrying = false;
|
|
650
647
|
timeout;
|
|
651
648
|
templateSpec;
|
|
652
|
-
env;
|
|
653
649
|
metadata;
|
|
654
650
|
network;
|
|
655
651
|
lifecycle;
|
|
656
652
|
connectionOpts;
|
|
653
|
+
_preferredSandboxId;
|
|
657
654
|
_instructionsOverride;
|
|
658
655
|
_constructorOptions;
|
|
659
656
|
/** Resolved template ID after building (if needed) */
|
|
@@ -664,12 +661,11 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
664
661
|
super({
|
|
665
662
|
...options,
|
|
666
663
|
name: "E2BSandbox",
|
|
667
|
-
processes: new E2BProcessManager(
|
|
664
|
+
processes: new E2BProcessManager()
|
|
668
665
|
});
|
|
669
666
|
this.id = options.id ?? this.generateId();
|
|
670
667
|
this.timeout = options.timeout ?? 3e5;
|
|
671
668
|
this.templateSpec = options.template;
|
|
672
|
-
this.env = options.env ?? {};
|
|
673
669
|
this.metadata = options.metadata ?? {};
|
|
674
670
|
this.network = options.network;
|
|
675
671
|
this.lifecycle = options.lifecycle ?? { onTimeout: "pause" };
|
|
@@ -679,6 +675,7 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
679
675
|
...options.apiKey && { apiKey: options.apiKey },
|
|
680
676
|
...options.accessToken && { accessToken: options.accessToken }
|
|
681
677
|
};
|
|
678
|
+
this._preferredSandboxId = options.sandboxId;
|
|
682
679
|
this._instructionsOverride = options.instructions;
|
|
683
680
|
this._constructorOptions = { ...options };
|
|
684
681
|
this._templatePreparePromise = this.resolveTemplate().catch((err) => {
|
|
@@ -697,13 +694,16 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
697
694
|
* independent sandboxes (e.g. one per project).
|
|
698
695
|
*
|
|
699
696
|
* `options.idleTimeoutMinutes` maps to the E2B sandbox `timeout` (ms);
|
|
700
|
-
* `options.sandboxId`
|
|
697
|
+
* `options.sandboxId` reattaches the clone to that exact E2B sandbox on
|
|
698
|
+
* `start()`. The parent's own preferred provider sandbox ID is never
|
|
699
|
+
* inherited — physical identity is per-instance.
|
|
701
700
|
*/
|
|
702
701
|
clone(options = {}) {
|
|
703
|
-
const { id: _id, ...base } = this._constructorOptions;
|
|
702
|
+
const { id: _id, sandboxId: _sandboxId, ...base } = this._constructorOptions;
|
|
704
703
|
return new E2BSandbox({
|
|
705
704
|
...base,
|
|
706
705
|
...options.id !== void 0 && { id: options.id },
|
|
706
|
+
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
707
707
|
...options.env !== void 0 && { env: options.env },
|
|
708
708
|
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
709
709
|
});
|
|
@@ -735,25 +735,43 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
735
735
|
return this._sandbox;
|
|
736
736
|
}
|
|
737
737
|
/**
|
|
738
|
-
*
|
|
739
|
-
* Handles template preparation, existing sandbox reconnection, and new sandbox creation.
|
|
738
|
+
* The E2B provider sandbox ID resolved after connect or create.
|
|
740
739
|
*
|
|
741
|
-
*
|
|
740
|
+
* Persist this to reattach deterministically later via the `sandboxId`
|
|
741
|
+
* option (or `clone({ sandboxId })`). Undefined until the sandbox has been
|
|
742
|
+
* started (attached) in this process.
|
|
742
743
|
*/
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
744
|
+
get sandboxId() {
|
|
745
|
+
return this._sandbox?.sandboxId;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Acquisition primitives (base-orchestrated start): the base derives
|
|
749
|
+
* `outcome: 'created'` only when a brand-new sandbox VM was created;
|
|
750
|
+
* reconnecting (including resuming a paused sandbox) is `outcome: 'connected'`.
|
|
751
|
+
*
|
|
752
|
+
* `find` returns an already-connected E2B handle: `Sandbox.connect`
|
|
753
|
+
* resumes paused sandboxes, and its failures are deliberately swallowed
|
|
754
|
+
* (unusable handle → create fresh) — that forgiveness is this provider's
|
|
755
|
+
* policy, so it lives here rather than in `connect`. The exception is the
|
|
756
|
+
* `sandboxId` reattach inside {@link acquireExistingSandbox}, which is
|
|
757
|
+
* fail-closed: only a "sandbox gone" error falls through to discovery.
|
|
758
|
+
*/
|
|
759
|
+
async find() {
|
|
760
|
+
if (this._sandbox) return this._sandbox;
|
|
761
|
+
return await this.acquireExistingSandbox() ?? void 0;
|
|
762
|
+
}
|
|
763
|
+
async connect(existingSandbox) {
|
|
764
|
+
if (existingSandbox === this._sandbox) return;
|
|
765
|
+
this._sandbox = existingSandbox;
|
|
766
|
+
this._createdAt = /* @__PURE__ */ new Date();
|
|
767
|
+
this.logger.debug(`${LOG_PREFIX} Reconnected to existing sandbox for: ${this.id}`);
|
|
768
|
+
const expectedPaths = Array.from(this.mounts.entries.keys());
|
|
769
|
+
this.logger.debug(`${LOG_PREFIX} Running mount reconciliation...`);
|
|
770
|
+
await this.reconcileMounts(expectedPaths);
|
|
771
|
+
this.logger.debug(`${LOG_PREFIX} Mount reconciliation complete`);
|
|
772
|
+
}
|
|
773
|
+
async create() {
|
|
774
|
+
let resolvedTemplateId = await (this._templatePreparePromise || this.resolveTemplate());
|
|
757
775
|
if (!resolvedTemplateId) {
|
|
758
776
|
this.logger.debug(`${LOG_PREFIX} Template preparation failed earlier, retrying...`);
|
|
759
777
|
resolvedTemplateId = await this.resolveTemplate();
|
|
@@ -773,7 +791,7 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
773
791
|
} catch (createError) {
|
|
774
792
|
const errorStr = String(createError);
|
|
775
793
|
if (errorStr.includes("404") && errorStr.includes("not found") && !this.templateSpec) {
|
|
776
|
-
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${
|
|
794
|
+
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${resolvedTemplateId}`);
|
|
777
795
|
this._resolvedTemplateId = void 0;
|
|
778
796
|
const rebuiltTemplateId = await this.buildDefaultTemplate();
|
|
779
797
|
this.logger.debug(`${LOG_PREFIX} Retrying sandbox creation with rebuilt template: ${rebuiltTemplateId}`);
|
|
@@ -854,7 +872,10 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
854
872
|
path,
|
|
855
873
|
filesystem: entry.filesystem?.provider ?? entry.config?.type ?? "unknown"
|
|
856
874
|
})),
|
|
857
|
-
metadata: {
|
|
875
|
+
metadata: {
|
|
876
|
+
...this.metadata,
|
|
877
|
+
...this._sandbox && { sandboxId: this._sandbox.sandboxId }
|
|
878
|
+
}
|
|
858
879
|
};
|
|
859
880
|
}
|
|
860
881
|
/**
|
|
@@ -1132,6 +1153,52 @@ var E2BSandbox = class E2BSandbox extends _mastra_core_workspace.MastraSandbox {
|
|
|
1132
1153
|
return null;
|
|
1133
1154
|
}
|
|
1134
1155
|
/**
|
|
1156
|
+
* Acquire an existing sandbox: try the preferred provider sandbox ID first
|
|
1157
|
+
* (deterministic reattach), then fall back to logical-id metadata discovery.
|
|
1158
|
+
*/
|
|
1159
|
+
async acquireExistingSandbox() {
|
|
1160
|
+
if (this._preferredSandboxId) {
|
|
1161
|
+
const preferred = await this.connectToPreferredSandbox(this._preferredSandboxId);
|
|
1162
|
+
if (preferred) return preferred;
|
|
1163
|
+
}
|
|
1164
|
+
return this.findExistingSandbox();
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Deterministically reattach to a sandbox by its E2B provider ID.
|
|
1168
|
+
*
|
|
1169
|
+
* Fail-closed: only a typed "sandbox gone" error (not found / killed /
|
|
1170
|
+
* not running) returns null so the caller can fall through to logical-id
|
|
1171
|
+
* discovery or creation. Any other error (auth, quota, rate limit,
|
|
1172
|
+
* timeout, network) propagates so a duplicate sandbox is never created.
|
|
1173
|
+
*
|
|
1174
|
+
* Ownership is validated before connecting: a sandbox tagged with a
|
|
1175
|
+
* different `mastra-sandbox-id` is refused (without resuming it).
|
|
1176
|
+
* Sandboxes without the tag (created outside Mastra) are attachable.
|
|
1177
|
+
*/
|
|
1178
|
+
async connectToPreferredSandbox(preferredSandboxId) {
|
|
1179
|
+
let info;
|
|
1180
|
+
try {
|
|
1181
|
+
info = await e2b.Sandbox.getInfo(preferredSandboxId, this.connectionOpts);
|
|
1182
|
+
} catch (e) {
|
|
1183
|
+
if (this.isSandboxDeadError(e)) {
|
|
1184
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} is gone, falling back to logical-id discovery:`, e);
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
throw e;
|
|
1188
|
+
}
|
|
1189
|
+
const owner = info.metadata?.["mastra-sandbox-id"];
|
|
1190
|
+
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}"`);
|
|
1191
|
+
try {
|
|
1192
|
+
return await e2b.Sandbox.connect(preferredSandboxId, this.connectionOpts);
|
|
1193
|
+
} catch (e) {
|
|
1194
|
+
if (this.isSandboxDeadError(e)) {
|
|
1195
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} vanished before connect, falling back:`, e);
|
|
1196
|
+
return null;
|
|
1197
|
+
}
|
|
1198
|
+
throw e;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1135
1202
|
* Find an existing sandbox with matching mastra-sandbox-id metadata.
|
|
1136
1203
|
* Returns the connected sandbox if found, null otherwise.
|
|
1137
1204
|
* Connecting to a paused sandbox resumes it.
|