From 0087bcc496b504ad02ab884aaeb0aefa440ce8db Mon Sep 17 00:00:00 2001 From: Chummy Date: Tue, 17 Feb 2026 19:01:36 +0800 Subject: [PATCH] fix(security): resolve rebase conflicts and provider regressions --- src/providers/compatible.rs | 35 ++++++++++--------------------- src/providers/openrouter.rs | 4 ++-- src/providers/traits.rs | 18 ++++------------ src/tools/hardware_memory_read.rs | 10 +++++---- 4 files changed, 23 insertions(+), 44 deletions(-) diff --git a/src/providers/compatible.rs b/src/providers/compatible.rs index b3d3a7c..e21d284 100644 --- a/src/providers/compatible.rs +++ b/src/providers/compatible.rs @@ -281,16 +281,12 @@ fn parse_sse_line(line: &str) -> StreamResult> { } /// Convert SSE byte stream to text chunks. -async fn sse_bytes_to_chunks( - mut response: reqwest::Response, +fn sse_bytes_to_chunks( + response: reqwest::Response, count_tokens: bool, ) -> stream::BoxStream<'static, StreamResult> { - use tokio::io::AsyncBufReadExt; - - let name = "stream".to_string(); - // Create a channel to send chunks - let (mut tx, rx) = tokio::sync::mpsc::channel::>(100); + let (tx, rx) = tokio::sync::mpsc::channel::>(100); tokio::spawn(async move { // Buffer for incomplete lines @@ -341,10 +337,7 @@ async fn sse_bytes_to_chunks( return; // Receiver dropped } } - Ok(None) => { - // Empty line or [DONE] sentinel - continue - continue; - } + Ok(None) => {} Err(e) => { let _ = tx.send(Err(e)).await; return; @@ -365,10 +358,7 @@ async fn sse_bytes_to_chunks( // Convert channel receiver to stream stream::unfold(rx, |mut rx| async { - match rx.recv().await { - Some(chunk) => Some((chunk, rx)), - None => None, - } + rx.recv().await.map(|chunk| (chunk, rx)) }) .boxed() } @@ -692,7 +682,7 @@ impl Provider for OpenAiCompatibleProvider { temperature: f64, options: StreamOptions, ) -> stream::BoxStream<'static, StreamResult> { - let api_key = match self.api_key.as_ref() { + let credential = match self.credential.as_ref() { Some(key) => key.clone(), None => { let provider_name = self.name.clone(); @@ -739,10 +729,10 @@ impl Provider for OpenAiCompatibleProvider { // Apply auth header req_builder = match &auth_header { AuthStyle::Bearer => { - req_builder.header("Authorization", format!("Bearer {}", api_key)) + req_builder.header("Authorization", format!("Bearer {}", credential)) } - AuthStyle::XApiKey => req_builder.header("x-api-key", &api_key), - AuthStyle::Custom(header) => req_builder.header(header, &api_key), + AuthStyle::XApiKey => req_builder.header("x-api-key", &credential), + AuthStyle::Custom(header) => req_builder.header(header, &credential), }; // Set accept header for streaming @@ -771,7 +761,7 @@ impl Provider for OpenAiCompatibleProvider { } // Convert to chunk stream and forward to channel - let mut chunk_stream = sse_bytes_to_chunks(response, options.count_tokens).await; + let mut chunk_stream = sse_bytes_to_chunks(response, options.count_tokens); while let Some(chunk) = chunk_stream.next().await { if tx.send(chunk).await.is_err() { break; // Receiver dropped @@ -781,10 +771,7 @@ impl Provider for OpenAiCompatibleProvider { // Convert channel receiver to stream stream::unfold(rx, |mut rx| async move { - match rx.recv().await { - Some(chunk) => Some((chunk, rx)), - None => None, - } + rx.recv().await.map(|chunk| (chunk, rx)) }) .boxed() } diff --git a/src/providers/openrouter.rs b/src/providers/openrouter.rs index 859a500..b27bff4 100644 --- a/src/providers/openrouter.rs +++ b/src/providers/openrouter.rs @@ -409,7 +409,7 @@ impl Provider for OpenRouterProvider { model: &str, temperature: f64, ) -> anyhow::Result { - let api_key = self.api_key.as_ref().ok_or_else(|| { + let credential = self.credential.as_ref().ok_or_else(|| { anyhow::anyhow!( "OpenRouter API key not set. Run `zeroclaw onboard` or set OPENROUTER_API_KEY env var." ) @@ -462,7 +462,7 @@ impl Provider for OpenRouterProvider { let response = self .client .post("https://openrouter.ai/api/v1/chat/completions") - .header("Authorization", format!("Bearer {api_key}")) + .header("Authorization", format!("Bearer {credential}")) .header( "HTTP-Referer", "https://github.com/theonlyhennygod/zeroclaw", diff --git a/src/providers/traits.rs b/src/providers/traits.rs index f69ddd0..a6253e4 100644 --- a/src/providers/traits.rs +++ b/src/providers/traits.rs @@ -329,21 +329,11 @@ pub trait Provider: Send + Sync { /// Default implementation falls back to stream_chat_with_system with last user message. fn stream_chat_with_history( &self, - messages: &[ChatMessage], - model: &str, - temperature: f64, - options: StreamOptions, + _messages: &[ChatMessage], + _model: &str, + _temperature: f64, + _options: StreamOptions, ) -> stream::BoxStream<'static, StreamResult> { - let system = messages - .iter() - .find(|m| m.role == "system") - .map(|m| m.content.clone()); - let last_user = messages - .iter() - .rfind(|m| m.role == "user") - .map(|m| m.content.clone()) - .unwrap_or_default(); - // For default implementation, we need to convert to owned strings // This is a limitation of the default implementation let provider_name = "unknown".to_string(); diff --git a/src/tools/hardware_memory_read.rs b/src/tools/hardware_memory_read.rs index 4cc42d5..3232c78 100644 --- a/src/tools/hardware_memory_read.rs +++ b/src/tools/hardware_memory_read.rs @@ -94,14 +94,16 @@ impl Tool for HardwareMemoryReadTool { .get("address") .and_then(|v| v.as_str()) .unwrap_or("0x20000000"); - let address = parse_hex_address(address_str).unwrap_or(NUCLEO_RAM_BASE); + let _address = parse_hex_address(address_str).unwrap_or(NUCLEO_RAM_BASE); - let length = args.get("length").and_then(|v| v.as_u64()).unwrap_or(128) as usize; - let length = length.min(256).max(1); + let requested_length = args.get("length").and_then(|v| v.as_u64()).unwrap_or(128); + let _length = usize::try_from(requested_length) + .unwrap_or(256) + .clamp(1, 256); #[cfg(feature = "probe")] { - match probe_read_memory(chip.unwrap(), address, length) { + match probe_read_memory(chip.unwrap(), _address, _length) { Ok(output) => { return Ok(ToolResult { success: true,