Migrate from AgentBay to FC Agent Sandbox Desktop
A guide to migrating from Wuying AgentBay (
linux_latest) to the FC Agent Sandbox e2b-desktop SDK.
1. Capability mapping
Method names and signatures follow the e2b-desktop SDK release. Some capabilities are covered by shell commands inside the sandbox and are marked as "shell-aligned".
Wuying AgentBay session.computer.* | e2b-desktop equivalent | Notes |
| Mouse | ||
click_mouse(x, y) | left_click(x, y) | Pass coordinates directly; or use move_mouse + left_click() in two steps |
click_mouse(x, y, button="right") | right_click(x, y) | Same as above |
move_mouse(x, y) | move_mouse(x, y) | Same name |
drag_mouse(x1, y1, x2, y2) | drag((x1, y1), (x2, y2)) | Different signature; takes tuples |
double_click() | double_click() | Same name |
scroll(x, y, direction, amount) | move_mouse(x, y) + scroll(direction, amount) | No coordinate parameter; move first, then scroll |
get_cursor_position() | commands.run("xdotool getmouselocation") | Shell-aligned |
| Keyboard | ||
input_text(text) | write(text) | Different method name |
press_keys(keys) | press(key) / press(["ctrl", "c"]) | Single key or key combination list |
release_keys | commands.run("xdotool keyup <key>") | Shell-aligned |
| Screenshot | ||
screenshot() | screenshot() | Same name; PNG bytes |
beta_take_screenshot(format) | commands.run("scrot -q 80 /tmp/s.jpg") + files.read(path, format="bytes") | Shell-aligned (scrot supports jpg/png; jpg must be read explicitly as bytes) |
| Screen info | ||
get_screen_size() | commands.run("xdpyinfo -display :0 | grep dimensions") | Shell-aligned |
get_current_window_id() | get_current_window_id() | Same name |
| Window management | ||
list_root_windows | commands.run('xdotool search --onlyvisible --name ""') | Shell-aligned |
activate_window | commands.run("xdotool windowactivate --sync <id>") | Shell-aligned |
maximize_window | commands.run("xprop -id <id> -f _NET_WM_STATE 32a -set _NET_WM_STATE _NET_WM_STATE_MAXIMIZED_VERT,_NET_WM_STATE_MAXIMIZED_HORZ") | Shell-aligned |
minimize_window | commands.run("xdotool windowminimize <id>") | Shell-aligned |
resize_window | commands.run("xdotool windowsize --sync <id> <w> <h>") | Shell-aligned (terminal apps snap to character cells) |
close_window | commands.run("xdotool windowclose <id>") | Shell-aligned |
focus_mode | commands.run("xfconf-query -c xfwm4 -p /general/click_to_focus -s ...") | Shell-aligned |
| App management | ||
start_app(app, work_directory) | commands.run("cd <workdir> && <app> &") | Shell-aligned (launch(app[, url]) also works) |
stop_app | commands.run("pkill <app>") | Shell-aligned |
get_installed_apps | commands.run("ls /usr/share/applications/") | Shell-aligned |
list_visible_apps | commands.run('xdotool search --onlyvisible --name ""') | Shell-aligned |
| Agent (differences) | ||
execute_task | None | C-1: not supported |
get_task_status | None | C-2: not supported |
terminate_task | None | C-3: not supported |
| Desktop observation | ||
get_link_url / VNC | stream.start() + get_url() | Built-in auth; see §5 |
C-1 to C-3 (natural-language Agent) have no delivery plan and are outside the FC Agent Sandbox product scope. Everything else can be aligned through the SDK or shell commands.
When Computer Use is not needed: use Browser Use (CDP) for web-only operations, and commands/code for pure batch processing.
The mapping above was verified item by item with
e2b-desktop==2.4.1and thedesktop:v0.0.44template (35/35).The template ships with
xdotool/scrot/xdpyinfo/xprop/xwininfo/xfconf-querypreinstalled, but notwmctrl(use the equivalent xdotool/xprop commands above for window management; if you need wmctrl semantics, runapt-get install -y wmctrl, the sandbox can reach Debian repositories).The §6.2 frontend was verified in a real Chrome (RFB connection, screen rendering, and live desktop refresh all passed).
2. Scope limits
Linux desktop (Computer Use) migration uses the e2b-desktop SDK:
Mouse / keyboard / screenshot are existing SDK methods (two-step style); the VNC live stream is built in with auth.
You must use its
Sandboxsubclass to create sandboxes. Sandbox lifecycle semantics are the same as e2b (still used ascreate/connect/list/kill).Superset capabilities such as window and app management are outside this SDK; see the capability mapping in §1.
3. Built-in template mapping
| AgentBay | FC E2B | Notes |
linux_latest (Computer Use) | desktop template (internal image fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/desktop:v0.0.44) | The FC image ships with a desktop environment; build it with Template.build(), see §4 |
4. Prerequisites: Desktop template
The desktop template is not a built-in template available out of the box; you must build it from the FC Desktop image. The image already includes Xvfb + xfce4 + x11vnc + websockify + Chrome, so no Dockerfile is needed.
4.1 Set environment variables
export E2B_API_KEY=<key>
export E2B_API_URL=https://api.cn-beijing.e2b.fc.aliyuncs.com
export E2B_DOMAIN=cn-beijing.e2b.fc.aliyuncs.com4.2 Build the template
pip install "e2b>=2.31.0" e2b-desktop==2.4.1 python-dotenv# build.py
from dotenv import load_dotenv
from e2b import Template, default_build_logger
load_dotenv()
# Current stable version; will be updated later
FROM_IMAGE = "fc-e2b-registry.cn-beijing.cr.aliyuncs.com/runtime/desktop:v0.0.44"
build = Template.build(
Template().from_image(FROM_IMAGE),
name="my-desktop-template", # any name you like
cpu_count=4,
memory_mb=8192,
on_build_logs=default_build_logger(),
)
print(f"template_id: {build.template_id}")python build.pyThe image registry region must match
E2B_API_URL/E2B_DOMAIN. For other regions, replacecn-beijingin the image address.Recommended size: 4 vCPU, 8192 MB memory. The template needs no startup or readiness command; the Desktop SDK starts the desktop environment and live stream automatically.
5. Migration examples
5.1 Common interaction loop (screenshot + action)
# === Before migration: AgentBay ===
session = agent_bay.create(CreateSessionParams(image_id="linux_latest")).session
session.computer.move_mouse(640, 400)
session.computer.click_mouse(640, 400)
session.computer.input_text("echo hello")
session.computer.press_keys(["enter"])
png = session.computer.screenshot()
# === After migration: e2b-desktop SDK ===
from e2b_desktop import Sandbox
desktop = Sandbox.create(template="my-desktop-template", timeout=600)
try:
# Launch Chrome and wait for rendering
desktop.launch("google-chrome", "https://example.com")
desktop.wait(10000)
# Mouse: the E2B SDK is two-step (move + click); dragging uses tuples
desktop.move_mouse(640, 400)
desktop.left_click()
desktop.double_click()
desktop.drag((100, 100), (300, 300))
desktop.scroll("down", 5)
# Keyboard: write for text, press for a single key or a combination
desktop.launch("xfce4-terminal")
desktop.wait(3000)
desktop.write("echo hello")
desktop.press("enter")
desktop.press(["ctrl", "c"])
# Screenshot (PNG bytes, feed to a vision model)
image = desktop.screenshot()
with open("desktop.png", "wb") as f:
f.write(image)
finally:
desktop.kill()5.2 VNC live stream (manual takeover)
# Replaces the link_url observation in Wuying desktop scenarios; the E2B SDK stream has built-in auth
desktop.stream.start()
stream_url = desktop.stream.get_url()
# Hand stream_url to your controlled backend or browser noVNC
desktop.stream.stop()6. WebSDK integration
The browser connects to the sandbox VNC live stream directly through noVNC (CDN JS), with no control-plane SDK or npm build required. The sandbox lifecycle is managed by the backend (Python); the frontend only renders the desktop.
6.1 Backend: get the VNC stream URL
# Backend Python: create the sandbox and get the noVNC stream URL
from e2b_desktop import Sandbox
desktop = Sandbox.create(template="my-desktop-template", timeout=600)
desktop.stream.start()
stream_url = desktop.stream.get_url()
# If the FC E2B data plane requires a traffic access token, pass it as a query parameter
# token = getattr(desktop, "traffic_access_token", None)
# if token:
# stream_url += f"&traffic_access_token={token}"
# Return stream_url to the frontend (for example, in an API response or over WebSocket)6.2 Frontend: HTML + CDN noVNC
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>FC Desktop</title>
<style>
#vnc-screen { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="vnc-screen"></div>
<script type="module">
// ESM import of the official noVNC build (pinned version)
import RFB from 'https://cdn.jsdelivr.net/gh/novnc/noVNC@v1.6.0/core/rfb.js';
async function initVNC() {
// Get stream_url from the backend API
const resp = await fetch('/api/sandbox/vnc-url');
const { streamUrl } = await resp.json();
const rfb = new RFB(
document.getElementById('vnc-screen'),
streamUrl, // https://{sandbox-host}/vnc.html?autoconnect=true&resize=scale
// One URL, two roles: opening it in a browser serves the noVNC page;
// RFB upgrades it to wss://{sandbox-host}/vnc.html as the WebSocket
// endpoint (proxied by websockify to x11vnc)
{ credentials: { password: '' } } // empty when stream.start() has auth disabled
);
rfb.scaleViewport = true;
rfb.resizeSession = false;
}
initVNC();
</script>
</body>
</html>The browser connects directly to
wss://{sandbox-host}/vnc.htmlwith no custom request headers (verified: GET 200, RFB handshake succeeds). VNC auth is controlled bystream.start(require_auth=True)+get_url(auth_key=...); when enabled, returnauth_keyto the frontend together with the URL and pass it in ascredentials.password.If the data plane requires a traffic access token, the FC backend supports passing it as a query parameter (appended to
stream_urlby the backend, see §6.1). The browser WebSocket needs no custom headers and no relay service; noVNC is transparent to extra query parameters (verified with thepassword=parameter).No custom Web SDK is needed: noVNC (CDN ESM) is the complete data-plane client, with all connection parameters carried in the URL.
Context storage
FC Agent Sandbox provides storage as composable capabilities; use AgentFS or OSS for persistence and sharing: