deepseek-rust-cli 1.20.7

A lightweight, high-speed autonomous CLI system agent port of DeepSeek CLI.
Documentation
use std::{collections::HashMap, path::Path};

use anyhow::Result;
use async_trait::async_trait;
use serde_json::Value;

use crate::{agent::types::UndoAction, tools, tools::base::Tool};

pub struct RunPythonTool;
#[async_trait]
impl Tool for RunPythonTool {
    fn name(&self) -> &str {
        "run_python_code"
    }
    async fn execute(
        &self,
        args: &HashMap<String, Value>,
        _undo: &mut Vec<UndoAction>,
        _cwd: Option<&Path>,
    ) -> Result<String> {
        let code = args
            .get("code")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'code'"))?;
        tools::code_ops::run_python_code(code).await
    }
}

pub struct FetchUrlTool;
#[async_trait]
impl Tool for FetchUrlTool {
    fn name(&self) -> &str {
        "fetch_url"
    }
    async fn execute(
        &self,
        args: &HashMap<String, Value>,
        _undo: &mut Vec<UndoAction>,
        _cwd: Option<&Path>,
    ) -> Result<String> {
        let url = args
            .get("url")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'url'"))?;
        tools::web_ops::fetch_url(url).await
    }
}

pub struct GetEnvVarTool;
#[async_trait]
impl Tool for GetEnvVarTool {
    fn name(&self) -> &str {
        "get_env_var"
    }
    async fn execute(
        &self,
        args: &HashMap<String, Value>,
        _undo: &mut Vec<UndoAction>,
        _cwd: Option<&Path>,
    ) -> Result<String> {
        let name = args
            .get("name")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'name'"))?;
        Ok(tools::web_ops::get_env_var(name))
    }
}

pub struct ScreenshotWebappTool;
#[async_trait]
impl Tool for ScreenshotWebappTool {
    fn name(&self) -> &str {
        "screenshot_webapp"
    }
    async fn execute(
        &self,
        args: &HashMap<String, Value>,
        _undo: &mut Vec<UndoAction>,
        _cwd: Option<&Path>,
    ) -> Result<String> {
        let url = args
            .get("url")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'url'"))?;
        let output_path = args
            .get("output_path")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'output_path'"))?;

        let p = crate::tools::base::validate_path(output_path)?;
        if let Some(parent) = p.parent() {
            tokio::fs::create_dir_all(parent).await?;
        }
        let p_str = p.to_string_lossy().to_string();

        let mut cmd = tokio::process::Command::new("msedge");
        cmd.arg("--headless")
            .arg("--disable-gpu")
            .arg(format!("--screenshot={}", p_str))
            .arg(url);

        let res = cmd.output().await;
        match res {
            Ok(output) if output.status.success() => Ok(format!("Screenshot saved to {}", p_str)),
            _ => {
                let mut cmd2 = tokio::process::Command::new("chrome");
                cmd2.arg("--headless")
                    .arg("--disable-gpu")
                    .arg(format!("--screenshot={}", p_str))
                    .arg(url);
                match cmd2.output().await {
                    Ok(output) if output.status.success() => {
                        Ok(format!("Screenshot saved to {}", p_str))
                    }
                    Ok(output) => Err(anyhow::anyhow!(
                        "Browser exited with error: {}",
                        String::from_utf8_lossy(&output.stderr)
                    )),
                    Err(e) => Err(anyhow::anyhow!(
                        "Failed to start msedge or chrome. Make sure at least one is installed \
                         and in your PATH. Error: {}",
                        e
                    )),
                }
            }
        }
    }
}

pub struct WebSearchDuckDuckGoTool;
#[async_trait]
impl Tool for WebSearchDuckDuckGoTool {
    fn name(&self) -> &str {
        "web_search_duckduckgo"
    }
    async fn execute(
        &self,
        args: &HashMap<String, Value>,
        _undo: &mut Vec<UndoAction>,
        _cwd: Option<&Path>,
    ) -> Result<String> {
        let query = args
            .get("query")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing 'query'"))?;
        tools::web_ops::web_search_duckduckgo(query).await
    }
}