全栈应用服务器

  • 全栈应用服务器 > 使用指南 > 沙箱服务概述 > Desktop 桌面自动化

    Desktop 桌面自动化

    最近更新时间: 2026-09-11 17:43:36

    概述

    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_sizegetScreenSize() 返回的分辨率保持一致。draw_mouse 用于把鼠标指针写入画面, 帧率、编码预设和 CRF 可以根据流畅度、CPU 使用量与文件大小要求调整。

    如果录制时长由业务流程决定,应在结束时向 FFmpeg 发送 SIGINT 并等待进程退出, 再读取视频文件,确保 MP4 封装完成。较长的视频建议通过 files.read(recordingPath, { format: 'stream' }) 流式下载。视频必须在调用 desktop.kill() 前下载;当前模板默认只录制桌面画面,不包含系统音频。

    以上内容是否对您有帮助?