Search

Codex Drove My Mac and “cua” Showed Up — Tracing ChatGPT.app's Bundled cua_repl and How It Relates to the OSS cua-driver

Tadashi Shigeoka · Thu, October 1, 2026

I gave Codex a task that called for Computer Use, operating Keynote on my Mac, and while watching the agent’s log as it ran, I noticed it calling a tool named mcp__cua_repl.

The string “cua” immediately reminded me of cua-driver, part of the OSS trycua/cua repository whose code I read in an earlier post on this site (Reading Cua Driver 0.26.1 Computer Use Internals). I also have CuaDriver.app installed manually on this Mac.

So is Codex’s Computer Use built on trycua’s cua-driver? This post does not answer that from the name alone. Instead, it walks through the files bundled with the app, the launch code, the running processes, and public sources, and records how far each link can actually be verified.

Results First: The Evidence

The investigation was done on a single Apple Silicon machine running macOS 27.0.1. At work I also use Codex’s Computer Use on Windows and have confirmed that it works there, but the bundled files and processes examined in this post come from the macOS machine only. Everything here is an observation of this environment and these versions, and should not be generalized to every version or OS of ChatGPT or Codex.

WhatKindObservationHow
Tool used for KeynoteLocal (execution log)26 calls to the js tool of mcp__cua_repl; the app was obtained with cua.getApp("com.apple.iWork.Keynote")JSONL logs under ~/.codex/sessions
cua-driver in the same sessionLocal (execution log)No record of running the cua-driver command (the string only appears in web searches and prose)Same
Plugin definitionLocal (config)The unified-computer-use plugin registers an MCP server named cua_replplugin.json and .mcp.json inside the app
Actual launch commandLocal (config)cua_node/bin/node runs @oai/cua-repl/bin/cua-repl.mjs with CUA_REPL_ENABLED_SURFACES=browser,computer.mcp.json materialized under ~/.codex/plugins/cache
Process treeLocal (runtime)codex app-server --managed-daemon → node cua-repl.mjs → node_replps
@oai/cua-repl → @oai/cuaLocal (code)The bootstrap runs import("@oai/cua/tinyskyAlt"), which defines globalThis.cuainstructions/banner.js, tinysky_alt/globals.js
@oai/cua → @oai/skyLocal (code, config)With computer enabled, it loads a sky client that RPCs to the REPL’s trusted service sky (@oai/sky/service)create_tinysky_alt.js, env vars in .mcp.json
@oai/sky → native sideLocal (code)Connects to ~/Library/Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/computeruse.socktargets/mac/native-pipe.js
Native appLocal (signature)Codex Computer Use.app, bundle ID com.openai.sky.CUAService, Team ID 2DC432GLL2defaults read, codesign -dv
Manually installed cua-driverLocal (signature)CuaDriver.app 0.26.0, bundle ID com.trycua.driver, Team ID YCK386LBJ7, socket at ~/Library/Caches/cua-driver/cua-driver.sockSame
References to trycuaLocal (string search)No match for trycua / cua-driver / CuaDriver in the JS under cua_node, Codex Computer Use.app, app.asar, or THIRD_PARTY_NOTICES.txtgrep -a -i
Public Codex issuePublic (reporter’s observation)A bug report that names mcp__cua_repl and unified-computer-useopenai/codex#47378
Public trycua issuesPublic (trycua’s statements)Describe Codex’s bundled Computer Use as a separate provider routed through node_repl / @oai/skytrycua/cua#2813, openai/codex#36741

Versions checked:

ComponentVersion
ChatGPT.app (bundle ID com.openai.codex)26.928.21956 (build 12404)
cua_node runtime0.0.27 (20260927214556-b77d38801cca), Node.js 24.21.0
@oai/cua-repl0.1.0
@oai/cua0.2.5
@oai/sky0.7.5
Codex Computer Use.app26.924.1001281
Codex CLI (Homebrew) and app-server daemon0.160.0
CuaDriver.app (trycua)0.26.0

The Question That Came Out of Letting Codex Drive Keynote

The task was a Computer Use demo with Keynote as the subject. Codex obtained Keynote as its target app and worked by repeatedly reading the screen state, clicking, and typing.

Tallying the session log (JSONL under ~/.codex/sessions/) afterwards, there were only two kinds of tool calls:

custom_tool_call  exec                78 calls
function_call     mcp__cua_repl / js  26 calls

The js tool of mcp__cua_repl received JavaScript code, and inside that code the app was obtained with calls like cua.getApp("com.apple.iWork.Keynote"). In Codex, Model Context Protocol tools appear under an mcp__<server name> namespace, so this means the js tool of an MCP server named cua_repl was being called.

