A unified interface for Linux-based desktop sandbox providers. It can be used to create virtual desktop environments for AI agents to interact with graphical applications.
This repository is based on sandboxjs
, an abstraction layer for cloud sandbox providers.
import { Sandbox } from "./src/index.js";
async function runExample() {
// Create a new desktop sandbox
const desktop = await Sandbox.create("e2b"); // or "daytona"
// Perform basic desktop interactions
await desktop.moveMouse(100, 100);
await desktop.leftClick();
await desktop.write("Hello from desktop sandbox!");
const screenshot = await desktop.screenshot();
// Destroy the sandbox when done
await desktop.destroy();
}
runExample();
Provider | Desktop Environment | VNC Streaming | Mouse Interaction | Keyboard Input | Screenshots |
---|---|---|---|---|---|
E2B | âś… | âś… | âś… | âś… | âś… |
Daytona | âś… | âś… | âś… | âś… | âś… |
Create a .env
file in the root directory of the project and add at least one of the following environment variables:
# Get an E2B API key here: https://e2b.dev/dashboard
E2B_API_KEY=
# Get a Daytona API key here: https://app.daytona.io/dashboard/keys
DAYTONA_API_KEY=
npm install
Compiles the TypeScript source files to JavaScript in the dist/
directory.
npm run build
After building, run the example script:
node dist/examples/run-desktop.js
To run the test suite:
npm test
// Create default desktop sandbox
const desktop = await Sandbox.create("e2b"); // or "daytona"
// Create desktop sandbox with additional parameters
const e2bDesktop = await Sandbox.create("e2b", {
template: "gitwit-desktop", // default template for desktop sandboxes
resolution: [1280, 720], // set screen resolution
dpi: 96,
envs: { DISPLAY: ":0" }
});
const sandboxId = desktop.id();
await desktop.suspend();
await desktop.resume();
await desktop.destroy();
console.log(await desktop.readFile("/path/to/file"));
await desktop.writeFile("/path/to/file", "content");
console.log(await desktop.listFiles("/path/to/directory"));
await desktop.moveFile("/path/to/file", "/path/to/new/file");
await desktop.deleteFile("/path/to/file");
await desktop.createDirectory("/path/to/directory");
console.log(await desktop.getPreviewUrl(6080)); // VNC stream port
Execute commands in the sandbox with support for background execution and command options.
// Basic command execution
const { exitCode, output } = await desktop.runCommand("echo 'hello world'");
console.log(output); // "hello world"
console.log(exitCode); // 0
// Command with options
const result = await desktop.runCommand("ls -la", {
cwd: "/tmp",
envs: { MY_VAR: "value" },
timeoutMs: 5000
});
// Background command execution
const { pid } = await desktop.runCommand("sleep 10", { background: true });
console.log(`Background process started with PID: ${pid}`);
const imageBytes = await desktop.screenshot();
// Save the screenshot, e.g., using Node.js fs
// writeFileSync("screenshot.png", Buffer.from(imageBytes));
await desktop.leftClick(); // Clicks at current mouse position
await desktop.leftClick(100, 100); // Moves mouse to 100, 100 and clicks
await desktop.doubleClick();
await desktop.doubleClick(200, 200);
await desktop.rightClick();
await desktop.rightClick(300, 300);
await desktop.middleClick();
await desktop.middleClick(400, 400);
await desktop.scroll('up', 2); // Scrolls up twice
await desktop.scroll('down'); // Scrolls down once (default amount)
await desktop.moveMouse(500, 500);
await desktop.mousePress('left');
await desktop.mouseRelease('left');
const { x, y } = await desktop.getCursorPosition();
console.log(`Cursor is at x: ${x}, y: ${y}`);
const { width, height } = await desktop.getScreenSize();
console.log(`Screen size: ${width}x${height}`);
await desktop.write("Hello, World!");
await desktop.write("Another text", { chunkSize: 10, delayInMs: 100 });
await desktop.press("enter");
await desktop.press(["control", "c"]);
await desktop.drag([10, 10], [100, 100]); // Drags from (10,10) to (100,100)
await desktop.wait(2000); // Waits for 2 seconds
await desktop.open("https://google.com");
await desktop.open("/home/user/documents/report.pdf");
const windowId = await desktop.getCurrentWindowId();
console.log(`Current window ID: ${windowId}`);
const firefoxWindows = await desktop.getApplicationWindows("firefox");
console.log(`Firefox window IDs: ${firefoxWindows}`);
const title = await desktop.getWindowTitle("someWindowId");
console.log(`Window title: ${title}`);
await desktop.launch("firefox", "https://e2b.dev");
The stream
property provides methods to control the VNC server.
await desktop.stream.start({ requireAuth: true });
await desktop.stream.stop();
const authKey = desktop.stream.getAuthKey();
const url = desktop.stream.getUrl({ authKey });
Build custom desktop templates from your projects.
Note: Your project directory must contain a
Dockerfile
(or*.Dockerfile
file).
import { buildTemplate } from "@gitwit/sandbox";
const templateDir = "./template" // Directory containing Dockerfile and other template files
const templateName = "gitwit-desktop" // Name for your custom template/snapshot
// Build E2B desktop template
await buildTemplate('e2b', templateDir, templateName, {
cpuCount: 8,
memoryMB: 8192,
// teamId: 'your-team-id' // Optional: if you want to build for a specific team
});
// Build Daytona desktop snapshot
await buildTemplate('daytona', templateDir, templateName, {
cpu: 4,
memory: 8,
disk: 8,
gpu: 1
});
// Use built template
const desktopE2B = await Sandbox.create('e2b', { template: templateName }); // or "daytona"
Parts of this project are derived from the Dockerfile and TypeScript SDK in the E2B Desktop SDK. The Dockerfile is under the Apache License 2.0 and the TypeScript SDK is under the MIT License.
- Add support for watching file system changes
- Extend provider support