codex-mobile-bridge 0.3.3

Remote bridge and service manager for codex-mobile.
Documentation
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()
}