systemprompt-cli 0.2.1

Unified CLI for systemprompt.io AI governance: agent orchestration, MCP governance, analytics, profiles, cloud deploy, and self-hosted operations.
Documentation
use anyhow::{Result, anyhow};
use clap::Args;
use systemprompt_files::{FileService, TypeSpecificMetadata};
use systemprompt_identifiers::FileId;
use systemprompt_runtime::AppContext;

use crate::CliConfig;
use crate::commands::core::files::types::{
    ChecksumsOutput, FileDetailOutput, FileMetadataOutput, ImageMetadataOutput,
};
use crate::shared::CommandResult;

#[derive(Debug, Clone, Args)]
pub struct ShowArgs {
    #[arg(value_name = "FILE_ID", help = "AI image file ID (UUID format)")]
    pub file: String,
}

pub async fn execute(
    args: ShowArgs,
    _config: &CliConfig,
) -> Result<CommandResult<FileDetailOutput>> {
    let file_id = parse_file_id(&args.file)?;

    let ctx = AppContext::new().await?;
    let service = FileService::new(ctx.db_pool())?;

    let file = service
        .find_by_id(&file_id)
        .await?
        .ok_or_else(|| anyhow!("File not found: {}", args.file))?;

    if !file.ai_content {
        return Err(anyhow!(
            "File '{}' is not an AI-generated image. Use 'files show' for regular files.",
            args.file
        ));
    }

    let metadata_output = convert_metadata(&file);

    let output = FileDetailOutput {
        id: file.id(),
        path: file.path,
        public_url: file.public_url,
        mime_type: file.mime_type,
        size_bytes: file.size_bytes,
        ai_content: file.ai_content,
        user_id: file.user_id,
        session_id: file.session_id,
        trace_id: file.trace_id,
        context_id: file.context_id,
        metadata: metadata_output,
        created_at: file.created_at,
        updated_at: file.updated_at,
    };

    Ok(CommandResult::card(output).with_title(format!("AI Image: {}", args.file)))
}

fn parse_file_id(id: &str) -> Result<FileId> {
    uuid::Uuid::parse_str(id).map_err(|_| {
        anyhow!(
            "Invalid file ID format. Expected UUID like 'b75940ac-c50f-4d46-9fdd-ebb4970b2a7d', \
             got '{}'",
            id
        )
    })?;
    Ok(FileId::new(id.to_string()))
}

fn convert_metadata(file: &systemprompt_files::File) -> FileMetadataOutput {
    let Ok(metadata) = file.metadata() else {
        return FileMetadataOutput::default();
    };

    let checksums = metadata.checksums.map(|c| ChecksumsOutput {
        md5: c.md5,
        sha256: c.sha256,
    });

    let image = match metadata.type_specific {
        Some(TypeSpecificMetadata::Image(img)) => Some(ImageMetadataOutput {
            width: img.width,
            height: img.height,
            alt_text: img.alt_text,
            description: img.description,
        }),
        _ => None,
    };

    FileMetadataOutput {
        checksums,
        image,
        document: None,
        audio: None,
        video: None,
    }
}