That raised two questions:

  • Where does the cua_repl MCP server come from?
  • Is it the same thing as trycua’s cua-driver?

A note on terminology first. “CUA” stands for Computer-Using Agent, a term OpenAI itself used in its January 2025 Computer-Using Agent announcement. trycua’s “cua” most likely comes from the same abbreviation. Containing “cua” in a name says nothing about sharing an implementation.

The mcp__cua_repl That Was Actually Called

The plugin definition

Searching inside ChatGPT.app (read-only), I found a plugin named unified-computer-use.

ChatGPT.app/Contents/Resources/plugins/openai-bundled/plugins/unified-computer-use/
├── .codex-plugin/plugin.json
└── .mcp.json

An excerpt of plugin.json:

{
  "version": "26.928.21956",
  "name": "unified-computer-use",
  "description": "App-managed browser automation runtime.",
  "mcpServers": "./.mcp.json"
}

The real file also contains Stop, SubagentStop, and Interrupt hooks that call the turn_ended tool on cua_repl, which notifies the server that a turn has ended.

The .mcp.json inside the app has command set to just node, empty args, and enabled: false:

{
  "mcpServers": {
    "cua_repl": {
      "command": "node",
      "args": [],
      "enabled": false,
      "enabled_tools": ["js", "js_reset", "turn_ended"]
    }
  }
}

As written, this cannot launch anything.

The launch config the app filled in

The README bundled with @oai/cua-repl says the desktop app fills in this empty config:

The packaged `plugin/.mcp.template.json` is disabled and has no launch arguments.
Desktop renames it to `.mcp.json`, the filename required by the plugin loader.
...
Desktop fills in the downloaded Node executable and package bin path before
enabling it.

And indeed, ~/.codex/plugins/cache/openai-bundled/unified-computer-use/26.928.21956/.mcp.json has the launch command filled in and enabled set to true (username replaced with ~):

{
  "cua_repl": {
    "command": "/Applications/ChatGPT.app/Contents/Resources/cua_node/bin/node",
    "args": [
      "/Applications/ChatGPT.app/Contents/Resources/cua_node/lib/node_modules/@oai/cua-repl/bin/cua-repl.mjs"
    ],
    "enabled": true,
    "env": {
      "NODE_REPL_TRUSTED_SERVICES": "{\"browser\":\"@oai/browser-desktop/service\",\"sky\":\"@oai/sky/service\"}",
      "SKY_CUA_SERVICE_PATH": "~/.codex/computer-use/Codex Computer Use.app",
      "CUA_REPL_NODE_REPL_PATH": "/Applications/ChatGPT.app/Contents/Resources/cua_node/bin/node_repl",
      "CUA_REPL_ENABLED_SURFACES": "browser,computer"
    }
  }
}

env is abridged. cua_repl runs the @oai/cua-repl bin with the Node.js bundled in ChatGPT.app, and passes @oai/sky/service as a trusted service named sky.

Confirming with processes

Beyond config, I checked the parent/child relationship of the running processes:

ps -axo pid,ppid,command | grep -E "cua-repl|node_repl"
47671 29477 .../cua_node/bin/node .../@oai/cua-repl/bin/cua-repl.mjs
47698 47671 .../cua_node/bin/node_repl

Parent PID 29477 was ~/.codex/packages/app-server-daemon/releases/0.160.0-aarch64-apple-darwin/bin/codex app-server --listen unix:// --managed-daemon. Codex’s resident app-server starts cua-repl.mjs, and cua-repl.mjs starts node_repl as a child.

cua-repl.mjs itself is a short script that calls launch(). Reading launch() (dist/.../oai_js_cua_repl/src/launch.js, minified), it does the following:

  • Fails startup if CUA_REPL_ENABLED_SURFACES contains anything other than browser or computer
  • When computer is enabled, sets the trusted service sky: "@oai/sky/service"
  • Builds the tool descriptions for js and js_reset from per-platform Markdown and passes them as NODE_REPL_TOOL_OVERRIDES
  • spawns the node_repl at CUA_REPL_NODE_REPL_PATH with stdio: "inherit"

Because stdio is inherited, the process Codex actually talks to as the MCP server cua_repl is the child node_repl.

The @oai/cua Packages Inside ChatGPT.app

Three packages

cua_node/lib/node_modules/@oai/ contains cua-repl, cua, sky, and browser-desktop. Key fields from their package.json files:

Packageversiondescription / main exports
@oai/cua-repl0.1.0"CUA MCP interface for NodeREPL.", bin cua-repl
@oai/cua0.2.5exports ., ./tinyskyAlt, ./tinyskyBrowser
@oai/sky0.7.5exports ., ./service; publishConfig.executableFiles lists executables inside Codex Computer Use.app

