feat(llm): improve UTF-8 handling in response processing to prevent infinite loops
feat(shell): enhance output capturing by using threads for stdout and stderr feat(tui): update usage widget to display token counts and provider/model information refactor(tui): simplify status bar rendering by removing unnecessary token calculations
This commit is contained in:
@@ -89,30 +89,47 @@ impl Tool for Bash {
|
||||
let start = std::time::Instant::now();
|
||||
let timeout = Duration::from_millis(timeout_ms);
|
||||
|
||||
let mut child_stdout = child.stdout.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to capture stdout"))?;
|
||||
let mut child_stderr = child.stderr.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("failed to capture stderr"))?;
|
||||
|
||||
let stdout_handle = std::thread::spawn(move || -> std::io::Result<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
let mut buf = Vec::new();
|
||||
child_stdout.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
});
|
||||
let stderr_handle = std::thread::spawn(move || -> std::io::Result<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
let mut buf = Vec::new();
|
||||
child_stderr.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
});
|
||||
|
||||
loop {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
let stdout = stdout_handle.join().unwrap_or(Ok(Vec::new()))?;
|
||||
let stderr = stderr_handle.join().unwrap_or(Ok(Vec::new()))?;
|
||||
|
||||
let elapsed = start.elapsed().as_secs_f64();
|
||||
let output = child
|
||||
.wait_with_output()
|
||||
.map_err(|e| anyhow::anyhow!("failed to collect output: {e}"))?;
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
let combined = if stderr.is_empty() {
|
||||
stdout
|
||||
let stdout_str = String::from_utf8_lossy(&stdout).to_string();
|
||||
let stderr_str = String::from_utf8_lossy(&stderr).to_string();
|
||||
let combined = if stderr_str.is_empty() {
|
||||
stdout_str
|
||||
} else {
|
||||
format!("{stdout}\n{stderr}")
|
||||
format!("{stdout_str}\n{stderr_str}")
|
||||
};
|
||||
let trimmed = combined.trim().to_string();
|
||||
|
||||
if status.success() {
|
||||
debug!(elapsed_secs = elapsed, "bash command completed successfully");
|
||||
return Ok(if trimmed.is_empty() {
|
||||
format!("Command completed in {elapsed:.2}s (exit code 0)")
|
||||
} else {
|
||||
format!("{trimmed}\n\nExit code: 0 ({elapsed:.2}s)")
|
||||
});
|
||||
}
|
||||
warn!(exit_code = status.code().unwrap_or(-1), elapsed_secs = elapsed, "bash command failed");
|
||||
return Ok(format!(
|
||||
"{}\n\nExit code: {} ({:.2}s)",
|
||||
trimmed,
|
||||
@@ -124,7 +141,6 @@ impl Tool for Bash {
|
||||
if start.elapsed() > timeout {
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
warn!(timeout_ms, "bash command timed out");
|
||||
anyhow::bail!("command timed out after {timeout_ms}ms");
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
|
||||
Reference in New Issue
Block a user