@swapnoneel/open-gpt
v0.1.0
Published
Agent-native CLI for your own ChatGPT account. Sign in through the browser once, then ask questions and generate images from any terminal or coding agent.
Maintainers
Readme
open-gpt
Your ChatGPT account, from the command line — built for coding agents.
No API key. No per-token billing. The user signs in once through the browser with the account they already pay for, and every call after that runs on their own ChatGPT plan.
npm install -g @swapnoneel/open-gpt
open-gpt login
open-gpt ask "why is this test flaky?" -f test/session.test.tsWhy
Agents — Claude Code, Codex, Antigravity, opencode — increasingly want a second model's opinion, or an image asset, mid-task. The usual answer is an OpenAI API key, which means a second bill and a secret to manage.
open-gpt uses the OAuth flow the official Codex CLI uses, so the model runs on
the subscription the user already has. Usage is billed to them and to nobody
else.
Install
npm install -g @swapnoneel/open-gptOr run it without installing:
npx @swapnoneel/open-gpt ask "hello"Node 18.17+. Zero runtime dependencies — the install is the CLI and nothing else.
Sign in
open-gpt loginOpens your browser, waits for the redirect on localhost:1455, and writes the
tokens to ~/.open-gpt/auth.json with 0600 permissions.
On a machine with no browser — SSH, a container, CI, an agent sandbox — it
detects that and falls back to the device-code flow automatically, printing a
URL and a short code you enter from any other device. Force it with
open-gpt login --device.
Use
# ask
open-gpt ask "explain optimistic locking"
# pipe context in
git diff | open-gpt ask "review this diff"
# attach files
open-gpt ask -f src/auth.ts -f src/session.ts "where can these desync?"
# multi-turn, reasoning carried between turns
open-gpt chat audit "list the risky columns in this schema"
open-gpt chat audit "now propose migrations for the top three"
# images (see the note below on plan support)
open-gpt image "isometric server rack, flat vector" -o rack.png
open-gpt image edit "make it night time" -i photo.png -o night.png
open-gpt image edit "replace the sky" -i room.png --mask sky.png
# what can this account run? (the set depends on the plan — don't hardcode)
open-gpt models
# how much of the plan's allowance is left (costs no quota to check)
open-gpt usage
# sign out
open-gpt logout # forget credentials, keep saved threads
open-gpt logout --all # remove everything, threads included
# why isn't it working?
open-gpt doctorUsage limits
Codex plan limits apply, and you can see exactly where you stand:
$ open-gpt usage
plan free
primary 2% used of a 30 days window · resets in 29d 23hThis spends no model quota, so checking your limits never eats into them. It
exits 5 if a limit is already reached, so a script can gate on it. Plans may
report a long window, a short burst window, or both.
It works two ways, preferring the first:
- A dedicated usage endpoint, when reachable.
- Otherwise the
x-codex-*headers the backend attaches to every request, captured as a side effect of whatever you last ran and cached locally.
The fallback exists because that endpoint rejects some HTTP client fingerprints
— Node's among them, which is what the published CLI runs on. The headers are
present on every runtime, so this path is the reliable one. When a figure comes
from the cache rather than a live lookup, the output says so and --json
reports "source": "headers" with a capturedAt timestamp.
doctor includes the same figure and warns once you pass 90%.
A note on image generation
Image support depends on the ChatGPT plan behind the login, and it is not something this CLI can work around.
The backend recognizes image_generation as a valid tool but silently removes
it from the request for accounts that are not entitled to it — verified against
a live free-plan account, where web_search and function tools are accepted
and echoed back while image_generation is stripped. When that happens you get
a clear error and exit 4:
$ open-gpt image "a lighthouse" -o out.png
error This ChatGPT account cannot generate images through open-gpt.
Image generation is not available on every ChatGPT plan (free accounts
generally cannot). Text commands such as `open-gpt ask` are unaffected.Every text command works regardless of plan.
Built for agents
This is the part that matters if you are wiring open-gpt into a harness.
One JSON object, or nothing. --json prints exactly one object on stdout and
moves every diagnostic to stderr. Streaming, progress, and colour all switch off.
$ open-gpt ask "name three caching strategies" --json
{
"ok": true,
"text": "…",
"model": "gpt-5.5",
"usage": { "inputTokens": 24, "outputTokens": 180, "totalTokens": 204 }
}Failures use the same envelope, so a parser never needs a special case:
$ open-gpt whoami --json; echo "exit=$?"
{
"ok": false,
"error": {
"code": "not_authenticated",
"message": "You are not signed in.",
"exitCode": 3,
"hint": "Run `open-gpt login` to sign in with your ChatGPT account."
}
}
exit=3Exit codes are a contract. Act on the number without parsing anything:
| Code | Meaning |
| --- | --- |
| 0 | success |
| 1 | unclassified failure |
| 2 | bad invocation — unknown command or flag, missing argument |
| 3 | not signed in, or the saved login can no longer be refreshed |
| 4 | upstream rejected or failed the request |
| 5 | rate limited, or the plan's quota is exhausted |
| 6 | could not reach the network |
| 7 | cancelled (SIGINT) or --timeout elapsed |
Self-describing. open-gpt schema emits the entire surface — every command,
flag, type, default, and environment variable — as JSON. An agent can discover
the CLI without being told about it, and an unknown flag is a hard error
(exit 2) rather than a silently ignored argument.
Never interactive. Nothing prompts, ever. login is the only command that
needs a human, and it says so.
stdin and stdout behave. Piped input becomes the prompt when no argument is
given. Answers stream to stdout as plain text; everything else goes to stderr,
so open-gpt ask "…" 2>/dev/null yields a clean answer.
Agent skill
A ready-made skill lives in skills/open-gpt/SKILL.md.
Drop it into your agent's skills directory and it will use the right flags,
check auth before calling, and avoid the sharp edges.
mkdir -p ~/.claude/skills/open-gpt
cp node_modules/@swapnoneel/open-gpt/skills/open-gpt/SKILL.md ~/.claude/skills/open-gpt/→ Full agent integration guide — per-harness setup for Claude Code, Codex, Cursor, Windsurf, opencode, Zed and others, the stability contract, error-handling recipes, and how to make an agent use this well rather than just successfully.
→ Editor & IDE setup — VS Code tasks and keybindings, JetBrains external tools, Neovim mappings, Zed tasks, shell functions, and a commit-message git hook.
Configuration
Every setting has an environment variable, so a harness can configure the CLI without touching files or passing flags.
| Variable | Purpose |
| --- | --- |
| OPEN_GPT_HOME | Where credentials and threads live. Default ~/.open-gpt. |
| OPEN_GPT_ACCESS_TOKEN | Use this token instead of the stored credentials. Never written to disk. |
| OPEN_GPT_ACCOUNT_ID | Account id paired with the above. |
| OPEN_GPT_MODEL | Default model slug. |
| OPEN_GPT_JSON | Force --json on every command. |
| OPEN_GPT_TIMEOUT | Default timeout, in seconds. |
| OPEN_GPT_NO_BROWSER | Never try to open a browser; use the device-code flow. |
| OPEN_GPT_CLIENT_VERSION | Client version reported upstream. Gates the model list. |
| OPEN_GPT_BASE_URL / OPEN_GPT_ISSUER | Point at a different backend or issuer. |
| OPEN_GPT_LOOPBACK_PORT | Port for the browser login redirect. Default 1455. |
| OPEN_GPT_DEBUG | Print error codes, upstream bodies, and stack traces. |
| NO_COLOR | Disable ANSI colour. |
Per-project credentials:
OPEN_GPT_HOME=./.open-gpt open-gpt loginSecurity
- Tokens are written to
$OPEN_GPT_HOME/auth.jsonwith owner-only permissions, via a temp file that is chmod'd before being renamed into place — so the credentials are never briefly world-readable. whoamireports the account, plan, and expiry, and never the token.open-gpt tokenprints a live access token, but only with--yes-i-know, because it is a full-access credential for the ChatGPT account and anything printed there lands in shell history and agent transcripts.- The browser login checks the OAuth
stateon return, so another page on the machine cannot complete someone else's sign-in. The PKCE verifier never leaves the process. logout --allremoves every local file, threads included.
Library use
The CLI is the product, but the engine underneath is importable:
import { createClient, resolveModel } from "@swapnoneel/open-gpt";
const client = createClient();
const model = await resolveModel(client, undefined);
const { text, usage } = await client.respond({
model,
input: "summarize this changelog",
onTextDelta: (delta) => process.stdout.write(delta),
});It reads the same credentials open-gpt login writes, so a script inherits
whatever the user already signed into.
How it works
The ChatGPT backend is not the public API, and it has some non-obvious rules.
open-gpt handles them so callers can send ordinary OpenAI-shaped requests:
- It runs stateless (
store: false), so reasoning is carried between turns as encrypted content that must be explicitly requested viainclude. Threads replay the previous turn's output items to make follow-ups coherent. - Input items must not carry server-side ids, and
item_referenceitems are rejected outright. max_output_tokensis rejected.- The available model set is gated on the
client_versionthe client reports — send a stale one and every model reports as unsupported. Ifopen-gpt modelscomes back empty, that is usually why; setOPEN_GPT_CLIENT_VERSION.
This is not Codex
open-gpt authenticates with the same OAuth client the Codex CLI uses and talks
to the same model-serving endpoint (chatgpt.com/backend-api/codex) — that is
what lets it run on the ChatGPT plan instead of a separate API key. It does
not run the Codex agent. Inspecting what the backend actually configures for
an open-gpt request shows "tools": [] and no sandbox: no apply_patch, no
shell execution, no file-system access, no working-directory awareness. The
sandboxed tool-calling loop lives entirely in the official codex binary, which
this project does not reimplement.
What you get is the same underlying model, billed the same way, with none of the agent scaffolding — a thin client for asking a model a question, not a second coding agent.
Development
bun install
bun test # 128 tests
bun run typecheck
bun run build
bun run dev -- ask "hello"License
MIT © Swapnoneel Saha
This project is not affiliated with, endorsed by, or sponsored by OpenAI. It authenticates users against their own ChatGPT accounts; you are responsible for using it within OpenAI's terms of service.
