Codex Drove My Mac and “cua” Showed Up — Tracing ChatGPT.app's Bundled cua_repl and How It Relates to the OSS cua-driver
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.
| What | Kind | Observation | How |
|---|---|---|---|
| Tool used for Keynote | Local (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 session | Local (execution log) | No record of running the cua-driver command (the string only appears in web searches and prose) | Same |
| Plugin definition | Local (config) | The unified-computer-use plugin registers an MCP server named cua_repl | plugin.json and .mcp.json inside the app |
| Actual launch command | Local (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 tree | Local (runtime) | codex app-server --managed-daemon → node cua-repl.mjs → node_repl | ps |
@oai/cua-repl → @oai/cua | Local (code) | The bootstrap runs import("@oai/cua/tinyskyAlt"), which defines globalThis.cua | instructions/banner.js, tinysky_alt/globals.js |
@oai/cua → @oai/sky | Local (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 side | Local (code) | Connects to ~/Library/Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/computeruse.sock | targets/mac/native-pipe.js |
| Native app | Local (signature) | Codex Computer Use.app, bundle ID com.openai.sky.CUAService, Team ID 2DC432GLL2 | defaults read, codesign -dv |
| Manually installed cua-driver | Local (signature) | CuaDriver.app 0.26.0, bundle ID com.trycua.driver, Team ID YCK386LBJ7, socket at ~/Library/Caches/cua-driver/cua-driver.sock | Same |
| References to trycua | Local (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.txt | grep -a -i |
| Public Codex issue | Public (reporter’s observation) | A bug report that names mcp__cua_repl and unified-computer-use | openai/codex#47378 |
| Public trycua issues | Public (trycua’s statements) | Describe Codex’s bundled Computer Use as a separate provider routed through node_repl / @oai/sky | trycua/cua#2813, openai/codex#36741 |
Versions checked:
| Component | Version |
|---|---|
ChatGPT.app (bundle ID com.openai.codex) | 26.928.21956 (build 12404) |
cua_node runtime | 0.0.27 (20260927214556-b77d38801cca), Node.js 24.21.0 |
@oai/cua-repl | 0.1.0 |
@oai/cua | 0.2.5 |
@oai/sky | 0.7.5 |
| Codex Computer Use.app | 26.924.1001281 |
| Codex CLI (Homebrew) and app-server daemon | 0.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 callsThe 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_replMCP 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.jsonAn 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_replParent 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_SURFACEScontains anything other thanbrowserorcomputer - When
computeris enabled, sets the trusted servicesky: "@oai/sky/service" - Builds the tool descriptions for
jsandjs_resetfrom per-platform Markdown and passes them asNODE_REPL_TOOL_OVERRIDES spawns thenode_replatCUA_REPL_NODE_REPL_PATHwithstdio: "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:
| Package | version | description / main exports |
|---|---|---|
@oai/cua-repl | 0.1.0 | "CUA MCP interface for NodeREPL.", bin cua-repl |
@oai/cua | 0.2.5 | exports ., ./tinyskyAlt, ./tinyskyBrowser |
@oai/sky | 0.7.5 | exports ., ./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.sock2DC432GLL2.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=2DC432GLL2The 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"]
Looking for a Link to the OSS cua-driver
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.appand~/.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) andcua_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.
| Item | Bundled in ChatGPT.app (cua_repl) | Manually installed (cua-driver) |
|---|---|---|
| MCP server launch | cua_node/bin/node .../@oai/cua-repl/bin/cua-repl.mjs | cua-driver mcp |
| Exposed MCP tools | js, js_reset, turn_ended | Individual tools such as click, get_window_state |
| Native app | Codex Computer Use.app (com.openai.sky.CUAService) | CuaDriver.app (com.trycua.driver) |
| Signing Team ID | 2DC432GLL2 | YCK386LBJ7 |
| IPC socket | ~/Library/Group Containers/2DC432GLL2.com.openai.sky.CUAService/IPC/computeruse.sock | ~/Library/Caches/cua-driver/cua-driver.sock |
| Distribution | Bundled with ChatGPT.app | trycua 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/skyeven 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-useskill routed actions throughnode_repland@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.apphas the bundle IDcom.openai.codexand bundles the Codex CLI (codex-cli) andcua_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-useand the oldercomputer-use. Thecomputer-useplugin’s skill (SKILL.md) instructs the model to import@oai/skydirectly innode_repl, whileunified-computer-usegoes through thecuaAPI from@oai/cua. The@oai/cua-replREADME states: “Desktop suppresses legacy skills and hints only for the providers handled bycua_repl.” This defines what happens when both are enabled. codex mcp listfrom the Homebrew-installed Codex CLI 0.160.0 also showscua_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.jsoncontains 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-replhasinstructions/windows/andinstructions/linux/, and@oai/skyhas 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:
- Check the tool name in the session log or tool call display. In this environment the bundled runtime appeared as
jsonmcp__cua_repl. If cua-driver were registered withcodex mcp add cua-driver, I would expect individual tools such asget_window_stateunder acua-drivernamespace (an inference from the naming convention; I did not register it here). - Run
codex mcp listto see registered MCP servers and their launch commands. - Run
ps -axo pid,ppid,commandand see whethercua-repl.mjs,node_repl, andSkyComputerUseServiceare running, or/Applications/CuaDriver.app/Contents/MacOS/cua-driver. - Check the native app’s signature with
codesign -dv:com.openai.sky.CUAService(Team ID2DC432GLL2) versuscom.trycua.driver(Team IDYCK386LBJ7). - 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
jstool ofmcp__cua_repl, and the same session has no record of running thecua-drivercommand. mcp__cua_replis an MCP server registered by theunified-computer-useplugin bundled in ChatGPT.app, started by Codex’s resident app-server asnode cua-repl.mjsand thennode_repl.- On startup,
node_replimports@oai/cua/tinyskyAltto define thecuaAPI, and computer actions go through@oai/sky/serviceto the IPC socket ofcom.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
SkyComputerUseServiceinCodex Computer Use.appis what listens oncomputeruse.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
- Computer Use | ChatGPT Learn
- Codex for (almost) everything | OpenAI
- Computer-Using Agent | OpenAI
- OpenAI acquires Software Applications Incorporated, maker of Sky | OpenAI
- openai/codex#47378: [macOS Computer Use] Tk clicks use stale global pointer despite correct synthetic event coordinates
- openai/codex#36741: Let explicit user tool/MCP choice override auto-triggered bundled skills
- trycua/cua#2813: feat(cua-driver): ship an OpenAI Codex plugin (MCP + focused skills)
- Connect your agent | Cua Driver
- Inside macOS window internals: how SkyLight enables multi-cursor background agents | trycua
- Reading Cua Driver 0.26.1 Computer Use Internals — Pinning the Window Target and Separating Action Facts from Postconditions
- Notes on Cross-Platform Computer Use Agents for macOS and Windows — Selection with Hermes Agent and cua-driver as of September 2026
- Shortening the Development Cycle by Handing QA Testing to an AI Agent — Design Notes for OpenAI GPT-6 Astra and Computer Use