1use std::fs;
2use std::path::Path;
3
4use agent_stream_kit::{
5 ASKit, AgentContext, AgentData, AgentError, AgentOutput, AgentSpec, AgentValue, AsAgent,
6 askit_agent, async_trait,
7};
8
9static CATEGORY: &str = "Std/File";
10
11static PIN_PATH: &str = "path";
12static PIN_FILES: &str = "files";
13static PIN_TEXT: &str = "text";
14static PIN_DATA: &str = "data";
15
16#[askit_agent(
18 title = "List Files",
19 category = CATEGORY,
20 inputs = [PIN_PATH],
21 outputs = [PIN_FILES]
22)]
23struct ListFilesAgent {
24 data: AgentData,
25}
26
27#[async_trait]
28impl AsAgent for ListFilesAgent {
29 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
30 Ok(Self {
31 data: AgentData::new(askit, id, spec),
32 })
33 }
34
35 async fn process(
36 &mut self,
37 ctx: AgentContext,
38 _pin: String,
39 value: AgentValue,
40 ) -> Result<(), AgentError> {
41 let path = value
42 .as_str()
43 .ok_or_else(|| AgentError::InvalidValue("path is not a string".to_string()))?;
44 let path = Path::new(path);
45
46 if !path.exists() {
47 return Err(AgentError::InvalidValue(format!(
48 "Path does not exist: {}",
49 path.display()
50 )));
51 }
52
53 if !path.is_dir() {
54 return Err(AgentError::InvalidValue(format!(
55 "Path is not a directory: {}",
56 path.display()
57 )));
58 }
59
60 let mut files = Vec::new();
61 let entries = fs::read_dir(path).map_err(|e| {
62 AgentError::InvalidValue(format!(
63 "Failed to read directory {}: {}",
64 path.display(),
65 e
66 ))
67 })?;
68
69 for entry in entries {
70 let entry = entry.map_err(|e| {
71 AgentError::InvalidValue(format!("Failed to read directory entry: {}", e))
72 })?;
73 let file_name = entry.file_name().to_string_lossy().to_string();
74 files.push(file_name.into());
75 }
76
77 let out_value = AgentValue::array(files);
78 self.try_output(ctx, PIN_FILES, out_value)
79 }
80}
81
82#[askit_agent(
84 title = "Read Text File",
85 category = CATEGORY,
86 inputs = [PIN_PATH],
87 outputs = [PIN_TEXT]
88)]
89struct ReadTextFileAgent {
90 data: AgentData,
91}
92
93#[async_trait]
94impl AsAgent for ReadTextFileAgent {
95 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
96 Ok(Self {
97 data: AgentData::new(askit, id, spec),
98 })
99 }
100
101 async fn process(
102 &mut self,
103 ctx: AgentContext,
104 _pin: String,
105 value: AgentValue,
106 ) -> Result<(), AgentError> {
107 let path = value
108 .as_str()
109 .ok_or_else(|| AgentError::InvalidValue("path is not a string".into()))?;
110 let path = Path::new(path);
111
112 if !path.exists() {
113 return Err(AgentError::InvalidValue(format!(
114 "Path does not exist: {}",
115 path.display()
116 )));
117 }
118
119 if !path.is_file() {
120 return Err(AgentError::InvalidValue(format!(
121 "Path is not a file: {}",
122 path.display()
123 )));
124 }
125
126 let content = fs::read_to_string(path).map_err(|e| {
127 AgentError::InvalidValue(format!("Failed to read file {}: {}", path.display(), e))
128 })?;
129 let out_value = AgentValue::string(content);
130 self.try_output(ctx, PIN_TEXT, out_value)
131 }
132}
133
134#[askit_agent(
136 title = "Write Text File",
137 category = CATEGORY,
138 inputs = [PIN_DATA],
139 outputs = [PIN_DATA]
140)]
141struct WriteTextFileAgent {
142 data: AgentData,
143}
144
145#[async_trait]
146impl AsAgent for WriteTextFileAgent {
147 fn new(askit: ASKit, id: String, spec: AgentSpec) -> Result<Self, AgentError> {
148 Ok(Self {
149 data: AgentData::new(askit, id, spec),
150 })
151 }
152
153 async fn process(
154 &mut self,
155 ctx: AgentContext,
156 _pin: String,
157 value: AgentValue,
158 ) -> Result<(), AgentError> {
159 let input = value
160 .as_object()
161 .ok_or_else(|| AgentError::InvalidValue("Input is not an object".into()))?;
162
163 let path = input
164 .get("path")
165 .ok_or_else(|| AgentError::InvalidValue("Missing 'path' in input".into()))?
166 .as_str()
167 .ok_or_else(|| AgentError::InvalidValue("'path' is not a string".into()))?;
168
169 let text = input
170 .get("text")
171 .ok_or_else(|| AgentError::InvalidValue("Missing 'text' in input".into()))?
172 .as_str()
173 .ok_or_else(|| AgentError::InvalidValue("'text' is not a string".into()))?;
174
175 let path = Path::new(path);
176
177 if let Some(parent) = path.parent() {
179 if !parent.exists() {
180 fs::create_dir_all(parent).map_err(|e| {
181 AgentError::InvalidValue(format!("Failed to create parent directories: {}", e))
182 })?
183 }
184 }
185
186 fs::write(path, text).map_err(|e| {
187 AgentError::InvalidValue(format!("Failed to write file {}: {}", path.display(), e))
188 })?;
189
190 self.try_output(ctx, PIN_DATA, value)
191 }
192}