hint for codex resume on tui exit (#3757)
<img width="931" height="438" alt="Screenshot 2025-09-16 at 4 25 19 PM" src="https://github.com/user-attachments/assets/ccfb8df1-feaf-45b4-8f7f-56100de916d5" />
This commit is contained in:
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -929,6 +929,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"mcp-types",
|
"mcp-types",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"owo-colors",
|
||||||
"path-clean",
|
"path-clean",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
|||||||
@@ -177,8 +177,11 @@ async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()
|
|||||||
root_config_overrides.clone(),
|
root_config_overrides.clone(),
|
||||||
);
|
);
|
||||||
let usage = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
|
let usage = codex_tui::run_main(interactive, codex_linux_sandbox_exe).await?;
|
||||||
if !usage.is_zero() {
|
if !usage.token_usage.is_zero() {
|
||||||
println!("{}", codex_core::protocol::FinalOutput::from(usage));
|
println!(
|
||||||
|
"{}",
|
||||||
|
codex_core::protocol::FinalOutput::from(usage.token_usage)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Subcommand::Exec(mut exec_cli)) => {
|
Some(Subcommand::Exec(mut exec_cli)) => {
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ unicode-segmentation = "1.12.0"
|
|||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
url = "2"
|
url = "2"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
|
owo-colors = "4.2.0"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use codex_core::config::persist_model_selection;
|
|||||||
use codex_core::model_family::find_family_for_model;
|
use codex_core::model_family::find_family_for_model;
|
||||||
use codex_core::protocol::TokenUsage;
|
use codex_core::protocol::TokenUsage;
|
||||||
use codex_core::protocol_config_types::ReasoningEffort as ReasoningEffortConfig;
|
use codex_core::protocol_config_types::ReasoningEffort as ReasoningEffortConfig;
|
||||||
|
use codex_protocol::mcp_protocol::ConversationId;
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use color_eyre::eyre::WrapErr;
|
use color_eyre::eyre::WrapErr;
|
||||||
use crossterm::event::KeyCode;
|
use crossterm::event::KeyCode;
|
||||||
@@ -33,6 +34,12 @@ use tokio::select;
|
|||||||
use tokio::sync::mpsc::unbounded_channel;
|
use tokio::sync::mpsc::unbounded_channel;
|
||||||
// use uuid::Uuid;
|
// use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AppExitInfo {
|
||||||
|
pub token_usage: TokenUsage,
|
||||||
|
pub conversation_id: Option<ConversationId>,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct App {
|
pub(crate) struct App {
|
||||||
pub(crate) server: Arc<ConversationManager>,
|
pub(crate) server: Arc<ConversationManager>,
|
||||||
pub(crate) app_event_tx: AppEventSender,
|
pub(crate) app_event_tx: AppEventSender,
|
||||||
@@ -70,7 +77,7 @@ impl App {
|
|||||||
initial_prompt: Option<String>,
|
initial_prompt: Option<String>,
|
||||||
initial_images: Vec<PathBuf>,
|
initial_images: Vec<PathBuf>,
|
||||||
resume_selection: ResumeSelection,
|
resume_selection: ResumeSelection,
|
||||||
) -> Result<TokenUsage> {
|
) -> Result<AppExitInfo> {
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
let (app_event_tx, mut app_event_rx) = unbounded_channel();
|
let (app_event_tx, mut app_event_rx) = unbounded_channel();
|
||||||
let app_event_tx = AppEventSender::new(app_event_tx);
|
let app_event_tx = AppEventSender::new(app_event_tx);
|
||||||
@@ -153,7 +160,10 @@ impl App {
|
|||||||
}
|
}
|
||||||
} {}
|
} {}
|
||||||
tui.terminal.clear()?;
|
tui.terminal.clear()?;
|
||||||
Ok(app.token_usage())
|
Ok(AppExitInfo {
|
||||||
|
token_usage: app.token_usage(),
|
||||||
|
conversation_id: app.chat_widget.conversation_id(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle_tui_event(
|
pub(crate) async fn handle_tui_event(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#![deny(clippy::print_stdout, clippy::print_stderr)]
|
#![deny(clippy::print_stdout, clippy::print_stderr)]
|
||||||
#![deny(clippy::disallowed_methods)]
|
#![deny(clippy::disallowed_methods)]
|
||||||
use app::App;
|
use app::App;
|
||||||
|
pub use app::AppExitInfo;
|
||||||
use codex_core::AuthManager;
|
use codex_core::AuthManager;
|
||||||
use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID;
|
use codex_core::BUILT_IN_OSS_MODEL_PROVIDER_ID;
|
||||||
use codex_core::CodexAuth;
|
use codex_core::CodexAuth;
|
||||||
@@ -86,7 +87,7 @@ use codex_core::internal_storage::InternalStorage;
|
|||||||
pub async fn run_main(
|
pub async fn run_main(
|
||||||
cli: Cli,
|
cli: Cli,
|
||||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||||
) -> std::io::Result<codex_core::protocol::TokenUsage> {
|
) -> std::io::Result<AppExitInfo> {
|
||||||
let (sandbox_mode, approval_policy) = if cli.full_auto {
|
let (sandbox_mode, approval_policy) = if cli.full_auto {
|
||||||
(
|
(
|
||||||
Some(SandboxMode::WorkspaceWrite),
|
Some(SandboxMode::WorkspaceWrite),
|
||||||
@@ -258,7 +259,7 @@ async fn run_ratatui_app(
|
|||||||
mut internal_storage: InternalStorage,
|
mut internal_storage: InternalStorage,
|
||||||
active_profile: Option<String>,
|
active_profile: Option<String>,
|
||||||
should_show_trust_screen: bool,
|
should_show_trust_screen: bool,
|
||||||
) -> color_eyre::Result<codex_core::protocol::TokenUsage> {
|
) -> color_eyre::Result<AppExitInfo> {
|
||||||
let mut config = config;
|
let mut config = config;
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
@@ -370,7 +371,10 @@ async fn run_ratatui_app(
|
|||||||
resume_picker::ResumeSelection::Exit => {
|
resume_picker::ResumeSelection::Exit => {
|
||||||
restore();
|
restore();
|
||||||
session_log::log_session_end();
|
session_log::log_session_end();
|
||||||
return Ok(codex_core::protocol::TokenUsage::default());
|
return Ok(AppExitInfo {
|
||||||
|
token_usage: codex_core::protocol::TokenUsage::default(),
|
||||||
|
conversation_id: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
other => other,
|
other => other,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use codex_arg0::arg0_dispatch_or_else;
|
|||||||
use codex_common::CliConfigOverrides;
|
use codex_common::CliConfigOverrides;
|
||||||
use codex_tui::Cli;
|
use codex_tui::Cli;
|
||||||
use codex_tui::run_main;
|
use codex_tui::run_main;
|
||||||
|
use owo_colors::OwoColorize;
|
||||||
|
use supports_color::Stream;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct TopCli {
|
struct TopCli {
|
||||||
@@ -21,9 +23,21 @@ fn main() -> anyhow::Result<()> {
|
|||||||
.config_overrides
|
.config_overrides
|
||||||
.raw_overrides
|
.raw_overrides
|
||||||
.splice(0..0, top_cli.config_overrides.raw_overrides);
|
.splice(0..0, top_cli.config_overrides.raw_overrides);
|
||||||
let usage = run_main(inner, codex_linux_sandbox_exe).await?;
|
let exit_info = run_main(inner, codex_linux_sandbox_exe).await?;
|
||||||
if !usage.is_zero() {
|
let token_usage = exit_info.token_usage;
|
||||||
println!("{}", codex_core::protocol::FinalOutput::from(usage));
|
let conversation_id = exit_info.conversation_id;
|
||||||
|
if !token_usage.is_zero() {
|
||||||
|
println!("{}", codex_core::protocol::FinalOutput::from(token_usage),);
|
||||||
|
if let Some(session_id) = conversation_id {
|
||||||
|
let command = format!("codex resume {session_id}");
|
||||||
|
let prefix = "To continue this session, run ";
|
||||||
|
let suffix = ".";
|
||||||
|
if supports_color::on(Stream::Stdout).is_some() {
|
||||||
|
println!("{}{}{}", prefix, command.cyan(), suffix);
|
||||||
|
} else {
|
||||||
|
println!("{prefix}{command}{suffix}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user