Skip to main content

synwire_agent/vfs/
mod.rs

1//! VFS provider implementations.
2//!
3//! Each provider exposes a different data source through the [`Vfs`] trait,
4//! allowing LLMs to interact with it using filesystem-like operations.
5
6pub mod clone;
7pub mod composite;
8pub mod local;
9pub mod store;
10
11pub use composite::{CompositeProvider, Mount};
12pub use local::LocalProvider;
13pub use store::StoreProvider;
14
15use synwire_core::BoxFuture;
16use synwire_core::vfs::types::{CpOptions, LsOptions, RmOptions};
17use synwire_core::vfs::{Vfs, VfsError};
18
19/// Translate a bash-style command string into a VFS operation.
20///
21/// Supported commands: `ls`, `cat`, `rm`, `cp`, `mv`, `pwd`, `cd`.
22/// Unrecognised commands return [`VfsError::Unsupported`].
23pub fn bash_command<'a>(
24    vfs: &'a dyn Vfs,
25    cmd: &'a str,
26    args: &'a [&'a str],
27) -> BoxFuture<'a, Result<String, VfsError>> {
28    Box::pin(async move {
29        match cmd {
30            "ls" => {
31                let path = args.first().copied().unwrap_or(".");
32                let entries = vfs.ls(path, LsOptions::default()).await?;
33                let names: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
34                Ok(names.join("\n"))
35            }
36            "cat" => {
37                let path = args
38                    .first()
39                    .ok_or_else(|| VfsError::Unsupported("cat requires a path argument".into()))?;
40                let content = vfs.read(path).await?;
41                String::from_utf8(content.content)
42                    .map_err(|_| VfsError::Unsupported("binary file".into()))
43            }
44            "rm" => {
45                let path = args
46                    .first()
47                    .ok_or_else(|| VfsError::Unsupported("rm requires a path argument".into()))?;
48                vfs.rm(path, RmOptions::default()).await?;
49                Ok(String::new())
50            }
51            "cp" => {
52                if args.len() < 2 {
53                    return Err(VfsError::Unsupported("cp requires src and dst".into()));
54                }
55                let result = vfs.cp(args[0], args[1], CpOptions::default()).await?;
56                Ok(format!(
57                    "copied {} bytes to {}",
58                    result.bytes_transferred, result.path
59                ))
60            }
61            "mv" => {
62                if args.len() < 2 {
63                    return Err(VfsError::Unsupported("mv requires src and dst".into()));
64                }
65                let result = vfs.mv_file(args[0], args[1]).await?;
66                Ok(format!("moved to {}", result.path))
67            }
68            "pwd" => vfs.pwd().await,
69            "cd" => {
70                let path = args
71                    .first()
72                    .ok_or_else(|| VfsError::Unsupported("cd requires a path argument".into()))?;
73                vfs.cd(path).await?;
74                Ok(String::new())
75            }
76            other => Err(VfsError::Unsupported(format!("unknown command: {other}"))),
77        }
78    })
79}