Skip to content

About

Unofficial Codex Browser Use compatibility layer for Linux Chromium remote hosts.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

28 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codex-browser-use-linux-chromium

Unofficial compatibility layer for using Codex Browser Use / Chrome skill with Chromium on Linux remote hosts.

This project does not redistribute the official Codex desktop app, official Chrome extension, or official Browser Use plugin. It only installs Linux-side compatibility files and small local patches so a Linux Chromium host can satisfy the runtime shape expected by the official Codex Chrome/Browser Use skill.

Chromium Browser Use sample

What It Does

  • Installs a Linux native-messaging bridge for the official Codex Chrome extension.
  • Installs a minimal node_repl MCP server exposing js, js_reset, and browser_cleanup.
  • Exposes nodeRepl and __codexNativePipe so the official browser-client.mjs can connect to Chromium through the native host.
  • Exposes nodeRepl.ensureChromiumExtensionReady(), which opens the patched Chromium profile when a fresh Codex conversation reaches Browser setup before any extension-backed browser session is online. It waits for the extension's native-host socket before the Browser skill retries agent.browsers.get.
  • When several Chromium extension sockets are alive, prefers the foreground default-profile Chromium socket over headless, incognito, or temporary-profile sockets so logged-in browser state wins backend discovery.
  • Launches Linux Chromium with --password-store=basic so cookie-store startup does not block HTTP/HTTPS page navigation behind a desktop keyring backend.
  • Provides nodeRepl.import() and __dynamicImport() for relative imports resolved from the REPL cwd.
  • Saves emitted browser images to absolute files under the current workspace and prints a Markdown image link, so Codex Desktop can render screenshot deliverables as visible file artifacts.
  • Accepts the official Node REPL js call shape, including optional title and per-call timeout_ms/timeoutMs fields.
  • Adds Browser client CDP call timeouts around page-level operations such as screenshot capture and DOM snapshots, so Chromium-side hangs return a recoverable error instead of stalling the MCP call until the outer tool transport closes.
  • Revalidates Browser Use's virtual clipboard in the current document before paste-backed text input, so repeated navigation in the same tab does not leave locator.type() / paste-style input with a stale clipboard install.
  • On Linux Chromium, can patch the official extension with a non-CDP chrome.tabs.captureVisibleTab endpoint. Normal visible-viewport screenshots use that endpoint first, bypassing the Page.captureScreenshot debugger path that can time out or detach on complex pages. Full-page and cropped screenshots still use CDP.
  • Bumps the unpacked Chromium extension manifest version when applying that patch, so Chromium re-registers the service worker instead of continuing to run stale cached extension code.
  • Keeps a faster CDP visible-viewport fallback that lets Chromium capture the current viewport directly instead of first computing a layout clip rectangle. Screenshot capture remains a supported first-class operation.
  • Cleans up native browser sockets after a js timeout, so a timed-out browser command is less likely to poison follow-up tool calls in the same MCP process.
  • Exposes a dedicated browser_cleanup MCP tool that calls the official Browser Use session finalizer with keep: [], so agents can close task tabs without running arbitrary cleanup JavaScript. js_reset and normal MCP process shutdown also best-effort run the same cleanup. Long-lived MCP processes also run an idle cleanup timer so forgotten task tabs do not sit in Chromium indefinitely.
  • Restarts the native host bridge when a client disconnects with an in-flight browser command, which clears orphaned screenshot/DOM commands that can later surface as Detached while handling command.
  • Adds skill guidance so agents stop retrying the same page-level browser operation after repeated tab.playwright / tab.cua / input timeouts.
  • Gives each MCP process a unique Browser Use session_id and stable process-scoped turn_id, matching the isolation the official desktop runtime normally provides for browser-session state.
  • Patches locally cached Codex Browser Use / Chrome plugin scripts so they recognize Chromium on Linux.
  • Patches plugin-local mcpServers metadata for both Chrome and Browser Use, so Codex CLI 0.130+ plugin caches expose the selected plugin version's MCP entry instead of depending on a global or stale MCP entry. Chrome keeps the official node_repl name; Browser Use is renamed to browser_node_repl to avoid Codex's duplicate plugin MCP server-name de-duplication.
  • Layers Codex CLI 0.131+ diagnostics into this project's doctor: it reads codex doctor --json, codex mcp list, and codex plugin list to verify the effective CLI install, MCP registry, and Browser/Chrome plugin state alongside the Chromium/native-host checks that the upstream doctor does not know about.
  • On Linux, patches the official Browser / browser-use skill to keep the @browser entrypoint but select the Chromium-backed extension backend. Linux remote hosts do not have the Codex Desktop iab browser, and treating the extension as iab is not equivalent for screenshot-heavy flows. The patched bootstrap also asks the local runtime to start Chromium when the extension backend is missing, so a new conversation does not have to remember a manual browser-open step.
  • Optionally installs macOS Desktop remote path shims under /Applications/... on the Linux host. This is needed when Codex Desktop on macOS remotely connects to the Linux host and sends its own node_repl path through RefreshMcpServers.
  • Optionally installs Windows Desktop remote path shims for common Codex and Codex Beta install paths under AppData\\Local\\Programs, plus hashed AppData\\Local\\OpenAI\\Codex\\bin\\... paths discovered in app-server logs. This is needed when Codex Desktop on Windows sends a Windows-local node_repl.exe command to the Linux app-server.

