From de2458c51832a0259be9bebe35b3d3f6b2a1c8c9 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 6 Mar 2026 17:01:09 -0500 Subject: [PATCH] Forward provider API keys to sandbox, wire up Daytona env vars, improve CLI error logging - Collect provider API key env vars and pass them to sandbox exec_command so CLI tools can authenticate in the sandbox environment - Wire up previously-ignored env_vars parameter in DaytonaSandbox, explicitly set all ExecuteCommandOptions fields (catches new fields at compile time) - Write stdout/stderr logs on CLI failure, fall back to stdout in error detail Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/arc-workflows/src/cli/cli_backend.rs | 24 +++++++++++++++++---- crates/arc-workflows/src/daytona_sandbox.rs | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/crates/arc-workflows/src/cli/cli_backend.rs b/crates/arc-workflows/src/cli/cli_backend.rs index 633ef6a89..87951c15a 100644 --- a/crates/arc-workflows/src/cli/cli_backend.rs +++ b/crates/arc-workflows/src/cli/cli_backend.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::Path; use std::sync::Arc; @@ -317,8 +318,15 @@ impl CodergenBackend for AgentCliBackend { let _ = tokio::fs::write(stage_dir.join("provider_used.json"), json).await; } + // Forward provider API keys so CLI tools can authenticate + let env_vars: HashMap = provider + .api_key_env_vars() + .iter() + .filter_map(|name| std::env::var(name).ok().map(|val| (name.to_string(), val))) + .collect(); + let result = sandbox - .exec_command(&command, 600_000, None, None, None) + .exec_command(&command, 600_000, None, Some(&env_vars), None) .await .map_err(|e| ArcError::handler(format!("CLI command failed: {e}")))?; @@ -332,11 +340,19 @@ impl CodergenBackend for AgentCliBackend { } if result.exit_code != 0 { + let _ = tokio::fs::write(stage_dir.join("cli_stdout.log"), &result.stdout).await; + let _ = tokio::fs::write(stage_dir.join("cli_stderr.log"), &result.stderr).await; + let stderr: String = result.stderr.chars().rev().take(500).collect::>().into_iter().rev().collect(); - let detail = if stderr.is_empty() { - format!("command: {command}") - } else { + let detail = if !stderr.is_empty() { stderr + } else { + let stdout: String = result.stdout.chars().rev().take(500).collect::>().into_iter().rev().collect(); + if !stdout.is_empty() { + format!("stdout: {stdout}") + } else { + format!("command: {command}") + } }; return Err(ArcError::handler(format!( "CLI command exited with code {}: {detail}", diff --git a/crates/arc-workflows/src/daytona_sandbox.rs b/crates/arc-workflows/src/daytona_sandbox.rs index e67fd2488..1b2a4bb8d 100644 --- a/crates/arc-workflows/src/daytona_sandbox.rs +++ b/crates/arc-workflows/src/daytona_sandbox.rs @@ -781,7 +781,7 @@ impl Sandbox for DaytonaSandbox { command: &str, timeout_ms: u64, working_dir: Option<&str>, - _env_vars: Option<&HashMap>, + env_vars: Option<&HashMap>, _cancel_token: Option, ) -> Result { let sandbox = self.sandbox()?; @@ -798,8 +798,8 @@ impl Sandbox for DaytonaSandbox { let options = daytona_sdk::ExecuteCommandOptions { cwd: Some(cwd), + env: env_vars.cloned(), timeout: Some(std::time::Duration::from_millis(timeout_ms)), - ..Default::default() }; // Wrap with `bash -c` so pipes, env vars, and shell features work.