Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/tall-lions-give.md
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 8 additions & 0 deletions .changeset/wet-bees-poke.md
Original file line number Diff line number Diff line change
@@ -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.
26 changes: 13 additions & 13 deletions packages/sandbox-daytona/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as daytonaModule from "@daytonaio/sdk";
import type { Sandbox as DaytonaOriginalSandbox } from "@daytonaio/sdk";
import {
type WorkspaceSandbox,
type WorkspaceSandboxExecuteOptions,
type WorkspaceSandboxResult,
normalizeCommandAndArgs,
} from "@voltagent/core";

export type DaytonaSandboxInstance = DaytonaOriginalSandbox;

export type DaytonaSandboxOptions = {
apiKey?: string;
apiUrl?: string;
Expand All @@ -29,17 +32,6 @@ type DaytonaExecResult = {
};
};

type DaytonaSandboxInstance = {
process: {
executeCommand: (
command: string,
cwd?: string,
env?: Record<string, string>,
timeoutSeconds?: number,
) => Promise<DaytonaExecResult>;
};
};

type DaytonaClient = {
create: (
params?: Record<string, unknown>,
Expand Down Expand Up @@ -157,7 +149,7 @@ export class DaytonaSandbox implements WorkspaceSandbox {
return await client.create(this.createParams, options);
}

private async getSandbox(): Promise<DaytonaSandboxInstance> {
private async resolveSandbox(): Promise<DaytonaSandboxInstance> {
if (this.sandbox) {
return this.sandbox;
}
Expand All @@ -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<DaytonaSandboxInstance> {
return this.resolveSandbox();
}

async execute(options: WorkspaceSandboxExecuteOptions): Promise<WorkspaceSandboxResult> {
const startTime = Date.now();
const normalized = normalizeCommandAndArgs(options.command ?? "", options.args);
Expand All @@ -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
Expand Down
17 changes: 14 additions & 3 deletions packages/sandbox-e2b/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -376,7 +379,7 @@ export class E2BSandbox implements WorkspaceSandbox {
return await Sandbox.create(createOptions);
}

private async getSandbox(): Promise<E2BSdkSandbox> {
private async resolveSandbox(): Promise<E2BSdkSandbox> {
if (this.sandbox) {
return this.sandbox;
}
Expand All @@ -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<E2BSandboxInstance> {
return (await this.resolveSandbox()) as unknown as E2BSandboxInstance;
}

private async killCommand(sandbox: E2BSdkSandbox, handle?: E2BCommandHandle): Promise<void> {
if (!handle) {
return;
Expand Down Expand Up @@ -473,7 +484,7 @@ export class E2BSandbox implements WorkspaceSandbox {
timedOut = true;
resolve(null);
}, remaining);
this.getSandbox()
this.resolveSandbox()
.then((resolved) => {
if (settled) {
return;
Expand All @@ -491,7 +502,7 @@ export class E2BSandbox implements WorkspaceSandbox {
reject(error);
});
})
: await this.getSandbox();
: await this.resolveSandbox();

if (!sandbox) {
return {
Expand Down
29 changes: 29 additions & 0 deletions website/docs/workspaces/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down