Security and Approval Model

This compatibility layer runs in trusted local mode by default. Browser Use approval is effectively allow: navigation, browser history access, downloads, and uploads are not connected to the Codex Desktop approval prompts.

The Linux node_repl runtime intentionally sets:

x-codex-browser-use-security-mode: disabled-for-local-testing

and its createElicitation() helper currently auto-accepts approval requests. This matches the practical behavior of "Always allow", but it is not the same as integrating with the Codex Desktop settings page under Computer use > Google Chrome.

The Codex Desktop approval UI, allowed/blocked domain lists, and per-feature history/download/upload settings are owned by the closed-source desktop app and are not exposed to this Linux remote runtime. Use this project only on hosts, browser profiles, and network environments you trust. A future local policy file could add project-owned allow/deny checks, but it would not be synchronized with the official desktop approval UI.

Requirements

  • Linux host with Node.js 20+.
  • Chromium installed as chromium or chromium-browser.
  • The official Codex Chrome extension already installed or loaded in Chromium.
  • Codex CLI/app-server already set up on the Linux host.
  • A cached official Codex Chrome plugin under ~/.codex/plugins/cache/....

The default extension ID used by this installer is:

hehggadaopoacecdllhhajmbjkdcmajg

Override it with --extension-id if your extension ID differs.

Install

From the project directory on the Linux host:

node bin/codex-browser-use-linux-chromium.js install --patch-chromium-extension --desktop-shims --windows-shims
node bin/codex-browser-use-linux-chromium.js doctor

