diff --git a/packages/tui-rs/src/hosted_runner/config.rs b/packages/tui-rs/src/hosted_runner/config.rs index c9bdce4df..ff6c24250 100644 --- a/packages/tui-rs/src/hosted_runner/config.rs +++ b/packages/tui-rs/src/hosted_runner/config.rs @@ -3,7 +3,8 @@ use std::fs; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; -const DEFAULT_LISTEN_HOST: &str = "0.0.0.0"; +const DEFAULT_LISTEN_HOST: &str = "127.0.0.1"; +const PORT_ONLY_LISTEN_HOST: &str = "0.0.0.0"; const DEFAULT_LISTEN_PORT: u16 = 8080; #[derive(Debug, Clone)] @@ -47,10 +48,18 @@ impl HostedRunnerConfig { .or(hosted_runner_port) .or(port_env) .unwrap_or(DEFAULT_LISTEN_PORT); + let explicit_port = + listen.port.is_some() || hosted_runner_port.is_some() || port_env.is_some(); let host = listen .host .or_else(|| env_value(env, "MAESTRO_HOSTED_RUNNER_HOST")) - .unwrap_or_else(|| DEFAULT_LISTEN_HOST.to_string()); + .unwrap_or_else(|| { + if explicit_port { + PORT_ONLY_LISTEN_HOST.to_string() + } else { + DEFAULT_LISTEN_HOST.to_string() + } + }); let bind_addr = resolve_bind_addr(&host, port)?; let snapshot_root = resolve_snapshot_root( first_env( diff --git a/packages/tui-rs/src/hosted_runner/tests.rs b/packages/tui-rs/src/hosted_runner/tests.rs index 4e89a4306..3c9c6423b 100644 --- a/packages/tui-rs/src/hosted_runner/tests.rs +++ b/packages/tui-rs/src/hosted_runner/tests.rs @@ -116,6 +116,19 @@ fn test_config(workspace_root: PathBuf) -> HostedRunnerConfig { } } +fn base_hosted_runner_env(workspace_root: &Path) -> HashMap { + HashMap::from([ + ( + "MAESTRO_RUNNER_SESSION_ID".to_string(), + "mrs_123".to_string(), + ), + ( + "MAESTRO_WORKSPACE_ROOT".to_string(), + workspace_root.display().to_string(), + ), + ]) +} + #[tokio::test] async fn route_rejects_connection_prefix_without_separator() { let workspace = tempdir().expect("workspace"); @@ -172,19 +185,11 @@ fn supervisor_executor_reports_runtime_not_ready_until_connected() { #[test] fn resolves_env_config_with_hosted_runner_contract_names() { let workspace = tempdir().expect("workspace"); - let mut env = HashMap::new(); - env.insert( - "MAESTRO_RUNNER_SESSION_ID".to_string(), - "mrs_123".to_string(), - ); + let mut env = base_hosted_runner_env(workspace.path()); env.insert( "MAESTRO_REMOTE_RUNNER_OWNER_INSTANCE_ID".to_string(), "pod_1".to_string(), ); - env.insert( - "MAESTRO_WORKSPACE_ROOT".to_string(), - workspace.path().display().to_string(), - ); env.insert( "MAESTRO_HOSTED_RUNNER_LISTEN".to_string(), "127.0.0.1:9090".to_string(), @@ -240,6 +245,52 @@ fn resolves_env_config_with_hosted_runner_contract_names() { ); } +#[test] +fn defaults_hosted_runner_bind_to_localhost_when_no_listen_env_is_set() { + let workspace = tempdir().expect("workspace"); + let env = base_hosted_runner_env(workspace.path()); + + let config = HostedRunnerConfig::from_env_map(&env).expect("config"); + + assert_eq!(config.bind_addr, "127.0.0.1:8080".parse().unwrap()); +} + +#[test] +fn preserves_wildcard_bind_for_port_only_hosted_runner_env() { + let workspace = tempdir().expect("workspace"); + for (key, value) in [ + ("MAESTRO_HOSTED_RUNNER_LISTEN", "9090"), + ("MAESTRO_HOSTED_RUNNER_PORT", "9091"), + ("PORT", "9092"), + ] { + let mut env = base_hosted_runner_env(workspace.path()); + env.insert(key.to_string(), value.to_string()); + + let config = HostedRunnerConfig::from_env_map(&env).expect("config"); + + assert_eq!( + config.bind_addr, + format!("0.0.0.0:{value}").parse().unwrap(), + "{key} should preserve hosted ingress wildcard binding" + ); + } +} + +#[test] +fn explicit_host_env_overrides_port_only_wildcard_bind() { + let workspace = tempdir().expect("workspace"); + let mut env = base_hosted_runner_env(workspace.path()); + env.insert("MAESTRO_HOSTED_RUNNER_PORT".to_string(), "9090".to_string()); + env.insert( + "MAESTRO_HOSTED_RUNNER_HOST".to_string(), + "127.0.0.1".to_string(), + ); + + let config = HostedRunnerConfig::from_env_map(&env).expect("config"); + + assert_eq!(config.bind_addr, "127.0.0.1:9090".parse().unwrap()); +} + #[test] fn sse_lag_reset_envelope_includes_current_snapshot() { let workspace = tempdir().expect("workspace");