From 2b2cb118bd5e859d5b30963122bd0d8cedd187b1 Mon Sep 17 00:00:00 2001 From: Omer Aplak Date: Fri, 13 Feb 2026 12:29:41 -0800 Subject: [PATCH] feat(sandbox): expose underlying provider sandbox instances --- .changeset/tall-lions-give.md | 8 ++++++++ .changeset/wet-bees-poke.md | 8 ++++++++ packages/sandbox-daytona/src/index.ts | 26 ++++++++++++------------ packages/sandbox-e2b/src/index.ts | 17 +++++++++++++--- website/docs/workspaces/sandbox.md | 29 +++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 .changeset/tall-lions-give.md create mode 100644 .changeset/wet-bees-poke.md diff --git a/.changeset/tall-lions-give.md b/.changeset/tall-lions-give.md new file mode 100644 index 000000000..1d58a39c8 --- /dev/null +++ b/.changeset/tall-lions-give.md @@ -0,0 +1,8 @@ +--- +"@voltagent/sandbox-e2b": patch +--- + +feat: expose the underlying E2B SDK sandbox instance from `E2BSandbox`. + +- Added a public `getSandbox()` method that returns the original `e2b` `Sandbox` instance so provider-specific APIs (for example `files.read`) can be used directly. +- Added `E2BSandboxInstance` type export for the underlying SDK sandbox type. diff --git a/.changeset/wet-bees-poke.md b/.changeset/wet-bees-poke.md new file mode 100644 index 000000000..45c956a73 --- /dev/null +++ b/.changeset/wet-bees-poke.md @@ -0,0 +1,8 @@ +--- +"@voltagent/sandbox-daytona": patch +--- + +feat: expose the underlying Daytona SDK sandbox instance from `DaytonaSandbox`. + +- Added a public `getSandbox()` method that returns the original `@daytonaio/sdk` `Sandbox` instance so provider-specific APIs can be used directly. +- Added `DaytonaSandboxInstance` type export for the underlying SDK sandbox type. diff --git a/packages/sandbox-daytona/src/index.ts b/packages/sandbox-daytona/src/index.ts index c519eb6c4..27eb354b8 100644 --- a/packages/sandbox-daytona/src/index.ts +++ b/packages/sandbox-daytona/src/index.ts @@ -1,4 +1,5 @@ import * as daytonaModule from "@daytonaio/sdk"; +import type { Sandbox as DaytonaOriginalSandbox } from "@daytonaio/sdk"; import { type WorkspaceSandbox, type WorkspaceSandboxExecuteOptions, @@ -6,6 +7,8 @@ import { normalizeCommandAndArgs, } from "@voltagent/core"; +export type DaytonaSandboxInstance = DaytonaOriginalSandbox; + export type DaytonaSandboxOptions = { apiKey?: string; apiUrl?: string; @@ -29,17 +32,6 @@ type DaytonaExecResult = { }; }; -type DaytonaSandboxInstance = { - process: { - executeCommand: ( - command: string, - cwd?: string, - env?: Record, - timeoutSeconds?: number, - ) => Promise; - }; -}; - type DaytonaClient = { create: ( params?: Record, @@ -157,7 +149,7 @@ export class DaytonaSandbox implements WorkspaceSandbox { return await client.create(this.createParams, options); } - private async getSandbox(): Promise { + private async resolveSandbox(): Promise { if (this.sandbox) { return this.sandbox; } @@ -175,6 +167,14 @@ export class DaytonaSandbox implements WorkspaceSandbox { return this.sandboxPromise; } + /** + * Returns the underlying Daytona SDK sandbox instance. + * Use this when you need Daytona-specific APIs beyond `execute`. + */ + async getSandbox(): Promise { + return this.resolveSandbox(); + } + async execute(options: WorkspaceSandboxExecuteOptions): Promise { const startTime = Date.now(); const normalized = normalizeCommandAndArgs(options.command ?? "", options.args); @@ -197,7 +197,7 @@ export class DaytonaSandbox implements WorkspaceSandbox { }; } - const sandbox = await this.getSandbox(); + const sandbox = await this.resolveSandbox(); const maxOutputBytes = options.maxOutputBytes === undefined ? this.maxOutputBytes diff --git a/packages/sandbox-e2b/src/index.ts b/packages/sandbox-e2b/src/index.ts index 4c7813dac..ae7343712 100644 --- a/packages/sandbox-e2b/src/index.ts +++ b/packages/sandbox-e2b/src/index.ts @@ -3,8 +3,11 @@ import type { WorkspaceSandboxExecuteOptions, WorkspaceSandboxResult, } from "@voltagent/core"; +import type { Sandbox as E2BOriginalSandbox } from "e2b"; import * as e2bModule from "e2b"; +export type E2BSandboxInstance = E2BOriginalSandbox; + export type E2BSandboxOptions = { apiKey?: string; template?: string; @@ -376,7 +379,7 @@ export class E2BSandbox implements WorkspaceSandbox { return await Sandbox.create(createOptions); } - private async getSandbox(): Promise { + private async resolveSandbox(): Promise { if (this.sandbox) { return this.sandbox; } @@ -394,6 +397,14 @@ export class E2BSandbox implements WorkspaceSandbox { return this.sandboxPromise; } + /** + * Returns the underlying E2B SDK sandbox instance. + * Use this when you need E2B-specific APIs beyond `execute`. + */ + async getSandbox(): Promise { + return (await this.resolveSandbox()) as unknown as E2BSandboxInstance; + } + private async killCommand(sandbox: E2BSdkSandbox, handle?: E2BCommandHandle): Promise { if (!handle) { return; @@ -473,7 +484,7 @@ export class E2BSandbox implements WorkspaceSandbox { timedOut = true; resolve(null); }, remaining); - this.getSandbox() + this.resolveSandbox() .then((resolved) => { if (settled) { return; @@ -491,7 +502,7 @@ export class E2BSandbox implements WorkspaceSandbox { reject(error); }); }) - : await this.getSandbox(); + : await this.resolveSandbox(); if (!sandbox) { return { diff --git a/website/docs/workspaces/sandbox.md b/website/docs/workspaces/sandbox.md index 570368480..2a76526bd 100644 --- a/website/docs/workspaces/sandbox.md +++ b/website/docs/workspaces/sandbox.md @@ -127,6 +127,35 @@ const daytonaWorkspace = new Workspace({ }); ``` +If you need provider-specific APIs, keep a reference to the provider and access its native SDK instance: + +```ts +import { E2BSandbox } from "@voltagent/sandbox-e2b"; + +const sandbox = new E2BSandbox({ + apiKey: process.env.E2B_API_KEY, +}); + +const workspace = new Workspace({ sandbox }); + +const e2bSandbox = await sandbox.getSandbox(); +const bytes = await e2bSandbox.files.read("/workspace/file.txt", { format: "bytes" }); +``` + +```ts +import { DaytonaSandbox } from "@voltagent/sandbox-daytona"; + +const sandbox = new DaytonaSandbox({ + apiKey: process.env.DAYTONA_API_KEY, + apiUrl: "http://localhost:3000", +}); + +const workspace = new Workspace({ sandbox }); + +const daytonaSandbox = await sandbox.getSandbox(); +const response = await daytonaSandbox.process.executeCommand("ls -la"); +``` + ## Custom sandbox provider You can implement `WorkspaceSandbox` and plug it into `Workspace` directly.