|
| 1 | +mod config; |
| 2 | +mod signal; |
| 3 | + |
| 4 | +use acp_nats::{agent::Bridge, client, nats, otel}; |
| 5 | +use agent_client_protocol::AgentSideConnection; |
| 6 | +use async_nats::Client as NatsAsyncClient; |
| 7 | +use std::sync::Arc; |
| 8 | +use tracing::{error, info}; |
| 9 | +use trogon_std::env::SystemEnv; |
| 10 | + |
| 11 | +#[tokio::main] |
| 12 | +async fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 13 | + let config = config::from_env_with_provider(&SystemEnv); |
| 14 | + otel::init_logger(&config)?; |
| 15 | + |
| 16 | + let log_file = otel::get_log_file_path()?; |
| 17 | + info!(log_file = %log_file.display(), "ACP bridge starting"); |
| 18 | + |
| 19 | + let nats_client = match nats::connect(&config.nats).await { |
| 20 | + Ok(client) => Some(client), |
| 21 | + Err(e) => { |
| 22 | + error!(error = %e, "Failed to connect to NATS"); |
| 23 | + None |
| 24 | + } |
| 25 | + }; |
| 26 | + let local = tokio::task::LocalSet::new(); |
| 27 | + |
| 28 | + local |
| 29 | + .run_until(async { |
| 30 | + let stdin = async_compat::Compat::new(tokio::io::stdin()); |
| 31 | + let stdout = async_compat::Compat::new(tokio::io::stdout()); |
| 32 | + |
| 33 | + let bridge = Arc::new(Bridge::<NatsAsyncClient>::new( |
| 34 | + nats_client.clone(), |
| 35 | + config.acp_prefix.clone(), |
| 36 | + )); |
| 37 | + |
| 38 | + let (connection, io_task) = |
| 39 | + AgentSideConnection::new(bridge.clone(), stdout, stdin, |fut| { |
| 40 | + tokio::task::spawn_local(fut); |
| 41 | + }); |
| 42 | + |
| 43 | + let connection = Arc::new(connection); |
| 44 | + |
| 45 | + if let Some(nats_instance) = nats_client { |
| 46 | + let client_connection = connection.clone(); |
| 47 | + let bridge_for_client = bridge.clone(); |
| 48 | + tokio::task::spawn_local(async move { |
| 49 | + client::run::<NatsAsyncClient, _>( |
| 50 | + nats_instance, |
| 51 | + client_connection, |
| 52 | + bridge_for_client, |
| 53 | + ) |
| 54 | + .await; |
| 55 | + }); |
| 56 | + info!("ACP bridge running on stdio with NATS client proxy"); |
| 57 | + } else { |
| 58 | + info!("ACP bridge running on stdio (no NATS)"); |
| 59 | + } |
| 60 | + |
| 61 | + tokio::select! { |
| 62 | + result = io_task => { |
| 63 | + if let Err(e) = result { |
| 64 | + error!(error = %e, "IO task error"); |
| 65 | + } |
| 66 | + info!("ACP bridge shutting down (IO closed)"); |
| 67 | + } |
| 68 | + _ = signal::shutdown_signal() => { |
| 69 | + info!("ACP bridge shutting down (signal received)"); |
| 70 | + } |
| 71 | + } |
| 72 | + }) |
| 73 | + .await; |
| 74 | + |
| 75 | + info!("Flushing telemetry..."); |
| 76 | + otel::shutdown_otel().await; |
| 77 | + info!("ACP bridge stopped"); |
| 78 | + |
| 79 | + Ok(()) |
| 80 | +} |
0 commit comments