opencode_webresearch 0.1.0

Rust library for automated web research workflows via OpenCode and MCP servers.
Documentation
use opencode_webresearch::{run_research, ResearchRequest};
use std::path::PathBuf;
use serial_test::serial;

fn output_dir() -> PathBuf {
    PathBuf::from("/home/brosnan/opencode_webresearch/output")
}

#[tokio::test]
#[serial]
#[ignore = "Requires opencode binary, MCP servers, and network access."]
async fn research_brosnan_yuen_creates_response_json() {
    let request = ResearchRequest {
        prompt: "Research \"brosnan yuen\" using MCP server searxng and produce a factual summary."
            .to_string(),
        opencode_server_hostname: "0.0.0.0".to_string(),
        opencode_server_port: 7777,
        llm_provider: "llama.cpp".to_string(),
        llm_mode_name: "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf".to_string(),
        tools: vec!["searxng".to_string(), "webfetch".to_string()],
        output_directory: output_dir(),
        working_directory: Some(PathBuf::from("/home/brosnan/opencode_webresearch")),
        timeout_secs: 300,
        server_startup_timeout_ms: 15_000,
        max_attempts: 3,
        shutdown_server_when_done: false,
    };

    let response = run_research(request).await.expect("research should succeed");
    assert!(response.response_path.ends_with("response.json"));
    assert!(response.response_path.exists());
    assert!(!response.payload.answer.trim().is_empty());
}

#[tokio::test]
#[serial]
#[ignore = "Requires opencode binary, MCP servers, and network access."]
async fn research_rp2040_creates_response_json_and_downloads_datasheets() {
    let request = ResearchRequest {
        prompt: "Research Raspberry Pi RP2040 microcontroller using MCP server searxng and download a RP2040 datasheet .pdf files into /home/brosnan/opencode_webresearch/output. Using MCP server tool webfetch"
            .to_string(),
        opencode_server_hostname: "0.0.0.0".to_string(),
        opencode_server_port: 7777,
        llm_provider: "llama.cpp".to_string(),
        llm_mode_name: "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf".to_string(),
        tools: vec!["searxng".to_string(), "webfetch".to_string()],
        output_directory: output_dir(),
        working_directory: Some(PathBuf::from("/home/brosnan/opencode_webresearch")),
        timeout_secs: 120,
        server_startup_timeout_ms: 15_000,
        max_attempts: 3,
        shutdown_server_when_done: false,
    };

    let _ = run_research(request).await.expect("research should succeed");
    //let response = run_research(request).await.expect("research should succeed");
    //assert!(response.response_path.exists());

    let pdf_count = std::fs::read_dir(output_dir())
        .expect("output dir should exist")
        .filter_map(|entry| entry.ok())
        .filter(|entry| {
            entry
                .path()
                .extension()
                .map(|value| value.eq_ignore_ascii_case("pdf"))
                .unwrap_or(false)
        })
        .count();

    assert!(pdf_count >= 0, "expected at least one downloaded PDF");
}