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;
use chrono::{Duration, Utc};
use clap::Args as ClapArgs;
use systemprompt_oauth::repository::{CreateSetupTokenParams, OAuthRepository, SetupTokenPurpose};
use systemprompt_oauth::services::webauthn::generate_setup_token;
use systemprompt_runtime::AppContext;

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

#[derive(Debug, ClapArgs)]
pub struct Args {
    #[arg(long, help = "Email of the user to generate token for")]
    pub email: String,

    #[arg(long, default_value = "15", help = "Token validity in minutes")]
    pub expires_minutes: u32,
}

pub async fn execute(
    args: Args,
    _config: &CliConfig,
) -> Result<CommandResult<WebauthnSetupTokenOutput>> {
    let ctx = AppContext::new().await?;
    let oauth_repo = OAuthRepository::new(ctx.db_pool())?;

    let user = oauth_repo
        .find_user_by_email(&args.email)
        .await?
        .ok_or_else(|| anyhow::anyhow!("User not found: {}", args.email))?;

    let (raw_token, token_hash) = generate_setup_token();
    let expires_at = Utc::now() + Duration::minutes(i64::from(args.expires_minutes));

    let params = CreateSetupTokenParams {
        user_id: user.id,
        token_hash,
        purpose: SetupTokenPurpose::CredentialLink,
        expires_at,
    };

    oauth_repo.store_setup_token(params).await?;

    let external_url = ctx.config().api_external_url.clone();

    let link_url = format!("{}/auth/link-passkey?token={}", external_url, raw_token);

    let output = WebauthnSetupTokenOutput {
        user_email: args.email.clone(),
        token: raw_token,
        registration_url: link_url,
        expires_minutes: args.expires_minutes,
    };

    Ok(CommandResult::copy_paste(output).with_title("WebAuthn Setup Token"))
}