agent_workspace_contract/
config.rs1use super::*;
2use std::collections::BTreeMap;
3
4#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
6pub struct FileVisibilityOptions {
7 #[serde(default)]
8 pub include_hidden: bool,
9 #[serde(default)]
10 pub include_ignored: bool,
11}
12
13#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
16#[serde(deny_unknown_fields, rename_all = "camelCase")]
17pub struct SecretRef {
18 pub provider: String,
19 pub key: String,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub version: Option<String>,
22}
23
24impl Debug for SecretRef {
25 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
26 formatter
27 .debug_struct("SecretRef")
28 .field("provider", &self.provider)
29 .field("key", &"[REDACTED]")
30 .field("version", &self.version.as_ref().map(|_| "[REDACTED]"))
31 .finish()
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39#[serde(deny_unknown_fields, rename_all = "camelCase")]
40pub struct WorkspaceConfig {
41 pub backend: String,
42 #[serde(default)]
43 pub settings: BTreeMap<String, String>,
44 #[serde(default)]
45 pub secrets: BTreeMap<String, SecretRef>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub durable_backend_id: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub working_root: Option<PathBuf>,
50}
51
52impl Default for WorkspaceConfig {
53 fn default() -> Self {
54 Self {
55 backend: "local".into(),
56 settings: BTreeMap::new(),
57 secrets: BTreeMap::new(),
58 durable_backend_id: None,
59 working_root: Some(std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))),
60 }
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(transparent)]
68pub struct ResolvedBackendId(pub String);
69
70impl ResolvedBackendId {
71 pub fn as_str(&self) -> &str {
72 &self.0
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn secret_ref_never_debugs_secret_coordinates() {
82 let secret = SecretRef {
83 provider: "vault".into(),
84 key: "prod/workspace/api-token".into(),
85 version: Some("42".into()),
86 };
87 let debug = format!("{secret:?}");
88 assert!(!debug.contains("prod/workspace/api-token"));
89 assert!(!debug.contains("42"));
90 let json = serde_json::to_string(&secret).unwrap();
91 assert!(!json.contains("token-value"));
92 }
93
94 #[test]
95 fn provider_config_contains_references_not_plaintext_credentials() {
96 let json = r#"{"backend":"daytona","settings":{"apiUrl":"https://provider"},"secrets":{"credential":{"provider":"vault","key":"workspace/daytona"}}}"#;
97 let config: WorkspaceConfig = serde_json::from_str(json).unwrap();
98 assert_eq!(config.backend, "daytona");
99 assert_eq!(config.secrets["credential"].provider, "vault");
100 }
101}