@mastra/e2b 0.5.1 → 0.6.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 CHANGED
@@ -1,5 +1,45 @@
1
1
  # @mastra/e2b
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add `E2BCodeModeTransport` for running Code Mode in an `E2BSandbox`. ([#19372](https://github.com/mastra-ai/mastra/pull/19372))
8
+
9
+ The default `StdioCodeModeTransport` writes the runner/program to the host tmpdir and spawns `node <hostPath>`, which only works when the sandbox shares the host filesystem (e.g. `LocalSandbox`). E2B runs the program in a remote micro-VM, so those host paths don't exist there. `E2BCodeModeTransport` writes the program and runner into the sandbox via the E2B files API, strips TypeScript on the host with esbuild (no `--experimental-strip-types`, no Node-version dependency), auto-starts the sandbox when needed, surfaces captured stderr in timeout/no-result errors, and cleans up the sandbox directory afterwards.
10
+
11
+ ```ts
12
+ import { createCodeMode } from '@mastra/core/tools';
13
+ import { E2BSandbox, E2BCodeModeTransport } from '@mastra/e2b';
14
+
15
+ const { tool, instructions } = createCodeMode({ tools, sandbox: new E2BSandbox() }, new E2BCodeModeTransport());
16
+ ```
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies [[`bd6d240`](https://github.com/mastra-ai/mastra/commit/bd6d2402db93dddaef0721667e7e8a030e7c6e16), [`0111486`](https://github.com/mastra-ai/mastra/commit/01114867612593eef5cfa2fda6a1194dfedda841), [`96a3749`](https://github.com/mastra-ai/mastra/commit/96a37492235f5b8076b3e3177d83ed5a5e44a640), [`fe1bda0`](https://github.com/mastra-ai/mastra/commit/fe1bda06f6af92a694a51712db747cda1e7185f0), [`25e7c12`](https://github.com/mastra-ai/mastra/commit/25e7c126a770069ae7fb7ecf1d2adb40e017b009), [`1ce5121`](https://github.com/mastra-ai/mastra/commit/1ce512155d122bb21f47d98383e82ffbf84b39e8), [`fb8aea3`](https://github.com/mastra-ai/mastra/commit/fb8aea384291e77311be3a64ee1717320d5c3c73), [`4adc391`](https://github.com/mastra-ai/mastra/commit/4adc3911075249c352bb4832d2471922826344de), [`a5c6337`](https://github.com/mastra-ai/mastra/commit/a5c6337d23c7686c81a32ce62f550f610543a240), [`3cfc47a`](https://github.com/mastra-ai/mastra/commit/3cfc47a6b89940aadd0f46fb01ae9624a73a865d), [`2bb7817`](https://github.com/mastra-ai/mastra/commit/2bb78176112fde628483de2830528f7eee911e56), [`51d9870`](https://github.com/mastra-ai/mastra/commit/51d987032c689c2855374d0f244f5d654da809d1), [`5cab274`](https://github.com/mastra-ai/mastra/commit/5cab2744250e22d12fefa7b32637dce224233cee), [`7fa27d3`](https://github.com/mastra-ai/mastra/commit/7fa27d3b6f5ed68cd34e454a4d3ad9c482a0cfbc), [`8b97958`](https://github.com/mastra-ai/mastra/commit/8b979589f9aa59ba67cac565949475f2ffeb4ac3), [`8410541`](https://github.com/mastra-ai/mastra/commit/84105412c60ecd3bb33a9838146f59c4b588228f), [`a58dcbb`](https://github.com/mastra-ai/mastra/commit/a58dcbb546d7e1d65ebdc1f39e55f0908fcd9391), [`aa38805`](https://github.com/mastra-ai/mastra/commit/aa38805b878b827403be785eb90688d7172f5a40), [`153bd3b`](https://github.com/mastra-ai/mastra/commit/153bd3b396bdfed6b74cf43de12db8fd2d83c04a), [`45a8e65`](https://github.com/mastra-ai/mastra/commit/45a8e65e1556d1362cb3f25187023c36de26661d), [`e955965`](https://github.com/mastra-ai/mastra/commit/e955965dce575a903e37cf054d28ea99aa48785e), [`2d22570`](https://github.com/mastra-ai/mastra/commit/2d22570c7dfdd02123d0ecc529efb05ccba2d9fc), [`07bb863`](https://github.com/mastra-ai/mastra/commit/07bb8631919c6f7cf377dccd45b096e0f17fbed0), [`c8ed116`](https://github.com/mastra-ai/mastra/commit/c8ed11699f62bcac70102ab4ec84d80d20541da6), [`01b338c`](https://github.com/mastra-ai/mastra/commit/01b338c56271f0219606710e3e8b26dee27ac6c2), [`a99eae8`](https://github.com/mastra-ai/mastra/commit/a99eae8908e500c1b2d12f9d277be616b98617a5), [`860ef7e`](https://github.com/mastra-ai/mastra/commit/860ef7e77d92b63469cbe5857aa1e626197e43e9), [`17e818c`](https://github.com/mastra-ai/mastra/commit/17e818c51a958ba90641b1a959dc38faf8c034e9), [`edce8d2`](https://github.com/mastra-ai/mastra/commit/edce8d2769f19e27a05737c627af2d765472a4f8), [`8a586ec`](https://github.com/mastra-ai/mastra/commit/8a586eca9a4914f31dff6140d0d45ac375b00669), [`4451dfe`](https://github.com/mastra-ai/mastra/commit/4451dfe857428e7abcc0261a507a2e186dae6d47), [`8b7361d`](https://github.com/mastra-ai/mastra/commit/8b7361d35de68b80d05d30a74e0c69e7218fd612), [`1d39058`](https://github.com/mastra-ai/mastra/commit/1d39058e548efd691799985d5c8af2737f1c3bd2), [`3927473`](https://github.com/mastra-ai/mastra/commit/392747323ddb10c643d12be7b9ae913159dfaeed), [`dce50dc`](https://github.com/mastra-ai/mastra/commit/dce50dc9a1c1fcd0f427bb5f6250ec74910cb04b), [`fd13f8e`](https://github.com/mastra-ai/mastra/commit/fd13f8e21990f9904c3eedba3a626bb4a929cdb8), [`634caff`](https://github.com/mastra-ai/mastra/commit/634caff29a9200ad058b67d53f96d9e5832fb8a2), [`f703f87`](https://github.com/mastra-ai/mastra/commit/f703f878de072d51fda557f9c50867d8252bef05), [`3e26c87`](https://github.com/mastra-ai/mastra/commit/3e26c87de0c5bc2583b795ce6ca5889b6b161acb), [`33f2b88`](https://github.com/mastra-ai/mastra/commit/33f2b88842c09a567f906fac4cb61cd5277ced59), [`177010f`](https://github.com/mastra-ai/mastra/commit/177010ff096d2e4b28d89803be5b1a4cad2a0d6b), [`0ad646f`](https://github.com/mastra-ai/mastra/commit/0ad646f71a530f2454664299e5e01bfd13fa12e5), [`b486abf`](https://github.com/mastra-ai/mastra/commit/b486abfa2a7528c6f527e4015c819ea9fa54aaad), [`54a51e0`](https://github.com/mastra-ai/mastra/commit/54a51e0a484fe1ebad3fb1f7ef5282a075709eb7), [`c43f3a9`](https://github.com/mastra-ai/mastra/commit/c43f3a9d1efde99b38789364ba4d0ba670f430e3), [`a5008f2`](https://github.com/mastra-ai/mastra/commit/a5008f22ae710ad9402ea9f2547d8c02f74d384b), [`e2d5f37`](https://github.com/mastra-ai/mastra/commit/e2d5f373bd289be534d5f8694d34465010533df6), [`4ce0163`](https://github.com/mastra-ai/mastra/commit/4ce0163dc86e675a86809685c8ce6c49f1aeb87e), [`4378341`](https://github.com/mastra-ai/mastra/commit/43783412df5ea3dd35f5b1f6e4851e79c346fc89)]:
21
+ - @mastra/core@1.51.0
22
+
23
+ ## 0.6.0-alpha.0
24
+
25
+ ### Minor Changes
26
+
27
+ - Add `E2BCodeModeTransport` for running Code Mode in an `E2BSandbox`. ([#19372](https://github.com/mastra-ai/mastra/pull/19372))
28
+
29
+ The default `StdioCodeModeTransport` writes the runner/program to the host tmpdir and spawns `node <hostPath>`, which only works when the sandbox shares the host filesystem (e.g. `LocalSandbox`). E2B runs the program in a remote micro-VM, so those host paths don't exist there. `E2BCodeModeTransport` writes the program and runner into the sandbox via the E2B files API, strips TypeScript on the host with esbuild (no `--experimental-strip-types`, no Node-version dependency), auto-starts the sandbox when needed, surfaces captured stderr in timeout/no-result errors, and cleans up the sandbox directory afterwards.
30
+
31
+ ```ts
32
+ import { createCodeMode } from '@mastra/core/tools';
33
+ import { E2BSandbox, E2BCodeModeTransport } from '@mastra/e2b';
34
+
35
+ const { tool, instructions } = createCodeMode({ tools, sandbox: new E2BSandbox() }, new E2BCodeModeTransport());
36
+ ```
37
+
38
+ ### Patch Changes
39
+
40
+ - Updated dependencies [[`edce8d2`](https://github.com/mastra-ai/mastra/commit/edce8d2769f19e27a05737c627af2d765472a4f8)]:
41
+ - @mastra/core@1.51.0-alpha.9
42
+
3
43
  ## 0.5.1
4
44
 
5
45
  ### Patch Changes
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Code Mode — E2B transport
3
+ *
4
+ * The default {@link StdioCodeModeTransport} in `@mastra/core` writes the
5
+ * runner/program files to the *host* tmpdir and spawns `node <hostPath>`. That
6
+ * only works when the sandbox shares the host filesystem (e.g. `LocalSandbox`).
7
+ * E2B runs the program in a remote micro-VM with its own filesystem, so the
8
+ * host paths don't exist there and `node` exits immediately.
9
+ *
10
+ * `E2BCodeModeTransport` writes the runner/program *into* the sandbox via the
11
+ * E2B files API and runs plain `node <runnerPath>` inside the VM. TypeScript is
12
+ * stripped on the host with esbuild before upload, so it doesn't depend on the
13
+ * sandbox's Node version (the core transport relies on
14
+ * `node --experimental-strip-types`, which needs Node >= 22.6).
15
+ *
16
+ * The RPC frame protocol (host <-> runner) is unchanged: it reuses
17
+ * `buildProgramModule`, `buildRunner`, and `FRAME_PREFIX` from
18
+ * `@mastra/core/tools`.
19
+ */
20
+ import type { CodeModeToolResult, CodeModeTransport } from '@mastra/core/tools';
21
+ /**
22
+ * Code Mode transport for {@link E2BSandbox}.
23
+ *
24
+ * Writes the generated program and runner into the sandbox filesystem, runs
25
+ * `node` there, and bridges `external_*` RPC calls back to the host over the
26
+ * process's stdout/stdin — the same frame protocol as the core stdio transport.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import { createCodeMode } from '@mastra/core/tools';
31
+ * import { E2BSandbox, E2BCodeModeTransport } from '@mastra/e2b';
32
+ *
33
+ * const { tool, instructions } = createCodeMode(
34
+ * { tools: { getWeather, getForecast }, sandbox: new E2BSandbox() },
35
+ * new E2BCodeModeTransport(),
36
+ * );
37
+ * ```
38
+ */
39
+ export declare class E2BCodeModeTransport implements CodeModeTransport {
40
+ run(opts: Parameters<CodeModeTransport['run']>[0]): Promise<CodeModeToolResult>;
41
+ }
42
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/code-mode/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAuB,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAcrG;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IACtD,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAkMtF"}
package/dist/index.cjs CHANGED
@@ -3,6 +3,8 @@
3
3
  var workspace = require('@mastra/core/workspace');
4
4
  var e2b = require('e2b');
5
5
  var crypto = require('crypto');
6
+ var tools = require('@mastra/core/tools');
7
+ var esbuild = require('esbuild');
6
8
 
7
9
  // src/sandbox/index.ts
8
10
  var MOUNTABLE_TEMPLATE_VERSION = "v1";
@@ -1214,7 +1216,169 @@ var e2bSandboxProvider = {
1214
1216
  },
1215
1217
  createSandbox: (config) => new E2BSandbox(config)
1216
1218
  };
1219
+ var SANDBOX_TMP = "/home/user/mastra-code-mode";
1220
+ function sanitize(id) {
1221
+ const cleaned = id.replace(/[^A-Za-z0-9_$]/g, "_");
1222
+ return /^[A-Za-z_$]/.test(cleaned) ? cleaned : `_${cleaned}`;
1223
+ }
1224
+ var E2BCodeModeTransport = class {
1225
+ async run(opts) {
1226
+ const { sandbox, program, toolIds, dispatch, timeout, abortSignal, onExternalCall, onExternalResult } = opts;
1227
+ if (!(sandbox instanceof E2BSandbox)) {
1228
+ throw new Error("E2BCodeModeTransport requires an E2BSandbox");
1229
+ }
1230
+ if (!sandbox.processes) {
1231
+ throw new Error("Sandbox has no process manager");
1232
+ }
1233
+ if (sandbox.status !== "running") {
1234
+ await sandbox.start();
1235
+ }
1236
+ const e2b = sandbox.e2b;
1237
+ const externals = toolIds.map((toolId) => ({ toolId, externalName: sanitize(toolId) }));
1238
+ const allowList = new Set(toolIds);
1239
+ const suffix = crypto.randomBytes(4).toString("hex");
1240
+ const dir = `${SANDBOX_TMP}/${suffix}`;
1241
+ const programPath = `${dir}/program-${suffix}.mjs`;
1242
+ const runnerPath = `${dir}/runner-${suffix}.mjs`;
1243
+ const programSource = esbuild.transformSync(tools.buildProgramModule(program), { loader: "ts", target: "es2022" }).code;
1244
+ const runnerSource = tools.buildRunner({ programModule: `file://${programPath}`, externals });
1245
+ const logs = [];
1246
+ let stderr = "";
1247
+ let done;
1248
+ let stdoutBuffer = "";
1249
+ let resolveDone;
1250
+ const donePromise = new Promise((resolve) => {
1251
+ resolveDone = resolve;
1252
+ });
1253
+ const notifyCall = (tool, args) => {
1254
+ try {
1255
+ onExternalCall?.(tool, args);
1256
+ } catch {
1257
+ }
1258
+ };
1259
+ const notifyResult = (tool, durationMs, error) => {
1260
+ try {
1261
+ onExternalResult?.(tool, durationMs, error);
1262
+ } catch {
1263
+ }
1264
+ };
1265
+ try {
1266
+ await e2b.files.makeDir(dir);
1267
+ await e2b.files.write(programPath, programSource);
1268
+ await e2b.files.write(runnerPath, runnerSource);
1269
+ let handle;
1270
+ const respond = async (id, ok, result, error) => {
1271
+ await handle.sendStdin(JSON.stringify({ type: "rpc-result", id, ok, result, error }) + "\n");
1272
+ };
1273
+ const serveRpc = async (id, tool, args) => {
1274
+ const started = Date.now();
1275
+ notifyCall(tool, args);
1276
+ if (!allowList.has(tool)) {
1277
+ notifyResult(tool, Date.now() - started, new Error("not allowed"));
1278
+ await respond(id, false, void 0, {
1279
+ message: `Tool "${tool}" is not available in Code Mode`,
1280
+ name: "NotAllowedError"
1281
+ });
1282
+ return;
1283
+ }
1284
+ try {
1285
+ const result = await dispatch(tool, args);
1286
+ notifyResult(tool, Date.now() - started);
1287
+ await respond(id, true, result);
1288
+ } catch (error) {
1289
+ const err = error;
1290
+ notifyResult(tool, Date.now() - started, error instanceof Error ? error : new Error(String(error)));
1291
+ await respond(id, false, void 0, {
1292
+ message: err?.message ?? String(error),
1293
+ name: err?.name
1294
+ });
1295
+ }
1296
+ };
1297
+ const handleFrame = (frame) => {
1298
+ switch (frame.type) {
1299
+ case "log":
1300
+ logs.push(frame.message);
1301
+ return;
1302
+ case "done":
1303
+ done = frame.ok ? { success: true, result: frame.result, logs } : { success: false, error: frame.error, logs };
1304
+ resolveDone();
1305
+ return;
1306
+ case "rpc":
1307
+ void serveRpc(frame.id, frame.tool, frame.args).catch(() => {
1308
+ });
1309
+ return;
1310
+ }
1311
+ };
1312
+ handle = await sandbox.processes.spawn(`node ${runnerPath}`, {
1313
+ cwd: dir,
1314
+ abortSignal,
1315
+ // E2B failures are otherwise silent, which makes them painful to debug.
1316
+ // Capture stderr and surface it in Timeout/NoResult errors below.
1317
+ onStderr: (chunk) => {
1318
+ stderr += chunk;
1319
+ },
1320
+ onStdout: (chunk) => {
1321
+ stdoutBuffer += chunk;
1322
+ let idx;
1323
+ while ((idx = stdoutBuffer.indexOf("\n")) >= 0) {
1324
+ const line = stdoutBuffer.slice(0, idx);
1325
+ stdoutBuffer = stdoutBuffer.slice(idx + 1);
1326
+ if (!line.startsWith(tools.FRAME_PREFIX)) continue;
1327
+ let frame;
1328
+ try {
1329
+ frame = JSON.parse(line.slice(tools.FRAME_PREFIX.length));
1330
+ } catch {
1331
+ continue;
1332
+ }
1333
+ handleFrame(frame);
1334
+ }
1335
+ }
1336
+ });
1337
+ let timer;
1338
+ const timeoutPromise = new Promise((resolve) => {
1339
+ timer = setTimeout(() => resolve("timeout"), timeout);
1340
+ });
1341
+ const exitPromise = handle.wait().then(() => "exited");
1342
+ const outcome = await Promise.race([
1343
+ donePromise.then(() => "done"),
1344
+ exitPromise.catch(() => "exited"),
1345
+ timeoutPromise
1346
+ ]);
1347
+ if (timer) clearTimeout(timer);
1348
+ if (outcome === "timeout") {
1349
+ await handle.kill().catch(() => {
1350
+ });
1351
+ return {
1352
+ success: false,
1353
+ logs,
1354
+ error: {
1355
+ message: `Code Mode execution timed out after ${timeout}ms${stderr ? `
1356
+ stderr: ${stderr}` : ""}`,
1357
+ name: "TimeoutError"
1358
+ }
1359
+ };
1360
+ }
1361
+ if (!done) {
1362
+ await exitPromise.catch(() => {
1363
+ });
1364
+ }
1365
+ return done ?? {
1366
+ success: false,
1367
+ logs,
1368
+ error: {
1369
+ message: `Program exited without returning a result${stderr ? `
1370
+ stderr: ${stderr}` : ""}`,
1371
+ name: "NoResultError"
1372
+ }
1373
+ };
1374
+ } finally {
1375
+ await e2b.files.remove(dir).catch(() => {
1376
+ });
1377
+ }
1378
+ }
1379
+ };
1217
1380
 
1381
+ exports.E2BCodeModeTransport = E2BCodeModeTransport;
1218
1382
  exports.E2BProcessManager = E2BProcessManager;
1219
1383
  exports.E2BSandbox = E2BSandbox;
1220
1384
  exports.createDefaultMountableTemplate = createDefaultMountableTemplate;