agent-style workers

This commit is contained in:
2026-03-21 08:27:20 -05:00
parent 8ba7e3bb84
commit 4d5a3b1bf5
11 changed files with 469 additions and 161 deletions

View File

@@ -113,6 +113,10 @@ async fn main() -> Result<()> {
let runtime_list: Vec<&str> = detected.iter().map(|r| r.name.as_str()).collect();
let runtime_csv = runtime_list.join(",");
info!("Setting ATTUNE_WORKER_RUNTIMES={}", runtime_csv);
// SAFETY: std::env::set_var is safe in Rust 2021 edition. If upgrading
// to edition 2024+, this call will need to be wrapped in `unsafe {}`.
// It's sound here because detection runs single-threaded before tokio
// starts any worker tasks.
std::env::set_var("ATTUNE_WORKER_RUNTIMES", &runtime_csv);
// Stash for Phase 2: pass to WorkerService for rich capability registration
@@ -133,9 +137,10 @@ async fn main() -> Result<()> {
println!();
let detected = detect_runtimes();
print_detection_report(&detected);
} else if let Some(ref detected) = agent_detected_runtimes {
print_detection_report(detected);
} else {
// We already ran detection above; re-run to get a fresh Vec for the report
// (the previous one was consumed by env var setup).
// No detection ran (empty results), run it fresh
let detected = detect_runtimes();
print_detection_report(&detected);
}
@@ -144,6 +149,7 @@ async fn main() -> Result<()> {
// --- Phase 2: Load configuration ---
if let Some(config_path) = args.config {
// SAFETY: std::env::set_var is safe in Rust 2021 edition. See note above.
std::env::set_var("ATTUNE_CONFIG", config_path);
}

View File

@@ -19,6 +19,7 @@ use attune_common::models::runtime::RuntimeExecutionConfig;
use attune_common::models::{runtime::Runtime as RuntimeModel, Action, Execution, ExecutionStatus};
use attune_common::repositories::artifact::{ArtifactRepository, ArtifactVersionRepository};
use attune_common::repositories::execution::{ExecutionRepository, UpdateExecutionInput};
use attune_common::repositories::runtime::SELECT_COLUMNS as RUNTIME_SELECT_COLUMNS;
use attune_common::repositories::runtime_version::RuntimeVersionRepository;
use attune_common::repositories::{FindById, Update};
use attune_common::version_matching::select_best_version;
@@ -410,16 +411,14 @@ impl ActionExecutor {
// Load runtime information if specified
let runtime_record = if let Some(runtime_id) = action.runtime {
match sqlx::query_as::<_, RuntimeModel>(
r#"SELECT id, ref, pack, pack_ref, description, name,
distributions, installation, installers, execution_config,
auto_detected, detection_config,
created, updated
FROM runtime WHERE id = $1"#,
)
.bind(runtime_id)
.fetch_optional(&self.pool)
.await
let query = format!(
"SELECT {} FROM runtime WHERE id = $1",
RUNTIME_SELECT_COLUMNS
);
match sqlx::query_as::<_, RuntimeModel>(&query)
.bind(runtime_id)
.fetch_optional(&self.pool)
.await
{
Ok(Some(runtime)) => {
debug!(

View File

@@ -393,4 +393,72 @@ mod tests {
registration.deregister().await.unwrap();
}
#[test]
fn test_detected_runtimes_json_structure() {
// Test the JSON structure that set_detected_runtimes builds
let runtimes = vec![
DetectedRuntime {
name: "python".to_string(),
path: "/usr/bin/python3".to_string(),
version: Some("3.12.1".to_string()),
},
DetectedRuntime {
name: "shell".to_string(),
path: "/bin/bash".to_string(),
version: None,
},
];
let interpreters: Vec<serde_json::Value> = runtimes
.iter()
.map(|rt| {
json!({
"name": rt.name,
"path": rt.path,
"version": rt.version,
})
})
.collect();
let json_value = json!(interpreters);
// Verify structure
let arr = json_value.as_array().unwrap();
assert_eq!(arr.len(), 2);
assert_eq!(arr[0]["name"], "python");
assert_eq!(arr[0]["path"], "/usr/bin/python3");
assert_eq!(arr[0]["version"], "3.12.1");
assert_eq!(arr[1]["name"], "shell");
assert_eq!(arr[1]["path"], "/bin/bash");
assert!(arr[1]["version"].is_null());
}
#[test]
fn test_detected_runtimes_empty() {
let runtimes: Vec<DetectedRuntime> = vec![];
let interpreters: Vec<serde_json::Value> = runtimes
.iter()
.map(|rt| {
json!({
"name": rt.name,
"path": rt.path,
"version": rt.version,
})
})
.collect();
let json_value = json!(interpreters);
assert_eq!(json_value.as_array().unwrap().len(), 0);
}
#[test]
fn test_agent_mode_capability_value() {
// Verify the JSON value for agent_mode capability
let value = json!(true);
assert_eq!(value, true);
let value = json!(false);
assert_eq!(value, false);
}
}