Skip to content

Codex CLI supports Chrome plugin #22164

Open

Description

@haofanwang

What variant of Codex are you using?

CLI

What feature would you like to see?

Is there any plan to support Codex Chrome in CLI? I notice that there is skill.md, but when I use it in CLI, it reports "privileged native pipe bridge is not available; browser-client is not trusted".

Additional information

No response

Activity

  1. github-actions commented on May 11, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  2. Co5mos commented on May 29, 2026

    @Co5mos

    I can reproduce the same limitation via @openai/codex-sdk, so this is not only an interactive CLI request.

    Environment:

    • macOS arm64
    • Codex CLI: 0.134.0
    • @openai/codex-sdk: 0.135.0
    • Codex Desktop Chrome plugin works in a normal Desktop thread on the same host

    Control check from a normal Codex Desktop thread:

    const { setupBrowserRuntime } = await import('/Users/.../.codex/plugins/cache/openai-bundled/chrome/26.519.81530/scripts/browser-client.mjs');
    await setupBrowserRuntime({ globals: globalThis });
    console.log(await agent.browsers.list());

    That returns browser backends including:

    • Chrome, type: "extension"
    • Codex In-app Browser, type: "iab"

    SDK repro:

    import { Codex } from "@openai/codex-sdk";
    
    const codex = new Codex();
    const thread = codex.startThread({
      workingDirectory: "/path/to/trusted/repo",
    });
    
    const result = await thread.run(
      "Use @Chrome to open https://example.com and read the page title."
    );
    
    console.log(result.finalResponse);
    console.log(result.items);

    Observed in the SDK-created turn:

    • The turn loads/attempts the Chrome skill.
    • agent.browsers.get("extension") fails with Browser is not available: extension.
    • Retrying after 2 seconds fails the same way.
    • Chrome running check passes.
    • Codex Chrome Extension installed/enabled check passes.
    • Native messaging host manifest check passes.
    • agent.browsers.list() inside the SDK-created turn returns [].

    The final SDK result was:

    CHROME_RESULT: unable to read; Chrome extension communication unavailable (Browser is not available: extension).
    

    Additional check from a plain external Node process:

    const { setupBrowserRuntime } = await import('/Users/.../.codex/plugins/cache/openai-bundled/chrome/26.519.81530/scripts/browser-client.mjs');
    await setupBrowserRuntime({ globals: globalThis });

    fails with:

    privileged native pipe bridge is not available; browser-client is not trusted
    

    Looking at the SDK implementation also explains the boundary: @openai/codex-sdk@0.135.0 shells out to codex exec --experimental-json, so the SDK-created runtime does not appear to receive the same trusted native pipe / browser backend context that a normal Codex Desktop thread receives.

    Expected behavior, depending on product intent:

    1. @openai/codex-sdk / codex exec can use the Chrome plugin when running on the same host where Codex Desktop has a healthy Chrome backend; or
    2. the CLI/SDK docs explicitly state that Codex Desktop Chrome Use is unavailable from SDK/codex exec runtimes.

    This appears closely related to this issue, with the additional SDK angle.

  3. Jorjon commented on May 31, 2026

    @Jorjon

    I can reproduce this on macOS with Codex CLI 0.135.0, even after installing and enabling the bundled Chrome plugin and verifying the local Chrome extension/native-host setup.

    Environment:

    • macOS arm64
    • Codex CLI: 0.135.0
    • chrome@openai-bundled: installed/enabled, version 26.527.31326
    • Google Chrome: 148.0.7778.179
    • Codex Chrome Extension: installed/enabled in the selected Chrome profile, version 1.1.5_0

    Local setup checks all pass. These setup checks are the diagnostic scripts shipped with the bundled Codex Chrome plugin itself, from ~/.codex/plugins/cache/openai-bundled/chrome/26.527.31326/scripts/, not custom scripts.

    • scripts/check-extension-installed.js --json reports installed: true, enabled: true for selected profile Profile 1.
    • scripts/check-native-host-manifest.js --json reports correct: true.
    • scripts/chrome-is-running.js --json reports Chrome is running.
    • The native host process is actually running:
      • extension-host chrome-extension://hehggadaopoacecdllhhajmbjkdcmajg/
    • lsof -U shows the extension host owns a browser-use socket:
      • /tmp/codex-browser-use/<uuid>.sock

    But from the CLI node_repl runtime:

    const { setupBrowserRuntime } = await import("/Users/<redacted>/.codex/plugins/cache/openai-bundled/chrome/26.527.31326/scripts/browser-client.mjs");
    await setupBrowserRuntime({ globals: globalThis });
    
    console.log(await agent.browsers.list());
    
    try {
      console.log(await agent.browsers.get("extension"));
    } catch (e) {
      console.log(e.message);
    }

    Observed result:

    agent.browsers.list() => []
    agent.browsers.get("extension") => Browser is not available: extension
    

    During setup, discovery also logs:

    IAB_DISCOVERY no session-owned iab browser
    failures:
    - pipe-connect/Error: failed to connect native pipe: Connection refused (os error 61)
    - backend-info-request/native pipe closed before response
    

    So this does not appear to be just a missing extension, missing native host manifest, or disabled Chrome plugin. The Chrome extension/native host is present and running, but the CLI runtime still does not discover an extension backend.

  4. 0xdevalias commented on Oct 18, 2026

    @0xdevalias

    Verified Chrome control from current CLI and SDK, and two causes of empty discovery

    Tested 18 October 2026: standalone CLI and official SDK 0.163.0 can control the installed Chrome extension after repairing two local prerequisites. The SDK opened one new tab, loaded https://example.com/, verified Example Domain and the URL, then closed that test tab. Existing tabs were not enumerated. No native authorization checks or installed binaries were changed.

    The two failures were a missing chrome/latest alias and a swallowed configuration error when CODEX_HOME named a symlink. Neither the empty browser inventory nor Desktop's simultaneous ancestry rejection logs were sufficient to diagnose the Chrome connection alone.

    Tested environment

    Component Version
    Platform macOS 26.6, Apple Silicon
    Standalone CLI and @openai/codex-sdk 0.163.0 — stable release
    Desktop / bundled Browser and Chrome plugins 26.1007.71326, Desktop build 20398, bundle ID com.openai.codex
    App-bundled CLI / configured browser sandbox launcher 0.162.0-alpha.17.2
    Bundled CUA Node 24.21.0-cua.1; runtime archive 0.0.30/20261008210730-dff8b428775e
    Loaded Chrome extension, reported by native-host getInfo 1.26.1005.11759

    Updating the terminal CLI does not update the separately configured bundled launcher, node_repl, or browser service. These results describe that combination. Prerelease 0.164.0-alpha.3, Windows/WSL, API-key authentication, and a fresh Desktop-owned IAB session were not tested. SDK requests reused the existing CLI login and Codex allowance; no new API key was needed.

    1. Passing manifest diagnostics did not establish that the native host could start

    The real Chrome Native Messaging manifest pointed through:

    <CODEX_HOME>/plugins/cache/openai-bundled/chrome/latest/extension-host/macos/arm64/ChatGPT for Chrome
    

    latest was absent, although the host existed in chrome/26.1007.71326. The installed manifest checker still returned correct: true. In this version, scripts/check-native-host-manifest.js, getNativeHostManifestStatus, lines 230–262, validates the host name, extension origins and registry/manifest agreement, but does not validate existence or executability of manifest.path. A synthetic manifest pointing to a deliberately nonexistent executable also passed with exit 0 / correct: true.

    Restoring only the missing relative alias chrome/latest -> 26.1007.71326 caused Chrome to start its native host automatically. No Chrome restart was needed in this experiment. A read-only getInfo request then succeeded.

    Before creating an alias, verify the installed version's .codex-plugin/plugin.json and host executable, and identify the intended active version. Do not select an arbitrary newest directory or overwrite an existing directory/junction. This macOS repair is not a tested Windows junction repair. The absence of the older extension-host-config.json does not itself establish failure of the current v2 provisioning route.

    2. The connected Chrome provider was then discarded because its TOML root was a symlink

    After the host repair, the official service still returned []. A disposable copy with diagnostic logging exposed this error during discovery:

    backend-info-request
    ConfigReadError: Browser Use could not read config.
    cause: Codex TOML directory must not be a symlink
    

    The Chrome handshake had already succeeded. During capability enrichment the service checks Browser preferences/full-CDP state, which reads browser/config.toml. The native configuration reader rejected the symlinked root, and the discovery catch dropped the entire provider. The public inventory hid the underlying configuration error.

    Controlled privileged configuration tests separated the operations:

    CODEX_HOME supplied to node_repl config.read() readRequirements() readToml('browser/config.toml')
    Logical path naming the symlink passed passed Codex TOML directory must not be a symlink
    Existing physical directory passed passed passed

    The workaround was to change only mcp_servers.node_repl.env.CODEX_HOME in the existing user config to the physical directory. For example, if ~/.codex -> /Users/me/dotfiles/codex, merge this value into the existing environment table:

    [mcp_servers.node_repl.env]
    CODEX_HOME = "/Users/me/dotfiles/codex"

    Compute the actual value locally; do not copy that example path:

    python3 -c 'import os; print(os.path.realpath(os.environ.get("CODEX_HOME", os.path.expanduser("~/.codex"))))'

    Preserve the table's other runtime settings. An explicit MCP environment value can override the shell's exported value. This scopes the workaround to newly launched node_repl processes and their services, rather than globally changing the Codex root. Existing processes retain their original environment. It preserves the ~/.codex symlink and leaves child-path symlink validation and policy checks in place; it does not fix an independently symlinked browser/sessions or other rejected child directory.

    This reproduces the underlying error in #35310. The current service's effect is broader than that older report's missing CDP capability: it removes Chrome from discovery entirely.

    Verification through the unchanged service

    The positive tests used the original installed browser-client.mjs, configured Browser service, signed node_repl, and native host. The diagnostic service copy was not used to establish successful navigation.

    • Official SDK @openai/codex-sdk@0.163.0, selecting CLI 0.163.0, discovered Chrome with the physical-root environment. The SDK wrapper used --ignore-user-config --ephemeral and supplied the existing MCP configuration explicitly, with the tested environment correction.
    • The SDK read the browser's control documentation, created one new tab, navigated to https://example.com/, read title Example Domain and URL https://example.com/, and closed only that tab in finally.
    • A separate real CLI test used the repaired ordinary user configuration. It temporarily overrode only BROWSER_USE_BACKEND_PATHS to test late availability: first discovery returned []; after a harness made that path resolve to the working Chrome socket, a second call on the same initialized agent returned Chrome. No reset, reimport or setup call occurred between them. The temporary alias was removed afterward.

    The current public list/get handlers call refresh() even though an internal discovery cache exists. The controlled result is relevant to #41168: permanent empty discovery was not reproduced for an endpoint becoming reachable in this build. It does not cover every provider crash or disconnect race, nor identify which release changed older behavior.

    Diagnose the failing stage before changing trust settings

    The current Chrome client forwards through nodeRepl.rpc to a configured trusted Browser service. The privileged transport lives inside that service. An ordinary user-JS check returning !!nodeRepl.nativePipe === false does not establish that the service lacks native-pipe access. Running the current client as an ordinary Node script instead fails with Browser use requires a trusted Node REPL browser service; old browser-client hash recipes do not establish trust for this current RPC architecture.

    The configured service discovers candidates under /tmp/codex-browser-use, unless BROWSER_USE_BACKEND_PATHS supplies explicit paths. A stale entry directly reproduced ECONNREFUSED, errno 61, at connect(). That is distinct from a connection accepted then closed by an authorizer, a failed getInfo, a configuration error after getInfo, policy denial, or a session-filtered IAB provider. No stale sockets were deleted during this investigation.

    An OS-level capture identified the caller of the service's normal native-pipe connection as the Rust node_repl supervisor, not its JavaScript worker. The worker sends native_pipe_request to that supervisor, which owns the OS socket. Its peer identity comes from the kernel, not a PID supplied in request JSON.

    Desktop IAB/app-tools and Chrome's extension native host have different authorizers. The Desktop addon checks ancestry relative to the process executing it, then signing identities for the peer, parent and grandparent. Chrome's current Rust native host uses a separate authorizer; standalone SDK navigation passed without descent from Desktop. A Desktop untrusted-process-ancestry warning therefore must not be treated as proof that the independent Chrome endpoint rejected the same request. This investigation does not establish an IAB/shared-server workaround.

    Why the alias may fail to recover

    Desktop's current jne reconciliation function creates/repairs latest before synchronizing the host. On this machine, retained October 12–18 logs repeatedly show reconciliation stopping at marketplace registration:

    marketplace `openai-bundled` is reserved and cannot be added from this source
    

    The live Desktop and shared-daemon process environments had no explicit CODEX_HOME. That distinction matters: find_codex_home_from_env canonicalizes an explicitly supplied directory, but its unset-variable branch returns <home>/.codex without canonicalization. Desktop separately resolves the marketplace root with realpath() before submitting it. CLI 0.163.0's managed_local_marketplace_name / is_expected_managed_path compares that source with its owned bundled root; on macOS it handles /var versus /private/var, but does not resolve arbitrary home symlinks. An unrecognized source cannot claim the reserved name.

    A model-free CLI comparison reproduced the distinction:

    • With CODEX_HOME unset and this machine's symlinked default home, adding the physical generated marketplace path failed with the same reserved-source error. This check ran under the filesystem sandbox; no registry write succeeded.
    • Disposable Codex homes with explicit logical or physical CODEX_HOME accepted all three tested logical/physical source combinations. The app-bundled CLI also passed those three explicit-home cases. These tests used temporary empty marketplace fixtures, no login/API/model request, and did not change the live registration.

    Thus the default-home canonicalization asymmetry explains the reproduced registration blocker. Setting the Browser runtime's environment fixes its independent TOML reader; it does not change the already-running Desktop/daemon environment or repair their reconciliation. The versioned Chrome cache directory was created at 2026-10-15T07:58:05Z, while reconciliation errors were already occurring. Retained logs do not establish whether latest was removed or simply never recreated/provisioned for that update. The durable fix is consistent identity for the owned home in both branches, rather than repeatedly recreating aliases.

    Reproducibility fingerprints and source locations

    SHA-256 values identify the exact inspected artifacts, independently of filenames:

    Artifact SHA-256
    Standalone CLI 0.163.0 8813367285524ac798dd96d1b071aea30d58708c7226fd236cd275cf1cfdee31
    Desktop Contents/Resources/app.asar 2f231a64e6dd30111bcd09e03c149cd06c8679b25914842772423251c4608ded
    Extracted .vite/build/main-CwxgTOle.js 87d52265eb13608a380fabcac47ecf13a39b981115c0960c18af563ea6d32d51
    Bundled cua_node/bin/node_repl 540d1c5f3197ac078bcb407f9fed9d81bcf134e31c4e9bdb1fde272fb281c32e
    Bundled cua_node/bin/node 9d15ff36c0c6526429b426e77a0cc89b0717892723c3b4b8ea748d9c1fa287e0
    App-bundled CLI 0d5ac7aae20c4eda6242428d5e218c55ba96d8763fda2a271088ff37104b9d28
    Cached Chrome scripts/browser-client.mjs ce253a7fdaaac7f1845283be71a57510feda7422e7e7b2194a72eb2b2ba1522e
    Configured cached Browser scripts/browser-service.mjs 4295ad5e22e0504b53e32daa4844169610fd65a613290593649b97277b12978f
    Chrome ARM64 native host cc43d9b512e43965eb70e8cdcf304b48b4c413516e9fc176c3e3f8de22bf827c
    Chrome scripts/check-native-host-manifest.js 15a3c021162bd23f367818ac9b4d17f123e745e4902d68f05a17cc410ae9cfd9

    In that exact configured service: kF discovers candidates; $te captures provider failures; Hte performs getInfo and enrichment; Vte checks capabilities; fullCdpAccessState reads Browser TOML; SF wraps the privileged config adapter. Character-offset anchors are fullCdpAccessState around 1,221,891 and SF around 1,296,162. The native node_repl symlink-error literal starts at file byte offset 10,728,888. Its native config implementation is not in the public CLI source tree; generated privileged-node-repl-config.js shows the JS-to-supervisor request, and the controlled reads establish the rejection.

    For independent checking, record codex --version, the loaded extension's getInfo version, shasum -a 256 of the actual configured runtime files, the manifest's executable target, readlink/physical-root results, and a new runtime's discovery result before and after each repair. A complete browser test must proceed beyond discovery to a bounded operation in a newly created tab. The bundled @oai/browser-desktop service has a different hash from the configured cached Browser service; inspecting the former alone would not identify this execution path.

    Upstream improvements supported by these tests

    Validate the manifest executable and alias, surface per-provider causes instead of returning only [], canonicalize the owned configuration root safely before validating child paths, and use consistent owned-root identity in managed marketplace reconciliation. Report missing trusted services, transport refusal, peer rejection, configuration failure, policy denial and missing session routes as distinct errors.

    Related reports, grouped by the boundary they help diagnose

    These are related symptoms and diagnostic boundaries, not a claim that every report has the same root cause or is fixed by the two repairs above. Platform-specific reports were reviewed as context; Windows/WSL were not reproduced.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbrowserenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions