diff --git a/agent/src/config.rs b/agent/src/config.rs index eb9924b..50a0671 100644 --- a/agent/src/config.rs +++ b/agent/src/config.rs @@ -3,6 +3,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use uuid::Uuid; /// Agent configuration #[derive(Debug, Clone, Serialize, Deserialize)] @@ -13,6 +14,10 @@ pub struct Config { /// Agent API key for authentication pub api_key: String, + /// Unique agent identifier (generated on first run) + #[serde(default = "generate_agent_id")] + pub agent_id: String, + /// Optional hostname override pub hostname_override: Option, @@ -25,6 +30,10 @@ pub struct Config { pub encoding: EncodingConfig, } +fn generate_agent_id() -> String { + Uuid::new_v4().to_string() +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CaptureConfig { /// Target frames per second (1-60) @@ -101,9 +110,15 @@ impl Config { let contents = std::fs::read_to_string(&config_path) .with_context(|| format!("Failed to read config from {:?}", config_path))?; - let config: Config = toml::from_str(&contents) + let mut config: Config = toml::from_str(&contents) .with_context(|| "Failed to parse config file")?; + // Ensure agent_id is set and saved + if config.agent_id.is_empty() { + config.agent_id = generate_agent_id(); + let _ = config.save(); + } + return Ok(config); } @@ -114,13 +129,22 @@ impl Config { let api_key = std::env::var("GURUCONNECT_API_KEY") .unwrap_or_else(|_| "dev-key".to_string()); - Ok(Config { + let agent_id = std::env::var("GURUCONNECT_AGENT_ID") + .unwrap_or_else(|_| generate_agent_id()); + + let config = Config { server_url, api_key, + agent_id, hostname_override: std::env::var("GURUCONNECT_HOSTNAME").ok(), capture: CaptureConfig::default(), encoding: EncodingConfig::default(), - }) + }; + + // Save config with generated agent_id for persistence + let _ = config.save(); + + Ok(config) } /// Get the configuration file path @@ -182,6 +206,7 @@ pub fn example_config() -> &'static str { # Server connection server_url = "wss://connect.example.com/ws" api_key = "your-agent-api-key" +agent_id = "auto-generated-uuid" # Optional: override hostname # hostname_override = "custom-hostname" diff --git a/agent/src/session/mod.rs b/agent/src/session/mod.rs index ce689d0..1c87eae 100644 --- a/agent/src/session/mod.rs +++ b/agent/src/session/mod.rs @@ -48,6 +48,7 @@ impl SessionManager { let transport = WebSocketTransport::connect( &self.config.server_url, + &self.config.agent_id, &self.config.api_key, ).await?; diff --git a/agent/src/transport/websocket.rs b/agent/src/transport/websocket.rs index edd8969..2fa675c 100644 --- a/agent/src/transport/websocket.rs +++ b/agent/src/transport/websocket.rs @@ -29,17 +29,17 @@ pub struct WebSocketTransport { impl WebSocketTransport { /// Connect to the server - pub async fn connect(url: &str, api_key: &str) -> Result { - // Append API key as query parameter - let url_with_auth = if url.contains('?') { - format!("{}&api_key={}", url, api_key) + pub async fn connect(url: &str, agent_id: &str, api_key: &str) -> Result { + // Append agent_id and API key as query parameters + let url_with_params = if url.contains('?') { + format!("{}&agent_id={}&api_key={}", url, agent_id, api_key) } else { - format!("{}?api_key={}", url, api_key) + format!("{}?agent_id={}&api_key={}", url, agent_id, api_key) }; - tracing::info!("Connecting to {}", url); + tracing::info!("Connecting to {} as agent {}", url, agent_id); - let (ws_stream, response) = connect_async(&url_with_auth) + let (ws_stream, response) = connect_async(&url_with_params) .await .context("Failed to connect to WebSocket server")?;