diff --git a/crates/aof-core/src/registry.rs b/crates/aof-core/src/registry.rs index ea6ecc2..60231cb 100644 --- a/crates/aof-core/src/registry.rs +++ b/crates/aof-core/src/registry.rs @@ -72,6 +72,12 @@ impl Registry for AgentRegistry { let file_path = entry.path(); if file_path.extension().map_or(false, |e| e == "yaml" || e == "yml") { + // Skip non-Agent YAML files (Trigger, Fleet, Flow, etc.) + if !yaml_file_has_kind(&file_path, "Agent") { + tracing::debug!("Skipping non-Agent file: {:?}", file_path); + continue; + } + match load_yaml_file::(&file_path) { Ok(agent) => { let name = agent.name.clone(); @@ -663,6 +669,26 @@ fn load_yaml_file(path: &Path) -> AofResult { Ok(resource) } +/// Check if a YAML file has a specific `kind` field value +/// Returns true if the file has the expected kind, false otherwise +fn yaml_file_has_kind(path: &Path, expected_kind: &str) -> bool { + // Helper struct to just parse kind field + #[derive(serde::Deserialize)] + struct KindCheck { + kind: Option, + } + + let content = match std::fs::read_to_string(path) { + Ok(c) => c, + Err(_) => return false, + }; + + match serde_yaml::from_str::(&content) { + Ok(check) => check.kind.as_deref() == Some(expected_kind), + Err(_) => false, + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/aof-triggers/src/handler/mod.rs b/crates/aof-triggers/src/handler/mod.rs index 3f4b510..123a194 100644 --- a/crates/aof-triggers/src/handler/mod.rs +++ b/crates/aof-triggers/src/handler/mod.rs @@ -767,6 +767,12 @@ impl TriggerHandler { continue; } + // Check if file has kind: Agent before trying to parse + if !Self::yaml_file_has_kind(&path, "Agent") { + debug!("Skipping non-Agent file: {:?}", path); + continue; + } + // Try to load as agent match runtime.load_agent_from_file(path.to_str().unwrap()).await { Ok(agent_name) => { @@ -774,8 +780,8 @@ impl TriggerHandler { count += 1; } Err(e) => { - // Not all YAML files are agents, this is fine - debug!("Skipping {:?}: {}", path, e); + // Agent file failed to parse - this is a real error + warn!("Failed to load agent from {:?}: {}", path, e); } } } @@ -787,6 +793,24 @@ impl TriggerHandler { Ok(count) } + /// Check if a YAML file has a specific `kind` field value + fn yaml_file_has_kind(path: &std::path::Path, expected_kind: &str) -> bool { + #[derive(serde::Deserialize)] + struct KindCheck { + kind: Option, + } + + let content = match std::fs::read_to_string(path) { + Ok(c) => c, + Err(_) => return false, + }; + + match serde_yaml::from_str::(&content) { + Ok(check) => check.kind.as_deref() == Some(expected_kind), + Err(_) => false, + } + } + /// Register a platform pub fn register_platform(&mut self, platform: Arc) { let name = platform.platform_name(); diff --git a/crates/aofctl/src/commands/run.rs b/crates/aofctl/src/commands/run.rs index 097c315..5498aec 100644 --- a/crates/aofctl/src/commands/run.rs +++ b/crates/aofctl/src/commands/run.rs @@ -27,7 +27,33 @@ struct K8sMetadata { /// Parse agent config with detailed error messages including field path and line numbers fn parse_agent_config(content: &str, file_path: &str) -> Result { - // First, try the normal parse + // First, check if this is the right kind of resource + #[derive(serde::Deserialize)] + struct KindCheck { + kind: Option, + } + + if let Ok(check) = serde_yaml::from_str::(content) { + if let Some(kind) = check.kind { + if kind != "Agent" { + let hint = match kind.as_str() { + "Trigger" => "Triggers are used with 'aofctl serve', not 'aofctl run'".to_string(), + "Fleet" => "Use 'aofctl run fleet ' to run a fleet".to_string(), + "Flow" | "AgentFlow" => "Use 'aofctl run flow ' to run a flow".to_string(), + "Workflow" => "Use 'aofctl run workflow ' to run a workflow".to_string(), + _ => format!("This file contains a {} resource, not an Agent", kind), + }; + return Err(anyhow!( + "Wrong resource type: {}\n\n Expected: kind: Agent\n Found: kind: {}\n\n Hint: {}\n", + file_path, + kind, + hint + )); + } + } + } + + // Now try the normal parse let deserializer = serde_yaml::Deserializer::from_str(content); let result: Result = serde_path_to_error::deserialize(deserializer);