Skip to main content

Crate codexwire

Crate codexwire 

Source
Expand description

codexwire: a client for OpenAI Codex’s app-server (codex app-server, JSON-RPC over the child’s stdio), the Codex counterpart to ccwire.

Faithful to Codex’s own protocol: protocol is generated from the pinned Codex’s JSON Schema (scripts/gen-codex-protocol; the schema and its VERSION are in schema/, CODEX_VERSION is the version), rpc frames its messages, client runs the child and matches responses to requests, and auth gives a session its own copy of auth.json. exec runs one-shot codex exec jobs in a private CODEX_HOME.

The README follows (its examples are compiled as doctests).

§codexwire

CI crates.io docs.rs Rust 1.85+ Apache-2.0

A Rust client for OpenAI Codex. Run codex app-server over stdio for typed requests and streaming notifications, or use codex exec --json for a single prompt in an isolated working directory.

Early release, targeting Codex 0.167.0, the stable release used to generate and check the protocol types. The client supports Unix; the protocol crate is platform independent. This project is not affiliated with or endorsed by OpenAI.

CratePurposePackageAPI docs
codexwireTokio client, JSON-RPC framing, child processes, isolated exec runs, auth helperscrates.iorustdoc
codexwire-protocolGenerated serde types, with no process or async runtime dependencycrates.iorustdoc

codexwire::protocol re-exports codexwire-protocol, so applications can keep using the client crate’s existing import paths.

§Install

Rust 1.85 or newer is required:

cargo add codexwire
cargo add tokio --features macros,rt,time

For wire types alone, use cargo add codexwire-protocol --git https://github.com/talaagent/codexwire. After publication, the equivalent registry commands are cargo add codexwire and cargo add codexwire-protocol.

Live examples require the codex CLI on PATH. For authenticated work, sign in with codex login first. Parsing protocol messages and running the normal tests need no login, CLI, or network access.

§Initialize an app-server

Requests use the generated Request trait: each params type supplies its method name and response type. Give calls a timeout; the client does not impose one.

use core::time::Duration;
use codexwire::client::spawn_app_server;
use codexwire::protocol::{ClientInfo, InitializeParams};

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn core::error::Error>> {
    let mut server = spawn_app_server("codex", &[], &[], ".".as_ref())?;
    let params = InitializeParams {
        capabilities: None,
        client_info: ClientInfo {
            name: "example".into(), title: None, version: "0.1.0".into(),
        },
    };
    let init = tokio::time::timeout(
        Duration::from_secs(10), server.client.request(&params),
    ).await??;
    println!("codex: {}", init.user_agent);
    server.client.notify("initialized", None).await?;
    server.client.shutdown().await;
    let status = tokio::time::timeout(Duration::from_secs(10), server.child.wait()).await??;
    if !status.success() {
        return Err(std::io::Error::other(format!("app-server exited with {status}")).into());
    }
    Ok(())
}

Run the complete example:

cargo run -p codexwire --example initialize

For conversations, send ThreadStartParams and TurnStartParams, then consume server.messages. Respond to approval requests explicitly. Notifications and server requests arrive in order. Malformed JSON or UTF-8 becomes ServerMessage::Malformed; once stdout closes, pending and later calls fail with ClientError::Closed. Dropping the child kills it; for graceful cleanup, close the client input and wait for the child under a timeout as above.

§Run a single prompt

Exec uses a fresh private CODEX_HOME and working directory, copies in only auth.json, and copies refreshed credentials back when the original login has not changed. Its default sandbox is read-only and its timeout is five minutes. Use an existing ChatGPT login and a private temporary root:

use codexwire::exec::{Exec, Input};
use std::path::Path;

async fn query(codex_home: &Path, temporary_root: &Path)
    -> Result<(), Box<dyn core::error::Error>>
{
    let run = Exec::new("codex", codex_home, temporary_root)
        .run(&Input { prompt: "Say hello in one sentence.".into(), images: Vec::new() })
        .await?;
    if let Some(error) = run.failure() {
        return Err(std::io::Error::other(error.to_string()).into());
    }
    if run.code != Some(0) {
        return Err(std::io::Error::other(format!("codex exited with {:?}", run.code)).into());
    }
    println!("{}", String::from_utf8_lossy(&run.last_message));
    Ok(())
}

Run the executable with cargo run -p codexwire --example exec -- "Say hello.". It uses CODEX_HOME, or $HOME/.codex, for the existing login. Live runs use your account. The run’s files are removed when Run is dropped; copy generated images out before dropping it if you need to retain them.

§Protocol compatibility

codexwire-protocol::CODEX_VERSION records the CLI version used for codegen. The schema covers the methods and notifications this client currently uses; raw JSON-RPC calls remain available for other methods. Generated enums keep unrecognized values as Unknown(serde_json::Value) (or UnknownValue when the schema already has an unknown variant). A known variant with an incompatible body also falls back to raw JSON; inspect these values when diagnosing drift.

The client starts the CLI with an allowlisted environment. Additional variables are explicit. Stderr has best-effort secret redaction; returned events, prompts, auth files, and raw output can still contain sensitive data. Handle them as application data rather than assuming all output is safe to log.

The auth refresh lock serializes runs within this process. Applications sharing one login across processes must also serialize refresh and copy-back operations. See the auth API.

§Development and releases

The workspace keeps runtime code in crates/codexwire, generated wire types in crates/codexwire-protocol, and the pinned schema and generators at the root. See CONTRIBUTING.md for local checks, fixture scrubbing, schema updates, and release-plz setup. Report bugs in GitHub Issues. Report vulnerabilities privately as described in SECURITY.md.

§License

Apache-2.0. Generated types and the schema derive from OpenAI Codex; attribution is in NOTICE.

For development, nix develop supplies Rust and cargo-nextest. See CONTRIBUTING.md for tests and protocol generation.

Re-exports§

pub use codexwire_protocol as protocol;

Modules§

auth
Codex’s ChatGPT login (<CODEX_HOME>/auth.json) shared with a session’s own CODEX_HOME: copied in before the app-server starts, copied back when Codex refreshed its tokens and nobody else changed the real one since. (The same rule a tool that refreshes the login and copies it back follows.)
client
The app-server client: writes requests, matches responses by id, and hands the server’s notifications and requests to the caller in order. Generic over the transport (the child’s stdio, or an in-process duplex in tests).
exec
One-shot codex exec runs on a ChatGPT login, each in a fresh private CODEX_HOME.
rpc
JSON-RPC framing for the app-server: one JSON object per line, no "jsonrpc" member (Codex leaves it out). Requests go both ways (the server asks for approvals), so a line is one of four shapes.

Constants§

CODEX_VERSION
The Codex version this schema came from (codex --version).

Functions§

user_agent_version
The version in an app-server userAgent (<client>/<version> (...)).