agentmux_srv/backend/work_facts/
cross_channel.rs1use std::collections::HashMap;
12use std::time::Duration;
13
14use super::WorkFacts;
15
16pub const TIMEOUT: Duration = Duration::from_millis(1500);
18
19pub fn own_channel() -> String {
21 std::env::var("AGENTMUX_CHANNEL").unwrap_or_else(|_| "stable".to_string())
22}
23
24struct Peer {
26 local_url: String,
27 auth_key: String,
28 channel: String,
29}
30
31fn other_channels(own_url: &str) -> Vec<Peer> {
34 let own = own_channel();
35 let Some(shared_dir) = crate::registry::resolve_shared_reactive_dir() else {
36 return Vec::new();
37 };
38 let mut seen: HashMap<String, Peer> = HashMap::new();
39 for e in crate::backend::reactive::registry::list_all_shared(&shared_dir) {
40 if e.channel == own || e.local_url == own_url || e.auth_key.is_empty() {
41 continue;
42 }
43 seen.entry(e.local_url.clone()).or_insert(Peer {
44 local_url: e.local_url,
45 auth_key: e.auth_key,
46 channel: e.channel,
47 });
48 }
49 seen.into_values().collect()
50}
51
52pub async fn fetch(client: &reqwest::Client, own_url: &str) -> (Vec<WorkFacts>, Vec<String>) {
55 let mut set = tokio::task::JoinSet::new();
56 for peer in other_channels(own_url) {
57 let client = client.clone();
58 set.spawn(async move {
59 let fetch = client
60 .get(format!("{}/api/v1/work-facts", peer.local_url.trim_end_matches('/')))
61 .header(agentmux_common::AUTH_KEY_HEADER, &peer.auth_key)
62 .send();
63 let body = match tokio::time::timeout(TIMEOUT, fetch).await {
64 Ok(Ok(resp)) if resp.status().is_success() => {
65 tokio::time::timeout(TIMEOUT, resp.json::<serde_json::Value>()).await.ok().and_then(Result::ok)
66 }
67 _ => None,
68 };
69 let agents: Option<Vec<WorkFacts>> =
70 body.and_then(|b| serde_json::from_value(b.get("agents")?.clone()).ok());
71 (peer.channel, agents)
72 });
73 }
74 let (mut facts, mut unreachable) = (Vec::new(), Vec::new());
75 while let Some(res) = set.join_next().await {
76 match res {
77 Ok((_, Some(agents))) => facts.extend(agents),
78 Ok((channel, None)) => unreachable.push(channel),
79 Err(_) => {}
80 }
81 }
82 unreachable.sort();
83 (facts, unreachable)
84}