Add agent_id to WebSocket authentication

- Add UUID-based agent_id field to Config struct
- Auto-generate and persist agent_id on first run
- Include agent_id in WebSocket query parameters
- Update production server URL to connect.azcomputerguru.com

Fixes WebSocket connection failure where server expected agent_id parameter.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 13:51:31 -07:00
parent 7c16b2bf4d
commit e118fe6698
3 changed files with 36 additions and 10 deletions

View File

@@ -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<String>,
@@ -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"

View File

@@ -48,6 +48,7 @@ impl SessionManager {
let transport = WebSocketTransport::connect(
&self.config.server_url,
&self.config.agent_id,
&self.config.api_key,
).await?;

View File

@@ -29,17 +29,17 @@ pub struct WebSocketTransport {
impl WebSocketTransport {
/// Connect to the server
pub async fn connect(url: &str, api_key: &str) -> Result<Self> {
// 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<Self> {
// 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")?;