@mastra/e2b 0.10.0-alpha.3 → 0.10.0-alpha.5
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 +36 -0
- package/dist/index.cjs +132 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +132 -43
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +82 -15
- package/dist/sandbox/index.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -649,6 +649,7 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
649
649
|
network;
|
|
650
650
|
lifecycle;
|
|
651
651
|
connectionOpts;
|
|
652
|
+
_preferredSandboxId;
|
|
652
653
|
_instructionsOverride;
|
|
653
654
|
_constructorOptions;
|
|
654
655
|
/** Resolved template ID after building (if needed) */
|
|
@@ -673,6 +674,7 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
673
674
|
...options.apiKey && { apiKey: options.apiKey },
|
|
674
675
|
...options.accessToken && { accessToken: options.accessToken }
|
|
675
676
|
};
|
|
677
|
+
this._preferredSandboxId = options.sandboxId;
|
|
676
678
|
this._instructionsOverride = options.instructions;
|
|
677
679
|
this._constructorOptions = { ...options };
|
|
678
680
|
this._templatePreparePromise = this.resolveTemplate().catch((err) => {
|
|
@@ -691,13 +693,16 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
691
693
|
* independent sandboxes (e.g. one per project).
|
|
692
694
|
*
|
|
693
695
|
* `options.idleTimeoutMinutes` maps to the E2B sandbox `timeout` (ms);
|
|
694
|
-
* `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.
|
|
695
699
|
*/
|
|
696
700
|
clone(options = {}) {
|
|
697
|
-
const { id: _id, ...base } = this._constructorOptions;
|
|
701
|
+
const { id: _id, sandboxId: _sandboxId, ...base } = this._constructorOptions;
|
|
698
702
|
return new E2BSandbox({
|
|
699
703
|
...base,
|
|
700
704
|
...options.id !== void 0 && { id: options.id },
|
|
705
|
+
...options.sandboxId !== void 0 && { sandboxId: options.sandboxId },
|
|
701
706
|
...options.env !== void 0 && { env: options.env },
|
|
702
707
|
...options.idleTimeoutMinutes !== void 0 && { timeout: options.idleTimeoutMinutes * 6e4 }
|
|
703
708
|
});
|
|
@@ -729,61 +734,73 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
729
734
|
return this._sandbox;
|
|
730
735
|
}
|
|
731
736
|
/**
|
|
732
|
-
*
|
|
733
|
-
* Handles template preparation, existing sandbox reconnection, and new sandbox creation.
|
|
737
|
+
* The E2B provider sandbox ID resolved after connect or create.
|
|
734
738
|
*
|
|
735
|
-
*
|
|
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.
|
|
736
742
|
*/
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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());
|
|
751
774
|
if (!resolvedTemplateId) {
|
|
752
775
|
this.logger.debug(`${LOG_PREFIX} Template preparation failed earlier, retrying...`);
|
|
753
776
|
resolvedTemplateId = await this.resolveTemplate();
|
|
754
777
|
}
|
|
755
778
|
this.logger.debug(`${LOG_PREFIX} Creating new sandbox for: ${this.id} with template: ${resolvedTemplateId}`);
|
|
779
|
+
const createOpts = {
|
|
780
|
+
...this.connectionOpts,
|
|
781
|
+
lifecycle: this.lifecycle,
|
|
782
|
+
metadata: {
|
|
783
|
+
...this.metadata,
|
|
784
|
+
"mastra-sandbox-id": this.id
|
|
785
|
+
},
|
|
786
|
+
...this.network && { network: this.network },
|
|
787
|
+
timeoutMs: this.timeout
|
|
788
|
+
};
|
|
789
|
+
let sdkSandbox;
|
|
756
790
|
try {
|
|
757
|
-
|
|
758
|
-
...this.connectionOpts,
|
|
759
|
-
lifecycle: this.lifecycle,
|
|
760
|
-
metadata: {
|
|
761
|
-
...this.metadata,
|
|
762
|
-
"mastra-sandbox-id": this.id
|
|
763
|
-
},
|
|
764
|
-
...this.network && { network: this.network },
|
|
765
|
-
timeoutMs: this.timeout
|
|
766
|
-
});
|
|
791
|
+
sdkSandbox = await this.createSdkSandbox(resolvedTemplateId, createOpts);
|
|
767
792
|
} catch (createError) {
|
|
768
793
|
const errorStr = String(createError);
|
|
769
794
|
if (errorStr.includes("404") && errorStr.includes("not found") && !this.templateSpec) {
|
|
770
|
-
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${
|
|
795
|
+
this.logger.debug(`${LOG_PREFIX} Template not found, rebuilding: ${resolvedTemplateId}`);
|
|
771
796
|
this._resolvedTemplateId = void 0;
|
|
772
797
|
const rebuiltTemplateId = await this.buildDefaultTemplate();
|
|
773
798
|
this.logger.debug(`${LOG_PREFIX} Retrying sandbox creation with rebuilt template: ${rebuiltTemplateId}`);
|
|
774
|
-
|
|
775
|
-
...this.connectionOpts,
|
|
776
|
-
lifecycle: this.lifecycle,
|
|
777
|
-
metadata: {
|
|
778
|
-
...this.metadata,
|
|
779
|
-
"mastra-sandbox-id": this.id
|
|
780
|
-
},
|
|
781
|
-
...this.network && { network: this.network },
|
|
782
|
-
timeoutMs: this.timeout
|
|
783
|
-
});
|
|
799
|
+
sdkSandbox = await this.createSdkSandbox(rebuiltTemplateId, createOpts);
|
|
784
800
|
} else throw createError;
|
|
785
801
|
}
|
|
786
|
-
this.
|
|
802
|
+
this._sandbox = sdkSandbox;
|
|
803
|
+
this.logger.debug(`${LOG_PREFIX} Created sandbox ${sdkSandbox.sandboxId} for logical ID: ${this.id}`);
|
|
787
804
|
this._createdAt = /* @__PURE__ */ new Date();
|
|
788
805
|
}
|
|
789
806
|
/**
|
|
@@ -848,7 +865,10 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
848
865
|
path,
|
|
849
866
|
filesystem: entry.filesystem?.provider ?? entry.config?.type ?? "unknown"
|
|
850
867
|
})),
|
|
851
|
-
metadata: {
|
|
868
|
+
metadata: {
|
|
869
|
+
...this.metadata,
|
|
870
|
+
...this._sandbox && { sandboxId: this._sandbox.sandboxId }
|
|
871
|
+
}
|
|
852
872
|
};
|
|
853
873
|
}
|
|
854
874
|
/**
|
|
@@ -1126,6 +1146,52 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
1126
1146
|
return null;
|
|
1127
1147
|
}
|
|
1128
1148
|
/**
|
|
1149
|
+
* Acquire an existing sandbox: try the preferred provider sandbox ID first
|
|
1150
|
+
* (deterministic reattach), then fall back to logical-id metadata discovery.
|
|
1151
|
+
*/
|
|
1152
|
+
async acquireExistingSandbox() {
|
|
1153
|
+
if (this._preferredSandboxId) {
|
|
1154
|
+
const preferred = await this.connectToPreferredSandbox(this._preferredSandboxId);
|
|
1155
|
+
if (preferred) return preferred;
|
|
1156
|
+
}
|
|
1157
|
+
return this.findExistingSandbox();
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Deterministically reattach to a sandbox by its E2B provider ID.
|
|
1161
|
+
*
|
|
1162
|
+
* Fail-closed: only a typed "sandbox gone" error (not found / killed /
|
|
1163
|
+
* not running) returns null so the caller can fall through to logical-id
|
|
1164
|
+
* discovery or creation. Any other error (auth, quota, rate limit,
|
|
1165
|
+
* timeout, network) propagates so a duplicate sandbox is never created.
|
|
1166
|
+
*
|
|
1167
|
+
* Ownership is validated before connecting: a sandbox tagged with a
|
|
1168
|
+
* different `mastra-sandbox-id` is refused (without resuming it).
|
|
1169
|
+
* Sandboxes without the tag (created outside Mastra) are attachable.
|
|
1170
|
+
*/
|
|
1171
|
+
async connectToPreferredSandbox(preferredSandboxId) {
|
|
1172
|
+
let info;
|
|
1173
|
+
try {
|
|
1174
|
+
info = await Sandbox.getInfo(preferredSandboxId, this.connectionOpts);
|
|
1175
|
+
} catch (e) {
|
|
1176
|
+
if (this.isSandboxDeadError(e)) {
|
|
1177
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} is gone, falling back to logical-id discovery:`, e);
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
throw e;
|
|
1181
|
+
}
|
|
1182
|
+
const owner = info.metadata?.["mastra-sandbox-id"];
|
|
1183
|
+
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}"`);
|
|
1184
|
+
try {
|
|
1185
|
+
return await Sandbox.connect(preferredSandboxId, this.connectionOpts);
|
|
1186
|
+
} catch (e) {
|
|
1187
|
+
if (this.isSandboxDeadError(e)) {
|
|
1188
|
+
this.logger.debug(`${LOG_PREFIX} Preferred sandbox ${preferredSandboxId} vanished before connect, falling back:`, e);
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
throw e;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1129
1195
|
* Find an existing sandbox with matching mastra-sandbox-id metadata.
|
|
1130
1196
|
* Returns the connected sandbox if found, null otherwise.
|
|
1131
1197
|
* Connecting to a paused sandbox resumes it.
|
|
@@ -1134,19 +1200,39 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
1134
1200
|
const info = await this.lookupExistingSandboxInfo();
|
|
1135
1201
|
if (!info) return null;
|
|
1136
1202
|
try {
|
|
1137
|
-
return await
|
|
1203
|
+
return await this.connectSdkSandbox(info.sandboxId, this.connectionOpts);
|
|
1138
1204
|
} catch (e) {
|
|
1139
1205
|
this.logger.debug(`${LOG_PREFIX} Error connecting to existing sandbox:`, e);
|
|
1140
1206
|
return null;
|
|
1141
1207
|
}
|
|
1142
1208
|
}
|
|
1143
1209
|
/**
|
|
1210
|
+
* Create a new SDK sandbox from a resolved template ID.
|
|
1211
|
+
*
|
|
1212
|
+
* Override point for providers layered on the E2B SDK whose `Sandbox`
|
|
1213
|
+
* class extends `e2b`'s (e.g. `@e2b/desktop`): override to call their
|
|
1214
|
+
* `Sandbox.create`. Connection options are already spread into `opts`.
|
|
1215
|
+
*/
|
|
1216
|
+
async createSdkSandbox(templateId, opts) {
|
|
1217
|
+
return Sandbox.create(templateId, opts);
|
|
1218
|
+
}
|
|
1219
|
+
/**
|
|
1220
|
+
* Connect to (and resume) an existing SDK sandbox by its E2B sandbox ID.
|
|
1221
|
+
* Override point — see {@link createSdkSandbox}.
|
|
1222
|
+
*/
|
|
1223
|
+
async connectSdkSandbox(sandboxId, opts) {
|
|
1224
|
+
return Sandbox.connect(sandboxId, opts);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1144
1227
|
* Resolve the template specification to a template ID.
|
|
1145
1228
|
*
|
|
1146
1229
|
* - String: Use as-is (template ID)
|
|
1147
1230
|
* - TemplateBuilder: Build and return the template ID
|
|
1148
1231
|
* - Function: Apply to base mountable template, then build
|
|
1149
1232
|
* - undefined: Use default mountable template (cached)
|
|
1233
|
+
*
|
|
1234
|
+
* Override point: subclasses with a different default template (e.g.
|
|
1235
|
+
* desktop sandboxes) override this and {@link buildDefaultTemplate}.
|
|
1150
1236
|
*/
|
|
1151
1237
|
async resolveTemplate() {
|
|
1152
1238
|
if (this._resolvedTemplateId) return this._resolvedTemplateId;
|
|
@@ -1185,6 +1271,9 @@ var E2BSandbox = class E2BSandbox extends MastraSandbox {
|
|
|
1185
1271
|
}
|
|
1186
1272
|
/**
|
|
1187
1273
|
* Build the default mountable template (bypasses exists check).
|
|
1274
|
+
*
|
|
1275
|
+
* Override point: called from the template-not-found retry path in
|
|
1276
|
+
* `start()` when no explicit template was configured.
|
|
1188
1277
|
*/
|
|
1189
1278
|
async buildDefaultTemplate() {
|
|
1190
1279
|
const { template, id } = createDefaultMountableTemplate();
|