install performs these steps:

  • Copies runtime files into ~/.local/share/codex-browser-use-linux-chromium.
  • Writes user-level Chromium and Google Chrome native host manifests. Use --system-native-host when Chromium already has a system manifest under /etc/chromium/native-messaging-hosts or Chrome has one under /etc/opt/chrome/native-messaging-hosts.
  • Patches discovered official Browser Use / Chrome plugin caches and Codex 0.130 staged bundled-marketplace plugin roots under ~/.codex/.tmp/bundled-marketplaces/.... Plugin patching is planned transactionally per plugin root: if any required patch point is missing, that plugin root is left untouched.
  • Updates or creates plugin-local .mcp.json entries pointing to the installed runtime. Chrome plugin roots use node_repl; Browser Use plugin roots use browser_node_repl. The installer also adds mcpServers metadata to Chrome and Browser Use plugin manifests when the official cache does not ship it.
  • Ensures installed bundled plugin caches keep a latest alias pointing at the installed version directory, so Codex can still discover plugin skills such as @chrome after codex plugin add or update operations materialize only a versioned cache path.
  • With --patch-chromium-extension, patches the system Chromium Codex extension background script under /usr/share/chromium/extensions/codex so viewport screenshots can use chrome.tabs.captureVisibleTab instead of the debugger Page.captureScreenshot path. Use --extension-root PATH when the extension is installed elsewhere. Patched extension instances advertise this capability through getInfo(), and the Browser client prefers those instances when old unpatched sockets are still present. The installer also bumps the unpacked extension version, for example from 1.1.4 to 1.1.4.1, to force Chromium to refresh the extension service worker.
  • Sets features.tool_search_always_defer_mcp_tools in ~/.codex/config.toml for the detected Codex CLI version. Codex 0.130-0.132 need true so node_repl/js is discoverable through tool_search; Codex 0.133+ works better with false on Desktop remote sessions so small MCP tool sets such as node_repl/js are exposed directly. Pass --skip-feature-config only if you want to manage that feature flag yourself.
  • With --desktop-shims, creates Linux shims for macOS Codex Desktop remote paths:
    • /Applications/Codex.app/Contents/Resources/node_repl
    • /Applications/Codex.app/Contents/Resources/cua_node/bin/node_repl
    • /Applications/Codex (Beta).app/Contents/Resources/node_repl
    • /Applications/Codex (Beta).app/Contents/Resources/cua_node/bin/node_repl
  • With --windows-shims, creates Linux shims for common Windows Codex Desktop remote paths, including stable and Beta paths under C:\\Users\\<name>\\AppData\\Local\\Programs\\...\\resources\\node_repl.exe. The installer generates both the local Linux username and a capitalized variant; pass --windows-username NAME when the Windows account name differs. It also scans the last 7 days of ~/.codex/logs_2.sqlite for Codex 0.133-style hashed paths such as C:\\Users\\<name>\\AppData\\Local\\OpenAI\\Codex\\bin\\<hash>\\node_repl.exe and creates shims for the exact commands it finds. Backslash-form Windows commands are installed into both ~/.local/bin and ~/.npm-global/bin, so the app-server can find them through PATH.
  • With --system-native-host, writes system native host manifests:
    • /etc/chromium/native-messaging-hosts/com.openai.codexextension.json
    • /etc/opt/chrome/native-messaging-hosts/com.openai.codexextension.json

The /Applications shims, system native host manifests, and system Chromium extension patch require root permissions. The installer preflights passwordless sudo -n before making changes when a privileged option is requested.

For tests or custom Chromium profile locations, override the manifest root:

node bin/codex-browser-use-linux-chromium.js install --browser-config-root /tmp/browser-config

The installer refuses real writes on non-Linux hosts unless --allow-non-linux is passed. Use that only for local tests with temporary directories.

Optional Codex CLI Config

The installer manages this compatibility flag unless --skip-feature-config is passed:

[features]
tool_search_always_defer_mcp_tools = false

For Codex 0.133+ through the current tested CLI (0.137.0), false keeps small MCP tool sets directly callable. This avoids Desktop remote sessions that can start node_repl but fail to include it in the remote tool_search index. For Codex 0.130-0.132, the installer keeps the older true behavior because those versions relied on tool_search for Chrome/Browser skill discovery. Codex 0.135+ marks the old js_repl feature as removed, so this compatibility layer should continue to expose node_repl through MCP rather than trying to restore the removed built-in REPL.

Codex 0.130 also de-duplicates plugin MCP servers by name. If both the Chrome and Browser Use plugin caches declare node_repl, the first loaded plugin can claim the name and the other plugin's JS tool may not be discoverable in a desktop remote conversation. This installer avoids that collision by keeping Chrome on node_repl and moving Browser Use to browser_node_repl. The patched Browser skill knows to search for browser_node_repl js when node_repl/js is not visible.

The patched Chrome skill is deliberately stricter: Chrome tasks should use node_repl, not browser_node_repl. The latter exists only for Browser / in-app-browser compatibility and can lead a Chrome request down Browser-specific routing instructions.

