@mastra/e2b 0.0.0-studio-deploy-20260404182525 → 0.0.0-studio-cli-20260504022012

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,11 +1,107 @@
1
1
  # @mastra/e2b
2
2
 
3
- ## 0.0.0-studio-deploy-20260404182525
3
+ ## 0.0.0-studio-cli-20260504022012
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - Updated dependencies [[`c55b527`](https://github.com/mastra-ai/mastra/commit/c55b52758a31368b2077b0dbbc3badfe4063f560), [`7eb2596`](https://github.com/mastra-ai/mastra/commit/7eb25960d607e07468c9a10c5437abd2deaf1e9a)]:
8
- - @mastra/core@0.0.0-studio-deploy-20260404182525
7
+ - Updated dependencies [[`6dcd65f`](https://github.com/mastra-ai/mastra/commit/6dcd65f2a34069e6dc43ba35f1d11119b9b40bef), [`c05c9a1`](https://github.com/mastra-ai/mastra/commit/c05c9a13230988cef6d438a62f37760f31927bc7), [`e24aacb`](https://github.com/mastra-ai/mastra/commit/e24aacba07bd66f5d95b636dc24016fca26b52cf), [`1c2dda8`](https://github.com/mastra-ai/mastra/commit/1c2dda805fbfccc0abf55d4cb20cc34402dc3f0c), [`c721164`](https://github.com/mastra-ai/mastra/commit/c7211643f7ac861f83b19a3757cc921487fc9d75), [`1b55954`](https://github.com/mastra-ai/mastra/commit/1b559541c1e08a10e49d01ffc51a634dfc37a286), [`5adc55e`](https://github.com/mastra-ai/mastra/commit/5adc55e63407be8ee977914957d68bcc2a075ceb), [`70017d7`](https://github.com/mastra-ai/mastra/commit/70017d72ab741b5d7040e2a15c251a317782e39e), [`e4942bc`](https://github.com/mastra-ai/mastra/commit/e4942bc7fdc903572f7d84f26d5e15f9d39c763d)]:
8
+ - @mastra/core@0.0.0-studio-cli-20260504022012
9
+
10
+ ## 0.3.0
11
+
12
+ ### Minor Changes
13
+
14
+ - Added Azure Blob sandbox mount support via blobfuse2 in @mastra/e2b and @mastra/daytona. `sandbox.mount(azureBlobFilesystem, '/data')` now works for Azure containers, matching the existing s3fs (S3) and gcsfuse (GCS) integration. Supports authentication via accountKey, sasToken, connectionString, or managed identity/default credentials, and preserves AzureBlobFilesystem prefixes when mounting. ([#15874](https://github.com/mastra-ai/mastra/pull/15874))
15
+
16
+ ```ts
17
+ import { E2BSandbox } from '@mastra/e2b';
18
+ import { AzureBlobFilesystem } from '@mastra/azure/blob';
19
+
20
+ const azureFs = new AzureBlobFilesystem({ container: 'my-data', connectionString: '...' });
21
+ const sandbox = new E2BSandbox();
22
+ await sandbox.mount(azureFs, '/data');
23
+ // Sandbox processes can now read/write /data/* directly against the Azure container.
24
+ ```
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [[`6db978c`](https://github.com/mastra-ai/mastra/commit/6db978c42e94e75540a504f7230086f0b5cd35f9), [`512a013`](https://github.com/mastra-ai/mastra/commit/512a013f285aa9c0aa8f08a35b2ce09f9938b017), [`e9becde`](https://github.com/mastra-ai/mastra/commit/e9becdeed9176b9f8392e557bde12b933f99cf7a), [`703a443`](https://github.com/mastra-ai/mastra/commit/703a44390c587d9c0b8ae94ec4edd8afb2a74044), [`808df1b`](https://github.com/mastra-ai/mastra/commit/808df1b39358b5f10b7317107e42b1fda7c87185)]:
29
+ - @mastra/core@1.29.1
30
+
31
+ ## 0.3.0-alpha.0
32
+
33
+ ### Minor Changes
34
+
35
+ - Added Azure Blob sandbox mount support via blobfuse2 in @mastra/e2b and @mastra/daytona. `sandbox.mount(azureBlobFilesystem, '/data')` now works for Azure containers, matching the existing s3fs (S3) and gcsfuse (GCS) integration. Supports authentication via accountKey, sasToken, connectionString, or managed identity/default credentials, and preserves AzureBlobFilesystem prefixes when mounting. ([#15874](https://github.com/mastra-ai/mastra/pull/15874))
36
+
37
+ ```ts
38
+ import { E2BSandbox } from '@mastra/e2b';
39
+ import { AzureBlobFilesystem } from '@mastra/azure/blob';
40
+
41
+ const azureFs = new AzureBlobFilesystem({ container: 'my-data', connectionString: '...' });
42
+ const sandbox = new E2BSandbox();
43
+ await sandbox.mount(azureFs, '/data');
44
+ // Sandbox processes can now read/write /data/* directly against the Azure container.
45
+ ```
46
+
47
+ ### Patch Changes
48
+
49
+ - Updated dependencies [[`512a013`](https://github.com/mastra-ai/mastra/commit/512a013f285aa9c0aa8f08a35b2ce09f9938b017), [`e9becde`](https://github.com/mastra-ai/mastra/commit/e9becdeed9176b9f8392e557bde12b933f99cf7a)]:
50
+ - @mastra/core@1.29.1-alpha.2
51
+
52
+ ## 0.2.0
53
+
54
+ ### Minor Changes
55
+
56
+ - Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the `prefix` option on your S3 filesystem. ([#15171](https://github.com/mastra-ai/mastra/pull/15171))
57
+
58
+ **Example:**
59
+
60
+ ```typescript
61
+ const fs = new S3Filesystem({
62
+ bucket: 'my-bucket',
63
+ region: 'us-east-1',
64
+ prefix: 'workspace/data',
65
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
66
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
67
+ });
68
+ ```
69
+
70
+ When mounted in a sandbox, only the contents under `workspace/data/` in the bucket will be visible at the mount path. This uses the s3fs `bucket:/path` syntax under the hood.
71
+
72
+ Closes #15147.
73
+
74
+ ### Patch Changes
75
+
76
+ - Updated dependencies [[`f112db1`](https://github.com/mastra-ai/mastra/commit/f112db179557ae9b5a0f1d25dc47f928d7d61cd9), [`21d9706`](https://github.com/mastra-ai/mastra/commit/21d970604d89eee970cbf8013d26d7551aff6ea5), [`0a0aa94`](https://github.com/mastra-ai/mastra/commit/0a0aa94729592e99885af2efb90c56aaada62247), [`ed07df3`](https://github.com/mastra-ai/mastra/commit/ed07df32a9d539c8261e892fc1bade783f5b41a6), [`01a7d51`](https://github.com/mastra-ai/mastra/commit/01a7d513493d21562f677f98550f7ceb165ba78c)]:
77
+ - @mastra/core@1.27.0
78
+
79
+ ## 0.2.0-alpha.0
80
+
81
+ ### Minor Changes
82
+
83
+ - Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the `prefix` option on your S3 filesystem. ([#15171](https://github.com/mastra-ai/mastra/pull/15171))
84
+
85
+ **Example:**
86
+
87
+ ```typescript
88
+ const fs = new S3Filesystem({
89
+ bucket: 'my-bucket',
90
+ region: 'us-east-1',
91
+ prefix: 'workspace/data',
92
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
93
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
94
+ });
95
+ ```
96
+
97
+ When mounted in a sandbox, only the contents under `workspace/data/` in the bucket will be visible at the mount path. This uses the s3fs `bucket:/path` syntax under the hood.
98
+
99
+ Closes #15147.
100
+
101
+ ### Patch Changes
102
+
103
+ - Updated dependencies [[`f112db1`](https://github.com/mastra-ai/mastra/commit/f112db179557ae9b5a0f1d25dc47f928d7d61cd9), [`21d9706`](https://github.com/mastra-ai/mastra/commit/21d970604d89eee970cbf8013d26d7551aff6ea5)]:
104
+ - @mastra/core@1.26.1-alpha.0
9
105
 
10
106
  ## 0.1.2
11
107
 
package/README.md CHANGED
@@ -31,11 +31,12 @@ const agent = new Agent({
31
31
 
32
32
  ### Mounting Cloud Storage
33
33
 
34
- E2B sandboxes can mount S3 or GCS filesystems, making cloud storage accessible as a local directory inside the sandbox:
34
+ E2B sandboxes can mount S3, GCS, or Azure Blob filesystems, making cloud storage accessible as a local directory inside the sandbox:
35
35
 
36
36
  ```typescript
37
37
  import { Workspace } from '@mastra/core/workspace';
38
38
  import { S3Filesystem } from '@mastra/s3';
39
+ import { AzureBlobFilesystem } from '@mastra/azure/blob';
39
40
  import { E2BSandbox } from '@mastra/e2b';
40
41
 
41
42
  const workspace = new Workspace({
@@ -46,6 +47,11 @@ const workspace = new Workspace({
46
47
  accessKeyId: process.env.AWS_ACCESS_KEY_ID,
47
48
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
48
49
  }),
50
+ '/azure-data': new AzureBlobFilesystem({
51
+ container: 'my-container',
52
+ connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
53
+ prefix: 'workspace/data',
54
+ }),
49
55
  },
50
56
  sandbox: new E2BSandbox(),
51
57
  });
package/dist/index.cjs CHANGED
@@ -35,6 +35,27 @@ function validateEndpoint(endpoint) {
35
35
  throw new Error(`Invalid endpoint URL: "${endpoint}"`);
36
36
  }
37
37
  }
38
+ function validatePrefix(prefix) {
39
+ let normalized = prefix;
40
+ while (normalized.startsWith("/")) normalized = normalized.slice(1);
41
+ while (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
42
+ if (!normalized) {
43
+ throw new Error("Mount prefix cannot be empty after normalization.");
44
+ }
45
+ if (normalized.includes("//") || normalized.split("/").some((s) => s === "." || s === "..")) {
46
+ throw new Error(`Invalid mount prefix: "${prefix}". Path traversal is not allowed.`);
47
+ }
48
+ if (/[\x00-\x1f\x7f]/.test(normalized)) {
49
+ throw new Error(`Invalid mount prefix: "${prefix}". Control characters are not allowed.`);
50
+ }
51
+ return normalized;
52
+ }
53
+
54
+ // src/utils/shell-quote.ts
55
+ function shellQuote(arg) {
56
+ if (/^[a-zA-Z0-9._\-/@:=]+$/.test(arg)) return arg;
57
+ return "'" + arg.replace(/'/g, "'\\''") + "'";
58
+ }
38
59
 
39
60
  // src/sandbox/mounts/s3.ts
40
61
  async function mountS3(mountPath, config, ctx) {
@@ -103,7 +124,12 @@ Error details: ${installResult.stderr || installResult.stdout}`
103
124
  mountOptions.push("ro");
104
125
  logger.debug(`${LOG_PREFIX} Mounting as read-only`);
105
126
  }
106
- const mountCmd = `sudo s3fs ${config.bucket} ${mountPath} -o ${mountOptions.join(" -o ")}`;
127
+ let bucketArg = config.bucket;
128
+ if (config.prefix) {
129
+ const normalizedPrefix = validatePrefix(config.prefix);
130
+ bucketArg = `${config.bucket}:/${normalizedPrefix}`;
131
+ }
132
+ const mountCmd = `sudo s3fs ${shellQuote(bucketArg)} ${shellQuote(mountPath)} -o ${mountOptions.join(" -o ")}`;
107
133
  logger.debug(`${LOG_PREFIX} Mounting S3:`, hasCredentials ? mountCmd.replace(credentialsPath, "***") : mountCmd);
108
134
  try {
109
135
  const result = await sandbox.commands.run(mountCmd, { timeoutMs: 6e4 });
@@ -171,6 +197,231 @@ async function mountGCS(mountPath, config, ctx) {
171
197
  throw new Error(`Failed to mount GCS bucket: ${stderr || stdout || error}`);
172
198
  }
173
199
  }
200
+ var SAFE_CONTAINER_NAME = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
201
+ var BLOBFUSE2_GITHUB_DEB = "https://github.com/Azure/azure-storage-fuse/releases/download/blobfuse2-2.5.1/blobfuse2-2.5.1-Ubuntu-22.04.x86_64.deb";
202
+ function validateContainerName(name) {
203
+ if (!SAFE_CONTAINER_NAME.test(name) || name.includes("--")) {
204
+ throw new Error(
205
+ `Invalid Azure container name: "${name}". Container names must be 3-63 lowercase alphanumeric characters or hyphens, with no consecutive hyphens.`
206
+ );
207
+ }
208
+ }
209
+ function parseConnectionString(cs) {
210
+ const out = {};
211
+ for (const part of cs.split(";")) {
212
+ const eq = part.indexOf("=");
213
+ if (eq === -1) continue;
214
+ const key = part.slice(0, eq).trim();
215
+ const value = part.slice(eq + 1).trim();
216
+ if (!value) continue;
217
+ if (key === "AccountName") out.accountName = value;
218
+ else if (key === "AccountKey") out.accountKey = value;
219
+ else if (key === "SharedAccessSignature") out.sasToken = value;
220
+ else if (key === "BlobEndpoint") out.endpoint = value;
221
+ else if (key === "EndpointSuffix") out.endpointSuffix = value;
222
+ else if (key === "DefaultEndpointsProtocol") out.protocol = value;
223
+ }
224
+ if (!out.endpoint && out.accountName) {
225
+ out.endpoint = `${out.protocol || "https"}://${out.accountName}.blob.${out.endpointSuffix || "core.windows.net"}`;
226
+ }
227
+ return out;
228
+ }
229
+ function yamlString(value) {
230
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
231
+ }
232
+ function parseOsRelease(output) {
233
+ const values = {};
234
+ for (const line of output.split("\n")) {
235
+ const eq = line.indexOf("=");
236
+ if (eq === -1) continue;
237
+ const key = line.slice(0, eq);
238
+ const value = line.slice(eq + 1).trim().replace(/^"|"$/g, "");
239
+ values[key] = value;
240
+ }
241
+ return values;
242
+ }
243
+ function resolveMicrosoftAptRepos(osReleaseOutput) {
244
+ const osRelease = parseOsRelease(osReleaseOutput);
245
+ const distroId = osRelease.ID || "ubuntu";
246
+ const codename = osRelease.VERSION_CODENAME || (distroId === "debian" ? "bookworm" : "jammy");
247
+ const versionId = osRelease.VERSION_ID || (distroId === "debian" ? "12" : "22.04");
248
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(codename)) {
249
+ throw new Error(`Invalid distro codename for blobfuse2 repo: "${codename}"`);
250
+ }
251
+ if (!/^\d+(?:\.\d+)?$/.test(versionId)) {
252
+ throw new Error(`Invalid distro version for blobfuse2 repo: "${versionId}"`);
253
+ }
254
+ if (distroId === "debian") {
255
+ const repos = [
256
+ { repoUrl: `https://packages.microsoft.com/debian/${versionId.split(".")[0]}/prod`, suite: codename }
257
+ ];
258
+ if (versionId.split(".")[0] !== "12" || codename !== "bookworm") {
259
+ repos.push({ repoUrl: "https://packages.microsoft.com/debian/12/prod", suite: "bookworm" });
260
+ }
261
+ return repos;
262
+ }
263
+ if (distroId === "ubuntu") {
264
+ const repos = [{ repoUrl: `https://packages.microsoft.com/ubuntu/${versionId}/prod`, suite: codename }];
265
+ if (versionId !== "24.04" || codename !== "noble") {
266
+ repos.push({ repoUrl: "https://packages.microsoft.com/ubuntu/24.04/prod", suite: "noble" });
267
+ }
268
+ if (versionId !== "22.04" || codename !== "jammy") {
269
+ repos.push({ repoUrl: "https://packages.microsoft.com/ubuntu/22.04/prod", suite: "jammy" });
270
+ }
271
+ return repos;
272
+ }
273
+ throw new Error(`Unsupported distro for blobfuse2 runtime installation: "${distroId}"`);
274
+ }
275
+ function resolveAuth(config) {
276
+ let accountName = config.accountName;
277
+ let accountKey = config.accountKey;
278
+ let sasToken = config.sasToken;
279
+ let endpoint = config.endpoint;
280
+ if (config.connectionString) {
281
+ const parsed = parseConnectionString(config.connectionString);
282
+ accountName = accountName ?? parsed.accountName;
283
+ accountKey = accountKey ?? parsed.accountKey;
284
+ sasToken = sasToken ?? parsed.sasToken;
285
+ endpoint = endpoint ?? parsed.endpoint;
286
+ }
287
+ let mode;
288
+ if (config.useDefaultCredential) {
289
+ mode = "msi";
290
+ } else if (sasToken) {
291
+ mode = "sas";
292
+ } else if (accountKey) {
293
+ mode = "key";
294
+ } else {
295
+ throw new Error(
296
+ "Azure Blob mount requires credentials: provide connectionString, accountKey + accountName, sasToken + accountName, or useDefaultCredential."
297
+ );
298
+ }
299
+ if (!accountName) {
300
+ throw new Error("Azure Blob mount requires an accountName (either explicitly or via connectionString).");
301
+ }
302
+ if (endpoint) {
303
+ validateEndpoint(endpoint);
304
+ }
305
+ return { mode, accountName, accountKey, sasToken, endpoint };
306
+ }
307
+ function buildBlobfuseConfig(container, auth, cachePath, readOnly) {
308
+ const lines = [
309
+ "allow-other: true",
310
+ "foreground: false",
311
+ `read-only: ${readOnly ? "true" : "false"}`,
312
+ "logging:",
313
+ " type: silent",
314
+ "components:",
315
+ " - libfuse",
316
+ " - file_cache",
317
+ " - attr_cache",
318
+ " - azstorage",
319
+ "libfuse:",
320
+ " attribute-expiration-sec: 240",
321
+ " entry-expiration-sec: 240",
322
+ " negative-entry-expiration-sec: 120",
323
+ "file_cache:",
324
+ ` path: ${yamlString(cachePath)}`,
325
+ " timeout-sec: 120",
326
+ "attr_cache:",
327
+ " timeout-sec: 7200",
328
+ "azstorage:",
329
+ ` mode: ${auth.mode}`,
330
+ ` account-name: ${yamlString(auth.accountName)}`,
331
+ ` container: ${yamlString(container)}`
332
+ ];
333
+ if (auth.mode === "key" && auth.accountKey) {
334
+ lines.push(` account-key: ${yamlString(auth.accountKey)}`);
335
+ } else if (auth.mode === "sas" && auth.sasToken) {
336
+ lines.push(` sas: ${yamlString(auth.sasToken)}`);
337
+ }
338
+ if (auth.endpoint) {
339
+ lines.push(` endpoint: ${yamlString(auth.endpoint.replace(/\/$/, ""))}`);
340
+ }
341
+ return lines.join("\n") + "\n";
342
+ }
343
+ async function mountAzure(mountPath, config, ctx) {
344
+ const { sandbox, logger } = ctx;
345
+ validateContainerName(config.container);
346
+ const auth = resolveAuth(config);
347
+ const prefix = config.prefix ? validatePrefix(config.prefix) : void 0;
348
+ const checkResult = await sandbox.commands.run('which blobfuse2 || echo "not found"');
349
+ if (checkResult.stdout.includes("not found")) {
350
+ logger.warn(`${LOG_PREFIX} blobfuse2 not found, attempting runtime installation...`);
351
+ logger.info(
352
+ `${LOG_PREFIX} Tip: For faster startup, pre-install blobfuse2 in your sandbox template via createMountableTemplate()`
353
+ );
354
+ const osReleaseResult = await sandbox.commands.run("cat /etc/os-release 2>/dev/null || true");
355
+ const repos = resolveMicrosoftAptRepos(osReleaseResult.stdout);
356
+ const repoSetupResult = await sandbox.commands.run(
357
+ "sudo mkdir -p /etc/apt/keyrings && curl --retry 3 --retry-all-errors --retry-delay 2 -fsSL https://packages.microsoft.com/keys/microsoft.asc -o /tmp/ms-key.asc && sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/microsoft.gpg /tmp/ms-key.asc",
358
+ { timeoutMs: 6e4 }
359
+ );
360
+ let installResult;
361
+ if (repoSetupResult.exitCode === 0) {
362
+ for (const { repoUrl, suite } of repos) {
363
+ installResult = await sandbox.commands.run(
364
+ `echo "deb [signed-by=/etc/apt/keyrings/microsoft.gpg] ${repoUrl} ${suite} main" | sudo tee /etc/apt/sources.list.d/microsoft-prod.list && sudo apt-get update 2>&1 && sudo apt-get install -y blobfuse2 fuse3 2>&1`,
365
+ { timeoutMs: 18e4 }
366
+ );
367
+ if (installResult.exitCode === 0) break;
368
+ logger.warn(`${LOG_PREFIX} blobfuse2 install failed for ${repoUrl} ${suite}, trying fallback if available`);
369
+ }
370
+ } else {
371
+ logger.warn(`${LOG_PREFIX} Failed to set up Microsoft apt repository, trying GitHub release fallback`);
372
+ }
373
+ let verifyResult = await sandbox.commands.run("which blobfuse2 && blobfuse2 --version", { timeoutMs: 3e4 });
374
+ if (verifyResult.exitCode !== 0) {
375
+ installResult = await sandbox.commands.run(
376
+ `sudo apt-get update -qq 2>&1 || true && sudo apt-get install -y fuse3 ca-certificates curl 2>&1 && curl -L --retry 3 --retry-all-errors --retry-delay 2 -fSLo /tmp/blobfuse2.deb ${BLOBFUSE2_GITHUB_DEB} && sudo dpkg -i /tmp/blobfuse2.deb 2>&1 && sudo bash -c 'lib=$(find /usr/lib -name "libfuse3.so.3.*" | head -1); [ -z "$lib" ] || ln -sf "$lib" /usr/lib/x86_64-linux-gnu/libfuse3.so.3'`,
377
+ { timeoutMs: 18e4 }
378
+ );
379
+ verifyResult = await sandbox.commands.run("which blobfuse2 && blobfuse2 --version", { timeoutMs: 3e4 });
380
+ }
381
+ if (!installResult || verifyResult.exitCode !== 0) {
382
+ throw new Error(
383
+ `Failed to install blobfuse2. For Azure Blob mounting, your template needs blobfuse2 and fuse3.
384
+
385
+ Option 1: Use createMountableTemplate() helper:
386
+ import { E2BSandbox, createMountableTemplate } from '@mastra/e2b';
387
+ const sandbox = new E2BSandbox({ template: createMountableTemplate() });
388
+
389
+ Option 2: Customize the base template:
390
+ new E2BSandbox({ template: base => base.aptInstall(['your-packages']) })
391
+
392
+ Error details: ${verifyResult.stderr || verifyResult.stdout || installResult?.stderr || installResult?.stdout || "unknown error"}`
393
+ );
394
+ }
395
+ }
396
+ const mountHash = crypto.createHash("md5").update(mountPath).digest("hex").slice(0, 8);
397
+ const configPath = `/tmp/.blobfuse2-config-${mountHash}.yaml`;
398
+ const cachePath = `/tmp/blobfuse2-cache-${mountHash}`;
399
+ const yaml = buildBlobfuseConfig(config.container, auth, cachePath, !!config.readOnly);
400
+ await sandbox.commands.run(`sudo rm -f ${configPath}`);
401
+ await sandbox.files.write(configPath, yaml);
402
+ await sandbox.commands.run(`sudo chown root:root ${configPath} && sudo chmod 600 ${configPath}`);
403
+ await sandbox.commands.run(`sudo rm -rf ${shellQuote(cachePath)} && sudo mkdir -p ${shellQuote(cachePath)}`);
404
+ const prefixFlags = prefix ? ` --virtual-directory=true --subdirectory=${shellQuote(prefix)}` : "";
405
+ const mountCmd = `sudo blobfuse2 mount ${shellQuote(mountPath)} --config-file=${shellQuote(configPath)}${prefixFlags}`;
406
+ logger.debug(`${LOG_PREFIX} Mounting Azure Blob:`, mountCmd);
407
+ try {
408
+ const result = await sandbox.commands.run(mountCmd, { timeoutMs: 6e4 });
409
+ logger.debug(`${LOG_PREFIX} blobfuse2 result:`, {
410
+ exitCode: result.exitCode,
411
+ stdout: result.stdout,
412
+ stderr: result.stderr
413
+ });
414
+ if (result.exitCode !== 0) {
415
+ throw new Error(`Failed to mount Azure Blob container: ${result.stderr || result.stdout}`);
416
+ }
417
+ } catch (error) {
418
+ const errorObj = error;
419
+ const stderr = errorObj.result?.stderr || "";
420
+ const stdout = errorObj.result?.stdout || "";
421
+ logger.error(`${LOG_PREFIX} blobfuse2 error:`, { stderr, stdout, error: String(error) });
422
+ throw new Error(`Failed to mount Azure Blob container: ${stderr || stdout || error}`);
423
+ }
424
+ }
174
425
  var E2BProcessHandle = class extends workspace.ProcessHandle {
175
426
  pid;
176
427
  _e2bHandle;
@@ -578,6 +829,11 @@ var E2BSandbox = class extends workspace.MastraSandbox {
578
829
  await mountGCS(mountPath, config, mountCtx);
579
830
  this.logger.debug(`${LOG_PREFIX} Mounted GCS bucket at ${mountPath}`);
580
831
  break;
832
+ case "azure-blob":
833
+ this.logger.debug(`${LOG_PREFIX} Mounting Azure Blob container at ${mountPath}...`);
834
+ await mountAzure(mountPath, config, mountCtx);
835
+ this.logger.debug(`${LOG_PREFIX} Mounted Azure Blob container at ${mountPath}`);
836
+ break;
581
837
  default:
582
838
  this.mounts.set(mountPath, {
583
839
  filesystem,
@@ -653,7 +909,7 @@ var E2BSandbox = class extends workspace.MastraSandbox {
653
909
  }
654
910
  this.logger.debug(`${LOG_PREFIX} Reconciling mounts. Expected paths:`, expectedMountPaths);
655
911
  const mountsResult = await this._sandbox.commands.run(
656
- `grep -E 'fuse\\.(s3fs|gcsfuse)' /proc/mounts | awk '{print $2}'`
912
+ `grep -E 'fuse\\.(s3fs|gcsfuse|blobfuse2)' /proc/mounts | awk '{print $2}'`
657
913
  );
658
914
  const currentMounts = mountsResult.stdout.trim().split("\n").filter((p) => p.length > 0);
659
915
  this.logger.debug(`${LOG_PREFIX} Current FUSE mounts in sandbox:`, currentMounts);