概述
desktop 是预构建的图形化 Linux 沙箱模板,提供 Xfce 桌面、Xvfb、noVNC、
Google Chrome、Firefox、VS Code、Noto CJK 字体,以及鼠标、键盘和截图所需的
桌面自动化工具。
Desktop SDK 在创建 Sandbox 时启动图形桌面,并提供查看屏幕和执行桌面操作的 API。
典型的 Computer Use 流程是:创建 Sandbox、获取截图、让视觉模型生成下一步动作、 执行鼠标或键盘操作,然后持续循环,直到任务完成。
安装 Desktop SDK
JavaScript/TypeScript:
npm i @e2b/desktop dotenv
npm i -D tsx typescript @types/node
Python:
pip install e2b-desktop python-dotenv
配置 API
从七牛云获取 API Key,并在项目目录中创建 .env:
E2B_API_KEY=<api-key>
E2B_API_URL=https://<RegionID>-sandbox.qiniuapi.com
<RegionID> 是沙箱所在区域的标识符。desktop 模板按区域发布,API Endpoint
必须指向已经发布该模板的区域。API Key 只应在服务端使用,不要暴露给浏览器。
创建 Desktop Sandbox
以下 TypeScript 示例创建一个分辨率为 1024 × 768 的桌面,打开网页并将截图保存到 本地:
import 'dotenv/config'
import { writeFile } from 'node:fs/promises'
import { Sandbox } from '@e2b/desktop'
const desktop = await Sandbox.create('desktop', {
resolution: [1024, 768],
dpi: 96,
timeoutMs: 300_000,
})
try {
await desktop.open('https://www.qiniu.com')
await desktop.wait(3_000)
const screenshot = await desktop.screenshot()
await writeFile('desktop.png', screenshot)
} finally {
await desktop.kill()
}
运行示例:
npx tsx desktop.ts
Python 的调用方式如下:
from pathlib import Path
from dotenv import load_dotenv
from e2b_desktop import Sandbox
load_dotenv()
desktop = Sandbox.create(
template="desktop",
resolution=(1024, 768),
dpi=96,
timeout=300,
)
try:
desktop.open("https://www.qiniu.com")
desktop.wait(3_000)
screenshot = desktop.screenshot()
Path("desktop.png").write_bytes(screenshot)
finally:
desktop.kill()
在浏览器中查看桌面
调用 stream.start() 会启动 x11vnc 和 noVNC。建议开启流服务鉴权,并通过应用自身
的鉴权接口把 noVNC URL 交给前端:
await desktop.stream.start({ requireAuth: true })
const authKey = desktop.stream.getAuthKey()
const streamUrl = desktop.stream.getUrl({ authKey })
// 通过应用自身的鉴权响应把 streamUrl 交给前端,不要写入日志。
noVNC URL 包含桌面访问凭据,只在对应 Sandbox 和流服务存活期间有效。每个 Sandbox 同一时间只能运行一个桌面流;切换为其他窗口流之前,需要先执行:
await desktop.stream.stop()
执行桌面操作
Desktop SDK 提供以下常用操作:
| 操作 | JavaScript/TypeScript API |
|---|---|
| 移动鼠标 | moveMouse(x, y) |
| 单击、双击和右键 | leftClick(x, y)、doubleClick(x, y)、rightClick(x, y) |
| 拖动 | drag([startX, startY], [endX, endY]) |
| 滚动 | scroll('up' \| 'down', amount) |
| 输入文本 | write(text) |
| 按键或组合键 | press('enter')、press(['ctrl', 'l']) |
| 获取截图 | screenshot() |
| 执行终端命令 | commands.run(command) |
例如,可以聚焦浏览器地址栏并打开新页面:
await desktop.press(['ctrl', 'l'])
await desktop.write('https://developer.qiniu.com')
await desktop.press('enter')
await desktop.wait(3_000)
鼠标坐标以创建 Sandbox 时指定的分辨率为基准。把截图交给视觉模型时,应让模型返回 同一坐标系中的位置。
实现 Computer Use 循环
Computer Use Agent 通常重复获取截图并执行模型返回的动作。下面的
getNextActionFromModel() 代表业务侧接入的视觉模型:
const desktop = await Sandbox.create('desktop', {
resolution: [1024, 768],
timeoutMs: 300_000,
})
try {
while (true) {
const screenshot = await desktop.screenshot()
const action = await getNextActionFromModel(screenshot)
if (!action) break
switch (action.type) {
case 'click':
await desktop.leftClick(action.x, action.y)
break
case 'type':
await desktop.write(action.text)
break
case 'keypress':
await desktop.press(action.keys)
break
case 'scroll':
await desktop.scroll(
action.deltaY < 0 ? 'up' : 'down',
Math.abs(action.deltaY),
)
break
case 'drag':
await desktop.drag(
[action.startX, action.startY],
[action.endX, action.endY],
)
break
}
}
} finally {
await desktop.kill()
}
应用需要校验模型返回的动作类型和参数,再将动作交给 Desktop SDK 执行。任务结束后
应始终调用 kill() 释放 Sandbox;仅关闭 noVNC 页面不会终止 Sandbox。
录制桌面操作
desktop 模板内置 FFmpeg,可以通过 x11grab 捕获 Desktop SDK 启动的整个 X11
桌面,并将鼠标、键盘和窗口操作录制成视频。录制直接读取 X11 Display,不依赖
noVNC;需要实时查看桌面时,可以同时启动桌面流。
下面的示例录制 15 秒桌面操作,并将 Sandbox 中生成的 MP4 文件保存到本地:
import 'dotenv/config'
import { writeFile } from 'node:fs/promises'
import { Sandbox } from '@e2b/desktop'
const desktop = await Sandbox.create('desktop', {
resolution: [1024, 768],
timeoutMs: 300_000,
})
const recordingPath = '/tmp/desktop-recording.mp4'
try {
const screen = await desktop.getScreenSize()
const recorder = await desktop.commands.run(
[
'ffmpeg -y -v error',
'-f x11grab',
'-draw_mouse 1',
'-framerate 15',
`-video_size ${screen.width}x${screen.height}`,
`-i ${desktop.display}`,
'-t 15',
'-c:v libx264',
'-preset veryfast',
'-crf 23',
'-pix_fmt yuv420p',
'-movflags +faststart',
recordingPath,
].join(' '),
{ background: true, timeoutMs: 0 },
)
await desktop.open('https://www.qiniu.com')
await desktop.wait(3_000)
await desktop.moveMouse(500, 400)
await desktop.leftClick()
await recorder.wait()
const recording = await desktop.files.read(recordingPath, {
format: 'bytes',
})
await writeFile('desktop-recording.mp4', recording)
} finally {
await desktop.kill()
}
应通过 desktop.display 获取当前 Display,并让 video_size 与
getScreenSize() 返回的分辨率保持一致。draw_mouse 用于把鼠标指针写入画面,
帧率、编码预设和 CRF 可以根据流畅度、CPU 使用量与文件大小要求调整。
如果录制时长由业务流程决定,应在结束时向 FFmpeg 发送 SIGINT 并等待进程退出,
再读取视频文件,确保 MP4 封装完成。较长的视频建议通过
files.read(recordingPath, { format: 'stream' }) 流式下载。视频必须在调用
desktop.kill() 前下载;当前模板默认只录制桌面画面,不包含系统音频。