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_identifiers::SessionId;
use systemprompt_runtime::AppContext;
use systemprompt_users::{UserAdminService, UserService};

use crate::CliConfig;
use crate::commands::admin::users::types::SessionEndOutput;
use crate::shared::CommandResult;

#[derive(Debug, Args)]
pub struct EndArgs {
    #[arg(
        value_name = "SESSION_ID",
        help = "Session ID to end (optional if using --user --all)"
    )]
    pub session: Option<String>,

    #[arg(
        long,
        help = "User identifier (ID, email, or username) to end sessions for"
    )]
    pub user: Option<String>,

    #[arg(
        long,
        help = "End all sessions for the specified user (requires --user)"
    )]
    pub all: bool,

    #[arg(short = 'y', long)]
    pub yes: bool,
}

pub async fn execute(
    args: EndArgs,
    _config: &CliConfig,
) -> Result<CommandResult<SessionEndOutput>> {
    if !args.yes {
        return Err(anyhow!(
            "This will end user session(s). Use --yes to confirm."
        ));
    }

    let ctx = AppContext::new().await?;
    let user_service = UserService::new(ctx.db_pool())?;
    let admin_service = UserAdminService::new(user_service.clone());

    if args.all {
        let user_identifier = args
            .user
            .ok_or_else(|| anyhow!("--user is required when using --all"))?;

        let user = admin_service
            .find_user(&user_identifier)
            .await?
            .ok_or_else(|| anyhow!("User not found: {}", user_identifier))?;

        let count = user_service.end_all_sessions(&user.id).await?;

        let output = SessionEndOutput {
            ended: vec![format!("all sessions for user '{}'", user.name)],
            count,
            message: format!("{} session(s) ended for user '{}'", count, user.name),
        };

        return Ok(CommandResult::text(output).with_title("Sessions Ended"));
    }

    let session_id_str = args.session.ok_or_else(|| {
        anyhow!("Session ID is required (or use --user --all to end all sessions for a user)")
    })?;

    let session_id = SessionId::new(&session_id_str);
    let ended = user_service.end_session(&session_id).await?;

    let output = SessionEndOutput {
        ended: if ended {
            vec![session_id_str.clone()]
        } else {
            vec![]
        },
        count: u64::from(ended),
        message: if ended {
            format!("Session '{}' ended successfully", session_id_str)
        } else {
            format!(
                "Session '{}' was not found or already ended",
                session_id_str
            )
        },
    };

    Ok(CommandResult::text(output).with_title("Sessions Ended"))
}