use std::path::Path;
use std::process::ExitCode;
use crate::cli::workspace::WorkspaceConfig;
use super::common::{build_remote, resolve_orchestrators};
pub async fn run(config_path: &Path, orchestrator: Option<&str>) -> ExitCode {
let config = match WorkspaceConfig::load_or_remote_default(config_path) {
Ok(c) => c,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
};
let targets = match resolve_orchestrators(&config, orchestrator) {
Ok(t) => t,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
};
let mut any_failed = false;
for (name, orch) in &targets {
let client = match build_remote(name, orch) {
Ok(c) => c,
Err(e) => {
eprintln!("error: {e}");
any_failed = true;
continue;
}
};
let address = orch.address.as_deref().unwrap_or("?");
println!("Orchestrator: {name} ({address})");
match client.discover_rooms().await {
Ok(rooms) => {
if rooms.is_empty() {
println!(" Rooms: (none available to you)");
} else {
println!(" Rooms ({}):", rooms.len());
for r in &rooms {
let tags = if r.tags.is_empty() {
"-".to_string()
} else {
r.tags.join(",")
};
println!(
" {:<24} {:<9} {} agent(s) tags: {tags}",
r.id, r.visibility, r.eligible_agent_count
);
}
}
}
Err(e) => {
println!(" Rooms: failed to fetch ({e})");
any_failed = true;
}
}
}
if any_failed {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[serial_test::serial(home_env)]
async fn config_free_rooms_discovers_against_persisted_endpoint() {
use wiremock::matchers::{header, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
let mock = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/rooms"))
.and(header("authorization", "Bearer home-bearer"))
.respond_with(
ResponseTemplate::new(200).set_body_json(serde_json::json!([{
"id": "noosphera",
"tags": ["noosphera:0v1"],
"visibility": "public",
"eligible_agent_count": 2,
"eligible_agent_ids": ["a", "b"],
}])),
)
.expect(1)
.mount(&mock)
.await;
let home = tempfile::TempDir::new().unwrap();
let nsed = home.path().join(".nsed");
std::fs::create_dir_all(&nsed).unwrap();
std::fs::write(nsed.join("orchestrator"), format!("{}\n", mock.uri())).unwrap();
std::fs::write(nsed.join("operator.token"), "home-bearer\n").unwrap();
let prev_home = std::env::var_os("HOME");
unsafe {
std::env::set_var("HOME", home.path());
std::env::remove_var("QUORUM_ORCHESTRATOR");
}
let missing = home.path().join("nsed.yaml");
let _code = run(&missing, None).await;
unsafe {
match prev_home {
Some(v) => std::env::set_var("HOME", v),
None => std::env::remove_var("HOME"),
}
}
let reqs = mock.received_requests().await.unwrap();
assert_eq!(reqs.len(), 1, "exactly one GET /rooms expected");
}
}