detect terminal and include in request headers (#2437)
This adds the terminal version to the UA header.
This commit is contained in:
@@ -49,6 +49,7 @@ pub(crate) mod safety;
|
|||||||
pub mod seatbelt;
|
pub mod seatbelt;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
pub mod spawn;
|
pub mod spawn;
|
||||||
|
pub mod terminal;
|
||||||
pub mod turn_diff_tracker;
|
pub mod turn_diff_tracker;
|
||||||
pub mod user_agent;
|
pub mod user_agent;
|
||||||
mod user_notification;
|
mod user_notification;
|
||||||
|
|||||||
72
codex-rs/core/src/terminal.rs
Normal file
72
codex-rs/core/src/terminal.rs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static TERMINAL: OnceLock<String> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn user_agent() -> String {
|
||||||
|
TERMINAL.get_or_init(detect_terminal).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sanitize a header value to be used in a User-Agent string.
|
||||||
|
///
|
||||||
|
/// This function replaces any characters that are not allowed in a User-Agent string with an underscore.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `value` - The value to sanitize.
|
||||||
|
fn is_valid_header_value_char(c: char) -> bool {
|
||||||
|
c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.' || c == '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sanitize_header_value(value: String) -> String {
|
||||||
|
value.replace(|c| !is_valid_header_value_char(c), "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detect_terminal() -> String {
|
||||||
|
sanitize_header_value(
|
||||||
|
if let Ok(tp) = std::env::var("TERM_PROGRAM")
|
||||||
|
&& !tp.trim().is_empty()
|
||||||
|
{
|
||||||
|
let ver = std::env::var("TERM_PROGRAM_VERSION").ok();
|
||||||
|
match ver {
|
||||||
|
Some(v) if !v.trim().is_empty() => format!("{tp}/{v}"),
|
||||||
|
_ => tp,
|
||||||
|
}
|
||||||
|
} else if let Ok(v) = std::env::var("WEZTERM_VERSION") {
|
||||||
|
if !v.trim().is_empty() {
|
||||||
|
format!("WezTerm/{v}")
|
||||||
|
} else {
|
||||||
|
"WezTerm".to_string()
|
||||||
|
}
|
||||||
|
} else if std::env::var("KITTY_WINDOW_ID").is_ok()
|
||||||
|
|| std::env::var("TERM")
|
||||||
|
.map(|t| t.contains("kitty"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
"kitty".to_string()
|
||||||
|
} else if std::env::var("ALACRITTY_SOCKET").is_ok()
|
||||||
|
|| std::env::var("TERM")
|
||||||
|
.map(|t| t == "alacritty")
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
"Alacritty".to_string()
|
||||||
|
} else if let Ok(v) = std::env::var("KONSOLE_VERSION") {
|
||||||
|
if !v.trim().is_empty() {
|
||||||
|
format!("Konsole/{v}")
|
||||||
|
} else {
|
||||||
|
"Konsole".to_string()
|
||||||
|
}
|
||||||
|
} else if std::env::var("GNOME_TERMINAL_SCREEN").is_ok() {
|
||||||
|
return "gnome-terminal".to_string();
|
||||||
|
} else if let Ok(v) = std::env::var("VTE_VERSION") {
|
||||||
|
if !v.trim().is_empty() {
|
||||||
|
format!("VTE/{v}")
|
||||||
|
} else {
|
||||||
|
"VTE".to_string()
|
||||||
|
}
|
||||||
|
} else if std::env::var("WT_SESSION").is_ok() {
|
||||||
|
return "WindowsTerminal".to_string();
|
||||||
|
} else {
|
||||||
|
std::env::var("TERM").unwrap_or_else(|_| "unknown".to_string())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -4,11 +4,12 @@ pub fn get_codex_user_agent(originator: Option<&str>) -> String {
|
|||||||
let build_version = env!("CARGO_PKG_VERSION");
|
let build_version = env!("CARGO_PKG_VERSION");
|
||||||
let os_info = os_info::get();
|
let os_info = os_info::get();
|
||||||
format!(
|
format!(
|
||||||
"{}/{build_version} ({} {}; {})",
|
"{}/{build_version} ({} {}; {}) {}",
|
||||||
originator.unwrap_or(DEFAULT_ORIGINATOR),
|
originator.unwrap_or(DEFAULT_ORIGINATOR),
|
||||||
os_info.os_type(),
|
os_info.os_type(),
|
||||||
os_info.version(),
|
os_info.version(),
|
||||||
os_info.architecture().unwrap_or("unknown"),
|
os_info.architecture().unwrap_or("unknown"),
|
||||||
|
crate::terminal::user_agent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +28,10 @@ mod tests {
|
|||||||
fn test_macos() {
|
fn test_macos() {
|
||||||
use regex_lite::Regex;
|
use regex_lite::Regex;
|
||||||
let user_agent = get_codex_user_agent(None);
|
let user_agent = get_codex_user_agent(None);
|
||||||
let re =
|
let re = Regex::new(
|
||||||
Regex::new(r"^codex_cli_rs/\d+\.\d+\.\d+ \(Mac OS \d+\.\d+\.\d+; (x86_64|arm64)\)$")
|
r"^codex_cli_rs/\d+\.\d+\.\d+ \(Mac OS \d+\.\d+\.\d+; (x86_64|arm64)\) (\S+)$",
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
assert!(re.is_match(&user_agent));
|
assert!(re.is_match(&user_agent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user