None of them list a trycua package under dependencies. The only dependency of @oai/sky is @statsig/js-client. The author fields contain what looks like a personal handle, but that says nothing about who builds what, so I leave it out of this post.

From @oai/cua-repl to @oai/cua

The @oai/cua-repl README describes its role like this:

`@oai/cua-repl` launches NodeREPL with the `cua` API from
`@oai/cua/tinyskyAlt`. The `cua_node` archive includes this package.
Codex Desktop's `unified-computer-use` plugin points at its installed bin.

The bootstrap that runs when the REPL starts (instructions/banner.js) is one line:

await import("@oai/cua/tinyskyAlt");

tinysky_alt/globals.js in @oai/cua reads CUA_REPL_ENABLED_SURFACES, creates the browser and computer providers, and assigns the result to globalThis.cua. The cua in the cua.getApp() calls from the Keynote session is this object. The macOS tool instructions (instructions/macos/computer.md) open with the same call:

let app = await cua.getApp("Example App");

From @oai/cua to @oai/sky

create_tinysky_alt.js dynamically imports a sky client when computer is enabled:

const { sky } = yield import("../../../../../project/cua/sky_js/src/index.js");

The import target is dist/project/cua/sky_js/ inside the @oai/cua package, which has almost the same file layout as dist/project/cua/sky_js/ in @oai/sky (with diff -rq, only @oai/sky has service.js, and the only other difference is one telemetry file). This client asks the REPL host via nodeRepl.rpc({ type: "setup" }), and the host hands the request to the trusted service sky, which is handleRpc in @oai/sky/service. handleRpc accepts setup and execute, and execute runs operations by method name such as click, type_text, and get_app_state.

From @oai/sky to the native app

The macOS implementation in @oai/sky (targets/mac/native-pipe.js) connects to this Unix domain socket:

~/Library/Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/computeruse.sock

2DC432GLL2.com.openai.sky.CUAService in the path matches the Team ID and bundle ID of Codex Computer Use.app, which ships inside @oai/sky:

defaults read ".../@oai/sky/Codex Computer Use.app/Contents/Info" CFBundleIdentifier
# => com.openai.sky.CUAService
codesign -dv ".../@oai/sky/Codex Computer Use.app" 2>&1 | grep TeamIdentifier
# => TeamIdentifier=2DC432GLL2

The executable of Codex Computer Use.app is SkyComputerUseService, and its SharedSupport contains SkyComputerUseClient.app, CUALockScreenGuardian.app, and Codex Computer Use Installer.app. A copy with the same version (26.924.1001281) lives at ~/.codex/computer-use/Codex Computer Use.app, which is where SKY_CUA_SERVICE_PATH in .mcp.json points. ChatGPT.app itself is also signed with Team ID 2DC432GLL2.

However, SkyComputerUseService was not running at the time of the investigation, and the IPC directory contained only computeruse.sock.lock, not the socket itself. I did not observe at runtime that SkyComputerUseService listens on that socket.

As for the name “sky”: in October 2025 OpenAI announced that it had acquired Software Applications Incorporated, the maker of Sky, a natural-language interface for the Mac (OpenAI acquires Software Applications Incorporated, maker of Sky). It is reasonable to think the “sky” in package names and bundle IDs relates to that acquisition, but nothing in the bundled files says so, so I treat it as an inference.

Call graph

Edges are split by how they were verified. Solid lines were confirmed at runtime or in code/config; the dotted line is an inference. There is deliberately no edge to cua-driver.

flowchart TD
  M["Model"] -->|"Log: mcp__cua_repl / js"| A["codex app-server<br/>--managed-daemon"]
  A -->|"Runtime (ps): child process"| B["node cua-repl.mjs<br/>@oai/cua-repl 0.1.0"]
  B -->|"Runtime (ps) + code: spawn"| C["node_repl<br/>the process behind MCP server cua_repl"]
  C -->|"Code: import via banner.js"| D["@oai/cua/tinyskyAlt 0.2.5<br/>defines globalThis.cua"]
  D -->|"Code: import when computer is enabled"| E["sky client<br/>sky_js inside @oai/cua"]
  E -->|"Code + config: nodeRepl.rpc → trusted service sky"| F["@oai/sky/service 0.7.5"]
  F -->|"Code: native pipe"| G["computeruse.sock<br/>2DC432GLL2.com.openai.sky.CUAService"]
  G -.->|"Inference: app with the same bundle ID listens"| H["Codex Computer Use.app<br/>SkyComputerUseService"]
  X["CuaDriver.app 0.26.0<br/>com.trycua.driver"] --- Y["~/Library/Caches/cua-driver/<br/>cua-driver.sock"]

