agentmux_srv\backend\reactive/
poller.rs

1// Copyright 2025-2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4
5use std::collections::HashMap;
6use std::sync::{Mutex, RwLock};
7
8use super::handler::ReactiveHandler;
9use super::types::{PollerConfig, PollerStatus};
10use super::now_unix_millis;
11
12/// Poller state for cross-host message polling from AgentMux.
13pub struct Poller {
14    config: RwLock<PollerConfig>,
15    _handler: &'static ReactiveHandler,
16    running: Mutex<bool>,
17    poll_count: Mutex<u64>,
18    injections_count: Mutex<u64>,
19    last_poll: Mutex<Option<u64>>,
20    last_error: Mutex<Option<String>>,
21}
22
23impl Poller {
24    /// Create a new poller with the given config.
25    pub fn new(config: PollerConfig, handler: &'static ReactiveHandler) -> Self {
26        Self {
27            config: RwLock::new(config),
28            _handler: handler,
29            running: Mutex::new(false),
30            poll_count: Mutex::new(0),
31            injections_count: Mutex::new(0),
32            last_poll: Mutex::new(None),
33            last_error: Mutex::new(None),
34        }
35    }
36
37    /// Check if the poller is configured (has URL and token).
38    #[allow(dead_code)]
39    pub fn is_configured(&self) -> bool {
40        let config = self.config.read().unwrap();
41        config.agentmux_url.is_some() && config.agentmux_token.is_some()
42    }
43
44    /// Check if the poller is running.
45    pub fn is_running(&self) -> bool {
46        *self.running.lock().unwrap()
47    }
48
49    /// Get poller statistics.
50    pub fn stats(&self) -> HashMap<String, serde_json::Value> {
51        let mut m = HashMap::new();
52        m.insert(
53            "poll_count".to_string(),
54            serde_json::json!(*self.poll_count.lock().unwrap()),
55        );
56        m.insert(
57            "injections_count".to_string(),
58            serde_json::json!(*self.injections_count.lock().unwrap()),
59        );
60        m.insert(
61            "last_poll".to_string(),
62            serde_json::json!(*self.last_poll.lock().unwrap()),
63        );
64        m.insert(
65            "last_error".to_string(),
66            serde_json::json!(*self.last_error.lock().unwrap()),
67        );
68        m
69    }
70
71    /// Get poller status.
72    pub fn status(&self) -> PollerStatus {
73        let config = self.config.read().unwrap();
74        PollerStatus {
75            configured: config.agentmux_url.is_some() && config.agentmux_token.is_some(),
76            running: self.is_running(),
77            url: config.agentmux_url.clone(),
78            has_token: config.agentmux_token.is_some(),
79            poll_count: *self.poll_count.lock().unwrap(),
80            injections_count: *self.injections_count.lock().unwrap(),
81            last_poll: *self.last_poll.lock().unwrap(),
82        }
83    }
84
85    /// Reconfigure the poller with new URL and token.
86    pub fn reconfigure(&self, url: Option<String>, token: Option<String>) {
87        let mut config = self.config.write().unwrap();
88        config.agentmux_url = url;
89        config.agentmux_token = token;
90    }
91
92    /// Record a successful poll.
93    #[allow(dead_code)]
94    pub fn record_poll(&self) {
95        *self.poll_count.lock().unwrap() += 1;
96        *self.last_poll.lock().unwrap() = Some(now_unix_millis());
97        *self.last_error.lock().unwrap() = None;
98    }
99
100    /// Record a poll error.
101    #[allow(dead_code)]
102    pub fn record_error(&self, err: &str) {
103        *self.last_error.lock().unwrap() = Some(err.to_string());
104    }
105
106    /// Record injections delivered.
107    #[allow(dead_code)]
108    pub fn record_injections(&self, count: u64) {
109        *self.injections_count.lock().unwrap() += count;
110    }
111
112    /// Set the running state.
113    #[allow(dead_code)]
114    pub fn set_running(&self, running: bool) {
115        *self.running.lock().unwrap() = running;
116    }
117}