diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 62fceb34db..5bd4332c3c 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -13,7 +13,6 @@ use codex_app_server_protocol::AccountLoginCompletedNotification; use codex_app_server_protocol::AccountUpdatedNotification; use codex_app_server_protocol::AddConversationListenerParams; use codex_app_server_protocol::AddConversationSubscriptionResponse; -use codex_app_server_protocol::AppInfo as ApiAppInfo; use codex_app_server_protocol::AppsListParams; use codex_app_server_protocol::AppsListResponse; use codex_app_server_protocol::ArchiveConversationParams; @@ -3486,18 +3485,7 @@ impl CodexMessageProcessor { } let end = start.saturating_add(effective_limit).min(total); - let data = connectors[start..end] - .iter() - .cloned() - .map(|connector| ApiAppInfo { - id: connector.connector_id, - name: connector.connector_name, - description: connector.connector_description, - logo_url: connector.logo_url, - install_url: connector.install_url, - is_accessible: connector.is_accessible, - }) - .collect(); + let data = connectors[start..end].to_vec(); let next_cursor = if end < total { Some(end.to_string()) diff --git a/codex-rs/app-server/tests/suite/v2/app_list.rs b/codex-rs/app-server/tests/suite/v2/app_list.rs index 4c3e22ffa7..ae3904f6ce 100644 --- a/codex-rs/app-server/tests/suite/v2/app_list.rs +++ b/codex-rs/app-server/tests/suite/v2/app_list.rs @@ -20,7 +20,6 @@ use codex_app_server_protocol::AppsListResponse; use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::RequestId; use codex_core::auth::AuthCredentialsStoreMode; -use codex_core::connectors::ConnectorInfo; use pretty_assertions::assert_eq; use rmcp::handler::server::ServerHandler; use rmcp::model::JsonObject; @@ -71,18 +70,18 @@ async fn list_apps_returns_empty_when_connectors_disabled() -> Result<()> { #[tokio::test] async fn list_apps_returns_connectors_with_accessible_flags() -> Result<()> { let connectors = vec![ - ConnectorInfo { - connector_id: "alpha".to_string(), - connector_name: "Alpha".to_string(), - connector_description: Some("Alpha connector".to_string()), + AppInfo { + id: "alpha".to_string(), + name: "Alpha".to_string(), + description: Some("Alpha connector".to_string()), logo_url: Some("https://example.com/alpha.png".to_string()), install_url: None, is_accessible: false, }, - ConnectorInfo { - connector_id: "beta".to_string(), - connector_name: "beta".to_string(), - connector_description: None, + AppInfo { + id: "beta".to_string(), + name: "beta".to_string(), + description: None, logo_url: None, install_url: None, is_accessible: false, @@ -150,18 +149,18 @@ async fn list_apps_returns_connectors_with_accessible_flags() -> Result<()> { #[tokio::test] async fn list_apps_paginates_results() -> Result<()> { let connectors = vec![ - ConnectorInfo { - connector_id: "alpha".to_string(), - connector_name: "Alpha".to_string(), - connector_description: Some("Alpha connector".to_string()), + AppInfo { + id: "alpha".to_string(), + name: "Alpha".to_string(), + description: Some("Alpha connector".to_string()), logo_url: None, install_url: None, is_accessible: false, }, - ConnectorInfo { - connector_id: "beta".to_string(), - connector_name: "beta".to_string(), - connector_description: None, + AppInfo { + id: "beta".to_string(), + name: "beta".to_string(), + description: None, logo_url: None, install_url: None, is_accessible: false, @@ -289,7 +288,7 @@ impl ServerHandler for AppListMcpServer { } async fn start_apps_server( - connectors: Vec, + connectors: Vec, tools: Vec, ) -> Result<(String, JoinHandle<()>)> { let state = AppsServerState { diff --git a/codex-rs/chatgpt/src/connectors.rs b/codex-rs/chatgpt/src/connectors.rs index 5d913ae165..df87f8c712 100644 --- a/codex-rs/chatgpt/src/connectors.rs +++ b/codex-rs/chatgpt/src/connectors.rs @@ -7,7 +7,7 @@ use crate::chatgpt_client::chatgpt_post_request; use crate::chatgpt_token::get_chatgpt_token_data; use crate::chatgpt_token::init_chatgpt_token_from_auth; -pub use codex_core::connectors::ConnectorInfo; +pub use codex_core::connectors::AppInfo; pub use codex_core::connectors::connector_display_label; use codex_core::connectors::connector_install_url; pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools; @@ -33,10 +33,10 @@ enum PrincipalType { #[derive(Debug, Deserialize)] struct ListConnectorsResponse { - connectors: Vec, + connectors: Vec, } -pub async fn list_connectors(config: &Config) -> anyhow::Result> { +pub async fn list_connectors(config: &Config) -> anyhow::Result> { if !config.features.enabled(Feature::Connectors) { return Ok(Vec::new()); } @@ -49,7 +49,7 @@ pub async fn list_connectors(config: &Config) -> anyhow::Result anyhow::Result> { +pub async fn list_all_connectors(config: &Config) -> anyhow::Result> { if !config.features.enabled(Feature::Connectors) { return Ok(Vec::new()); } @@ -91,19 +91,17 @@ pub async fn list_all_connectors(config: &Config) -> anyhow::Result install_url, - None => connector_install_url(&connector.connector_name, &connector.connector_id), + None => connector_install_url(&connector.name, &connector.id), }; - connector.connector_name = - normalize_connector_name(&connector.connector_name, &connector.connector_id); - connector.connector_description = - normalize_connector_value(connector.connector_description.as_deref()); + connector.name = normalize_connector_name(&connector.name, &connector.id); + connector.description = normalize_connector_value(connector.description.as_deref()); connector.install_url = Some(install_url); connector.is_accessible = false; } connectors.sort_by(|left, right| { - left.connector_name - .cmp(&right.connector_name) - .then_with(|| left.connector_id.cmp(&right.connector_id)) + left.name + .cmp(&right.name) + .then_with(|| left.id.cmp(&right.id)) }); Ok(connectors) } diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index ea52aad32f..853696ea14 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -3024,9 +3024,9 @@ async fn run_auto_compact(sess: &Arc, turn_context: &Arc) } fn filter_connectors_for_input( - connectors: Vec, + connectors: Vec, input: &[ResponseItem], -) -> Vec { +) -> Vec { let user_messages = collect_user_messages(input); if user_messages.is_empty() { return Vec::new(); @@ -3039,7 +3039,7 @@ fn filter_connectors_for_input( } fn connector_inserted_in_messages( - connector: &connectors::ConnectorInfo, + connector: &connectors::AppInfo, user_messages: &[String], ) -> bool { let label = connectors::connector_display_label(connector); @@ -3053,11 +3053,11 @@ fn connector_inserted_in_messages( fn filter_codex_apps_mcp_tools( mut mcp_tools: HashMap, - connectors: &[connectors::ConnectorInfo], + connectors: &[connectors::AppInfo], ) -> HashMap { let allowed: HashSet<&str> = connectors .iter() - .map(|connector| connector.connector_id.as_str()) + .map(|connector| connector.id.as_str()) .collect(); mcp_tools.retain(|_, tool| { diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index fa97dacc3c..ebe3889045 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -3,9 +3,8 @@ use std::env; use std::path::PathBuf; use async_channel::unbounded; +pub use codex_app_server_protocol::AppInfo; use codex_protocol::protocol::SandboxPolicy; -use serde::Deserialize; -use serde::Serialize; use tokio_util::sync::CancellationToken; use crate::AuthManager; @@ -17,25 +16,9 @@ use crate::mcp::auth::compute_auth_statuses; use crate::mcp::with_codex_apps_mcp; use crate::mcp_connection_manager::McpConnectionManager; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConnectorInfo { - #[serde(rename = "id")] - pub connector_id: String, - #[serde(rename = "name")] - pub connector_name: String, - #[serde(default, rename = "description")] - pub connector_description: Option, - #[serde(default, rename = "logo_url")] - pub logo_url: Option, - #[serde(default, rename = "install_url")] - pub install_url: Option, - #[serde(default)] - pub is_accessible: bool, -} - pub async fn list_accessible_connectors_from_mcp_tools( config: &Config, -) -> anyhow::Result> { +) -> anyhow::Result> { if !config.features.enabled(Feature::Connectors) { return Ok(Vec::new()); } @@ -86,13 +69,13 @@ fn auth_manager_from_config(config: &Config) -> std::sync::Arc { ) } -pub fn connector_display_label(connector: &ConnectorInfo) -> String { - format_connector_label(&connector.connector_name, &connector.connector_id) +pub fn connector_display_label(connector: &AppInfo) -> String { + format_connector_label(&connector.name, &connector.id) } pub(crate) fn accessible_connectors_from_mcp_tools( mcp_tools: &HashMap, -) -> Vec { +) -> Vec { let tools = mcp_tools.values().filter_map(|tool| { if tool.server_name != CODEX_APPS_MCP_SERVER_NAME { return None; @@ -105,30 +88,27 @@ pub(crate) fn accessible_connectors_from_mcp_tools( } pub fn merge_connectors( - connectors: Vec, - accessible_connectors: Vec, -) -> Vec { - let mut merged: HashMap = connectors + connectors: Vec, + accessible_connectors: Vec, +) -> Vec { + let mut merged: HashMap = connectors .into_iter() .map(|mut connector| { connector.is_accessible = false; - (connector.connector_id.clone(), connector) + (connector.id.clone(), connector) }) .collect(); for mut connector in accessible_connectors { connector.is_accessible = true; - let connector_id = connector.connector_id.clone(); + let connector_id = connector.id.clone(); if let Some(existing) = merged.get_mut(&connector_id) { existing.is_accessible = true; - if existing.connector_name == existing.connector_id - && connector.connector_name != connector.connector_id - { - existing.connector_name = connector.connector_name; + if existing.name == existing.id && connector.name != connector.id { + existing.name = connector.name; } - if existing.connector_description.is_none() && connector.connector_description.is_some() - { - existing.connector_description = connector.connector_description; + if existing.description.is_none() && connector.description.is_some() { + existing.description = connector.description; } if existing.logo_url.is_none() && connector.logo_url.is_some() { existing.logo_url = connector.logo_url; @@ -141,23 +121,20 @@ pub fn merge_connectors( let mut merged = merged.into_values().collect::>(); for connector in &mut merged { if connector.install_url.is_none() { - connector.install_url = Some(connector_install_url( - &connector.connector_name, - &connector.connector_id, - )); + connector.install_url = Some(connector_install_url(&connector.name, &connector.id)); } } merged.sort_by(|left, right| { right .is_accessible .cmp(&left.is_accessible) - .then_with(|| left.connector_name.cmp(&right.connector_name)) - .then_with(|| left.connector_id.cmp(&right.connector_id)) + .then_with(|| left.name.cmp(&right.name)) + .then_with(|| left.id.cmp(&right.id)) }); merged } -fn collect_accessible_connectors(tools: I) -> Vec +fn collect_accessible_connectors(tools: I) -> Vec where I: IntoIterator)>, { @@ -172,14 +149,14 @@ where connectors.insert(connector_id, connector_name); } } - let mut accessible: Vec = connectors + let mut accessible: Vec = connectors .into_iter() - .map(|(connector_id, connector_name)| ConnectorInfo { - install_url: Some(connector_install_url(&connector_name, &connector_id)), - connector_id, - connector_name, - connector_description: None, + .map(|(connector_id, connector_name)| AppInfo { + id: connector_id.clone(), + name: connector_name.clone(), + description: None, logo_url: None, + install_url: Some(connector_install_url(&connector_name, &connector_id)), is_accessible: true, }) .collect(); @@ -187,8 +164,8 @@ where right .is_accessible .cmp(&left.is_accessible) - .then_with(|| left.connector_name.cmp(&right.connector_name)) - .then_with(|| left.connector_id.cmp(&right.connector_id)) + .then_with(|| left.name.cmp(&right.name)) + .then_with(|| left.id.cmp(&right.id)) }); accessible } diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index a499ffdca6..5b18988499 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -10,7 +10,7 @@ use std::path::PathBuf; -use codex_chatgpt::connectors::ConnectorInfo; +use codex_chatgpt::connectors::AppInfo; use codex_common::approval_presets::ApprovalPreset; use codex_core::protocol::Event; use codex_core::protocol::RateLimitSnapshot; @@ -42,7 +42,7 @@ pub(crate) enum WindowsSandboxFallbackReason { #[derive(Debug, Clone)] pub(crate) struct ConnectorsSnapshot { - pub(crate) connectors: Vec, + pub(crate) connectors: Vec, } #[allow(clippy::large_enum_variant)] diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 9be8616bbd..8125adcd18 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -141,7 +141,7 @@ use crate::clipboard_paste::pasted_image_format; use crate::history_cell; use crate::ui_consts::LIVE_PREFIX_COLS; use codex_chatgpt::connectors; -use codex_chatgpt::connectors::ConnectorInfo; +use codex_chatgpt::connectors::AppInfo; use codex_core::skills::model::SkillMetadata; use codex_file_search::FileMatch; use std::cell::RefCell; @@ -2496,7 +2496,7 @@ impl ChatComposer { } let display_name = connectors::connector_display_label(connector); let description = Some(Self::connector_brief_description(connector)); - let search_terms = vec![display_name.clone(), connector.connector_id.clone()]; + let search_terms = vec![display_name.clone(), connector.id.clone()]; mentions.push(MentionItem { display_name: display_name.clone(), description, @@ -2509,7 +2509,7 @@ impl ChatComposer { mentions } - fn connector_brief_description(connector: &ConnectorInfo) -> String { + fn connector_brief_description(connector: &AppInfo) -> String { let status_label = if connector.is_accessible { "Connected" } else { @@ -2521,9 +2521,9 @@ impl ChatComposer { } } - fn connector_description(connector: &ConnectorInfo) -> Option { + fn connector_description(connector: &AppInfo) -> Option { connector - .connector_description + .description .as_deref() .map(str::trim) .filter(|description| !description.is_empty()) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 045facc3e8..70314cc104 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -4721,13 +4721,13 @@ impl ChatWidget { self.request_redraw(); } - fn open_connectors_popup(&mut self, connectors: &[connectors::ConnectorInfo]) { + fn open_connectors_popup(&mut self, connectors: &[connectors::AppInfo]) { let mut items: Vec = Vec::with_capacity(connectors.len()); for connector in connectors { let connector_label = connectors::connector_display_label(connector); let connector_title = connector_label.clone(); let description = Self::connector_brief_description(connector); - let search_value = format!("{connector_label} {}", connector.connector_id); + let search_value = format!("{connector_label} {}", connector.id); let mut item = SelectionItem { name: connector_label, description: Some(description), @@ -4793,7 +4793,7 @@ impl ChatWidget { ]) } - fn connector_brief_description(connector: &connectors::ConnectorInfo) -> String { + fn connector_brief_description(connector: &connectors::AppInfo) -> String { let status_label = if connector.is_accessible { "Connected" } else { @@ -4805,9 +4805,9 @@ impl ChatWidget { } } - fn connector_description(connector: &connectors::ConnectorInfo) -> Option { + fn connector_description(connector: &connectors::AppInfo) -> Option { connector - .connector_description + .description .as_deref() .map(str::trim) .filter(|description| !description.is_empty())