pro-cli/explained
a field manual · v0.1.0
cdp :9222 OK
Field Manual · 2026.05.21

How pro-cli
actually works.

pro-cli does not call the OpenAI API. It puppets a real, logged-in Chrome window over a localhost debug socket — and Chrome is the one that talks to ChatGPT. This manual walks the entire path, with diagrams.

live install · this machine
binary
~/.bun/bin/pro-cli
version
0.1.0
runtime
Bun 1.2.20
chrome pid
17184
cdp port
:9222
cookies stored
41 across 5 domains
plan
ChatGPT Team
default model
gpt-5-5-pro

$ pro-cli doctor --json  → ready: true

5
actors in the loop
you · pro-cli · ~/.pro-cli · chrome · chatgpt.com
1
tcp port (localhost)
127.0.0.1:9222 — chrome devtools protocol
0
openai api keys used
your chatgpt web session is the auth
20+
models exposed
pro · thinking · instant · deep research · agent
§ 01 / 06

The CDP
roundtrip.

Five actors sit between your keystroke and the Pro answer. Only one of them — Chrome — is allowed to cross the network boundary into the public internet. That single fact is the whole trick.

Figure 1.1 · System architecture
local process cdp transport chrome (your browser) https · public internet
PUBLIC INTERNET → ← YOUR MAC YOU · TERMINAL $ pro-cli ask "…" --json ↳ stdout JSON envelope PRO-CLI · BUN PROCESS ~/.bun/bin/ pro-cli • reads ~/.pro-cli/ • opens CDP socket • marshals JSON to stdout FILESYSTEM ~/.pro-cli/ cookies/ tokens/ chrome-profile/ jobs.sqlite CHROME · DEDICATED PROFILE Google Chrome pid 17184 · --remote-debugging-port=9222 --user-data-dir=~/.pro-cli/chrome-profile CDP ENDPOINT ws://127.0.0.1:9222 /devtools/browser/<guid> CHATGPT TAB · LOGGED IN chatgpt.com session cookies live here fetch() runs INSIDE this page → Pro models, web search, canvas… CHATGPT.COM OpenAI /backend-api/ f/conversation SSE stream out argv stdout read · write CDP method calls → ← CDP events (results, console, network) HTTPS · POST SSE response stream THE TRICK Chrome makes the HTTPS call. pro-cli never speaks to OpenAI.
Why this matters

Your auth lives in ~/.pro-cli/cookies/ as ChatGPT session cookies — not as an API key. There is no API key. The web app's auth model is the auth model.

Why Chrome (not headless)

ChatGPT's frontend assembles request headers, Cloudflare challenges, and websocket sessions from inside the live page. Running them from a real, logged-in Chrome tab keeps the fingerprint identical to a human user.

Common misread

"pro-cli" sounds like a Pro API client. It isn't. Closer to "a remote control for the ChatGPT tab you already have open."

§ 02 / 06

What lives
where.

Two places hold pro-cli on disk. One is the source code & binary (rebuildable). The other — ~/.pro-cli/ — is the irreplaceable state: your session cookies, your sandboxed Chrome profile, and the durable-job database.

