Skip to main content

ironflow_cli/commands/
stats.rs

1//! Stats commands: aggregate, historical and per agent role.
2
3use anyhow::Result;
4use clap::{Args, Subcommand};
5use ironflow_sdk::IronflowClient;
6use ironflow_sdk::client::StatsHistoryParams;
7use uuid::Uuid;
8
9use crate::output;
10
11/// Stats subcommand arguments.
12///
13/// # Examples
14///
15/// ```
16/// use ironflow_cli::commands::stats::StatsArgs;
17/// ```
18#[derive(Debug, Args)]
19pub struct StatsArgs {
20    /// Stats subcommand. Omit for aggregate stats.
21    #[command(subcommand)]
22    pub command: Option<StatsCommands>,
23}
24
25/// Available stats subcommands.
26///
27/// # Examples
28///
29/// ```
30/// use ironflow_cli::commands::stats::StatsCommands;
31/// ```
32#[derive(Debug, Subcommand)]
33pub enum StatsCommands {
34    /// Show time-bucketed historical statistics.
35    History {
36        /// Filter by workflow name (case-insensitive substring match).
37        #[arg(long)]
38        workflow: Option<String>,
39        /// Time period: 24h, 7d, 30d, 90d. Defaults to 7d.
40        #[arg(long, default_value = "7d")]
41        period: String,
42        /// Bucket granularity: 1h, 1d, 1w. Auto-derived from period when omitted.
43        #[arg(long)]
44        granularity: Option<String>,
45        /// Filter by run status (pending, running, completed, failed, etc.).
46        #[arg(long)]
47        status: Option<String>,
48        /// Filter by labels. Comma-separated `key:value` pairs.
49        #[arg(long)]
50        label: Option<String>,
51        /// Filter by step presence (only applies to completed/cancelled runs).
52        #[arg(long)]
53        has_steps: Option<bool>,
54        /// Filter by author: the user ID that triggered the run.
55        ///
56        /// Also matches runs triggered by one of that user's API keys.
57        #[arg(long)]
58        created_by: Option<Uuid>,
59        /// Filter by concurrency group: only runs that belong to this group.
60        #[arg(long)]
61        concurrency_group: Option<String>,
62    },
63    /// Show cost, executions and failures per agent role, all workflows combined.
64    Roles {
65        /// Time period: 24h, 7d, 30d, 90d. Defaults to 7d.
66        #[arg(long, default_value = "7d")]
67        period: String,
68    },
69}
70
71/// Execute the `stats` command tree.
72///
73/// # Errors
74///
75/// Returns an error on API failure.
76pub async fn execute(client: &IronflowClient, args: &StatsArgs, json_mode: bool) -> Result<()> {
77    match &args.command {
78        None => {
79            let response = client.get_stats().await?;
80            output::print_output(json_mode, &response, || output::stats_table(&response.data))?;
81        }
82        Some(StatsCommands::History {
83            workflow,
84            period,
85            granularity,
86            status,
87            label,
88            has_steps,
89            created_by,
90            concurrency_group,
91        }) => {
92            let params = StatsHistoryParams {
93                workflow: workflow.as_deref(),
94                period: Some(period.as_str()),
95                granularity: granularity.as_deref(),
96                status: status.as_deref(),
97                label: label.as_deref(),
98                has_steps: *has_steps,
99                created_by: *created_by,
100                concurrency_group: concurrency_group.as_deref(),
101            };
102            let response = client.stats_history_with(&params).await?;
103            output::print_output(json_mode, &response, || {
104                output::stats_history_table(&response.data)
105            })?;
106        }
107        Some(StatsCommands::Roles { period }) => {
108            let response = client.agent_role_stats(Some(period.as_str())).await?;
109            output::print_output(json_mode, &response, || {
110                output::agent_role_stats_table(&response.data)
111            })?;
112        }
113    }
114    Ok(())
115}