feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164
Merged
Conversation
7941a36 to
433f3d1
Compare
Install ChromeDriver matching the Chromium version in both headful and headless images, managed via supervisord on internal port 9225. A new ChromeDriver proxy (exposed on port 9224) intercepts session creation to inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to the already-running browser, rewrites webSocketUrl in responses to route BiDi traffic back through the proxy, and transparently proxies all other HTTP and WebSocket traffic. Extracts the shared WebSocket bidirectional pump logic from devtoolsproxy into a reusable wsproxy package with a MessageTransform hook, used by both the existing DevTools proxy and the new ChromeDriver proxy. Expands CDP discovery endpoint proxying to include /json/list and makes proxy ports configurable via environment variables. Includes comprehensive unit tests for the ChromeDriver proxy and manual BiDi validation scripts (raw WebSocket, Puppeteer, Selenium). Co-authored-by: Cursor <cursoragent@cursor.com>
Make the chromedriver proxy runtime-configurable and more robust, then add explicit Vibium BiDi e2e coverage using dynamic session endpoints so remote WebDriver workflows are easier and less brittle. Made-with: Cursor
Prevent parallel e2e tests from racing pnpm setup by making playwright dependency installation single-flight, avoiding intermittent ENOENT failures in CI. Made-with: Cursor
6b38b7c to
d428c05
Compare
Serialize BiDi fixture dependency installation to avoid parallel npm races in e2e and recurse through nested Chrome JSON objects so URL rewriting is applied consistently. Made-with: Cursor
Update the Vibium fixture to use the current start/stop API and exercise the ws://.../session path directly, so the e2e suite matches Vibium's documented remote-browser contract while keeping HTTP /session coverage for WebDriver clients. Made-with: Cursor
Drop the newline-only difference in display.go so the PR no longer carries an unrelated formatting change. Made-with: Cursor
Rename the ChromeDriver proxy setting to DevToolsProxyAddr so it reflects the CDP proxy endpoint it targets, and update the config surface to use DEVTOOLS_PROXY_ADDR consistently. Made-with: Cursor
Restore the checked-in server/api file so this PR stops carrying an unrelated binary deletion, and ignore local reference checkouts plus the server/api build artifact path. Made-with: Cursor
hiroTamada
approved these changes
Mar 9, 2026
hiroTamada
left a comment
Contributor
There was a problem hiding this comment.
Clean, well-structured addition of ChromeDriver/BiDi support. The proxy design is minimal — only intercepts session creation and WebSocket URL rewriting. Good extraction of the shared wsproxy package, and solid test coverage across unit, integration, and real e2e with Puppeteer/Selenium/Vibium. Left two minor nits on the wrapper scripts.
Cancel upstream Chrome JSON fetches with the caller context and standardize headful/headless readiness checks so timeout warnings are visible and consistent across wrappers. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
AbdulRashidReshamwala
pushed a commit
to reclaimprotocol/popcorn-images
that referenced
this pull request
Mar 26, 2026
…ernel#164) Install ChromeDriver matching the Chromium version in both headful and headless images, managed via supervisord on internal port 9225. A new ChromeDriver proxy (exposed on port 9224) intercepts session creation to inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to the already-running browser (at port 9222), rewrites webSocketUrl in responses to route BiDi traffic back through the proxy, and transparently proxies all other HTTP and WebSocket traffic. Extracts the shared WebSocket bidirectional pump logic from devtoolsproxy into a reusable wsproxy package with a MessageTransform hook, used by both the existing DevTools proxy and the new ChromeDriver proxy. Expands CDP discovery endpoint proxying to include /json/list and makes proxy ports configurable via environment variables. Includes comprehensive unit tests for the ChromeDriver proxy and manual BiDi validation scripts (raw WebSocket, Puppeteer, Selenium). - [ ] A link to a related issue in our repository - [ ] A description of the changes proposed in the pull request. - [ ] @mentions of the person or team responsible for reviewing proposed changes. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Introduces a new network-facing ChromeDriver proxy with request/response rewriting and adds new ports/services in the runtime images, which could affect client connectivity and session creation behavior if misconfigured. > > **Overview** > **Adds ChromeDriver support end-to-end** by installing a matching `chromedriver` into both headful/headless images, running it under supervisord on `127.0.0.1:9225`, and exposing a new external proxy port `9224` (docker + unikernel scripts updated accordingly). > > **Extends the Go API container** with a new ChromeDriver proxy server that intercepts `POST /session` and BiDi `session.new` to inject `goog:chromeOptions.debuggerAddress` (pointing at the DevTools proxy) and rewrites returned `capabilities.webSocketUrl` so BiDi traffic routes back through the proxy; proxy/DevTools ports and upstream addresses are now configurable via env. > > **Refactors WebSocket proxying** by extracting shared bidirectional pump logic into `lib/wsproxy` (with an optional message transform hook), migrating the existing DevTools proxy to use it, and improving DevTools discovery proxying to handle `/json/version` and `/json(/list)` with recursive URL rewriting. > > **Adds test coverage and tooling**: unit tests for URL rewriting + ChromeDriver proxy behavior, new BiDi e2e tests (raw WS + Puppeteer/Selenium/Vibium scripts with npm deps), and minor `.gitignore` updates. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit fd8e1b9. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Rafael Garcia <raf@kernel.sh>
IlyaasK
added a commit
that referenced
this pull request
Jun 26, 2026
## Summary This is the public-repo equivalent of the private dead-payload cleanup PR, with the additional public-only tracked `server/api` binary removed. What changed: - Deletes `images/chromium-headful/image-chromium/`, including old demo HTML, Streamlit config, static-content files, tint2 config, and legacy startup scripts. - Removes the broad `COPY images/chromium-headful/image-chromium/ /` from the public headful Dockerfile. - Deletes the tracked `server/api` binary artifact. - Adds `server/api` to `.dockerignore` so local rebuilt API binaries do not get sent in Docker build context. - Leaves the current supervised headful runtime path intact: the image still copies Neko config, supervisor service definitions, WebRTC client output, Envoy config, cert bootstrap, API binary from the builder stage, Chromium launcher, wrapper, Playwright daemon bundle, and extensions via current explicit Dockerfile steps. ## Why The removed `image-chromium` directory was an old payload copied directly into `/` during the headful image build. The current image no longer uses that legacy startup/demo path, and the broad root copy makes it easy for unrelated files under `image-chromium` to silently land in the final runtime image. The tracked `server/api` binary is a local build artifact. The Dockerfile compiles/copies the runtime API binary from the build stages; it does not need a checked-in executable under `server/api`. Keeping it tracked increases repository size and can invalidate Docker build context/layers when the binary changes. ## Git History / Removal Rationale | removed item | likely reason it was added | why it is removed here | | --- | --- | --- | | `entrypoint.sh`, `start_all.sh`, `xvfb_startup.sh`, `mutter_startup.sh`, `tint2_startup.sh` | Added with the WebRTC OSS launch (`5c71470`, PR #13) from the old headful demo image. These scripts manually started Xvfb, tint2, mutter, x11vnc/noVNC, then launched a demo server. | The current headful image uses supervisor service definitions, wrapper scripts, Neko/WebRTC components, and explicit Dockerfile copies. These legacy scripts are not the active process model. | | `http_server.py`, `index.html`, `static_content/index.html`, `.streamlit/config.toml` | Added with the same demo payload. The old entrypoint started a Python static server and Streamlit demo app and printed local demo instructions. | The current public runtime does not launch that demo stack. Keeping this content in `/` only preserves stale files that are not part of the current browser/session path. | | `.config/tint2/*` | Added to support the old desktop panel configuration used by the demo/Xvfb/tint2 startup scripts. | Current headful runtime behavior is managed by the current image service/config path; this tint2 payload is only reachable through the removed legacy startup scripts. | | `COPY images/chromium-headful/image-chromium/ /` | Added during the save/reuse user-data and supervisor transition. It preserved the old root payload while the image moved toward explicit supervised services. | The broad copy is now the risky part: any file under the legacy directory silently mutates the final root filesystem. Current-purpose files are already copied explicitly elsewhere in the Dockerfile. | | `server/api` | Added as a 14 MB executable in `0fba5a0` (PR #148), alongside smooth mouse movement source changes. A later PR (`9816e34`, PR #164) added `server/api` to `.gitignore`, which strongly suggests it was recognized as a local build artifact but was already tracked. | The image build compiles the API from source; the tracked binary is not referenced by Dockerfiles or runtime code. This PR removes it from git and adds `.dockerignore` coverage so local rebuilds do not pollute Docker context. | ## Validation Ran locally after merging current `main` into this branch: - `git diff --check origin/main...HEAD` - `DOCKER_BUILDKIT=1 docker build --check -f images/chromium-headful/Dockerfile .` - `cd server && go test ./e2e -run TestDoesNotExist -count=0` - `~/.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main` Autoreview result: - Clean, no accepted/actionable findings. GitHub Actions on the current-main refresh: - image builds: passed - server test/e2e job: passed - launcher test, scan, and Socket: passed ## Unknowns / Final Gates - The deleted legacy payload did not have current references after removal. Full image CI passed on the current-main refresh. - Public does not have the private CapMonster e2e test file that changed in private PR #200, so this PR does not include that private-only test hardening. - The tracked `server/api` binary is removed from git, but local developers can still build a local `server/api`; `.gitignore` and now `.dockerignore` keep it out of commits and Docker build context. ## Fast Docker Review This PR follows the fast-build guidance by removing dead build context and a broad root copy from the headful image. Against the checklist: - The old `image-chromium` root payload copy is removed, so files cannot silently land in `/` just because they sit under a legacy directory. - The tracked `server/api` binary is removed and added to `.dockerignore`, so local binary rebuilds do not invalidate image build context or layers. - The current runtime files continue to be copied explicitly where the Dockerfile needs them. - No build tools are added to runtime images, and no source copies are moved earlier in the Dockerfile. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Dead files and a broad root COPY are removed; the active supervised headful image path is unchanged and CI validated the Dockerfile build. > > **Overview** > Removes legacy **Computer Use Demo** payload from the public headful image and stops shipping a checked-in API binary. > > The entire `images/chromium-headful/image-chromium/` tree is deleted (Streamlit/static HTML, tint2 panel config, Xvfb/tint2/mutter startup scripts, and related demo entrypoints). The headful **Dockerfile** no longer does `COPY images/chromium-headful/image-chromium/ /`; runtime files continue to be copied explicitly (Neko, supervisor, Envoy, built API from `server-builder`, etc.). > > The tracked **`server/api`** executable is removed from the repo, and **`server/api`** is added to **`.dockerignore`** so local rebuilds do not bloat Docker build context or invalidate layers. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit fb7e962. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Install ChromeDriver matching the Chromium version in both headful and headless images, managed via supervisord on internal port 9225. A new ChromeDriver proxy (exposed on port 9224) intercepts session creation to inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to the already-running browser (at port 9222), rewrites webSocketUrl in responses to route BiDi traffic back through the proxy, and transparently proxies all other HTTP and WebSocket traffic.
Extracts the shared WebSocket bidirectional pump logic from devtoolsproxy into a reusable wsproxy package with a MessageTransform hook, used by both the existing DevTools proxy and the new ChromeDriver proxy. Expands CDP discovery endpoint proxying to include /json/list and makes proxy ports configurable via environment variables.
Includes comprehensive unit tests for the ChromeDriver proxy and manual BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).
Checklist
Note
Medium Risk
Moderate risk because it adds a new externally exposed ChromeDriver proxy/port and rewrites WebDriver/BiDi session payloads, which can break existing automation clients or routing if misconfigured.
Overview
Adds first-class WebDriver/BiDi support via ChromeDriver. The Chromium headful/headless images now install a matching
chromedriver, run it under supervisord on127.0.0.1:9225, and expose a new public port9224for ChromeDriver access.Introduces a Go ChromeDriver proxy that intercepts
POST /session(and BiDisession.new) to injectgoog:chromeOptions.debuggerAddresspointing at the DevTools proxy, rewrites returnedcapabilities.webSocketUrlto route BiDi back through the proxy, and otherwise reverse-proxies ChromeDriver HTTP/WebSocket traffic.Refactors and hardens proxy plumbing. DevTools proxy ports become configurable (
DEVTOOLS_PROXY_PORT,CHROMEDRIVER_PROXY_PORT, etc.), DevTools JSON discovery proxying is generalized to also cover/json/listwith recursive URL rewriting, and the shared WebSocket pump is extracted into a reusablewsproxypackage.Testing/ops updates. Adds unit tests for JSON rewriting and ChromeDriver proxy behavior, new BiDi e2e tests plus Node-based validation scripts, and updates e2e container helpers to map/wait on the new ChromeDriver port.
Written by Cursor Bugbot for commit 62f0fc5. This will update automatically on new commits. Configure here.