fix(security): resolve rebase conflicts and provider regressions

This commit is contained in:
Chummy 2026-02-17 19:01:36 +08:00
parent 5d131a8903
commit 0087bcc496
4 changed files with 23 additions and 44 deletions

View file

@ -281,16 +281,12 @@ fn parse_sse_line(line: &str) -> StreamResult<Option<String>> {
}
/// 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<StreamChunk>> {
use tokio::io::AsyncBufReadExt;
let name = "stream".to_string();
// Create a channel to send chunks
let (mut tx, rx) = tokio::sync::mpsc::channel::<StreamResult<StreamChunk>>(100);
let (tx, rx) = tokio::sync::mpsc::channel::<StreamResult<StreamChunk>>(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<StreamChunk>> {
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()
}

View file

@ -409,7 +409,7 @@ impl Provider for OpenRouterProvider {
model: &str,
temperature: f64,
) -> anyhow::Result<ProviderChatResponse> {
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",

View file

@ -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<StreamChunk>> {
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();

View file

@ -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,