UNPKG

@bigppwong/desktop

Version:

E2B Desktop Sandbox - isolated cloud environment with a desktop-like interface powered by E2B. Ready for AI Computer Use

1 lines 31.7 kB
{"version":3,"sources":["../src/index.ts","../src/sandbox.ts","../src/utils.ts"],"sourcesContent":["export * from '@bigppwong/e2b'\n\nexport { Sandbox } from './sandbox'","import {\n Sandbox as SandboxBase,\n SandboxOpts as SandboxOptsBase,\n CommandHandle,\n CommandResult,\n CommandExitError,\n ConnectionConfig,\n TimeoutError,\n} from '@bigppwong/e2b'\n\nimport { generateRandomString } from './utils'\n\ninterface CursorPosition {\n x: number\n y: number\n}\n\ninterface ScreenSize {\n width: number\n height: number\n}\n\nconst MOUSE_BUTTONS = {\n left: 1,\n right: 3,\n middle: 2,\n}\n\nconst KEYS = {\n alt: 'Alt_L',\n alt_left: 'Alt_L',\n alt_right: 'Alt_R',\n backspace: 'BackSpace',\n break: 'Pause',\n caps_lock: 'Caps_Lock',\n cmd: 'Super_L',\n command: 'Super_L',\n control: 'Control_L',\n control_left: 'Control_L',\n control_right: 'Control_R',\n ctrl: 'Control_L',\n del: 'Delete',\n delete: 'Delete',\n down: 'Down',\n end: 'End',\n enter: 'Return',\n esc: 'Escape',\n escape: 'Escape',\n f1: 'F1',\n f2: 'F2',\n f3: 'F3',\n f4: 'F4',\n f5: 'F5',\n f6: 'F6',\n f7: 'F7',\n f8: 'F8',\n f9: 'F9',\n f10: 'F10',\n f11: 'F11',\n f12: 'F12',\n home: 'Home',\n insert: 'Insert',\n left: 'Left',\n menu: 'Menu',\n meta: 'Meta_L',\n num_lock: 'Num_Lock',\n page_down: 'Page_Down',\n page_up: 'Page_Up',\n pause: 'Pause',\n print: 'Print',\n right: 'Right',\n scroll_lock: 'Scroll_Lock',\n shift: 'Shift_L',\n shift_left: 'Shift_L',\n shift_right: 'Shift_R',\n space: 'space',\n super: 'Super_L',\n super_left: 'Super_L',\n super_right: 'Super_R',\n tab: 'Tab',\n up: 'Up',\n win: 'Super_L',\n windows: 'Super_L',\n}\n\nfunction mapKey(key: string): string {\n const lowerKey = key.toLowerCase()\n if (lowerKey in KEYS) {\n return KEYS[lowerKey as keyof typeof KEYS]\n }\n return lowerKey\n}\n\n/**\n * Configuration options for the Sandbox environment.\n * @interface SandboxOpts\n * @extends {SandboxOptsBase}\n */\nexport interface SandboxOpts extends SandboxOptsBase {\n /**\n * The screen resolution in pixels, specified as [width, height].\n * @type {[number, number]}\n */\n resolution?: [number, number]\n\n /**\n * Dots per inch (DPI) setting for the display.\n * @type {number}\n */\n dpi?: number\n\n /**\n * Display identifier.\n * @type {string}\n */\n display?: string\n}\n\nexport class Sandbox extends SandboxBase {\n protected static override readonly defaultTemplate: string = 'desktop'\n private lastXfce4Pid: number | null = null\n readonly display: string\n readonly stream: VNCServer\n\n /**\n * Use {@link Sandbox.create} to create a new Sandbox instead.\n *\n * @hidden\n * @hide\n * @internal\n * @access protected\n */\n constructor(\n opts: Omit<SandboxOpts, 'timeoutMs' | 'envs' | 'metadata'> & {\n sandboxId: string\n envdVersion?: string\n }\n ) {\n super(opts)\n this.display = opts.display || ':0'\n this.lastXfce4Pid = null\n this.stream = new VNCServer(this)\n }\n /**\n * Create a new sandbox from the default `desktop` sandbox template.\n *\n * @param opts connection options.\n *\n * @returns sandbox instance for the new sandbox.\n *\n * @example\n * ```ts\n * const sandbox = await Sandbox.create()\n * ```\n * @constructs Sandbox\n */\n static async create<S extends typeof Sandbox>(\n this: S,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>>\n /**\n * Create a new sandbox from the specified sandbox template.\n *\n * @param template sandbox template name or ID.\n * @param opts connection options.\n *\n * @returns sandbox instance for the new sandbox.\n *\n * @example\n * ```ts\n * const sandbox = await Sandbox.create('<template-name-or-id>')\n * ```\n * @constructs Sandbox\n */\n static async create<S extends typeof Sandbox>(\n this: S,\n template: string,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>>\n static async create<S extends typeof Sandbox>(\n this: S,\n templateOrOpts?: SandboxOpts | string,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>> {\n const { template, sandboxOpts } =\n typeof templateOrOpts === 'string'\n ? { template: templateOrOpts, sandboxOpts: opts }\n : { template: this.defaultTemplate, sandboxOpts: templateOrOpts }\n\n const config = new ConnectionConfig(sandboxOpts)\n\n let sbx\n if (config.debug) {\n sbx = new this({\n sandboxId: 'desktop',\n ...sandboxOpts,\n ...config,\n }) as InstanceType<S>\n } else {\n const sandbox = await this.createSandbox(\n template,\n sandboxOpts?.timeoutMs ?? this.defaultSandboxTimeoutMs,\n sandboxOpts\n )\n sbx = new this({\n ...sandbox,\n ...sandboxOpts,\n ...config,\n }) as InstanceType<S>\n }\n\n const [width, height] = sandboxOpts?.resolution ?? [1024, 768]\n await sbx.commands.run(\n `Xvfb ${sbx.display} -ac -screen 0 ${width}x${height}x24 ` +\n `-retro -dpi ${sandboxOpts?.dpi ?? 96} -nolisten tcp -nolisten unix`,\n { background: true, timeoutMs: 0 }\n )\n\n let hasStarted = await sbx.waitAndVerify(\n `xdpyinfo -display ${sbx.display}`,\n (r: CommandResult) => r.exitCode === 0\n )\n if (!hasStarted) {\n throw new TimeoutError('Could not start Xvfb')\n }\n\n await sbx.startXfce4()\n\n return sbx\n }\n\n /**\n * Wait for a command to return a specific result.\n * @param cmd - The command to run.\n * @param onResult - The function to check the result of the command.\n * @param timeout - The maximum time to wait for the command to return the result.\n * @param interval - The interval to wait between checks.\n * @returns `true` if the command returned the result within the timeout, otherwise `false`.\n */\n async waitAndVerify(\n cmd: string,\n onResult: (result: CommandResult) => boolean,\n timeout: number = 10,\n interval: number = 0.5\n ): Promise<boolean> {\n let elapsed = 0\n\n while (elapsed < timeout) {\n try {\n if (onResult(await this.commands.run(cmd))) {\n return true\n }\n } catch (e) {\n if (e instanceof CommandExitError) {\n continue\n }\n throw e\n }\n\n await new Promise((resolve) => setTimeout(resolve, interval * 1000))\n elapsed += interval\n }\n\n return false\n }\n\n /**\n * Start xfce4 session if logged out or not running.\n */\n private async startXfce4(): Promise<void> {\n if (\n this.lastXfce4Pid === null ||\n (\n await this.commands.run(\n `ps aux | grep ${this.lastXfce4Pid} | grep -v grep | head -n 1`\n )\n ).stdout\n .trim()\n .includes('[xfce4-session] <defunct>')\n ) {\n const result = await this.commands.run('startxfce4', {\n envs: { DISPLAY: this.display },\n background: true,\n timeoutMs: 0,\n })\n this.lastXfce4Pid = result.pid\n }\n }\n\n /**\n * Take a screenshot and save it to the given name.\n * @param format - The format of the screenshot.\n * @returns A Uint8Array bytes representation of the screenshot.\n */\n async screenshot(): Promise<Uint8Array>\n /**\n * Take a screenshot and save it to the given name.\n * @param format - The format of the screenshot.\n * @returns A Uint8Array bytes representation of the screenshot.\n */\n async screenshot(format: 'bytes'): Promise<Uint8Array>\n /**\n * Take a screenshot and save it to the given name.\n * @returns A Blob representation of the screenshot.\n */\n async screenshot(format: 'blob'): Promise<Blob>\n /**\n * Take a screenshot and save it to the given name.\n * @returns A ReadableStream of bytes representation of the screenshot.\n */\n async screenshot(format: 'stream'): Promise<ReadableStream<Uint8Array>>\n async screenshot(format: 'bytes' | 'blob' | 'stream' = 'bytes') {\n const path = `/tmp/screenshot-${generateRandomString()}.png`\n await this.commands.run(`scrot --pointer ${path}`, {\n envs: { DISPLAY: this.display },\n })\n\n // @ts-expect-error\n const file = await this.files.read(path, { format })\n this.files.remove(path)\n return file\n }\n\n /**\n * Left click on the mouse position.\n */\n async leftClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 1', {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Double left click on the mouse position.\n */\n async doubleClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click --repeat 2 1', {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Right click on the mouse position.\n */\n async rightClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 3', {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Middle click on the mouse position.\n */\n async middleClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 2', {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Scroll the mouse wheel by the given amount.\n * @param direction - The direction to scroll. Can be \"up\" or \"down\".\n * @param amount - The amount to scroll.\n */\n async scroll(\n direction: 'up' | 'down' = 'down',\n amount: number = 1\n ): Promise<void> {\n const button = direction === 'up' ? '4' : '5'\n await this.commands.run(`xdotool click --repeat ${amount} ${button}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Move the mouse to the given coordinates.\n * @param x - The x coordinate.\n * @param y - The y coordinate.\n */\n async moveMouse(x: number, y: number): Promise<void> {\n await this.commands.run(`xdotool mousemove --sync ${x} ${y}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Press the mouse button.\n */\n async mousePress(\n button: 'left' | 'right' | 'middle' = 'left'\n ): Promise<void> {\n await this.commands.run(`xdotool mousedown ${MOUSE_BUTTONS[button]}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Release the mouse button.\n */\n async mouseRelease(\n button: 'left' | 'right' | 'middle' = 'left'\n ): Promise<void> {\n await this.commands.run(`xdotool mouseup ${MOUSE_BUTTONS[button]}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Get the current cursor position.\n * @returns A object with the x and y coordinates\n * @throws Error if cursor position cannot be determined\n */\n async getCursorPosition(): Promise<CursorPosition> {\n const result = await this.commands.run('xdotool getmouselocation', {\n envs: { DISPLAY: this.display },\n })\n\n const match = result.stdout.match(/x:(\\d+)\\s+y:(\\d+)/)\n if (!match) {\n throw new Error(\n `Failed to parse cursor position from output: ${result.stdout}`\n )\n }\n\n const [, x, y] = match\n if (!x || !y) {\n throw new Error(`Invalid cursor position values: x=${x}, y=${y}`)\n }\n\n return { x: parseInt(x), y: parseInt(y) }\n }\n\n /**\n * Get the current screen size.\n * @returns An {@link ScreenSize} object\n * @throws Error if screen size cannot be determined\n */\n async getScreenSize(): Promise<ScreenSize> {\n const result = await this.commands.run('xrandr', {\n envs: { DISPLAY: this.display },\n })\n\n const match = result.stdout.match(/(\\d+x\\d+)/)\n if (!match) {\n throw new Error(\n `Failed to parse screen size from output: ${result.stdout}`\n )\n }\n\n try {\n const [width, height] = match[1].split('x').map((val) => parseInt(val))\n return { width, height }\n } catch (error) {\n throw new Error(`Invalid screen size format: ${match[1]}`)\n }\n }\n\n private *breakIntoChunks(text: string, n: number): Generator<string> {\n for (let i = 0; i < text.length; i += n) {\n yield text.slice(i, i + n)\n }\n }\n\n private quoteString(s: string): string {\n if (!s) {\n return \"''\"\n }\n\n if (!/[^\\w@%+=:,./-]/.test(s)) {\n return s\n }\n\n // use single quotes, and put single quotes into double quotes\n // the string $'b is then quoted as '$'\"'\"'b'\n return \"'\" + s.replace(/'/g, \"'\\\"'\\\"'\") + \"'\"\n }\n\n /**\n * Write the given text at the current cursor position.\n * @param text - The text to write.\n * @param options - An object containing the chunk size and delay between each chunk of text.\n * @param options.chunkSize - The size of each chunk of text to write. Default is 25 characters.\n * @param options.delayInMs - The delay between each chunk of text. Default is 75 ms.\n */\n async write(\n text: string,\n options: { chunkSize: number; delayInMs: number } = {\n chunkSize: 25,\n delayInMs: 75,\n }\n ): Promise<void> {\n const chunks = this.breakIntoChunks(text, options.chunkSize)\n\n for (const chunk of chunks) {\n await this.commands.run(\n `xdotool type --delay ${options.delayInMs} ${this.quoteString(chunk)}`,\n { envs: { DISPLAY: this.display } }\n )\n }\n }\n\n /**\n * Press a key.\n * @param key - The key to press (e.g. \"enter\", \"space\", \"backspace\", etc.). Can be a single key or an array of keys.\n */\n async press(key: string | string[]): Promise<void> {\n if (Array.isArray(key)) {\n key = key.map(mapKey).join('+')\n } else {\n key = mapKey(key)\n }\n\n await this.commands.run(`xdotool key ${key}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Drag the mouse from the given position to the given position.\n * @param from - The starting position.\n * @param to - The ending position.\n */\n async drag(\n [x1, y1]: [number, number],\n [x2, y2]: [number, number]\n ): Promise<void> {\n await this.moveMouse(x1, y1)\n await this.mousePress()\n await this.moveMouse(x2, y2)\n await this.mouseRelease()\n }\n\n /**\n * Wait for the given amount of time.\n * @param ms - The amount of time to wait in milliseconds.\n */\n async wait(ms: number): Promise<void> {\n await this.commands.run(`sleep ${ms / 1000}`, {\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Open a file or a URL in the default application.\n * @param fileOrUrl - The file or URL to open.\n */\n async open(fileOrUrl: string): Promise<void> {\n await this.commands.run(`xdg-open ${fileOrUrl}`, {\n background: true,\n envs: { DISPLAY: this.display },\n })\n }\n\n /**\n * Get the current window ID.\n * @returns The ID of the current window.\n */\n async getCurrentWindowId(): Promise<string> {\n const result = await this.commands.run('xdotool getwindowfocus', {\n envs: { DISPLAY: this.display },\n })\n return result.stdout.trim()\n }\n\n /**\n * Get the window ID of the window with the given title.\n * @param title - The title of the window.\n * @returns The ID of the window.\n */\n async getApplicationWindows(application: string): Promise<string[]> {\n const result = await this.commands.run(\n `xdotool search --onlyvisible --class ${application}`,\n {\n envs: { DISPLAY: this.display },\n }\n )\n\n return result.stdout.trim().split('\\n')\n }\n\n /**\n * Get the title of the window with the given ID.\n * @param windowId - The ID of the window.\n * @returns The title of the window.\n */\n async getWindowTitle(windowId: string): Promise<string> {\n const result = await this.commands.run(\n `xdotool getwindowname ${windowId}`,\n {\n envs: { DISPLAY: this.display },\n }\n )\n\n return result.stdout.trim()\n }\n}\n\ninterface VNCServerOptions {\n vncPort?: number\n port?: number\n requireAuth?: boolean\n windowId?: string\n}\n\ninterface UrlOptions {\n autoConnect?: boolean\n viewOnly?: boolean\n resize?: 'off' | 'scale' | 'remote'\n authKey?: string\n}\n\n// Modified VNCServer class\nclass VNCServer {\n private vncPort: number = 5900\n private port: number = 6080\n private novncAuthEnabled: boolean = false\n private url: URL | null = null\n private novncHandle: CommandHandle | null = null\n private password: string | undefined\n private readonly novncCommand: string\n private readonly desktop: Sandbox\n\n constructor(desktop: Sandbox) {\n this.desktop = desktop\n this.novncCommand =\n `cd /opt/noVNC/utils && ./novnc_proxy --vnc localhost:${this.vncPort} ` +\n `--listen ${this.port} --web /opt/noVNC > /tmp/novnc.log 2>&1`\n }\n\n public getAuthKey(): string {\n if (!this.password) {\n throw new Error(\n 'Unable to retrieve stream auth key, check if requireAuth is enabled'\n )\n }\n\n return this.password\n }\n\n /**\n * Set the VNC command to start the VNC server.\n */\n private async getVNCCommand(windowId?: string): Promise<string> {\n let pwdFlag = '-nopw'\n if (this.novncAuthEnabled) {\n await this.desktop.commands.run('mkdir ~/.vnc')\n await this.desktop.commands.run(\n `x11vnc -storepasswd ${this.password} ~/.vnc/passwd`\n )\n pwdFlag = '-usepw'\n }\n\n return (\n `x11vnc -bg -display ${this.desktop.display} -forever -wait 50 -shared ` +\n `-rfbport ${this.vncPort} ${pwdFlag} 2>/tmp/x11vnc_stderr.log` +\n (windowId ? ` -id ${windowId}` : '')\n )\n }\n\n private async waitForPort(port: number): Promise<boolean> {\n return await this.desktop.waitAndVerify(\n `netstat -tuln | grep \":${port} \"`,\n (r: CommandResult) => r.stdout.trim() !== ''\n )\n }\n\n /**\n * Check if the VNC server is running.\n * @returns Whether the VNC server is running.\n */\n private async checkVNCRunning(): Promise<boolean> {\n try {\n const result = await this.desktop.commands.run('pgrep -x x11vnc')\n return result.stdout.trim() !== ''\n } catch (error) {\n return false\n }\n }\n\n /**\n * Get the URL to a web page with a stream of the desktop sandbox.\n * @param autoConnect - Whether to automatically connect to the server after opening the URL.\n * @param viewOnly - Whether to prevent user interaction through the client.\n * @param resize - Whether to resize the view when the window resizes.\n * @param authKey - The password to use to connect to the server.\n * @returns The URL to connect to the VNC server.\n */\n public getUrl({\n autoConnect = true,\n viewOnly = false,\n resize = 'scale',\n authKey,\n }: UrlOptions = {}): string {\n if (this.url === null) {\n throw new Error('Server is not running')\n }\n\n let url = new URL(this.url)\n if (autoConnect) {\n url.searchParams.set('autoconnect', 'true')\n }\n if (viewOnly) {\n url.searchParams.set('view_only', 'true')\n }\n if (resize) {\n url.searchParams.set('resize', resize)\n }\n if (authKey) {\n url.searchParams.set('password', authKey)\n }\n return url.toString()\n }\n\n /**\n * Start the VNC server.\n */\n public async start(opts: VNCServerOptions = {}): Promise<void> {\n // If stream is already running, throw an error.\n if (await this.checkVNCRunning()) {\n // throw new Error('Stream is already running')\n console.log('Stream is already running')\n this.url = new URL(`http://${this.desktop.getHost(this.port)}/vnc.html`)\n return\n }\n\n this.vncPort = opts.vncPort ?? this.vncPort\n this.port = opts.port ?? this.port\n this.novncAuthEnabled = opts.requireAuth ?? this.novncAuthEnabled\n this.password = this.novncAuthEnabled ? generateRandomString() : undefined\n this.url = new URL(`https://${this.desktop.getHost(this.port)}/vnc.html`)\n\n const vncCommand = await this.getVNCCommand(opts.windowId)\n await this.desktop.commands.run(vncCommand)\n\n this.novncHandle = await this.desktop.commands.run(this.novncCommand, {\n background: true,\n timeoutMs: 0,\n })\n if (!(await this.waitForPort(this.port))) {\n throw new Error('Could not start noVNC server')\n }\n }\n\n /**\n * Stop the VNC server.\n */\n public async stop(): Promise<void> {\n if (await this.checkVNCRunning()) {\n await this.desktop.commands.run('pkill x11vnc')\n }\n\n if (this.novncHandle) {\n await this.novncHandle.kill()\n this.novncHandle = null\n }\n }\n}\n","import { randomBytes } from 'crypto';\n\nexport function generateRandomString(length: number = 16): string {\n const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n const bytes = randomBytes(length);\n let result = '';\n\n for (let i = 0; i < length; i++) {\n result += characters[bytes[i] % characters.length];\n }\n\n return result;\n}"],"mappings":"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","names":[]}