magi-code 0.102.1

Repository-aware CLI coding agent for terminal work
Documentation
/// Opens an HTTP(S) URL with the platform browser handler.
/// May block during handler startup; UI callers must run this on a worker.
///
/// Rejects other schemes and NUL bytes so platform handlers never receive
/// `file:`, custom-scheme, or option-like arguments.
pub(crate) fn open_url_in_browser(url: &str) -> std::io::Result<()> {
    validate_browser_url(url)?;
    open_validated_url(url)
}

fn validate_browser_url(url: &str) -> std::io::Result<()> {
    let parsed = reqwest::Url::parse(url).map_err(|_| {
        std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid browser URL")
    })?;
    if !matches!(parsed.scheme(), "http" | "https") || url.contains('\0') {
        return Err(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "browser URL must use HTTP or HTTPS",
        ));
    }
    Ok(())
}

#[cfg(target_os = "windows")]
fn open_validated_url(url: &str) -> std::io::Result<()> {
    use windows_sys::Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOWNORMAL};
    let url: Vec<u16> = url.encode_utf16().chain(Some(0)).collect();
    let operation: Vec<u16> = "open".encode_utf16().chain(Some(0)).collect();
    // SAFETY: both strings are NUL-terminated and live throughout the call;
    // optional window, parameters, and directory pointers are null.
    let result = unsafe {
        ShellExecuteW(
            std::ptr::null_mut(),
            operation.as_ptr(),
            url.as_ptr(),
            std::ptr::null(),
            std::ptr::null(),
            SW_SHOWNORMAL,
        )
    };
    if result as isize <= 32 {
        Err(std::io::Error::other("could not open browser"))
    } else {
        Ok(())
    }
}

#[cfg(not(target_os = "windows"))]
fn open_validated_url(url: &str) -> std::io::Result<()> {
    let opener = if cfg!(target_os = "macos") {
        "open"
    } else {
        "xdg-open"
    };
    let mut child = std::process::Command::new(opener).arg(url).spawn()?;
    // Reap the short-lived opener off the caller's thread so the UI never waits on it.
    std::thread::spawn(move || {
        let _ = child.wait();
    });
    Ok(())
}