loa_core/agent.rs
1//! Agent - Main entry point for running an observability agent
2
3use crate::{builder::AgentBuilder, Result};
4use ractor::ActorRef;
5use ractor_supervisor::SupervisorMsg;
6use std::path::PathBuf;
7use tokio::signal::ctrl_c;
8use tokio::task::JoinHandle;
9
10/// The main agent handle returned by the builder
11///
12/// Holds references to the root supervisor and can be used to run the agent until shutdown.
13pub struct Agent {
14 storage_path: PathBuf,
15 identity_path: Option<PathBuf>,
16 dashboard_port: Option<u16>,
17 supervisor: ActorRef<SupervisorMsg>,
18 supervisor_handle: JoinHandle<()>,
19}
20
21impl Agent {
22 /// Create a new agent builder
23 ///
24 /// This is the recommended way to create an agent.
25 /// Use `.build().await` to construct the agent after configuration.
26 ///
27 /// # Example
28 ///
29 /// ```no_run
30 /// use elo::Agent;
31 ///
32 /// # async fn example() -> anyhow::Result<()> {
33 /// let agent = Agent::builder()
34 /// .storage_path("/var/lib/loa")
35 /// .dashboard_port(3000)
36 /// .build()
37 /// .await?;
38 /// # Ok(())
39 /// # }
40 /// ```
41 pub fn builder() -> AgentBuilder {
42 AgentBuilder::new()
43 }
44
45 /// Internal constructor used by the builder
46 #[doc(hidden)]
47 pub(crate) fn new(
48 storage_path: PathBuf,
49 identity_path: Option<PathBuf>,
50 dashboard_port: Option<u16>,
51 supervisor: ActorRef<SupervisorMsg>,
52 supervisor_handle: JoinHandle<()>,
53 ) -> Self {
54 Self {
55 storage_path,
56 identity_path,
57 dashboard_port,
58 supervisor,
59 supervisor_handle,
60 }
61 }
62
63 /// Run the agent until shutdown signal (Ctrl+C)
64 ///
65 /// This will block until:
66 /// - Ctrl+C is pressed
67 /// - An unrecoverable error occurs
68 /// - shutdown() is called from another task
69 ///
70 /// # Example
71 ///
72 /// ```no_run
73 /// use elo::Agent;
74 ///
75 /// # async fn example() -> anyhow::Result<()> {
76 /// let agent = Agent::builder()
77 /// .storage_path("/var/lib/loa")
78 /// .build()
79 /// .await?;
80 ///
81 /// agent.run().await?;
82 /// # Ok(())
83 /// # }
84 /// ```
85 pub async fn run(self) -> Result<()> {
86 tracing::debug!("Agent running");
87 tracing::debug!(" Storage: {}", self.storage_path.display());
88 if let Some(ref identity_path) = self.identity_path {
89 tracing::debug!(" Identity: {}", identity_path.display());
90 }
91 if let Some(port) = self.dashboard_port {
92 tracing::debug!(" Dashboard: http://localhost:{}", port);
93 }
94
95 // Wait for shutdown signal
96 ctrl_c().await?;
97
98 tracing::info!("Shutdown signal received");
99
100 // Stop supervisor (will cascade to all children)
101 tracing::debug!("Stopping supervisor...");
102 self.supervisor.stop(None);
103
104 // Wait for supervisor to finish
105 let _ = self.supervisor_handle.await;
106
107 tracing::info!("Agent stopped");
108
109 Ok(())
110 }
111}