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