| 554 | } |
| 555 | |
| 556 | pub async fn wait_for_dashboard(agent: &ureq::Agent, base_url: &str) { |
| 557 | let probe = format!("{base_url}/api/capabilities"); |
| 558 | // Poll until the server both accepts the connection AND returns a real |
| 559 | // HTTP response (2xx). A bare connect success is not enough — the server |
| 560 | // can accept then drop the socket during startup ("Peer disconnected"). |
| 561 | for _ in 0..160 { |
| 562 | if let Ok(response) = agent.get(&probe).call() { |
| 563 | if response.status().is_success() { |
| 564 | return; |
| 565 | } |
| 566 | } |
| 567 | tokio::time::sleep(Duration::from_millis(50)).await; |
| 568 | } |
| 569 | panic!("dashboard server did not become ready at {base_url}"); |
| 570 | } |
| 571 | |
| 572 | pub fn isolated_lcm_db_path(tmp: &TempDir) -> std::path::PathBuf { |
| 573 | tmp.path().join(".tracedecay").join("sessions.db") |