Codex 0.131 keeps the same compatibility requirements. The official CLI now has codex doctor, codex mcp list, and codex plugin list; this project shells out to those commands from doctor and reports the upstream status as additional evidence. A failing upstream terminal check such as TERM=dumb does not by itself mean the Chromium bridge is broken; use this project's native-host, extension, socket, and plugin-cache checks for the browser-specific path.

Codex 0.133 keeps those bridge requirements in place. Its codex plugin list output is marketplace-grouped table output with installed version and plugin path columns; this project's doctor accepts both the older 0.131/0.132 list format and the 0.133 table format when reporting Browser Use / Chrome plugin state. install and patch-plugin also recreate the installed bundled plugin latest cache alias when Codex has only left a versioned directory such as chrome/0.1.7; without that alias, @chrome may disappear from skill discovery even though the Chrome plugin is still installed and enabled.

Codex 0.137 adds codex plugin list --json and --available --json. This project's doctor prefers that structured output and falls back to the table output for older Codex builds; the plugin manifest and .mcp.json patch format remain unchanged.

For direct Codex CLI usage, also add:

node bin/codex-browser-use-linux-chromium.js install --write-codex-config

This appends a marked node_repl MCP block to ~/.codex/config.toml if no [mcp_servers.node_repl] block exists. If the block already points at a known older codex-chrome-extension or codex-browser-use-linux-chromium REPL path, the installer updates it to the current runtime path. Custom node_repl entries are left unchanged.

Codex Desktop remote sessions may still override MCP config via RefreshMcpServers; the desktop path shims are what handle that case.

Windows Desktop Clients

Windows Codex Desktop may send a Windows-local node_repl command path through RefreshMcpServers, for example a path under AppData\\Local\\Programs or a hashed OpenAI runtime path under AppData\\Local\\OpenAI\\Codex\\bin or AppData\\Local\\OpenAI\\Codex\\runtimes\\cua_node. That path varies by Windows username, install channel, and app update. Install the common Windows stable/Beta shims and any hashed paths already seen in app-server logs with:

node bin/codex-browser-use-linux-chromium.js install --windows-shims --windows-username YOUR_WINDOWS_USER

If the Windows username matches the Linux username, --windows-username is not needed. The generated shims cover both backslash commands like:

C:\Users\Josh\AppData\Local\Programs\Codex Beta\resources\node_repl.exe

and forward-slash variants like C:/Users/Josh/.../node_repl.exe. If Codex Desktop has already sent a hashed command like C:\\Users\\Josh\\AppData\\Local\\OpenAI\\Codex\\bin\\3c238e29bbc930ff\\node_repl.exe or C:\\Users\\Josh\\AppData\\Local\\OpenAI\\Codex\\runtimes\\cua_node\\a89897d3d9baa117\\bin\\node_repl.exe, the installer discovers it from the last 7 days of ~/.codex/logs_2.sqlite automatically. If Codex Desktop uses a custom install path that is not in the logs, inspect the Linux host app-server log for the exact RefreshMcpServers node_repl.command value and pass it explicitly:

node bin/codex-browser-use-linux-chromium.js install --windows-shims \
  --windows-node-repl-path 'C:\Users\Josh\AppData\Local\Programs\Custom Codex\resources\node_repl.exe'

Restore Plugin Patches

Plugin and extension patches are backed up next to each patched file with a .codex-browser-use-linux-chromium.bak.<timestamp> suffix.

Restore the latest adjacent backups with:

node bin/codex-browser-use-linux-chromium.js restore-plugin
node bin/codex-browser-use-linux-chromium.js restore-extension

If Chromium keeps reporting the old extension metadata after a file patch, close Chromium and reset its service worker registration cache:

node bin/codex-browser-use-linux-chromium.js reset-extension-cache

Debugging

Useful logs on the Linux host:

/tmp/codex-native-host-bridge.log
/tmp/codex-node-repl-mcp.log
~/.codex/logs_2.sqlite