Dependencies, bundled copies, and string references

If trycua’s cua-driver were in use, traces could show up in declared dependencies, bundled binaries, license notices, or config files. I ran grep -a -i for trycua, cua-driver, cua_driver, CuaDriver, and com.trycua over:

  • Every text file under cua_node/lib/node_modules/ (including @oai/*)
  • Every file in @oai/sky/Codex Computer Use.app and ~/.codex/computer-use/Codex Computer Use.app, binaries included
  • ChatGPT.app’s app.asar
  • ChatGPT.app’s THIRD_PARTY_NOTICES.txt (about 50,000 lines) and cua_node/LICENSE

None matched. The dependencies listed in cua_node/lib/node_modules/.package-map.json are @oai/*, playwright, sharp, @statsig/*, and similar; no trycua package appears.

What this supports is: “in this environment and version, the bundled files contain no dependency on, bundled copy of, or name reference to trycua’s cua-driver.” I did not analyze the compiled binaries beyond string search, so this cannot fully rule out that some code is shared.

Comparing APIs and processes

cua-driver’s MCP tools can be listed with cua-driver list-tools. It exposes individual tools such as click, drag, list_apps, get_window_state, and launch_app. cua_repl, by contrast, exposes three tools, js, js_reset, and turn_ended, and actions happen by calling APIs like cua.getApp() inside the JavaScript passed to js. Some methods of the @oai/sky macOS client share names with cua-driver tools (click, drag, list_apps), but those are generic verbs for computer use, and matching names do not indicate a shared implementation.

ItemBundled in ChatGPT.app (cua_repl)Manually installed (cua-driver)
MCP server launchcua_node/bin/node .../@oai/cua-repl/bin/cua-repl.mjscua-driver mcp
Exposed MCP toolsjs, js_reset, turn_endedIndividual tools such as click, get_window_state
Native appCodex Computer Use.app (com.openai.sky.CUAService)CuaDriver.app (com.trycua.driver)
Signing Team ID2DC432GLL2YCK386LBJ7
IPC socket~/Library/Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/computeruse.sock~/Library/Caches/cua-driver/cua-driver.sock
DistributionBundled with ChatGPT.apptrycua GitHub releases

What public sources say

trycua’s public material treats Codex’s bundled Computer Use and cua-driver as separate providers:

  • trycua’s blog post Inside macOS window internals (published 2026-04-23) explains that, after OpenAI announced background computer use in Codex for (almost) everything, they dug into macOS window management internals and built cua-driver on the view that a background computer-use driver should be a commodity any harness can use, not a feature of one agent’s product.
  • trycua’s issue #2813 (created 2026-08-03) proposes shipping cua-driver as a Codex plugin. It points out that Codex auto-selects its bundled Computer Use skill and routes through node_repl / @oai/sky even when the user asks for CUA Driver.
  • openai/codex#36741, by the same reporter, asks Codex to prioritize an explicitly chosen tool or MCP server over bundled skills, and includes a reproduction in which the bundled computer-use:computer-use skill routed actions through node_repl and @oai/sky.

These are trycua’s statements and a reporter’s observations, not an official OpenAI specification. Still, the fact that cua-driver’s own developers treat Codex’s bundled Computer Use as a separate implementation is consistent with what I observed locally.

On the OpenAI side, the public issue openai/codex#47378 (created 2026-09-22) is a bug report about failing to click tabs in a Tk app via mcp__cua_repl and unified-computer-use. It shows that the reporter’s environment used the same tool and plugin names, but it is also a reporter’s observation rather than a description of the implementation.

Connecting cua-driver to Codex

cua-driver’s documentation, Connect your agent, registers it with Codex by running cua-driver mcp-config --client codex, which prints codex mcp add cua-driver -- <path> mcp for you to run (the older path, how-to-guides/driver/connect-your-agent.mdx, had moved to cua-driver/guides/connect-your-agent.mdx at the time of writing). In other words, using cua-driver from Codex requires the user to register it explicitly as an MCP server.

In this environment, ~/.codex/config.toml has no MCP server entry for cua-driver, and it does not appear in codex mcp list. CuaDriver.app is installed and ~/.local/bin/cua-driver exists, but it was not wired into the path the Keynote session used.

How Much Do ChatGPT and Codex Share?

Separating what I could verify in this environment from what I could not:

Verified:

  • /Applications/ChatGPT.app has the bundle ID com.openai.codex and bundles the Codex CLI (codex-cli) and cua_node. The public Computer Use docs also describe choosing ChatGPT (Work) or Codex in the ChatGPT desktop app and installing the Computer Use plugin.
  • Two plugins are bundled: unified-computer-use and the older computer-use. The computer-use plugin’s skill (SKILL.md) instructs the model to import @oai/sky directly in node_repl, while unified-computer-use goes through the cua API from @oai/cua. The @oai/cua-repl README states: “Desktop suppresses legacy skills and hints only for the providers handled by cua_repl.” This defines what happens when both are enabled.
  • codex mcp list from the Homebrew-installed Codex CLI 0.160.0 also shows cua_repl, but its launch command points into /Applications/ChatGPT.app/.... This is most likely because the CLI and the desktop app share the config and plugin cache in ~/.codex.

Not verified:

  • Whether running Computer Use from ChatGPT (Work) mode calls the same cua_repl. ~/.codex/computer-use/config.json contains the UI string “ChatGPT is using your computer,” which hints at shared components, but this remains an inference.
  • Whether the Codex CLI alone can use Computer Use on a machine without ChatGPT.app. The public docs only name the desktop app.
  • The internals of the Windows and Linux setups. The public docs list macOS and Windows as supported. @oai/cua-repl has instructions/windows/ and instructions/linux/, and @oai/sky has targets for each OS, but this post only looked inside the macOS build.

Telling the Bundled Runtime Apart from a Manually Installed cua-driver

When both are installed on the same Mac, you can check which one is running in this order:

  1. Check the tool name in the session log or tool call display. In this environment the bundled runtime appeared as js on mcp__cua_repl. If cua-driver were registered with codex mcp add cua-driver, I would expect individual tools such as get_window_state under a cua-driver namespace (an inference from the naming convention; I did not register it here).
  2. Run codex mcp list to see registered MCP servers and their launch commands.
  3. Run ps -axo pid,ppid,command and see whether cua-repl.mjs, node_repl, and SkyComputerUseService are running, or /Applications/CuaDriver.app/Contents/MacOS/cua-driver.
  4. Check the native app’s signature with codesign -dv: com.openai.sky.CUAService (Team ID 2DC432GLL2) versus com.trycua.driver (Team ID YCK386LBJ7).
  5. Check where the IPC socket lives: the bundled runtime uses Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/, while cua-driver uses ~/Library/Caches/cua-driver/.

What Was Confirmed and What Remains Unknown

Confirmed (locally, for this environment and version)

  • The Keynote session called the js tool of mcp__cua_repl, and the same session has no record of running the cua-driver command.
  • mcp__cua_repl is an MCP server registered by the unified-computer-use plugin bundled in ChatGPT.app, started by Codex’s resident app-server as node cua-repl.mjs and then node_repl.
  • On startup, node_repl imports @oai/cua/tinyskyAlt to define the cua API, and computer actions go through @oai/sky/service to the IPC socket of com.openai.sky.CUAService.
  • No dependency on, bundled copy of, or name reference to trycua’s cua-driver was found in the bundled files (JS, native apps, app.asar, license notices).
  • The bundled runtime and the manually installed cua-driver differ in bundle ID, signing Team ID, IPC socket location, and the shape of their MCP tools.

What public sources say

  • trycua describes cua-driver as a driver any harness can use rather than a feature of one agent’s product, built after Codex’s background computer use announcement, and treats Codex’s bundled Computer Use (node_repl / @oai/sky) as a separate provider.
  • OpenAI announced its acquisition of Sky’s maker in October 2025.

Inferences

  • SkyComputerUseService in Codex Computer Use.app is what listens on computeruse.sock (based on the matching bundle ID and path; neither the process nor the socket existed at the time of the investigation).
  • The name “sky” derives from the acquired Sky team or technology (based on public information and the name match).

Not verified

  • The internals of native binaries such as SkyComputerUseService. I only ran string searches, with no disassembly.
  • Whether Computer Use in ChatGPT (Work) mode uses the same path.
  • Other versions, the internals on Windows and Linux, and Codex CLI behavior without ChatGPT.app.

Not finding a dependency does not prove that trycua’s cua-driver is never used. What I can say is this: in ChatGPT.app 26.928.21956 in this environment, the path from mcp__cua_repl to OpenAI-signed Codex Computer Use.app can be traced, and cua-driver does not appear anywhere on that path. Even when the names share “cua,” the distribution source, signature, processes, and socket tell you which one is actually running.

That’s all from tracing Codex’s mcp__cua_repl through ChatGPT.app’s bundled files and confirming it runs on a path separate from trycua’s cua-driver, from the Gemba.

References