Figure 2.1 · On-disk map
/Users/adeelafzal/
├─ Projects/pro-cli/ — source clone, rebuildable
│  ├─ src/ …typescript
│  ├─ scripts/install.sh
│  └─ package.json → bun link target
├─ .bun/bin/
│  └─ pro-cli — symlink put on PATH by `bun link`
└─ .pro-cli/ — state. irreplaceable.
   ├─ cookies/
   │  ├─ chatgpt.json sensitive
   │  └─ chatgpt.txt (Netscape jar)
   ├─ tokens/
   │  └─ chatgpt-session.json sensitive
   ├─ chrome-profile/ — --user-data-dir target
   │  ├─ Default/ (Chrome's own profile guts)
   │  └─ Local State
   └─ jobs.sqlite — async-job DB
/tmp/pro-cli-501-ead47f28dc98/
├─ daemon.json — localhost control endpoint
└─ daemon.log — rolling stdout/stderr
code · rebuildable state · sensitive chrome sandbox on PATH
~/Projects/pro-cli/

The git checkout. Edit it, blow it away, re-clone it — none of that touches your auth. pro-cli update is just git pull --ff-only here plus a re-link.

~/.bun/bin/pro-cli

A symlink Bun's bun link drops into your PATH. Resolves into the Projects clone. That's how typing pro-cli finds it from anywhere.

~/.pro-cli/cookies/
0600

Your live ChatGPT web session, frozen to disk. 41 cookies across chatgpt.com, auth.openai.com, sentinel.openai.com, etc. Never paste, commit, or share these.

~/.pro-cli/tokens/

The ChatGPT session token (bearer-ish JWT). Current token expires 2026-05-31; re-capture via pro-cli auth capture when it lapses.

~/.pro-cli/chrome-profile/

A throwaway Chrome user-data-dir whose only job is to hold the ChatGPT login. Keeping it separate means the open debug port can't snoop your personal browsing.

~/.pro-cli/jobs.sqlite

Async-job ledger. Direct pro-cli ask calls run as transient jobs (temporary: true) and don't persist. pro-cli job create calls do.

§ 03 / 06

Anatomy of one ask.

A real timeline. The example below is the exact call you just ran — "What's the best way to learn Rust in 2026?" against gpt-5-5-pro, 8,140 characters back, 4 minutes 5 seconds elapsed.

The call being traced
# Figure 3.0 — the one-liner
$ pro-cli ask "What's the best way to learn Rust in 2026?" --json
→ job ask_6ad14479… · model gpt-5-5-pro · reasoning standard · 4m 5s · 8140 chars
Figure 3.1 · Sequence trace
time flows  · T+0 = you press Enter
YOU terminal PRO-CLI bun process ~/.pro-cli filesystem CHROME cdp · :9222 · chatgpt tab CHATGPT.COM openai backend T+0s T+0.05s T+0.10s T+0.20s T+0.35s T+0.7s T+1.2s T+30s T+3m 50s T+4m 5s argv + stdin `ask "…" --json` read cookies + token cookies/chatgpt.json · tokens/chatgpt-session.json 41 cookies returned WS connect · ws://127.0.0.1:9222/devtools/browser/<guid> Target.getTargets → find chatgpt.com tab Target.attachToTarget · enable Page/Network/Runtime sessionId issued Runtime.evaluate · fetch('/backend-api/f/conversation', {…}) runs INSIDE the ChatGPT tab — auto-attaches cookies + headers HTTPS POST /backend-api/f/conversation model: gpt-5-5-pro · reasoning: standard · stream: true SSE stream opens delta events · search tool calls · reasoning Network/Runtime events ↑ stream back to pro-cli accumulate partial deltas · update job record in jobs.sqlite stream closes · final delta 8140 chars · citation markers `turn…view0` inline stdout · {ok:true, data:{job, result, …}} your agent parses it NET BOUNDARY Only steps 6 + 7 + 9 leave your Mac.
~0.7s
setup
cookie read · CDP attach · tab discovery
~1s
injection
Runtime.evaluate fetch into the chat tab
~3m 50s
model + tools
gpt-5-5-pro reasoning + live web search (citation `turn…` markers)
~0.1s
teardown
last delta → marshal JSON → stdout
Where the auth happens

The fetch() in step 5 runs inside the ChatGPT page context. That means Chrome auto-attaches the same cookies, CSRF tokens, and frontend headers that the ChatGPT UI would have sent if you'd clicked Send yourself. pro-cli never builds an Authorization header.

Where it can break

Three brittle points: Chrome closes (CDP socket dies on step 3) · session cookies expire (ChatGPT returns 401 on step 6) · the daemon at /tmp/pro-cli-501-…/ can't be reached (only matters for job create, not direct ask). pro-cli doctor covers all three.

§ 04 / 06

Where does
the chat live?

Every pro-cli ask is, by default, a Temporary Chat. It consumes Pro quota, returns a real answer, and then evaporates — no sidebar entry, no memory contribution, no thread continuity. That's deliberate.

Figure 4.1 · The fork
default · temporary --save · persists
THE CALL pro-cli ask "…" one fetch · two destinies flag check (default) or --temporary ENDPOINT /backend-api/f/conversation the "f/" = fork / fast / temporary OUTCOME · TEMPORARY runs · returns · gone ✗ not in chatgpt.com sidebar ✗ doesn't feed ChatGPT memory ✗ no thread continuity ✓ still consumed Pro quota ✓ real Pro answer in stdout when you pass --save ENDPOINT /backend-api/conversation the persistent one OUTCOME · PERSISTED lands in your sidebar ✓ visible at chatgpt.com ✓ contributes to memory ✓ returns conversationId ✓ threadable via --conversation ⚠ adds to your real history
Why this default?

pro-cli is built for agentic use — scripts, Claude Code, cron jobs. If every call wrote into your real history, your sidebar would fill with bot-junk in a day and ChatGPT's memory would start "learning" from automated prompts. Temporary is the polite default.

How to opt in

Pass --save. The response surfaces a conversationId + messageId you can grab and pass back via --conversation + --parent to thread follow-ups in the same chat.

Same toggle, GUI version

If you've used the Temporary Chat button on chatgpt.com (the icon next to the model picker), it's the same feature. pro-cli just flips that switch programmatically on every call.

the rule of thumb

One-shot agent queries → leave it temporary.
Conversations you want to come back to → --save, then thread with --conversation + --parent.

§ 05 / 06

Why not just
use the API?

The OpenAI API and pro-cli reach different ChatGPT. The API is the developer surface — clean, billed per token, no Pro models. pro-cli is the consumer surface — your account, your Pro quota, your Deep Research and web search. They aren't substitutes; they're complements.

Path A · OpenAI API
3 actors
api.openai.com
PUBLIC INTERNET → YOU · SCRIPT your code OPENAI_API_KEY=sk-… OPENAI api /v1/chat/... HTTPS · Bearer sk-… SSE delta stream

Two-hop call. Stateless. Your script owns auth. Billed per input/output token. Models capped at the API catalog — no gpt-5-5-pro, no Deep Research, no canvas.

Path B · pro-cli
5 actors
chatgpt.com (via Chrome)
PUBLIC INTERNET → YOU $ ask PRO-CLI bun ~/.pro-cli cookies CHROME · :9222 dedicated profile chatgpt tab logged-in CHATGPT openai backend-api

Five-hop call. Only the last hop crosses the public internet. Chrome owns auth. Billed against your ChatGPT plan quota. Full Pro catalog including Deep Research, web search, canvas.

Figure 4.1 · Dimension-by-dimension
dimension
OpenAI API
api.openai.com
pro-cli
your ChatGPT account
Auth
An API key (sk-…) you generate in the dashboard. Stateless.
Session cookies + a session token captured from a real Chrome login. Same as if you were using ChatGPT.com.
Billing
Per input + output token. Charged to your OpenAI dev account.
Counts against your ChatGPT Team plan quota — the same caps you see when ChatGPT.com tells you "you've hit your weekly Pro limit."
Statefulness
Stateless HTTP. Each request is independent. (Threads via the new Responses API are opt-in.)
Drives a real, persistent browser tab. Conversations can thread via --save --conversation … --parent …
Models available
The API catalog. No Pro variants. No Deep Research as a model. No agent mode.
Everything your account sees in the ChatGPT picker: gpt-5-5-pro, gpt-5-5-thinking, research, agent-mode, o3, gpt-4.5…
Tools / features
Function calling, code interpreter, file uploads, retrieval — all explicit, programmer-managed.
Whatever ChatGPT's frontend exposes: web search, canvas, image gen, voice, browsing. Implicit. The model decides.
Failure mode
401 · 429 · insufficient_quota. Easy to diagnose.
CDP_UNAVAILABLE (Chrome closed) · session expired (logged-out on chatgpt.com) · plan quota exhausted. Run pro-cli doctor --json to triage.
Best for
Production systems. Multi-tenant apps. Predictable billing. Fine-grained tool orchestration.
Personal agents that benefit from Pro reasoning + Deep Research, when you already pay for ChatGPT and want your existing quota usable from the terminal.
the mental model

pro-cli isn't a Pro API client.
It's a remote control for the ChatGPT tab you already have open — and the tab itself is the one making the call.

§ 06 / 06

In your
terminal.

Five real scenarios. The shape of output below is faithful to pro-cli 0.1.0 (abbreviated for screen size). One thing to internalize: every command emits a JSON envelope to stdout — pipe it to jq, hand it to another agent, store it.

Scenario 01 · the main event
A real Pro call with web search.
~4 min · 8,140 chars · gpt-5-5-pro
adeelafzal@mac · ~ · zsh · 100×30
➜ ~ pro-cli ask "What's the best way to learn Rust in 2026?" --json
# …4 min 5 s of Pro reasoning + live web search…
{
  "ok": true,
  "data": {
    "job": {
      "id": "ask_6ad14479-df06-4cbd-a5a9-75742732895d",
      "status": "succeeded",
      "model": "gpt-5-5-pro",
      "reasoning": "standard",
      "options": { "temporary": true }
    },
    "result": "## Best 2026 path: 7–9 weeks, ~70–100 hours\n\nAssuming you already…",
    "resultStats": { "chars": 8140, "approximateTokens": 2035 }
  }
}
➜ ~

Notice "options.temporary": true — that's why this never showed up in your chatgpt.com sidebar. See § 04.

Scenario 02 · is it wired?
free · 0 quota
~ · doctor
➜ ~ pro-cli doctor --json | jq '.data | {auth, browserSession, ready}'
{
  "auth": {
    "status": "present",
    "cookieCount": 41,
    "tokenExpiresAt": "2026-05-31"
  },
  "browserSession": {
    "status": "present",
    "cdpBase": "http://127.0.0.1:9222"
  },
  "ready": true
}
➜ ~

Run this before anything else. Never burns Pro quota.

Scenario 03 · calibrated probability
~30s · gpt-5-5-pro
~ · odds
➜ ~ pro-cli odds "Will Rust 2027 edition ship in 2027?"
62
 
# or with --json for the full envelope
➜ ~ pro-cli odds "…" --samples 5 --aggregate median --json
{
  "ok": true,
  "data": {
    "probability": 62,
    "probabilityRaw": 61.4,
    "attempts": [ /* 5 sample runs */ ]
  }
}
➜ ~

Bare integer to stdout by default — prob=$(pro-cli odds "…") just works in shell.

Scenario 04 · I want this in my chat history
Saving and threading.
--save
~ · save + thread
# 1. start a real, saved conversation
➜ ~ pro-cli ask "Explain Rust lifetimes" \
    --save --json | jq '.data.job | {options, conversationId}'
{
  "options": {
    "temporary": false,
    "conversationId": "c7e1d9a3-9f2a"
  }
}
 
# 2. continue that exact thread
➜ ~ pro-cli ask "now show borrow checker errors" \
    --save \
    --conversation "c7e1d9a3-9f2a" \
    --parent "msg_4d2b…" --json
➜ ~
ChatGPT
chatgpt.com
Today
Explain Rust lifetimes
↳ now show borrow checker errors
just appeared from `pro-cli --save`
Q4 planning notes
Tailwind v4 migration ideas
Yesterday
SSH config for the Hetzner box
Sourdough hydration tweaks
↑ saved pro-cli call appears here.
the temporary Rust question from §03 didn't.
Scenario 05 · when Chrome died
The most common failure (and the fix).
CDP_UNAVAILABLE
~ · recovery flow
➜ ~ pro-cli ask "…" --json
{
  "ok": false,
  "error": {
    "code": "CDP_UNAVAILABLE",
    "message": "Cannot connect to Chrome CDP at http://127.0.0.1:9222.",
    "suggestions": [
      "Open Chrome with --remote-debugging-port=9222."
    ]
  }
}
 
# Fix: relaunch the dedicated Chrome window
➜ ~ open -na "Google Chrome" --args \
    --user-data-dir=~/.pro-cli/chrome-profile \
    --remote-debugging-port=9222 \
    https://chatgpt.com/
 
# Verify with the free check before retrying
➜ ~ pro-cli doctor --json | jq '.data.ready'
true
➜ ~

If CDP_UNAVAILABLE persists after relaunch, your ChatGPT session probably expired — sign back in and run pro-cli auth capture --cdp http://127.0.0.1:9222 --json.

appendix · operations

Day-to-day cheatsheet.

The five commands that cover ~95% of usage, plus the two recovery paths for when something snaps.

free · safe
$ pro-cli doctor --json

Health check. Never consumes Pro quota. Always your first diagnostic.

consumes quota
$ pro-cli ask "…" --json
$ pro-cli ask @prompt.md \
    --reasoning extended --json

Direct blocking call. Default model gpt-5-5-pro.

durable
$ pro-cli job create \
    @prompt.md --wait --json
$ pro-cli job list --json

Persisted in jobs.sqlite, survives terminal exit.

probability
$ pro-cli odds "…?" --json

Calibrated 0–100 probability of YES. Different code path than ask.

housekeeping
$ pro-cli models --json
$ pro-cli limits --json
$ pro-cli update  --json

List models · check plan/usage · fast-forward your local clone.

recovery · chrome died
# relaunch the dedicated profile
$ open -na "Google Chrome" --args \
   --user-data-dir=~/.pro-cli/chrome-profile \
   --remote-debugging-port=9222 \
   https://chatgpt.com/

# re-capture if you got logged out
$ pro-cli auth capture \
    --cdp http://127.0.0.1:9222 --json
field manual compiled 2026-05-21 · macOS 24.6.0 · bun 1.2.20 · pro-cli 0.1.0