If a fresh Codex Desktop conversation cannot see mcp__node_repl__js, run node bin/codex-browser-use-linux-chromium.js doctor first. The output now includes the upstream Codex version, official codex mcp list entries for node_repl and browser_node_repl, and the Browser/Chrome plugin install state. If those look correct, search logs_2.sqlite for RefreshMcpServers and verify the exact node_repl.command exists on the Linux host; on Windows Desktop 0.133+ this may be a hashed AppData\\Local\\OpenAI\\Codex\\bin\\...\\node_repl.exe command or a AppData\\Local\\OpenAI\\Codex\\runtimes\\cua_node\\...\\bin\\node_repl.exe command, so rerun install --windows-shims after the failed attempt to let the installer create that exact shim. Also search the logs for skipping duplicate plugin MCP server name: if Chrome and Browser Use both declare node_repl, reinstall this compatibility layer so Browser Use is moved to browser_node_repl, then restart codex app-server. On Codex 0.130+ remote hosts, verify both ~/.codex/plugins/cache/... and ~/.codex/.tmp/bundled-marketplaces/openai-bundled/plugins/...; the staged bundled-marketplace copy is what some Desktop remote turns load.

If mcp__node_repl__js appears to hang, check for old codex-node-repl-mcp.js processes and open connections in /tmp/codex-native-host-bridge.log. Each MCP process should log a distinct session_id and a stable turn_id in /tmp/codex-node-repl-mcp.log. The JavaScript tool has a default 100 second timeout; override it with CODEX_NODE_REPL_JS_TIMEOUT_MS=0 to disable the timeout or another millisecond value to tune it. The js tool also accepts per-call timeout_ms/timeoutMs values, matching the official runtime's call shape. A timeout returns a normal MCP tool result with isError: true instead of a JSON-RPC transport error, so the model can read the recovery instruction and continue with follow-up calls such as js_reset. By default the runtime destroys native browser pipe connections and resets the JS context after a timeout; set CODEX_NODE_REPL_RESET_ON_TIMEOUT=0 only if you need to preserve in-memory JS state across timeouts. Set CODEX_NODE_REPL_EXIT_ON_TIMEOUT=1 only if you explicitly want timeout errors to terminate the MCP process.

The timeout error includes a consecutive_js_timeouts counter and recovery hint. domSnapshot() and screenshot capture are normal supported Browser Use features on Linux Chromium; the compatibility layer should not steer agents to silently avoid them. With --patch-chromium-extension, normal viewport screenshots first use chrome.tabs.captureVisibleTab, so they no longer depend on Page.captureScreenshot. After a js timeout or stale bridge error, do not run a lightweight tab check such as tab.url() or tab.title() before re-bootstrap: the runtime resets the JS context by default, so tab is not defined is expected. Run js_reset, re-bootstrap, create a new tab, navigate to the target URL again, and retry the requested evidence in a fresh single-purpose call. Do not recover by finding an existing tab with the same URL after a bridge reset; that tab may still be tied to an orphaned CDP command. If it still fails after a fresh bootstrap, report that page-level blocker instead of claiming the screenshot or DOM feature is unavailable.

On Linux Chromium, keep browser bridge calls short and single-purpose. Do not combine click/fill/keyboard/navigation with domSnapshot(), screenshots, dev logs, or per-element extraction loops in one js call. Run the interaction, then verify in a fresh follow-up call. Use screenshots for visual evidence and domSnapshot() when a full accessibility snapshot is the right evidence; use supported locator reads such as scoped count(), allTextContents(), getAttribute(), textContent(), or innerText() only when the task does not need the full tree. This runtime does not expose evaluate, page.evaluate, or locator(...).evaluateAll().

browser.tabs.list() and browser.user.openTabs() return info objects, not controllable Tab handles. Do not call tab.goto() or Playwright/CUA methods on those list results. Reacquire a current-session tab with await browser.tabs.get(info.id), or claim a user tab with await browser.user.claimTab(info), then call goto() on the returned Tab.

agent.browsers is also not an array. It is a registry object with async list() and get() methods. Use const browsers = await agent.browsers.list(); before browsers.map(...), or use await agent.browsers.get("extension") to select Chromium directly.

