From c5fea7d2d51311b8e3e3445ec915c5e4495f04f1 Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:50:24 +0200 Subject: [PATCH 1/3] Disconnect from fire-and-forget background commands Call .disconnect() after background commands.run calls whose output is never read, so the SDK stops holding the event stream open while the process keeps running. Co-Authored-By: Claude Opus 4.8 --- packages/js-sdk/src/sandbox.ts | 19 +++++++++++++------ packages/python-sdk/e2b_desktop/main.py | 15 ++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/packages/js-sdk/src/sandbox.ts b/packages/js-sdk/src/sandbox.ts index 71ef691..9e2a135 100644 --- a/packages/js-sdk/src/sandbox.ts +++ b/packages/js-sdk/src/sandbox.ts @@ -467,9 +467,10 @@ export class Sandbox extends SandboxBase { * @param fileOrUrl - The file or URL to open. */ async open(fileOrUrl: string): Promise { - await this.commands.run(`xdg-open ${fileOrUrl}`, { + const handle = await this.commands.run(`xdg-open ${fileOrUrl}`, { background: true, }) + await handle.disconnect() } /** @@ -511,10 +512,14 @@ export class Sandbox extends SandboxBase { * @param uri - The URI to open in the application. */ async launch(application: string, uri?: string): Promise { - await this.commands.run(`gtk-launch ${application} ${uri ?? ''}`, { - background: true, - timeoutMs: 0, - }) + const handle = await this.commands.run( + `gtk-launch ${application} ${uri ?? ''}`, + { + background: true, + timeoutMs: 0, + } + ) + await handle.disconnect() } protected async _start(display: string, opts?: SandboxOpts): Promise { @@ -523,11 +528,12 @@ export class Sandbox extends SandboxBase { this.stream = new VNCServer(this) const [width, height] = opts?.resolution ?? [1024, 768] - await this.commands.run( + const xvfbHandle = await this.commands.run( `Xvfb ${display} -ac -screen 0 ${width}x${height}x24 ` + `-retro -dpi ${opts?.dpi ?? 96} -nolisten tcp -nolisten unix`, { background: true, timeoutMs: 0 } ) + await xvfbHandle.disconnect() const hasStarted = await this.waitAndVerify( `xdpyinfo -display ${display}`, @@ -559,6 +565,7 @@ export class Sandbox extends SandboxBase { timeoutMs: 0, }) this.lastXfce4Pid = result.pid + await result.disconnect() } } diff --git a/packages/python-sdk/e2b_desktop/main.py b/packages/python-sdk/e2b_desktop/main.py index 2077721..6a36233 100644 --- a/packages/python-sdk/e2b_desktop/main.py +++ b/packages/python-sdk/e2b_desktop/main.py @@ -260,12 +260,13 @@ def create( sbx._display = display width, height = resolution or (1024, 768) - sbx.commands.run( + xvfb_handle = sbx.commands.run( f"Xvfb {display} -ac -screen 0 {width}x{height}x24" f" -retro -dpi {dpi or 96} -nolisten tcp -nolisten unix", background=True, timeout=0, ) + xvfb_handle.disconnect() if not sbx._wait_and_verify( f"xdpyinfo -display {display}", lambda r: r.exit_code == 0 @@ -306,9 +307,11 @@ def _start_xfce4(self): f"ps aux | grep {self._last_xfce4_pid} | grep -v grep | head -n 1" ).stdout.strip() ): - self._last_xfce4_pid = self.commands.run( + xfce4_handle = self.commands.run( "startxfce4", background=True, timeout=0 - ).pid + ) + self._last_xfce4_pid = xfce4_handle.pid + xfce4_handle.disconnect() @property def stream(self) -> _VNCServer: @@ -511,7 +514,8 @@ def open(self, file_or_url: str): :param file_or_url: The file or URL to open. """ - self.commands.run(f"xdg-open {file_or_url}", background=True) + handle = self.commands.run(f"xdg-open {file_or_url}", background=True) + handle.disconnect() def get_current_window_id(self) -> str: """ @@ -539,6 +543,7 @@ def launch(self, application: str, uri: Optional[str] = None): """ Launch an application. """ - self.commands.run( + handle = self.commands.run( f"gtk-launch {application} {uri or ''}", background=True, timeout=0 ) + handle.disconnect() From 7efb449ad260a8b37c4ab1acdf0c5c691dc518bf Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:51:39 +0200 Subject: [PATCH 2/3] Add changeset for background command disconnect Co-Authored-By: Claude Opus 4.8 --- .changeset/disconnect-after-run.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/disconnect-after-run.md diff --git a/.changeset/disconnect-after-run.md b/.changeset/disconnect-after-run.md new file mode 100644 index 0000000..06bb86c --- /dev/null +++ b/.changeset/disconnect-after-run.md @@ -0,0 +1,6 @@ +--- +"@e2b/desktop": patch +"@e2b/desktop-python": patch +--- + +Disconnect from fire-and-forget background commands. After background `commands.run` calls whose output is never read (`xdg-open`, `gtk-launch`, `Xvfb`, `startxfce4`), the SDK now calls `.disconnect()` so it stops holding the command's event stream open while the process keeps running. From 09a29f2aef888586fadd854e542cbfc3a044085b Mon Sep 17 00:00:00 2001 From: Mish Ushakov <10400064+mishushakov@users.noreply.github.com> Date: Sat, 6 Jun 2026 13:57:43 +0200 Subject: [PATCH 3/3] Format: collapse startxfce4 commands.run onto one line Co-Authored-By: Claude Opus 4.8 --- packages/python-sdk/e2b_desktop/main.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/python-sdk/e2b_desktop/main.py b/packages/python-sdk/e2b_desktop/main.py index 6a36233..1cac91c 100644 --- a/packages/python-sdk/e2b_desktop/main.py +++ b/packages/python-sdk/e2b_desktop/main.py @@ -307,9 +307,7 @@ def _start_xfce4(self): f"ps aux | grep {self._last_xfce4_pid} | grep -v grep | head -n 1" ).stdout.strip() ): - xfce4_handle = self.commands.run( - "startxfce4", background=True, timeout=0 - ) + xfce4_handle = self.commands.run("startxfce4", background=True, timeout=0) self._last_xfce4_pid = xfce4_handle.pid xfce4_handle.disconnect()