steer_cli/commands/session/
list.rs1use 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 let Some(_remote_addr) = &self.remote {
21 return self.handle_remote().await;
22 }
23
24 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 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 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}