winx-code-agent 0.2.352

Remote-first Rust MCP runtime with Streamable HTTP, durable PTYs, and guarded agent tools
Documentation
//! Read-only runtime diagnostics shared by the CLI and future health endpoints.

use serde_json::{json, Value};

/// Build a redacted snapshot of configuration and live runtime topology.
pub async fn doctor_report() -> Value {
    let runtime = crate::runtime::configured_runtime_mode().map_or_else(
        |error| format!("invalid: {error}"),
        |mode| format!("{mode:?}").to_ascii_lowercase(),
    );
    let build = crate::build_info::BuildIdentity::current();
    let mut report = json!({
        "version": crate::build_info::package_version(),
        "build": build,
        "platform": {
            "os": std::env::consts::OS,
            "arch": std::env::consts::ARCH
        },
        "cwd": std::env::current_dir().ok().map(|path| path.display().to_string()),
        "runtime": runtime,
        "environment": {
            "sandbox_requested": crate::config::env_flag("WINX_SANDBOX"),
            "redaction_disabled": crate::config::env_flag("WINX_NO_REDACT"),
            "compression_disabled": crate::config::env_flag("WINX_NO_COMPRESS"),
            "http_token_configured": crate::config::env_text("WINX_HTTP_TOKEN").is_some(),
            "usage_log_configured": crate::config::env_text("WINX_USAGE_LOG").is_some()
        }
    });

    let allowed_roots = crate::utils::path::configured_allowed_roots();
    let unconfined =
        cfg!(unix) && allowed_roots.iter().any(|root| root == std::path::Path::new("/"));
    let containment_mode = if unconfined {
        "unconfined"
    } else if allowed_roots.is_empty() {
        "workspace"
    } else {
        "extended"
    };
    report["file_tool_containment"] = json!({
        "mode": containment_mode,
        "extra_roots": allowed_roots
            .iter()
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>()
    });

    report["shell"] = match crate::utils::executable::bash_executable() {
        Some(path) => json!({"available": true, "bash": path.display().to_string()}),
        None => json!({
            "available": false,
            "hint": if cfg!(windows) {
                "no bash.exe found: install Git for Windows (the System32 WSL launcher is not used), or set WINX_SHELL to an absolute bash.exe"
            } else {
                "bash not found on PATH"
            }
        }),
    };

    add_runtime_report(&mut report).await;
    report
}

#[allow(clippy::too_many_lines)] // one bounded pass builds the complete per-socket doctor report
async fn add_runtime_report(report: &mut Value) {
    use std::time::Duration;

    use crate::daemon::{socket_candidates, DaemonClient};

    // `doctor` is observational and must never wait indefinitely on a stale
    // socket that accepts a connection but no longer serves frames.
    const PROBE_TIMEOUT: Duration = Duration::from_secs(2);

    let expected = crate::build_info::BuildIdentity::current();
    let candidates = socket_candidates();
    let selected = candidates
        .iter()
        .find(|candidate| candidate.selected)
        .map(|candidate| candidate.path.display().to_string());
    let mut entries = Vec::with_capacity(candidates.len());
    let mut reachable = 0_u64;
    let mut compatible = 0_u64;
    let mut mixed_guardians = 0_u64;

    for candidate in candidates {
        let client = DaemonClient::new(&candidate.path);
        let entry = match tokio::time::timeout(PROBE_TIMEOUT, client.hello()).await {
            Ok(Ok(hello)) => {
                reachable += 1;
                let build_compatible = hello.build_matches(&expected);
                compatible += u64::from(build_compatible);
                let sessions =
                    match tokio::time::timeout(PROBE_TIMEOUT, client.list_sessions()).await {
                        Ok(Ok(sessions)) => sessions,
                        Ok(Err(error)) => {
                            entries.push(json!({
                                "path": candidate.path.display().to_string(),
                                "sources": candidate.sources,
                                "selected": candidate.selected,
                                "reachable": true,
                                "build_compatible": build_compatible,
                                "hello": hello,
                                "sessions_error": error.to_string()
                            }));
                            continue;
                        }
                        Err(_) => {
                            entries.push(json!({
                                "path": candidate.path.display().to_string(),
                                "sources": candidate.sources,
                                "selected": candidate.selected,
                                "reachable": true,
                                "build_compatible": build_compatible,
                                "hello": hello,
                                "sessions_error": "timed out waiting for winxd"
                            }));
                            continue;
                        }
                    };
                let session_entries = sessions
                    .into_iter()
                    .map(|session| {
                        let guardian_build_compatible = session
                            .runtime
                            .as_ref()
                            .is_some_and(|runtime| runtime.build_matches(&expected));
                        if session.runtime.is_some() && !guardian_build_compatible {
                            mixed_guardians += 1;
                        }
                        json!({
                            "thread_id": session.thread_id,
                            "cwd": session.cwd,
                            "running": session.running,
                            "guardian_build_compatible": guardian_build_compatible,
                            "runtime": session.runtime
                        })
                    })
                    .collect::<Vec<_>>();
                json!({
                    "path": candidate.path.display().to_string(),
                    "sources": candidate.sources,
                    "selected": candidate.selected,
                    "reachable": true,
                    "build_compatible": build_compatible,
                    "hello": hello,
                    "session_count": session_entries.len(),
                    "sessions": session_entries
                })
            }
            Ok(Err(error)) => json!({
                "path": candidate.path.display().to_string(),
                "sources": candidate.sources,
                "selected": candidate.selected,
                "reachable": false,
                "error": error.to_string()
            }),
            Err(_) => json!({
                "path": candidate.path.display().to_string(),
                "sources": candidate.sources,
                "selected": candidate.selected,
                "reachable": false,
                "error": "timed out waiting for winxd"
            }),
        };
        entries.push(entry);
    }

    let last_exit = selected
        .as_deref()
        .map(std::path::Path::new)
        .and_then(crate::daemon::exit::read_record)
        .map_or(Value::Null, |record| serde_json::to_value(record).unwrap_or(Value::Null));
    report["daemon_topology"] = json!({
        "selected_socket": selected,
        "last_exit": last_exit,
        "reachable_count": reachable,
        "compatible_count": compatible,
        "split_brain": reachable > 1,
        "mixed_guardian_builds": mixed_guardians,
        "healthy": reachable == 1 && mixed_guardians == 0 && compatible == reachable,
        "candidates": entries
    });
    report["daemon_binary"] = match crate::runtime::configured_daemon_binary() {
        Ok(path) => json!({"available": true, "path": path.display().to_string()}),
        Err(error) => json!({"available": false, "error": error.to_string()}),
    };
}

#[cfg(test)]
mod tests {
    #[tokio::test]
    async fn doctor_report_never_exposes_secret_values() {
        let report = super::doctor_report().await.to_string();
        assert!(!report.contains("WINX_HTTP_TOKEN="));
        assert!(report.contains("http_token_configured"));
        assert!(report.contains("daemon_topology"));
    }
}