Skip to content

Commit e9ea251

Browse files
echobtfactorydroid
andauthored
fix(tui): wait for /me API response before displaying TUI (#476)
Previously, the TUI was initialized immediately and the /me API call was made in the background with a 500ms timeout. If the response didn't arrive in time, the welcome screen would display 'User' as a placeholder instead of the actual username. This change moves the /me API call to execute BEFORE the TUI is initialized, ensuring the user's name is available when the welcome screen is rendered. The API timeout remains at 5 seconds to allow sufficient time for the response, even on slower connections. Co-authored-by: Droid Agent <droid@factory.ai>
1 parent e9f9e43 commit e9ea251

1 file changed

Lines changed: 56 additions & 49 deletions

File tree

cortex-tui/src/runner/app_runner.rs

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -613,10 +613,58 @@ impl AppRunner {
613613
}
614614

615615
// ====================================================================
616-
// PERFORMANCE OPTIMIZATION: Start TUI immediately, fetch data in background
616+
// Fetch user info BEFORE showing TUI to avoid "User" placeholder
617+
// ====================================================================
618+
let mut user_name: Option<String> = None;
619+
let mut user_email: Option<String> = None;
620+
let mut org_name: Option<String> = None;
621+
622+
// Fetch user info from /me API - wait for this before showing TUI
623+
if let Some(token) = cortex_login::get_auth_token() {
624+
tracing::debug!("Fetching user info from /me API...");
625+
if let Ok(client) = cortex_engine::create_default_client() {
626+
match client
627+
.get("https://api.cortex.foundation/auth/me")
628+
.bearer_auth(&token)
629+
.timeout(std::time::Duration::from_secs(5))
630+
.send()
631+
.await
632+
{
633+
Ok(resp) if resp.status().is_success() => {
634+
if let Ok(json) = resp.json::<serde_json::Value>().await {
635+
if let Some(name) = json.get("name").and_then(|v| v.as_str()) {
636+
user_name = Some(name.to_string());
637+
tracing::info!("User info loaded: {}", name);
638+
}
639+
if let Some(email) = json.get("email").and_then(|v| v.as_str()) {
640+
user_email = Some(email.to_string());
641+
}
642+
if let Some(orgs) = json.get("organizations").and_then(|v| v.as_array())
643+
{
644+
if let Some(first_org) = orgs.first() {
645+
if let Some(org) =
646+
first_org.get("org_name").and_then(|v| v.as_str())
647+
{
648+
org_name = Some(org.to_string());
649+
}
650+
}
651+
}
652+
}
653+
}
654+
Ok(resp) => {
655+
tracing::warn!("Failed to fetch user info: HTTP {}", resp.status());
656+
}
657+
Err(e) => {
658+
tracing::warn!("Failed to fetch user info: {}", e);
659+
}
660+
}
661+
}
662+
}
663+
664+
// ====================================================================
665+
// Now initialize TUI after we have user info
617666
// ====================================================================
618667

619-
// Initialize terminal FIRST to minimize perceived latency
620668
let mut terminal = CortexTerminal::with_options(self.terminal_options)?;
621669
terminal.set_title("Cortex")?;
622670

@@ -641,12 +689,17 @@ impl AppRunner {
641689

642690
let _session_id = cortex_session.id().to_string();
643691

644-
// Create app state
692+
// Create app state with user info already loaded
645693
let mut app_state = AppState::new()
646694
.with_model(model.clone())
647695
.with_provider(provider.clone())
648696
.with_terminal_size(width, height);
649697

698+
// Set user info from pre-fetched data
699+
app_state.user_name = user_name;
700+
app_state.user_email = user_email;
701+
app_state.org_name = org_name;
702+
650703
// ====================================================================
651704
// BACKGROUND TASKS: Spawn non-blocking operations in parallel
652705
// ====================================================================
@@ -655,31 +708,6 @@ impl AppRunner {
655708
let session_history_task =
656709
tokio::task::spawn_blocking(|| CortexSession::list_recent(50).ok());
657710

658-
// 2. Fetch user info from API (HTTP request) - spawn in background
659-
let user_info_task = {
660-
let token = cortex_login::get_auth_token();
661-
tokio::spawn(async move {
662-
if let Some(token) = token {
663-
if let Ok(client) = cortex_engine::create_default_client() {
664-
if let Ok(resp) = client
665-
.get("https://api.cortex.foundation/auth/me")
666-
.bearer_auth(&token)
667-
.timeout(std::time::Duration::from_secs(5))
668-
.send()
669-
.await
670-
{
671-
if resp.status().is_success() {
672-
if let Ok(json) = resp.json::<serde_json::Value>().await {
673-
return Some(json);
674-
}
675-
}
676-
}
677-
}
678-
}
679-
None
680-
})
681-
};
682-
683711
// 3. Models prefetch and session validation - spawn in background
684712
// We use a channel to receive results and update provider_manager later
685713
let models_and_validation_task = {
@@ -769,27 +797,6 @@ impl AppRunner {
769797
);
770798
}
771799

772-
// Wait for user info (with short timeout - don't block TUI)
773-
if let Ok(Some(json)) =
774-
tokio::time::timeout(std::time::Duration::from_millis(500), user_info_task)
775-
.await
776-
.unwrap_or(Ok(None))
777-
{
778-
if let Some(name) = json.get("name").and_then(|v| v.as_str()) {
779-
app_state.user_name = Some(name.to_string());
780-
}
781-
if let Some(email) = json.get("email").and_then(|v| v.as_str()) {
782-
app_state.user_email = Some(email.to_string());
783-
}
784-
if let Some(orgs) = json.get("organizations").and_then(|v| v.as_array()) {
785-
if let Some(first_org) = orgs.first() {
786-
if let Some(org_name) = first_org.get("org_name").and_then(|v| v.as_str()) {
787-
app_state.org_name = Some(org_name.to_string());
788-
}
789-
}
790-
}
791-
}
792-
793800
// Check validation result (with short timeout - don't block TUI)
794801
// We'll handle models update after event loop is created
795802
let validation_result = tokio::time::timeout(

0 commit comments

Comments
 (0)