From 4dc348bb4172f4d2a0c1b7a82613ede21fa50ec1 Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 25 Feb 2026 09:26:43 +0000 Subject: [PATCH 1/2] feat(params): add secret parameter support --- crates/config/src/towerfile.rs | 24 ++++++++++++++++++++ crates/tower-api/src/models/parameter.rs | 4 ++++ crates/tower-api/src/models/run_parameter.rs | 9 +++++++- crates/tower-cmd/src/api.rs | 12 ++++++++-- crates/tower-cmd/src/mcp.rs | 17 +++++++++++++- crates/tower/src/lib.rs | 11 ++++----- 6 files changed, 67 insertions(+), 10 deletions(-) diff --git a/crates/config/src/towerfile.rs b/crates/config/src/towerfile.rs index aa385e1a..f29d511b 100644 --- a/crates/config/src/towerfile.rs +++ b/crates/config/src/towerfile.rs @@ -12,6 +12,9 @@ pub struct Parameter { #[serde(default)] pub default: String, + + #[serde(default)] + pub secret: bool, } #[derive(Deserialize, Serialize, Debug)] @@ -122,6 +125,7 @@ impl Towerfile { name, description, default, + secret: false, }); } } @@ -272,6 +276,26 @@ mod test { assert_eq!(towerfile.parameters.len(), 2); assert_eq!(towerfile.parameters[0].name, "my_first_param"); assert_eq!(towerfile.parameters[1].name, "my_second_param"); + assert!(!towerfile.parameters[0].secret); + } + + #[test] + fn test_parses_secret_parameters() { + let toml = r#" + [app] + name = "my-app" + script = "./script.py" + source = ["*.py"] + + [[parameters]] + name = "MY_PARAMETER" + secret = true + "#; + + let towerfile = crate::Towerfile::from_toml(toml).unwrap(); + assert_eq!(towerfile.parameters.len(), 1); + assert_eq!(towerfile.parameters[0].name, "MY_PARAMETER"); + assert!(towerfile.parameters[0].secret); } #[test] diff --git a/crates/tower-api/src/models/parameter.rs b/crates/tower-api/src/models/parameter.rs index ce85288d..15ab6414 100644 --- a/crates/tower-api/src/models/parameter.rs +++ b/crates/tower-api/src/models/parameter.rs @@ -23,6 +23,9 @@ pub struct Parameter { #[serde_as(as = "DefaultOnNull")] #[serde(rename = "name")] pub name: String, + #[serde(default)] + #[serde(rename = "secret")] + pub secret: bool, } impl Parameter { @@ -31,6 +34,7 @@ impl Parameter { default, description, name, + secret: false, } } } diff --git a/crates/tower-api/src/models/run_parameter.rs b/crates/tower-api/src/models/run_parameter.rs index 7e9164d4..815575b2 100644 --- a/crates/tower-api/src/models/run_parameter.rs +++ b/crates/tower-api/src/models/run_parameter.rs @@ -20,10 +20,17 @@ pub struct RunParameter { #[serde_as(as = "DefaultOnNull")] #[serde(rename = "value")] pub value: String, + #[serde(default)] + #[serde(rename = "secret")] + pub secret: bool, } impl RunParameter { pub fn new(name: String, value: String) -> RunParameter { - RunParameter { name, value } + RunParameter { + name, + value, + secret: false, + } } } diff --git a/crates/tower-cmd/src/api.rs b/crates/tower-cmd/src/api.rs index f3ddc421..16a8a576 100644 --- a/crates/tower-cmd/src/api.rs +++ b/crates/tower-cmd/src/api.rs @@ -840,7 +840,11 @@ pub async fn create_schedule( let run_parameters = parameters.map(|params| { params .into_iter() - .map(|(key, value)| RunParameter { name: key, value }) + .map(|(key, value)| RunParameter { + name: key, + value, + secret: false, + }) .collect() }); @@ -874,7 +878,11 @@ pub async fn update_schedule( let run_parameters = parameters.map(|params| { params .into_iter() - .map(|(key, value)| RunParameter { name: key, value }) + .map(|(key, value)| RunParameter { + name: key, + value, + secret: false, + }) .collect() }); diff --git a/crates/tower-cmd/src/mcp.rs b/crates/tower-cmd/src/mcp.rs index 95d9440d..630b4d9b 100644 --- a/crates/tower-cmd/src/mcp.rs +++ b/crates/tower-cmd/src/mcp.rs @@ -889,7 +889,22 @@ IMPORTANT REMINDERS: async fn tower_schedules_list(&self) -> Result { match api::list_schedules(&self.config, None, None).await { Ok(response) => { - Self::json_success(serde_json::json!({"schedules": response.schedules})) + let schedules = response + .schedules + .into_iter() + .map(|mut schedule| { + if let Some(parameters) = schedule.parameters.as_mut() { + for parameter in parameters { + if parameter.secret { + parameter.value = "[hidden]".to_string(); + } + } + } + schedule + }) + .collect::>(); + + Self::json_success(serde_json::json!({"schedules": schedules})) } Err(e) => Self::error_result("Failed to list schedules", e), } diff --git a/crates/tower/src/lib.rs b/crates/tower/src/lib.rs index 751f4ba6..1549e234 100644 --- a/crates/tower/src/lib.rs +++ b/crates/tower/src/lib.rs @@ -23,8 +23,8 @@ mod bindings { let spec = PackageSpec::from_towerfile(&towerfile); - let runtime = tokio::runtime::Runtime::new() - .map_err(|e| PyRuntimeError::new_err(e.to_string()))?; + let runtime = + tokio::runtime::Runtime::new().map_err(|e| PyRuntimeError::new_err(e.to_string()))?; let output = PathBuf::from(output); @@ -40,8 +40,7 @@ mod bindings { .as_ref() .ok_or_else(|| PyRuntimeError::new_err("package build produced no output file"))?; - std::fs::copy(src, &output) - .map_err(|e| PyRuntimeError::new_err(e.to_string()))?; + std::fs::copy(src, &output).map_err(|e| PyRuntimeError::new_err(e.to_string()))?; Ok(()) }) @@ -53,8 +52,8 @@ mod bindings { /// args: Command line arguments (typically sys.argv). #[pyfunction] fn _run_cli(args: Vec) -> PyResult<()> { - let runtime = tokio::runtime::Runtime::new() - .map_err(|e| PyRuntimeError::new_err(e.to_string()))?; + let runtime = + tokio::runtime::Runtime::new().map_err(|e| PyRuntimeError::new_err(e.to_string()))?; // App::new_from_args() must run inside block_on because // Session::from_config_dir() requires an active tokio reactor. From 204808f283d2e44abe1e2119e5d6c2c7291d0d18 Mon Sep 17 00:00:00 2001 From: Brad Heller Date: Wed, 25 Feb 2026 13:55:28 +0000 Subject: [PATCH 2/2] Rename secret parameter field to hidden --- crates/config/src/towerfile.rs | 10 +++++----- crates/tower-api/src/models/parameter.rs | 6 +++--- crates/tower-api/src/models/run_parameter.rs | 6 +++--- crates/tower-cmd/src/api.rs | 4 ++-- crates/tower-cmd/src/mcp.rs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/config/src/towerfile.rs b/crates/config/src/towerfile.rs index f29d511b..703c999d 100644 --- a/crates/config/src/towerfile.rs +++ b/crates/config/src/towerfile.rs @@ -14,7 +14,7 @@ pub struct Parameter { pub default: String, #[serde(default)] - pub secret: bool, + pub hidden: bool, } #[derive(Deserialize, Serialize, Debug)] @@ -125,7 +125,7 @@ impl Towerfile { name, description, default, - secret: false, + hidden: false, }); } } @@ -276,7 +276,7 @@ mod test { assert_eq!(towerfile.parameters.len(), 2); assert_eq!(towerfile.parameters[0].name, "my_first_param"); assert_eq!(towerfile.parameters[1].name, "my_second_param"); - assert!(!towerfile.parameters[0].secret); + assert!(!towerfile.parameters[0].hidden); } #[test] @@ -289,13 +289,13 @@ mod test { [[parameters]] name = "MY_PARAMETER" - secret = true + hidden = true "#; let towerfile = crate::Towerfile::from_toml(toml).unwrap(); assert_eq!(towerfile.parameters.len(), 1); assert_eq!(towerfile.parameters[0].name, "MY_PARAMETER"); - assert!(towerfile.parameters[0].secret); + assert!(towerfile.parameters[0].hidden); } #[test] diff --git a/crates/tower-api/src/models/parameter.rs b/crates/tower-api/src/models/parameter.rs index 15ab6414..f53c7b45 100644 --- a/crates/tower-api/src/models/parameter.rs +++ b/crates/tower-api/src/models/parameter.rs @@ -24,8 +24,8 @@ pub struct Parameter { #[serde(rename = "name")] pub name: String, #[serde(default)] - #[serde(rename = "secret")] - pub secret: bool, + #[serde(rename = "hidden")] + pub hidden: bool, } impl Parameter { @@ -34,7 +34,7 @@ impl Parameter { default, description, name, - secret: false, + hidden: false, } } } diff --git a/crates/tower-api/src/models/run_parameter.rs b/crates/tower-api/src/models/run_parameter.rs index 815575b2..f45cc23d 100644 --- a/crates/tower-api/src/models/run_parameter.rs +++ b/crates/tower-api/src/models/run_parameter.rs @@ -21,8 +21,8 @@ pub struct RunParameter { #[serde(rename = "value")] pub value: String, #[serde(default)] - #[serde(rename = "secret")] - pub secret: bool, + #[serde(rename = "hidden")] + pub hidden: bool, } impl RunParameter { @@ -30,7 +30,7 @@ impl RunParameter { RunParameter { name, value, - secret: false, + hidden: false, } } } diff --git a/crates/tower-cmd/src/api.rs b/crates/tower-cmd/src/api.rs index 16a8a576..e4024606 100644 --- a/crates/tower-cmd/src/api.rs +++ b/crates/tower-cmd/src/api.rs @@ -843,7 +843,7 @@ pub async fn create_schedule( .map(|(key, value)| RunParameter { name: key, value, - secret: false, + hidden: false, }) .collect() }); @@ -881,7 +881,7 @@ pub async fn update_schedule( .map(|(key, value)| RunParameter { name: key, value, - secret: false, + hidden: false, }) .collect() }); diff --git a/crates/tower-cmd/src/mcp.rs b/crates/tower-cmd/src/mcp.rs index 630b4d9b..efe7e2a0 100644 --- a/crates/tower-cmd/src/mcp.rs +++ b/crates/tower-cmd/src/mcp.rs @@ -895,7 +895,7 @@ IMPORTANT REMINDERS: .map(|mut schedule| { if let Some(parameters) = schedule.parameters.as_mut() { for parameter in parameters { - if parameter.secret { + if parameter.hidden { parameter.value = "[hidden]".to_string(); } }