feat: single app-server bootstrap in TUI (#16582)

Before this, the TUI was starting 2 app-server. One to check the login
status and one to actually start the session

This PR make only one app-server startup and defer the login check in
async, outside of the frame rendering path

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
jif-oaiandCodex authored and GitHub committed 2026-04-08 13:49:06 +01:00
1 parent d47b755aa2
commit 2bbab7d8f9
7 files changed
+86 -106

No files matched your search

@@ -1,9 +1,9 @@
use codex_execpolicy::Decision; use codex_execpolicy::Decision;
use codex_execpolicy::PatternToken;
use codex_execpolicy::Policy; use codex_execpolicy::Policy;
use codex_execpolicy::PrefixPattern;
use codex_execpolicy::PrefixRule;
use codex_execpolicy::RuleRef; use codex_execpolicy::RuleRef;
use codex_execpolicy::rule::PatternToken;
use codex_execpolicy::rule::PrefixPattern;
use codex_execpolicy::rule::PrefixRule;
use multimap::MultiMap; use multimap::MultiMap;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
+1 -1
View File
@@ -5,7 +5,7 @@ pub(crate) mod execpolicycheck;
mod executable_name; mod executable_name;
pub(crate) mod parser; pub(crate) mod parser;
pub(crate) mod policy; pub(crate) mod policy;
pub(crate) mod rule; pub mod rule;
pub use amend::AmendError; pub use amend::AmendError;
pub use amend::blocking_append_allow_prefix_rule; pub use amend::blocking_append_allow_prefix_rule;
@@ -455,7 +455,10 @@ impl ChatComposer {
realtime_conversation_enabled: false, realtime_conversation_enabled: false,
audio_device_selection_enabled: false, audio_device_selection_enabled: false,
windows_degraded_sandbox_active: false, windows_degraded_sandbox_active: false,
is_zellij: codex_terminal_detection::terminal_info().is_zellij(), is_zellij: matches!(
codex_terminal_detection::terminal_info().multiplexer,
Some(codex_terminal_detection::Multiplexer::Zellij {})
),
status_line_value: None, status_line_value: None,
status_line_enabled: false, status_line_enabled: false,
active_agent_label: None, active_agent_label: None,
+72 -95
View File
@@ -14,6 +14,7 @@ use codex_app_server_client::InProcessAppServerClient;
use codex_app_server_client::InProcessClientStartArgs; use codex_app_server_client::InProcessClientStartArgs;
use codex_app_server_client::RemoteAppServerClient; use codex_app_server_client::RemoteAppServerClient;
use codex_app_server_client::RemoteAppServerConnectArgs; use codex_app_server_client::RemoteAppServerConnectArgs;
use codex_app_server_protocol::Account as AppServerAccount;
use codex_app_server_protocol::AuthMode as AppServerAuthMode; use codex_app_server_protocol::AuthMode as AppServerAuthMode;
use codex_app_server_protocol::ConfigWarningNotification; use codex_app_server_protocol::ConfigWarningNotification;
use codex_app_server_protocol::Thread as AppServerThread; use codex_app_server_protocol::Thread as AppServerThread;
@@ -1016,32 +1017,33 @@ async fn run_ratatui_app(
// Initialize high-fidelity session event logging if enabled. // Initialize high-fidelity session event logging if enabled.
session_log::maybe_init(&initial_config); session_log::maybe_init(&initial_config);
let mut app_server = Some(
match start_app_server(
&app_server_target,
arg0_paths.clone(),
initial_config.clone(),
cli_kv_overrides.clone(),
loader_overrides.clone(),
cloud_requirements.clone(),
feedback.clone(),
)
.await
{
Ok(app_server) => AppServerSession::new(app_server)
.with_remote_cwd_override(remote_cwd_override.clone()),
Err(err) => {
terminal_restore_guard.restore_silently();
session_log::log_session_end();
return Err(err);
}
},
);
let should_show_trust_screen_flag = !remote_mode && should_show_trust_screen(&initial_config); let should_show_trust_screen_flag = !remote_mode && should_show_trust_screen(&initial_config);
let mut trust_decision_was_made = false; let mut trust_decision_was_made = false;
let needs_onboarding_app_server =
should_show_trust_screen_flag || initial_config.model_provider.requires_openai_auth;
let mut onboarding_app_server = if needs_onboarding_app_server {
Some(
AppServerSession::new(
start_app_server(
&app_server_target,
arg0_paths.clone(),
initial_config.clone(),
cli_kv_overrides.clone(),
loader_overrides.clone(),
cloud_requirements.clone(),
feedback.clone(),
)
.await?,
)
.with_remote_cwd_override(remote_cwd_override.clone()),
)
} else {
None
};
let login_status = if initial_config.model_provider.requires_openai_auth { let login_status = if initial_config.model_provider.requires_openai_auth {
let Some(app_server) = onboarding_app_server.as_mut() else { let Some(app_server) = app_server.as_mut() else {
unreachable!("onboarding app server should exist when auth is required"); unreachable!("app server should exist when auth is required");
}; };
get_login_status(app_server, &initial_config).await? get_login_status(app_server, &initial_config).await?
} else { } else {
@@ -1057,13 +1059,13 @@ async fn run_ratatui_app(
show_login_screen, show_login_screen,
show_trust_screen: should_show_trust_screen_flag, show_trust_screen: should_show_trust_screen_flag,
login_status, login_status,
app_server_request_handle: onboarding_app_server app_server_request_handle: app_server
.as_ref() .as_ref()
.map(AppServerSession::request_handle), .map(AppServerSession::request_handle),
config: initial_config.clone(), config: initial_config.clone(),
}, },
if show_login_screen { if show_login_screen {
onboarding_app_server.take() app_server.as_mut()
} else { } else {
None None
}, },
@@ -1071,6 +1073,7 @@ async fn run_ratatui_app(
) )
.await?; .await?;
if onboarding_result.should_exit { if onboarding_result.should_exit {
shutdown_app_server_if_present(app_server.take()).await;
terminal_restore_guard.restore_silently(); terminal_restore_guard.restore_silently();
session_log::log_session_end(); session_log::log_session_end();
let _ = tui.terminal.clear(); let _ = tui.terminal.clear();
@@ -1110,10 +1113,8 @@ async fn run_ratatui_app(
initial_config initial_config
} }
} else { } else {
shutdown_app_server_if_present(onboarding_app_server.take()).await;
initial_config initial_config
}; };
shutdown_app_server_if_present(onboarding_app_server.take()).await;
let mut missing_session_exit = |id_str: &str, action: &str| { let mut missing_session_exit = |id_str: &str, action: &str| {
error!("Error finding conversation path: {id_str}"); error!("Error finding conversation path: {id_str}");
@@ -1131,42 +1132,16 @@ async fn run_ratatui_app(
}) })
}; };
let needs_app_server_session_lookup = cli.resume_last
|| cli.fork_last
|| cli.resume_session_id.is_some()
|| cli.fork_session_id.is_some()
|| cli.resume_picker
|| cli.fork_picker;
let mut session_lookup_app_server = if needs_app_server_session_lookup {
Some(
AppServerSession::new(
start_app_server(
&app_server_target,
arg0_paths.clone(),
config.clone(),
cli_kv_overrides.clone(),
loader_overrides.clone(),
cloud_requirements.clone(),
feedback.clone(),
)
.await?,
)
.with_remote_cwd_override(remote_cwd_override.clone()),
)
} else {
None
};
let use_fork = cli.fork_picker || cli.fork_last || cli.fork_session_id.is_some(); let use_fork = cli.fork_picker || cli.fork_last || cli.fork_session_id.is_some();
let session_selection = if use_fork { let session_selection = if use_fork {
if let Some(id_str) = cli.fork_session_id.as_deref() { if let Some(id_str) = cli.fork_session_id.as_deref() {
let Some(app_server) = session_lookup_app_server.as_mut() else { let Some(startup_app_server) = app_server.as_mut() else {
unreachable!("session lookup app server should be initialized for --fork <id>"); unreachable!("app server should be initialized for --fork <id>");
}; };
match lookup_session_target_with_app_server(app_server, id_str).await? { match lookup_session_target_with_app_server(startup_app_server, id_str).await? {
Some(target_session) => resume_picker::SessionSelection::Fork(target_session), Some(target_session) => resume_picker::SessionSelection::Fork(target_session),
None => { None => {
shutdown_app_server_if_present(session_lookup_app_server.take()).await; shutdown_app_server_if_present(app_server.take()).await;
return missing_session_exit(id_str, "fork"); return missing_session_exit(id_str, "fork");
} }
} }
@@ -1181,8 +1156,8 @@ async fn run_ratatui_app(
} else { } else {
None None
}; };
let Some(app_server) = session_lookup_app_server.as_mut() else { let Some(app_server) = app_server.as_mut() else {
unreachable!("session lookup app server should be initialized for --fork --last"); unreachable!("app server should be initialized for --fork --last");
}; };
match lookup_latest_session_target_with_app_server( match lookup_latest_session_target_with_app_server(
app_server, &config, filter_cwd, /*include_non_interactive*/ false, app_server, &config, filter_cwd, /*include_non_interactive*/ false,
@@ -1193,8 +1168,8 @@ async fn run_ratatui_app(
None => resume_picker::SessionSelection::StartFresh, None => resume_picker::SessionSelection::StartFresh,
} }
} else if cli.fork_picker { } else if cli.fork_picker {
let Some(app_server) = session_lookup_app_server.take() else { let Some(app_server) = app_server.take() else {
unreachable!("session lookup app server should be initialized for --fork picker"); unreachable!("app server should be initialized for --fork picker");
}; };
match resume_picker::run_fork_picker_with_app_server( match resume_picker::run_fork_picker_with_app_server(
&mut tui, &mut tui,
@@ -1221,13 +1196,13 @@ async fn run_ratatui_app(
resume_picker::SessionSelection::StartFresh resume_picker::SessionSelection::StartFresh
} }
} else if let Some(id_str) = cli.resume_session_id.as_deref() { } else if let Some(id_str) = cli.resume_session_id.as_deref() {
let Some(app_server) = session_lookup_app_server.as_mut() else { let Some(startup_app_server) = app_server.as_mut() else {
unreachable!("session lookup app server should be initialized for --resume <id>"); unreachable!("app server should be initialized for --resume <id>");
}; };
match lookup_session_target_with_app_server(app_server, id_str).await? { match lookup_session_target_with_app_server(startup_app_server, id_str).await? {
Some(target_session) => resume_picker::SessionSelection::Resume(target_session), Some(target_session) => resume_picker::SessionSelection::Resume(target_session),
None => { None => {
shutdown_app_server_if_present(session_lookup_app_server.take()).await; shutdown_app_server_if_present(app_server.take()).await;
return missing_session_exit(id_str, "resume"); return missing_session_exit(id_str, "resume");
} }
} }
@@ -1238,8 +1213,8 @@ async fn run_ratatui_app(
&config, &config,
cli.resume_show_all, cli.resume_show_all,
); );
let Some(app_server) = session_lookup_app_server.as_mut() else { let Some(app_server) = app_server.as_mut() else {
unreachable!("session lookup app server should be initialized for --resume --last"); unreachable!("app server should be initialized for --resume --last");
}; };
match lookup_latest_session_target_with_app_server( match lookup_latest_session_target_with_app_server(
app_server, app_server,
@@ -1253,8 +1228,8 @@ async fn run_ratatui_app(
None => resume_picker::SessionSelection::StartFresh, None => resume_picker::SessionSelection::StartFresh,
} }
} else if cli.resume_picker { } else if cli.resume_picker {
let Some(app_server) = session_lookup_app_server.take() else { let Some(app_server) = app_server.take() else {
unreachable!("session lookup app server should be initialized for --resume picker"); unreachable!("app server should be initialized for --resume picker");
}; };
match resume_picker::run_resume_picker_with_app_server( match resume_picker::run_resume_picker_with_app_server(
&mut tui, &mut tui,
@@ -1281,7 +1256,6 @@ async fn run_ratatui_app(
} else { } else {
resume_picker::SessionSelection::StartFresh resume_picker::SessionSelection::StartFresh
}; };
shutdown_app_server_if_present(session_lookup_app_server.take()).await;
let current_cwd = config.cwd.clone(); let current_cwd = config.cwd.clone();
let allow_prompt = !remote_mode && cli.cwd.is_none(); let allow_prompt = !remote_mode && cli.cwd.is_none();
@@ -1367,28 +1341,32 @@ async fn run_ratatui_app(
let use_alt_screen = determine_alt_screen_mode(no_alt_screen, config.tui_alternate_screen); let use_alt_screen = determine_alt_screen_mode(no_alt_screen, config.tui_alternate_screen);
tui.set_alt_screen_enabled(use_alt_screen); tui.set_alt_screen_enabled(use_alt_screen);
let app_server = match start_app_server( let app_server = match app_server {
&app_server_target, Some(app_server) => app_server,
arg0_paths, None => match start_app_server(
config.clone(), &app_server_target,
cli_kv_overrides.clone(), arg0_paths,
loader_overrides, config.clone(),
cloud_requirements.clone(), cli_kv_overrides.clone(),
feedback.clone(), loader_overrides,
) cloud_requirements.clone(),
.await feedback.clone(),
{ )
Ok(app_server) => app_server, .await
Err(err) => { {
terminal_restore_guard.restore_silently(); Ok(app_server) => AppServerSession::new(app_server)
session_log::log_session_end(); .with_remote_cwd_override(remote_cwd_override.clone()),
return Err(err); Err(err) => {
} terminal_restore_guard.restore_silently();
session_log::log_session_end();
return Err(err);
}
},
}; };
let app_result = App::run( let app_result = App::run(
&mut tui, &mut tui,
AppServerSession::new(app_server).with_remote_cwd_override(remote_cwd_override), app_server,
config, config,
cli_kv_overrides.clone(), cli_kv_overrides.clone(),
overrides.clone(), overrides.clone(),
@@ -1603,7 +1581,10 @@ fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScree
AltScreenMode::Never => false, AltScreenMode::Never => false,
AltScreenMode::Auto => { AltScreenMode::Auto => {
let terminal_info = terminal_info(); let terminal_info = terminal_info();
!terminal_info.is_zellij() !matches!(
terminal_info.multiplexer,
Some(codex_terminal_detection::Multiplexer::Zellij {})
)
} }
} }
} }
@@ -1628,12 +1609,8 @@ async fn get_login_status(
let account = app_server.read_account().await?; let account = app_server.read_account().await?;
Ok(match account.account { Ok(match account.account {
Some(codex_app_server_protocol::Account::ApiKey {}) => { Some(AppServerAccount::ApiKey {}) => LoginStatus::AuthMode(AppServerAuthMode::ApiKey),
LoginStatus::AuthMode(AppServerAuthMode::ApiKey) Some(AppServerAccount::Chatgpt { .. }) => LoginStatus::AuthMode(AppServerAuthMode::Chatgpt),
}
Some(codex_app_server_protocol::Account::Chatgpt { .. }) => {
LoginStatus::AuthMode(AppServerAuthMode::Chatgpt)
}
None => LoginStatus::NotAuthenticated, None => LoginStatus::NotAuthenticated,
}) })
} }
@@ -447,7 +447,7 @@ impl WidgetRef for Step {
pub(crate) async fn run_onboarding_app( pub(crate) async fn run_onboarding_app(
args: OnboardingScreenArgs, args: OnboardingScreenArgs,
mut app_server: Option<AppServerSession>, mut app_server: Option<&mut AppServerSession>,
tui: &mut Tui, tui: &mut Tui,
) -> Result<OnboardingResult> { ) -> Result<OnboardingResult> {
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
@@ -533,9 +533,6 @@ pub(crate) async fn run_onboarding_app(
} }
} }
} }
if let Some(app_server) = app_server {
app_server.shutdown().await.ok();
}
Ok(OnboardingResult { Ok(OnboardingResult {
directory_trust_decision: onboarding_screen.directory_trust_decision(), directory_trust_decision: onboarding_screen.directory_trust_decision(),
should_exit: onboarding_screen.should_exit(), should_exit: onboarding_screen.should_exit(),
+4 -1
View File
@@ -270,7 +270,10 @@ impl Tui {
// Cache this to avoid contention with the event reader. // Cache this to avoid contention with the event reader.
supports_color::on_cached(supports_color::Stream::Stdout); supports_color::on_cached(supports_color::Stream::Stdout);
let _ = crate::terminal_palette::default_colors(); let _ = crate::terminal_palette::default_colors();
let is_zellij = codex_terminal_detection::terminal_info().is_zellij(); let is_zellij = matches!(
codex_terminal_detection::terminal_info().multiplexer,
Some(codex_terminal_detection::Multiplexer::Zellij {})
);
Self { Self {
frame_requester, frame_requester,
+1 -1
View File
@@ -20,7 +20,7 @@ pub const MAX_WIDTH: u32 = 2048;
/// Maximum height used when resizing images before uploading. /// Maximum height used when resizing images before uploading.
pub const MAX_HEIGHT: u32 = 768; pub const MAX_HEIGHT: u32 = 768;
pub(crate) mod error; pub mod error;
pub use crate::error::ImageProcessingError; pub use crate::error::ImageProcessingError;