Skip to main content

steer_cli/commands/session/
list.rs

1use async_trait::async_trait;
2use chrono::{TimeZone, Utc};
3use eyre::{Result, eyre};
4
5use super::super::Command;
6use steer_core::api::Model;
7use steer_core::session::{SessionFilter, SessionManager, SessionManagerConfig, SessionStatus};
8
9pub struct ListSessionCommand {
10    pub active: bool,
11    pub limit: Option<u32>,
12    pub remote: Option<String>,
13    pub session_db: Option<std::path::PathBuf>,
14}
15
16#[async_trait]
17impl Command for ListSessionCommand {
18    async fn execute(&self) -> Result<()> {
19        // If remote is specified, handle via gRPC
20        if let Some(_remote_addr) = &self.remote {
21            return self.handle_remote().await;
22        }
23
24        // Local session handling
25        let store_config =
26            steer_core::utils::session::resolve_session_store_config(self.session_db.clone())?;
27        let session_store =
28            steer_core::utils::session::create_session_store_with_config(store_config).await?;
29        let session_manager_config = SessionManagerConfig {
30            max_concurrent_sessions: 10,
31            default_model: Model::default(),
32            auto_persist: true,
33        };
34
35        let session_manager = SessionManager::new(session_store, session_manager_config);
36
37        let filter = SessionFilter {
38            status_filter: if self.active {
39                Some(SessionStatus::Active)
40            } else {
41                None
42            },
43            limit: self.limit,
44            ..Default::default()
45        };
46
47        let sessions = session_manager
48            .list_sessions(filter)
49            .await
50            .map_err(|e| eyre!("Failed to list sessions: {}", e))?;
51
52        if sessions.is_empty() {
53            println!("No sessions found.");
54            return Ok(());
55        }
56
57        println!("Sessions:");
58        println!(
59            "{:<36} {:<20} {:<20} {:<10} {:<30}",
60            "ID", "Created", "Updated", "Messages", "Last Model"
61        );
62        println!("{}", "-".repeat(106));
63
64        for session in sessions {
65            let model_str = session
66                .last_model
67                .map(|m| m.as_ref().to_string())
68                .unwrap_or_else(|| "N/A".to_string());
69
70            println!(
71                "{:<36} {:<20} {:<20} {:<10} {:<30}",
72                session.id,
73                session.created_at.format("%Y-%m-%d %H:%M:%S"),
74                session.updated_at.format("%Y-%m-%d %H:%M:%S"),
75                session.message_count,
76                model_str,
77            );
78        }
79
80        Ok(())
81    }
82}
83
84impl ListSessionCommand {
85    async fn handle_remote(&self) -> Result<()> {
86        use steer_grpc::GrpcClientAdapter;
87
88        let remote_addr = self.remote.as_ref().unwrap();
89
90        // Connect to the gRPC server
91        let client = GrpcClientAdapter::connect(remote_addr).await.map_err(|e| {
92            eyre!(
93                "Failed to connect to remote server at {}: {}",
94                remote_addr,
95                e
96            )
97        })?;
98
99        // List remote sessions via gRPC
100        let sessions = client
101            .list_sessions()
102            .await
103            .map_err(|e| eyre!("Failed to list remote sessions: {}", e))?;
104
105        if sessions.is_empty() {
106            println!("No remote sessions found.");
107            return Ok(());
108        }
109
110        println!("Remote Sessions:");
111        println!(
112            "{:<36} {:<20} {:<20} {:<10}",
113            "ID", "Created", "Updated", "Status"
114        );
115        println!("{}", "-".repeat(86));
116
117        for session in sessions {
118            let created_str = session
119                .created_at
120                .as_ref()
121                .map(|ts: &prost_types::Timestamp| {
122                    let secs = ts.seconds;
123                    let nsecs = ts.nanos as u32;
124                    let datetime = Utc.timestamp_opt(secs, nsecs).single();
125                    match datetime {
126                        Some(dt) => dt.format("%Y-%m-%d %H:%M:%S").to_string(),
127                        None => "N/A".to_string(),
128                    }
129                })
130                .unwrap_or_else(|| "N/A".to_string());
131
132            let updated_str = session
133                .updated_at
134                .as_ref()
135                .map(|ts: &prost_types::Timestamp| {
136                    let secs = ts.seconds;
137                    let nsecs = ts.nanos as u32;
138                    let datetime = Utc.timestamp_opt(secs, nsecs).single();
139                    match datetime {
140                        Some(dt) => dt.format("%Y-%m-%d %H:%M:%S").to_string(),
141                        None => "N/A".to_string(),
142                    }
143                })
144                .unwrap_or_else(|| "N/A".to_string());
145
146            let status_str = match session.status {
147                0 => "Unspecified",
148                1 => "Active",
149                2 => "Inactive",
150                _ => "Unknown",
151            };
152
153            println!(
154                "{:<36} {:<20} {:<20} {:<10}",
155                session.id, created_str, updated_str, status_str,
156            );
157        }
158
159        Ok(())
160    }
161}