Skip to content

fix(desktop): honor display and stream options on create() and stream.start() - #2145

Open
PrabalParihar wants to merge 1 commit into
e2b-dev:mainfrom
PrabalParihar:fix/desktop-display-stream-options
Open

PrabalParihar wants to merge 1 commit into
e2b-dev:mainfrom
PrabalParihar:fix/desktop-display-stream-options

Conversation

@PrabalParihar

@PrabalParihar PrabalParihar commented Nov 14, 2026 •

Copy link
Copy Markdown

Three options the desktop SDKs already accept were being dropped: in JS, create({ display: ':1' }) still started on :0 and stream.start() ignored custom ports, then failed to start noVNC; in Python, restarting an authenticated stream saved the password as the literal None. I fixed those and lined each SDK up with the other where it already worked.

Three places where the desktop SDKs drop an option the caller passed. I found them while comparing the JS and Python create() and stream.start() paths.

Problem

JS: Sandbox.create({ display: ':1' }) starts the desktop on :0.
create() accepts (opts) or (template, opts) and resolves them into sandboxOpts, but it read the display from the raw second argument: const display = opts?.display || ':0'. With the options-only form that argument is undefined. So Xvfb, xdpyinfo, DISPLAY and sandbox.display all end up on :0, while resolution and dpi from the same object are applied. Sandbox.create('desktop', { display: ':1' }) works.

JS: stream.start({ vncPort, port }) doesn't move noVNC.
VNCServer built novncCommand once in its constructor, while vncPort/port still held the defaults (5900/6080). start() updates the ports afterwards, so x11vnc gets -rfbport 5901 and the stream URL points at 6081, but noVNC still runs --vnc localhost:5900 --listen 6080. The readiness check then waits on :6081, and start() throws Could not start noVNC server. The Python SDK builds this command inside start(), so it isn't affected.

Python: restarting the stream with auth enabled stores the password None.
_VNCServer.start() keeps _novnc_auth_enabled on across restarts (require_auth or self._novnc_auth_enabled), but it only made a password when require_auth=True was passed again. So stream.start(require_auth=True), then stream.stop(), then stream.start() runs x11vnc -storepasswd None ~/.vnc/passwd with -usepw. The stream's password becomes the literal string None, and get_auth_key() raises. The JS SDK generates the password whenever auth is enabled.

Change

  • JS create(): read display from the resolved sandboxOpts.
  • JS VNCServer: build the noVNC command in start(), after the ports are set, as the Python SDK does. The unused novncCommand field is removed.
  • Python _VNCServer.start(): generate the password whenever auth is enabled, as the JS SDK does.
  • Changeset: patch for @e2b/desktop and @e2b/desktop-python.

Usage is the same; these calls now do what they say:

const desktop = await Sandbox.create({ display: ':1' }) // desktop.display === ':1'
await desktop.stream.start({ vncPort: 5901, port: 6081 }) // noVNC on 6081 -> localhost:5901
desktop.stream.start(require_auth=True)
desktop.stream.stop()
desktop.stream.start()  # still password-protected, with a fresh key
key = desktop.stream.get_auth_key()

Tests

  • packages/desktop-js/tests/readiness.test.ts: a new test.each covering both create() call forms. It mocks BaseSandbox.create the same way the existing startup-failure test does, then checks that DISPLAY is :1 in the envs and that _start gets :1. The options-only case fails on main and passes here.
  • packages/desktop-js/tests/stream.test.ts (new): it runs _start() with recorded commands.run and a mocked waitAndVerify, then calls stream.start({ vncPort: 5901, port: 6081 }). It checks the x11vnc -rfbport, the noVNC --vnc / --listen ports, the port the readiness check waits on, and the stream URL. A second test checks that the defaults stay 5900/6080. The custom-ports test fails on main (noVNC gets localhost:5900 --listen 6080) and passes here.
  • packages/desktop-python/tests/test_stream.py (new): runs start with auth, then stop, then start again. It checks that a fresh password is stored and returned, with no -storepasswd None. A second test checks that a start without auth stays -nopw. The restart test fails on main (get_auth_key() raises) and passes here.
  • JS: vitest run tests/stream.test.ts tests/readiness.test.ts tests/client.test.ts: 18 passed. pnpm typecheck, pnpm lint (oxlint) and prettier --check src tests are clean.
  • Python: pytest tests/test_stream.py tests/test_readiness.py tests/test_client.py: 15 passed. ruff check, ruff format --check and ty check e2b_desktop are clean.
  • I didn't run the live-sandbox suites (apps, mouse, screen, test_controls) because they need an E2B_API_KEY.

This change was made with AI assistance; the tests listed above were run locally before opening this PR.


Devin Review

….start()

JS SDK:
- Sandbox.create() read `display` from its second argument, so
  Sandbox.create({ display: ':1' }) started Xvfb and set DISPLAY on :0.
  Read it from the resolved sandbox options, which covers both call forms.
- VNCServer built its noVNC command in the constructor with the default
  ports, so stream.start({ vncPort, port }) started x11vnc on the new
  rfbport but noVNC still on 6080 -> localhost:5900, and the readiness
  check on the new port timed out. Build the command in start(), after
  the ports are set (the Python SDK already does this).

Python SDK:
- _VNCServer.start() keeps auth enabled across restarts but only made a
  password when require_auth was passed again, so a second start() ran
  `x11vnc -storepasswd None` with -usepw and get_auth_key() raised.
  Generate the password whenever auth is enabled (as the JS SDK does).
@cla-bot

cla-bot Bot commented Nov 14, 2026

Copy link
Copy Markdown

We require contributors to sign our Contributor License Agreement, and we don't have @PrabalParihar on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check'

@changeset-bot

changeset-bot Bot commented Nov 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9208692

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@e2b/desktop Patch
@e2b/desktop-python Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-14T08:21:51.427811Z 9208692 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TASTE.md review: checked parity (T-1, T-1d, T-2), API shape and options (T-3, T-3a, T-10, T-20, T-22, T-23), defaults and config (T-47, T-50), shell composition (T-43), and errors (T-59, T-62, T-63) against the changed lines. No violations found: the fixes bring JS create()/stream.start() and Python _VNCServer.start() into line with each other without adding to the public surface.

Written by Devin

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Devin Review: 1 flag

Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)

Devin Review

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant