sh_layer3/builtin_tools/
system_tools.rs1use crate::builtin_tools::BuiltinTool;
6use crate::types::{Layer3Result, ToolCategory};
7use async_trait::async_trait;
8use std::collections::HashMap;
9
10pub struct GetEnvTool;
16
17#[async_trait]
18impl BuiltinTool for GetEnvTool {
19 fn name(&self) -> &str {
20 "get_env"
21 }
22
23 fn description(&self) -> &str {
24 "Get an environment variable value."
25 }
26
27 fn parameters_schema(&self) -> serde_json::Value {
28 serde_json::json!({
29 "type": "object",
30 "properties": {
31 "name": {
32 "type": "string",
33 "description": "Environment variable name"
34 }
35 },
36 "required": ["name"]
37 })
38 }
39
40 fn category(&self) -> ToolCategory {
41 ToolCategory::System
42 }
43
44 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
45 let name = args["name"]
46 .as_str()
47 .ok_or_else(|| anyhow::anyhow!("Missing name parameter"))?;
48
49 std::env::var(name)
50 .map_err(|_| anyhow::anyhow!("Environment variable '{}' not found", name))
51 }
52}
53
54pub struct ListEnvTool;
60
61#[async_trait]
62impl BuiltinTool for ListEnvTool {
63 fn name(&self) -> &str {
64 "list_env"
65 }
66
67 fn description(&self) -> &str {
68 "List all environment variables."
69 }
70
71 fn parameters_schema(&self) -> serde_json::Value {
72 serde_json::json!({
73 "type": "object",
74 "properties": {
75 "filter": {
76 "type": "string",
77 "description": "Optional filter pattern (e.g., 'PATH')"
78 }
79 }
80 })
81 }
82
83 fn category(&self) -> ToolCategory {
84 ToolCategory::System
85 }
86
87 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
88 let filter = args["filter"].as_str().unwrap_or("");
89
90 let env_vars: HashMap<String, String> = std::env::vars()
91 .filter(|(k, _)| filter.is_empty() || k.contains(filter))
92 .collect();
93
94 let mut result: Vec<(String, String)> = env_vars.into_iter().collect();
95 result.sort_by_key(|(k, _)| k.clone());
96
97 let output: Vec<String> = result.iter().map(|(k, v)| format!("{}={}", k, v)).collect();
98
99 Ok(output.join("\n"))
100 }
101}
102
103pub struct SetEnvTool;
109
110#[async_trait]
111impl BuiltinTool for SetEnvTool {
112 fn name(&self) -> &str {
113 "set_env"
114 }
115
116 fn description(&self) -> &str {
117 "Set an environment variable for the current process."
118 }
119
120 fn parameters_schema(&self) -> serde_json::Value {
121 serde_json::json!({
122 "type": "object",
123 "properties": {
124 "name": {
125 "type": "string",
126 "description": "Environment variable name"
127 },
128 "value": {
129 "type": "string",
130 "description": "Environment variable value"
131 }
132 },
133 "required": ["name", "value"]
134 })
135 }
136
137 fn category(&self) -> ToolCategory {
138 ToolCategory::System
139 }
140
141 fn requires_confirmation(&self) -> bool {
142 true
143 }
144
145 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
146 let name = args["name"]
147 .as_str()
148 .ok_or_else(|| anyhow::anyhow!("Missing name parameter"))?;
149
150 let value = args["value"]
151 .as_str()
152 .ok_or_else(|| anyhow::anyhow!("Missing value parameter"))?;
153
154 std::env::set_var(name, value);
155 Ok(format!("Set {}={}", name, value))
156 }
157}
158
159pub struct GetCwdTool;
165
166#[async_trait]
167impl BuiltinTool for GetCwdTool {
168 fn name(&self) -> &str {
169 "get_cwd"
170 }
171
172 fn description(&self) -> &str {
173 "Get the current working directory."
174 }
175
176 fn parameters_schema(&self) -> serde_json::Value {
177 serde_json::json!({
178 "type": "object",
179 "properties": {}
180 })
181 }
182
183 fn category(&self) -> ToolCategory {
184 ToolCategory::System
185 }
186
187 async fn execute(&self, _args: serde_json::Value) -> Layer3Result<String> {
188 std::env::current_dir()
189 .map(|p| p.display().to_string())
190 .map_err(|e| anyhow::anyhow!("Failed to get cwd: {}", e))
191 }
192}
193
194pub struct ChangeDirTool;
200
201#[async_trait]
202impl BuiltinTool for ChangeDirTool {
203 fn name(&self) -> &str {
204 "change_dir"
205 }
206
207 fn description(&self) -> &str {
208 "Change the current working directory."
209 }
210
211 fn parameters_schema(&self) -> serde_json::Value {
212 serde_json::json!({
213 "type": "object",
214 "properties": {
215 "path": {
216 "type": "string",
217 "description": "Directory path to change to"
218 }
219 },
220 "required": ["path"]
221 })
222 }
223
224 fn category(&self) -> ToolCategory {
225 ToolCategory::System
226 }
227
228 fn requires_confirmation(&self) -> bool {
229 true
230 }
231
232 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
233 let path = args["path"]
234 .as_str()
235 .ok_or_else(|| anyhow::anyhow!("Missing path parameter"))?;
236
237 std::env::set_current_dir(path)
238 .map_err(|e| anyhow::anyhow!("Failed to change directory: {}", e))?;
239
240 let new_cwd = std::env::current_dir()
241 .map(|p| p.display().to_string())
242 .unwrap_or_else(|_| path.to_string());
243
244 Ok(format!("Changed directory to: {}", new_cwd))
245 }
246}
247
248pub struct SystemInfoTool;
254
255#[async_trait]
256impl BuiltinTool for SystemInfoTool {
257 fn name(&self) -> &str {
258 "system_info"
259 }
260
261 fn description(&self) -> &str {
262 "Get system information (OS, architecture, version)."
263 }
264
265 fn parameters_schema(&self) -> serde_json::Value {
266 serde_json::json!({
267 "type": "object",
268 "properties": {}
269 })
270 }
271
272 fn category(&self) -> ToolCategory {
273 ToolCategory::System
274 }
275
276 async fn execute(&self, _args: serde_json::Value) -> Layer3Result<String> {
277 let os = std::env::consts::OS;
278 let arch = std::env::consts::ARCH;
279 let sep = std::path::MAIN_SEPARATOR;
280
281 let mut info = vec![
282 format!("OS: {}", os),
283 format!("Architecture: {}", arch),
284 format!("Path separator: {}", sep),
285 ];
286
287 if let Ok(hostname) = hostname::get() {
289 info.push(format!("Hostname: {}", hostname.to_string_lossy()));
290 }
291
292 if let Ok(user) = std::env::var("USER").or_else(|_| std::env::var("USERNAME")) {
294 info.push(format!("User: {}", user));
295 }
296
297 if let Some(home) = dirs::data_local_dir() {
299 info.push(format!("Data: {}", home.display()));
300 }
301
302 let temp = std::env::temp_dir();
304 info.push(format!("Temp: {}", temp.display()));
305
306 Ok(info.join("\n"))
307 }
308}
309
310pub struct ProcessListTool;
316
317#[async_trait]
318impl BuiltinTool for ProcessListTool {
319 fn name(&self) -> &str {
320 "process_list"
321 }
322
323 fn description(&self) -> &str {
324 "List running processes (platform-dependent, limited info)."
325 }
326
327 fn parameters_schema(&self) -> serde_json::Value {
328 serde_json::json!({
329 "type": "object",
330 "properties": {
331 "filter": {
332 "type": "string",
333 "description": "Optional filter by process name"
334 }
335 }
336 })
337 }
338
339 fn category(&self) -> ToolCategory {
340 ToolCategory::System
341 }
342
343 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
344 let filter = args["filter"].as_str().unwrap_or("");
345
346 let mut system = sysinfo::System::new_all();
348 system.refresh_all();
349
350 let processes: Vec<String> = system
351 .processes()
352 .iter()
353 .filter(|(_, proc)| filter.is_empty() || proc.name().contains(filter))
354 .map(|(pid, proc)| format!("{}\t{}\t{}", pid, proc.name(), proc.cmd().join(" ")))
355 .take(50) .collect();
357
358 Ok(format!("PID\tName\tCommand\n{}", processes.join("\n")))
359 }
360}
361
362pub struct DiskUsageTool;
368
369#[async_trait]
370impl BuiltinTool for DiskUsageTool {
371 fn name(&self) -> &str {
372 "disk_usage"
373 }
374
375 fn description(&self) -> &str {
376 "Get disk usage information for a path."
377 }
378
379 fn parameters_schema(&self) -> serde_json::Value {
380 serde_json::json!({
381 "type": "object",
382 "properties": {
383 "path": {
384 "type": "string",
385 "description": "Path to check (default: current directory)"
386 }
387 }
388 })
389 }
390
391 fn category(&self) -> ToolCategory {
392 ToolCategory::System
393 }
394
395 async fn execute(&self, args: serde_json::Value) -> Layer3Result<String> {
396 let _path = args["path"].as_str().unwrap_or(".");
397
398 let mut system = sysinfo::System::new_all();
400 system.refresh_all();
401
402 let disks = sysinfo::Disks::new_with_refreshed_list();
404
405 let results: Vec<String> = disks
406 .iter()
407 .map(|disk| {
408 let total = disk.total_space();
409 let available = disk.available_space();
410 let used = total - available;
411 let mount = disk.mount_point().display();
412 format!(
413 "{}: {:.2} GB used of {:.2} GB ({:.0}% free)",
414 mount,
415 used as f64 / 1e9,
416 total as f64 / 1e9,
417 (available as f64 / total as f64) * 100.0
418 )
419 })
420 .collect();
421
422 if results.is_empty() {
423 Ok("No disk information available".to_string())
424 } else {
425 Ok(results.join("\n"))
426 }
427 }
428}
429
430pub struct MemoryUsageTool;
436
437#[async_trait]
438impl BuiltinTool for MemoryUsageTool {
439 fn name(&self) -> &str {
440 "memory_usage"
441 }
442
443 fn description(&self) -> &str {
444 "Get memory usage information."
445 }
446
447 fn parameters_schema(&self) -> serde_json::Value {
448 serde_json::json!({
449 "type": "object",
450 "properties": {}
451 })
452 }
453
454 fn category(&self) -> ToolCategory {
455 ToolCategory::System
456 }
457
458 async fn execute(&self, _args: serde_json::Value) -> Layer3Result<String> {
459 let mut system = sysinfo::System::new_all();
460 system.refresh_memory();
461
462 let total = system.total_memory();
463 let used = system.used_memory();
464 let available = system.available_memory();
465
466 Ok(format!(
467 "Memory: {:.2} GB used of {:.2} GB ({:.0}% free)",
468 used as f64 / 1e9,
469 total as f64 / 1e9,
470 (available as f64 / total as f64) * 100.0
471 ))
472 }
473}
474
475#[cfg(test)]
480mod tests {
481 use super::*;
482 use serde_json::json;
483
484 #[test]
485 fn test_get_env_category() {
486 let tool = GetEnvTool;
487 assert_eq!(tool.category(), ToolCategory::System);
488 }
489
490 #[tokio::test]
491 async fn test_get_cwd() {
492 let tool = GetCwdTool;
493 let result = tool.execute(json!({})).await;
494 assert!(result.is_ok());
495 }
496
497 #[tokio::test]
498 async fn test_list_env() {
499 let tool = ListEnvTool;
500 let result = tool.execute(json!({})).await;
501 assert!(result.is_ok());
502 let output = result.unwrap();
503 assert!(!output.is_empty());
504 }
505
506 #[tokio::test]
507 async fn test_system_info() {
508 let tool = SystemInfoTool;
509 let result = tool.execute(json!({})).await;
510 assert!(result.is_ok());
511 let output = result.unwrap();
512 assert!(output.contains("OS:"));
513 }
514
515 #[tokio::test]
516 async fn test_memory_usage() {
517 let tool = MemoryUsageTool;
518 let result = tool.execute(json!({})).await;
519 assert!(result.is_ok());
520 let output = result.unwrap();
521 assert!(output.contains("Memory:"));
522 }
523}