(&self, input: &serde_json::Value)
| 284 | } |
| 285 | |
| 286 | async fn list_dir(&self, input: &serde_json::Value) -> AppResult<String> { |
| 287 | let Some(path_str) = input["path"].as_str() else { |
| 288 | return Err(AppError::Validation("Missing 'path' field".to_string())); |
| 289 | }; |
| 290 | let safe_path = self.validate_path(path_str)?; |
| 291 | let sandbox_canonical = self.sandbox.canonicalize().map_err(AppError::from)?; |
| 292 | |
| 293 | let mut entries = Vec::new(); |
| 294 | let walker = WalkDir::new(&safe_path) |
| 295 | .max_depth(3) |
| 296 | .into_iter() |
| 297 | .filter_entry(|e| { |
| 298 | if e.file_type().is_dir() { |
| 299 | if let Some(name) = e.file_name().to_str() { |
| 300 | return !should_skip_dir(name); |
| 301 | } |
| 302 | } |
| 303 | true |
| 304 | }); |
| 305 | |
| 306 | for entry_result in walker { |
| 307 | let entry = match entry_result { |
| 308 | Ok(entry) => entry, |
| 309 | Err(_) => continue, |
| 310 | }; |
| 311 | |
| 312 | let canonical = match entry.path().canonicalize() { |
| 313 | Ok(canonical) => canonical, |
| 314 | Err(_) => continue, |
| 315 | }; |
| 316 | if !canonical.starts_with(&sandbox_canonical) { |
| 317 | continue; |
| 318 | } |
| 319 | |
| 320 | let rel = canonical |
| 321 | .strip_prefix(&sandbox_canonical) |
| 322 | .map_err(|e| AppError::Internal(format!("strip_prefix failed: {e}")))?; |
| 323 | let kind = if entry.file_type().is_dir() { |
| 324 | "dir" |
| 325 | } else { |
| 326 | "file" |
| 327 | }; |
| 328 | entries.push(format!("[{}] {}", kind, rel.display())); |
| 329 | } |
| 330 | |
| 331 | Ok(entries.join("\n")) |
| 332 | } |
| 333 | |
| 334 | async fn search_files(&self, input: &serde_json::Value) -> AppResult<String> { |
| 335 | let pattern_str = input["pattern"] |
no test coverage detected