use anyhow::{Context, Result};
use serde_json::Value;
use super::{
APP_SERVER_OPT_OUT_NOTIFICATION_METHODS, AppServerLaunchConfig, InitializeInfo,
};
pub(super) fn parse_initialize_info(value: &Value) -> Result<InitializeInfo> {
Ok(InitializeInfo {
user_agent: required_string(value, "userAgent")?.to_string(),
codex_home: required_string(value, "codexHome")?.to_string(),
platform_family: required_string(value, "platformFamily")?.to_string(),
platform_os: required_string(value, "platformOs")?.to_string(),
})
}
fn required_string<'a>(value: &'a Value, key: &str) -> Result<&'a str> {
value
.get(key)
.and_then(Value::as_str)
.with_context(|| format!("缺少字段 {key}"))
}
pub(super) fn build_spawn_error_context(launch_config: &AppServerLaunchConfig) -> String {
let cwd = std::env::current_dir()
.map(|path| path.display().to_string())
.unwrap_or_else(|_| "<unknown>".to_string());
let path_env = std::env::var("PATH").unwrap_or_else(|_| "<unset>".to_string());
let codex_home = launch_config
.codex_home
.as_ref()
.map(|path| path.display().to_string())
.unwrap_or_else(|| "<unset>".to_string());
format!(
"启动 {} app-server 失败(runtime={} cwd={} CODEX_HOME={} PATH={})",
launch_config.codex_binary, launch_config.runtime_id, cwd, codex_home, path_env
)
}
pub(super) fn parse_app_server_stderr_line(line: &str) -> (String, String) {
let normalized = line.trim().to_string();
let upper = normalized.to_uppercase();
let level = if upper.contains(" ERROR ") || upper.starts_with("ERROR ") {
"error"
} else if upper.contains(" WARN ") || upper.starts_with("WARN ") || upper.contains(" WARNING ")
{
"warn"
} else if upper.contains(" DEBUG ") || upper.starts_with("DEBUG ") {
"debug"
} else {
"info"
};
(level.to_string(), normalized)
}
pub(super) fn default_opt_out_notification_methods() -> Vec<String> {
APP_SERVER_OPT_OUT_NOTIFICATION_METHODS
.iter()
.map(|method| (*method).to_string())
.collect()
}