An unofficial community-maintained OpenAI CLI and Go wrapper for the Codex CLI.
Requires Go 1.27 or newer.
go install github.com/picatz/openai/cmd/openai@latestFor API commands, set OPENAI_API_KEY in your environment. Obtain a key from the OpenAI platform. Requests use your account and may incur API charges. Never commit keys or paste them into bug reports. openai codex uses the separately installed Codex CLI's authentication.
With a terminal attached, openai or openai responses chat opens the Bubble Tea v2 Responses chat. openai chat opens the same interface using Chat Completions. One-shot commands and piped input use plain stdout without terminal control sequences:
openai responses create 'Explain Go contexts'
printf 'Explain Go contexts' | openai
cat question.txt | openai responses create -
openai responses create 'Explain Go contexts' --stream
openai responses create 'Explain Go contexts' --output json
openai responses create 'Explain Go contexts' --stream --output json
openai responses get resp_123 --output json
openai responses delete resp_123--stream prints text deltas as they arrive. With --output json, it emits one complete API response after successful completion, including usage and response ID. Failures go to stderr with a nonzero exit code; canceled commands exit with code 130. A truncated, failed, or incomplete stream is an error, even if partial text has already reached stdout.
One-shot creation sets store: false. Retrieval/deletion requires an existing response stored by another request. Web search is off by default for one-shot requests; add --web-search to enable it. The new interactive Responses chat also uses store: false, keeps native output items for same-endpoint replay, and only enables web search when requested. --legacy retains the previous temporary server-storage and web-search behavior and attempts bounded deletion on exit.
Migration: responses get now retrieves a response by ID. To generate a response from a prompt, replace the old responses get 'prompt' form with responses create 'prompt'. The removed Assistants commands have been deprecated in favor of Responses.
HTTP API commands use the SDK client configuration below. Live uses a separate WebSocket client with its own model and session settings.
OPENAI_API_KEY: API authenticationOPENAI_MODELor--model: text model (defaultgpt-4o, retained for compatibility)OPENAI_BASE_URLor--base-url: alternate API base URLOPENAI_API_URL: supported legacy base URL alias, only used whenOPENAI_BASE_URLis absentOPENAI_ORG_IDandOPENAI_PROJECT_ID: SDK organization/project options--timeout: HTTP API request timeout (default2m;0disables it). Live uses--durationinstead.
Flags take precedence over environment variables. The image command has its own image-model flag. --help and shell completion do not require credentials.
openai chat --legacy keeps the previous Chat Completions terminal and Pebble history format. Add --temporary for its memory-only history. Existing history at ~/.openai-cli-chat-pebble-storage-cache is preserved; no migration or automatic deletion is performed.
An OpenAI-compatible Chat Completions server can be selected with:
OPENAI_MODEL='your-local-model' OPENAI_BASE_URL='http://localhost:11434/v1/' openai chatResponses support depends on the chosen server. Both openai chat 'prompt' and openai responses create 'prompt' accept piped input, text/JSON output, and streaming. There is no automatic fallback or translation between the two APIs. Chat --stream --output json emits a normalized {id,text,usage} result; nonstream JSON preserves the full API response. Tool calls in the text-only Chat interface are rejected explicitly. The legacy terminal and image interfaces retain their existing output behavior.
The responsive Bubble Tea v2 UI has a multiline composer, scrollback, request status, response IDs, token usage, and a local session picker.
- Enter sends; Alt+Enter, Shift+Enter, or Ctrl+J adds a line
- PgUp/PgDown scroll; Ctrl+Home/Ctrl+End jump to the beginning/end
- Ctrl+C cancels the current request and restores its draft; when idle it clears the draft or exits
- Ctrl+O selects a saved session for the same API and endpoint; Ctrl+N starts a new one
- Ctrl+S retries a failed history save; Ctrl+Q quits (asks again before discarding an unsaved reply)
The UI saves successful turns locally under the OS configuration directory (openai/sessions). Use --history-dir to choose another directory or --temporary to disable saving. Files are created with owner-only permissions on Unix. Session history contains prompts and replies, so protect it as private data. The previous Pebble history is untouched and remains accessible with openai chat --legacy.
Responses sessions retain the full native output items, including unknown fields and encrypted reasoning when supplied, rather than rebuilding history from display text. Sessions are bound to their API and endpoint; the CLI refuses to send an existing session to another endpoint. No credentials are stored with sessions. A failed/canceled request does not append to saved history. Atomic writes and revision checks prevent silently overwriting another process's changes.
The same sessions work without the UI:
openai responses create 'Start a discussion' --session new
openai responses create 'Continue it' --session SESSION_ID
openai chat 'Start a local-model discussion' --session new --model your-model
openai sessions list
openai sessions show SESSION_ID --output jsonText mode prints the saved session ID on stderr. Session mode JSON emits {session_id,response_id,text,usage,saved}. Resume with the original model, API command, and base URL. Only successful responses are committed; save failures report an error and leave the reply available in output/the UI. Provider support for native item replay varies; unsupported features surface as API errors, without silently stripping state or switching providers.
--legacy preserves the former terminal commands, including file/URL/clipboard expansion and Codex delegation. Those shortcuts are not automatically interpreted by the new text-focused UI.
Run a foreground turn with a separately installed and authenticated Codex CLI:
openai codex exec 'Summarize this repository'
cat question.txt | openai codex exec -
openai codex exec --cd ./project --timeout 5m --output json 'Explain the test layout'
openai codex exec --resume THREAD_ID 'Explain the next step'Text output is the final response; JSON includes thread_id, final_response, and usage. Codex selects its model unless you pass --model. Its configured permissions are unchanged. This non-interactive command cannot answer approval requests. See the CLI guide for input, configuration, deadlines, and limitations. Runs may incur charges.
The codex package wraps codex exec --json, with resumable threads, structured output, cancellation, and forward-compatible events. It requires a separately installed Codex CLI. See the package guide for permission options, compatibility scope, and stream cleanup.
go test ./...
go test -race ./...
go vet ./...
go build ./cmd/openaiThe default suite is hermetic: API calls use synthetic transports and Codex process tests execute the test binary as a fake helper. No API key, installed Codex, microphone, or paid model access is needed. CI runs those checks on Linux, macOS, and Windows.
Historical live integration examples are separately gated. Running them requires both the integration build tag and OPENAI_LIVE_TESTS=1, plus deliberately configured credentials and an installed Codex CLI. They can incur charges, clone external repositories, and let Codex change temporary checkouts. They are not part of CI. Never enable them merely to run the normal test suite.
This modernization is staged: the CLI/SDK/test foundations and Bubble Tea interface precede provider proxy and newer API capabilities. Live-service and real-device verification is separate from mock coverage.
The native Decisions endpoint evaluates typed predicates, choices, and ordered scores. This first interface accepts text, including stdin; it does not translate Decisions into Chat/Responses requests on other providers.
openai decisions predicate 'This item is damaged' --question 'Is damage reported?'
openai decisions choice 'I need a refund' --question 'Which team should help?' \
--choice 'billing=Payments and refunds' --choice 'support=Product usage'
openai decisions score 'The service is unavailable' --question 'How severe is the issue?' \
--level 'low=Minor inconvenience' --level 'high=Work is blocked' --output jsonThe default is gpt-6-luna, the model currently documented for the public beta. Predicate values are probabilities, choice values select one supplied option, and scores are probability-weighted averages of zero-based level indices. JSON output retains the complete probability distributions and unknown response fields. Confidence is a model estimate, not a guarantee; validate thresholds against your own labeled data. Availability and beta behavior can change; see the official Decisions guide.
openai audio transcribe recording.wav
openai audio transcribe recording.wav --output json
openai audio speech 'Hello from the CLI' --file speech.mp3
printf 'Read this aloud' | openai audio speech --voice cedar --format wav --file speech.wav
openai audio speech 'Hello' --format pcm --file - > speech.pcmTranscription defaults to gpt-transcribe; speech defaults to gpt-4o-mini-tts with the marin voice. Both can be selected with the subcommand's --model. Transcription accepts a regular file up to 25 MB. Speech accepts at most 4096 characters and supports MP3, Opus, AAC, FLAC, WAV, and PCM. Raw PCM is not a WAV container.
Speech output never overwrites an existing file. A complete owner-readable temporary file is published without replacing another writer's destination; request or write errors remove the temporary data. Binary stdout must be redirected away from a terminal. These commands do not record from a microphone or play audio automatically. Make clear to listeners that generated speech is AI-generated. See file transcription and text to speech for model-specific capabilities.
The tests use in-memory mock HTTP transports and a generated silent WAV fixture. No recording, playback, real API call, or paid-service validation is part of the default tests. GPT-Live is a separate session protocol and is not emulated by combining these file-audio commands.
openai live is an explicit, headless file-audio client for the GPT-Live primary WebSocket protocol. It does not open a microphone, play speakers, or use the different Realtime API event protocol.
Live accepts the shared API key, organization, project, and base URL configuration, plus --output/-o for its final report. Its local --model selects the Live voice model rather than the text model. Use --duration to limit the session; the HTTP-only --timeout flag does not apply. Explicitly supplying --timeout, --legacy, --temporary/-t, --history-dir, or --session is an error, even with an empty, false, or default value. Live does not use local chat history. Keep --stream and --web-search disabled.
openai live --input-pcm input.pcm --output-pcm reply.pcm \
--sample-rate 24000 --duration 30s --listen-after 5s --output jsonBefore starting a session, add --check to inspect the same explicit paths and configuration offline:
openai live --check --input-pcm input.pcm --output-pcm reply.pcm \
--sample-rate 24000 --duration 30s --listen-after 5s --output jsonThis requires no credentials and makes no network connection. It reads only the selected regular input file's metadata and at most 12 header bytes, rejects the same invalid configuration and obvious WAV/size/sample-boundary errors as a normal run, and checks that output/log paths are new with existing parent directories. It creates no files and does not read --controls input. Both PCM paths remain required; omit --check to start the session. Text and JSON reports describe the selected settings and explicitly leave credentials, service/model/voice availability, output write permissions, and hardware unverified. Raw PCM's actual encoding, channels, sample rate, and audio quality cannot be inferred from these checks; the format is an assumption, and --sample-rate does not resample the input. Successful checks do not guarantee that a later session or file write will succeed.
Input/output are raw mono signed 16-bit little-endian PCM at 16 or 24 kHz, not WAV containers. Convert/resample input beforehand; changing --sample-rate does not resample bytes. Input is paced in 20 ms frames. After EOF, the client sends silence for --listen-after, then requests a close. The explicit duration limit can cut off a reply; GPT-Live has no output-audio-done event to infer when it is safe to stop listening.
Defaults: gpt-live-1, voice marin, a gpt-6-luna Responses backend with a 1024 output-token cap, a 1-minute session duration (maximum 10 minutes), and 5 seconds after EOF. No backend tools or server recording storage are configured. Voice is billed by duration, with backend model usage charged separately. The transport has 15-second startup/finalization bounds, no automatic reconnect/retry, capped frames/usage metadata, and same-origin-only authentication: redirects are rejected, and non-TLS connections are limited to loopback.
Add --controls to read line commands from stdin:
mute: stop forwarding file samples locally and request server muteunmute: resume only after the matching server acknowledgmentclose: stop input and finalize the session, then save successful output
Muting input does not stop backend work or assistant speech. Ctrl+C requests graceful finalization but returns cancellation and does not publish the output file. Other errors also discard temporary audio. A successful explicit/EOF/duration close publishes a complete new file without replacing existing data. Microphone capture, playback interruption, and hardware/device integration are later work, not implied by these controls.
Control/transcript/backend events are logged only when --event-log events.jsonl selects a new private regular file; audio remains in its selected PCM file. A slow event consumer triggers bounded shutdown rather than blocking the session. An unread stderr pipe cannot block the Live protocol loop. JSON stdout reports whether final usage was confirmed and whether audio was saved. session.usage.updated is cumulative, and session.closed supplies the final voice duration; backend token usage is kept separately. A socket failure or timeout before session.closed is reported as unconfirmed finalization, not successful completion.
Tests cover synthetic PCM, protocol ordering, acknowledged mute/unmute, cancellation/backpressure, final-usage collection, truncation/timeouts, and loopback WebSocket/auth/redirect contracts. Real GPT-Live service behavior, audio quality, microphone access, and playback are untested. See GPT-Live WebSockets and session lifecycle.
Run openai codex tui --cd ./project in a terminal to open a fresh thread using
the separately installed Codex app-server. Enter sends a prompt, Alt+Enter or
Ctrl+J inserts a newline, F6 switches composer/workspace focus, Ctrl+C requests
interruption of a known active turn, and Ctrl+Q closes the local session.
The current-turn timeline shows streamed messages, command output, file patches,
plans, and diffs. Installed Codex configuration and persistence remain in effect;
turns may incur charges. Approval/input requests are rejected with a visible
notice, so use the native Codex client for those workflows. No existing history
is read. --no-color (or nonempty NO_COLOR) and --reduced-motion are available.
See the runtime contract and
workspace controls for boundaries and synthetic-only
verification. The existing openai codex exec command is unchanged.