Skip to main content

hematite/agent/
searx_lifecycle.rs

1use crate::agent::config::HematiteConfig;
2use std::process::Command;
3use tokio::time::{timeout, Duration};
4
5/// Checks if SearXNG is responding at the configured URL.
6pub async fn is_searx_responding(url: &str) -> bool {
7    let client = reqwest::Client::builder()
8        .timeout(Duration::from_millis(500))
9        .build()
10        .unwrap_or_default();
11
12    match timeout(Duration::from_millis(600), client.get(url).send()).await {
13        Ok(Ok(resp)) => resp.status().is_success() || resp.status().as_u16() == 403, // 403 is fine, SearXNG might block generic UA but it's alive
14        _ => false,
15    }
16}
17
18/// Automatically boots SearXNG if it's offline and the user has auto-start enabled.
19pub async fn boot_searx_if_needed(config: &HematiteConfig) {
20    if !config.auto_start_searx {
21        return;
22    }
23
24    let url = config
25        .searx_url
26        .as_deref()
27        .unwrap_or("http://localhost:8080");
28
29    // Check if it's already alive.
30    if is_searx_responding(url).await {
31        return;
32    }
33
34    // It's offline. Try to boot it via the setup script.
35    // We run it with powershell -ExecutionPolicy Bypass -File scripts/setup-searxng.ps1
36    let script_path = "scripts/setup-searxng.ps1";
37    if std::path::Path::new(script_path).exists() {
38        let _ = Command::new("powershell")
39            .arg("-ExecutionPolicy")
40            .arg("Bypass")
41            .arg("-File")
42            .arg(script_path)
43            .spawn();
44    }
45}