From 7a44809a41c6832a177572419f890c7d61ca1dd8 Mon Sep 17 00:00:00 2001 From: Butterfly Dev Date: Tue, 7 Apr 2026 05:32:32 +0000 Subject: [PATCH] =?UTF-8?q?agent:=20config.rs=20=E2=80=94=20rename=20Agent?= =?UTF-8?q?Config=20to=20RunOptions,=20add=20Clone,=20windows=5Fservice=20?= =?UTF-8?q?field,=20to=5Fservice=5Fargs()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/src/config.rs | 79 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/agent/src/config.rs b/agent/src/config.rs index eab4641..bef0f87 100644 --- a/agent/src/config.rs +++ b/agent/src/config.rs @@ -1,14 +1,20 @@ -//! Agent configuration parsed from CLI arguments and environment variables. +//! Agent run configuration — shared options for `run` and `service install`. +//! +//! This struct holds all the operational parameters for the Butterfly agent: +//! server address, encoding, frame rate, quality, etc. It is used both when +//! running in foreground mode (`butterfly-agent run`) and when embedding +//! these options into a system service definition (`butterfly-agent service install`). -use clap::Parser; +use clap::Args; use std::time::Duration; -/// Butterfly Desktop Agent — captures display from this machine and streams -/// it to a Butterfly server. Also receives and executes HUD commands (mouse, -/// keyboard) from remote viewers for full remote control. -#[derive(Parser, Debug)] -#[command(name = "butterfly-agent", version, about)] -pub struct AgentConfig { +/// Operational options for running the Butterfly agent. +/// +/// These options control how the agent connects, captures, encodes, and streams. +/// They are shared between foreground mode and service installation (the service +/// definition embeds these options so the agent runs with the same configuration). +#[derive(Args, Debug, Clone)] +pub struct RunOptions { /// WebSocket URL of the Butterfly server. /// /// Example: ws://192.168.1.100:8080 @@ -52,14 +58,18 @@ pub struct AgentConfig { /// Maximum reconnection attempts (0 = infinite). #[arg(long, default_value_t = 0, env = "BUTTERFLY_MAX_RECONNECT")] pub max_reconnect: u32, + + /// (Windows only, internal) Run as a Windows Service. + /// This flag is set automatically when the service installer registers the + /// agent with the Windows Service Control Manager. It should not be used + /// manually — the SCM uses it to signal that the agent should connect to + /// the service dispatcher instead of running in the foreground. + #[cfg(windows)] + #[arg(long, hide = true)] + pub windows_service: bool, } -impl AgentConfig { - /// Parse configuration from CLI arguments. - pub fn parse_args() -> Self { - Self::parse() - } - +impl RunOptions { /// Duration between captured frames. pub fn frame_interval(&self) -> Duration { Duration::from_secs_f64(1.0 / self.fps as f64) @@ -91,4 +101,45 @@ impl AgentConfig { self.server.replace("ws://", "http://").replace("wss://", "https://") ) } + + /// Build the command-line arguments vector for embedding in service definitions. + /// + /// Returns something like `["run", "--server", "ws://...", "--encoder", "h264", ...]`. + /// The "run" subcommand is included so the service knows to start in agent mode. + pub fn to_service_args(&self) -> Vec { + let mut args = vec!["run".to_string()]; + + args.push(format!("--server={}", self.server)); + + if let Some(ref sid) = self.session_id { + args.push(format!("--session-id={}", sid)); + } + + if self.fps != 60 { + args.push(format!("--fps={}", self.fps)); + } + if self.encoder != "h264" { + args.push(format!("--encoder={}", self.encoder)); + } + if self.quality != 60 { + args.push(format!("--quality={}", self.quality)); + } + if self.display != 0 { + args.push(format!("--display={}", self.display)); + } + if self.audio { + args.push("--audio".to_string()); + } + if self.heartbeat_secs != 15 { + args.push(format!("--heartbeat={}", self.heartbeat_secs)); + } + if self.reconnect_delay_secs != 3 { + args.push(format!("--reconnect-delay={}", self.reconnect_delay_secs)); + } + if self.max_reconnect != 0 { + args.push(format!("--max-reconnect={}", self.max_reconnect)); + } + + args + } }