falsegreen 0.1.19

FalseGreen client — independent verification for coding agents
//! Configuration: stored credentials, API endpoint, and state.
//!
//! The Rust shim talks to mcp.falsegreen.com over HTTPS.
//! No Python dependency exists anywhere in this crate.

use std::path::PathBuf;

/// Default MCP service URL (matches the public documentation).
const DEFAULT_API_URL: &str = "https://mcp.falsegreen.com/v1/mcp";

/// MCP service URL, overridable with FALSEGREEN_API_URL env var.
pub fn api_url() -> String {
    std::env::var("FALSEGREEN_API_URL").unwrap_or_else(|_| DEFAULT_API_URL.to_string())
}

/// Workspace upload URL paired with the configured MCP endpoint.
pub fn workspace_url() -> String {
    if let Ok(url) = std::env::var("FALSEGREEN_WORKSPACE_URL") {
        return url;
    }
    let mcp = api_url();
    if let Some(prefix) = mcp.strip_suffix("/v1/mcp") {
        format!("{prefix}/v1/workspace")
    } else if let Some(prefix) = mcp.strip_suffix("/mcp") {
        format!("{prefix}/workspace")
    } else {
        format!("{}/workspace", mcp.trim_end_matches('/'))
    }
}

/// Config directory: ~/.config/falsegreen/ or $XDG_CONFIG_HOME/falsegreen/.
pub fn config_dir() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("/tmp"))
        .join("falsegreen")
}

/// Credentials file path.
pub fn credentials_path() -> PathBuf {
    config_dir().join("credentials.toml")
}

/// Load the API key: check FALSEGREEN_KEY env var first, then stored credentials.
pub fn load_token() -> Option<String> {
    if let Ok(key) = std::env::var("FALSEGREEN_KEY") {
        if !key.is_empty() {
            return Some(key);
        }
    }
    let path = credentials_path();
    let content = std::fs::read_to_string(&path).ok()?;
    let parsed: toml::Value = toml::from_str(&content).ok()?;
    parsed
        .get("token")
        .and_then(|v| v.as_str())
        .map(|s| s.to_string())
}

/// Save token and activation ID to credentials file.
pub fn save_credentials(token: &str, activation_id: &str) -> std::io::Result<()> {
    let dir = config_dir();
    std::fs::create_dir_all(&dir)?;
    let content = format!(
        "token = \"{}\"\nactivation_id = \"{}\"\n",
        token, activation_id
    );
    std::fs::write(credentials_path(), content)
}

/// Load stored activation ID if present.
pub fn load_activation_id() -> Option<String> {
    let path = credentials_path();
    let content = std::fs::read_to_string(&path).ok()?;
    let parsed: toml::Value = toml::from_str(&content).ok()?;
    parsed
        .get("activation_id")
        .and_then(|v| v.as_str())
        .map(|s| s.to_string())
}

/// Remove stored credentials.
pub fn clear_token() -> std::io::Result<()> {
    let path = credentials_path();
    if path.exists() {
        std::fs::remove_file(path)?;
    }
    Ok(())
}