Skip to content
Open
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ CEREBRAS_API_KEY=
MISTRAL_API_KEY=
DAYTONA_API_KEY=
E2B_API_KEY=
LEAP0_API_KEY=
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This template demonstrates how to build an AI coding assistant that can work wit
## Prerequisites

- Node.js 22.13.0 or later
- API key for your chosen sandbox provider ([Daytona](https://www.daytona.io/) or [E2B](https://e2b.dev))
- API key for your chosen sandbox provider ([Daytona](https://www.daytona.io/), [E2B](https://e2b.dev), or [Leap0](https://leap0.dev))
- API key for your chosen model provider

## Setup
Expand Down Expand Up @@ -48,6 +48,8 @@ This template demonstrates how to build an AI coding assistant that can work wit
# Option 2: Use E2B (set E2B_API_KEY)
# E2B_API_KEY="your-e2b-api-key-here"

# Option 3: Use Leap0 (set LEAP0_API_KEY; see https://github.com/leap0-dev/leap0-js)
# LEAP0_API_KEY="your-leap0-api-key-here"

# Model provider (required)
OPENAI_API_KEY="your-openai-api-key-here"
Expand Down Expand Up @@ -94,7 +96,7 @@ Complete toolkit for sandbox interaction with support for multiple providers:

**Provider Selection:**

- Automatically uses **Daytona** or **E2B** based on which API key you set
- Automatically uses **Daytona**, **E2B**, or **Leap0** based on which API key you set

**Sandbox Management:**

Expand Down Expand Up @@ -149,6 +151,9 @@ DAYTONA_API_KEY=your_daytona_api_key_here
# Option 2: E2B
E2B_API_KEY=your_e2b_api_key_here

# Option 3: Leap0
LEAP0_API_KEY=your_leap0_api_key_here

```

> [!Note]
Expand Down Expand Up @@ -179,10 +184,10 @@ export const codingAgent = new Agent({

## Common Issues

### "Please set either DAYTONA_API_KEY or E2B_API_KEY environment variable"
### "Please set either DAYTONA_API_KEY or E2B_API_KEY or LEAP0_API_KEY environment variable"

- You need to configure a sandbox provider by setting one of the API keys
- Add either `DAYTONA_API_KEY` or `E2B_API_KEY` to your `.env` file
- Add either `DAYTONA_API_KEY` or `E2B_API_KEY` or `LEAP0_API_KEY` to your `.env` file
- Only set ONE provider API key (not both)
- Restart the development server after adding the key

Expand Down Expand Up @@ -222,5 +227,8 @@ src/mastra/
daytona/
tools.ts # Daytona sandbox implementation
utils.ts # Daytona helper functions
leap0/
tools.ts # Leap0 sandbox implementation (leap0-js)
utils.ts # Leap0 client and path helpers
index.ts # Mastra configuration with storage and logging
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@mastra/mcp": "latest",
"@mastra/memory": "latest",
"@mastra/observability": "latest",
"leap0": "^0.5.0",
"supports-color": "^10.2.2",
"zod": "^3.25.76"
},
Expand Down
73 changes: 51 additions & 22 deletions src/mastra/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,63 @@
import * as e2bTools from './e2b';
import * as daytonaTools from './daytona/tools';
import * as leap0Tools from './leap0/tools';

function getProvider() {
type SandboxProvider = 'daytona' | 'e2b' | 'leap0';

function getProvider(): SandboxProvider {
if (process.env.DAYTONA_API_KEY) {
return 'daytona';
} else if (process.env.E2B_API_KEY) {
}
if (process.env.E2B_API_KEY) {
return 'e2b';
} else {
throw new Error(
'No sandbox provider configured. Please set either DAYTONA_API_KEY or E2B_API_KEY environment variable.',
);
}
if (process.env.LEAP0_API_KEY) {
return 'leap0';
}
throw new Error(
'No sandbox provider configured. Please set DAYTONA_API_KEY, E2B_API_KEY, or LEAP0_API_KEY environment variable.',
);
}

const provider = getProvider();

// Helper function to select the right tool (bundler can inline this)
// Using 'as any' because E2B and Daytona have slightly different schemas
const selectTool = (daytonaTool: any, e2bTool: any) => (provider === 'daytona' ? daytonaTool : e2bTool);
// Using 'as any' because providers have slightly different output schemas for some tools.
const pickTool = (daytonaTool: any, e2bTool: any, leap0Tool: any) => {
if (provider === 'daytona') {
return daytonaTool;
}
if (provider === 'e2b') {
return e2bTool;
}
return leap0Tool;
};

export const createSandbox = selectTool(daytonaTools.createSandbox, e2bTools.createSandbox);
export const runCode = selectTool(daytonaTools.runCode, e2bTools.runCode);
export const readFile = selectTool(daytonaTools.readFile, e2bTools.readFile);
export const writeFile = selectTool(daytonaTools.writeFile, e2bTools.writeFile);
export const writeFiles = selectTool(daytonaTools.writeFiles, e2bTools.writeFiles);
export const listFiles = selectTool(daytonaTools.listFiles, e2bTools.listFiles);
export const deleteFile = selectTool(daytonaTools.deleteFile, e2bTools.deleteFile);
export const createDirectory = selectTool(daytonaTools.createDirectory, e2bTools.createDirectory);
export const getFileInfo = selectTool(daytonaTools.getFileInfo, e2bTools.getFileInfo);
export const checkFileExists = selectTool(daytonaTools.checkFileExists, e2bTools.checkFileExists);
export const getFileSize = selectTool(daytonaTools.getFileSize, e2bTools.getFileSize);
export const watchDirectory = selectTool(daytonaTools.watchDirectory, e2bTools.watchDirectory);
export const runCommand = selectTool(daytonaTools.runCommand, e2bTools.runCommand);
export const createSandbox = pickTool(
daytonaTools.createSandbox,
e2bTools.createSandbox,
leap0Tools.createSandbox,
);
export const runCode = pickTool(daytonaTools.runCode, e2bTools.runCode, leap0Tools.runCode);
export const readFile = pickTool(daytonaTools.readFile, e2bTools.readFile, leap0Tools.readFile);
export const writeFile = pickTool(daytonaTools.writeFile, e2bTools.writeFile, leap0Tools.writeFile);
export const writeFiles = pickTool(daytonaTools.writeFiles, e2bTools.writeFiles, leap0Tools.writeFiles);
export const listFiles = pickTool(daytonaTools.listFiles, e2bTools.listFiles, leap0Tools.listFiles);
export const deleteFile = pickTool(daytonaTools.deleteFile, e2bTools.deleteFile, leap0Tools.deleteFile);
export const createDirectory = pickTool(
daytonaTools.createDirectory,
e2bTools.createDirectory,
leap0Tools.createDirectory,
);
export const getFileInfo = pickTool(daytonaTools.getFileInfo, e2bTools.getFileInfo, leap0Tools.getFileInfo);
export const checkFileExists = pickTool(
daytonaTools.checkFileExists,
e2bTools.checkFileExists,
leap0Tools.checkFileExists,
);
export const getFileSize = pickTool(daytonaTools.getFileSize, e2bTools.getFileSize, leap0Tools.getFileSize);
export const watchDirectory = pickTool(
daytonaTools.watchDirectory,
e2bTools.watchDirectory,
leap0Tools.watchDirectory,
);
export const runCommand = pickTool(daytonaTools.runCommand, e2bTools.runCommand, leap0Tools.runCommand);
Loading