in src/Services/ProcessExecution/ExternalProcessService.cs [16:94]
public async Task<ProcessResult> ExecuteAsync(
string executablePath,
string arguments,
int timeoutSeconds = 300,
IEnumerable<string>? customPaths = null)
{
ArgumentException.ThrowIfNullOrEmpty(executablePath);
if (!File.Exists(executablePath))
{
throw new FileNotFoundException($"Executable not found at path: {executablePath}");
}
var processStartInfo = new ProcessStartInfo
{
FileName = executablePath,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
foreach (var keyValuePair in environmentVariables)
{
processStartInfo.EnvironmentVariables[keyValuePair.Key] = keyValuePair.Value;
}
using var process = new Process { StartInfo = processStartInfo };
using var outputWaitHandle = new AutoResetEvent(false);
using var errorWaitHandle = new AutoResetEvent(false);
var outputBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
process.OutputDataReceived += (sender, e) =>
{
if (e.Data == null)
outputWaitHandle.Set();
else
outputBuilder.AppendLine(e.Data);
};
process.ErrorDataReceived += (sender, e) =>
{
if (e.Data == null)
errorWaitHandle.Set();
else
errorBuilder.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await Task.WhenAll(
Task.Run(() => process.WaitForExit(timeoutSeconds * 1000)),
Task.Run(() =>
{
outputWaitHandle.WaitOne(1000);
errorWaitHandle.WaitOne(1000);
})
);
if (!process.HasExited)
{
process.Kill();
throw new TimeoutException($"Process execution timed out after {timeoutSeconds} seconds");
}
return new ProcessResult(
process.ExitCode,
outputBuilder.ToString().TrimEnd(),
errorBuilder.ToString().TrimEnd(),
$"{executablePath} {arguments}");
}