feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
|
// Forbid accidental stdout/stderr writes in the *library* portion of the TUI.
|
|
|
|
|
|
// The standalone `codex-tui` binary prints a short help message before the
|
|
|
|
|
|
// alternate‑screen mode starts; that file opts‑out locally via `allow`.
|
|
|
|
|
|
#![deny(clippy::print_stdout, clippy::print_stderr)]
|
|
|
|
|
|
|
|
|
|
|
|
use app::App;
|
|
|
|
|
|
use codex_core::util::is_inside_git_repo;
|
|
|
|
|
|
use log_layer::TuiLogLayer;
|
|
|
|
|
|
use std::fs::OpenOptions;
|
|
|
|
|
|
use tracing_appender::non_blocking;
|
|
|
|
|
|
use tracing_subscriber::prelude::*;
|
|
|
|
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
|
|
|
|
|
|
|
|
mod app;
|
|
|
|
|
|
mod app_event;
|
|
|
|
|
|
mod bottom_pane;
|
|
|
|
|
|
mod chatwidget;
|
|
|
|
|
|
mod cli;
|
|
|
|
|
|
mod conversation_history_widget;
|
|
|
|
|
|
mod exec_command;
|
|
|
|
|
|
mod git_warning_screen;
|
|
|
|
|
|
mod history_cell;
|
|
|
|
|
|
mod log_layer;
|
2025-04-25 12:01:52 -07:00
|
|
|
|
mod scroll_event_helper;
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
|
mod status_indicator_widget;
|
|
|
|
|
|
mod tui;
|
|
|
|
|
|
mod user_approval_widget;
|
|
|
|
|
|
|
|
|
|
|
|
pub use cli::Cli;
|
|
|
|
|
|
|
|
|
|
|
|
pub fn run_main(cli: Cli) -> std::io::Result<()> {
|
|
|
|
|
|
assert_env_var_set();
|
|
|
|
|
|
|
|
|
|
|
|
// Open (or create) your log file, appending to it.
|
|
|
|
|
|
let file = OpenOptions::new()
|
|
|
|
|
|
.create(true)
|
|
|
|
|
|
.append(true)
|
|
|
|
|
|
.open("/tmp/codex-rs.log")?;
|
|
|
|
|
|
|
|
|
|
|
|
// Wrap file in non‑blocking writer.
|
|
|
|
|
|
let (non_blocking, _guard) = non_blocking(file);
|
|
|
|
|
|
|
|
|
|
|
|
// use RUST_LOG env var, default to trace for codex crates.
|
|
|
|
|
|
let env_filter = || {
|
|
|
|
|
|
EnvFilter::try_from_default_env()
|
|
|
|
|
|
.unwrap_or_else(|_| EnvFilter::new("codex=trace,codex_tui=trace"))
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Build layered subscriber:
|
|
|
|
|
|
let file_layer = tracing_subscriber::fmt::layer()
|
|
|
|
|
|
.with_writer(non_blocking)
|
|
|
|
|
|
.with_target(false)
|
|
|
|
|
|
.with_filter(env_filter());
|
|
|
|
|
|
|
|
|
|
|
|
// Channel that carries formatted log lines to the UI.
|
|
|
|
|
|
let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
|
|
|
|
|
let tui_layer = TuiLogLayer::new(log_tx.clone(), 120).with_filter(env_filter());
|
|
|
|
|
|
|
|
|
|
|
|
let _ = tracing_subscriber::registry()
|
|
|
|
|
|
.with(file_layer)
|
|
|
|
|
|
.with(tui_layer)
|
|
|
|
|
|
.try_init();
|
|
|
|
|
|
|
|
|
|
|
|
// Determine whether we need to display the "not a git repo" warning
|
|
|
|
|
|
// modal. The flag is shown when the current working directory is *not*
|
|
|
|
|
|
// inside a Git repository **and** the user did *not* pass the
|
|
|
|
|
|
// `--allow-no-git-exec` flag.
|
|
|
|
|
|
let show_git_warning = !cli.skip_git_repo_check && !is_inside_git_repo();
|
|
|
|
|
|
|
|
|
|
|
|
try_run_ratatui_app(cli, show_git_warning, log_rx);
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[expect(
|
|
|
|
|
|
clippy::print_stderr,
|
|
|
|
|
|
reason = "Resort to stderr in exceptional situations."
|
|
|
|
|
|
)]
|
|
|
|
|
|
fn try_run_ratatui_app(
|
|
|
|
|
|
cli: Cli,
|
|
|
|
|
|
show_git_warning: bool,
|
|
|
|
|
|
log_rx: tokio::sync::mpsc::UnboundedReceiver<String>,
|
|
|
|
|
|
) {
|
|
|
|
|
|
if let Err(report) = run_ratatui_app(cli, show_git_warning, log_rx) {
|
|
|
|
|
|
eprintln!("Error: {report:?}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn run_ratatui_app(
|
|
|
|
|
|
cli: Cli,
|
|
|
|
|
|
show_git_warning: bool,
|
|
|
|
|
|
mut log_rx: tokio::sync::mpsc::UnboundedReceiver<String>,
|
|
|
|
|
|
) -> color_eyre::Result<()> {
|
|
|
|
|
|
color_eyre::install()?;
|
|
|
|
|
|
|
|
|
|
|
|
// Forward panic reports through the tracing stack so that they appear in
|
|
|
|
|
|
// the status indicator instead of breaking the alternate screen – the
|
|
|
|
|
|
// normal colour‑eyre hook writes to stderr which would corrupt the UI.
|
|
|
|
|
|
std::panic::set_hook(Box::new(|info| {
|
|
|
|
|
|
tracing::error!("panic: {info}");
|
|
|
|
|
|
}));
|
|
|
|
|
|
let mut terminal = tui::init()?;
|
|
|
|
|
|
terminal.clear()?;
|
|
|
|
|
|
|
|
|
|
|
|
let Cli {
|
|
|
|
|
|
prompt,
|
|
|
|
|
|
images,
|
|
|
|
|
|
approval_policy,
|
|
|
|
|
|
sandbox_policy: sandbox,
|
|
|
|
|
|
model,
|
2025-04-25 12:08:18 -07:00
|
|
|
|
disable_response_storage,
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
|
..
|
|
|
|
|
|
} = cli;
|
|
|
|
|
|
|
|
|
|
|
|
let approval_policy = approval_policy.into();
|
|
|
|
|
|
let sandbox_policy = sandbox.into();
|
|
|
|
|
|
|
|
|
|
|
|
let mut app = App::new(
|
|
|
|
|
|
approval_policy,
|
|
|
|
|
|
sandbox_policy,
|
|
|
|
|
|
prompt,
|
|
|
|
|
|
show_git_warning,
|
|
|
|
|
|
images,
|
|
|
|
|
|
model,
|
2025-04-25 12:08:18 -07:00
|
|
|
|
disable_response_storage,
|
feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
As stated in `codex-rs/README.md`:
Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
run it. For a number of users, this runtime requirement inhibits
adoption: they would be better served by a standalone executable. As
maintainers, we want Codex to run efficiently in a wide range of
environments with minimal overhead. We also want to take advantage of
operating system-specific APIs to provide better sandboxing, where
possible.
To that end, we are moving forward with a Rust implementation of Codex
CLI contained in this folder, which has the following benefits:
- The CLI compiles to small, standalone, platform-specific binaries.
- Can make direct, native calls to
[seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
[landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
order to support sandboxing on Linux.
- No runtime garbage collection, resulting in lower memory consumption
and better, more predictable performance.
Currently, the Rust implementation is materially behind the TypeScript
implementation in functionality, so continue to use the TypeScript
implmentation for the time being. We will publish native executables via
GitHub Releases as soon as we feel the Rust version is usable.
2025-04-24 13:31:40 -07:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Bridge log receiver into the AppEvent channel so latest log lines update the UI.
|
|
|
|
|
|
{
|
|
|
|
|
|
let app_event_tx = app.event_sender();
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
|
while let Some(line) = log_rx.recv().await {
|
|
|
|
|
|
let _ = app_event_tx.send(crate::app_event::AppEvent::LatestLog(line));
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let app_result = app.run(&mut terminal);
|
|
|
|
|
|
|
|
|
|
|
|
restore();
|
|
|
|
|
|
app_result
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[expect(
|
|
|
|
|
|
clippy::print_stderr,
|
|
|
|
|
|
reason = "TUI should not have been displayed yet, so we can write to stderr."
|
|
|
|
|
|
)]
|
|
|
|
|
|
fn assert_env_var_set() {
|
|
|
|
|
|
if std::env::var("OPENAI_API_KEY").is_err() {
|
|
|
|
|
|
eprintln!("Welcome to codex! It looks like you're missing: `OPENAI_API_KEY`");
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"Create an API key (https://platform.openai.com) and export as an environment variable"
|
|
|
|
|
|
);
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[expect(
|
|
|
|
|
|
clippy::print_stderr,
|
|
|
|
|
|
reason = "TUI should no longer be displayed, so we can write to stderr."
|
|
|
|
|
|
)]
|
|
|
|
|
|
fn restore() {
|
|
|
|
|
|
if let Err(err) = tui::restore() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"failed to restore terminal. Run `reset` or restart your terminal to recover: {}",
|
|
|
|
|
|
err
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|