Move terminal module to terminal-detection crate (#15216)

- Move core/src/terminal.rs and its tests into a standalone
terminal-detection workspace crate.
- Update direct consumers to depend on codex-terminal-detection and
import terminal APIs directly.

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Ahmed Ibrahim 2026-03-19 14:08:04 -07:00 committed by GitHub
parent 668330acc1
commit 7eb19e5319
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 73 additions and 24 deletions

13
codex-rs/Cargo.lock generated
View file

@ -1664,6 +1664,7 @@ dependencies = [
"codex-rmcp-client",
"codex-state",
"codex-stdio-to-uds",
"codex-terminal-detection",
"codex-tui",
"codex-tui-app-server",
"codex-utils-cargo-bin",
@ -1858,6 +1859,7 @@ dependencies = [
"codex-shell-escalation",
"codex-skills",
"codex-state",
"codex-terminal-detection",
"codex-test-macros",
"codex-utils-absolute-path",
"codex-utils-cache",
@ -2507,6 +2509,14 @@ dependencies = [
"uds_windows",
]
[[package]]
name = "codex-terminal-detection"
version = "0.0.0"
dependencies = [
"pretty_assertions",
"tracing",
]
[[package]]
name = "codex-test-macros"
version = "0.0.0"
@ -2542,6 +2552,7 @@ dependencies = [
"codex-protocol",
"codex-shell-command",
"codex-state",
"codex-terminal-detection",
"codex-tui-app-server",
"codex-utils-absolute-path",
"codex-utils-approval-presets",
@ -2633,6 +2644,7 @@ dependencies = [
"codex-protocol",
"codex-shell-command",
"codex-state",
"codex-terminal-detection",
"codex-utils-absolute-path",
"codex-utils-approval-presets",
"codex-utils-cargo-bin",
@ -5815,6 +5827,7 @@ dependencies = [
"anyhow",
"codex-core",
"codex-mcp-server",
"codex-terminal-detection",
"codex-utils-cargo-bin",
"core_test_support",
"os_info",

View file

@ -66,6 +66,7 @@ members = [
"codex-client",
"codex-api",
"state",
"terminal-detection",
"codex-experimental-api-macros",
"test-macros",
"package-manager",
@ -131,6 +132,7 @@ codex-skills = { path = "skills" }
codex-state = { path = "state" }
codex-stdio-to-uds = { path = "stdio-to-uds" }
codex-test-macros = { path = "test-macros" }
codex-terminal-detection = { path = "terminal-detection" }
codex-tui = { path = "tui" }
codex-tui-app-server = { path = "tui_app_server" }
codex-utils-absolute-path = { path = "utils/absolute-path" }

View file

@ -37,6 +37,7 @@ codex-responses-api-proxy = { workspace = true }
codex-rmcp-client = { workspace = true }
codex-state = { workspace = true }
codex-stdio-to-uds = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-tui = { workspace = true }
codex-tui-app-server = { workspace = true }
libc = { workspace = true }

View file

@ -50,7 +50,7 @@ use codex_core::config::edit::ConfigEditsBuilder;
use codex_core::config::find_codex_home;
use codex_core::features::Stage;
use codex_core::features::is_known_feature_key;
use codex_core::terminal::TerminalName;
use codex_terminal_detection::TerminalName;
/// Codex CLI
///
@ -1049,7 +1049,7 @@ async fn run_interactive_tui(
interactive.prompt = Some(prompt.replace("\r\n", "\n").replace('\r', "\n"));
}
let terminal_info = codex_core::terminal::terminal_info();
let terminal_info = codex_terminal_detection::terminal_info();
if terminal_info.name == TerminalName::Dumb {
if !(std::io::stdin().is_terminal() && std::io::stderr().is_terminal()) {
return Ok(AppExitInfo::fatal(

View file

@ -48,6 +48,7 @@ codex-artifacts = { workspace = true }
codex-protocol = { workspace = true }
codex-rmcp-client = { workspace = true }
codex-state = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-cache = { workspace = true }
codex-utils-image = { workspace = true }

View file

@ -49,7 +49,6 @@ use crate::stream_events_utils::handle_output_item_done;
use crate::stream_events_utils::last_assistant_message_from_item;
use crate::stream_events_utils::raw_assistant_output_text_from_item;
use crate::stream_events_utils::record_completed_response_item;
use crate::terminal;
use crate::truncate::TruncationPolicy;
use crate::turn_metadata::TurnMetadataState;
use crate::util::error_or_panic;
@ -117,6 +116,7 @@ use codex_protocol::request_user_input::RequestUserInputArgs;
use codex_protocol::request_user_input::RequestUserInputResponse;
use codex_rmcp_client::ElicitationResponse;
use codex_rmcp_client::OAuthCredentialsStoreMode;
use codex_terminal_detection::user_agent;
use codex_utils_stream_parser::AssistantTextChunk;
use codex_utils_stream_parser::AssistantTextStreamParser;
use codex_utils_stream_parser::ProposedPlanSegment;
@ -1581,7 +1581,7 @@ impl Session {
let account_id = auth.and_then(CodexAuth::get_account_id);
let account_email = auth.and_then(CodexAuth::get_account_email);
let originator = crate::default_client::originator().value;
let terminal_type = terminal::user_agent();
let terminal_type = user_agent();
let session_model = session_configuration.collaboration_mode.model().to_string();
let auth_env_telemetry = collect_auth_env_telemetry(
&session_configuration.provider,

View file

@ -4,6 +4,7 @@ use codex_client::BuildCustomCaTransportError;
use codex_client::CodexHttpClient;
pub use codex_client::CodexRequestBuilder;
use codex_client::build_reqwest_client_with_custom_ca;
use codex_terminal_detection::user_agent;
use reqwest::header::HeaderMap;
use reqwest::header::HeaderValue;
use std::sync::LazyLock;
@ -130,7 +131,7 @@ pub fn get_codex_user_agent() -> String {
os_info.os_type(),
os_info.version(),
os_info.architecture().unwrap_or("unknown"),
crate::terminal::user_agent()
user_agent()
);
let suffix = USER_AGENT_SUFFIX
.lock()

View file

@ -120,7 +120,6 @@ pub mod shell_snapshot;
pub mod skills;
pub mod spawn;
pub mod state_db;
pub mod terminal;
mod tools;
pub mod turn_diff_tracker;
mod turn_metadata;

View file

@ -36,7 +36,7 @@ pub(crate) struct ExecServerFileSystem {
impl Default for ExecServerFileSystem {
fn default() -> Self {
Self {
file_system: Arc::new(Environment.get_filesystem()),
file_system: Arc::new(Environment::default().get_filesystem()),
}
}
}

View file

@ -11,6 +11,7 @@ path = "lib.rs"
anyhow = { workspace = true }
codex-core = { workspace = true }
codex-mcp-server = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
rmcp = { workspace = true }
os_info = { workspace = true }

View file

@ -11,6 +11,7 @@ use tokio::process::ChildStdout;
use anyhow::Context;
use codex_mcp_server::CodexToolCallParam;
use codex_terminal_detection::user_agent;
use pretty_assertions::assert_eq;
use rmcp::model::CallToolRequestParams;
@ -156,7 +157,7 @@ impl McpProcess {
os_info.os_type(),
os_info.version(),
os_info.architecture().unwrap_or("unknown"),
codex_core::terminal::user_agent()
user_agent()
);
let JsonRpcMessage::Response(JsonRpcResponse {
jsonrpc,

View file

@ -0,0 +1,6 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "terminal-detection",
crate_name = "codex_terminal_detection",
)

View file

@ -0,0 +1,18 @@
[package]
name = "codex-terminal-detection"
version.workspace = true
edition.workspace = true
license.workspace = true
[lib]
name = "codex_terminal_detection"
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
tracing = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -43,6 +43,7 @@ codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-shell-command = { workspace = true }
codex-state = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-tui-app-server = { workspace = true }
codex-utils-approval-presets = { workspace = true }
codex-utils-absolute-path = { workspace = true }

View file

@ -80,6 +80,7 @@ use codex_protocol::protocol::SessionConfiguredEvent;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SkillErrorInfo;
use codex_protocol::protocol::TokenUsage;
use codex_terminal_detection::user_agent;
use codex_utils_absolute_path::AbsolutePathBuf;
use color_eyre::eyre::Result;
use color_eyre::eyre::WrapErr;
@ -2079,7 +2080,7 @@ impl App {
auth_mode,
codex_core::default_client::originator().value,
config.otel.log_user_prompt,
codex_core::terminal::user_agent(),
user_agent(),
SessionSource::Cli,
);
if config

View file

@ -76,8 +76,6 @@ use codex_core::models_manager::manager::ModelsManager;
use codex_core::plugins::PluginsManager;
use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
use codex_core::skills::model::SkillMetadata;
use codex_core::terminal::TerminalName;
use codex_core::terminal::terminal_info;
#[cfg(target_os = "windows")]
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_otel::RuntimeMetricsSummary;
@ -155,6 +153,8 @@ use codex_protocol::request_permissions::RequestPermissionsEvent;
use codex_protocol::request_user_input::RequestUserInputEvent;
use codex_protocol::user_input::TextElement;
use codex_protocol::user_input::UserInput;
use codex_terminal_detection::TerminalName;
use codex_terminal_detection::terminal_info;
use codex_utils_sleep_inhibitor::SleepInhibitor;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;

View file

@ -38,7 +38,6 @@ use codex_core::features::Feature;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_core::models_manager::manager::ModelsManager;
use codex_core::skills::model::SkillMetadata;
use codex_core::terminal::TerminalName;
use codex_otel::RuntimeMetricsSummary;
use codex_otel::SessionTelemetry;
use codex_protocol::ThreadId;
@ -121,6 +120,7 @@ use codex_protocol::request_user_input::RequestUserInputQuestion;
use codex_protocol::request_user_input::RequestUserInputQuestionOption;
use codex_protocol::user_input::TextElement;
use codex_protocol::user_input::UserInput;
use codex_terminal_detection::TerminalName;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_approval_presets::builtin_approval_presets;
use crossterm::event::KeyCode;

View file

@ -93,9 +93,9 @@ use crate::terminal_palette::indexed_color;
use crate::terminal_palette::rgb_color;
use crate::terminal_palette::stdout_color_level;
use codex_core::git_info::get_git_repo_root;
use codex_core::terminal::TerminalName;
use codex_core::terminal::terminal_info;
use codex_protocol::protocol::FileChange;
use codex_terminal_detection::TerminalName;
use codex_terminal_detection::terminal_info;
/// Classifies a diff line for gutter sign rendering and style selection.
///

View file

@ -33,7 +33,6 @@ use codex_core::format_exec_policy_error_with_source;
use codex_core::path_utils;
use codex_core::read_session_meta_line;
use codex_core::state_db::get_state_db;
use codex_core::terminal::Multiplexer;
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_protocol::ThreadId;
use codex_protocol::config_types::AltScreenMode;
@ -43,6 +42,8 @@ use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::RolloutLine;
use codex_state::log_db;
use codex_terminal_detection::Multiplexer;
use codex_terminal_detection::terminal_info;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_oss::ensure_oss_provider_ready;
use codex_utils_oss::get_default_model_for_oss_provider;
@ -1138,7 +1139,7 @@ fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScree
AltScreenMode::Always => true,
AltScreenMode::Never => false,
AltScreenMode::Auto => {
let terminal_info = codex_core::terminal::terminal_info();
let terminal_info = terminal_info();
!matches!(terminal_info.multiplexer, Some(Multiplexer::Zellij { .. }))
}
}

View file

@ -48,6 +48,7 @@ codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-shell-command = { workspace = true }
codex-state = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-utils-approval-presets = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-cli = { workspace = true }

View file

@ -96,6 +96,7 @@ use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SkillErrorInfo;
use codex_protocol::protocol::TokenUsage;
use codex_terminal_detection::user_agent;
use codex_utils_absolute_path::AbsolutePathBuf;
use color_eyre::eyre::Result;
use color_eyre::eyre::WrapErr;
@ -2965,7 +2966,7 @@ impl App {
auth_mode,
codex_core::default_client::originator().value,
config.otel.log_user_prompt,
codex_core::terminal::user_agent(),
user_agent(),
SessionSource::Cli,
);
if config

View file

@ -94,8 +94,6 @@ use codex_core::git_info::local_git_branches;
use codex_core::plugins::PluginsManager;
use codex_core::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
use codex_core::skills::model::SkillMetadata;
use codex_core::terminal::TerminalName;
use codex_core::terminal::terminal_info;
#[cfg(target_os = "windows")]
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_otel::RuntimeMetricsSummary;
@ -199,6 +197,8 @@ use codex_protocol::request_user_input::RequestUserInputEvent;
use codex_protocol::request_user_input::RequestUserInputQuestionOption;
use codex_protocol::user_input::TextElement;
use codex_protocol::user_input::UserInput;
use codex_terminal_detection::TerminalName;
use codex_terminal_detection::terminal_info;
use codex_utils_sleep_inhibitor::SleepInhibitor;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;

View file

@ -61,7 +61,6 @@ use codex_core::features::FEATURES;
use codex_core::features::Feature;
use codex_core::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use codex_core::skills::model::SkillMetadata;
use codex_core::terminal::TerminalName;
use codex_otel::RuntimeMetricsSummary;
use codex_otel::SessionTelemetry;
use codex_protocol::ThreadId;
@ -144,6 +143,7 @@ use codex_protocol::request_user_input::RequestUserInputQuestion;
use codex_protocol::request_user_input::RequestUserInputQuestionOption;
use codex_protocol::user_input::TextElement;
use codex_protocol::user_input::UserInput;
use codex_terminal_detection::TerminalName;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_approval_presets::builtin_approval_presets;
use crossterm::event::KeyCode;

View file

@ -93,9 +93,9 @@ use crate::terminal_palette::indexed_color;
use crate::terminal_palette::rgb_color;
use crate::terminal_palette::stdout_color_level;
use codex_core::git_info::get_git_repo_root;
use codex_core::terminal::TerminalName;
use codex_core::terminal::terminal_info;
use codex_protocol::protocol::FileChange;
use codex_terminal_detection::TerminalName;
use codex_terminal_detection::terminal_info;
/// Classifies a diff line for gutter sign rendering and style selection.
///

View file

@ -38,7 +38,6 @@ use codex_core::format_exec_policy_error_with_source;
use codex_core::path_utils;
use codex_core::read_session_meta_line;
use codex_core::state_db::get_state_db;
use codex_core::terminal::Multiplexer;
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_protocol::ThreadId;
use codex_protocol::config_types::AltScreenMode;
@ -49,6 +48,8 @@ use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::RolloutLine;
use codex_protocol::protocol::TurnContextItem;
use codex_state::log_db;
use codex_terminal_detection::Multiplexer;
use codex_terminal_detection::terminal_info;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_oss::ensure_oss_provider_ready;
use codex_utils_oss::get_default_model_for_oss_provider;
@ -1485,7 +1486,7 @@ fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScree
AltScreenMode::Always => true,
AltScreenMode::Never => false,
AltScreenMode::Auto => {
let terminal_info = codex_core::terminal::terminal_info();
let terminal_info = terminal_info();
!matches!(terminal_info.multiplexer, Some(Multiplexer::Zellij { .. }))
}
}