3 releases
Uses new Rust 2024
| 0.1.2 | Apr 13, 2026 |
|---|---|
| 0.1.1 | Mar 23, 2026 |
| 0.1.0 | Mar 23, 2026 |
#580 in Development tools
Used in forza
155KB
4K
SLoC
codex-wrapper
A type-safe Codex CLI wrapper for Rust
Overview
codex-wrapper provides a type-safe builder-pattern interface for invoking the
codex CLI programmatically. It follows the same design philosophy as
claude-wrapper and
docker-wrapper: each CLI
subcommand is a builder struct that produces typed output.
Installation
cargo add codex-wrapper
Quick Start
use codex_wrapper::{Codex, CodexCommand, ExecCommand, SandboxMode};
#[tokio::main]
async fn main() -> codex_wrapper::Result<()> {
let codex = Codex::builder().build()?;
let output = ExecCommand::new("explain this error")
.model("o3")
.sandbox(SandboxMode::WorkspaceWrite)
.ephemeral()
.execute(&codex)
.await?;
println!("{}", output.stdout);
Ok(())
}
Two-Layer Builder Architecture
The Codex client holds shared configuration (binary path, environment,
timeout, retry policy). Command builders hold per-invocation options and call
execute(&codex).
Codex Client
Configure once, reuse across commands:
let codex = Codex::builder()
.env("OPENAI_API_KEY", "sk-...")
.timeout_secs(300)
.retry(RetryPolicy::new().max_attempts(3).exponential())
.build()?;
Options:
binary()-- path tocodexbinary (auto-detected viaPATHby default)working_dir()-- working directory for commandsenv()/envs()-- environment variablestimeout_secs()/timeout()-- command timeoutconfig()-- global config overrides (-c key=value)enable()/disable()-- global feature flagsretry()-- default retry policy
Command Builders
Each CLI subcommand is a separate builder. Available commands:
| Command | CLI Equivalent | Description |
|---|---|---|
ExecCommand |
codex exec |
Run Codex non-interactively |
ExecResumeCommand |
codex exec resume |
Resume a non-interactive session |
ReviewCommand |
codex exec review |
Code review with git integration |
ResumeCommand |
codex resume |
Resume an interactive session |
ForkCommand |
codex fork |
Fork an interactive session |
LoginCommand |
codex login |
Authenticate |
LoginStatusCommand |
codex login status |
Check auth status |
LogoutCommand |
codex logout |
Remove credentials |
McpListCommand |
codex mcp list |
List MCP servers |
McpGetCommand |
codex mcp get |
Get MCP server details |
McpAddCommand |
codex mcp add |
Add stdio or HTTP MCP server |
McpRemoveCommand |
codex mcp remove |
Remove MCP server |
McpLoginCommand |
codex mcp login |
Auth to MCP server |
McpLogoutCommand |
codex mcp logout |
Deauth from MCP server |
McpServerCommand |
codex mcp-server |
Start Codex as MCP server |
SandboxCommand |
codex sandbox |
Run command in sandbox |
ApplyCommand |
codex apply |
Apply agent diff |
CompletionCommand |
codex completion |
Generate shell completions |
FeaturesListCommand |
codex features list |
List feature flags |
FeaturesEnableCommand |
codex features enable |
Enable a feature |
FeaturesDisableCommand |
codex features disable |
Disable a feature |
VersionCommand |
codex --version |
Get CLI version |
RawCommand |
(any) | Escape hatch for arbitrary args |
ExecCommand: The Workhorse
Full coverage of codex exec options:
let output = ExecCommand::new("fix the failing tests")
.model("o3")
.sandbox(SandboxMode::WorkspaceWrite)
.approval_policy(ApprovalPolicy::Never)
.skip_git_repo_check()
.ephemeral()
.json()
.execute(&codex)
.await?;
All ExecCommand options:
| Method | CLI Flag | Description |
|---|---|---|
model() |
--model |
Model to use |
sandbox() |
--sandbox |
Sandbox policy |
approval_policy() |
--ask-for-approval |
Approval policy |
profile() |
--profile |
Config profile |
full_auto() |
--full-auto |
Auto sandbox + approval |
dangerously_bypass_approvals_and_sandbox() |
--dangerously-bypass-approvals-and-sandbox |
Skip all safety |
cd() |
--cd |
Working directory |
skip_git_repo_check() |
--skip-git-repo-check |
Run outside git repo |
add_dir() |
--add-dir |
Additional writable dirs |
search() |
--search |
Enable web search |
ephemeral() |
--ephemeral |
Don't persist session |
output_schema() |
--output-schema |
JSON Schema for response |
color() |
--color |
Color output mode |
progress_cursor() |
--progress-cursor |
Cursor-based progress |
json() |
--json |
JSONL event output |
output_last_message() |
--output-last-message |
Write last message to file |
image() |
--image |
Attach image(s) |
config() |
-c |
Config override |
enable() / disable() |
--enable / --disable |
Feature flags |
oss() |
--oss |
Use local OSS provider |
local_provider() |
--local-provider |
Specify lmstudio/ollama |
retry() |
(client-side) | Per-command retry policy |
JSONL Output Parsing
Use execute_json_lines() to parse structured events from --json mode:
let events = ExecCommand::new("what is 2+2?")
.ephemeral()
.execute_json_lines(&codex)
.await?;
for event in &events {
println!("{}: {:?}", event.event_type, event.extra);
}
Event types include thread.started, turn.started, item.completed,
turn.completed, and more.
Code Review
// Review uncommitted changes
let output = ReviewCommand::new()
.uncommitted()
.model("o3")
.execute(&codex)
.await?;
// Review against a base branch
let output = ReviewCommand::new()
.base("main")
.json()
.execute(&codex)
.await?;
MCP Server Management
// List servers
let output = McpListCommand::new().execute(&codex).await?;
// List as JSON
let servers = McpListCommand::new().execute_json(&codex).await?;
// Add stdio server
McpAddCommand::stdio("my-tool", "npx")
.arg("my-mcp-server")
.env("API_KEY", "secret")
.execute(&codex)
.await?;
// Add HTTP server
McpAddCommand::http("sentry", "https://mcp.sentry.dev/mcp")
.bearer_token_env_var("SENTRY_TOKEN")
.execute(&codex)
.await?;
// Remove server
McpRemoveCommand::new("old-server").execute(&codex).await?;
Sandbox Execution
Run commands inside the Codex sandbox:
let output = SandboxCommand::new(SandboxPlatform::MacOs, "ls")
.arg("-la")
.execute(&codex)
.await?;
Session Management
// Resume the most recent interactive session
ResumeCommand::new()
.last()
.model("o3")
.execute(&codex)
.await?;
// Fork a session to try a different approach
ForkCommand::new()
.session_id("abc-123")
.prompt("try a different approach")
.execute(&codex)
.await?;
Shell Completions
let output = CompletionCommand::new()
.shell(Shell::Zsh)
.execute(&codex)
.await?;
std::fs::write("_codex", &output.stdout)?;
Feature Flags
// List all feature flags
FeaturesListCommand::new().execute(&codex).await?;
// Enable/disable features persistently
FeaturesEnableCommand::new("web-search").execute(&codex).await?;
FeaturesDisableCommand::new("web-search").execute(&codex).await?;
Escape Hatch: RawCommand
For subcommands or flags not yet covered by typed builders:
let output = RawCommand::new("cloud")
.arg("--json")
.execute(&codex)
.await?;
Error Handling
All commands return Result<T>, with errors typed via thiserror:
use codex_wrapper::Error;
match ExecCommand::new("test").execute(&codex).await {
Ok(output) => println!("{}", output.stdout),
Err(Error::CommandFailed { stderr, exit_code, .. }) => {
eprintln!("failed (exit {}): {}", exit_code, stderr);
}
Err(Error::Timeout { .. }) => eprintln!("timed out"),
Err(Error::NotFound) => eprintln!("codex binary not in PATH"),
Err(e) => eprintln!("{e}"),
}
Retry Policy
Configure automatic retries for transient failures:
use codex_wrapper::RetryPolicy;
use std::time::Duration;
let policy = RetryPolicy::new()
.max_attempts(5)
.initial_backoff(Duration::from_secs(2))
.exponential()
.retry_on_timeout(true)
.retry_on_exit_codes([1, 2]);
// Set on the client (applies to all commands)
let codex = Codex::builder().retry(policy).build()?;
// Or override per-command
let output = ExecCommand::new("flaky task")
.retry(RetryPolicy::new().max_attempts(10))
.execute(&codex)
.await?;
Features
Optional Cargo features (enabled by default):
json-- JSONL output parsing viaserde_json(execute_json_lines(),execute_json(),JsonLineEvent)
Testing
cargo test --lib --all-features # Unit tests (no CLI required)
cargo test --test integration -- --ignored # Integration tests (requires codex in PATH)
License
MIT OR Apache-2.0
Dependencies
~7–12MB
~142K SLoC