If multiple extension sockets exist, the node_repl native-pipe shim scores them before connection. The Chromium process that owns the default profile singleton wins over headless, incognito, or /tmp user-data-dir sessions. Set CODEX_BROWSER_USE_CHROMIUM_SOCKET_SELECTION=all only when you explicitly need to expose every live socket to the Browser client.

If a call fails with native pipe is closed, Detached while handling command, or Timed out after ... waiting for CDP command, the REPL resets its stale browser context by default; run js_reset, re-bootstrap, create a new tab, and navigate to the target URL again before retrying. A follow-up tab is not defined error means the caller skipped that bootstrap step and tried to use a stale tab binding.

If Chromium accumulates tabs from Browser/Chrome tasks, use the browser_cleanup MCP tool on the same node_repl/browser_node_repl server. It runs the Browser Use tab finalizer for the current session with keep: []; it does not close arbitrary user tabs. The runtime also best-effort runs that cleanup before js_reset, when the MCP process exits, and after an idle period in a long-lived MCP process. If the same JS context already ran browser.tabs.finalize(...) directly, including a deliberate handoff or deliverable keep list, those automatic cleanup paths skip the second finalizer call. Set CODEX_NODE_REPL_CLEANUP_TABS_ON_RESET=0 or CODEX_NODE_REPL_CLEANUP_TABS_ON_EXIT=0 to disable those automatic cleanup paths, and tune the cleanup wait with CODEX_NODE_REPL_BROWSER_CLEANUP_TIMEOUT_MS (default 3000). Tune or disable the idle cleanup with CODEX_NODE_REPL_IDLE_BROWSER_CLEANUP_MS (default 600000, set 0 to disable).

If /tmp/codex-native-host-bridge.log contains repeated stdout backpressure lines, the native host is sending commands to Chromium faster than Chromium is reading them. This version serializes native-host stdout writes and waits for drain before sending more frames. You can lower the fail-fast queue cap with CODEX_NATIVE_HOST_MAX_CHROME_QUEUE_BYTES; the default is 64 MiB.

When a REPL timeout disconnects while a browser command is still pending, the native host bridge exits by default so Chromium restarts it with a clean command pipe. This prevents orphaned screenshot or DOM commands from causing later Detached while handling command failures. Set CODEX_NATIVE_HOST_EXIT_ON_ORPHANED_PENDING=0 to preserve the old behavior. The bridge only rewrites JSON-RPC request ids while multiplexing clients; it passes JSON-RPC responses through unchanged. This is required because the Chromium extension sends heartbeat requests to the browser client. If response ids are rewritten, the extension never observes the heartbeat response, assumes the client is gone, and detaches active browser sessions while page commands such as domSnapshot() or locator reads are still running.

If a screenshot task says it succeeded but no image appears in the final assistant message, check whether the final message references attachment://response_0.png. The screenshot usually did arrive as a previous tool-output image; that attachment URI is not stable in this compatibility runtime. The installer patches the Chrome skill to keep the screenshot image in the latest browser tool output, save it to a workspace file, and avoid synthetic attachment links. The REPL also replays the most recent emitted image when a follow-up js call only finalizes browser tabs, covering the common cleanup-after-screenshot ordering mistake.

doctor also reports Browser Use sockets under /tmp/codex-browser-use, plus the current upstream Codex CLI doctor/MCP/plugin-list summaries when codex is available on PATH. Sockets whose owner process is gone are stale; the native bridge removes stale chromium-<pid>.sock files on startup.

doctor also reports whether the Chromium Codex extension background script and manifest reload marker have the visible-tab screenshot patch. Use install --patch-chromium-extension or patch-extension to apply it. If a live extension still omits codexLinuxCaptureVisibleTab from getInfo(), close Chromium and run reset-extension-cache, then reopen Chromium. Use restore-extension to restore the latest backup made by this installer.

See docs/architecture.md for the runtime flow.

About

Unofficial Codex Browser Use compatibility layer for Linux Chromium remote hosts.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages