codex-rpc.ssh.codes · Discord Rich Presence for the OpenAI Codex CLI / Desktop — like claude-rpc but for Codex. Your Discord profile shows "Playing Codex" with live animations, thinking/working/sleeping states, your active model, lifetime token count, and "Get Codex RPC" / "GitHub" buttons. Private mode is enabled by default: project names, paths, and custom details stay hidden on both the card and image hover tooltips.
npx -y github:SSHdotCodes/codex-rpcThat one line installs it and starts it in the background — no terminal
window and starts at login on macOS and Windows. The macOS launch agent also
restarts it if it crashes. Re-run the command any time to update. On Windows,
it also installs a persistent
codex-rpc command in npm's normal global command directory, so commands such
as codex-rpc --help work in a new PowerShell window after the first run.
Starting the updated CLI also removes background copies left behind by older
Windows releases, so only one daemon updates Discord at a time.
If you ran version 1.0.1 on Windows, update once with:
npx -y github:SSHdotCodes/codex-rpcZero dependencies, single file, Node ≥ 18. Works with both the CLI and the
desktop app — no hooks or wrappers. It tails
~/.codex/sessions/**/rollout-*.jsonl locally to detect activity and count
tokens, then sends the resulting presence to Discord with project details
removed when private mode is on.
Private mode hides what the session is about: project names/paths and
custom details text. It removes the project from both the card's details
line and the image hover tooltip. For example,
Codex is pondering • my-private-project • 33% of 5h limit used becomes
Codex is pondering • 33% of 5h limit used.
Animations still change when Codex thinks, works, or sleeps. Generic activity
labels, lifetime token totals, the active model, rate-limit usage, elapsed
time, and buttons remain visible. Your assets, smallImage, showModel,
and showTokens preferences still apply.
Existing configs without a privateMode setting become private after updating
and restarting; only the JSON boolean false opts out.
codex-rpc --private # save private mode and restart the background agent
codex-rpc --no-private # also share project/custom details and restartThese settings persist across login and updates. You can also set
"privateMode": true or "privateMode": false in ~/.codex-rpc.json, then
restart with codex-rpc. Foreground commands accept the same flags for that
invocation only, for example codex-rpc run --no-private or
codex-rpc demo --no-private. status shows the live state and token count,
while hiding project/log names by default; use codex-rpc status --no-private
to include them locally.
To remove the card entirely, use codex-rpc stop.
These generic activity labels and animations remain available in private mode.
| state | shown as | animation | triggered by |
|---|---|---|---|
thinking |
🧠 Thinking… | thinking | reasoning, new prompts, task start, chewing on command results |
coding |
⌨️ Writing code | typing | apply_patch file edits |
reading |
📖 Reading files | typing | cat, head, tail, sed -n, … |
searching |
🔍 Searching | typing | rg/grep/find, web search, browsing tools |
building |
⚙️ Working… | typing | builds, installs, running commands (held while a command runs) |
debugging |
🐛 Debugging | typing | failing test runners / real command errors |
success |
✅ Task complete! | sleeping | task_complete (lingers ~3 min, then sleeps) |
error |
typing | failed patches, stream errors, aborted turns | |
sleeping |
😴 Sleeping | sleeping | no activity for 5 min, or Codex not running |
deploying |
🚀 Shipping it | typing | git push, rsync/scp, publish/deploy commands |
Three seamless ~19s loops cover everything — thinking while Codex
reasons, typing while it edits files and runs commands, sleeping when
it's idle or not running — so a long build or think just keeps looping
cleanly. The state line also shows your lifetime Codex token usage
(summed across every session in ~/.codex/sessions, updated live), and
hovering the art shows your 5-hour rate-limit usage. Set
clearWhenQuit: true to hide the presence entirely when Codex is closed
instead of showing 😴, and showTokens: false to hide the token counter.
None, out of the box. A shared "Codex" Discord application id is baked in (app ids are public identifiers, not secrets — same model as claude-rpc), and the animations are served from this repo's raw GitHub URLs — Discord only animates presence images that come from external URLs (it flattens uploaded art assets to static PNGs). Just run it. The "Playing X" headline is the Discord application's name — so it reads "Playing Codex".
Using your own Discord application instead
-
Create a Discord application at https://discord.com/developers/applications → New Application. The application name is the "Playing …" headline on your profile.
-
Upload the animations: in your app → Rich Presence → Art Assets → upload everything in
assets/(10 GIFs +codex.png). Keep the file names as the asset keys:thinking,coding,reading,searching,building,debugging,success,error,sleeping,deploying,codex. Assets can take a few minutes to propagate. -
Save your client id (the Application ID on the app's General page):
codex-rpc setup --client-id 123456789012345678
Run npx -y github:SSHdotCodes/codex-rpc to install/update and start the
background agent.
codex-rpc # start in the background (auto-starts at login)
codex-rpc stop # stop it codex-rpc uninstall # remove agent
codex-rpc logs # tail the daemon log
codex-rpc run # run in the foreground instead (--dry: no Discord)
codex-rpc demo # cycle all states every 12s
codex-rpc set success # hold one state manually
codex-rpc status # print live state/tokens (--no-private includes project)
codex-rpc doctor # sanity-check: node, sessions dir, Discord socket, config
codex-rpc clear # wipe the presenceThe details line shows the active model (from the live session). Both modes
carry two buttons — "Get Codex RPC" and "GitHub" (Discord
never shows you your own buttons; ask a friend or use a second account).
Both are configurable (showModel, buttons in ~/.codex-rpc.json).
The Discord desktop app must be running on the same machine (presence goes over Discord's local IPC socket). If Discord restarts, codex-rpc reconnects on its own.
Flags: --private, --no-private, --client-id, --details "<second line>",
--codex-home, --sleep-after <sec>, --dry (no Discord, log output only).
With private mode off, the card's second line is details · project · model
(details empty by default). The "Playing X" headline is the Discord app name,
not details.
Persistent config lives in ~/.codex-rpc.json:
{
"privateMode": true,
"clientId": "123456789012345678",
"details": "",
"sleepAfterSec": 300,
"successHoldSec": 180,
"smallImage": "codex",
"assets": { "thinking": "https://example.com/custom-thinking.gif" }
}assets entries may be uploaded asset keys or https URLs (Discord proxies
external images).
cat > ~/Library/LaunchAgents/codes.ssh.codex-rpc.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>codes.ssh.codex-rpc</string>
<key>ProgramArguments</key>
<array><string>/usr/local/bin/node</string><string>/Users/YOU/codex-rpc/codex-rpc.js</string></array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict></plist>
EOF
launchctl load ~/Library/LaunchAgents/codes.ssh.codex-rpc.plist(Adjust the node path — which node — and your username.)
Codex appends every session event to a rollout JSONL file. In both modes,
codex-rpc finds the newest logs (rescanning every 5s, so new sessions are
picked up automatically), tails them, and classifies each event — reasoning → thinking, apply_patch →
coding, exec_command by its command string, task_complete → success, etc.
Heartbeat events (token counts, streamed messages) keep the current state
alive; silence rolls over to 😴 after sleepAfterSec.
Run npm test to check privacy defaults, config persistence, Discord payloads,
live animations, and token updates using isolated fixtures and a simulated
Discord socket.
The animation sources (1024×1024 15s loop MP4s + the renderer that made them)
live in ~/codex-animations/ — re-render with python3 render.py all, then
regenerate the GIFs with the ffmpeg one-liner in that folder's history.