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
|
|
|
|
//! A live status indicator that shows the *latest* log line emitted by the
|
|
|
|
|
|
//! application while the agent is processing a long‑running task.
|
|
|
|
|
|
|
|
|
|
|
|
use std::time::Duration;
|
2025-08-06 21:20:09 -07:00
|
|
|
|
use std::time::Instant;
|
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
|
|
|
|
|
2025-08-12 15:43:07 -07:00
|
|
|
|
use codex_core::protocol::Op;
|
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
|
|
|
|
use ratatui::buffer::Buffer;
|
|
|
|
|
|
use ratatui::layout::Rect;
|
2025-08-25 14:38:38 -07:00
|
|
|
|
use ratatui::style::Stylize;
|
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
|
|
|
|
use ratatui::text::Line;
|
|
|
|
|
|
use ratatui::widgets::Paragraph;
|
|
|
|
|
|
use ratatui::widgets::WidgetRef;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::app_event::AppEvent;
|
2025-05-15 14:50:30 -07:00
|
|
|
|
use crate::app_event_sender::AppEventSender;
|
2025-09-04 10:55:50 -07:00
|
|
|
|
use crate::key_hint;
|
2025-08-14 16:59:47 -04:00
|
|
|
|
use crate::shimmer::shimmer_spans;
|
2025-08-20 13:47:24 -07:00
|
|
|
|
use crate::tui::FrameRequester;
|
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
|
|
|
|
|
|
|
|
|
|
pub(crate) struct StatusIndicatorWidget {
|
2025-08-20 16:58:56 -07:00
|
|
|
|
/// Animated header text (defaults to "Working").
|
|
|
|
|
|
header: String,
|
2025-08-25 14:38:38 -07:00
|
|
|
|
/// Queued user messages to display under the status line.
|
|
|
|
|
|
queued_messages: Vec<String>,
|
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
|
|
|
|
|
2025-09-04 12:37:43 -07:00
|
|
|
|
elapsed_running: Duration,
|
|
|
|
|
|
last_resume_at: Instant,
|
|
|
|
|
|
is_paused: bool,
|
2025-08-12 15:43:07 -07:00
|
|
|
|
app_event_tx: AppEventSender,
|
2025-08-20 13:47:24 -07:00
|
|
|
|
frame_requester: FrameRequester,
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-06 00:06:36 +02:00
|
|
|
|
// Format elapsed seconds into a compact human-friendly form used by the status line.
|
2025-09-12 12:05:57 -04:00
|
|
|
|
// Examples: 0s, 59s, 1m 00s, 59m 59s, 1h 00m 00s, 2h 03m 09s
|
2025-09-26 22:49:59 -07:00
|
|
|
|
pub fn fmt_elapsed_compact(elapsed_secs: u64) -> String {
|
2025-09-06 00:06:36 +02:00
|
|
|
|
if elapsed_secs < 60 {
|
|
|
|
|
|
return format!("{elapsed_secs}s");
|
|
|
|
|
|
}
|
|
|
|
|
|
if elapsed_secs < 3600 {
|
|
|
|
|
|
let minutes = elapsed_secs / 60;
|
|
|
|
|
|
let seconds = elapsed_secs % 60;
|
2025-09-12 12:05:57 -04:00
|
|
|
|
return format!("{minutes}m {seconds:02}s");
|
2025-09-06 00:06:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
let hours = elapsed_secs / 3600;
|
|
|
|
|
|
let minutes = (elapsed_secs % 3600) / 60;
|
|
|
|
|
|
let seconds = elapsed_secs % 60;
|
2025-09-12 12:05:57 -04:00
|
|
|
|
format!("{hours}h {minutes:02}m {seconds:02}s")
|
2025-09-06 00:06:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
impl StatusIndicatorWidget {
|
2025-08-20 13:47:24 -07:00
|
|
|
|
pub(crate) fn new(app_event_tx: AppEventSender, frame_requester: FrameRequester) -> Self {
|
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
|
|
|
|
Self {
|
2025-08-20 16:58:56 -07:00
|
|
|
|
header: String::from("Working"),
|
2025-08-25 14:38:38 -07:00
|
|
|
|
queued_messages: Vec::new(),
|
2025-09-04 12:37:43 -07:00
|
|
|
|
elapsed_running: Duration::ZERO,
|
|
|
|
|
|
last_resume_at: Instant::now(),
|
|
|
|
|
|
is_paused: false,
|
2025-08-04 21:23:22 -07:00
|
|
|
|
|
2025-08-12 15:43:07 -07:00
|
|
|
|
app_event_tx,
|
2025-08-20 13:47:24 -07:00
|
|
|
|
frame_requester,
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 14:38:38 -07:00
|
|
|
|
pub fn desired_height(&self, width: u16) -> u16 {
|
2025-09-14 15:37:28 -04:00
|
|
|
|
// Status line + optional blank line + wrapped queued messages (up to 3 lines per message)
|
2025-08-25 14:38:38 -07:00
|
|
|
|
// + optional ellipsis line per truncated message + 1 spacer line
|
|
|
|
|
|
let inner_width = width.max(1) as usize;
|
|
|
|
|
|
let mut total: u16 = 1; // status line
|
2025-09-14 15:37:28 -04:00
|
|
|
|
if !self.queued_messages.is_empty() {
|
|
|
|
|
|
total = total.saturating_add(1); // blank line between status and queued messages
|
|
|
|
|
|
}
|
2025-08-25 14:38:38 -07:00
|
|
|
|
let text_width = inner_width.saturating_sub(3); // account for " ↳ " prefix
|
|
|
|
|
|
if text_width > 0 {
|
|
|
|
|
|
for q in &self.queued_messages {
|
2025-09-05 07:10:32 -07:00
|
|
|
|
let wrapped = textwrap::wrap(q, text_width);
|
2025-08-25 14:38:38 -07:00
|
|
|
|
let lines = wrapped.len().min(3) as u16;
|
|
|
|
|
|
total = total.saturating_add(lines);
|
|
|
|
|
|
if wrapped.len() > 3 {
|
|
|
|
|
|
total = total.saturating_add(1); // ellipsis line
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if !self.queued_messages.is_empty() {
|
|
|
|
|
|
total = total.saturating_add(1); // keybind hint line
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// At least one line per message if width is extremely narrow
|
|
|
|
|
|
total = total.saturating_add(self.queued_messages.len() as u16);
|
2025-08-04 21:23:22 -07:00
|
|
|
|
}
|
2025-08-25 14:38:38 -07:00
|
|
|
|
total.saturating_add(1) // spacer line
|
2025-08-04 21:23:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-12 15:43:07 -07:00
|
|
|
|
pub(crate) fn interrupt(&self) {
|
|
|
|
|
|
self.app_event_tx.send(AppEvent::CodexOp(Op::Interrupt));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-20 16:58:56 -07:00
|
|
|
|
/// Update the animated header label (left of the brackets).
|
|
|
|
|
|
pub(crate) fn update_header(&mut self, header: String) {
|
|
|
|
|
|
if self.header != header {
|
|
|
|
|
|
self.header = header;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 14:38:38 -07:00
|
|
|
|
/// Replace the queued messages displayed beneath the header.
|
|
|
|
|
|
pub(crate) fn set_queued_messages(&mut self, queued: Vec<String>) {
|
|
|
|
|
|
self.queued_messages = queued;
|
|
|
|
|
|
// Ensure a redraw so changes are visible.
|
|
|
|
|
|
self.frame_requester.schedule_frame();
|
2025-08-22 14:03:58 -07:00
|
|
|
|
}
|
2025-09-04 12:37:43 -07:00
|
|
|
|
|
|
|
|
|
|
pub(crate) fn pause_timer(&mut self) {
|
|
|
|
|
|
self.pause_timer_at(Instant::now());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn resume_timer(&mut self) {
|
|
|
|
|
|
self.resume_timer_at(Instant::now());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn pause_timer_at(&mut self, now: Instant) {
|
|
|
|
|
|
if self.is_paused {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.elapsed_running += now.saturating_duration_since(self.last_resume_at);
|
|
|
|
|
|
self.is_paused = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn resume_timer_at(&mut self, now: Instant) {
|
|
|
|
|
|
if !self.is_paused {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.last_resume_at = now;
|
|
|
|
|
|
self.is_paused = false;
|
|
|
|
|
|
self.frame_requester.schedule_frame();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn elapsed_seconds_at(&self, now: Instant) -> u64 {
|
|
|
|
|
|
let mut elapsed = self.elapsed_running;
|
|
|
|
|
|
if !self.is_paused {
|
|
|
|
|
|
elapsed += now.saturating_duration_since(self.last_resume_at);
|
|
|
|
|
|
}
|
|
|
|
|
|
elapsed.as_secs()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-26 22:49:59 -07:00
|
|
|
|
pub fn elapsed_seconds(&self) -> u64 {
|
2025-09-04 12:37:43 -07:00
|
|
|
|
self.elapsed_seconds_at(Instant::now())
|
|
|
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl WidgetRef for StatusIndicatorWidget {
|
|
|
|
|
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
2025-08-25 14:38:38 -07:00
|
|
|
|
if area.is_empty() {
|
2025-08-04 21:23:22 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-14 16:59:47 -04:00
|
|
|
|
// Schedule next animation frame.
|
2025-08-20 13:47:24 -07:00
|
|
|
|
self.frame_requester
|
2025-08-20 16:58:56 -07:00
|
|
|
|
.schedule_frame_in(Duration::from_millis(32));
|
2025-09-04 12:37:43 -07:00
|
|
|
|
let elapsed = self.elapsed_seconds();
|
2025-09-06 00:06:36 +02:00
|
|
|
|
let pretty_elapsed = fmt_elapsed_compact(elapsed);
|
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
|
|
|
|
|
2025-08-04 21:23:22 -07:00
|
|
|
|
// Plain rendering: no borders or padding so the live cell is visually indistinguishable from terminal scrollback.
|
2025-10-01 14:29:05 -07:00
|
|
|
|
let mut spans = vec![" ".into()];
|
2025-08-25 14:38:38 -07:00
|
|
|
|
spans.extend(shimmer_spans(&self.header));
|
|
|
|
|
|
spans.extend(vec![
|
|
|
|
|
|
" ".into(),
|
2025-09-06 00:06:36 +02:00
|
|
|
|
format!("({pretty_elapsed} • ").dim(),
|
2025-08-25 14:38:38 -07:00
|
|
|
|
"Esc".dim().bold(),
|
|
|
|
|
|
" to interrupt)".dim(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// Build lines: status, then queued messages, then spacer.
|
|
|
|
|
|
let mut lines: Vec<Line<'static>> = Vec::new();
|
|
|
|
|
|
lines.push(Line::from(spans));
|
2025-09-14 15:37:28 -04:00
|
|
|
|
if !self.queued_messages.is_empty() {
|
|
|
|
|
|
lines.push(Line::from(""));
|
|
|
|
|
|
}
|
2025-08-25 14:38:38 -07:00
|
|
|
|
// Wrap queued messages using textwrap and show up to the first 3 lines per message.
|
|
|
|
|
|
let text_width = area.width.saturating_sub(3); // " ↳ " prefix
|
|
|
|
|
|
for q in &self.queued_messages {
|
2025-09-05 07:10:32 -07:00
|
|
|
|
let wrapped = textwrap::wrap(q, text_width as usize);
|
2025-08-25 14:38:38 -07:00
|
|
|
|
for (i, piece) in wrapped.iter().take(3).enumerate() {
|
|
|
|
|
|
let prefix = if i == 0 { " ↳ " } else { " " };
|
|
|
|
|
|
let content = format!("{prefix}{piece}");
|
2025-08-25 17:36:05 -07:00
|
|
|
|
lines.push(Line::from(content.dim().italic()));
|
2025-08-25 14:38:38 -07:00
|
|
|
|
}
|
|
|
|
|
|
if wrapped.len() > 3 {
|
2025-08-25 17:36:05 -07:00
|
|
|
|
lines.push(Line::from(" …".dim().italic()));
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-25 14:38:38 -07:00
|
|
|
|
if !self.queued_messages.is_empty() {
|
2025-09-04 10:55:50 -07:00
|
|
|
|
let shortcut = key_hint::alt("↑");
|
|
|
|
|
|
lines.push(Line::from(vec![" ".into(), shortcut, " edit".into()]).dim());
|
2025-08-25 14:38:38 -07:00
|
|
|
|
}
|
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
|
|
|
|
|
2025-08-04 21:23:22 -07:00
|
|
|
|
let paragraph = Paragraph::new(lines);
|
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
|
|
|
|
paragraph.render_ref(area, buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-03 11:51:33 -07:00
|
|
|
|
|
2025-08-04 21:23:22 -07:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
use crate::app_event::AppEvent;
|
|
|
|
|
|
use crate::app_event_sender::AppEventSender;
|
2025-08-25 14:38:38 -07:00
|
|
|
|
use ratatui::Terminal;
|
|
|
|
|
|
use ratatui::backend::TestBackend;
|
2025-09-04 12:37:43 -07:00
|
|
|
|
use std::time::Duration;
|
|
|
|
|
|
use std::time::Instant;
|
2025-08-20 10:11:09 -07:00
|
|
|
|
use tokio::sync::mpsc::unbounded_channel;
|
2025-08-04 21:23:22 -07:00
|
|
|
|
|
2025-09-06 00:06:36 +02:00
|
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn fmt_elapsed_compact_formats_seconds_minutes_hours() {
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(0), "0s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(1), "1s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(59), "59s");
|
2025-09-12 12:05:57 -04:00
|
|
|
|
assert_eq!(fmt_elapsed_compact(60), "1m 00s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(61), "1m 01s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(3 * 60 + 5), "3m 05s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(59 * 60 + 59), "59m 59s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(3600), "1h 00m 00s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(3600 + 60 + 1), "1h 01m 01s");
|
|
|
|
|
|
assert_eq!(fmt_elapsed_compact(25 * 3600 + 2 * 60 + 3), "25h 02m 03s");
|
2025-09-06 00:06:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-04 21:23:22 -07:00
|
|
|
|
#[test]
|
2025-08-25 14:38:38 -07:00
|
|
|
|
fn renders_with_working_header() {
|
2025-08-20 10:11:09 -07:00
|
|
|
|
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
2025-08-04 21:23:22 -07:00
|
|
|
|
let tx = AppEventSender::new(tx_raw);
|
2025-08-25 14:38:38 -07:00
|
|
|
|
let w = StatusIndicatorWidget::new(tx, crate::tui::FrameRequester::test_dummy());
|
|
|
|
|
|
|
|
|
|
|
|
// Render into a fixed-size test terminal and snapshot the backend.
|
|
|
|
|
|
let mut terminal = Terminal::new(TestBackend::new(80, 2)).expect("terminal");
|
|
|
|
|
|
terminal
|
|
|
|
|
|
.draw(|f| w.render_ref(f.area(), f.buffer_mut()))
|
|
|
|
|
|
.expect("draw");
|
2025-09-04 10:55:50 -07:00
|
|
|
|
insta::assert_snapshot!(terminal.backend());
|
2025-08-04 21:23:22 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-08-25 14:38:38 -07:00
|
|
|
|
fn renders_truncated() {
|
2025-08-20 10:11:09 -07:00
|
|
|
|
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
2025-08-04 21:23:22 -07:00
|
|
|
|
let tx = AppEventSender::new(tx_raw);
|
2025-08-25 14:38:38 -07:00
|
|
|
|
let w = StatusIndicatorWidget::new(tx, crate::tui::FrameRequester::test_dummy());
|
|
|
|
|
|
|
|
|
|
|
|
// Render into a fixed-size test terminal and snapshot the backend.
|
|
|
|
|
|
let mut terminal = Terminal::new(TestBackend::new(20, 2)).expect("terminal");
|
|
|
|
|
|
terminal
|
|
|
|
|
|
.draw(|f| w.render_ref(f.area(), f.buffer_mut()))
|
|
|
|
|
|
.expect("draw");
|
2025-09-04 10:55:50 -07:00
|
|
|
|
insta::assert_snapshot!(terminal.backend());
|
2025-08-04 21:23:22 -07:00
|
|
|
|
}
|
2025-08-07 01:45:04 -07:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-08-25 14:38:38 -07:00
|
|
|
|
fn renders_with_queued_messages() {
|
2025-08-20 10:11:09 -07:00
|
|
|
|
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
2025-08-07 01:45:04 -07:00
|
|
|
|
let tx = AppEventSender::new(tx_raw);
|
2025-08-20 13:47:24 -07:00
|
|
|
|
let mut w = StatusIndicatorWidget::new(tx, crate::tui::FrameRequester::test_dummy());
|
2025-08-25 14:38:38 -07:00
|
|
|
|
w.set_queued_messages(vec!["first".to_string(), "second".to_string()]);
|
|
|
|
|
|
|
|
|
|
|
|
// Render into a fixed-size test terminal and snapshot the backend.
|
|
|
|
|
|
let mut terminal = Terminal::new(TestBackend::new(80, 8)).expect("terminal");
|
|
|
|
|
|
terminal
|
|
|
|
|
|
.draw(|f| w.render_ref(f.area(), f.buffer_mut()))
|
|
|
|
|
|
.expect("draw");
|
2025-09-04 10:55:50 -07:00
|
|
|
|
insta::assert_snapshot!(terminal.backend());
|
2025-08-07 01:45:04 -07:00
|
|
|
|
}
|
2025-09-04 12:37:43 -07:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn timer_pauses_when_requested() {
|
|
|
|
|
|
let (tx_raw, _rx) = unbounded_channel::<AppEvent>();
|
|
|
|
|
|
let tx = AppEventSender::new(tx_raw);
|
|
|
|
|
|
let mut widget = StatusIndicatorWidget::new(tx, crate::tui::FrameRequester::test_dummy());
|
|
|
|
|
|
|
|
|
|
|
|
let baseline = Instant::now();
|
|
|
|
|
|
widget.last_resume_at = baseline;
|
|
|
|
|
|
|
|
|
|
|
|
let before_pause = widget.elapsed_seconds_at(baseline + Duration::from_secs(5));
|
|
|
|
|
|
assert_eq!(before_pause, 5);
|
|
|
|
|
|
|
|
|
|
|
|
widget.pause_timer_at(baseline + Duration::from_secs(5));
|
|
|
|
|
|
let paused_elapsed = widget.elapsed_seconds_at(baseline + Duration::from_secs(10));
|
|
|
|
|
|
assert_eq!(paused_elapsed, before_pause);
|
|
|
|
|
|
|
|
|
|
|
|
widget.resume_timer_at(baseline + Duration::from_secs(10));
|
|
|
|
|
|
let after_resume = widget.elapsed_seconds_at(baseline + Duration::from_secs(13));
|
|
|
|
|
|
assert_eq!(after_resume, before_pause + 3);
|
|
|
|
|
|
}
|
2025-08-04 21:23:22 -07:00
|
|
|
|
}
|