mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
//! Auth command handlers
//!
//! Orchestrates authentication services for login, logout, and whoami commands.

use crate::commands::auth::{LoginArgs, WhoamiArgs};
use crate::services::credentials::get_auth_url;
use crate::services::{AuthService, CredentialsService};
use anyhow::Result;
use mecha10_auth::{display_device_code_instructions, open_browser};

/// Handle the login command
///
/// Runs the device code authentication flow:
/// 1. Request device code from auth server
/// 2. Display user code and verification URL
/// 3. Open browser to verification URL
/// 4. Poll until user completes auth or code expires
/// 5. Save credentials on success
pub async fn handle_login(args: &LoginArgs) -> Result<()> {
    let credentials_service = CredentialsService::new();

    // Check if already logged in
    if credentials_service.is_logged_in() {
        if let Ok(Some((_, email, name))) = credentials_service.get_user_info() {
            let display_name = name.unwrap_or_else(|| email.clone());
            println!();
            println!("Already logged in as {}", display_name);
            println!();
            println!("Run `mecha10 auth logout` to log out first.");
            println!();
            return Ok(());
        }
    }

    // Create auth service (with custom URL if provided, or from env var)
    let auth_service = match &args.auth_url {
        Some(url) => AuthService::with_auth_url(url.clone()),
        None => AuthService::with_auth_url(get_auth_url()),
    };

    println!();
    println!("Logging in to mecha10...");
    println!();

    // Run device code flow
    let credentials = auth_service
        .run_device_code_flow(|device_code| {
            display_device_code_instructions(device_code);
            open_browser(&device_code.verification_uri);
        })
        .await?;

    // Save credentials
    credentials_service.save(&credentials)?;

    let display_name = credentials.name.as_ref().unwrap_or(&credentials.email);

    println!();
    println!("Successfully logged in as {}", display_name);
    println!();
    println!(
        "Credentials saved to {}",
        credentials_service.credentials_path().display()
    );
    println!();

    Ok(())
}

/// Handle the logout command
///
/// Removes stored credentials from ~/.mecha10/credentials.json
pub async fn handle_logout() -> Result<()> {
    let credentials_service = CredentialsService::new();

    if !credentials_service.is_logged_in() {
        println!();
        println!("Not currently logged in.");
        println!();
        return Ok(());
    }

    // Get user info for display before deleting
    let user_info = credentials_service.get_user_info()?;

    // Delete credentials
    credentials_service.delete()?;

    println!();
    if let Some((_, email, name)) = user_info {
        let display_name = name.unwrap_or(email);
        println!("Logged out from {}", display_name);
    } else {
        println!("Logged out successfully");
    }
    println!();

    Ok(())
}

/// Handle the whoami command
///
/// Displays current authentication status and user info
pub async fn handle_whoami(args: &WhoamiArgs) -> Result<()> {
    let credentials_service = CredentialsService::new();

    println!();

    match credentials_service.load()? {
        Some(creds) if creds.is_valid() => {
            let display_name = creds.name.as_ref().unwrap_or(&creds.email);

            println!("Logged in as: {}", display_name);
            println!("Email: {}", creds.email);
            println!("User ID: {}", creds.user_id);

            if args.verbose {
                // Show masked API key prefix
                let key_preview = if creds.api_key.len() > 12 {
                    format!(
                        "{}...{}",
                        &creds.api_key[..8],
                        &creds.api_key[creds.api_key.len() - 4..]
                    )
                } else {
                    "***".to_string()
                };
                println!("API Key: {}", key_preview);
            }

            println!(
                "Authenticated: {}",
                creds.authenticated_at.format("%Y-%m-%d %H:%M:%S UTC")
            );
        }
        Some(_) => {
            println!("Credentials found but invalid");
            println!();
            println!("Run `mecha10 auth login` to authenticate");
        }
        None => {
            println!("Not logged in");
            println!();
            println!("Run `mecha10 auth login` to authenticate");
        }
    }

    println!();

    Ok(())
}