Skip to main content

sh_layer3/builtin_tools/
system_tools.rs

1//! # System Tools
2//!
3//! 系统工具集:环境变量、进程管理、系统信息。
4
5use crate::builtin_tools::BuiltinTool;
6use crate::types::{Layer3Result, ToolCategory};
7use async_trait::async_trait;
8use std::collections::HashMap;
9
10// ============================================================================
11// Get Environment Variable Tool
12// ============================================================================
13
14/// 环境变量获取工具
15pub 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
54// ============================================================================
55// List Environment Variables Tool
56// ============================================================================
57
58/// 环境变量列表工具
59pub 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
103// ============================================================================
104// Set Environment Variable Tool
105// ============================================================================
106
107/// 环境变量设置工具
108pub 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
159// ============================================================================
160// Current Working Directory Tool
161// ============================================================================
162
163/// 当前目录获取工具
164pub 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
194// ============================================================================
195// Change Directory Tool
196// ============================================================================
197
198/// 目录切换工具
199pub 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
248// ============================================================================
249// System Info Tool
250// ============================================================================
251
252/// 系统信息工具
253pub 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        // Try to get hostname
288        if let Ok(hostname) = hostname::get() {
289            info.push(format!("Hostname: {}", hostname.to_string_lossy()));
290        }
291
292        // Get user info
293        if let Ok(user) = std::env::var("USER").or_else(|_| std::env::var("USERNAME")) {
294            info.push(format!("User: {}", user));
295        }
296
297        // Get home directory
298        if let Some(home) = dirs::data_local_dir() {
299            info.push(format!("Data: {}", home.display()));
300        }
301
302        // Get temp directory from env
303        let temp = std::env::temp_dir();
304        info.push(format!("Temp: {}", temp.display()));
305
306        Ok(info.join("\n"))
307    }
308}
309
310// ============================================================================
311// Process List Tool
312// ============================================================================
313
314/// 进程列表工具
315pub 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        // Use sysinfo crate for process listing
347        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) // Limit output
356            .collect();
357
358        Ok(format!("PID\tName\tCommand\n{}", processes.join("\n")))
359    }
360}
361
362// ============================================================================
363// Disk Usage Tool
364// ============================================================================
365
366/// 磁盘使用工具
367pub 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        // Use sysinfo to get disk info
399        let mut system = sysinfo::System::new_all();
400        system.refresh_all();
401
402        // Get disks using the Disks API
403        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
430// ============================================================================
431// Memory Usage Tool
432// ============================================================================
433
434/// 内存使用工具
435pub 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// ============================================================================
476// Tests
477// ============================================================================
478
479#[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}