Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/aof-core/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ impl Registry<AgentConfig> 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::<AgentConfig>(&file_path) {
Ok(agent) => {
let name = agent.name.clone();
Expand Down Expand Up @@ -663,6 +669,26 @@ fn load_yaml_file<T: serde::de::DeserializeOwned>(path: &Path) -> AofResult<T> {
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<String>,
}

let content = match std::fs::read_to_string(path) {
Ok(c) => c,
Err(_) => return false,
};

match serde_yaml::from_str::<KindCheck>(&content) {
Ok(check) => check.kind.as_deref() == Some(expected_kind),
Err(_) => false,
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
28 changes: 26 additions & 2 deletions crates/aof-triggers/src/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,15 +767,21 @@ 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) => {
info!("Loaded agent '{}' from {:?}", agent_name, path);
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);
}
}
}
Expand All @@ -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<String>,
}

let content = match std::fs::read_to_string(path) {
Ok(c) => c,
Err(_) => return false,
};

match serde_yaml::from_str::<KindCheck>(&content) {
Ok(check) => check.kind.as_deref() == Some(expected_kind),
Err(_) => false,
}
}

/// Register a platform
pub fn register_platform(&mut self, platform: Arc<dyn TriggerPlatform>) {
let name = platform.platform_name();
Expand Down
28 changes: 27 additions & 1 deletion crates/aofctl/src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AgentConfig> {
// First, try the normal parse
// First, check if this is the right kind of resource
#[derive(serde::Deserialize)]
struct KindCheck {
kind: Option<String>,
}

if let Ok(check) = serde_yaml::from_str::<KindCheck>(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 <file>' to run a fleet".to_string(),
"Flow" | "AgentFlow" => "Use 'aofctl run flow <file>' to run a flow".to_string(),
"Workflow" => "Use 'aofctl run workflow <file>' 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<AgentConfig, _> = serde_path_to_error::deserialize(deserializer);

Expand Down